pax_global_header00006660000000000000000000000064141774636240014530gustar00rootroot0000000000000052 comment=6ba6567ad897d56741159f8af90fc354ae050e61 cppcheck-2.7/000077500000000000000000000000001417746362400131605ustar00rootroot00000000000000cppcheck-2.7/.clang-tidy000066400000000000000000000040301417746362400152110ustar00rootroot00000000000000--- Checks: '*,-abseil-*,-altera-*,-android-*,-cert-*,-cppcoreguidelines-*,-fuchsia-*,-google-*,-hicpp-*,-linuxkernel-*,-llvm-*,-llvmlibc-*,-mpi-*,-objc-*,-openmp-*,-zircon-*,-readability-braces-around-statements,-readability-magic-numbers,-bugprone-macro-parentheses,-readability-isolate-declaration,-readability-function-size,-modernize-use-trailing-return-type,-readability-implicit-bool-conversion,-readability-uppercase-literal-suffix,-modernize-use-auto,-readability-else-after-return,-modernize-use-default-member-init,-readability-named-parameter,-readability-redundant-member-init,-performance-faster-string-find,-modernize-avoid-c-arrays,-modernize-use-equals-default,-readability-container-size-empty,-readability-simplify-boolean-expr,-modernize-use-override,-modernize-pass-by-value,-bugprone-branch-clone,-bugprone-narrowing-conversions,-modernize-raw-string-literal,-readability-convert-member-functions-to-static,-modernize-loop-convert,-misc-unused-using-decls,-modernize-use-emplace,-readability-const-return-type,-performance-unnecessary-value-param,-modernize-return-braced-init-list,-performance-inefficient-string-concatenation,-performance-for-range-copy,-misc-throw-by-value-catch-by-reference,-readability-avoid-const-params-in-decls,-readability-non-const-parameter,-misc-non-private-member-variables-in-classes,-bugprone-suspicious-string-compare,-readability-misleading-indentation,-clang-analyzer-*,-bugprone-signed-char-misuse,-readability-make-member-function-const,-misc-no-recursion,-readability-use-anyofallof,-performance-no-automatic-move,-bugprone-suspicious-include,-modernize-replace-random-shuffle,-readability-function-cognitive-complexity,-readability-redundant-access-specifiers,-modernize-use-equals-delete,-performance-noexcept-move-constructor,-concurrency-mt-unsafe,-bugprone-easily-swappable-parameters,-readability-suspicious-call-argument' WarningsAsErrors: '*' CheckOptions: - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic value: '1'cppcheck-2.7/.codacy.yml000066400000000000000000000003171417746362400152240ustar00rootroot00000000000000exclude_paths: - addons/test/** - addons/y2038/test/*.c - htmlreport/example.cc - samples/**/bad.c - samples/**/bad.cpp - test/test.cxx - test/cfg/*.c - test/cfg/*.cpp - test/synthetic/*.c cppcheck-2.7/.github/000077500000000000000000000000001417746362400145205ustar00rootroot00000000000000cppcheck-2.7/.github/workflows/000077500000000000000000000000001417746362400165555ustar00rootroot00000000000000cppcheck-2.7/.github/workflows/CI-cygwin.yml000066400000000000000000000025771417746362400211040ustar00rootroot00000000000000# Some convenient links: # - https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md # name: CI-cygwin on: [push,pull_request] defaults: run: shell: cmd jobs: build_cygwin: strategy: matrix: os: [windows-2019] arch: [x64, x86] fail-fast: true runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Set up Cygwin uses: egor-tensin/setup-cygwin@v3 with: platform: ${{ matrix.arch }} - name: Build cppcheck run: | C:\tools\cygwin\bin\bash.exe -l -c cd %GITHUB_WORKSPACE% && make -j2 - name: Build test run: | C:\tools\cygwin\bin\bash.exe -l -c cd %GITHUB_WORKSPACE% && make -j2 testrunner - name: Run test run: | C:\tools\cygwin\bin\bash.exe -l -c cd %GITHUB_WORKSPACE% && make -j2 check - name: Extra test for misra run: | C:\tools\cygwin\bin\bash.exe -l -c cd %GITHUB_WORKSPACE%\addons\test ..\..\cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra\misra-test.c --std=c89 --platform=unix64 && python3 ..\misra.py -verify misra\misra-test.c.dump C:\tools\cygwin\bin\bash.exe -l -c cd %GITHUB_WORKSPACE% .\cppcheck --addon=misra --inline-suppr --enable=information --error-exitcode=1 addons\test\misra\misra-ctu-*-test.c cppcheck-2.7/.github/workflows/CI-unixish-docker.yml000066400000000000000000000042561417746362400225340ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: CI-unixish-docker on: [push, pull_request] jobs: build: strategy: matrix: image: ["centos:7", "ubuntu:14.04", "ubuntu:16.04", "ubuntu:21.10"] fail-fast: false # Prefer quick result runs-on: ubuntu-20.04 container: image: ${{ matrix.image }} steps: - uses: actions/checkout@v2 - name: Install missing software on CentOS 7 if: matrix.image == 'centos:7' run: | yum install -y cmake gcc-c++ make yum install -y pcre-devel - name: Install missing software on ubuntu if: matrix.image != 'centos:7' run: | apt-get update apt-get install -y cmake g++ make python libxml2-utils apt-get install -y libpcre3-dev # tests require CMake 3.4 - name: Test CMake build (no tests) if: matrix.image != 'ubuntu:21.10' run: | mkdir cmake.output cd cmake.output cmake -G "Unix Makefiles" -DHAVE_RULES=On .. cmake --build . -- -j$(nproc) cd .. - name: Test CMake build if: matrix.image == 'ubuntu:21.10' run: | mkdir cmake.output cd cmake.output cmake -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On .. cmake --build . --target check -- -j$(nproc) cd .. - name: Build cppcheck run: | make clean make -j$(nproc) HAVE_RULES=yes - name: Build test run: | make -j$(nproc) testrunner HAVE_RULES=yes - name: Run test run: | make -j$(nproc) check HAVE_RULES=yes - name: Run extra tests run: | tools/generate_and_run_more_tests.sh - name: Validate run: | make -j$(nproc) checkCWEEntries validateXML - name: Test addons run: | ./cppcheck --addon=threadsafety addons/test/threadsafety ./cppcheck --addon=threadsafety --std=c++03 addons/test/threadsafety cppcheck-2.7/.github/workflows/CI-unixish.yml000066400000000000000000000203011417746362400212540ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: CI-unixish on: [push, pull_request] jobs: build: strategy: matrix: os: [ubuntu-18.04, ubuntu-20.04, macos-10.15] fail-fast: false # Prefer quick result runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Set up Python 3.10 uses: actions/setup-python@v2 with: python-version: '3.10' - name: Install missing software on ubuntu if: contains(matrix.os, 'ubuntu') run: | sudo apt-get update sudo apt-get install libxml2-utils sudo apt-get install z3 libz3-dev sudo apt-get install qtbase5-dev qttools5-dev libqt5charts5-dev qt5-default - name: Fix missing z3_version.h if: matrix.os == 'ubuntu-18.04' run: | cp externals/z3_version_old.h externals/z3_version.h - name: Install missing software on macos if: contains(matrix.os, 'macos') run: | brew install coreutils z3 qt@5 - name: Install missing Python packages run: | python -m pip install pip --upgrade python -m pip install pytest - name: CMake build on ubuntu (with GUI) if: contains(matrix.os, 'ubuntu') run: | mkdir cmake.output cd cmake.output cmake -G "Unix Makefiles" -DUSE_Z3=On -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On .. cmake --build . -- -j$(nproc) cd .. - name: CMake build on macos (with GUI) if: contains(matrix.os, 'macos') run: | mkdir cmake.output cd cmake.output cmake -G "Unix Makefiles" -DUSE_Z3=On -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DQt5_DIR=$(brew --prefix qt@5)/lib/cmake/Qt5 .. cmake --build . -- -j$(nproc) cd .. - name: Run CMake test run: | cmake --build cmake.output --target check -- -j$(nproc) - name: Run CTest run: | cd cmake.output ctest -j$(nproc) cd .. - name: Build and test with Unsigned char run: | make clean make -j$(nproc) CXXFLAGS=-funsigned-char testrunner ./testrunner TestSymbolDatabase - name: Check syntax with NONNEG run: | ls lib/*.cpp | xargs -n 1 -P $(nproc) g++ -fsyntax-only -std=c++0x -Ilib -Iexternals -Iexternals/picojson -Iexternals/simplecpp -Iexternals/tinyxml2 -DNONNEG - name: Build cppcheck run: | make clean make -j$(nproc) USE_Z3=yes HAVE_RULES=yes - name: Build test run: | make -j$(nproc) testrunner USE_Z3=yes HAVE_RULES=yes - name: Run test run: | make -j$(nproc) check USE_Z3=yes HAVE_RULES=yes # the script uses sed parameters not supported by MacOS - name: Run extra tests if: contains(matrix.os, 'ubuntu') run: | tools/generate_and_run_more_tests.sh - name: Run test/cli run: | cd test/cli pytest test-*.py cd ../../.. ln -s cppcheck 'cpp check' cd 'cpp check/test/cli' pytest test-*.py # fails on macos since some includes (e.g. sys/epoll.h) are not available - name: Run cfg tests if: contains(matrix.os, 'ubuntu') run: | make -j$(nproc) checkcfg # it seems macos has no "wc" command - name: Run showtimetop5 tests if: contains(matrix.os, 'ubuntu') run: | ./tools/test_showtimetop5.sh - name: Run --dump test run: | ./cppcheck test/testpreprocessor.cpp --dump xmllint --noout test/testpreprocessor.cpp.dump - name: Validate run: | make -j$(nproc) checkCWEEntries validateXML - name: Test addons run: | ./cppcheck --addon=threadsafety addons/test/threadsafety ./cppcheck --addon=threadsafety --std=c++03 addons/test/threadsafety ./cppcheck --addon=misra --inline-suppr --enable=information --error-exitcode=1 addons/test/misra/misra-ctu-*-test.c cd addons/test ../../cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra/misra-test.c --std=c89 --platform=unix64 && python3 ../misra.py -verify misra/misra-test.c.dump ../../cppcheck --addon=misra --platform=avr8 --error-exitcode=1 misra/misra-test-avr8.c - name: Build GUI on ubuntu if: contains(matrix.os, 'ubuntu') run: | pushd gui qmake CONFIG+=debug HAVE_QCHART=yes make -j$(nproc) - name: Run GUI tests on ubuntu if: contains(matrix.os, 'ubuntu') run: | pushd gui/test/cppchecklibrarydata qmake CONFIG+=debug make -j$(nproc) ./test-cppchecklibrarydata popd pushd gui/test/filelist qmake CONFIG+=debug make -j$(nproc) # TODO: requires X session #./test-filelist popd pushd gui/test/projectfile qmake CONFIG+=debug make -j$(nproc) ./test-projectfile popd pushd gui/test/translationhandler qmake CONFIG+=debug make -j$(nproc) # TODO: requires X session #./test-translationhandler popd pushd gui/test/xmlreportv2 qmake CONFIG+=debug make -j$(nproc) # TODO: requires X session #./test-xmlreportv2 - name: Generate Qt help file on ubuntu 18.04 if: matrix.os == 'ubuntu-18.04' run: | pushd gui/help qcollectiongenerator online-help.qhcp -o online-help.qhc - name: Generate Qt help file on ubuntu 20.04 if: matrix.os == 'ubuntu-20.04' run: | pushd gui/help qhelpgenerator online-help.qhcp -o online-help.qhc - name: Build triage on ubuntu if: matrix.os == 'ubuntu-20.04' run: | pushd tools/triage qmake CONFIG+=debug make -j$(nproc) - name: Build Fuzzer if: matrix.os == 'ubuntu-20.04' run: | pushd oss-fuzz make -j$(nproc) CXX=clang++ CXXFLAGS="-fsanitize=address" fuzz-client translate - name: Self check (build) if: matrix.os == 'ubuntu-20.04' run: | # compile with verification and ast matchers make clean make -j$(nproc) -s CPPFLAGS="-DCHECK_INTERNAL" CXXFLAGS="-g -O2" MATCHCOMPILER=yes VERIFY=1 # Run self check after "Build GUI" to include generated headers in analysis - name: Self check if: matrix.os == 'ubuntu-20.04' run: | # self check lib/cli mkdir b1 ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck --cppcheck-build-dir=b1 -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib --addon=naming.json -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings cli lib # check gui with qt settings mkdir b2 ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck --cppcheck-build-dir=b2 -D__CPPCHECK__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=qt --addon=naming.json -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings gui/*.cpp gui/temp/*.cpp # self check test and tools ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ -DQ_MOC_OUTPUT_REVISION=67 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli -Igui --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings test/*.cpp tools cppcheck-2.7/.github/workflows/CI-windows.yml000066400000000000000000000163521417746362400212720ustar00rootroot00000000000000# Some convenient links: # - https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md # name: CI-windows on: [push,pull_request] defaults: run: shell: cmd jobs: build: strategy: matrix: os: [windows-2019, windows-2022] arch: [x64, x86] qt_ver: ['', 5.15.2] fail-fast: false runs-on: ${{ matrix.os }} env: # see https://www.pcre.org/original/changelog.txt PCRE_VERSION: 8.45 # see https://github.com/Z3Prover/z3/releases: Z3_VERSION: 4.8.10 steps: - uses: actions/checkout@v2 if: matrix.arch == 'x64' || matrix.qt_ver == '' - name: Set up Python 3.10 if: matrix.qt_ver == '' uses: actions/setup-python@v2 with: python-version: '3.10' - name: Set up Visual Studio environment uses: ilammy/msvc-dev-cmd@v1 with: arch: ${{ matrix.arch }} - name: Cache PCRE id: cache-pcre uses: actions/cache@v2 if: matrix.arch == 'x64' || matrix.qt_ver == '' with: path: pcre-${{ env.PCRE_VERSION }}.zip key: pcre-${{ env.PCRE_VERSION }} - name: Download PCRE if: (matrix.arch == 'x64' || matrix.qt_ver == '') && steps.cache-pcre.outputs.cache-hit != 'true' run: | curl -fsSL https://github.com/pfultz2/pcre/archive/refs/tags/%PCRE_VERSION%.zip -o pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! - name: Install PCRE if: matrix.arch == 'x64' || matrix.qt_ver == '' run: | 7z x pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! cd pcre-%PCRE_VERSION% || exit /b !errorlevel! cmake . -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DPCRE_BUILD_PCRECPP=Off -DPCRE_BUILD_TESTS=Off -DPCRE_BUILD_PCREGREP=Off || exit /b !errorlevel! nmake || exit /b !errorlevel! copy pcre.h ..\externals || exit /b !errorlevel! if "${{ matrix.arch }}" == "x86" ( copy pcre.lib ..\externals\pcre.lib || exit /b !errorlevel! ) else ( copy pcre.lib ..\externals\pcre64.lib || exit /b !errorlevel! ) env: CL: /MP - name: Cache Z3 Library id: cache-z3 uses: actions/cache@v2 if: matrix.arch == 'x64' || matrix.qt_ver == '' with: path: z3-${{ env.Z3_VERSION }}-${{ matrix.arch }}-win.zip key: z3-${{ env.Z3_VERSION }}-${{ matrix.arch }}-win - name: Download Z3 library if: (matrix.arch == 'x64' || matrix.qt_ver == '') && steps.cache-z3.outputs.cache-hit != 'true' run: | curl -fsSL https://github.com/Z3Prover/z3/releases/download/z3-%Z3_VERSION%/z3-%Z3_VERSION%-${{ matrix.arch }}-win.zip -o z3-%Z3_VERSION%-${{ matrix.arch }}-win.zip || exit /b !errorlevel! - name: Install Z3 library if: matrix.arch == 'x64' || matrix.qt_ver == '' run: | 7z x z3-%Z3_VERSION%-${{ matrix.arch }}-win.zip -oexternals -r -y || exit /b !errorlevel! move externals\z3-%Z3_VERSION%-${{ matrix.arch }}-win externals\z3 || exit /b !errorlevel! - name: Cache Qt ${{ matrix.qt_ver }} if: matrix.qt_ver != '' && matrix.arch == 'x64' id: cache-qt uses: actions/cache@v1 # not v2! with: path: ../Qt key: Windows-QtCache-${{ matrix.qt_ver }}-qtcharts # no 32-bit Qt available - name: Install Qt ${{ matrix.qt_ver }} if: matrix.qt_ver != '' && matrix.arch == 'x64' uses: jurplel/install-qt-action@v2 with: version: ${{ matrix.qt_ver }} modules: 'qtcharts' cached: ${{ steps.cache-qt.outputs.cache-hit }} - name: Install missing Python packages if: matrix.qt_ver == '' run: | python -m pip install pip --upgrade || exit /b !errorlevel! python -m pip install pytest || exit /b !errorlevel! python -m pip install pytest-custom_exit_code || exit /b !errorlevel! - name: Build GUI release if: matrix.qt_ver != '' && matrix.arch == 'x64' run: | cd gui || exit /b !errorlevel! qmake HAVE_QCHART=yes || exit /b !errorlevel! nmake release || exit /b !errorlevel! env: CL: /MP - name: Deploy GUI if: matrix.qt_ver != '' && matrix.arch == 'x64' run: | windeployqt Build\gui || exit /b !errorlevel! del Build\gui\cppcheck-gui.ilk || exit /b !errorlevel! del Build\gui\cppcheck-gui.pdb || exit /b !errorlevel! - name: Run CMake if: false && matrix.qt_ver == '' run: | set ARCH=${{ matrix.arch }} if "${{ matrix.arch }}" == "x86" ( set ARCH=Win32 ) rm -rf build mkdir build cd build cmake -DBUILD_TESTS=On .. || exit /b !errorlevel! - name: Build CLI debug configuration using MSBuild if: matrix.qt_ver == '' run: | set ARCH=${{ matrix.arch }} if "${{ matrix.arch }}" == "x86" ( set ARCH=Win32 ) :: cmake --build build --target check --config Debug || exit /b !errorlevel! msbuild -m cppcheck.sln /p:Configuration=Debug-PCRE;Platform=%ARCH% -maxcpucount || exit /b !errorlevel! - name: Run Debug test if: matrix.qt_ver == '' run: .\bin\debug\testrunner.exe || exit /b !errorlevel! - name: Build CLI release configuration using MSBuild if: matrix.qt_ver == '' run: | set ARCH=${{ matrix.arch }} if "${{ matrix.arch }}" == "x86" ( set ARCH=Win32 ) :: cmake --build build --target check --config Release || exit /b !errorlevel! msbuild -m cppcheck.sln /p:Configuration=Release-PCRE;Platform=%ARCH% -maxcpucount || exit /b !errorlevel! - name: Run Release test if: matrix.qt_ver == '' run: .\bin\testrunner.exe || exit /b !errorlevel! - name: Run test/cli if: matrix.qt_ver == '' run: | :: since FILESDIR is not set copy the binary to the root so the addons are found :: copy .\build\bin\Release\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel! copy .\bin\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel! copy .\bin\cppcheck-core.dll .\cppcheck-core.dll || exit /b !errorlevel! cd test/cli || exit /b !errorlevel! :: python -m pytest --suppress-no-test-exit-code test-clang-import.py || exit /b !errorlevel! python -m pytest test-helloworld.py || exit /b !errorlevel! python -m pytest test-inline-suppress.py || exit /b !errorlevel! python -m pytest test-more-projects.py || exit /b !errorlevel! python -m pytest test-proj2.py || exit /b !errorlevel! python -m pytest test-suppress-syntaxError.py || exit /b !errorlevel! - name: Test addons if: matrix.qt_ver == '' run: | .\cppcheck.exe --addon=misra --inline-suppr --enable=information --error-exitcode=1 addons\test\misra\misra-ctu-*-test.c cd addons\test ..\..\cppcheck.exe --dump -DDUMMY --suppress=uninitvar --inline-suppr misra\misra-test.c --std=c89 --platform=unix64 && python3 ..\misra.py -verify misra\misra-test.c.dump cppcheck-2.7/.github/workflows/asan.yml000066400000000000000000000046121417746362400202250ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: address sanitizer on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 container: image: "ubuntu:21.10" env: ASAN_OPTIONS: detect_stack_use_after_return=1 steps: - uses: actions/checkout@v2 - name: Set up Python 3.10 uses: actions/setup-python@v2 with: python-version: '3.10' - name: Install missing software on ubuntu run: | apt-get update apt-get install -y make libz3-dev libpcre3-dev apt-get install -y clang-13 - name: Build run: make -j$(nproc) cppcheck testrunner USE_Z3=yes HAVE_RULES=yes MATCHCOMPILER=yes VERIFY=1 env: CC: clang-13 CXX: clang++-13 CXXFLAGS: "-fsanitize=address -O2 -g3 -DCPPCHK_GLIBCXX_DEBUG" CPPFLAGS: "-DCHECK_INTERNAL" - name: Run tests run: ./testrunner # TODO: re-enable - was being killed because of incresaed memory usage # - name: Self check # run: | # ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli --inconclusive --enable=style,performance,portability,warning,internal --exception-handling --debug-warnings cli lib # ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ -DQT_VERSION=0x050000 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=qt -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ --enable=style,performance,portability,warning,internal --exception-handling --debug-warnings gui/*.cpp # ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli -Igui --inconclusive --enable=style,performance,portability,warning,internal --exception-handling --debug-warnings test/*.cpp tools # TODO: This does takes too long to run # - name: Bughunting lib # run: ./cppcheck -D__CPPCHECK__ --bug-hunting -j$(nproc) lib cppcheck-2.7/.github/workflows/bughunting.yml000066400000000000000000000025231417746362400214540ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: bughunting # TODO: enable this when on: workflow_dispatch jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Set up Python 3.10 uses: actions/setup-python@v2 with: python-version: '3.10' - name: Install missing software run: | sudo apt-get update sudo apt-get install z3 libz3-dev - name: Build cppcheck run: | make -j$(nproc) USE_Z3=yes HAVE_RULES=yes MATCHCOMPILER=yes env: CXXFLAGS: "-O2 -march=native" # currently to slow to execute it in the CI - name: Run CVE suite run: | python test/bug-hunting/cve.py - name: Run ITC suite run: | git clone https://github.com/regehr/itc-benchmarks.git ~/itc python test/bug-hunting/itc.py - name: Run juliet run: | mkdir ~/juliet curl https://samate.nist.gov/SARD/testsuites/juliet/Juliet_Test_Suite_v1.3_for_C_Cpp.zip -o ~/juliet/juliet.zip cd ~/juliet && unzip -qq ~/juliet/juliet.zip python test/bug-hunting/juliet.py cppcheck-2.7/.github/workflows/buildman.yml000066400000000000000000000010201417746362400210640ustar00rootroot00000000000000name: Build manual on: [push, pull_request] jobs: convert_via_pandoc: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - run: | mkdir output - uses: docker://pandoc/latex:2.9 with: args: --output=output/manual.html man/manual.md - uses: docker://pandoc/latex:2.9 with: args: --output=output/manual.pdf man/manual.md - uses: actions/upload-artifact@v2 with: name: output path: output cppcheck-2.7/.github/workflows/clang-tidy.yml000066400000000000000000000037261417746362400213430ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: clang-tidy on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 container: image: "ubuntu:21.10" env: QT_VERSION: 5.15.2 steps: - uses: actions/checkout@v2 - name: Install missing software run: | apt-get update apt-get install -y cmake g++ make apt-get install -y libz3-dev apt-get install -y libpcre3-dev apt-get install -y libffi7 # work around missing dependency for Qt install step apt-get install -y clang-tidy-13 - name: Cache Qt ${{ env.QT_VERSION }} id: cache-qt uses: actions/cache@v1 # not v2! with: path: ../Qt key: Linux-QtCache-${{ env.QT_VERSION }}-qtcharts - name: Install Qt ${{ env.QT_VERSION }} uses: jurplel/install-qt-action@v2 with: install-deps: 'nosudo' version: ${{ env.QT_VERSION }} modules: 'qtcharts' cached: ${{ steps.cache-qt.outputs.cache-hit }} - name: Prepare CMake run: | mkdir cmake.output cd cmake.output cmake -G "Unix Makefiles" -DUSE_Z3=On -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCPPCHK_GLIBCXX_DEBUG=Off .. cd .. - name: Prepare CMake dependencies run: | # make sure the precompiled headers exist make -C cmake.output lib/CMakeFiles/lib_objs.dir/cmake_pch.hxx.cxx make -C cmake.output test/CMakeFiles/testrunner.dir/cmake_pch.hxx.cxx # make sure the auto-generated GUI sources exist make -C cmake.output autogen - name: Clang-Tidy run: | cmake --build cmake.output --target run-clang-tidy 2> /dev/null cppcheck-2.7/.github/workflows/codeql-analysis.yml000066400000000000000000000027631417746362400224000ustar00rootroot00000000000000name: "CodeQL" on: [push, pull_request] jobs: analyze: name: Analyze runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] language: ['cpp', 'python'] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install missing software on ubuntu run: | sudo apt-get update sudo apt-get install libxml2-utils sudo apt-get install libz3-dev libz3-4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main setup-python-dependencies: false - run: | make -j$(nproc) USE_Z3=yes HAVE_RULES=yes cppcheck - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 cppcheck-2.7/.github/workflows/coverage.yml000066400000000000000000000033261417746362400210770ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: Coverage on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Install missing software on ubuntu run: | sudo apt-get update sudo apt-get install libxml2-utils sudo apt-get install libz3-dev libz3-4 sudo apt-get install lcov sudo apt-get install libcppunit-dev python -m pip install pip --upgrade python -m pip install lcov_cobertura - name: Compile instrumented run: | make -j$(nproc) test CXXFLAGS="-g -fprofile-arcs -ftest-coverage" USE_Z3=yes HAVE_RULES=yes - name: Generate coverage report run: | rm -rf coverage_report ./testrunner test/cfg/runtests.sh gcov lib/*.cpp -o lib/ lcov --directory ./ --capture --output-file lcov_tmp.info -b ./ lcov --extract lcov_tmp.info "$(pwd)/*" --output-file lcov.info genhtml lcov.info -o coverage_report --frame --legend --demangle-cpp - uses: actions/upload-artifact@v2 with: name: Coverage results path: coverage_report - uses: codecov/codecov-action@v1.2.1 with: # token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos # file: ./coverage.xml # optional flags: unittests # optional name: ${{ github.repository }} # optional fail_ci_if_error: true # optional (default = false): cppcheck-2.7/.github/workflows/format.yml000066400000000000000000000022011417746362400205630ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: format on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Cache uncrustify uses: actions/cache@v2 id: cache-uncrustify with: path: | ~/uncrustify key: ${{ runner.os }}-uncrustify - name: build uncrustify if: steps.cache-uncrustify.outputs.cache-hit != 'true' run: | wget https://github.com/uncrustify/uncrustify/archive/refs/tags/uncrustify-0.72.0.tar.gz tar xzvf uncrustify-0.72.0.tar.gz && cd uncrustify-uncrustify-0.72.0 mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build . -- -j$(nproc) -s mkdir ~/uncrustify cp uncrustify ~/uncrustify/ - name: Uncrustify check run: | ~/uncrustify/uncrustify -c .uncrustify.cfg -l CPP --check */*.cpp */*.h cppcheck-2.7/.github/workflows/iwyu.yml000066400000000000000000000046631417746362400203060ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: include-what-you-use on: workflow_dispatch jobs: build: runs-on: ubuntu-20.04 container: image: "kalilinux/kali-rolling" steps: - uses: actions/checkout@v2 # TODO: the necessary packages are excessive - mostly because of Qt - use a pre-built image - name: Install missing software run: | apt-get update apt-get install -y cmake g++ make libz3-dev libpcre3-dev apt-get install -y qtbase5-dev qttools5-dev libqt5charts5-dev apt-get install -y wget iwyu - name: Prepare CMake run: | mkdir cmake.output cd cmake.output cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DUSE_Z3=On -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCPPCHK_GLIBCXX_DEBUG=Off -DUSE_MATCHCOMPILER=Off .. cd .. - name: Prepare CMake dependencies run: | # make sure the precompiled headers exist #make -C cmake.output lib/CMakeFiles/lib_objs.dir/cmake_pch.hxx.cxx #make -C cmake.output test/CMakeFiles/testrunner.dir/cmake_pch.hxx.cxx # make sure the auto-generated GUI sources exist make -C cmake.output autogen # make sure the auto-generated GUI dependencies exist make -C cmake.output gui-build-deps - name: Build Qt mappings run: | wget https://raw.githubusercontent.com/include-what-you-use/include-what-you-use/master/mapgen/iwyu-mapgen-qt.py python3 iwyu-mapgen-qt.py /usr/include/x86_64-linux-gnu/qt5/ > qt5.imp # TODO: the mapping file causes a massive slowdown so we cannot use it at the moment. # add "-Xiwyu --mapping_file=qt5.imp" at the end of the whole command if we can use them. - name: iwyu_tool run: | # do not fail for now so the output is being saved iwyu_tool -p cmake.output -j $(nproc) -- -w > iwyu.log || true - uses: actions/upload-artifact@v2 with: name: Qt Mappings path: ./qt5.imp - uses: actions/upload-artifact@v2 with: name: Logs path: ./*.log cppcheck-2.7/.github/workflows/release-windows.yml000066400000000000000000000113721417746362400224140ustar00rootroot00000000000000# Some convenient links: # - https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md # name: release-windows on: push: tags: - '2.*' schedule: - cron: '0 0 * * *' workflow_dispatch: defaults: run: shell: cmd jobs: build: runs-on: windows-2019 env: # see https://www.pcre.org/original/changelog.txt PCRE_VERSION: 8.45 # see https://github.com/Z3Prover/z3/releases: Z3_VERSION: 4.8.10 QT_VERSION: 5.15.2 steps: - uses: actions/checkout@v2 - name: Setup msbuild.exe uses: microsoft/setup-msbuild@v1.0.2 - name: Cache PCRE id: cache-pcre uses: actions/cache@v2 with: path: pcre-${{ env.PCRE_VERSION }}.zip key: pcre-${{ env.PCRE_VERSION }} - name: Download PCRE if: steps.cache-pcre.outputs.cache-hit != 'true' run: | curl -fsSL https://github.com/pfultz2/pcre/archive/refs/tags/%PCRE_VERSION%.zip -o pcre-%PCRE_VERSION%.zip - name: Install PCRE run: | 7z x pcre-%PCRE_VERSION%.zip cd pcre-%PCRE_VERSION% cmake . -G "Visual Studio 16 2019" -A x64 -DPCRE_BUILD_PCRECPP=OFF -DPCRE_BUILD_PCREGREP=OFF -DPCRE_BUILD_TESTS=OFF msbuild -m PCRE.sln /p:Configuration=Release /p:Platform=x64 copy pcre.h ..\externals copy Release\pcre.lib ..\externals\pcre64.lib - name: Cache Z3 Library id: cache-z3 uses: actions/cache@v2 with: path: z3-${{ env.Z3_VERSION }}-x64-win.zip key: z3-${{ env.Z3_VERSION }}-x64-win - name: Download Z3 library if: steps.cache-z3.outputs.cache-hit != 'true' run: | curl -fsSL https://github.com/Z3Prover/z3/releases/download/z3-%Z3_VERSION%/z3-%Z3_VERSION%-x64-win.zip -o z3-%Z3_VERSION%-x64-win.zip - name: Install Z3 library run: | 7z x z3-%Z3_VERSION%-x64-win.zip -oexternals -r -y move externals\z3-%Z3_VERSION%-x64-win externals\z3 - name: Cache Qt ${{ env.QT_VERSION }} id: cache-qt uses: actions/cache@v1 # not v2! with: path: ../Qt key: Windows-QtCache-${{ env.QT_VERSION }}-qtcharts-qthelp - name: Install Qt ${{ env.QT_VERSION }} uses: jurplel/install-qt-action@v2 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts qthelp' cached: ${{ steps.cache-qt.outputs.cache-hit }} - name: Create .qm run: | cd gui lupdate gui.pro lrelease gui.pro -removeidentical - name: Matchcompiler run: python tools\matchcompiler.py --write-dir lib - name: Build x64 release GUI run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" cd gui qmake HAVE_QCHART=yes nmake release env: CL: /MP - name: Deploy app run: | windeployqt Build\gui del Build\gui\cppcheck-gui.ilk del Build\gui\cppcheck-gui.pdb - name: Build CLI x64 release configuration using MSBuild run: msbuild -m cppcheck.sln /t:cli /p:Configuration=Release-PCRE /p:Platform=x64 - name: Collect files run: | move Build\gui win_installer\files mkdir win_installer\files\addons copy addons\*.* win_installer\files\addons mkdir win_installer\files\cfg copy cfg\*.cfg win_installer\files\cfg mkdir win_installer\files\platforms copy platforms\*.xml win_installer\files\platforms copy bin\cppcheck.exe win_installer\files copy bin\cppcheck-core.dll win_installer\files copy externals\z3\bin\libz3.dll win_installer\files mkdir win_installer\files\help xcopy /s gui\help win_installer\files\help del win_installer\files\translations\*.qm move gui\*.qm win_installer\files\translations - name: Build Installer run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" cd win_installer REM Read ProductVersion for /f "tokens=4 delims= " %%a in ('find "ProductVersion" productInfo.wxi') do set PRODUCTVER=%%a REM Remove double quotes set PRODUCTVER=%PRODUCTVER:"=% echo ProductVersion=%PRODUCTVER% msbuild -m cppcheck.wixproj /p:Platform=x64,ProductVersion=%PRODUCTVER%.${{ github.run_number }} - uses: actions/upload-artifact@v2 with: name: installer path: win_installer/Build/ - uses: actions/upload-artifact@v2 with: name: deploy path: win_installer\files cppcheck-2.7/.github/workflows/scriptcheck.yml000066400000000000000000000072411417746362400216060ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: scriptcheck on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Cache Cppcheck uses: actions/cache@v2 with: path: cppcheck key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} - name: build cppcheck run: | make -j$(nproc) -s strip -s ./cppcheck scriptcheck: needs: build runs-on: ubuntu-20.04 strategy: matrix: python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, '3.10'] fail-fast: false steps: - uses: actions/checkout@v2 - name: Restore Cppcheck uses: actions/cache@v2 with: path: cppcheck key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install missing software on ubuntu run: | sudo apt-get update sudo apt-get install tidy libxml2-utils - name: Install missing software on ubuntu (Python 2) if: matrix.python-version == '2.7' run: | python -m pip install pip --upgrade python -m pip install pytest python -m pip install pygments - name: Install missing software on ubuntu (Python 3) if: matrix.python-version != '2.7' run: | sudo apt-get install shellcheck python -m pip install pip --upgrade python -m pip install natsort python -m pip install pexpect python -m pip install pylint python -m pip install unittest2 python -m pip install pytest python -m pip install pygments python -m pip install requests python -m pip install psutil - name: run Shellcheck if: matrix.python-version == '3.10' run: | find . -name "*.sh" | xargs shellcheck --exclude SC2002,SC2013,SC2034,SC2035,SC2043,SC2046,SC2086,SC2089,SC2090,SC2129,SC2211,SC2231 - name: run pylint if: matrix.python-version == '3.10' run: | pylint --rcfile=pylintrc_travis --jobs $(nproc) addons/*.py htmlreport/cppcheck-htmlreport htmlreport/*.py tools/*.py - name: check .json files if: matrix.python-version == '3.10' run: | find . -name '*.json' | xargs -n 1 python -m json.tool > /dev/null - name: Validate if: matrix.python-version == '3.10' run: | make -j$(nproc) validateCFG validatePlatforms validateRules - name: check python syntax if: matrix.python-version != '2.7' run: | python -m py_compile addons/*.py python -m py_compile htmlreport/cppcheck-htmlreport python -m py_compile htmlreport/*.py python -m py_compile tools/*.py - name: compile addons run: | python -m compileall ./addons - name: test matchcompiler run: | python tools/test_matchcompiler.py - name: test addons run: | python -m pytest addons/test/test-*.py env: PYTHONPATH: ./addons - name: test htmlreport run: | htmlreport/test_htmlreport.py cd htmlreport ./check.sh - name: dmake if: matrix.python-version == '3.10' run: | make -j$(nproc) run-dmake git diff --exit-code cppcheck-2.7/.github/workflows/selfcheck.yml000066400000000000000000000072051417746362400212330ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: selfcheck on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 env: QT_VERSION: 5.15.2 steps: - uses: actions/checkout@v2 - name: Install missing software run: | sudo apt-get update sudo apt-get install libz3-dev - name: Cache Qt ${{ env.QT_VERSION }} id: cache-qt uses: actions/cache@v1 # not v2! with: path: ../Qt key: Linux-QtCache-${{ env.QT_VERSION }}-qtcharts - name: Install Qt ${{ env.QT_VERSION }} uses: jurplel/install-qt-action@v2 with: version: ${{ env.QT_VERSION }} modules: 'qtcharts' cached: ${{ steps.cache-qt.outputs.cache-hit }} # TODO: cache this - perform same build as for the other self check - name: Self check (build) run: | make clean make -j$(nproc) -s CXXFLAGS="-O2 -w" MATCHCOMPILER=yes - name: CMake run: | mkdir cmake.output pushd cmake.output cmake -G "Unix Makefiles" -DUSE_Z3=On -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=ON -DWITH_QCHART=ON -DCMAKE_GLOBAL_AUTOGEN_TARGET=On .. - name: Generate dependencies run: | # make sure the precompiled headers exist make -C cmake.output lib/CMakeFiles/lib_objs.dir/cmake_pch.hxx.cxx make -C cmake.output test/CMakeFiles/testrunner.dir/cmake_pch.hxx.cxx # make sure auto-generated GUI files exist make -C cmake.output autogen make -C cmake.output gui-build-deps # TODO: find a way to report unmatched suppressions without need to add information checks - name: Self check (unusedFunction) if: false # TODO: fails with preprocessorErrorDirective - see #10667 run: | ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__GNUC__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --inconclusive --enable=unusedFunction --exception-handling -rp=. --project=cmake.output/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr env: DISABLE_VALUEFLOW: 1 # the following steps are duplicated from above since setting up the buld node in a parallel step takes longer than the actual steps - name: CMake (no test) run: | mkdir cmake.output.notest pushd cmake.output.notest cmake -G "Unix Makefiles" -DUSE_Z3=On -DHAVE_RULES=On -DBUILD_TESTS=0 -DBUILD_GUI=ON -DWITH_QCHART=ON -DCMAKE_GLOBAL_AUTOGEN_TARGET=On .. - name: Generate dependencies (no test) run: | # make sure the precompiled headers exist make -C cmake.output.notest lib/CMakeFiles/lib_objs.dir/cmake_pch.hxx.cxx # make sure auto-generated GUI files exist make -C cmake.output.notest autogen make -C cmake.output.notest gui-build-deps # TODO: find a way to report unmatched suppressions without need to add information checks - name: Self check (unusedFunction / no test) run: | ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__GNUC__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --inconclusive --enable=unusedFunction --exception-handling -rp=. --project=cmake.output.notest/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr env: DISABLE_VALUEFLOW: 1cppcheck-2.7/.github/workflows/ubsan.yml000066400000000000000000000046161417746362400204170ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: undefined behaviour sanitizers on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 container: image: "ubuntu:21.10" env: UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 steps: - uses: actions/checkout@v2 - name: Set up Python 3.10 uses: actions/setup-python@v2 with: python-version: '3.10' - name: Install missing software on ubuntu run: | apt-get update apt-get install -y make libz3-dev libpcre3-dev apt-get install -y clang-13 - name: Build run: make -j$(nproc) cppcheck testrunner USE_Z3=yes HAVE_RULES=yes MATCHCOMPILER=yes VERIFY=1 env: CC: clang-13 CXX: clang++-13 CXXFLAGS: "-fsanitize=undefined -fsanitize=nullability -fno-sanitize=signed-integer-overflow -O2 -g3 -DCPPCHK_GLIBCXX_DEBUG" CPPFLAGS: "-DCHECK_INTERNAL" - name: Run tests run: ./testrunner - name: Self check run: | ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli --inconclusive --enable=style,performance,portability,warning,internal --exception-handling --debug-warnings cli lib ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ -DQT_VERSION=0x050000 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=qt -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ --enable=style,performance,portability,warning,internal --exception-handling --debug-warnings gui/*.cpp ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli -Igui --inconclusive --enable=style,performance,portability,warning,internal --exception-handling --debug-warnings test/*.cpp tools # TODO: This does takes too long to run # - name: Bughunting lib # run: ./cppcheck -D__CPPCHECK__ --bug-hunting -j$(nproc) lib cppcheck-2.7/.github/workflows/valgrind.yml000066400000000000000000000043721417746362400211140ustar00rootroot00000000000000# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions # Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners name: valgrind on: [push, pull_request] jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: Prepare run: | sudo apt-get update sudo apt-get install debian-goodies ubuntu-dbgsym-keyring # the key expired and the ubuntu-dbgsym-keyring package does not yet include the latest one - see https://bugs.launchpad.net/ubuntu/+source/ubuntu-keyring/+bug/1920640 wget -O - http://ddebs.ubuntu.com/dbgsym-release-key.asc | sudo apt-key add - - name: Add debug repos on ubuntu run: | echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list - name: Install missing software run: | sudo apt-get update sudo apt-get install libxml2-utils sudo apt-get install valgrind sudo apt-get install libz3-dev libz3-4 libz3-4-dbgsym sudo apt-get install libc6-dbg-amd64-cross - name: Build cppcheck run: | CXXFLAGS="-O1 -g" make -j$(nproc) USE_Z3=yes HAVE_RULES=yes MATCHCOMPILER=yes - name: Build test run: | CXXFLAGS="-O1 -g" make -j$(nproc) testrunner USE_Z3=yes HAVE_RULES=yes MATCHCOMPILER=yes - name: Run valgrind run: | valgrind --error-limit=yes --leak-check=full --num-callers=50 --show-reachable=yes --track-origins=yes --suppressions=valgrind/testrunner.supp --gen-suppressions=all --log-fd=9 --error-exitcode=42 ./testrunner TestGarbage TestOther TestSimplifyTemplate 9>memcheck.log cat memcheck.log - uses: actions/upload-artifact@v2 with: name: Logs path: ./*.log cppcheck-2.7/.gitignore000066400000000000000000000031671417746362400151570ustar00rootroot00000000000000*.bak *.gcno *.o *.pyc cppcheck cppcheck.exe cppcheck-core.dll dmake dmake.exe reduce reduce.exe tags testrunner testrunner.exe tools/daca2*.html tools/dmake tools/errmsg tools/extracttests # dump files generated by Cppcheck *.*.dump # VS generated files *.aps *.idb *.ncb *.obj *.opensdf *.orig *.pdb *.sdf *.suo *.user .vs/ # VS build folders bin/ Build/ BuildTmp/ cli/temp/ ipch/ lib/temp/ test/temp/ # XCode build folders and files *.mode[0-9]v[0-9] *.pbxuser build/ # GUI build folders gui/debug/ gui/release/ gui/temp/ # Other (generated) GUI files gui/*.qm gui/cppcheck-gui gui/cppcheck-gui.exe gui/gui.sln gui/gui.vcproj gui/help/online-help.qch gui/help/online-help.qhc gui/Makefile gui/Makefile.debug gui/Makefile.release gui/qrc_gui.cpp gui/test/Makefile gui/test/*/Makefile gui/test/*/*/Makefile gui/test/benchmark/simple/benchmark-simple gui/test/cppchecklibrarydata/qrc_resources.cpp gui/test/cppchecklibrarydata/test-cppchecklibrarydata gui/test/filelist/test-filelist gui/test/projectfile/test-projectfile gui/test/translationhandler/test-translationhandler gui/test/xmlreportv2/test-xmlreportv2 # Doxygen output folder doxyoutput/ # qmake generated htmlreport/.tox/ htmlreport/MANIFEST # Backup files and stuff from patches *.rej *~ # kdevelop 4.x *.kdev4 # Common cmake build directories build* # Temporal files *.swp # Snapcraft build part prime parts stage *.snap snap/.snapcraft # Manual folder man/manual.log man/manual.tex man/*.pdf man/*.html # CLion .idea /.metadata/ cmake-* # clang tooling temporary files .clangd/ compile_commands.json # qmake /gui/.qmake.stash #vs code .vscode #debian build system /debian cppcheck-2.7/.mailmap000066400000000000000000000054471417746362400146130ustar00rootroot00000000000000Andreas Bießmann Andrew Martin acm4me Ankita Gupta Ankita-gupta Benjamin Goose Daniel Marjamäki Daniel Marjamäki Daniel Marjamäki Daniel Marjam�ki Daniel Marjamäki Daniel Marjamäki Deepak Gupta deepak gupta Ettl Martin Martin Ettl Ettl Martin Ettl Martin Martin Ettl Frank Zingsheim Gianluca Scacco Gianluca Scacco Henrik Nilsson Kimmo Varis Kimmo varis Kimmo Varis Kimmo Varis Kimmo Varis Kimmo Varis Kimmo Varis Kimmo Varis Leandro Penz Leandro Lisboa Penz Leandro Penz Leandro Lisboa Penz makulik unknown Nicolas Le Cam Pete Johns PKEuS Philipp K PKEuS Philipp Kloke PKEuS Reijo Tomperi Robert Reif Ryan Pavlik Sébastien Debrard seb777 Sébastien Debrard S�bastien Debrard Sébastien Debrard Debrard Sébastien Stefan Weil Tim Gerundt Vesa Pikki XhmikosR Zachary Blair Zachary Blair Zachary Blair zblair cppcheck-2.7/.selfcheck_unused_suppressions000066400000000000000000000006341417746362400213330ustar00rootroot00000000000000# we are not using all methods of their interfaces unusedFunction:externals/tinyxml2/tinyxml2.cpp unusedFunction:externals/simplecpp/simplecpp.cpp # TODO: fix these # false positive - # 10660 unusedFunction:gui/mainwindow.cpp unusedFunction:gui/resultstree.cpp unusedFunction:gui/codeeditor.cpp # usage is disabled unusedFunction:lib/symboldatabase.cpp # false positive - #10661 unusedFunction:oss-fuzz/main.cppcppcheck-2.7/.travis.yml000066400000000000000000000125321417746362400152740ustar00rootroot00000000000000language: cpp dist: xenial compiler: - gcc - clang env: global: - ORIGINAL_CXXFLAGS="-pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar -D_GLIBCXX_DEBUG -g" # unfortunately we need this to stay within 50min timelimit given by travis. - CXXFLAGS="${ORIGINAL_CXXFLAGS} -O2 -march=native -Wstrict-aliasing=2 -Werror=strict-aliasing" - CPPCHECK=${TRAVIS_BUILD_DIR}/cppcheck matrix: - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" MAKEFLAGS="HAVE_RULES=yes" MATCHCOMPILER=yes VERIFY=1 before_install: # install needed deps - travis_retry sudo apt-get update -qq - travis_retry sudo apt-get install -qq python3-pip libxml2-utils libpcre3 gdb unzip wx-common xmlstarlet python3-dev liblua5.3-dev libcurl3 libcairo2-dev libsigc++-2.0-dev tidy libopencv-dev libz3-dev # Python 2 modules - travis_retry python2 -m pip install --user pytest==4.6.4 - travis_retry python2 -m pip install --user unittest2 - travis_retry python2 -m pip install --user pexpect # imported by tools/ci.py - travis_retry python2 -m pip install --user pygments # Python 3 modules - travis_retry python3 -m pip install --user setuptools --upgrade - travis_retry python3 -m pip install --user pytest - travis_retry python3 -m pip install --user unittest2 - travis_retry python3 -m pip install --user pexpect # imported by tools/ci.py - travis_retry python3 -m pip install --user requests # imported by tools/pr.py - travis_retry python3 -m pip install --user pygments - travis_retry python3 -m pip install --user natsort - cp externals/z3_version_old.h externals/z3_version.h # because travis z3 version is old matrix: # do notify immediately about it when a job of a build fails. fast_finish: true # defined extra jobs that run besides what is configured in the build matrix include: # check a lot of stuff that only needs to be checked in a single configuration - name: "misc" compiler: clang script: - make -j$(nproc) -s # check if DESTDIR works TODO: actually execute this - mkdir install_test - echo $CXXFLAGS - make -s DESTDIR=install_test FILESDIR=/usr/share/cppcheck install # rm everything - git clean -dfx # check what happens if we want to install it to some other dir, - echo $CXXFLAGS - make -s MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck -j$(nproc) - sudo make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck install # check if it actually works: - /usr/bin/cppcheck -j$(nproc) ./cli # check addons/misc.py - cd addons/test - ${CPPCHECK} --dump misc-test.cpp - python3 ../misc.py -verify misc-test.cpp.dump - cd ../../ # check addons/cert.py - cd addons/test - ${CPPCHECK} --dump cert-test.c - python3 ../cert.py -verify cert-test.c.dump - ${CPPCHECK} --dump cert-test.cpp - python3 ../cert.py -verify cert-test.cpp.dump - cd ../../ # check addons/misra.py - cd addons/test # We'll force C89 standard to enable an additional verification for # rules 5.4 and 5.5 which have standard-dependent options. - ${CPPCHECK} --dump -DDUMMY --suppress=uninitvar --suppress=uninitStructMember --std=c89 misra/misra-test.c - ${CPPCHECK} --dump -DDUMMY --suppress=uninitvar --suppress=uninitStructMember --std=c89 misra/misra-test.h - python3 ../misra.py -verify misra/misra-test.c.dump - ${CPPCHECK} --dump misra/misra-test.cpp - python3 ../misra.py -verify misra/misra-test.cpp.dump - python ../misra.py --rule-texts=misra/misra2012_rules_dummy_ascii.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_ascii.txt -verify misra/misra-test.cpp.dump - python ../misra.py --rule-texts=misra/misra2012_rules_dummy_utf8.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_utf8.txt -verify misra/misra-test.cpp.dump - python ../misra.py --rule-texts=misra/misra2012_rules_dummy_windows1250.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_windows1250.txt -verify misra/misra-test.cpp.dump - cd ../../ # check addons/naming.py - cd addons/test - ${CPPCHECK} --dump naming_test.c - python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump - ${CPPCHECK} --dump naming_test.cpp - python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump - cd ../.. # check addons/namingng.py - cd addons/test - ${CPPCHECK} --dump namingng_test.c - python3 ../namingng.py --configfile ../naming.json --verify namingng_test.c.dump - cd ../.. script: # fail the entire job as soon as one of the subcommands exits non-zero to save time and resources - set -e # check with TEST_MATHLIB_VALUE enabled - touch lib/mathlib.cpp test/testmathlib.cpp - echo $CXXFLAGS - make -s check -j$(nproc) CPPFLAGS=-DTEST_MATHLIB_VALUE - touch lib/mathlib.cpp test/testmathlib.cpp # compile cppcheck, default build - echo $CXXFLAGS - make -s check -j$(nproc) cppcheck-2.7/.travis_llvmcheck_suppressions000066400000000000000000000001501417746362400213520ustar00rootroot00000000000000unreadVariable shadowVar shadowFunction unusedStructMember nullPointer uninitvar noExplicitConstructor cppcheck-2.7/.travis_suppressions000066400000000000000000000010711417746362400173250ustar00rootroot00000000000000unusedPrivateFunction:test/testbufferoverrun.cpp unusedPrivateFunction:test/testcmdlineparser.cpp shadowFunction functionConst functionStatic bitwiseOnBoolean # temporary suppressions - fix the warnings! unusedPrivateFunction:test/test*.cpp useStlAlgorithm simplifyUsing:lib/valueptr.h symbolDatabaseWarning:gui/temp/moc_*.cpp simplifyUsing:gui/temp/moc_*.cpp symbolDatabaseWarning:tools/triage/moc_*.cpp # debug suppressions valueFlowBailout valueFlowBailoutIncompleteVar varid0 autoNoType bailoutUninitVar *:gui/test/* *:test/test.cxx *:test/cfg/* *:externals/*/* cppcheck-2.7/.uncrustify.cfg000066400000000000000000003622641417746362400161470ustar00rootroot00000000000000# Uncrustify-0.72.0_f # # General options # # The type of line endings. # # Default: auto newlines = auto # lf/crlf/cr/auto # The original size of tabs in the input. # # Default: 8 input_tab_size = 4 # unsigned number # The size of tabs in the output (only used if align_with_tabs=true). # # Default: 8 output_tab_size = 4 # unsigned number # The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^). # # Default: 92 string_escape_char = 92 # unsigned number # Alternate string escape char (usually only used for Pawn). # Only works right before the quote char. string_escape_char2 = 0 # unsigned number # Replace tab characters found in string literals with the escape sequence \t # instead. string_replace_tab_chars = false # true/false # Allow interpreting '>=' and '>>=' as part of a template in code like # 'void f(list>=val);'. If true, 'assert(x<0 && y>=3)' will be broken. # Improvements to template detection may make this option obsolete. tok_split_gte = false # true/false # Disable formatting of NL_CONT ('\\n') ended lines (e.g. multiline macros) disable_processing_nl_cont = false # true/false # Specify the marker used in comments to disable processing of part of the # file. # The comment should be used alone in one line. # # Default: *INDENT-OFF* disable_processing_cmt = " *INDENT-OFF*" # string # Specify the marker used in comments to (re)enable processing in a file. # The comment should be used alone in one line. # # Default: *INDENT-ON* enable_processing_cmt = " *INDENT-ON*" # string # Enable parsing of digraphs. enable_digraphs = false # true/false # Add or remove the UTF-8 BOM (recommend 'remove'). utf8_bom = ignore # ignore/add/remove/force # If the file contains bytes with values between 128 and 255, but is not # UTF-8, then output as UTF-8. utf8_byte = false # true/false # Force the output encoding to UTF-8. utf8_force = false # true/false # Add or remove space between 'do' and '{'. sp_do_brace_open = ignore # ignore/add/remove/force # Add or remove space between '}' and 'while'. sp_brace_close_while = ignore # ignore/add/remove/force # Add or remove space between 'while' and '('. sp_while_paren_open = add # ignore/add/remove/force # # Spacing options # # Add or remove space around non-assignment symbolic operators ('+', '/', '%', # '<<', and so forth). sp_arith = ignore # ignore/add/remove/force # Add or remove space around arithmetic operators '+' and '-'. # # Overrides sp_arith. sp_arith_additive = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=', '+=', etc. sp_assign = ignore # ignore/add/remove/force # Add or remove space around '=' in C++11 lambda capture specifications. # # Overrides sp_assign. sp_cpp_lambda_assign = ignore # ignore/add/remove/force # Add or remove space after the capture specification of a C++11 lambda when # an argument list is present, as in '[] (int x){ ... }'. sp_cpp_lambda_square_paren = ignore # ignore/add/remove/force # Add or remove space after the capture specification of a C++11 lambda with # no argument list is present, as in '[] { ... }'. sp_cpp_lambda_square_brace = ignore # ignore/add/remove/force # Add or remove space after the argument list of a C++11 lambda, as in # '[](int x) { ... }'. sp_cpp_lambda_paren_brace = ignore # ignore/add/remove/force # Add or remove space between a lambda body and its call operator of an # immediately invoked lambda, as in '[]( ... ){ ... } ( ... )'. sp_cpp_lambda_fparen = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=' in a prototype. # # If set to ignore, use sp_assign. sp_assign_default = ignore # ignore/add/remove/force # Add or remove space before assignment operator '=', '+=', etc. # # Overrides sp_assign. sp_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment operator '=', '+=', etc. # # Overrides sp_assign. sp_after_assign = ignore # ignore/add/remove/force # Add or remove space in 'NS_ENUM ('. sp_enum_paren = ignore # ignore/add/remove/force # Add or remove space around assignment '=' in enum. sp_enum_assign = ignore # ignore/add/remove/force # Add or remove space before assignment '=' in enum. # # Overrides sp_enum_assign. sp_enum_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment '=' in enum. # # Overrides sp_enum_assign. sp_enum_after_assign = ignore # ignore/add/remove/force # Add or remove space around assignment ':' in enum. sp_enum_colon = ignore # ignore/add/remove/force # Add or remove space around preprocessor '##' concatenation operator. # # Default: add sp_pp_concat = add # ignore/add/remove/force # Add or remove space after preprocessor '#' stringify operator. # Also affects the '#@' charizing operator. sp_pp_stringify = ignore # ignore/add/remove/force # Add or remove space before preprocessor '#' stringify operator # as in '#define x(y) L#y'. sp_before_pp_stringify = ignore # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||'. sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc. sp_compare = ignore # ignore/add/remove/force # Add or remove space inside '(' and ')'. sp_inside_paren = remove # ignore/add/remove/force # Add or remove space between nested parentheses, i.e. '((' vs. ') )'. sp_paren_paren = remove # ignore/add/remove/force # Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. sp_cparen_oparen = ignore # ignore/add/remove/force # Whether to balance spaces inside nested parentheses. sp_balance_nested_parens = false # true/false # Add or remove space between ')' and '{'. sp_paren_brace = force # ignore/add/remove/force # Add or remove space between nested braces, i.e. '{{' vs '{ {'. sp_brace_brace = ignore # ignore/add/remove/force # Add or remove space before pointer star '*'. sp_before_ptr_star = ignore # ignore/add/remove/force # Add or remove space before pointer star '*' that isn't followed by a # variable name. If set to ignore, sp_before_ptr_star is used instead. sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force # Add or remove space between pointer stars '*'. sp_between_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. # # Overrides sp_type_func. sp_after_ptr_star = ignore # ignore/add/remove/force # Add or remove space after pointer caret '^', if followed by a word. sp_after_ptr_block_caret = ignore # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a qualifier. sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by a function # prototype or function definition. # # Overrides sp_after_ptr_star and sp_type_func. sp_after_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by an open # parenthesis, as in 'void* (*)(). sp_ptr_star_paren = ignore # ignore/add/remove/force # Add or remove space before a pointer star '*', if followed by a function # prototype or function definition. sp_before_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&'. sp_before_byref = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&' that isn't followed by a # variable name. If set to ignore, sp_before_byref is used instead. sp_before_unnamed_byref = ignore # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. # # Overrides sp_type_func. sp_after_byref = ignore # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by a function # prototype or function definition. # # Overrides sp_after_byref and sp_type_func. sp_after_byref_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&', if followed by a function # prototype or function definition. sp_before_byref_func = ignore # ignore/add/remove/force # Add or remove space between type and word. In cases where total removal of # whitespace would be a syntax error, a value of 'remove' is treated the same # as 'force'. # # This also affects some other instances of space following a type that are # not covered by other options; for example, between the return type and # parenthesis of a function type template argument, between the type and # parenthesis of an array parameter, or between 'decltype(...)' and the # following word. # # Default: force sp_after_type = force # ignore/add/remove/force # Add or remove space between 'decltype(...)' and word. # # Overrides sp_after_type. sp_after_decltype = ignore # ignore/add/remove/force # (D) Add or remove space before the parenthesis in the D constructs # 'template Foo(' and 'class Foo('. sp_before_template_paren = ignore # ignore/add/remove/force # Add or remove space between 'template' and '<'. # If set to ignore, sp_before_angle is used. sp_template_angle = ignore # ignore/add/remove/force # Add or remove space before '<'. sp_before_angle = remove # ignore/add/remove/force # Add or remove space inside '<' and '>'. sp_inside_angle = remove # ignore/add/remove/force # Add or remove space inside '<>'. sp_inside_angle_empty = ignore # ignore/add/remove/force # Add or remove space between '>' and ':'. sp_angle_colon = ignore # ignore/add/remove/force # Add or remove space after '>'. sp_after_angle = add # ignore/add/remove/force # Add or remove space between '>' and '(' as found in 'new List(foo);'. sp_angle_paren = remove # ignore/add/remove/force # Add or remove space between '>' and '()' as found in 'new List();'. sp_angle_paren_empty = ignore # ignore/add/remove/force # Add or remove space between '>' and a word as in 'List m;' or # 'template static ...'. sp_angle_word = add # ignore/add/remove/force # Add or remove space between '>' and '>' in '>>' (template stuff). # # Default: add sp_angle_shift = ignore # ignore/add/remove/force # (C++11) Permit removal of the space between '>>' in 'foo >'. Note # that sp_angle_shift cannot remove the space without this option. sp_permit_cpp11_shift = true # true/false # Add or remove space before '(' of control statements ('if', 'for', 'switch', # 'while', etc.). sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside '(' and ')' of control statements. sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space after '(' of control statements. # # Overrides sp_inside_sparen. sp_inside_sparen_open = ignore # ignore/add/remove/force # Add or remove space before ')' of control statements. # # Overrides sp_inside_sparen. sp_inside_sparen_close = ignore # ignore/add/remove/force # Add or remove space after ')' of control statements. sp_after_sparen = force # ignore/add/remove/force # Add or remove space between ')' and '{' of of control statements. sp_sparen_brace = force # ignore/add/remove/force # (D) Add or remove space between 'invariant' and '('. sp_invariant_paren = ignore # ignore/add/remove/force # (D) Add or remove space after the ')' in 'invariant (C) c'. sp_after_invariant_paren = ignore # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while'. sp_special_semi = ignore # ignore/add/remove/force # Add or remove space before ';'. # # Default: remove sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements. sp_before_semi_for = remove # ignore/add/remove/force # Add or remove space before a semicolon of an empty part of a for statement. sp_before_semi_for_empty = ignore # ignore/add/remove/force # Add or remove space after ';', except when followed by a comment. # # Default: add sp_after_semi = add # ignore/add/remove/force # Add or remove space after ';' in non-empty 'for' statements. # # Default: force sp_after_semi_for = force # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for # statement, as in 'for ( ; ; )'. sp_after_semi_for_empty = remove # ignore/add/remove/force # Add or remove space before '[' (except '[]'). sp_before_square = ignore # ignore/add/remove/force # Add or remove space before '[' for a variable definition. # # Default: remove sp_before_vardef_square = remove # ignore/add/remove/force # Add or remove space before '[' for asm block. sp_before_square_asm_block = ignore # ignore/add/remove/force # Add or remove space before '[]'. sp_before_squares = remove # ignore/add/remove/force # Add or remove space before C++17 structured bindings. sp_cpp_before_struct_binding = ignore # ignore/add/remove/force # Add or remove space inside a non-empty '[' and ']'. sp_inside_square = remove # ignore/add/remove/force # Add or remove space inside '[]'. sp_inside_square_empty = ignore # ignore/add/remove/force # (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and # ']'. If set to ignore, sp_inside_square is used. sp_inside_square_oc_array = ignore # ignore/add/remove/force # Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. sp_after_comma = ignore # ignore/add/remove/force # Add or remove space before ','. # # Default: remove sp_before_comma = remove # ignore/add/remove/force # (C#) Add or remove space between ',' and ']' in multidimensional array type # like 'int[,,]'. sp_after_mdatype_commas = ignore # ignore/add/remove/force # (C#) Add or remove space between '[' and ',' in multidimensional array type # like 'int[,,]'. sp_before_mdatype_commas = ignore # ignore/add/remove/force # (C#) Add or remove space between ',' in multidimensional array type # like 'int[,,]'. sp_between_mdatype_commas = ignore # ignore/add/remove/force # Add or remove space between an open parenthesis and comma, # i.e. '(,' vs. '( ,'. # # Default: force sp_paren_comma = force # ignore/add/remove/force # Add or remove space before the variadic '...' when preceded by a # non-punctuator. sp_before_ellipsis = ignore # ignore/add/remove/force # Add or remove space between a type and '...'. sp_type_ellipsis = ignore # ignore/add/remove/force # (D) Add or remove space between a type and '?'. sp_type_question = ignore # ignore/add/remove/force # Add or remove space between ')' and '...'. sp_paren_ellipsis = ignore # ignore/add/remove/force # Add or remove space between ')' and a qualifier such as 'const'. sp_paren_qualifier = ignore # ignore/add/remove/force # Add or remove space between ')' and 'noexcept'. sp_paren_noexcept = ignore # ignore/add/remove/force # Add or remove space after class ':'. sp_after_class_colon = force # ignore/add/remove/force # Add or remove space before class ':'. sp_before_class_colon = force # ignore/add/remove/force # Add or remove space after class constructor ':'. sp_after_constr_colon = ignore # ignore/add/remove/force # Add or remove space before class constructor ':'. sp_before_constr_colon = ignore # ignore/add/remove/force # Add or remove space before case ':'. # # Default: remove sp_before_case_colon = remove # ignore/add/remove/force # Add or remove space between 'operator' and operator sign. sp_after_operator = ignore # ignore/add/remove/force # Add or remove space between the operator symbol and the open parenthesis, as # in 'operator ++('. sp_after_operator_sym = ignore # ignore/add/remove/force # Overrides sp_after_operator_sym when the operator has no arguments, as in # 'operator *()'. sp_after_operator_sym_empty = ignore # ignore/add/remove/force # Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or # '(int)a' vs. '(int) a'. sp_after_cast = ignore # ignore/add/remove/force # Add or remove spaces inside cast parentheses. sp_inside_paren_cast = ignore # ignore/add/remove/force # Add or remove space between the type and open parenthesis in a C++ cast, # i.e. 'int(exp)' vs. 'int (exp)'. sp_cpp_cast_paren = ignore # ignore/add/remove/force # Add or remove space between 'sizeof' and '('. sp_sizeof_paren = ignore # ignore/add/remove/force # Add or remove space between 'sizeof' and '...'. sp_sizeof_ellipsis = ignore # ignore/add/remove/force # Add or remove space between 'sizeof...' and '('. sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force # Add or remove space between 'decltype' and '('. sp_decltype_paren = ignore # ignore/add/remove/force # (Pawn) Add or remove space after the tag keyword. sp_after_tag = ignore # ignore/add/remove/force # Add or remove space inside enum '{' and '}'. sp_inside_braces_enum = ignore # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}'. sp_inside_braces_struct = ignore # ignore/add/remove/force # (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}' sp_inside_braces_oc_dict = ignore # ignore/add/remove/force # Add or remove space after open brace in an unnamed temporary # direct-list-initialization. sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force # Add or remove space before close brace in an unnamed temporary # direct-list-initialization. sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force # Add or remove space inside an unnamed temporary direct-list-initialization. sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space inside '{' and '}'. sp_inside_braces = ignore # ignore/add/remove/force # Add or remove space inside '{}'. sp_inside_braces_empty = remove # ignore/add/remove/force # Add or remove space around trailing return operator '->'. sp_trailing_return = ignore # ignore/add/remove/force # Add or remove space between return type and function name. A minimum of 1 # is forced except for pointer return types. sp_type_func = ignore # ignore/add/remove/force # Add or remove space between type and open brace of an unnamed temporary # direct-list-initialization. sp_type_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration. sp_func_proto_paren = ignore # ignore/add/remove/force # Add or remove space between function name and '()' on function declaration # without parameters. sp_func_proto_paren_empty = ignore # ignore/add/remove/force # Add or remove space between function name and '(' with a typedef specifier. sp_func_type_paren = ignore # ignore/add/remove/force # Add or remove space between alias name and '(' of a non-pointer function type typedef. sp_func_def_paren = ignore # ignore/add/remove/force # Add or remove space between function name and '()' on function definition # without parameters. sp_func_def_paren_empty = ignore # ignore/add/remove/force # Add or remove space inside empty function '()'. # Overrides sp_after_angle unless use_sp_after_angle_always is set to true. sp_inside_fparens = ignore # ignore/add/remove/force # Add or remove space inside function '(' and ')'. sp_inside_fparen = ignore # ignore/add/remove/force # Add or remove space inside the first parentheses in a function type, as in # 'void (*x)(...)'. sp_inside_tparen = ignore # ignore/add/remove/force # Add or remove space between the ')' and '(' in a function type, as in # 'void (*x)(...)'. sp_after_tparen_close = ignore # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of function. sp_fparen_brace = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of a function call in object # initialization. # # Overrides sp_fparen_brace. sp_fparen_brace_initializer = ignore # ignore/add/remove/force # (Java) Add or remove space between ')' and '{{' of double brace initializer. sp_fparen_dbrace = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function calls. sp_func_call_paren = ignore # ignore/add/remove/force # Add or remove space between function name and '()' on function calls without # parameters. If set to ignore (the default), sp_func_call_paren is used. sp_func_call_paren_empty = ignore # ignore/add/remove/force # Add or remove space between the user function name and '(' on function # calls. You need to set a keyword to be a user function in the config file, # like: # set func_call_user tr _ i18n sp_func_call_user_paren = ignore # ignore/add/remove/force # Add or remove space inside user function '(' and ')'. sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force # Add or remove space between nested parentheses with user functions, # i.e. '((' vs. '( ('. sp_func_call_user_paren_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open # parenthesis. sp_func_class_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor without parameters or destructor # and '()'. sp_func_class_paren_empty = ignore # ignore/add/remove/force # Add or remove space between 'return' and '('. sp_return_paren = ignore # ignore/add/remove/force # Add or remove space between 'return' and '{'. sp_return_brace = ignore # ignore/add/remove/force # Add or remove space between '__attribute__' and '('. sp_attribute_paren = ignore # ignore/add/remove/force # Add or remove space between 'defined' and '(' in '#if defined (FOO)'. sp_defined_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and '(' in 'throw (something)'. sp_throw_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and anything other than '(' as in # '@throw [...];'. sp_after_throw = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '(' in 'catch (something) { }'. # If set to ignore, sp_before_sparen is used. sp_catch_paren = ignore # ignore/add/remove/force # (OC) Add or remove space between '@catch' and '(' # in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. sp_oc_catch_paren = ignore # ignore/add/remove/force # (OC) Add or remove space before Objective-C protocol list # as in '@protocol Protocol' or '@interface MyClass : NSObject'. sp_before_oc_proto_list = ignore # ignore/add/remove/force # (OC) Add or remove space between class name and '(' # in '@interface className(categoryName):BaseClass' sp_oc_classname_paren = ignore # ignore/add/remove/force # (D) Add or remove space between 'version' and '(' # in 'version (something) { }'. If set to ignore, sp_before_sparen is used. sp_version_paren = ignore # ignore/add/remove/force # (D) Add or remove space between 'scope' and '(' # in 'scope (something) { }'. If set to ignore, sp_before_sparen is used. sp_scope_paren = ignore # ignore/add/remove/force # Add or remove space between 'super' and '(' in 'super (something)'. # # Default: remove sp_super_paren = remove # ignore/add/remove/force # Add or remove space between 'this' and '(' in 'this (something)'. # # Default: remove sp_this_paren = remove # ignore/add/remove/force # Add or remove space between a macro name and its definition. sp_macro = ignore # ignore/add/remove/force # Add or remove space between a macro function ')' and its definition. sp_macro_func = ignore # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line. sp_else_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line. sp_brace_else = force # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line. sp_brace_typedef = ignore # ignore/add/remove/force # Add or remove space before the '{' of a 'catch' statement, if the '{' and # 'catch' are on the same line, as in 'catch (decl) {'. sp_catch_brace = force # ignore/add/remove/force # (OC) Add or remove space before the '{' of a '@catch' statement, if the '{' # and '@catch' are on the same line, as in '@catch (decl) {'. # If set to ignore, sp_catch_brace is used. sp_oc_catch_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line. sp_brace_catch = force # ignore/add/remove/force # (OC) Add or remove space between '}' and '@catch' if on the same line. # If set to ignore, sp_brace_catch is used. sp_oc_brace_catch = ignore # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line. sp_finally_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line. sp_brace_finally = ignore # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line. sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line. sp_getset_brace = ignore # ignore/add/remove/force # Add or remove space between a variable and '{' for C++ uniform # initialization. sp_word_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space between a variable and '{' for a namespace. # # Default: add sp_word_brace_ns = add # ignore/add/remove/force # Add or remove space before the '::' operator. sp_before_dc = ignore # ignore/add/remove/force # Add or remove space after the '::' operator. sp_after_dc = ignore # ignore/add/remove/force # (D) Add or remove around the D named array initializer ':' operator. sp_d_array_colon = ignore # ignore/add/remove/force # Add or remove space after the '!' (not) unary operator. # # Default: remove sp_not = remove # ignore/add/remove/force # Add or remove space after the '~' (invert) unary operator. # # Default: remove sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) unary operator. This does not # affect the spacing after a '&' that is part of a type. # # Default: remove sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators. # # Default: remove sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) unary operator. This does # not affect the spacing after a '*' that is part of a type. # # Default: remove sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. # # Default: remove sp_sign = remove # ignore/add/remove/force # Add or remove space between '++' and '--' the word to which it is being # applied, as in '(--x)' or 'y++;'. # # Default: remove sp_incdec = remove # ignore/add/remove/force # Add or remove space before a backslash-newline at the end of a line. # # Default: add sp_before_nl_cont = add # ignore/add/remove/force # (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;' # or '+(int) bar;'. sp_after_oc_scope = ignore # ignore/add/remove/force # (OC) Add or remove space after the colon in message specs, # i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'. sp_after_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space before the colon in message specs, # i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'. sp_before_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space after the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};'. sp_after_oc_dict_colon = ignore # ignore/add/remove/force # (OC) Add or remove space before the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};'. sp_before_oc_dict_colon = ignore # ignore/add/remove/force # (OC) Add or remove space after the colon in message specs, # i.e. '[object setValue:1];' vs. '[object setValue: 1];'. sp_after_send_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space before the colon in message specs, # i.e. '[object setValue:1];' vs. '[object setValue :1];'. sp_before_send_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space after the (type) in message specs, # i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'. sp_after_oc_type = ignore # ignore/add/remove/force # (OC) Add or remove space after the first (type) in message specs, # i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'. sp_after_oc_return_type = ignore # ignore/add/remove/force # (OC) Add or remove space between '@selector' and '(', # i.e. '@selector(msgName)' vs. '@selector (msgName)'. # Also applies to '@protocol()' constructs. sp_after_oc_at_sel = ignore # ignore/add/remove/force # (OC) Add or remove space between '@selector(x)' and the following word, # i.e. '@selector(foo) a:' vs. '@selector(foo)a:'. sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force # (OC) Add or remove space inside '@selector' parentheses, # i.e. '@selector(foo)' vs. '@selector( foo )'. # Also applies to '@protocol()' constructs. sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force # (OC) Add or remove space before a block pointer caret, # i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'. sp_before_oc_block_caret = ignore # ignore/add/remove/force # (OC) Add or remove space after a block pointer caret, # i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'. sp_after_oc_block_caret = ignore # ignore/add/remove/force # (OC) Add or remove space between the receiver and selector in a message, # as in '[receiver selector ...]'. sp_after_oc_msg_receiver = ignore # ignore/add/remove/force # (OC) Add or remove space after '@property'. sp_after_oc_property = ignore # ignore/add/remove/force # (OC) Add or remove space between '@synchronized' and the open parenthesis, # i.e. '@synchronized(foo)' vs. '@synchronized (foo)'. sp_after_oc_synchronized = ignore # ignore/add/remove/force # Add or remove space around the ':' in 'b ? t : f'. sp_cond_colon = ignore # ignore/add/remove/force # Add or remove space before the ':' in 'b ? t : f'. # # Overrides sp_cond_colon. sp_cond_colon_before = ignore # ignore/add/remove/force # Add or remove space after the ':' in 'b ? t : f'. # # Overrides sp_cond_colon. sp_cond_colon_after = ignore # ignore/add/remove/force # Add or remove space around the '?' in 'b ? t : f'. sp_cond_question = ignore # ignore/add/remove/force # Add or remove space before the '?' in 'b ? t : f'. # # Overrides sp_cond_question. sp_cond_question_before = ignore # ignore/add/remove/force # Add or remove space after the '?' in 'b ? t : f'. # # Overrides sp_cond_question. sp_cond_question_after = ignore # ignore/add/remove/force # In the abbreviated ternary form '(a ?: b)', add or remove space between '?' # and ':'. # # Overrides all other sp_cond_* options. sp_cond_ternary_short = ignore # ignore/add/remove/force # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make # sense here. sp_case_label = ignore # ignore/add/remove/force # (D) Add or remove space around the D '..' operator. sp_range = ignore # ignore/add/remove/force # Add or remove space after ':' in a Java/C++11 range-based 'for', # as in 'for (Type var : expr)'. sp_after_for_colon = ignore # ignore/add/remove/force # Add or remove space before ':' in a Java/C++11 range-based 'for', # as in 'for (Type var : expr)'. sp_before_for_colon = ignore # ignore/add/remove/force # (D) Add or remove space between 'extern' and '(' as in 'extern (C)'. sp_extern_paren = ignore # ignore/add/remove/force # Add or remove space after the opening of a C++ comment, # i.e. '// A' vs. '//A'. sp_cmt_cpp_start = ignore # ignore/add/remove/force # If true, space is added with sp_cmt_cpp_start will be added after doxygen # sequences like '///', '///<', '//!' and '//!<'. sp_cmt_cpp_doxygen = false # true/false # If true, space is added with sp_cmt_cpp_start will be added after Qt # translator or meta-data comments like '//:', '//=', and '//~'. sp_cmt_cpp_qttr = false # true/false # Add or remove space between #else or #endif and a trailing comment. sp_endif_cmt = ignore # ignore/add/remove/force # Add or remove space after 'new', 'delete' and 'delete[]'. sp_after_new = ignore # ignore/add/remove/force # Add or remove space between 'new' and '(' in 'new()'. sp_between_new_paren = ignore # ignore/add/remove/force # Add or remove space between ')' and type in 'new(foo) BAR'. sp_after_newop_paren = ignore # ignore/add/remove/force # Add or remove space inside parenthesis of the new operator # as in 'new(foo) BAR'. sp_inside_newop_paren = ignore # ignore/add/remove/force # Add or remove space after the open parenthesis of the new operator, # as in 'new(foo) BAR'. # # Overrides sp_inside_newop_paren. sp_inside_newop_paren_open = ignore # ignore/add/remove/force # Add or remove space before the close parenthesis of the new operator, # as in 'new(foo) BAR'. # # Overrides sp_inside_newop_paren. sp_inside_newop_paren_close = ignore # ignore/add/remove/force # Add or remove space before a trailing or embedded comment. sp_before_tr_emb_cmt = ignore # ignore/add/remove/force # Number of spaces before a trailing or embedded comment. sp_num_before_tr_emb_cmt = 0 # unsigned number # (Java) Add or remove space between an annotation and the open parenthesis. sp_annotation_paren = ignore # ignore/add/remove/force # If true, vbrace tokens are dropped to the previous token and skipped. sp_skip_vbrace_tokens = false # true/false # Add or remove space after 'noexcept'. sp_after_noexcept = ignore # ignore/add/remove/force # Add or remove space after '_'. sp_vala_after_translation = ignore # ignore/add/remove/force # If true, a is inserted after #define. force_tab_after_define = false # true/false # # Indenting options # # The number of columns to indent per level. Usually 2, 3, 4, or 8. # # Default: 8 indent_columns = 4 # unsigned number # The continuation indent. If non-zero, this overrides the indent of '(', '[' # and '=' continuation indents. Negative values are OK; negative value is # absolute and not increased for each '(' or '[' level. # # For FreeBSD, this is set to 4. indent_continue = 0 # number # The continuation indent, only for class header line(s). If non-zero, this # overrides the indent of 'class' continuation indents. indent_continue_class_head = 0 # unsigned number # Whether to indent empty lines (i.e. lines which contain only spaces before # the newline character). indent_single_newlines = false # true/false # The continuation indent for func_*_param if they are true. If non-zero, this # overrides the indent. indent_param = 0 # unsigned number # How to use tabs when indenting code. # # 0: Spaces only # 1: Indent with tabs to brace level, align with spaces (default) # 2: Indent and align with tabs, using spaces when not on a tabstop # # Default: 1 indent_with_tabs = 0 # unsigned number # Whether to indent comments that are not at a brace level with tabs on a # tabstop. Requires indent_with_tabs=2. If false, will use spaces. indent_cmt_with_tabs = false # true/false # Whether to indent strings broken by '\' so that they line up. indent_align_string = false # true/false # The number of spaces to indent multi-line XML strings. # Requires indent_align_string=true. indent_xml_string = 0 # unsigned number # Spaces to indent '{' from level. indent_brace = 0 # unsigned number # Whether braces are indented to the body level. indent_braces = false # true/false # Whether to disable indenting function braces if indent_braces=true. indent_braces_no_func = false # true/false # Whether to disable indenting class braces if indent_braces=true. indent_braces_no_class = false # true/false # Whether to disable indenting struct braces if indent_braces=true. indent_braces_no_struct = false # true/false # Whether to indent based on the size of the brace parent, # i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. indent_brace_parent = false # true/false # Whether to indent based on the open parenthesis instead of the open brace # in '({\n'. indent_paren_open_brace = false # true/false # (C#) Whether to indent the brace of a C# delegate by another level. indent_cs_delegate_brace = false # true/false # (C#) Whether to indent a C# delegate (to handle delegates with no brace) by # another level. indent_cs_delegate_body = false # true/false # Whether to indent the body of a 'namespace'. indent_namespace = true # true/false # Whether to indent only the first namespace, and not any nested namespaces. # Requires indent_namespace=true. indent_namespace_single_indent = false # true/false # The number of spaces to indent a namespace block. # If set to zero, use the value indent_columns indent_namespace_level = 0 # unsigned number # If the body of the namespace is longer than this number, it won't be # indented. Requires indent_namespace=true. 0 means no limit. indent_namespace_limit = 0 # unsigned number # Whether the 'extern "C"' body is indented. indent_extern = false # true/false # Whether the 'class' body is indented. indent_class = true # true/false # Whether to indent the stuff after a leading base class colon. indent_class_colon = false # true/false # Whether to indent based on a class colon instead of the stuff after the # colon. Requires indent_class_colon=true. indent_class_on_colon = false # true/false # Whether to indent the stuff after a leading class initializer colon. indent_constr_colon = false # true/false # Virtual indent from the ':' for member initializers. # # Default: 2 indent_ctor_init_leading = 2 # unsigned number # Additional indent for constructor initializer list. # Negative values decrease indent down to the first column. indent_ctor_init = 0 # number # Whether to indent 'if' following 'else' as a new block under the 'else'. # If false, 'else\nif' is treated as 'else if' for indenting purposes. indent_else_if = false # true/false # Amount to indent variable declarations after a open brace. # # <0: Relative # >=0: Absolute indent_var_def_blk = 0 # number # Whether to indent continued variable declarations instead of aligning. indent_var_def_cont = false # true/false # Whether to indent continued shift expressions ('<<' and '>>') instead of # aligning. Set align_left_shift=false when enabling this. indent_shift = false # true/false # Whether to force indentation of function definitions to start in column 1. indent_func_def_force_col1 = false # true/false # Whether to indent continued function call parameters one indent level, # rather than aligning parameters under the open parenthesis. indent_func_call_param = false # true/false # Whether to indent continued function definition parameters one indent level, # rather than aligning parameters under the open parenthesis. indent_func_def_param = false # true/false # for function definitions, only if indent_func_def_param is false # Allows to align params when appropriate and indent them when not # behave as if it was true if paren position is more than this value # if paren position is more than the option value indent_func_def_param_paren_pos_threshold = 0 # unsigned number # Whether to indent continued function call prototype one indent level, # rather than aligning parameters under the open parenthesis. indent_func_proto_param = false # true/false # Whether to indent continued function call declaration one indent level, # rather than aligning parameters under the open parenthesis. indent_func_class_param = false # true/false # Whether to indent continued class variable constructors one indent level, # rather than aligning parameters under the open parenthesis. indent_func_ctor_var_param = false # true/false # Whether to indent continued template parameter list one indent level, # rather than aligning parameters under the open parenthesis. indent_template_param = false # true/false # Double the indent for indent_func_xxx_param options. # Use both values of the options indent_columns and indent_param. indent_func_param_double = false # true/false # Indentation column for standalone 'const' qualifier on a function # prototype. indent_func_const = 0 # unsigned number # Indentation column for standalone 'throw' qualifier on a function # prototype. indent_func_throw = 0 # unsigned number # How to indent within a macro followed by a brace on the same line # This allows reducing the indent in macros that have (for example) # `do { ... } while (0)` blocks bracketing them. # # true: add an indent for the brace on the same line as the macro # false: do not add an indent for the brace on the same line as the macro # # Default: true indent_macro_brace = true # true/false # The number of spaces to indent a continued '->' or '.'. # Usually set to 0, 1, or indent_columns. indent_member = 0 # unsigned number # Whether lines broken at '.' or '->' should be indented by a single indent. # The indent_member option will not be effective if this is set to true. indent_member_single = false # true/false # Spaces to indent single line ('//') comments on lines before code. indent_sing_line_comments = 0 # unsigned number # When opening a paren for a control statement (if, for, while, etc), increase # the indent level by this value. Negative values decrease the indent level. indent_sparen_extra = 0 # number # Whether to indent trailing single line ('//') comments relative to the code # instead of trying to keep the same absolute column. indent_relative_single_line_comments = false # true/false # Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. indent_switch_case = 0 # unsigned number # indent 'break' with 'case' from 'switch'. indent_switch_break_with_case = false # true/false # Whether to indent preprocessor statements inside of switch statements. # # Default: true indent_switch_pp = true # true/false # Spaces to shift the 'case' line, without affecting any other lines. # Usually 0. indent_case_shift = 0 # unsigned number # Spaces to indent '{' from 'case'. By default, the brace will appear under # the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. indent_case_brace = 0 # number # Whether to indent comments found in first column. indent_col1_comment = false # true/false # Whether to indent multi string literal in first column. indent_col1_multi_string_literal = false # true/false # How to indent goto labels. # # >0: Absolute column where 1 is the leftmost column # <=0: Subtract from brace indent # # Default: 1 indent_label = 1 # number # How to indent access specifiers that are followed by a # colon. # # >0: Absolute column where 1 is the leftmost column # <=0: Subtract from brace indent # # Default: 1 indent_access_spec = -4 # number # Whether to indent the code after an access specifier by one level. # If true, this option forces 'indent_access_spec=0'. indent_access_spec_body = false # true/false # If an open parenthesis is followed by a newline, whether to indent the next # line so that it lines up after the open parenthesis (not recommended). indent_paren_nl = false # true/false # How to indent a close parenthesis after a newline. # # 0: Indent to body level (default) # 1: Align under the open parenthesis # 2: Indent to the brace level indent_paren_close = 0 # unsigned number # Whether to indent the open parenthesis of a function definition, # if the parenthesis is on its own line. indent_paren_after_func_def = false # true/false # Whether to indent the open parenthesis of a function declaration, # if the parenthesis is on its own line. indent_paren_after_func_decl = false # true/false # Whether to indent the open parenthesis of a function call, # if the parenthesis is on its own line. indent_paren_after_func_call = false # true/false # Whether to indent a comma when inside a parenthesis. # If true, aligns under the open parenthesis. indent_comma_paren = false # true/false # Whether to indent a Boolean operator when inside a parenthesis. # If true, aligns under the open parenthesis. indent_bool_paren = false # true/false # Whether to indent a semicolon when inside a for parenthesis. # If true, aligns under the open for parenthesis. indent_semicolon_for_paren = false # true/false # Whether to align the first expression to following ones # if indent_bool_paren=true. indent_first_bool_expr = false # true/false # Whether to align the first expression to following ones # if indent_semicolon_for_paren=true. indent_first_for_expr = false # true/false # If an open square is followed by a newline, whether to indent the next line # so that it lines up after the open square (not recommended). indent_square_nl = false # true/false # (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies. indent_preserve_sql = false # true/false # Whether to align continued statements at the '='. If false or if the '=' is # followed by a newline, the next line is indent one tab. # # Default: true indent_align_assign = true # true/false # If true, the indentation of the chunks after a '=' sequence will be set at # LHS token indentation column before '='. indent_off_after_assign = false # true/false # Whether to align continued statements at the '('. If false or the '(' is # followed by a newline, the next line indent is one tab. # # Default: true indent_align_paren = true # true/false # (OC) Whether to indent Objective-C code inside message selectors. indent_oc_inside_msg_sel = false # true/false # (OC) Whether to indent Objective-C blocks at brace level instead of usual # rules. indent_oc_block = false # true/false # (OC) Indent for Objective-C blocks in a message relative to the parameter # name. # # =0: Use indent_oc_block rules # >0: Use specified number of spaces to indent indent_oc_block_msg = 0 # unsigned number # (OC) Minimum indent for subsequent parameters indent_oc_msg_colon = 0 # unsigned number # (OC) Whether to prioritize aligning with initial colon (and stripping spaces # from lines, if necessary). # # Default: true indent_oc_msg_prioritize_first_colon = true # true/false # (OC) Whether to indent blocks the way that Xcode does by default # (from the keyword if the parameter is on its own line; otherwise, from the # previous indentation level). Requires indent_oc_block_msg=true. indent_oc_block_msg_xcode_style = false # true/false # (OC) Whether to indent blocks from where the brace is, relative to a # message keyword. Requires indent_oc_block_msg=true. indent_oc_block_msg_from_keyword = false # true/false # (OC) Whether to indent blocks from where the brace is, relative to a message # colon. Requires indent_oc_block_msg=true. indent_oc_block_msg_from_colon = false # true/false # (OC) Whether to indent blocks from where the block caret is. # Requires indent_oc_block_msg=true. indent_oc_block_msg_from_caret = false # true/false # (OC) Whether to indent blocks from where the brace caret is. # Requires indent_oc_block_msg=true. indent_oc_block_msg_from_brace = false # true/false # When indenting after virtual brace open and newline add further spaces to # reach this minimum indent. indent_min_vbrace_open = 0 # unsigned number # Whether to add further spaces after regular indent to reach next tabstop # when indenting after virtual brace open and newline. indent_vbrace_open_on_tabstop = false # true/false # How to indent after a brace followed by another token (not a newline). # true: indent all contained lines to match the token # false: indent all contained lines to match the brace # # Default: true indent_token_after_brace = true # true/false # Whether to indent the body of a C++11 lambda. indent_cpp_lambda_body = false # true/false # How to indent compound literals that are being returned. # true: add both the indent from return & the compound literal open brace (ie: # 2 indent levels) # false: only indent 1 level, don't add the indent for the open brace, only add # the indent for the return. # # Default: true indent_compound_literal_return = true # true/false # (C#) Whether to indent a 'using' block if no braces are used. # # Default: true indent_using_block = true # true/false # How to indent the continuation of ternary operator. # # 0: Off (default) # 1: When the `if_false` is a continuation, indent it under `if_false` # 2: When the `:` is a continuation, indent it under `?` indent_ternary_operator = 0 # unsigned number # Whether to indent the statments inside ternary operator. indent_inside_ternary_operator = false # true/false # If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. indent_off_after_return = false # true/false # If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. indent_off_after_return_new = false # true/false # If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token. indent_single_after_return = false # true/false # Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they # have their own indentation). indent_ignore_asm_block = false # true/false # Don't indent the close parenthesis of a function definition, # if the parenthesis is on its own line. donot_indent_func_def_close_paren = false # true/false # # Newline adding and removing options # # Whether to collapse empty blocks between '{' and '}'. # If true, overrides nl_inside_empty_func nl_collapse_empty_body = true # true/false # Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'. nl_assign_leave_one_liners = false # true/false # Don't split one-line braced statements inside a 'class xx { }' body. nl_class_leave_one_liners = false # true/false # Don't split one-line enums, as in 'enum foo { BAR = 15 };' nl_enum_leave_one_liners = false # true/false # Don't split one-line get or set functions. nl_getset_leave_one_liners = false # true/false # (C#) Don't split one-line property get or set functions. nl_cs_property_leave_one_liners = false # true/false # Don't split one-line function definitions, as in 'int foo() { return 0; }'. # might modify nl_func_type_name nl_func_leave_one_liners = false # true/false # Don't split one-line C++11 lambdas, as in '[]() { return 0; }'. nl_cpp_lambda_leave_one_liners = false # true/false # Don't split one-line if/else statements, as in 'if(...) b++;'. nl_if_leave_one_liners = false # true/false # Don't split one-line while statements, as in 'while(...) b++;'. nl_while_leave_one_liners = false # true/false # Don't split one-line for statements, as in 'for(...) b++;'. nl_for_leave_one_liners = false # true/false # (OC) Don't split one-line Objective-C messages. nl_oc_msg_leave_one_liner = false # true/false # (OC) Add or remove newline between method declaration and '{'. nl_oc_mdef_brace = ignore # ignore/add/remove/force # (OC) Add or remove newline between Objective-C block signature and '{'. nl_oc_block_brace = ignore # ignore/add/remove/force # (OC) Add or remove blank line before '@interface' statement. nl_oc_before_interface = ignore # ignore/add/remove/force # (OC) Add or remove blank line before '@implementation' statement. nl_oc_before_implementation = ignore # ignore/add/remove/force # (OC) Add or remove blank line before '@end' statement. nl_oc_before_end = ignore # ignore/add/remove/force # (OC) Add or remove newline between '@interface' and '{'. nl_oc_interface_brace = ignore # ignore/add/remove/force # (OC) Add or remove newline between '@implementation' and '{'. nl_oc_implementation_brace = ignore # ignore/add/remove/force # Add or remove newlines at the start of the file. nl_start_of_file = ignore # ignore/add/remove/force # The minimum number of newlines at the start of the file (only used if # nl_start_of_file is 'add' or 'force'). nl_start_of_file_min = 0 # unsigned number # Add or remove newline at the end of the file. nl_end_of_file = ignore # ignore/add/remove/force # The minimum number of newlines at the end of the file (only used if # nl_end_of_file is 'add' or 'force'). nl_end_of_file_min = 0 # unsigned number # Add or remove newline between '=' and '{'. nl_assign_brace = ignore # ignore/add/remove/force # (D) Add or remove newline between '=' and '['. nl_assign_square = ignore # ignore/add/remove/force # Add or remove newline between '[]' and '{'. nl_tsquare_brace = ignore # ignore/add/remove/force # (D) Add or remove newline after '= ['. Will also affect the newline before # the ']'. nl_after_square_assign = ignore # ignore/add/remove/force # Add or remove newline between a function call's ')' and '{', as in # 'list_for_each(item, &list) { }'. nl_fcall_brace = ignore # ignore/add/remove/force # Add or remove newline between 'enum' and '{'. nl_enum_brace = ignore # ignore/add/remove/force # Add or remove newline between 'enum' and 'class'. nl_enum_class = ignore # ignore/add/remove/force # Add or remove newline between 'enum class' and the identifier. nl_enum_class_identifier = ignore # ignore/add/remove/force # Add or remove newline between 'enum class' type and ':'. nl_enum_identifier_colon = ignore # ignore/add/remove/force # Add or remove newline between 'enum class identifier :' and type. nl_enum_colon_type = ignore # ignore/add/remove/force # Add or remove newline between 'struct and '{'. nl_struct_brace = ignore # ignore/add/remove/force # Add or remove newline between 'union' and '{'. nl_union_brace = ignore # ignore/add/remove/force # Add or remove newline between 'if' and '{'. nl_if_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'else'. nl_brace_else = ignore # ignore/add/remove/force # Add or remove newline between 'else if' and '{'. If set to ignore, # nl_if_brace is used instead. nl_elseif_brace = ignore # ignore/add/remove/force # Add or remove newline between 'else' and '{'. nl_else_brace = ignore # ignore/add/remove/force # Add or remove newline between 'else' and 'if'. nl_else_if = ignore # ignore/add/remove/force # Add or remove newline before '{' opening brace nl_before_opening_brace_func_class_def = ignore # ignore/add/remove/force # Add or remove newline before 'if'/'else if' closing parenthesis. nl_before_if_closing_paren = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'finally'. nl_brace_finally = ignore # ignore/add/remove/force # Add or remove newline between 'finally' and '{'. nl_finally_brace = ignore # ignore/add/remove/force # Add or remove newline between 'try' and '{'. nl_try_brace = ignore # ignore/add/remove/force # Add or remove newline between get/set and '{'. nl_getset_brace = ignore # ignore/add/remove/force # Add or remove newline between 'for' and '{'. nl_for_brace = ignore # ignore/add/remove/force # Add or remove newline before the '{' of a 'catch' statement, as in # 'catch (decl) {'. nl_catch_brace = ignore # ignore/add/remove/force # (OC) Add or remove newline before the '{' of a '@catch' statement, as in # '@catch (decl) {'. If set to ignore, nl_catch_brace is used. nl_oc_catch_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'catch'. nl_brace_catch = ignore # ignore/add/remove/force # (OC) Add or remove newline between '}' and '@catch'. If set to ignore, # nl_brace_catch is used. nl_oc_brace_catch = ignore # ignore/add/remove/force # Add or remove newline between '}' and ']'. nl_brace_square = ignore # ignore/add/remove/force # Add or remove newline between '}' and ')' in a function invocation. nl_brace_fparen = ignore # ignore/add/remove/force # Add or remove newline between 'while' and '{'. nl_while_brace = ignore # ignore/add/remove/force # (D) Add or remove newline between 'scope (x)' and '{'. nl_scope_brace = ignore # ignore/add/remove/force # (D) Add or remove newline between 'unittest' and '{'. nl_unittest_brace = ignore # ignore/add/remove/force # (D) Add or remove newline between 'version (x)' and '{'. nl_version_brace = ignore # ignore/add/remove/force # (C#) Add or remove newline between 'using' and '{'. nl_using_brace = ignore # ignore/add/remove/force # Add or remove newline between two open or close braces. Due to general # newline/brace handling, REMOVE may not work. nl_brace_brace = ignore # ignore/add/remove/force # Add or remove newline between 'do' and '{'. nl_do_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement. nl_brace_while = ignore # ignore/add/remove/force # Add or remove newline between 'switch' and '{'. nl_switch_brace = ignore # ignore/add/remove/force # Add or remove newline between 'synchronized' and '{'. nl_synchronized_brace = ignore # ignore/add/remove/force # Add a newline between ')' and '{' if the ')' is on a different line than the # if/for/etc. # # Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and # nl_catch_brace. nl_multi_line_cond = false # true/false # Add a newline after '(' if an if/for/while/switch condition spans multiple # lines nl_multi_line_sparen_open = ignore # ignore/add/remove/force # Add a newline before ')' if an if/for/while/switch condition spans multiple # lines. Overrides nl_before_if_closing_paren if both are specified. nl_multi_line_sparen_close = ignore # ignore/add/remove/force # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = false # true/false # Whether to add a newline before 'case', and a blank line before a 'case' # statement that follows a ';' or '}'. nl_before_case = false # true/false # Whether to add a newline after a 'case' statement. nl_after_case = false # true/false # Add or remove newline between a case ':' and '{'. # # Overrides nl_after_case. nl_case_colon_brace = ignore # ignore/add/remove/force # Add or remove newline between ')' and 'throw'. nl_before_throw = ignore # ignore/add/remove/force # Add or remove newline between 'namespace' and '{'. nl_namespace_brace = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template class. nl_template_class = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template class declaration. # # Overrides nl_template_class. nl_template_class_decl = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized class declaration. # # Overrides nl_template_class_decl. nl_template_class_decl_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template class definition. # # Overrides nl_template_class. nl_template_class_def = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized class definition. # # Overrides nl_template_class_def. nl_template_class_def_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template function. nl_template_func = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template function # declaration. # # Overrides nl_template_func. nl_template_func_decl = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized function # declaration. # # Overrides nl_template_func_decl. nl_template_func_decl_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template function # definition. # # Overrides nl_template_func. nl_template_func_def = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized function # definition. # # Overrides nl_template_func_def. nl_template_func_def_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template variable. nl_template_var = ignore # ignore/add/remove/force # Add or remove newline between 'template<...>' and 'using' of a templated # type alias. nl_template_using = ignore # ignore/add/remove/force # Add or remove newline between 'class' and '{'. nl_class_brace = ignore # ignore/add/remove/force # Add or remove newline before or after (depending on pos_class_comma, # may not be IGNORE) each',' in the base class list. nl_class_init_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member # initialization. Related to nl_constr_colon, pos_constr_colon and # pos_constr_comma. nl_constr_init_args = ignore # ignore/add/remove/force # Add or remove newline before first element, after comma, and after last # element, in 'enum'. nl_enum_own_lines = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a function # definition. # might be modified by nl_func_leave_one_liners nl_func_type_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name inside a class # definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name # is used instead. nl_func_type_name_class = ignore # ignore/add/remove/force # Add or remove newline between class specification and '::' # in 'void A::f() { }'. Only appears in separate member implementation (does # not appear with in-line implementation). nl_func_class_scope = ignore # ignore/add/remove/force # Add or remove newline between function scope and name, as in # 'void A :: f() { }'. nl_func_scope_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype. nl_func_proto_type_name = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the # declaration. nl_func_paren = ignore # ignore/add/remove/force # Overrides nl_func_paren for functions with no parameters. nl_func_paren_empty = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the # definition. nl_func_def_paren = ignore # ignore/add/remove/force # Overrides nl_func_def_paren for functions with no parameters. nl_func_def_paren_empty = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the # call. nl_func_call_paren = ignore # ignore/add/remove/force # Overrides nl_func_call_paren for functions with no parameters. nl_func_call_paren_empty = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function declaration. nl_func_decl_start = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function definition. nl_func_def_start = ignore # ignore/add/remove/force # Overrides nl_func_decl_start when there is only one parameter. nl_func_decl_start_single = ignore # ignore/add/remove/force # Overrides nl_func_def_start when there is only one parameter. nl_func_def_start_single = ignore # ignore/add/remove/force # Whether to add a newline after '(' in a function declaration if '(' and ')' # are in different lines. If false, nl_func_decl_start is used instead. nl_func_decl_start_multi_line = false # true/false # Whether to add a newline after '(' in a function definition if '(' and ')' # are in different lines. If false, nl_func_def_start is used instead. nl_func_def_start_multi_line = false # true/false # Add or remove newline after each ',' in a function declaration. nl_func_decl_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function definition. nl_func_def_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in a function call. nl_func_call_args = ignore # ignore/add/remove/force # Whether to add a newline after each ',' in a function declaration if '(' # and ')' are in different lines. If false, nl_func_decl_args is used instead. nl_func_decl_args_multi_line = false # true/false # Whether to add a newline after each ',' in a function definition if '(' # and ')' are in different lines. If false, nl_func_def_args is used instead. nl_func_def_args_multi_line = false # true/false # Add or remove newline before the ')' in a function declaration. nl_func_decl_end = ignore # ignore/add/remove/force # Add or remove newline before the ')' in a function definition. nl_func_def_end = ignore # ignore/add/remove/force # Overrides nl_func_decl_end when there is only one parameter. nl_func_decl_end_single = ignore # ignore/add/remove/force # Overrides nl_func_def_end when there is only one parameter. nl_func_def_end_single = ignore # ignore/add/remove/force # Whether to add a newline before ')' in a function declaration if '(' and ')' # are in different lines. If false, nl_func_decl_end is used instead. nl_func_decl_end_multi_line = false # true/false # Whether to add a newline before ')' in a function definition if '(' and ')' # are in different lines. If false, nl_func_def_end is used instead. nl_func_def_end_multi_line = false # true/false # Add or remove newline between '()' in a function declaration. nl_func_decl_empty = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function definition. nl_func_def_empty = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function call. nl_func_call_empty = ignore # ignore/add/remove/force # Whether to add a newline after '(' in a function call, # has preference over nl_func_call_start_multi_line. nl_func_call_start = ignore # ignore/add/remove/force # Whether to add a newline before ')' in a function call. nl_func_call_end = ignore # ignore/add/remove/force # Whether to add a newline after '(' in a function call if '(' and ')' are in # different lines. nl_func_call_start_multi_line = false # true/false # Whether to add a newline after each ',' in a function call if '(' and ')' # are in different lines. nl_func_call_args_multi_line = false # true/false # Whether to add a newline before ')' in a function call if '(' and ')' are in # different lines. nl_func_call_end_multi_line = false # true/false # Whether to respect nl_func_call_XXX option incase of closure args. nl_func_call_args_multi_line_ignore_closures = false # true/false # Whether to add a newline after '<' of a template parameter list. nl_template_start = false # true/false # Whether to add a newline after each ',' in a template parameter list. nl_template_args = false # true/false # Whether to add a newline before '>' of a template parameter list. nl_template_end = false # true/false # (OC) Whether to put each Objective-C message parameter on a separate line. # See nl_oc_msg_leave_one_liner. nl_oc_msg_args = false # true/false # Add or remove newline between function signature and '{'. nl_fdef_brace = ignore # ignore/add/remove/force # Add or remove newline between function signature and '{', # if signature ends with ')'. Overrides nl_fdef_brace. nl_fdef_brace_cond = ignore # ignore/add/remove/force # Add or remove newline between C++11 lambda signature and '{'. nl_cpp_ldef_brace = ignore # ignore/add/remove/force # Add or remove newline between 'return' and the return expression. nl_return_expr = ignore # ignore/add/remove/force # Whether to add a newline after semicolons, except in 'for' statements. nl_after_semicolon = false # true/false # (Java) Add or remove newline between the ')' and '{{' of the double brace # initializer. nl_paren_dbrace_open = ignore # ignore/add/remove/force # Whether to add a newline after the type in an unnamed temporary # direct-list-initialization. nl_type_brace_init_lst = ignore # ignore/add/remove/force # Whether to add a newline after the open brace in an unnamed temporary # direct-list-initialization. nl_type_brace_init_lst_open = ignore # ignore/add/remove/force # Whether to add a newline before the close brace in an unnamed temporary # direct-list-initialization. nl_type_brace_init_lst_close = ignore # ignore/add/remove/force # Whether to add a newline after '{'. This also adds a newline before the # matching '}'. nl_after_brace_open = false # true/false # Whether to add a newline between the open brace and a trailing single-line # comment. Requires nl_after_brace_open=true. nl_after_brace_open_cmt = false # true/false # Whether to add a newline after a virtual brace open with a non-empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open = false # true/false # Whether to add a newline after a virtual brace open with an empty body. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open_empty = false # true/false # Whether to add a newline after '}'. Does not apply if followed by a # necessary ';'. nl_after_brace_close = false # true/false # Whether to add a newline after a virtual brace close, # as in 'if (foo) a++; return;'. nl_after_vbrace_close = false # true/false # Add or remove newline between the close brace and identifier, # as in 'struct { int a; } b;'. Affects enumerations, unions and # structures. If set to ignore, uses nl_after_brace_close. nl_brace_struct_var = ignore # ignore/add/remove/force # Whether to alter newlines in '#define' macros. nl_define_macro = false # true/false # Whether to alter newlines between consecutive parenthesis closes. The number # of closing parentheses in a line will depend on respective open parenthesis # lines. nl_squeeze_paren_close = false # true/false # Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and # '#endif'. Does not affect top-level #ifdefs. nl_squeeze_ifdef = false # true/false # Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. nl_squeeze_ifdef_top_level = false # true/false # Add or remove blank line before 'if'. nl_before_if = ignore # ignore/add/remove/force # Add or remove blank line after 'if' statement. Add/Force work only if the # next token is not a closing brace. nl_after_if = ignore # ignore/add/remove/force # Add or remove blank line before 'for'. nl_before_for = ignore # ignore/add/remove/force # Add or remove blank line after 'for' statement. nl_after_for = ignore # ignore/add/remove/force # Add or remove blank line before 'while'. nl_before_while = ignore # ignore/add/remove/force # Add or remove blank line after 'while' statement. nl_after_while = ignore # ignore/add/remove/force # Add or remove blank line before 'switch'. nl_before_switch = ignore # ignore/add/remove/force # Add or remove blank line after 'switch' statement. nl_after_switch = ignore # ignore/add/remove/force # Add or remove blank line before 'synchronized'. nl_before_synchronized = ignore # ignore/add/remove/force # Add or remove blank line after 'synchronized' statement. nl_after_synchronized = ignore # ignore/add/remove/force # Add or remove blank line before 'do'. nl_before_do = ignore # ignore/add/remove/force # Add or remove blank line after 'do/while' statement. nl_after_do = ignore # ignore/add/remove/force # Whether to put a blank line before 'return' statements, unless after an open # brace. nl_before_return = false # true/false # Whether to put a blank line after 'return' statements, unless followed by a # close brace. nl_after_return = false # true/false # Whether to put a blank line before a member '.' or '->' operators. nl_before_member = ignore # ignore/add/remove/force # (Java) Whether to put a blank line after a member '.' or '->' operators. nl_after_member = ignore # ignore/add/remove/force # Whether to double-space commented-entries in 'struct'/'union'/'enum'. nl_ds_struct_enum_cmt = false # true/false # Whether to force a newline before '}' of a 'struct'/'union'/'enum'. # (Lower priority than eat_blanks_before_close_brace.) nl_ds_struct_enum_close_brace = false # true/false # Add or remove newline before or after (depending on pos_class_colon) a class # colon, as in 'class Foo : public Bar'. nl_class_colon = ignore # ignore/add/remove/force # Add or remove newline around a class constructor colon. The exact position # depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma. nl_constr_colon = ignore # ignore/add/remove/force # Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' # into a single line. If true, prevents other brace newline rules from turning # such code into four lines. nl_namespace_two_to_one_liner = false # true/false # Whether to remove a newline in simple unbraced if statements, turning them # into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'. nl_create_if_one_liner = false # true/false # Whether to remove a newline in simple unbraced for statements, turning them # into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'. nl_create_for_one_liner = false # true/false # Whether to remove a newline in simple unbraced while statements, turning # them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'. nl_create_while_one_liner = false # true/false # Whether to collapse a function definition whose body (not counting braces) # is only one line so that the entire definition (prototype, braces, body) is # a single line. nl_create_func_def_one_liner = false # true/false # Whether to collapse a function definition whose body (not counting braces) # is only one line so that the entire definition (prototype, braces, body) is # a single line. nl_create_list_one_liner = false # true/false # Whether to split one-line simple unbraced if statements into two lines by # adding a newline, as in 'if(b) i++;'. nl_split_if_one_liner = false # true/false # Whether to split one-line simple unbraced for statements into two lines by # adding a newline, as in 'for (...) stmt;'. nl_split_for_one_liner = false # true/false # Whether to split one-line simple unbraced while statements into two lines by # adding a newline, as in 'while (expr) stmt;'. nl_split_while_one_liner = false # true/false # Don't add a newline before a cpp-comment in a parameter list of a function # call. donot_add_nl_before_cpp_comment = false # true/false # # Blank line options # # The maximum number of consecutive newlines (3 = 2 blank lines). nl_max = 0 # unsigned number # The maximum number of consecutive newlines in a function. nl_max_blank_in_func = 0 # unsigned number # The number of newlines inside an empty function body. # This option is overridden by nl_collapse_empty_body=true nl_inside_empty_func = 0 # unsigned number # The number of newlines before a function prototype. nl_before_func_body_proto = 0 # unsigned number # The number of newlines before a multi-line function definition. nl_before_func_body_def = 0 # unsigned number # The number of newlines before a class constructor/destructor prototype. nl_before_func_class_proto = 0 # unsigned number # The number of newlines before a class constructor/destructor definition. nl_before_func_class_def = 0 # unsigned number # The number of newlines after a function prototype. nl_after_func_proto = 0 # unsigned number # The number of newlines after a function prototype, if not followed by # another function prototype. nl_after_func_proto_group = 0 # unsigned number # The number of newlines after a class constructor/destructor prototype. nl_after_func_class_proto = 0 # unsigned number # The number of newlines after a class constructor/destructor prototype, # if not followed by another constructor/destructor prototype. nl_after_func_class_proto_group = 0 # unsigned number # Whether one-line method definitions inside a class body should be treated # as if they were prototypes for the purposes of adding newlines. # # Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def # and nl_before_func_class_def for one-liners. nl_class_leave_one_liner_groups = false # true/false # The number of newlines after '}' of a multi-line function body. nl_after_func_body = 0 # unsigned number # The number of newlines after '}' of a multi-line function body in a class # declaration. Also affects class constructors/destructors. # # Overrides nl_after_func_body. nl_after_func_body_class = 0 # unsigned number # The number of newlines after '}' of a single line function body. Also # affects class constructors/destructors. # # Overrides nl_after_func_body and nl_after_func_body_class. nl_after_func_body_one_liner = 0 # unsigned number # The number of blank lines after a block of variable definitions at the top # of a function body. # # 0: No change (default). nl_func_var_def_blk = 0 # unsigned number # The number of newlines before a block of typedefs. If nl_after_access_spec # is non-zero, that option takes precedence. # # 0: No change (default). nl_typedef_blk_start = 0 # unsigned number # The number of newlines after a block of typedefs. # # 0: No change (default). nl_typedef_blk_end = 0 # unsigned number # The maximum number of consecutive newlines within a block of typedefs. # # 0: No change (default). nl_typedef_blk_in = 0 # unsigned number # The number of newlines before a block of variable definitions not at the top # of a function body. If nl_after_access_spec is non-zero, that option takes # precedence. # # 0: No change (default). nl_var_def_blk_start = 0 # unsigned number # The number of newlines after a block of variable definitions not at the top # of a function body. # # 0: No change (default). nl_var_def_blk_end = 0 # unsigned number # The maximum number of consecutive newlines within a block of variable # definitions. # # 0: No change (default). nl_var_def_blk_in = 0 # unsigned number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. nl_before_block_comment = 0 # unsigned number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. nl_before_c_comment = 0 # unsigned number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. nl_before_cpp_comment = 0 # unsigned number # Whether to force a newline after a multi-line comment. nl_after_multiline_comment = false # true/false # Whether to force a newline after a label's colon. nl_after_label_colon = false # true/false # The number of newlines after '}' or ';' of a struct/enum/union definition. nl_after_struct = 0 # unsigned number # The number of newlines before a class definition. nl_before_class = 0 # unsigned number # The number of newlines after '}' or ';' of a class definition. nl_after_class = 0 # unsigned number # The number of newlines before a namespace. nl_before_namespace = 0 # unsigned number # The number of newlines after '{' of a namespace. This also adds newlines # before the matching '}'. # # 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if # applicable, otherwise no change. # # Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace. nl_inside_namespace = 0 # unsigned number # The number of newlines after '}' of a namespace. nl_after_namespace = 0 # unsigned number # The number of newlines before an access specifier label. This also includes # the Qt-specific 'signals:' and 'slots:'. Will not change the newline count # if after a brace open. # # 0: No change (default). nl_before_access_spec = 0 # unsigned number # The number of newlines after an access specifier label. This also includes # the Qt-specific 'signals:' and 'slots:'. Will not change the newline count # if after a brace open. # # 0: No change (default). # # Overrides nl_typedef_blk_start and nl_var_def_blk_start. nl_after_access_spec = 0 # unsigned number # The number of newlines between a function definition and the function # comment, as in '// comment\n void foo() {...}'. # # 0: No change (default). nl_comment_func_def = 0 # unsigned number # The number of newlines after a try-catch-finally block that isn't followed # by a brace close. # # 0: No change (default). nl_after_try_catch_finally = 0 # unsigned number # (C#) The number of newlines before and after a property, indexer or event # declaration. # # 0: No change (default). nl_around_cs_property = 0 # unsigned number # (C#) The number of newlines between the get/set/add/remove handlers. # # 0: No change (default). nl_between_get_set = 0 # unsigned number # (C#) Add or remove newline between property and the '{'. nl_property_brace = ignore # ignore/add/remove/force # Whether to remove blank lines after '{'. eat_blanks_after_open_brace = false # true/false # Whether to remove blank lines before '}'. eat_blanks_before_close_brace = false # true/false # How aggressively to remove extra newlines not in preprocessor. # # 0: No change (default) # 1: Remove most newlines not handled by other config # 2: Remove all newlines and reformat completely by config nl_remove_extra_newlines = 0 # unsigned number # (Java) Add or remove newline after an annotation statement. Only affects # annotations that are after a newline. nl_after_annotation = ignore # ignore/add/remove/force # (Java) Add or remove newline between two annotations. nl_between_annotation = ignore # ignore/add/remove/force # The number of newlines before a whole-file #ifdef. # # 0: No change (default). nl_before_whole_file_ifdef = 0 # unsigned number # The number of newlines after a whole-file #ifdef. # # 0: No change (default). nl_after_whole_file_ifdef = 0 # unsigned number # The number of newlines before a whole-file #endif. # # 0: No change (default). nl_before_whole_file_endif = 0 # unsigned number # The number of newlines after a whole-file #endif. # # 0: No change (default). nl_after_whole_file_endif = 0 # unsigned number # # Positioning options # # The position of arithmetic operators in wrapped expressions. pos_arith = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of assignment in wrapped expressions. Do not affect '=' # followed by '{'. pos_assign = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of Boolean operators in wrapped expressions. pos_bool = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of comparison operators in wrapped expressions. pos_compare = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of conditional operators, as in the '?' and ':' of # 'expr ? stmt : stmt', in wrapped expressions. pos_conditional = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of the comma in wrapped expressions. pos_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of the comma in enum entries. pos_enum_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of the comma in the base class list if there is more than one # line. Affects nl_class_init_args. pos_class_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of the comma in the constructor initialization list. # Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. pos_constr_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of trailing/leading class colon, between class and base class # list. Affects nl_class_colon. pos_class_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of colons between constructor and member initialization. # Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. pos_constr_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # The position of shift operators in wrapped expressions. pos_shift = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force # # Line splitting options # # Try to limit code width to N columns. code_width = 0 # unsigned number # Whether to fully split long 'for' statements at semi-colons. ls_for_split_full = false # true/false # Whether to fully split long function prototypes/calls at commas. # The option ls_code_width has priority over the option ls_func_split_full. ls_func_split_full = false # true/false # Whether to split lines as close to code_width as possible and ignore some # groupings. # The option ls_code_width has priority over the option ls_func_split_full. ls_code_width = false # true/false # # Code alignment options (not left column spaces/tabs) # # Whether to keep non-indenting tabs. align_keep_tabs = false # true/false # Whether to use tabs for aligning. align_with_tabs = false # true/false # Whether to bump out to the next tab when aligning. align_on_tabstop = false # true/false # Whether to right-align numbers. align_number_right = false # true/false # Whether to keep whitespace not required for alignment. align_keep_extra_space = false # true/false # Whether to align variable definitions in prototypes and functions. align_func_params = false # true/false # The span for aligning parameter definitions in function on parameter name. # # 0: Don't align (default). align_func_params_span = 0 # unsigned number # The threshold for aligning function parameter definitions. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_func_params_thresh = 0 # number # The gap for aligning function parameter definitions. align_func_params_gap = 0 # unsigned number # The span for aligning constructor value. # # 0: Don't align (default). align_constr_value_span = 0 # unsigned number # The threshold for aligning constructor value. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_constr_value_thresh = 0 # number # The gap for aligning constructor value. align_constr_value_gap = 0 # unsigned number # Whether to align parameters in single-line functions that have the same # name. The function names must already be aligned with each other. align_same_func_call_params = false # true/false # The span for aligning function-call parameters for single line functions. # # 0: Don't align (default). align_same_func_call_params_span = 0 # unsigned number # The threshold for aligning function-call parameters for single line # functions. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_same_func_call_params_thresh = 0 # number # The span for aligning variable definitions. # # 0: Don't align (default). align_var_def_span = 0 # unsigned number # How to consider (or treat) the '*' in the alignment of variable definitions. # # 0: Part of the type 'void * foo;' (default) # 1: Part of the variable 'void *foo;' # 2: Dangling 'void *foo;' # Dangling: the '*' will not be taken into account when aligning. align_var_def_star_style = 0 # unsigned number # How to consider (or treat) the '&' in the alignment of variable definitions. # # 0: Part of the type 'long & foo;' (default) # 1: Part of the variable 'long &foo;' # 2: Dangling 'long &foo;' # Dangling: the '&' will not be taken into account when aligning. align_var_def_amp_style = 0 # unsigned number # The threshold for aligning variable definitions. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_var_def_thresh = 0 # number # The gap for aligning variable definitions. align_var_def_gap = 0 # unsigned number # Whether to align the colon in struct bit fields. align_var_def_colon = false # true/false # The gap for aligning the colon in struct bit fields. align_var_def_colon_gap = 0 # unsigned number # Whether to align any attribute after the variable name. align_var_def_attribute = false # true/false # Whether to align inline struct/enum/union variable definitions. align_var_def_inline = false # true/false # The span for aligning on '=' in assignments. # # 0: Don't align (default). align_assign_span = 0 # unsigned number # The span for aligning on '=' in function prototype modifier. # # 0: Don't align (default). align_assign_func_proto_span = 0 # unsigned number # The threshold for aligning on '=' in assignments. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_assign_thresh = 0 # number # How to apply align_assign_span to function declaration "assignments", i.e. # 'virtual void foo() = 0' or '~foo() = {default|delete}'. # # 0: Align with other assignments (default) # 1: Align with each other, ignoring regular assignments # 2: Don't align align_assign_decl_func = 0 # unsigned number # The span for aligning on '=' in enums. # # 0: Don't align (default). align_enum_equ_span = 0 # unsigned number # The threshold for aligning on '=' in enums. # Use a negative number for absolute thresholds. # # 0: no limit (default). align_enum_equ_thresh = 0 # number # The span for aligning class member definitions. # # 0: Don't align (default). align_var_class_span = 0 # unsigned number # The threshold for aligning class member definitions. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_var_class_thresh = 0 # number # The gap for aligning class member definitions. align_var_class_gap = 0 # unsigned number # The span for aligning struct/union member definitions. # # 0: Don't align (default). align_var_struct_span = 0 # unsigned number # The threshold for aligning struct/union member definitions. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_var_struct_thresh = 0 # number # The gap for aligning struct/union member definitions. align_var_struct_gap = 0 # unsigned number # The span for aligning struct initializer values. # # 0: Don't align (default). align_struct_init_span = 0 # unsigned number # The span for aligning single-line typedefs. # # 0: Don't align (default). align_typedef_span = 0 # unsigned number # The minimum space between the type and the synonym of a typedef. align_typedef_gap = 0 # unsigned number # How to align typedef'd functions with other typedefs. # # 0: Don't mix them at all (default) # 1: Align the open parenthesis with the types # 2: Align the function type name with the other type names align_typedef_func = 0 # unsigned number # How to consider (or treat) the '*' in the alignment of typedefs. # # 0: Part of the typedef type, 'typedef int * pint;' (default) # 1: Part of type name: 'typedef int *pint;' # 2: Dangling: 'typedef int *pint;' # Dangling: the '*' will not be taken into account when aligning. align_typedef_star_style = 0 # unsigned number # How to consider (or treat) the '&' in the alignment of typedefs. # # 0: Part of the typedef type, 'typedef int & intref;' (default) # 1: Part of type name: 'typedef int &intref;' # 2: Dangling: 'typedef int &intref;' # Dangling: the '&' will not be taken into account when aligning. align_typedef_amp_style = 0 # unsigned number # The span for aligning comments that end lines. # # 0: Don't align (default). align_right_cmt_span = 0 # unsigned number # Minimum number of columns between preceding text and a trailing comment in # order for the comment to qualify for being aligned. Must be non-zero to have # an effect. align_right_cmt_gap = 0 # unsigned number # If aligning comments, whether to mix with comments after '}' and #endif with # less than three spaces before the comment. align_right_cmt_mix = false # true/false # Whether to only align trailing comments that are at the same brace level. align_right_cmt_same_level = false # true/false # Minimum column at which to align trailing comments. Comments which are # aligned beyond this column, but which can be aligned in a lesser column, # may be "pulled in". # # 0: Ignore (default). align_right_cmt_at_col = 0 # unsigned number # The span for aligning function prototypes. # # 0: Don't align (default). align_func_proto_span = 0 # unsigned number # The threshold for aligning function prototypes. # Use a negative number for absolute thresholds. # # 0: No limit (default). align_func_proto_thresh = 0 # number # Minimum gap between the return type and the function name. align_func_proto_gap = 0 # unsigned number # Whether to align function prototypes on the 'operator' keyword instead of # what follows. align_on_operator = false # true/false # Whether to mix aligning prototype and variable declarations. If true, # align_var_def_XXX options are used instead of align_func_proto_XXX options. align_mix_var_proto = false # true/false # Whether to align single-line functions with function prototypes. # Uses align_func_proto_span. align_single_line_func = false # true/false # Whether to align the open brace of single-line functions. # Requires align_single_line_func=true. Uses align_func_proto_span. align_single_line_brace = false # true/false # Gap for align_single_line_brace. align_single_line_brace_gap = 0 # unsigned number # (OC) The span for aligning Objective-C message specifications. # # 0: Don't align (default). align_oc_msg_spec_span = 0 # unsigned number # Whether to align macros wrapped with a backslash and a newline. This will # not work right if the macro contains a multi-line comment. align_nl_cont = false # true/false # Whether to align macro functions and variables together. align_pp_define_together = false # true/false # The span for aligning on '#define' bodies. # # =0: Don't align (default) # >0: Number of lines (including comments) between blocks align_pp_define_span = 0 # unsigned number # The minimum space between label and value of a preprocessor define. align_pp_define_gap = 0 # unsigned number # Whether to align lines that start with '<<' with previous '<<'. # # Default: true align_left_shift = true # true/false # Whether to align comma-separated statements following '<<' (as used to # initialize Eigen matrices). align_eigen_comma_init = false # true/false # Whether to align text after 'asm volatile ()' colons. align_asm_colon = false # true/false # (OC) Span for aligning parameters in an Objective-C message call # on the ':'. # # 0: Don't align. align_oc_msg_colon_span = 0 # unsigned number # (OC) Whether to always align with the first parameter, even if it is too # short. align_oc_msg_colon_first = false # true/false # (OC) Whether to align parameters in an Objective-C '+' or '-' declaration # on the ':'. align_oc_decl_colon = false # true/false # (OC) Whether to not align parameters in an Objectve-C message call if first # colon is not on next line of the message call (the same way Xcode does # aligment) align_oc_msg_colon_xcode_like = false # true/false # # Comment modification options # # Try to wrap comments at N columns. cmt_width = 0 # unsigned number # How to reflow comments. # # 0: No reflowing (apart from the line wrapping due to cmt_width) (default) # 1: No touching at all # 2: Full reflow cmt_reflow_mode = 0 # unsigned number # Whether to convert all tabs to spaces in comments. If false, tabs in # comments are left alone, unless used for indenting. cmt_convert_tab_to_spaces = true # true/false # Whether to apply changes to multi-line comments, including cmt_width, # keyword substitution and leading chars. # # Default: true cmt_indent_multi = true # true/false # Whether to group c-comments that look like they are in a block. cmt_c_group = false # true/false # Whether to put an empty '/*' on the first line of the combined c-comment. cmt_c_nl_start = false # true/false # Whether to add a newline before the closing '*/' of the combined c-comment. cmt_c_nl_end = false # true/false # Whether to change cpp-comments into c-comments. cmt_cpp_to_c = false # true/false # Whether to group cpp-comments that look like they are in a block. Only # meaningful if cmt_cpp_to_c=true. cmt_cpp_group = false # true/false # Whether to put an empty '/*' on the first line of the combined cpp-comment # when converting to a c-comment. # # Requires cmt_cpp_to_c=true and cmt_cpp_group=true. cmt_cpp_nl_start = false # true/false # Whether to add a newline before the closing '*/' of the combined cpp-comment # when converting to a c-comment. # # Requires cmt_cpp_to_c=true and cmt_cpp_group=true. cmt_cpp_nl_end = false # true/false # Whether to put a star on subsequent comment lines. cmt_star_cont = false # true/false # The number of spaces to insert at the start of subsequent comment lines. cmt_sp_before_star_cont = 0 # unsigned number # The number of spaces to insert after the star on subsequent comment lines. cmt_sp_after_star_cont = 0 # unsigned number # For multi-line comments with a '*' lead, remove leading spaces if the first # and last lines of the comment are the same length. # # Default: true cmt_multi_check_last = true # true/false # For multi-line comments with a '*' lead, remove leading spaces if the first # and last lines of the comment are the same length AND if the length is # bigger as the first_len minimum. # # Default: 4 cmt_multi_first_len_minimum = 4 # unsigned number # Path to a file that contains text to insert at the beginning of a file if # the file doesn't start with a C/C++ comment. If the inserted text contains # '$(filename)', that will be replaced with the current file's name. cmt_insert_file_header = "" # string # Path to a file that contains text to insert at the end of a file if the # file doesn't end with a C/C++ comment. If the inserted text contains # '$(filename)', that will be replaced with the current file's name. cmt_insert_file_footer = "" # string # Path to a file that contains text to insert before a function definition if # the function isn't preceded by a C/C++ comment. If the inserted text # contains '$(function)', '$(javaparam)' or '$(fclass)', these will be # replaced with, respectively, the name of the function, the javadoc '@param' # and '@return' stuff, or the name of the class to which the member function # belongs. cmt_insert_func_header = "" # string # Path to a file that contains text to insert before a class if the class # isn't preceded by a C/C++ comment. If the inserted text contains '$(class)', # that will be replaced with the class name. cmt_insert_class_header = "" # string # Path to a file that contains text to insert before an Objective-C message # specification, if the method isn't preceded by a C/C++ comment. If the # inserted text contains '$(message)' or '$(javaparam)', these will be # replaced with, respectively, the name of the function, or the javadoc # '@param' and '@return' stuff. cmt_insert_oc_msg_header = "" # string # Whether a comment should be inserted if a preprocessor is encountered when # stepping backwards from a function name. # # Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and # cmt_insert_class_header. cmt_insert_before_preproc = false # true/false # Whether a comment should be inserted if a function is declared inline to a # class definition. # # Applies to cmt_insert_func_header. # # Default: true cmt_insert_before_inlines = true # true/false # Whether a comment should be inserted if the function is a class constructor # or destructor. # # Applies to cmt_insert_func_header. cmt_insert_before_ctor_dtor = false # true/false # # Code modifying options (non-whitespace) # # Add or remove braces on a single-line 'do' statement. mod_full_brace_do = ignore # ignore/add/remove/force # Add or remove braces on a single-line 'for' statement. mod_full_brace_for = ignore # ignore/add/remove/force # (Pawn) Add or remove braces on a single-line function definition. mod_full_brace_function = ignore # ignore/add/remove/force # Add or remove braces on a single-line 'if' statement. Braces will not be # removed if the braced statement contains an 'else'. mod_full_brace_if = ignore # ignore/add/remove/force # Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either # have, or do not have, braces. If true, braces will be added if any block # needs braces, and will only be removed if they can be removed from all # blocks. # # Overrides mod_full_brace_if. mod_full_brace_if_chain = false # true/false # Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. # If true, mod_full_brace_if_chain will only remove braces from an 'if' that # does not have an 'else if' or 'else'. mod_full_brace_if_chain_only = false # true/false # Add or remove braces on single-line 'while' statement. mod_full_brace_while = ignore # ignore/add/remove/force # Add or remove braces on single-line 'using ()' statement. mod_full_brace_using = ignore # ignore/add/remove/force # Don't remove braces around statements that span N newlines mod_full_brace_nl = 0 # unsigned number # Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks # which span multiple lines. # # Affects: # mod_full_brace_for # mod_full_brace_if # mod_full_brace_if_chain # mod_full_brace_if_chain_only # mod_full_brace_while # mod_full_brace_using # # Does not affect: # mod_full_brace_do # mod_full_brace_function mod_full_brace_nl_block_rem_mlcond = false # true/false # Add or remove unnecessary parenthesis on 'return' statement. mod_paren_on_return = ignore # ignore/add/remove/force # (Pawn) Whether to change optional semicolons to real semicolons. mod_pawn_semicolon = false # true/false # Whether to fully parenthesize Boolean expressions in 'while' and 'if' # statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. mod_full_paren_if_bool = false # true/false # Whether to remove superfluous semicolons. mod_remove_extra_semicolon = false # true/false # If a function body exceeds the specified number of newlines and doesn't have # a comment after the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 0 # unsigned number # If a namespace body exceeds the specified number of newlines and doesn't # have a comment after the close brace, a comment will be added. mod_add_long_namespace_closebrace_comment = 0 # unsigned number # If a class body exceeds the specified number of newlines and doesn't have a # comment after the close brace, a comment will be added. mod_add_long_class_closebrace_comment = 0 # unsigned number # If a switch body exceeds the specified number of newlines and doesn't have a # comment after the close brace, a comment will be added. mod_add_long_switch_closebrace_comment = 0 # unsigned number # If an #ifdef body exceeds the specified number of newlines and doesn't have # a comment after the #endif, a comment will be added. mod_add_long_ifdef_endif_comment = 0 # unsigned number # If an #ifdef or #else body exceeds the specified number of newlines and # doesn't have a comment after the #else, a comment will be added. mod_add_long_ifdef_else_comment = 0 # unsigned number # Whether to take care of the case by the mod_sort_xx options. mod_sort_case_sensitive = false # true/false # Whether to sort consecutive single-line 'import' statements. mod_sort_import = false # true/false # (C#) Whether to sort consecutive single-line 'using' statements. mod_sort_using = false # true/false # Whether to sort consecutive single-line '#include' statements (C/C++) and # '#import' statements (Objective-C). Be aware that this has the potential to # break your code if your includes/imports have ordering dependencies. mod_sort_include = false # true/false # Whether to prioritize '#include' and '#import' statements that contain # filename without extension when sorting is enabled. mod_sort_incl_import_prioritize_filename = false # true/false # Whether to prioritize '#include' and '#import' statements that does not # contain extensions when sorting is enabled. mod_sort_incl_import_prioritize_extensionless = false # true/false # Whether to prioritize '#include' and '#import' statements that contain # angle over quotes when sorting is enabled. mod_sort_incl_import_prioritize_angle_over_quotes = false # true/false # Whether to ignore file extension in '#include' and '#import' statements # for sorting comparison. mod_sort_incl_import_ignore_extension = false # true/false # Whether to group '#include' and '#import' statements when sorting is enabled. mod_sort_incl_import_grouping_enabled = false # true/false # Whether to move a 'break' that appears after a fully braced 'case' before # the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. mod_move_case_break = false # true/false # Add or remove braces around a fully braced case statement. Will only remove # braces if there are no variable declarations in the block. mod_case_brace = ignore # ignore/add/remove/force # Whether to remove a void 'return;' that appears as the last statement in a # function. mod_remove_empty_return = false # true/false # Add or remove the comma after the last value of an enumeration. mod_enum_last_comma = ignore # ignore/add/remove/force # (OC) Whether to organize the properties. If true, properties will be # rearranged according to the mod_sort_oc_property_*_weight factors. mod_sort_oc_properties = false # true/false # (OC) Weight of a class property modifier. mod_sort_oc_property_class_weight = 0 # number # (OC) Weight of 'atomic' and 'nonatomic'. mod_sort_oc_property_thread_safe_weight = 0 # number # (OC) Weight of 'readwrite' when organizing properties. mod_sort_oc_property_readwrite_weight = 0 # number # (OC) Weight of a reference type specifier ('retain', 'copy', 'assign', # 'weak', 'strong') when organizing properties. mod_sort_oc_property_reference_weight = 0 # number # (OC) Weight of getter type ('getter=') when organizing properties. mod_sort_oc_property_getter_weight = 0 # number # (OC) Weight of setter type ('setter=') when organizing properties. mod_sort_oc_property_setter_weight = 0 # number # (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified', # 'null_resettable') when organizing properties. mod_sort_oc_property_nullability_weight = 0 # number # # Preprocessor options # # Add or remove indentation of preprocessor directives inside #if blocks # at brace level 0 (file-level). pp_indent = ignore # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level. If false, these are # indented from column 1. pp_indent_at_level = false # true/false # Specifies the number of columns to indent preprocessors per level # at brace level 0 (file-level). If pp_indent_at_level=false, also specifies # the number of columns to indent preprocessors per level # at brace level > 0 (function-level). # # Default: 1 pp_indent_count = 1 # unsigned number # Add or remove space after # based on pp_level of #if blocks. pp_space = ignore # ignore/add/remove/force # Sets the number of spaces per level added with pp_space. pp_space_count = 0 # unsigned number # The indent for '#region' and '#endregion' in C# and '#pragma region' in # C/C++. Negative values decrease indent down to the first column. pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion. pp_region_indent_code = false # true/false # If pp_indent_at_level=true, sets the indent for #if, #else and #endif when # not at file-level. Negative values decrease indent down to the first column. # # =0: Indent preprocessors using output_tab_size # >0: Column at which all preprocessors will be indented pp_indent_if = 0 # number # Whether to indent the code between #if, #else and #endif. pp_if_indent_code = false # true/false # Whether to indent '#define' at the brace level. If false, these are # indented from column 1. pp_define_at_level = false # true/false # Whether to ignore the '#define' body while formatting. pp_ignore_define_body = false # true/false # Whether to indent case statements between #if, #else, and #endif. # Only applies to the indent of the preprocesser that the case statements # directly inside of. # # Default: true pp_indent_case = true # true/false # Whether to indent whole function definitions between #if, #else, and #endif. # Only applies to the indent of the preprocesser that the function definition # is directly inside of. # # Default: true pp_indent_func_def = true # true/false # Whether to indent extern C blocks between #if, #else, and #endif. # Only applies to the indent of the preprocesser that the extern block is # directly inside of. # # Default: true pp_indent_extern = true # true/false # Whether to indent braces directly inside #if, #else, and #endif. # Only applies to the indent of the preprocesser that the braces are directly # inside of. # # Default: true pp_indent_brace = true # true/false # # Sort includes options # # The regex for include category with priority 0. include_category_0 = "" # string # The regex for include category with priority 1. include_category_1 = "" # string # The regex for include category with priority 2. include_category_2 = "" # string # # Use or Do not Use options # # true: indent_func_call_param will be used (default) # false: indent_func_call_param will NOT be used # # Default: true use_indent_func_call_param = true # true/false # The value of the indentation for a continuation line is calculated # differently if the statement is: # - a declaration: your case with QString fileName ... # - an assignment: your case with pSettings = new QSettings( ... # # At the second case the indentation value might be used twice: # - at the assignment # - at the function call (if present) # # To prevent the double use of the indentation value, use this option with the # value 'true'. # # true: indent_continue will be used only once # false: indent_continue will be used every time (default) use_indent_continue_only_once = false # true/false # The value might be used twice: # - at the assignment # - at the opening brace # # To prevent the double use of the indentation value, use this option with the # value 'true'. # # true: indentation will be used only once # false: indentation will be used every time (default) indent_cpp_lambda_only_once = true # true/false # Whether sp_after_angle takes precedence over sp_inside_fparen. This was the # historic behavior, but is probably not the desired behavior, so this is off # by default. use_sp_after_angle_always = false # true/false # Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially, # this tries to format these so that they match Qt's normalized form (i.e. the # result of QMetaObject::normalizedSignature), which can slightly improve the # performance of the QObject::connect call, rather than how they would # otherwise be formatted. # # See options_for_QT.cpp for details. # # Default: true use_options_overriding_for_qt_macros = true # true/false # If true: the form feed character is removed from the list # of whitespace characters. # See https://en.cppreference.com/w/cpp/string/byte/isspace use_form_feed_no_more_as_whitespace_character = false # true/false # # Warn levels - 1: error, 2: warning (default), 3: note # # (C#) Warning is given if doing tab-to-\t replacement and we have found one # in a C# verbatim string literal. # # Default: 2 warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number # Limit the number of loops. # Used by uncrustify.cpp to exit from infinite loop. # 0: no limit. debug_max_number_of_loops = 0 # number # Set the number of the line to protocol; # Used in the function prot_the_line if the 2. parameter is zero. # 0: nothing protocol. debug_line_number_to_protocol = 0 # number # Set the number of second(s) before terminating formatting the current file, # 0: no timeout. # only for linux debug_timeout = 0 # number # Meaning of the settings: # Ignore - do not do any changes # Add - makes sure there is 1 or more space/brace/newline/etc # Force - makes sure there is exactly 1 space/brace/newline/etc, # behaves like Add in some contexts # Remove - removes space/brace/newline/etc # # # - Token(s) can be treated as specific type(s) with the 'set' option: # `set tokenType tokenString [tokenString...]` # # Example: # `set BOOL __AND__ __OR__` # # tokenTypes are defined in src/token_enum.h, use them without the # 'CT_' prefix: 'CT_BOOL' => 'BOOL' # # # - Token(s) can be treated as type(s) with the 'type' option. # `type tokenString [tokenString...]` # # Example: # `type int c_uint_8 Rectangle` # # This can also be achieved with `set TYPE int c_uint_8 Rectangle` # # # To embed whitespace in tokenStrings use the '\' escape character, or quote # the tokenStrings. These quotes are supported: "'` # # # - Support for the auto detection of languages through the file ending can be # added using the 'file_ext' command. # `file_ext langType langString [langString..]` # # Example: # `file_ext CPP .ch .cxx .cpp.in` # # langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use # them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' # # # - Custom macro-based indentation can be set up using 'macro-open', # 'macro-else' and 'macro-close'. # `(macro-open | macro-else | macro-close) tokenString` # # Example: # `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` # `macro-open BEGIN_MESSAGE_MAP` # `macro-close END_MESSAGE_MAP` # # # option(s) with 'not default' value: 24 # cppcheck-2.7/AUTHORS000066400000000000000000000121321417746362400142270ustar00rootroot00000000000000The cppcheck team, in alphabetical order: Abhijit Sawant Abhishek Bharadwaj Abigail Buccaneer Adam J Richter Adrien Chardon Ahti Legonkov Akhilesh Nema Akio Idehara Albert Aribaud Aleksandr Pikalev Aleksey Palazhchenko Alexander Alekseev Alexander Festini Alexander Gushchin Alexander Mai Alexander Tkachev Alexandre Chouvellon Alexey Eryomenko Alexey Zhikhartsev Alfi Maulana Ali Can Demiralp Alon Liberman Ameen Ali Andreas Bacher Andreas Bießmann Andreas Grob Andreas Pokorny Andreas Rönnquist Andreas Vollenweider Andrei Karas Andrew C. Martin Andy Maloney Aneesh Azhakesan S Ankita Gupta Antti Tuppurainen Anurag Garg Armin Müller Arpit Chaudhary August Sodora Ayaz Salikhov Balázs Tóth Baris Demiray Bartlomiej Grzeskowiak bbennetts Benjamin Bannier Benjamin Fovet Benjamin Goose Benjamin Kramer Benjamin Woester Benjamin Wolsey Ben T Bernd Buschinski Bill Egert Björge Dijkstra booga Boris Barbulovski Boris Egorov Boussaffa Walid Bo Rydberg Carl Michael Grüner Monzón Carlo Marcelo Arenas Belón Carlos Gomes Martinho Carl-Oskar Larsson Cary R Changkyoon Kim Chris Lalancette Christian Ehrlicher Christian Franke Christoph Schmidt Christoph Strehle Chuck Larson Claus Jensby Madsen Colomban Wendling Conrado Gouvea daisuke-chiba Daniel Friedrich Daniel Marjamäki David Hallas David Korth Dávid Slivka Debrard Sebastien Deepak Gupta dencat Diego de las Heras Dirk Jagdmann Dmitriy Dmitry Marakasov Dmitry-Me Duraffort Edoardo Prezioso Eivind Tagseth Elbert Pol Emmanuel Blot Eric Malenfant Eric Sesterhenn Erik Hovland Erik Lax Ettl Martin Even Rouault Evgeny Mandrikov Felipe Pena Felix Geyer Felix Passenberg Felix Wolff Florin Iucha Francesc Elies Frank Zingsheim Frederik Schwarzer fu7mu4 Galimov Albert Garrett Bodily Gary Leutheuser gaurav kaushik Gennady Feldman Georgy Komarov Gerhard Zlabinger Gianfranco Costamagna Gianluca Scacco Gleydson Soares Goran Džaferi Graham Whitted Greg Hewgill Guillaume Miossec Gustav Palmqvist Günther Makulik Harald Scheidl Heiko Bauke Heiko Eißfeldt Heinrich Schuchardt Henrik Nilsson He Yuqi Hoang Tuan Su Igor Rondarev Igor Zhukov Ivan Maidanski Iván Matellanes Ivan Ryabov Ivar Bonsaksen Jakub Melka Jan Egil Ruud Jan Hellwig János Maros Jay Sigbrandt Jens Bäckman Jérémy Lefaure Jes Ramsing Jesse Boswell Jim Zhou jlguardi Johan Samuelson John Marshall John-Paul Ore John Smits Jonathan Clohessy Jonathan Neuschäfer Jonathan Thackray Jose Roquette Joshua Beck Joshua Rogers Julian Santander Julien Peyregne Jure Menart Jussi Lehtola Jørgen Kvalsvik Kamil Dudka Kartik Bajaj keinflue Ken-Patrick Lehrmann Ketil Skjerve Kevin Christian Kevin Kendzia Kimmo Varis Konrad Grochowski Konrad Windszus Kumar Ashwani Kyle Chisholm Lars Even Almaas larudwer Lau bakman Lauri Nurmi Leandro Lisboa Penz Lena Herscheid Leon De Andrade Lieven de Cock lioncash Lionel Gimbert Lucas Manuel Rodriguez Luis Díaz Más Lukas Grützmacher Lukasz Czajczyk Łukasz Jankowski Luxon Jean-Pierre Maksim Derbasov Malcolm Parsons Marc-Antoine Perennou Marcel Raad Marco Trevisan Marek Zmysłowski Marian Klymov Mark de Wever Markus Elfring Martin Ettl Martin Exner Martin Güthle Martin Herren Márton Csordás Masafumi Koba Massimo Paladin Mateusz Pusz Mathias De Maré Mathias Schmid Matthias Krüger Matthias Kuhn Matthias Schmieder Matt Johnson Maurice Gilden Mavik Michael Løiten Miika-Petteri Matikainen Mika Attila Mike Tzou Milhan Kim Mil Tolstoy Mischa Aster Alff Mohit Mate Monika Lukow Moritz Barsnick Moritz Lipp Moshe Kaplan ms Neszt Tibor Nguyen Duong Tuan Ni2c2k Nick Ridgway Nicolás Alvarez Nicolas Le Cam Nilesh Kumar Ogawa KenIchi Oleksandr Redko Oliver Schode Oliver Stöneberg Olivier Croquette Paul Aitken Paul Fultz II Pavel Bibergal Pavel Pimenov Pavel Roschin Pavel Skipenes Pavel Šimovec Pavol Misik Pete Johns Peter Pentchev Peter Schops Philipp Kloke Pierre Schweitzer Pino Toscano Pranav Khanna Ramzan Bekbulatov Raphael Geissert Reijo Tomperi Riccardo Ghetta Richard A. Smith Richard Quirk Rick van der Sluijs Rikard Falkeborn rivdsl Robert Habrich Robert Morin Roberto Martelloni Robert Reif rofl0r Roman Zaytsev Borisovich Ronald Hiemstra root Rosen Penev Rudi Danner Rudolf Grauberger Ryan Pavlik Samir Aguiar Sam Truscott Samuel Degrande Sandeep Dutta Savvas Etairidis Scott Furry Sebastian Held Sebastian Matuschka Sébastien Debrard Sergey Burgsdorf Shane Tapp Simon Cornell Simon Kagstrom Simon Large Simon Martin Simon Shanks Slava Semushin Stas Cymbalov Stefan Beller Stefan Naewe Stefan van Kessel Stefan Weil Stéphane Michel Steve Browne Steve Duan Steve Mokris Steven Cook Steven Myint Susi Lehtola Swasti Shrivastava Sylvain Joubert Tam Do Thanh Teddy Didé Thomas Arnhold Thomas Jarosch Thomas Niederberger Thomas Otto Thomas Sondergaard Thorsten Sick Tim Gerundt tititiou36 Tobias Weibel Tomasz Kłoczko Tom Pollok Toralf Förster Troshin V.S. Tyson Nottingham Valerii Lashmanov Vasily Maslyukov Veli-Matti Visuri WenChung Chiu Vesa Pikki Ville-Pekka Vahteala Ville Skyttä Vincent Le Garrec Wolfgang Stöggl x29a XhmikosR Xuecheng Zhang Yichen Yan Yurii Putin Zachary Blair Zhao Qifa Zhiyuan Zhang Zhu Lei Дмитрий Старцев GUI graphics courtesy of Tango Desktop Project: http://tango.freedesktop.org cppcheck-2.7/CMakeLists.txt000066400000000000000000000040251417746362400157210ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.12) project(Cppcheck) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include(GNUInstallDirs) include(cmake/versions.cmake) include(cmake/options.cmake) include(cmake/findDependencies.cmake) include(cmake/compileroptions.cmake) include(cmake/compilerDefinitions.cmake) include(cmake/buildFiles.cmake) include(cmake/cxx11.cmake) use_cxx11() file(GLOB addons "addons/*.py") file(GLOB cfgs "cfg/*.cfg") file(GLOB platforms "platforms/*.xml") find_program(LIBXML2_XMLLINT_EXECUTABLE xmllint) if (LIBXML2_XMLLINT_EXECUTABLE) add_custom_target(validateCFG ${LIBXML2_XMLLINT_EXECUTABLE} --noout ${CMAKE_SOURCE_DIR}/cfg/cppcheck-cfg.rng) foreach(cfg ${cfgs}) get_filename_component(cfgname ${cfg} NAME_WE) add_custom_target(validateCFG-${cfgname} ${LIBXML2_XMLLINT_EXECUTABLE} --noout --relaxng ${CMAKE_SOURCE_DIR}/cfg/cppcheck-cfg.rng ${cfg}) add_dependencies(validateCFG validateCFG-${cfgname}) endforeach() endif() if (BUILD_TESTS) enable_testing() endif() add_custom_target(copy_cfg ALL ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/cfg" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/cfg" COMMENT "Copying cfg files to ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}") add_custom_target(copy_addons ALL ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/addons" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/addons" COMMENT "Copying addons files to ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}") if(USE_BUNDLED_TINYXML2) message(STATUS "Using bundled version of tinyxml2") add_subdirectory(externals/tinyxml2) endif() add_subdirectory(externals/simplecpp) add_subdirectory(lib) # CppCheck Library add_subdirectory(cli) # Client application add_subdirectory(test) # Tests add_subdirectory(gui) # Graphical application add_subdirectory(tools/triage) # Triage tool add_subdirectory(oss-fuzz) # OSS-Fuzz clients add_subdirectory(tools) include(cmake/printInfo.cmake) include(cmake/clang_tidy.cmake) cppcheck-2.7/COPYING000066400000000000000000001045131417746362400142170ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . cppcheck-2.7/Makefile000066400000000000000000002227671417746362400146400ustar00rootroot00000000000000# This file is generated by tools/dmake, do not edit. # To compile with rules, use 'make HAVE_RULES=yes' ifndef HAVE_RULES HAVE_RULES=no endif ifndef USE_Z3 USE_Z3=no endif ifeq ($(USE_Z3),yes) CPPFLAGS += -DUSE_Z3 LIBS += -lz3 endif # use match compiler ifeq ($(SRCDIR),build) $(warning Usage of SRCDIR to activate match compiler is deprecated. Use MATCHCOMPILER=yes instead.) MATCHCOMPILER:=yes endif ifeq ($(MATCHCOMPILER),yes) # Find available Python interpreter PYTHON_INTERPRETER := $(shell which python) ifndef PYTHON_INTERPRETER PYTHON_INTERPRETER := $(shell which python3) endif ifndef PYTHON_INTERPRETER $(error Did not find a Python interpreter) endif ifdef VERIFY matchcompiler_S := $(shell $(PYTHON_INTERPRETER) tools/matchcompiler.py --verify) else matchcompiler_S := $(shell $(PYTHON_INTERPRETER) tools/matchcompiler.py) endif libcppdir:=build else libcppdir:=lib endif ifdef FILESDIR CPPFILESDIR=-DFILESDIR=\"$(FILESDIR)\" else CPPFILESDIR= endif RDYNAMIC=-rdynamic # Set the CPPCHK_GLIBCXX_DEBUG flag. This flag is not used in release Makefiles. # The _GLIBCXX_DEBUG define doesn't work in Cygwin or other Win32 systems. ifndef COMSPEC ifdef ComSpec #### ComSpec is defined on some WIN32's. COMSPEC=$(ComSpec) endif # ComSpec endif # COMSPEC ifdef COMSPEC #### Maybe Windows ifndef CPPCHK_GLIBCXX_DEBUG CPPCHK_GLIBCXX_DEBUG= endif # !CPPCHK_GLIBCXX_DEBUG ifeq ($(MSYSTEM),MINGW32 MINGW64) LDFLAGS=-lshlwapi else RDYNAMIC=-lshlwapi endif else # !COMSPEC uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') ifeq ($(uname_S),Linux) ifndef CPPCHK_GLIBCXX_DEBUG CPPCHK_GLIBCXX_DEBUG=-D_GLIBCXX_DEBUG endif # !CPPCHK_GLIBCXX_DEBUG endif # Linux ifeq ($(uname_S),GNU/kFreeBSD) ifndef CPPCHK_GLIBCXX_DEBUG CPPCHK_GLIBCXX_DEBUG=-D_GLIBCXX_DEBUG endif # !CPPCHK_GLIBCXX_DEBUG endif # GNU/kFreeBSD endif # COMSPEC # Set the UNDEF_STRICT_ANSI flag to address compile time warnings # with tinyxml2 and Cygwin. ifdef COMSPEC uname_S := $(shell uname -s) ifneq (,$(findstring CYGWIN,$(uname_S))) UNDEF_STRICT_ANSI=-U__STRICT_ANSI__ endif # CYGWIN endif # COMSPEC ifndef CXX CXX=g++ endif ifeq (clang++, $(findstring clang++,$(CXX))) CPPCHK_GLIBCXX_DEBUG= endif ifndef CXXFLAGS CXXFLAGS=-std=c++0x -O2 -DNDEBUG -Wall -Wno-sign-compare endif # Increase stack size for Cygwin builds to avoid segmentation fault in limited recursive tests. ifdef COMSPEC uname_S := $(shell uname -s) ifneq (,$(findstring CYGWIN,$(uname_S))) CXXFLAGS+=-Wl,--stack,8388608 endif # CYGWIN endif # COMSPEC ifeq (g++, $(findstring g++,$(CXX))) override CXXFLAGS += -std=c++0x else ifeq (clang++, $(findstring clang++,$(CXX))) override CXXFLAGS += -std=c++0x else ifeq ($(CXX), c++) ifeq ($(shell uname -s), Darwin) override CXXFLAGS += -std=c++0x endif endif ifeq ($(HAVE_RULES),yes) override CXXFLAGS += -DHAVE_RULES -DTIXML_USE_STL $(shell pcre-config --cflags) ifdef LIBS LIBS += $(shell pcre-config --libs) else LIBS=$(shell pcre-config --libs) endif endif ifndef PREFIX PREFIX=/usr endif ifndef INCLUDE_FOR_LIB INCLUDE_FOR_LIB=-Ilib -isystem externals -isystem externals/picojson -isystem externals/simplecpp -isystem externals/tinyxml2 endif ifndef INCLUDE_FOR_CLI INCLUDE_FOR_CLI=-Ilib -isystem externals/simplecpp -isystem externals/tinyxml2 endif ifndef INCLUDE_FOR_TEST INCLUDE_FOR_TEST=-Ilib -Icli -isystem externals/simplecpp -isystem externals/tinyxml2 endif BIN=$(DESTDIR)$(PREFIX)/bin # For 'make man': sudo apt-get install xsltproc docbook-xsl docbook-xml on Linux DB2MAN?=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl XP=xsltproc -''-nonet -''-param man.charmap.use.subset "0" MAN_SOURCE=man/cppcheck.1.xml ###### Object Files LIBOBJ = $(libcppdir)/analyzerinfo.o \ $(libcppdir)/astutils.o \ $(libcppdir)/bughuntingchecks.o \ $(libcppdir)/check.o \ $(libcppdir)/check64bit.o \ $(libcppdir)/checkassert.o \ $(libcppdir)/checkautovariables.o \ $(libcppdir)/checkbool.o \ $(libcppdir)/checkboost.o \ $(libcppdir)/checkbufferoverrun.o \ $(libcppdir)/checkclass.o \ $(libcppdir)/checkcondition.o \ $(libcppdir)/checkexceptionsafety.o \ $(libcppdir)/checkfunctions.o \ $(libcppdir)/checkinternal.o \ $(libcppdir)/checkio.o \ $(libcppdir)/checkleakautovar.o \ $(libcppdir)/checkmemoryleak.o \ $(libcppdir)/checknullpointer.o \ $(libcppdir)/checkother.o \ $(libcppdir)/checkpostfixoperator.o \ $(libcppdir)/checksizeof.o \ $(libcppdir)/checkstl.o \ $(libcppdir)/checkstring.o \ $(libcppdir)/checktype.o \ $(libcppdir)/checkuninitvar.o \ $(libcppdir)/checkunusedfunctions.o \ $(libcppdir)/checkunusedvar.o \ $(libcppdir)/checkvaarg.o \ $(libcppdir)/clangimport.o \ $(libcppdir)/color.o \ $(libcppdir)/cppcheck.o \ $(libcppdir)/ctu.o \ $(libcppdir)/errorlogger.o \ $(libcppdir)/errortypes.o \ $(libcppdir)/exprengine.o \ $(libcppdir)/forwardanalyzer.o \ $(libcppdir)/importproject.o \ $(libcppdir)/infer.o \ $(libcppdir)/library.o \ $(libcppdir)/mathlib.o \ $(libcppdir)/path.o \ $(libcppdir)/pathanalysis.o \ $(libcppdir)/pathmatch.o \ $(libcppdir)/platform.o \ $(libcppdir)/preprocessor.o \ $(libcppdir)/programmemory.o \ $(libcppdir)/reverseanalyzer.o \ $(libcppdir)/settings.o \ $(libcppdir)/summaries.o \ $(libcppdir)/suppressions.o \ $(libcppdir)/symboldatabase.o \ $(libcppdir)/templatesimplifier.o \ $(libcppdir)/timer.o \ $(libcppdir)/token.o \ $(libcppdir)/tokenize.o \ $(libcppdir)/tokenlist.o \ $(libcppdir)/utils.o \ $(libcppdir)/valueflow.o EXTOBJ = externals/simplecpp/simplecpp.o \ externals/tinyxml2/tinyxml2.o CLIOBJ = cli/cmdlineparser.o \ cli/cppcheckexecutor.o \ cli/filelister.o \ cli/main.o \ cli/threadexecutor.o TESTOBJ = test/options.o \ test/test64bit.o \ test/testassert.o \ test/testastutils.o \ test/testautovariables.o \ test/testbool.o \ test/testboost.o \ test/testbufferoverrun.o \ test/testbughuntingchecks.o \ test/testcharvar.o \ test/testclangimport.o \ test/testclass.o \ test/testcmdlineparser.o \ test/testcondition.o \ test/testconstructors.o \ test/testcppcheck.o \ test/testerrorlogger.o \ test/testexceptionsafety.o \ test/testexprengine.o \ test/testfilelister.o \ test/testfunctions.o \ test/testgarbage.o \ test/testimportproject.o \ test/testincompletestatement.o \ test/testinternal.o \ test/testio.o \ test/testleakautovar.o \ test/testlibrary.o \ test/testmathlib.o \ test/testmemleak.o \ test/testnullpointer.o \ test/testoptions.o \ test/testother.o \ test/testpath.o \ test/testpathmatch.o \ test/testplatform.o \ test/testpostfixoperator.o \ test/testpreprocessor.o \ test/testrunner.o \ test/testsimplifytemplate.o \ test/testsimplifytokens.o \ test/testsimplifytypedef.o \ test/testsimplifyusing.o \ test/testsizeof.o \ test/teststl.o \ test/teststring.o \ test/testsuite.o \ test/testsummaries.o \ test/testsuppressions.o \ test/testsymboldatabase.o \ test/testthreadexecutor.o \ test/testtimer.o \ test/testtoken.o \ test/testtokenize.o \ test/testtokenlist.o \ test/testtokenrange.o \ test/testtype.o \ test/testuninitvar.o \ test/testunusedfunctions.o \ test/testunusedprivfunc.o \ test/testunusedvar.o \ test/testutils.o \ test/testvaarg.o \ test/testvalueflow.o \ test/testvarid.o .PHONY: run-dmake tags ###### Targets cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) all: cppcheck testrunner testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) test: all ./testrunner check: all ./testrunner -q checkcfg: cppcheck validateCFG ./test/cfg/runtests.sh dmake: tools/dmake.o cli/filelister.o $(libcppdir)/pathmatch.o $(libcppdir)/path.o $(libcppdir)/utils.o externals/simplecpp/simplecpp.o $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) run-dmake: dmake ./dmake generate_cfg_tests: tools/generate_cfg_tests.o $(EXTOBJ) g++ -isystem externals/tinyxml2 -o generate_cfg_tests tools/generate_cfg_tests.o $(EXTOBJ) clean: rm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner dmake cppcheck cppcheck.exe cppcheck.1 man: man/cppcheck.1 man/cppcheck.1: $(MAN_SOURCE) $(XP) $(DB2MAN) $(MAN_SOURCE) tags: ctags -R --exclude=doxyoutput --exclude=test/cfg --exclude=test/synthetic cli externals gui lib test install: cppcheck install -d ${BIN} install cppcheck ${BIN} install htmlreport/cppcheck-htmlreport ${BIN} ifdef FILESDIR install -d ${DESTDIR}${FILESDIR} install -d ${DESTDIR}${FILESDIR}/addons install -m 644 addons/*.py ${DESTDIR}${FILESDIR}/addons install -d ${DESTDIR}${FILESDIR}/cfg install -m 644 cfg/*.cfg ${DESTDIR}${FILESDIR}/cfg install -d ${DESTDIR}${FILESDIR}/platforms install -m 644 platforms/*.xml ${DESTDIR}${FILESDIR}/platforms else $(error FILESDIR must be set!) endif uninstall: @if test -d ${BIN}; then \ files="cppcheck cppcheck-htmlreport"; \ echo '(' cd ${BIN} '&&' rm -f $$files ')'; \ ( cd ${BIN} && rm -f $$files ); \ fi ifdef FILESDIR @if test -d ${DESTDIR}${FILESDIR}; then \ echo rm -rf ${DESTDIR}${FILESDIR}; \ rm -rf ${DESTDIR}${FILESDIR}; \ fi endif ifdef CFGDIR @if test -d ${DESTDIR}${CFGDIR}; then \ files="`cd cfg 2>/dev/null && ls`"; \ if test -n "$$files"; then \ echo '(' cd ${DESTDIR}${CFGDIR} '&&' rm -f $$files ')'; \ ( cd ${DESTDIR}${CFGDIR} && rm -f $$files ); \ fi; \ fi endif # Validation of library files: ConfigFiles := $(wildcard cfg/*.cfg) ConfigFilesCHECKED := $(patsubst %.cfg,%.checked,$(ConfigFiles)) .PHONY: validateCFG %.checked:%.cfg xmllint --noout --relaxng cfg/cppcheck-cfg.rng $< validateCFG: ${ConfigFilesCHECKED} xmllint --noout cfg/cppcheck-cfg.rng # Validation of platforms files: PlatformFiles := $(wildcard platforms/*.xml) PlatformFilesCHECKED := $(patsubst %.xml,%.checked,$(PlatformFiles)) .PHONY: validatePlatforms %.checked:%.xml xmllint --noout --relaxng platforms/cppcheck-platforms.rng $< validatePlatforms: ${PlatformFilesCHECKED} # Validate XML output (to detect regressions) /tmp/errorlist.xml: cppcheck ./cppcheck --errorlist >$@ /tmp/example.xml: cppcheck ./cppcheck --xml --enable=all --inconclusive --max-configs=1 samples 2>/tmp/example.xml createXMLExamples:/tmp/errorlist.xml /tmp/example.xml .PHONY: validateXML validateXML: createXMLExamples xmllint --noout cppcheck-errors.rng xmllint --noout --relaxng cppcheck-errors.rng /tmp/errorlist.xml xmllint --noout --relaxng cppcheck-errors.rng /tmp/example.xml checkCWEEntries: /tmp/errorlist.xml ./tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml .PHONY: validateRules validateRules: xmllint --noout rules/*.xml ###### Build $(libcppdir)/analyzerinfo.o: lib/analyzerinfo.cpp externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/path.h lib/platform.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/analyzerinfo.o $(libcppdir)/analyzerinfo.cpp $(libcppdir)/astutils.o: lib/astutils.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/astutils.o $(libcppdir)/astutils.cpp $(libcppdir)/bughuntingchecks.o: lib/bughuntingchecks.cpp lib/astutils.h lib/bughuntingchecks.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/bughuntingchecks.o $(libcppdir)/bughuntingchecks.cpp $(libcppdir)/check.o: lib/check.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/check.o $(libcppdir)/check.cpp $(libcppdir)/check64bit.o: lib/check64bit.cpp lib/check.h lib/check64bit.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/check64bit.o $(libcppdir)/check64bit.cpp $(libcppdir)/checkassert.o: lib/checkassert.cpp lib/check.h lib/checkassert.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkassert.o $(libcppdir)/checkassert.cpp $(libcppdir)/checkautovariables.o: lib/checkautovariables.cpp lib/astutils.h lib/check.h lib/checkautovariables.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkautovariables.o $(libcppdir)/checkautovariables.cpp $(libcppdir)/checkbool.o: lib/checkbool.cpp lib/astutils.h lib/check.h lib/checkbool.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkbool.o $(libcppdir)/checkbool.cpp $(libcppdir)/checkboost.o: lib/checkboost.cpp lib/check.h lib/checkboost.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkboost.o $(libcppdir)/checkboost.cpp $(libcppdir)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/checkbufferoverrun.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkbufferoverrun.o $(libcppdir)/checkbufferoverrun.cpp $(libcppdir)/checkclass.o: lib/checkclass.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkclass.o $(libcppdir)/checkclass.cpp $(libcppdir)/checkcondition.o: lib/checkcondition.cpp lib/astutils.h lib/check.h lib/checkcondition.h lib/checkother.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkcondition.o $(libcppdir)/checkcondition.cpp $(libcppdir)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/check.h lib/checkexceptionsafety.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkexceptionsafety.o $(libcppdir)/checkexceptionsafety.cpp $(libcppdir)/checkfunctions.o: lib/checkfunctions.cpp lib/astutils.h lib/check.h lib/checkfunctions.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkfunctions.o $(libcppdir)/checkfunctions.cpp $(libcppdir)/checkinternal.o: lib/checkinternal.cpp lib/astutils.h lib/check.h lib/checkinternal.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkinternal.o $(libcppdir)/checkinternal.cpp $(libcppdir)/checkio.o: lib/checkio.cpp lib/check.h lib/checkio.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkio.o $(libcppdir)/checkio.cpp $(libcppdir)/checkleakautovar.o: lib/checkleakautovar.cpp lib/astutils.h lib/check.h lib/checkleakautovar.h lib/checkmemoryleak.h lib/checknullpointer.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkleakautovar.o $(libcppdir)/checkleakautovar.cpp $(libcppdir)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/astutils.h lib/check.h lib/checkmemoryleak.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkmemoryleak.o $(libcppdir)/checkmemoryleak.cpp $(libcppdir)/checknullpointer.o: lib/checknullpointer.cpp lib/astutils.h lib/check.h lib/checknullpointer.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checknullpointer.o $(libcppdir)/checknullpointer.cpp $(libcppdir)/checkother.o: lib/checkother.cpp lib/astutils.h lib/check.h lib/checkother.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkother.o $(libcppdir)/checkother.cpp $(libcppdir)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/check.h lib/checkpostfixoperator.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkpostfixoperator.o $(libcppdir)/checkpostfixoperator.cpp $(libcppdir)/checksizeof.o: lib/checksizeof.cpp lib/check.h lib/checksizeof.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checksizeof.o $(libcppdir)/checksizeof.cpp $(libcppdir)/checkstl.o: lib/checkstl.cpp lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkstl.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/pathanalysis.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkstl.o $(libcppdir)/checkstl.cpp $(libcppdir)/checkstring.o: lib/checkstring.cpp lib/astutils.h lib/check.h lib/checkstring.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkstring.o $(libcppdir)/checkstring.cpp $(libcppdir)/checktype.o: lib/checktype.cpp lib/check.h lib/checktype.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checktype.o $(libcppdir)/checktype.cpp $(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkuninitvar.o $(libcppdir)/checkuninitvar.cpp $(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkunusedfunctions.o $(libcppdir)/checkunusedfunctions.cpp $(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp externals/simplecpp/simplecpp.h lib/astutils.h lib/check.h lib/checkunusedvar.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkunusedvar.o $(libcppdir)/checkunusedvar.cpp $(libcppdir)/checkvaarg.o: lib/checkvaarg.cpp lib/astutils.h lib/check.h lib/checkvaarg.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkvaarg.o $(libcppdir)/checkvaarg.cpp $(libcppdir)/clangimport.o: lib/clangimport.cpp lib/clangimport.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/clangimport.o $(libcppdir)/clangimport.cpp $(libcppdir)/color.o: lib/color.cpp lib/color.h lib/config.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/color.o $(libcppdir)/color.cpp $(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/clangimport.h lib/color.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/cppcheck.o $(libcppdir)/cppcheck.cpp $(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/check.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/ctu.o $(libcppdir)/ctu.cpp $(libcppdir)/errorlogger.o: lib/errorlogger.cpp externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/errorlogger.o $(libcppdir)/errorlogger.cpp $(libcppdir)/errortypes.o: lib/errortypes.cpp lib/config.h lib/errortypes.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/errortypes.o $(libcppdir)/errortypes.cpp $(libcppdir)/exprengine.o: lib/exprengine.cpp lib/astutils.h lib/bughuntingchecks.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/exprengine.o $(libcppdir)/exprengine.cpp $(libcppdir)/forwardanalyzer.o: lib/forwardanalyzer.cpp lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/forwardanalyzer.o $(libcppdir)/forwardanalyzer.cpp $(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson/picojson.h externals/tinyxml2/tinyxml2.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/importproject.o $(libcppdir)/importproject.cpp $(libcppdir)/infer.o: lib/infer.cpp lib/calculate.h lib/config.h lib/errortypes.h lib/infer.h lib/mathlib.h lib/utils.h lib/valueflow.h lib/valueptr.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/infer.o $(libcppdir)/infer.cpp $(libcppdir)/library.o: lib/library.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/library.o $(libcppdir)/library.cpp $(libcppdir)/mathlib.o: lib/mathlib.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errortypes.h lib/mathlib.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/mathlib.o $(libcppdir)/mathlib.cpp $(libcppdir)/path.o: lib/path.cpp externals/simplecpp/simplecpp.h lib/config.h lib/path.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/path.o $(libcppdir)/path.cpp $(libcppdir)/pathanalysis.o: lib/pathanalysis.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathanalysis.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/pathanalysis.o $(libcppdir)/pathanalysis.cpp $(libcppdir)/pathmatch.o: lib/pathmatch.cpp lib/config.h lib/path.h lib/pathmatch.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/pathmatch.o $(libcppdir)/pathmatch.cpp $(libcppdir)/platform.o: lib/platform.cpp externals/tinyxml2/tinyxml2.h lib/config.h lib/path.h lib/platform.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/platform.o $(libcppdir)/platform.cpp $(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/preprocessor.o $(libcppdir)/preprocessor.cpp $(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/calculate.h lib/config.h lib/errortypes.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/programmemory.o $(libcppdir)/programmemory.cpp $(libcppdir)/reverseanalyzer.o: lib/reverseanalyzer.cpp lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/reverseanalyzer.o $(libcppdir)/reverseanalyzer.cpp $(libcppdir)/settings.o: lib/settings.cpp externals/picojson/picojson.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/timer.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/settings.o $(libcppdir)/settings.cpp $(libcppdir)/summaries.o: lib/summaries.cpp lib/analyzerinfo.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/summaries.o $(libcppdir)/summaries.cpp $(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/mathlib.h lib/path.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/suppressions.o $(libcppdir)/suppressions.cpp $(libcppdir)/symboldatabase.o: lib/symboldatabase.cpp lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/symboldatabase.o $(libcppdir)/symboldatabase.cpp $(libcppdir)/templatesimplifier.o: lib/templatesimplifier.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/templatesimplifier.o $(libcppdir)/templatesimplifier.cpp $(libcppdir)/timer.o: lib/timer.cpp lib/config.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/timer.o $(libcppdir)/timer.cpp $(libcppdir)/token.o: lib/token.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/tokenrange.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/token.o $(libcppdir)/token.cpp $(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/tokenize.o $(libcppdir)/tokenize.cpp $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/tokenlist.o $(libcppdir)/tokenlist.cpp $(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/utils.o $(libcppdir)/utils.cpp $(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/valueflow.o $(libcppdir)/valueflow.cpp cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp cli/filelister.o: cli/filelister.cpp cli/filelister.h lib/config.h lib/path.h lib/pathmatch.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/filelister.o cli/filelister.cpp cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/main.o cli/main.cpp cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/threadexecutor.o cli/threadexecutor.cpp test/options.o: test/options.cpp test/options.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/options.o test/options.cpp test/test64bit.o: test/test64bit.cpp lib/check.h lib/check64bit.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/test64bit.o test/test64bit.cpp test/testassert.o: test/testassert.cpp lib/check.h lib/checkassert.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testassert.o test/testassert.cpp test/testastutils.o: test/testastutils.cpp lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testastutils.o test/testastutils.cpp test/testautovariables.o: test/testautovariables.cpp lib/check.h lib/checkautovariables.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testautovariables.o test/testautovariables.cpp test/testbool.o: test/testbool.cpp lib/check.h lib/checkbool.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testbool.o test/testbool.cpp test/testboost.o: test/testboost.cpp lib/check.h lib/checkboost.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testboost.o test/testboost.cpp test/testbufferoverrun.o: test/testbufferoverrun.cpp externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/check.h lib/checkbufferoverrun.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testbufferoverrun.o test/testbufferoverrun.cpp test/testbughuntingchecks.o: test/testbughuntingchecks.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testbughuntingchecks.o test/testbughuntingchecks.cpp test/testcharvar.o: test/testcharvar.cpp lib/check.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcharvar.o test/testcharvar.cpp test/testclangimport.o: test/testclangimport.cpp lib/clangimport.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testclangimport.o test/testclangimport.cpp test/testclass.o: test/testclass.cpp externals/tinyxml2/tinyxml2.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testclass.o test/testclass.cpp test/testcmdlineparser.o: test/testcmdlineparser.cpp cli/cmdlineparser.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/redirect.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcmdlineparser.o test/testcmdlineparser.cpp test/testcondition.o: test/testcondition.cpp externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/check.h lib/checkcondition.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcondition.o test/testcondition.cpp test/testconstructors.o: test/testconstructors.cpp lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testconstructors.o test/testconstructors.cpp test/testcppcheck.o: test/testcppcheck.cpp lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcppcheck.o test/testcppcheck.cpp test/testerrorlogger.o: test/testerrorlogger.cpp externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testerrorlogger.o test/testerrorlogger.cpp test/testexceptionsafety.o: test/testexceptionsafety.cpp lib/check.h lib/checkexceptionsafety.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testexceptionsafety.o test/testexceptionsafety.cpp test/testexprengine.o: test/testexprengine.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testexprengine.o test/testexprengine.cpp test/testfilelister.o: test/testfilelister.cpp cli/filelister.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/pathmatch.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testfilelister.o test/testfilelister.cpp test/testfunctions.o: test/testfunctions.cpp externals/tinyxml2/tinyxml2.h lib/check.h lib/checkfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testfunctions.o test/testfunctions.cpp test/testgarbage.o: test/testgarbage.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testgarbage.o test/testgarbage.cpp test/testimportproject.o: test/testimportproject.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testimportproject.o test/testimportproject.cpp test/testincompletestatement.o: test/testincompletestatement.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testincompletestatement.o test/testincompletestatement.cpp test/testinternal.o: test/testinternal.cpp lib/check.h lib/checkinternal.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testinternal.o test/testinternal.cpp test/testio.o: test/testio.cpp lib/check.h lib/checkio.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testio.o test/testio.cpp test/testleakautovar.o: test/testleakautovar.cpp externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/check.h lib/checkleakautovar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testleakautovar.o test/testleakautovar.cpp test/testlibrary.o: test/testlibrary.cpp externals/tinyxml2/tinyxml2.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testlibrary.o test/testlibrary.cpp test/testmathlib.o: test/testmathlib.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/mathlib.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testmathlib.o test/testmathlib.cpp test/testmemleak.o: test/testmemleak.cpp lib/check.h lib/checkmemoryleak.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testmemleak.o test/testmemleak.cpp test/testnullpointer.o: test/testnullpointer.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checknullpointer.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testnullpointer.o test/testnullpointer.cpp test/testoptions.o: test/testoptions.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h test/options.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testoptions.o test/testoptions.cpp test/testother.o: test/testother.cpp externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/check.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testother.o test/testother.cpp test/testpath.o: test/testpath.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/path.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpath.o test/testpath.cpp test/testpathmatch.o: test/testpathmatch.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/pathmatch.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpathmatch.o test/testpathmatch.cpp test/testplatform.o: test/testplatform.cpp externals/tinyxml2/tinyxml2.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/platform.h lib/suppressions.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testplatform.o test/testplatform.cpp test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/check.h lib/checkpostfixoperator.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpostfixoperator.o test/testpostfixoperator.cpp test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpreprocessor.o test/testpreprocessor.cpp test/testrunner.o: test/testrunner.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/preprocessor.h lib/suppressions.h test/options.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testrunner.o test/testrunner.cpp test/testsimplifytemplate.o: test/testsimplifytemplate.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytemplate.o test/testsimplifytemplate.cpp test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytokens.o test/testsimplifytokens.cpp test/testsimplifytypedef.o: test/testsimplifytypedef.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytypedef.o test/testsimplifytypedef.cpp test/testsimplifyusing.o: test/testsimplifyusing.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifyusing.o test/testsimplifyusing.cpp test/testsizeof.o: test/testsizeof.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checksizeof.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsizeof.o test/testsizeof.cpp test/teststl.o: test/teststl.cpp lib/check.h lib/checkstl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/teststl.o test/teststl.cpp test/teststring.o: test/teststring.cpp lib/check.h lib/checkstring.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/teststring.o test/teststring.cpp test/testsuite.o: test/testsuite.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h test/options.h test/redirect.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuite.o test/testsuite.cpp test/testsummaries.o: test/testsummaries.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsummaries.o test/testsummaries.cpp test/testsuppressions.o: test/testsuppressions.cpp cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuppressions.o test/testsuppressions.cpp test/testsymboldatabase.o: test/testsymboldatabase.cpp externals/tinyxml2/tinyxml2.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsymboldatabase.o test/testsymboldatabase.cpp test/testthreadexecutor.o: test/testthreadexecutor.cpp cli/threadexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testthreadexecutor.o test/testthreadexecutor.cpp test/testtimer.o: test/testtimer.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h lib/timer.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtimer.o test/testtimer.cpp test/testtoken.o: test/testtoken.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtoken.o test/testtoken.cpp test/testtokenize.o: test/testtokenize.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtokenize.o test/testtokenize.cpp test/testtokenlist.o: test/testtokenlist.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtokenlist.o test/testtokenlist.cpp test/testtokenrange.o: test/testtokenrange.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/tokenrange.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtokenrange.o test/testtokenrange.cpp test/testtype.o: test/testtype.cpp lib/check.h lib/checktype.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtype.o test/testtype.cpp test/testuninitvar.o: test/testuninitvar.cpp lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testuninitvar.o test/testuninitvar.cpp test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedfunctions.o test/testunusedfunctions.cpp test/testunusedprivfunc.o: test/testunusedprivfunc.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedprivfunc.o test/testunusedprivfunc.cpp test/testunusedvar.o: test/testunusedvar.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkunusedvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedvar.o test/testunusedvar.cpp test/testutils.o: test/testutils.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testutils.o test/testutils.cpp test/testvaarg.o: test/testvaarg.cpp lib/check.h lib/checkvaarg.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvaarg.o test/testvaarg.cpp test/testvalueflow.o: test/testvalueflow.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvalueflow.o test/testvalueflow.cpp test/testvarid.o: test/testvarid.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvarid.o test/testvarid.cpp externals/simplecpp/simplecpp.o: externals/simplecpp/simplecpp.cpp externals/simplecpp/simplecpp.h $(CXX) $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) -w $(UNDEF_STRICT_ANSI) -c -o externals/simplecpp/simplecpp.o externals/simplecpp/simplecpp.cpp externals/tinyxml2/tinyxml2.o: externals/tinyxml2/tinyxml2.cpp externals/tinyxml2/tinyxml2.h $(CXX) $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) -w $(UNDEF_STRICT_ANSI) -c -o externals/tinyxml2/tinyxml2.o externals/tinyxml2/tinyxml2.cpp tools/dmake.o: tools/dmake.cpp cli/filelister.h lib/config.h lib/pathmatch.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o tools/dmake.o tools/dmake.cpp tools/generate_cfg_tests.o: tools/generate_cfg_tests.cpp externals/tinyxml2/tinyxml2.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o tools/generate_cfg_tests.o tools/generate_cfg_tests.cpp cppcheck-2.7/addons/000077500000000000000000000000001417746362400144305ustar00rootroot00000000000000cppcheck-2.7/addons/README.md000066400000000000000000000045441417746362400157160ustar00rootroot00000000000000# Cppcheck addons Addons are scripts that analyses Cppcheck dump files to check compatibility with secure coding standards and to locate various issues. ## Supported addons + [cert.py](https://github.com/danmar/cppcheck/blob/main/addons/cert.py) Checks for compliance with the safe programming standard [CERT](http://www.cert.org/secure-coding/). + [misra.py](https://github.com/danmar/cppcheck/blob/main/addons/misra.py) Used to verify compliance with MISRA C 2012 - a proprietary set of guidelines to avoid such questionable code, developed for embedded systems. Since this standard is proprietary, cppcheck does not display error text by specifying only the number of violated rules (for example, [c2012-21.3]). If you want to display full texts for violated rules, you will need to create a text file containing MISRA rules, which you will have to pass when calling the script with `--rule-texts` key. Some examples of rule texts files available in [tests directory](https://github.com/danmar/cppcheck/blob/main/addons/test/misra/). + [y2038.py](https://github.com/danmar/cppcheck/blob/main/addons/y2038.py) Checks Linux system for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/main/addons/doc/y2038.txt). + [threadsafety.py](https://github.com/danmar/cppcheck/blob/main/addons/threadsafety.py) Analyse Cppcheck dump files to locate threadsafety issues like static local objects used by multiple threads. ## Usage ### Command line interface ```bash cppcheck --addon=cert --addon=y2038 src/test.c ``` It is also possible to call scripts as follows: ```bash cppcheck --dump --quiet src/test.c python cert.py src/test.c.dump python misra.py --rule-texts=~/misra_rules.txt src/test.c.dump ``` This allows you to add additional parameters when calling the script (for example, `--rule-texts` for `misra.py`). The full list of available parameters can be found by calling any script with the `--help` flag. ### GUI When using the graphical interface `cppcheck-gui`, the selection and configuration of addons is carried out on the tab `Addons and tools` in the project settings (`Edit Project File`): ![Screenshot](https://raw.githubusercontent.com/danmar/cppcheck/main/addons/doc/img/cppcheck-gui-addons.png) cppcheck-2.7/addons/ROS_naming.json000066400000000000000000000021641417746362400173220ustar00rootroot00000000000000{ "RE_FILE": {".*[A-Z]": [true, "under_scored"]}, "RE_NAMESPACE": {".*[A-Z]": [true, "under_scored"], ".*\\_$": [true, "under_scored"]}, "RE_FUNCTIONNAME": {".*\\_": [true, "camelCase"], ".*^[a-z]": [false, "camelCase"]}, "RE_CLASS_NAME": {".*^[A-Z]": [false, "CamelCase"], ".*\\_": [true, "CamelCase"]}, "RE_GLOBAL_VARNAME": {".*^([g]\\_)": [false, "g_under_scored"], ".*[A-Z]": [true, "g_under_scored"], ".*\\_$": [true, "g_under_scored"]}, "RE_VARNAME": {".*^([g]\\_)": [true, "under_scored"], ".*[A-Z]": [true, "under_scored"], ".*\\_$": [true, "under_scored"]}, "RE_PRIVATE_MEMBER_VARIABLE": {".*\\_$": [false, "under_scored_"], ".*[A-Z]": [true, "under_scored_"]}, "RE_PUBLIC_MEMBER_VARIABLE": {".*\\_$": [false, "under_scored_"], ".*[A-Z]": [true, "under_scored_"]}, "var_prefixes": {"uint32_t": "ui32", "int*": "intp"}, "function_prefixes": {"uint16_t": "ui16", "uint32_t": "ui32"}, "skip_one_char_variables": false }cppcheck-2.7/addons/__init__.py000066400000000000000000000000001417746362400165270ustar00rootroot00000000000000cppcheck-2.7/addons/cert.py000077500000000000000000000401361417746362400157460ustar00rootroot00000000000000#!/usr/bin/env python3 # # Cert: Some extra CERT checkers # # Cppcheck itself handles many CERT rules. Cppcheck warns when there is undefined behaviour. # CERT Homepage: https://www.cert.org/secure-coding/ # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python cert.py main.cpp.dump import argparse import cppcheckdata import sys import re VERIFY = ('-verify' in sys.argv) VERIFY_EXPECTED = [] VERIFY_ACTUAL = [] def reportError(token, severity, msg, id): if VERIFY: VERIFY_ACTUAL.append(str(token.linenr) + ':cert-' + id) else: cppcheckdata.reportError(token, severity, msg, 'cert', id) def simpleMatch(token, pattern): return cppcheckdata.simpleMatch(token, pattern) def isUnpackedStruct(token): if token.valueType is None: return False if token.valueType.typeScope is None: return False if token.valueType.typeScope.type != "Struct": return False startToken = token.valueType.typeScope.bodyStart linenr = int(startToken.linenr) for line in open(startToken.file): linenr -= 1 if linenr == 0: return True if linenr < 3 and re.match(r'#pragma\s+pack\s*\(', line): return False return True def isLocalUnpackedStruct(arg): if arg and arg.str == '&' and not arg.astOperand2: arg = arg.astOperand1 return arg and arg.variable and (arg.variable.isLocal or arg.variable.isArgument) and isUnpackedStruct(arg) def isBitwiseOp(token): return token and (token.str in {'&', '|', '^'}) def isComparisonOp(token): return token and (token.str in {'==', '!=', '>', '>=', '<', '<='}) def isCast(expr): if not expr or expr.str != '(' or not expr.astOperand1 or expr.astOperand2: return False if simpleMatch(expr, '( )'): return False return True def isStandardFunction(token): if token.function: return False prev = token.previous if prev: if prev.str == '.': return False if prev.str == '::': prevprev = prev.previous if prevprev and not prevprev.str == 'std': return False return True # Is this a function call def isFunctionCall(token, function_names, number_of_arguments=None): if not token.isName: return False if token.str not in function_names: return False if (token.next is None) or token.next.str != '(' or token.next != token.astParent: return False if number_of_arguments is None: return True return len(cppcheckdata.getArguments(token)) == number_of_arguments # EXP05-C # do not attempt to cast away const def exp05(data): # TODO Reuse code in misra rule 11.8 for token in data.tokenlist: if isCast(token): # C-style cast if not token.valueType: continue if not token.astOperand1.valueType: continue if token.valueType.pointer == 0: continue if token.astOperand1.valueType.pointer == 0: continue const1 = token.valueType.constness const2 = token.astOperand1.valueType.constness if (const1 % 2) < (const2 % 2): reportError(token, 'style', "Attempt to cast away const", 'EXP05-C') elif token.str == '(' and token.astOperand1 and token.astOperand2 and token.astOperand1.function: function = token.astOperand1.function arguments = cppcheckdata.getArguments(token.previous) if not arguments: continue for argnr, argvar in function.argument.items(): if argnr < 1 or argnr > len(arguments): continue if not argvar.isPointer: continue if (argvar.constness % 2) == 1: # data is const continue argtok = arguments[argnr - 1] if not argtok.valueType: continue if argtok.valueType.pointer == 0: continue const2 = arguments[argnr - 1].valueType.constness if (const2 % 2) == 1: reportError(token, 'style', "Attempt to cast away const", 'EXP05-C') # EXP42-C # do not compare padding data def exp42(data): for token in data.tokenlist: if token.str != '(' or not token.astOperand1 or token.astOperand1.str != 'memcmp': continue arg1 = None arg2 = None if token.astOperand2 and token.astOperand2.str == ',': if token.astOperand2.astOperand1 and token.astOperand2.astOperand1.str == ',': arg1 = token.astOperand2.astOperand1.astOperand1 arg2 = token.astOperand2.astOperand1.astOperand2 if isLocalUnpackedStruct(arg1) or isLocalUnpackedStruct(arg2): reportError( token, 'style', "Comparison of struct padding data " + "(fix either by packing the struct using '#pragma pack' or by rewriting the comparison)", 'EXP42-C') # EXP15-C # Do not place a semicolon on the same line as an if, for or while statement def exp15(data): for scope in data.scopes: if scope.type in ('If', 'For', 'While'): token = scope.bodyStart.next if token.str==';' and token.linenr==scope.bodyStart.linenr: reportError(token, 'style', 'Do not place a semicolon on the same line as an IF, FOR or WHILE', 'EXP15-C') # EXP46-C # Do not use a bitwise operator with a Boolean-like operand # int x = (a == b) & c; def exp46(data): for token in data.tokenlist: if isBitwiseOp(token) and (isComparisonOp(token.astOperand1) or isComparisonOp(token.astOperand2)): reportError( token, 'style', 'Bitwise operator is used with a Boolean-like operand', 'EXP46-c') # INT31-C # Ensure that integer conversions do not result in lost or misinterpreted data def int31(data, platform): if not platform: return for token in data.tokenlist: to_value_type = None from_values = None action = '' if isCast(token): to_value_type = token.valueType from_values = token.astOperand1.values action = 'casting' elif token.str == '=' and token.astOperand1 and token.astOperand2: to_value_type = token.astOperand1.valueType from_values = token.astOperand2.values action = 'assign' else: continue if to_value_type is None or not from_values: continue bits = None if to_value_type.type == 'char': bits = platform.char_bit elif to_value_type.type == 'short': bits = platform.short_bit elif to_value_type.type == 'int': bits = platform.int_bit elif to_value_type.type == 'long': bits = platform.long_bit elif to_value_type.type == 'long long': bits = platform.long_long_bit else: continue if to_value_type.sign == 'unsigned': found = False for value in from_values: if value.intvalue and value.intvalue < 0: found = True reportError( token, 'style', 'Ensure that integer conversions do not result in lost or misinterpreted data (' + action + ' ' + str(value.intvalue) + ' to unsigned ' + token.valueType.type + ')', 'INT31-c') break if found: continue if bits >= 64: continue minval = 0 maxval = 1 if token.valueType.sign == 'signed': minval = -(1 << (bits - 1)) maxval = ((1 << (bits - 1)) - 1) else: minval = 0 maxval = ((1 << bits) - 1) for value in from_values: if value.intvalue and (value.intvalue < minval or value.intvalue > maxval): destType = '' if token.valueType.sign: destType = token.valueType.sign + ' ' + token.valueType.type else: destType = token.valueType.type reportError( token, 'style', 'Ensure that integer conversions do not result in lost or misinterpreted data (' + action + ' ' + str(value.intvalue) + ' to ' + destType + ')', 'INT31-c') break # ENV33-C # Do not call system() def env33(data): for token in data.tokenlist: if isFunctionCall(token, ('system',), 1): # Invalid syntax if not token.next.astOperand2: continue # ENV33-C-EX1: It is permissible to call system() with a null # pointer argument to determine the presence of a command processor # for the system. argValue = token.next.astOperand2.getValue(0) if argValue and argValue.intvalue == 0 and argValue.isKnown(): continue reportError(token, 'style', 'Do not call system()', 'ENV33-C') # MSC24-C # Do not use deprecated or obsolescent functions def msc24(data): for token in data.tokenlist: if isFunctionCall(token, ('asctime',), 1): reportError(token,'style','Do not use asctime() better use asctime_s()', 'MSC24-C') elif isFunctionCall(token, ('atof',), 1): reportError(token,'style','Do not use atof() better use strtod()', 'MSC24-C') elif isFunctionCall(token, ('atoi',), 1): reportError(token,'style','Do not use atoi() better use strtol()', 'MSC24-C') elif isFunctionCall(token, ('atol',), 1): reportError(token,'style','Do not use atol() better use strtol()', 'MSC24-C') elif isFunctionCall(token, ('atoll',), 1): reportError(token,'style','Do not use atoll() better use strtoll()', 'MSC24-C') elif isFunctionCall(token, ('ctime',), 1): reportError(token,'style','Do not use ctime() better use ctime_s()', 'MSC24-C') elif isFunctionCall(token, ('fopen',), 2): reportError(token,'style','Do not use fopen() better use fopen_s()', 'MSC24-C') elif isFunctionCall(token, ('freopen',), 3): reportError(token,'style','Do not use freopen() better use freopen_s()', 'MSC24-C') elif isFunctionCall(token, ('rewind',), 1): reportError(token,'style','Do not use rewind() better use fseek()', 'MSC24-C') elif isFunctionCall(token, ('setbuf',), 2): reportError(token,'style','Do not use setbuf() better use setvbuf()', 'MSC24-C') # MSC30-C # Do not use the rand() function for generating pseudorandom numbers def msc30(data): for token in data.tokenlist: if simpleMatch(token, "rand ( )") and isStandardFunction(token): reportError(token, 'style', 'Do not use the rand() function for generating pseudorandom numbers', 'MSC30-c') # STR03-C # Do not inadvertently truncate a string def str03(data): for token in data.tokenlist: if not isFunctionCall(token, 'strncpy'): continue arguments = cppcheckdata.getArguments(token) if len(arguments)!=3: continue if arguments[2].str=='(' and arguments[2].astOperand1.str=='sizeof': reportError(token, 'style', 'Do not inadvertently truncate a string', 'STR03-C') # STR05-C # Use pointers to const when referring to string literals def str05(data): for token in data.tokenlist: if token.isString: parent = token.astParent if parent is None: continue parentOp1 = parent.astOperand1 if parent.isAssignmentOp and parentOp1.valueType: if (parentOp1.valueType.type in ('char', 'wchar_t')) and parentOp1.valueType.pointer and not parentOp1.valueType.constness: reportError(parentOp1, 'style', 'Use pointers to const when referring to string literals', 'STR05-C') # STR07-C # Use the bounds-checking interfaces for string manipulation def str07(data): if data.standards.c=='c89' or data.standards.c=='c99': return for token in data.tokenlist: if not isFunctionCall(token, ('strcpy', 'strcat')): continue args = cppcheckdata.getArguments(token) if len(args)!=2: continue if args[1].isString: continue reportError(token, 'style', 'Use the bounds-checking interfaces %s_s()' % token.str, 'STR07-C') # STR11-C # Do not specify the bound of a character array initialized with a string literal def str11(data): for token in data.tokenlist: if not token.isString: continue strlen = token.strlen parent = token.astParent if parent is None: continue parentOp1 = parent.astOperand1 if parentOp1 is None or parentOp1.str!='[': continue if not parent.isAssignmentOp: continue varToken = parentOp1.astOperand1 if varToken is None or not varToken.isName: continue if varToken.variable is None: continue if varToken != varToken.variable.nameToken: continue valueToken = parentOp1.astOperand2 if valueToken is None: continue if valueToken.isNumber and int(valueToken.str)==strlen: reportError(valueToken, 'style', 'Do not specify the bound of a character array initialized with a string literal', 'STR11-C') # API01-C # Avoid laying out strings in memory directly before sensitive data def api01(data): for scope in data.scopes: if scope.type!='Struct': continue token = scope.bodyStart arrayFound=False # loop through the complete struct while token != scope.bodyEnd: if token.isName and token.variable: if token.variable.isArray: arrayFound=True elif arrayFound and not token.variable.isArray and not token.variable.isConst: reportError(token, 'style', 'Avoid laying out strings in memory directly before sensitive data', 'API01-C') # reset flags to report other positions in the same struct arrayFound=False token = token.next def get_args_parser(): parser = cppcheckdata.ArgumentParser() parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true") return parser if __name__ == '__main__': parser = get_args_parser() args = parser.parse_args() if args.verify: VERIFY = True if not args.dumpfile: if not args.quiet: print("no input files.") sys.exit(0) for dumpfile in args.dumpfile: if not args.quiet: print('Checking %s...' % dumpfile) data = cppcheckdata.CppcheckData(dumpfile) if VERIFY: VERIFY_ACTUAL = [] VERIFY_EXPECTED = [] for tok in data.rawTokens: if tok.str.startswith('//') and 'TODO' not in tok.str: for word in tok.str[2:].split(' '): if re.match(r'cert-[A-Z][A-Z][A-Z][0-9][0-9].*',word): VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word) for cfg in data.iterconfigurations(): if not args.quiet: print('Checking %s, config %s...' % (dumpfile, cfg.name)) exp05(cfg) exp42(cfg) exp46(cfg) exp15(cfg) int31(cfg, data.platform) str03(cfg) str05(cfg) str07(cfg) str11(cfg) env33(cfg) msc24(cfg) msc30(cfg) api01(cfg) if VERIFY: for expected in VERIFY_EXPECTED: if expected not in VERIFY_ACTUAL: print('Expected but not seen: ' + expected) sys.exit(1) for actual in VERIFY_ACTUAL: if actual not in VERIFY_EXPECTED: print('Not expected: ' + actual) sys.exit(1) sys.exit(cppcheckdata.EXIT_CODE) cppcheck-2.7/addons/cppcheck.py000066400000000000000000000020301417746362400165550ustar00rootroot00000000000000 import cppcheckdata, sys, os __checkers__ = [] def checker(f): __checkers__.append(f) return f __errorid__ = '' __addon_name__ = '' def reportError(location, severity, message, errorId=None): cppcheckdata.reportError(location, severity, message, __addon_name__, errorId or __errorid__) def runcheckers(): # If there are no checkers then don't run if len(__checkers__) == 0: return global __addon_name__ global __errorid__ addon = sys.argv[0] parser = cppcheckdata.ArgumentParser() args = parser.parse_args() __addon_name__ = os.path.splitext(os.path.basename(addon))[0] for dumpfile in args.dumpfile: if not args.quiet: print('Checking %s...' % dumpfile) data = cppcheckdata.CppcheckData(dumpfile) for cfg in data.iterconfigurations(): if not args.quiet: print('Checking %s, config %s...' % (dumpfile, cfg.name)) for c in __checkers__: __errorid__ = c.__name__ c(cfg, data) cppcheck-2.7/addons/cppcheckdata.doxyfile000066400000000000000000002250471417746362400206210ustar00rootroot00000000000000# Doxyfile 1.8.1.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "cppcheckdata" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = cppcheckdata.py # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = NO # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = NO cppcheck-2.7/addons/cppcheckdata.py000077500000000000000000001331671417746362400174320ustar00rootroot00000000000000""" cppcheckdata This is a Python module that helps you access Cppcheck dump data. License: No restrictions, use this as you need. """ import argparse import json import sys from xml.etree import ElementTree from fnmatch import fnmatch EXIT_CODE = 0 current_dumpfile_suppressions = [] def _load_location(location, element): """Load location from element/dict""" location.file = element.get('file') line = element.get('line') if line is None: line = element.get('linenr') if line is None: line = '0' location.linenr = int(line) location.column = int(element.get('column', '0')) class Location: """Utility location class""" file = None linenr = None column = None def __init__(self, element): _load_location(self, element) class Directive: """ Directive class. Contains information about each preprocessor directive in the source code. Attributes: str The directive line, with all C or C++ comments removed file Name of (possibly included) file where directive is defined linenr Line number in (possibly included) file where directive is defined To iterate through all directives use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: for directive in cfg.directives: print(directive.str) @endcode """ str = None file = None linenr = None column = None def __init__(self, element): self.str = element.get('str') _load_location(self, element) def __repr__(self): attrs = ["str", "file", "linenr"] return "{}({})".format( "Directive", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class MacroUsage: """ Tracks preprocessor macro usage """ name = None # Macro name file = None linenr = None column = None usefile = None uselinenr = None usecolumn = None def __init__(self, element): self.name = element.get('name') _load_location(self, element) self.usefile = element.get('usefile') self.useline = element.get('useline') self.usecolumn = element.get('usecolumn') def __repr__(self): attrs = ["name", "file", "linenr", "column", "usefile", "useline", "usecolumn"] return "{}({})".format( "MacroUsage", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class PreprocessorIfCondition: """ Information about #if/#elif conditions """ file = None linenr = None column = None E = None result = None def __init__(self, element): _load_location(self, element) self.E = element.get('E') self.result = int(element.get('result')) def __repr__(self): attrs = ["file", "linenr", "column", "E", "result"] return "{}({})".format( "PreprocessorIfCondition", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class ValueType: """ ValueType class. Contains (promoted) type information for each node in the AST. """ type = None sign = None bits = 0 constness = 0 pointer = 0 typeScopeId = None typeScope = None originalTypeName = None def __init__(self, element): self.type = element.get('valueType-type') self.sign = element.get('valueType-sign') bits = element.get('valueType-bits') if bits: self.bits = int(bits) self.typeScopeId = element.get('valueType-typeScope') self.originalTypeName = element.get('valueType-originalTypeName') constness = element.get('valueType-constness') if constness: self.constness = int(constness) else: self.constness = 0 pointer = element.get('valueType-pointer') if pointer: self.pointer = int(pointer) else: self.pointer = 0 def __repr__(self): attrs = ["type", "sign", "bits", "typeScopeId", "originalTypeName", "constness", "pointer"] return "{}({})".format( "ValueType", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.typeScope = IdMap[self.typeScopeId] def isIntegral(self): return self.type in {'bool', 'char', 'short', 'int', 'long', 'long long'} def isFloat(self): return self.type in {'float', 'double', 'long double'} def isEnum(self): return self.typeScope and self.typeScope.type == "Enum" class Token: """ Token class. Contains information about each token in the source code. The CppcheckData.tokenlist is a list of Token items C++ class: https://cppcheck.sourceforge.io/devinfo/doxyoutput/classToken.html Attributes: str Token string next Next token in tokenlist. For last token, next is None. previous Previous token in tokenlist. For first token, previous is None. link Linked token in tokenlist. Each '(', '[' and '{' are linked to the corresponding '}', ']' and ')'. For templates, the '<' is linked to the corresponding '>'. scope Scope information for this token. See the Scope class. isName Is this token a symbol name isNumber Is this token a number, for example 123, 12.34 isInt Is this token a int value such as 1234 isFloat Is this token a float value such as 12.34 isString Is this token a string literal such as "hello" strlen string length for string literal isChar Is this token a char literal such as 'x' isOp Is this token a operator isArithmeticalOp Is this token a arithmetic operator isAssignmentOp Is this token a assignment operator isComparisonOp Is this token a comparison operator isLogicalOp Is this token a logical operator: && || isUnsigned Is this token a unsigned type isSigned Is this token a signed type isExpandedMacro Is this token a expanded macro token isSplittedVarDeclComma Is this a comma changed to semicolon in a splitted variable declaration ('int a,b;' => 'int a; int b;') isSplittedVarDeclEq Is this a '=' changed to semicolon in a splitted variable declaration ('int a=5;' => 'int a; a=5;') isImplicitInt Is this token an implicit "int"? varId varId for token, each variable has a unique non-zero id variable Variable information for this token. See the Variable class. function If this token points at a function call, this attribute has the Function information. See the Function class. values Possible/Known values of token impossible_values Impossible values of token valueType type information typeScope type scope (token->type()->classScope) astParent ast parent astOperand1 ast operand1 astOperand2 ast operand2 file file name linenr line number column column To iterate through all tokens use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: code = '' for token in cfg.tokenlist: code = code + token.str + ' ' print(code) @endcode """ Id = None str = None next = None previous = None linkId = None link = None scopeId = None scope = None isName = False isNumber = False isInt = False isFloat = False isString = False strlen = None isChar = False isOp = False isArithmeticalOp = False isAssignmentOp = False isComparisonOp = False isLogicalOp = False isUnsigned = False isSigned = False isExpandedMacro = False isSplittedVarDeclComma = False isSplittedVarDeclEq = False isImplicitInt = False varId = None variableId = None variable = None functionId = None function = None valuesId = None values = None impossible_values = None valueType = None typeScopeId = None typeScope = None astParentId = None astParent = None astOperand1Id = None astOperand1 = None astOperand2Id = None astOperand2 = None file = None linenr = None column = None def __init__(self, element): self.Id = element.get('id') self.str = element.get('str') self.next = None self.previous = None self.scopeId = element.get('scope') self.scope = None type = element.get('type') if type == 'name': self.isName = True if element.get('isUnsigned'): self.isUnsigned = True if element.get('isSigned'): self.isSigned = True elif type == 'number': self.isNumber = True if element.get('isInt'): self.isInt = True elif element.get('isFloat'): self.isFloat = True elif type == 'string': self.isString = True self.strlen = int(element.get('strlen')) elif type == 'char': self.isChar = True elif type == 'op': self.isOp = True if element.get('isArithmeticalOp'): self.isArithmeticalOp = True elif element.get('isAssignmentOp'): self.isAssignmentOp = True elif element.get('isComparisonOp'): self.isComparisonOp = True elif element.get('isLogicalOp'): self.isLogicalOp = True if element.get('isExpandedMacro'): self.isExpandedMacro = True if element.get('isSplittedVarDeclComma'): self.isSplittedVarDeclComma = True if element.get('isSplittedVarDeclEq'): self.isSplittedVarDeclEq = True if element.get('isImplicitInt'): self.isImplicitInt = True self.linkId = element.get('link') self.link = None if element.get('varId'): self.varId = int(element.get('varId')) self.variableId = element.get('variable') self.variable = None self.functionId = element.get('function') self.function = None self.valuesId = element.get('values') self.values = None if element.get('valueType-type'): self.valueType = ValueType(element) else: self.valueType = None self.typeScopeId = element.get('type-scope') self.typeScope = None self.astParentId = element.get('astParent') self.astParent = None self.astOperand1Id = element.get('astOperand1') self.astOperand1 = None self.astOperand2Id = element.get('astOperand2') self.astOperand2 = None _load_location(self, element) def __repr__(self): attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned", "isNumber", "isInt", "isFloat", "isString", "strlen", "isChar", "isOp", "isArithmeticalOp", "isComparisonOp", "isLogicalOp", "isExpandedMacro", "isSplittedVarDeclComma", "isSplittedVarDeclEq", "isImplicitInt", "linkId", "varId", "variableId", "functionId", "valuesId", "valueType", "typeScopeId", "astParentId", "astOperand1Id", "file", "linenr", "column"] return "{}({})".format( "Token", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.scope = IdMap[self.scopeId] self.link = IdMap[self.linkId] self.variable = IdMap[self.variableId] self.function = IdMap[self.functionId] self.values = [] self.impossible_values = [] if IdMap[self.valuesId]: for v in IdMap[self.valuesId]: if v.isImpossible(): self.impossible_values.append(v) else: self.values.append(v) v.setId(IdMap) self.typeScope = IdMap[self.typeScopeId] self.astParent = IdMap[self.astParentId] self.astOperand1 = IdMap[self.astOperand1Id] self.astOperand2 = IdMap[self.astOperand2Id] if self.valueType: self.valueType.setId(IdMap) def getValue(self, v): """ Get value if it exists Returns None if it doesn't exist """ if not self.values: return None for value in self.values: if value.intvalue == v: return value return None def getKnownIntValue(self): """ If token has a known int value then return that. Otherwise returns None """ if not self.values: return None for value in self.values: if value.valueKind == 'known': return value.intvalue return None def isUnaryOp(self, op): return self.astOperand1 and (self.astOperand2 is None) and self.str == op def isBinaryOp(self): return self.astOperand1 and self.astOperand2 class Scope: """ Scope. Information about global scope, function scopes, class scopes, inner scopes, etc. C++ class: https://cppcheck.sourceforge.io/devinfo/doxyoutput/classScope.html Attributes bodyStart The { Token for this scope bodyEnd The } Token for this scope className Name of this scope. For a function scope, this is the function name; For a class scope, this is the class name. function If this scope belongs at a function call, this attribute has the Function information. See the Function class. type Type of scope: Global, Function, Class, If, While """ Id = None bodyStartId = None bodyStart = None bodyEndId = None bodyEnd = None className = None functionId = None function = None nestedInId = None nestedIn = None type = None isExecutable = None varlistId = None varlist = None def __init__(self, element): self.Id = element.get('id') self.className = element.get('className') self.functionId = element.get('function') self.function = None self.bodyStartId = element.get('bodyStart') self.bodyStart = None self.bodyEndId = element.get('bodyEnd') self.bodyEnd = None self.nestedInId = element.get('nestedIn') self.nestedIn = None self.type = element.get('type') self.isExecutable = (self.type in ('Function', 'If', 'Else', 'For', 'While', 'Do', 'Switch', 'Try', 'Catch', 'Unconditional', 'Lambda')) self.varlistId = list() self.varlist = list() def __repr__(self): attrs = ["Id", "className", "functionId", "bodyStartId", "bodyEndId", "nestedInId", "nestedIn", "type", "isExecutable"] return "{}({})".format( "Scope", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.bodyStart = IdMap[self.bodyStartId] self.bodyEnd = IdMap[self.bodyEndId] self.nestedIn = IdMap[self.nestedInId] self.function = IdMap[self.functionId] for v in self.varlistId: value = IdMap.get(v) if value: self.varlist.append(value) class Function: """ Information about a function C++ class: https://cppcheck.sourceforge.io/devinfo/doxyoutput/classFunction.html Attributes argument Argument list token Token in function implementation tokenDef Token in function definition isVirtual Is this function is virtual isImplicitlyVirtual Is this function is virtual this in the base classes isInlineKeyword Is inline keyword used isStatic Is this function static? """ Id = None argument = None argumentId = None token = None tokenId = None tokenDef = None tokenDefId = None name = None type = None isVirtual = None isImplicitlyVirtual = None isInlineKeyword = None isStatic = None nestedIn = None def __init__(self, element, nestedIn): self.Id = element.get('id') self.tokenId = element.get('token') self.tokenDefId = element.get('tokenDef') self.name = element.get('name') self.type = element.get('type') self.isImplicitlyVirtual = element.get('isImplicitlyVirtual', 'false') == 'true' self.isVirtual = element.get('isVirtual', 'false') == 'true' self.isInlineKeyword = element.get('isInlineKeyword', 'false') == 'true' self.isStatic = element.get('isStatic', 'false') == 'true' self.nestedIn = nestedIn self.argument = {} self.argumentId = {} def __repr__(self): attrs = ["Id", "tokenId", "tokenDefId", "name", "type", "isVirtual", "isImplicitlyVirtual", "isInlineKeyword", "isStatic", "argumentId"] return "{}({})".format( "Function", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): for argnr, argid in self.argumentId.items(): self.argument[argnr] = IdMap[argid] self.token = IdMap.get(self.tokenId, None) self.tokenDef = IdMap[self.tokenDefId] class Variable: """ Information about a variable C++ class: https://cppcheck.sourceforge.io/devinfo/doxyoutput/classVariable.html Attributes: nameToken Name token in variable declaration typeStartToken Start token of variable declaration typeEndToken End token of variable declaration access Global/Local/Namespace/Public/Protected/Public/Throw/Argument scope Variable scope isArgument Is this variable a function argument? isArray Is this variable an array? isClass Is this variable a class or struct? isConst Is this variable a const variable? isGlobal Is this variable a global variable? isExtern Is this variable an extern variable? isLocal Is this variable a local variable? isPointer Is this variable a pointer isReference Is this variable a reference isStatic Is this variable static? constness Variable constness (same encoding as ValueType::constness) """ Id = None nameTokenId = None nameToken = None typeStartTokenId = None typeStartToken = None typeEndTokenId = None typeEndToken = None access = None scopeId = None scope = None isArgument = False isArray = False isClass = False isConst = False isExtern = False isGlobal = False isLocal = False isPointer = False isReference = False isStatic = False constness = 0 def __init__(self, element): self.Id = element.get('id') self.nameTokenId = element.get('nameToken') self.nameToken = None self.typeStartTokenId = element.get('typeStartToken') self.typeStartToken = None self.typeEndTokenId = element.get('typeEndToken') self.typeEndToken = None self.access = element.get('access') self.scopeId = element.get('scope') self.scope = None self.isArgument = (self.access and self.access == 'Argument') self.isArray = element.get('isArray') == 'true' self.isClass = element.get('isClass') == 'true' self.isConst = element.get('isConst') == 'true' self.isGlobal = (self.access and self.access == 'Global') self.isExtern = element.get('isExtern') == 'true' self.isLocal = (self.access and self.access == 'Local') self.isPointer = element.get('isPointer') == 'true' self.isReference = element.get('isReference') == 'true' self.isStatic = element.get('isStatic') == 'true' self.constness = element.get('constness') if self.constness: self.constness = int(self.constness) def __repr__(self): attrs = ["Id", "nameTokenId", "typeStartTokenId", "typeEndTokenId", "access", "scopeId", "isArgument", "isArray", "isClass", "isConst", "isGlobal", "isExtern", "isLocal", "isPointer", "isReference", "isStatic", "constness", ] return "{}({})".format( "Variable", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.nameToken = IdMap[self.nameTokenId] self.typeStartToken = IdMap[self.typeStartTokenId] self.typeEndToken = IdMap[self.typeEndTokenId] self.scope = IdMap[self.scopeId] class TypedefInfo: """ TypedefInfo class -- information about typedefs """ name = None used = None file = None linenr = None column = None def __init__(self, element): self.name = element.get('name') _load_location(self, element) self.used = (element.get('used') == '1') class Value: """ Value class Attributes: intvalue integer value tokvalue token value floatvalue float value containerSize container size condition condition where this Value comes from valueKind 'known' or 'possible' inconclusive Is value inconclusive? """ intvalue = None tokvalue = None floatvalue = None containerSize = None condition = None valueKind = None inconclusive = False def isKnown(self): return self.valueKind and self.valueKind == 'known' def isPossible(self): return self.valueKind and self.valueKind == 'possible' def isImpossible(self): return self.valueKind and self.valueKind == 'impossible' def __init__(self, element): self.intvalue = element.get('intvalue') if self.intvalue: self.intvalue = int(self.intvalue) self._tokvalueId = element.get('tokvalue') self.floatvalue = element.get('floatvalue') self.containerSize = element.get('container-size') self.iteratorStart = element.get('iterator-start') self.iteratorEnd = element.get('iterator-end') self._lifetimeId = element.get('lifetime') self.lifetimeScope = element.get('lifetime-scope') self.lifetimeKind = element.get('lifetime-kind') self._symbolicId = element.get('symbolic') self.symbolicDelta = element.get('symbolic-delta') self.condition = element.get('condition-line') self.bound = element.get('bound') self.path = element.get('path') if self.condition: self.condition = int(self.condition) if element.get('known'): self.valueKind = 'known' elif element.get('possible'): self.valueKind = 'possible' elif element.get('impossible'): self.valueKind = 'impossible' if element.get('inconclusive'): self.inconclusive = True def setId(self, IdMap): self.tokvalue = IdMap.get(self._tokvalueId) self.lifetime = IdMap.get(self._lifetimeId) self.symbolic = IdMap.get(self._symbolicId) def __repr__(self): attrs = ["intvalue", "tokvalue", "floatvalue", "containerSize", "condition", "valueKind", "inconclusive"] return "{}({})".format( "Value", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class ValueFlow: """ ValueFlow::Value class Each possible value has a ValueFlow::Value item. Each ValueFlow::Value either has a intvalue or tokvalue C++ class: https://cppcheck.sourceforge.io/devinfo/doxyoutput/classValueFlow_1_1Value.html Attributes: values Possible values """ Id = None values = None def __init__(self, element): self.Id = element.get('id') self.values = [] def __repr__(self): attrs = ["Id", "values"] return "{}({})".format( "ValueFlow", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class Suppression: """ Suppression class This class contains a suppression entry to suppress a warning. Attributes errorId The id string of the error to suppress, can be a wildcard fileName The name of the file to suppress warnings for, can include wildcards lineNumber The number of the line to suppress warnings from, can be 0 to represent any line symbolName The name of the symbol to match warnings for, can include wildcards """ errorId = None fileName = None lineNumber = None symbolName = None def __init__(self, element): self.errorId = element.get('errorId') self.fileName = element.get('fileName') self.lineNumber = element.get('lineNumber') self.symbolName = element.get('symbolName') def __repr__(self): attrs = ['errorId' , "fileName", "lineNumber", "symbolName"] return "{}({})".format( "Suppression", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def isMatch(self, file, line, message, errorId): if ((self.fileName is None or fnmatch(file, self.fileName)) and (self.lineNumber is None or int(line) == int(self.lineNumber)) and (self.symbolName is None or fnmatch(message, '*'+self.symbolName+'*')) and fnmatch(errorId, self.errorId)): return True return False class Configuration: """ Configuration class This class contains the directives, tokens, scopes, functions, variables, value flows, and suppressions for one configuration. Attributes: name Name of the configuration, "" for default directives List of Directive items macro_usage List of used macros preprocessor_if_conditions List of preprocessor if conditions that was evaluated during preprocessing tokenlist List of Token items scopes List of Scope items functions List of Function items variables List of Variable items valueflow List of ValueFlow values standards List of Standards values """ name = '' directives = [] macro_usage = [] preprocessor_if_conditions = [] tokenlist = [] scopes = [] functions = [] variables = [] typedefInfo = [] valueflow = [] standards = None clang_warnings = [] def __init__(self, name): self.name = name self.directives = [] self.macro_usage = [] self.preprocessor_if_conditions = [] self.tokenlist = [] self.scopes = [] self.functions = [] self.variables = [] self.typedefInfo = [] self.valueflow = [] self.standards = Standards() self.clang_warnings = [] def set_tokens_links(self): """Set next/previous links between tokens.""" prev = None for token in self.tokenlist: token.previous = prev if prev: prev.next = token prev = token def set_id_map(self, arguments): IdMap = {None: None, '0': None, '00000000': None, '0000000000000000': None, '0x0': None} for token in self.tokenlist: IdMap[token.Id] = token for scope in self.scopes: IdMap[scope.Id] = scope for function in self.functions: IdMap[function.Id] = function for variable in self.variables: IdMap[variable.Id] = variable for variable in arguments: IdMap[variable.Id] = variable for values in self.valueflow: IdMap[values.Id] = values.values for token in self.tokenlist: token.setId(IdMap) for scope in self.scopes: scope.setId(IdMap) for function in self.functions: function.setId(IdMap) for variable in self.variables: variable.setId(IdMap) for variable in arguments: variable.setId(IdMap) def setIdMap(self, functions_arguments): """Set relationships between objects stored in this configuration. :param functions_arguments: List of Variable objects which are function arguments """ self.set_tokens_links() self.set_id_map(functions_arguments) class Platform: """ Platform class This class contains type sizes Attributes: name Name of the platform char_bit CHAR_BIT value short_bit SHORT_BIT value int_bit INT_BIT value long_bit LONG_BIT value long_long_bit LONG_LONG_BIT value pointer_bit POINTER_BIT value """ name = '' char_bit = 0 short_bit = 0 int_bit = 0 long_bit = 0 long_long_bit = 0 pointer_bit = 0 def __init__(self, platformnode): self.name = platformnode.get('name') self.char_bit = int(platformnode.get('char_bit')) self.short_bit = int(platformnode.get('short_bit')) self.int_bit = int(platformnode.get('int_bit')) self.long_bit = int(platformnode.get('long_bit')) self.long_long_bit = int(platformnode.get('long_long_bit')) self.pointer_bit = int(platformnode.get('pointer_bit')) def __repr__(self): attrs = ["name", "char_bit", "short_bit", "int_bit", "long_bit", "long_long_bit", "pointer_bit"] return "{}({})".format( "Platform", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class Standards: """ Standards class This class contains versions of standards that were used for the cppcheck Attributes: c C Standard used cpp C++ Standard used posix If Posix was used """ c = "" cpp = "" posix = False def set_c(self, node): self.c = node.get("version") def set_cpp(self, node): self.cpp = node.get("version") def set_posix(self, node): self.posix = node.get("posix") is not None def __repr__(self): attrs = ["c", "cpp", "posix"] return "{}({})".format( "Standards", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class CppcheckData: """ Class that makes cppcheck dump data available Contains a list of Configuration instances Attributes: filename Path to Cppcheck dump file rawTokens List of rawToken elements suppressions List of Suppressions files Source files for elements occurred in this configuration To iterate through all configurations use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: print('cfg: ' + cfg.name) @endcode To iterate through all tokens in each configuration use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: print('cfg: ' + cfg.name) code = '' for token in cfg.tokenlist: code = code + token.str + ' ' print(' ' + code) @endcode To iterate through all scopes (functions, types, etc) use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: print('cfg: ' + cfg.name) for scope in cfg.scopes: print(' type:' + scope.type + ' name:' + scope.className) @endcode """ def __init__(self, filename): """ :param filename: Path to Cppcheck dump file """ self.filename = filename self.rawTokens = [] self.platform = None self.suppressions = [] self.files = [] platform_done = False rawtokens_done = False suppressions_done = False # Parse general configuration options from node # We intentionally don't clean node resources here because we # want to serialize in memory only small part of the XML tree. for event, node in ElementTree.iterparse(self.filename, events=('start', 'end')): if platform_done and rawtokens_done and suppressions_done: break if node.tag == 'platform' and event == 'start': self.platform = Platform(node) platform_done = True elif node.tag == 'rawtokens' and event == 'end': for rawtokens_node in node: if rawtokens_node.tag == 'file': self.files.append(rawtokens_node.get('name')) elif rawtokens_node.tag == 'tok': tok = Token(rawtokens_node) tok.file = self.files[int(rawtokens_node.get('fileIndex'))] self.rawTokens.append(tok) rawtokens_done = True elif node.tag == 'suppressions' and event == 'end': for suppressions_node in node: self.suppressions.append(Suppression(suppressions_node)) suppressions_done = True global current_dumpfile_suppressions current_dumpfile_suppressions = self.suppressions # Set links between rawTokens. for i in range(len(self.rawTokens)-1): self.rawTokens[i+1].previous = self.rawTokens[i] self.rawTokens[i].next = self.rawTokens[i+1] @property def configurations(self): """ Return the list of all available Configuration objects. """ return list(self.iterconfigurations()) def iterconfigurations(self): """ Create and return iterator for the available Configuration objects. The iterator loops over all Configurations in the dump file tree, in document order. """ cfg = None cfg_arguments = [] # function arguments for Configuration node initialization cfg_function = None cfg_valueflow = None # Iterating in a . iter_scope_varlist = False # Iterating iter_typedef_info = False # Use iterable objects to traverse XML tree for dump files incrementally. # Iterative approach is required to avoid large memory consumption. # Calling .clear() is necessary to let the element be garbage collected. for event, node in ElementTree.iterparse(self.filename, events=('start', 'end')): # Serialize new configuration node if node.tag == 'dump': if event == 'start': cfg = Configuration(node.get('cfg')) continue elif event == 'end': cfg.setIdMap(cfg_arguments) yield cfg cfg = None cfg_arguments = [] elif node.tag == 'clang-warning' and event == 'start': cfg.clang_warnings.append({'file': node.get('file'), 'line': int(node.get('line')), 'column': int(node.get('column')), 'message': node.get('message')}) # Parse standards elif node.tag == "standards" and event == 'start': continue elif node.tag == 'c' and event == 'start': cfg.standards.set_c(node) elif node.tag == 'cpp' and event == 'start': cfg.standards.set_cpp(node) elif node.tag == 'posix' and event == 'start': cfg.standards.set_posix(node) # Parse directives list elif node.tag == 'directive' and event == 'start': cfg.directives.append(Directive(node)) # Parse macro usage elif node.tag == 'macro' and event == 'start': cfg.macro_usage.append(MacroUsage(node)) # Preprocessor #if/#elif condition elif node.tag == "if-cond" and event == 'start': cfg.preprocessor_if_conditions.append(PreprocessorIfCondition(node)) # Parse tokens elif node.tag == 'tokenlist' and event == 'start': continue elif node.tag == 'token' and event == 'start': cfg.tokenlist.append(Token(node)) # Parse scopes elif node.tag == 'scopes' and event == 'start': continue elif node.tag == 'scope' and event == 'start': cfg.scopes.append(Scope(node)) elif node.tag == 'varlist': if event == 'start': iter_scope_varlist = True elif event == 'end': iter_scope_varlist = False # Parse functions elif node.tag == 'functionList' and event == 'start': continue elif node.tag == 'function': if event == 'start': cfg_function = Function(node, cfg.scopes[-1]) continue elif event == 'end': cfg.functions.append(cfg_function) cfg_function = None # Parse function arguments elif node.tag == 'arg' and event == 'start': arg_nr = int(node.get('nr')) arg_variable_id = node.get('variable') cfg_function.argumentId[arg_nr] = arg_variable_id # Parse variables elif node.tag == 'var' and event == 'start': if iter_scope_varlist: cfg.scopes[-1].varlistId.append(node.get('id')) else: var = Variable(node) if var.nameTokenId: cfg.variables.append(var) else: cfg_arguments.append(var) # Parse typedef info elif node.tag == 'typedef-info': iter_typedef_info = (event == 'start') elif iter_typedef_info and node.tag == 'info' and event == 'start': cfg.typedefInfo.append(TypedefInfo(node)) # Parse valueflows (list of values) elif node.tag == 'valueflow' and event == 'start': continue elif node.tag == 'values': if event == 'start': cfg_valueflow = ValueFlow(node) continue elif event == 'end': cfg.valueflow.append(cfg_valueflow) cfg_valueflow = None # Parse values elif node.tag == 'value' and event == 'start': cfg_valueflow.values.append(Value(node)) # Remove links to the sibling nodes node.clear() def __repr__(self): attrs = ["configurations", "platform"] return "{}({})".format( "CppcheckData", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) # Get function arguments def getArgumentsRecursive(tok, arguments): if tok is None: return if tok.str == ',': getArgumentsRecursive(tok.astOperand1, arguments) getArgumentsRecursive(tok.astOperand2, arguments) else: arguments.append(tok) def getArguments(ftok): if (not ftok.isName) or (ftok.next is None) or ftok.next.str != '(': return None args = [] getArgumentsRecursive(ftok.next.astOperand2, args) return args def parsedump(filename): """ parse a cppcheck dump file """ return CppcheckData(filename) def astIsFloat(token): """ Check if type of ast node is float/double """ if not token: return False if token.str == '.': return astIsFloat(token.astOperand2) if token.str in '+-*/%': return astIsFloat(token.astOperand1) or astIsFloat(token.astOperand2) if not token.variable: # float literal? if token.str[0].isdigit(): for c in token.str: if c == 'f' or c == '.' or c == 'E': return True return False typeToken = token.variable.typeStartToken endToken = token.variable.typeEndToken while typeToken != endToken: if typeToken.str == 'float' or typeToken.str == 'double': return True typeToken = typeToken.next if typeToken.str == 'float' or typeToken.str == 'double': return True return False class CppCheckFormatter(argparse.HelpFormatter): """ Properly formats multiline argument helps """ def _split_lines(self, text, width): # this is the RawTextHelpFormatter._split_lines if text.startswith('R|'): return text[2:].splitlines() return argparse.HelpFormatter._split_lines(self, text, width) def ArgumentParser(): """ Returns an argparse argument parser with an already-added argument definition for -t/--template """ parser = argparse.ArgumentParser(formatter_class=CppCheckFormatter) parser.add_argument('-t', '--template', metavar='', default='{callstack}: ({severity}) {message}', help="R|Format the error messages. E.g.\n" "'{file}:{line},{severity},{id},{message}' or\n" "'{file}({line}):({severity}) {message}' or\n" "'{callstack} {message}'\n" "Pre-defined templates: gcc, vs, edit") parser.add_argument("dumpfile", nargs='*', help="Path of dump files from cppcheck.") parser.add_argument("--cli", help="Addon is executed from Cppcheck", action="store_true") parser.add_argument("--file-list", metavar='', default=None, help="file list in a text file") parser.add_argument("-q", "--quiet", help='do not print "Checking ..." lines', action="store_true") return parser def get_files(args): """Return dump_files, ctu_info_files""" all_files = args.dumpfile if args.file_list: with open(args.file_list, 'rt') as f: for line in f.readlines(): all_files.append(line.rstrip()) dump_files = [] ctu_info_files = [] for f in all_files: if f.endswith('.ctu-info'): ctu_info_files.append(f) else: dump_files.append(f) return dump_files, ctu_info_files def simpleMatch(token, pattern): for p in pattern.split(' '): if not token or token.str != p: return False token = token.next return True def get_function_call_name_args(token): """Get function name and arguments for function call name, args = get_function_call_name_args(tok) """ if token is None: return None, None if not token.isName or not token.scope.isExecutable: return None, None if not simpleMatch(token.next, '('): return None, None if token.function: nametok = token.function.token if nametok is None: nametok = token.function.tokenDef if token in (token.function.token, token.function.tokenDef): return None, None name = nametok.str while nametok.previous and nametok.previous.previous and nametok.previous.str == '::' and nametok.previous.previous.isName: name = nametok.previous.previous.str + '::' + name nametok = nametok.previous.previous scope = token.function.nestedIn while scope: if scope.className: name = scope.className + '::' + name scope = scope.nestedIn else: nametok = token name = nametok.str while nametok.previous and nametok.previous.previous and nametok.previous.str == '::' and nametok.previous.previous.isName: name = nametok.previous.previous.str + '::' + name nametok = nametok.previous.previous return name, getArguments(token) def is_suppressed(location, message, errorId): for suppression in current_dumpfile_suppressions: if suppression.isMatch(location.file, location.linenr, message, errorId): return True return False def reportError(location, severity, message, addon, errorId, extra=''): if '--cli' in sys.argv: msg = { 'file': location.file, 'linenr': location.linenr, 'column': location.column, 'severity': severity, 'message': message, 'addon': addon, 'errorId': errorId, 'extra': extra} sys.stdout.write(json.dumps(msg) + '\n') else: if is_suppressed(location, message, '%s-%s' % (addon, errorId)): return loc = '[%s:%i]' % (location.file, location.linenr) if len(extra) > 0: message += ' (' + extra + ')' sys.stderr.write('%s (%s) %s [%s-%s]\n' % (loc, severity, message, addon, errorId)) global EXIT_CODE EXIT_CODE = 1 def reportSummary(dumpfile, summary_type, summary_data): # dumpfile ends with ".dump" ctu_info_file = dumpfile[:-4] + "ctu-info" with open(ctu_info_file, 'at') as f: msg = {'summary': summary_type, 'data': summary_data} f.write(json.dumps(msg) + '\n') cppcheck-2.7/addons/doc/000077500000000000000000000000001417746362400151755ustar00rootroot00000000000000cppcheck-2.7/addons/doc/img/000077500000000000000000000000001417746362400157515ustar00rootroot00000000000000cppcheck-2.7/addons/doc/img/cppcheck-gui-addons.png000066400000000000000000000561351417746362400223010ustar00rootroot00000000000000PNG  IHDR4觿 pHYs+ IDATxy\Lgi3մLMEP.QiB咾_7pzu* ݒ,mJ6?jܩ~yy99gg3||~Z[[;ځ!"""JJJȷ#@.D^^h2椥!&L0ځ|#TRR8aaaaaaYY1r"Ҍ0lu٣Xɓюo./))TRR"atcL&,--׿cm{4# 0>zo.onnޮݣ#''3F^\^?2юpFx~ B[[y9rxiFG; x%8 1v^\^|3;߿׻#[}V^o>6-F({zz:ujdB޽; SVVfii9iҤSNmڴѣOZQ8}tL87=G _0ntȟ3ghKKhaZ.733h4MUU}ռr劃?g̘cǎ~ǜ9sFOOÇfH ;;{'OVRRz3~k@~~~6l~FbH!֭[{<ܴi?b 6~q#'OH7ov,` y\~С{GL޾͛7AAAͫ~ii2lll1"aֻwשּׂǏΝݻw߸qcRWWy-rrr Nggg_O}.]]jŋ†*k͛7ofm&LB(//OLLl'N|–-[aÆYf͘1ٳgX >}PSSƏ܅ N4i?Pvcnn{' ]r{ OGѱw^mmm55M6577c׮]9s YxʹiLc_{Ç͛8qӖ-[8rc=h4Z[[BHKKٳ ~~~666uvvM4kDKKѣ344Guuumڴɩ~ҤIQQQ2%%%,{L<xÆ ?({{M>};vk2}M6-,,F555>NN!zzzϟ?G}򥱱qɒ%\,@Kׯ766>|_~<6o~9v؜9sOiLfDDÇcbb ݱFB/_믿Lʕ+Ϟ=LtwwTVVzիWG)A~:LWUUegQQђ%KvU]]dee؈5ӧOeeek֬.aqqq/ :u*//̙3xꕂB@@QZZӧOU^m۶>0 BCC?~!##ӣ'޻w/77wիWO =|0$$$!!AWWk}ܹ999k׮p,,,RRRB ǏܹsG&y!ssm۶M4u݄WWקOoܸ+\jF{ULLLxxB{3gNNNNff+mod2/^ZSmm߯[0((˗OKKkjj_Bqqq;v ͽr F[KJJZbEHHy{%Y~k8۷oݺrK.!Ly^|N}}}SSScbb믂,ѱf͚ .>N777--Cmٲ͛7d {ύX`{p[YYYT*_~=**ѣGIIIx#lN244ľ&''O>}C}P`_˖-{٪UPo.V}?vU\\x_&oݺuɤK.\;---88x =z\\\駟=0șKNN˳TQQ ʯ^ $$n:ƍg677'H[n¼UUU8plD$=/##$)==NTTtԩ=QUU5k//̆ rM lľZ[[zzzƲǒ>>>}}ocȹoԩ'Oܾ};Bh̙&MҲ²coZZZ|||ښ`0dee{xb)))"HP>|7MyYXX`MeffVWW_D"͜9s'c… V ]{iffVZZZ__/++?޺u ![XXX^^;mڴD5H---l2e//sII ŋ򋔔OLL B(,,C__`(**b-zyy?P=Y[[I$ {R ? ԩS322B˖-߲e իyyyyyyMMMìWWqXa3++LJW__ɒ%X122JJJs{{MRRRYYϟY{a{p޾}{ƌ3g$Hƍ*GGGZJVVV@@ :89ŋ:::__MИ1chCG_l{7nضm(NߴiSdd$BJFDڵkB4mʔ)؄%HʪQQQ>ŋB4{cccCCWyy9^H"sز4L9r~~~l+***::Z^^̬jUU'MeP{7W`gyy9^'i0SWW nGxbQQQc2 lWij 67y䀀_5//"00Iׯ:u TJb XSUUUt:Da}QYY)&&*))y%h?zBDTRRajjc]v?` 2\YYY|oc٨D"!d2ۻ~-VVVZZZlh48BN dk޼y;wĖMJJ [񃰫kwmjj"MMMا[hmmĽJiii`0 dhhx-EEE##P}}}|bAN"e썀- }֖///=tuuA:Z8Ѐ%d2&$%%4"$$xT/^ ޹s_ooގmi{{/_^zMP]TUUէCjlAFF?eSUU&++ݻmmmWa!!7o ^|رc}/,,6HVon ʞ;wd>~ܼϿÐƍ\Џ?dɒ/^zjѢEY/F_-[v/_*..=xׯ F?3h4!XSS3`׭ӧOGz{{?[ff&V>x X /_Ħpedd=gϞ7@ ̜93::]$ **zY,¤lYYY!!!*AAvlyH7222>>͛_{.wcǎK --]YYq/))>SRRR={fhh-X)--zNщ+))|kqqq؁ؗSbÇl1κggg"&W2}TTTsssӁ111>}"Ӷmbcc󛛛_|}nnn۷oOee%vE˟-((*_~ƍ|f5uT*Օ}C0uԔj?c+~UJJJBB!tzKct:6㗚tR)իWt[z,G}VVV`%::YhٳgZZZa}e```yĉ l?(8b0Ǟ+**9ϐ޽r vFL#Ĵbrą /_.///7oo˗/wtt8pFOOf…X}SN-Y,))IWW+?|ss3gd8q`HHHN;w 9s݀eld%((WGGJfddS^^ڵk۶mtRLL0k#״iӨTeΜ9#..>nrCc~hRRR7#yyS Դm۶>>>CCݻw>;eʔe˖3 55)Sٳg7mJII4FEDDDDDdannbw?sQQѸq㌍¹s [[['LuVPrrmZ[[%%%9r۷WKKˮ.eeNp`|||9xZgggsss33;vlܸ\FFIIk׮bA$Ϟ=O?>|X__ѢEy```O͛7o+:::ǛKII͝;Xti_IJJ۷r;e422:ۃ`M2]tinn9sdO?YFFF'O^[[>bA9onddD"lmmW^ jkkğ;szI&8qbFzc2ϟ߼y3@8qC8ؕ.llllllB"""cKBK,YdI–-[lZ8qDuuu/_{+j Yڵk׮]q",-----{39rȑ~b!`iiiZZZ$ED"͛7r~밾 4? Ν;A H|I Fƍ7,,,F;o8ŋةǧO>}tΜ9јѣ{.X`#!co0wB0گ?9v0?~\jU[[F;~x pc2AAA^^^حthG4Bcr1Řюb|۱LO_.䮳bVTy9rxiFOQmFX{9rxiuo`,Zh={W߸=G;!4A.rA.rA.rA.rA.rA.sm۶nڻΝ; rܹsTjKKpDZ./))y)D0$C塡:::+Wx"^{###111kk/_ѓ'Oܲe ^ظvZYYYyyy??NP^^o7iҤCaoݺ&..`0N86r.wrrrrr+//GuwwYYYlڴҥKX͢"WWא *?)((|YLLѣGz˗/w]PPZjٳgkkk322fΜɩmMSRR544&OzmEE/D2773gVƍg677'H[n/_{n11199m۶bD"qƍ! tDzm]])S8rŋh4Bf///#HX%%%lBQQ[&H !T__?~x\AA#eFPTTԭ[=XY-""]VV!˗ׯ_ZUUNGȼx/ Q(b Aaa!V/wikk vww`5qyLLLwwwzzzZZZZZZFFƬY.^)&&Ͽ}6V&666??!Wׯ_onn&T*`ŋ/_.//xyy[~8z-dqqqMMM|?DrvvްaC_=2̓'OY@ L<ߺ,66f,,,{w\p7w\p7wMMMM555D"H$_@%)))$$4˫( @znii}0yyy_M!2D""C==k R-\p7w\p7w\p7w\p7w\p!޾}0`5 ~AWWwj8B"""l gH/rppPSSkkkc[vDV^^~I}}}11'ݻdb>|0k, 2mڴիW3f̐srr,--Ǐ5 呑޽svv&l+?~ڵkBuڵ+NػwoNNιsBBB.\/--]vm]]BHZZSLYbʕ+Ǐ_\\ӘӧO&#fr9>ڵkO_i4ځ֬Y`0V\ڹsɒ%K^xJKK+((P(k֬uBz\g IDATNJJJJKK+((|kYHHHIIiΜ9ô|. brOOOZ...&Mruu=}ɓ'{|8222&&k.zaaal@ h4uuu__e˖566.\޾ 33300s a9_|rmm'N8::rPޗǏ8q55?`{ׯ_?}TUUUSS% @>|ߡa<<<Ɩ9(gԩSۭ[[[[;::B::: wnnn>w\UU… B7oLHHs^^^]]uɺuTTTɠСC""""""vvv!W&&&JKK9rڵkOдzyy)((̚5֭[H$^~t:}ڴiwF_ *%%%߽{Ƕɓ{:88888Fp NrQQѸT`\0nnnnnn*o߾mhhB`X\Р;`Tv qcCQQQww7ţ 0aBxxxH\\\tׯ_6iii'eeeOOO1lܸNYYK.=z(ޑ7Np*`!6(hڥK+++wڅ777z>}Zvmyyc\]]+**B&MJLLvvvnnnFEDD\tٳgwݹsZZZˏ?ǶS޹sGBB"777$$m ]b+!;;vspJJJ```MM`srr!nnn_7oތ"׮]6m=H466={{B4H$Q(#^)///$$׻;TSSC lbW ,XP\\tRg|.ڻwoCC?#t: ((_sNP墢T=˗/+)))++"***{wnff //WS]xx8ɼr劓ӷN_[|cN8qĉ2( @ iDDk 6ihh LTWWnL&ӦMa+1 xxW%WWW'''333dllm0,׾>|['MJJzjGGG[[[bbbqqqCC??2ByyyXe;;Ϸ ޹d2T*//o_M BHJJǏx5]]]͛7;99~Ẏdݺu*** %%ug2 %%wuuihh[[[?}R#,--ϟڻ~ARRRGGz…}55` !۷KIIXML`A/_ bccmllZ^^...N"9-`nn>`qqq&&&C ʕ+ǏOHH@|:;;kkk؊r|(~{w8)iii9y$vO<)l_ɓ'K,1557,ױfϞ8k$~r9  r9  r9  r9  r9ݾ\J&Bk׮=phGkCCCCC`|.u̙3EEEeeeᇤ8BpM{\~9wwwgg第+WSSiӦq6Q9!d.okk_ٳfYYYQQŋ?~!`0oߎ%9&(''7a„p|ٳVVV?F||>>l^8/_lll\ZZ7"---466644^WMMm߾}˗/f`p2kiiQHB&)$$DP": Ϥ+++ӧO~d2m6l8qD~~~nnF !aaa 7ruuMLL|}ss={@od2y޽ӧ+**o޼ZlΝ;7'''[ZZBǎd/T*СC]]]>]ƍ555ARRD"wtta땕[ZZΜ9llhhWVVF=x //!!!f'%%uvv7nܸq$'Z0p4ww3g\pAUUUUUٳ..._UVVvԩFFFsF988XZZ͟?_UUu]īWFEEhڒutttO:%...((뫯/%%ٳg;88#8544\]]>}v}Z[[cMMM6mVTTlnn޶m PllMUſA5kF;:;;kkk}bQ7\$)) ; `ю5 {rssdddΝ;`0F;"`pe.wsse+\p7w\p7w\p7w\p7NV۷oFP4559/0q2744X-55cUDDmyzz:lw]]]jjjmmmTu̙3EEEeeeᇤAEOcHw9;;;w9+++++kʕisgN$]v U_~ٳgϚ5kŋ/Z!jmm,$$GCCí[+**>IYYB 'Ώ˫lllؠӓFʛ7ojkkY w~g>|WW^]vm߾}&&&A)))555#>L&TjN>Ho~u͛7S>0fq~]HHh޽3g|͛#Tj[[ϟ{O>\H$"LfGGGww7BB qq℣〃rJd-d2T*ի<<<DZ`l,sal32w_~7oB^z˖-^]JJpњ[NEEeA9̙3.\PUUUUU={ Bh&&&sY ĉ9woIC EXN0q\\TT4..n08)0q2s5 \p7w\p7w\p7w\p7w\p7w\p7wVymCCÀ(&2N]]rS`d.G-OOOg0bbb#qAMM:zzzd29??/3gLNKKCm޼õDDD-ZTQQU d2Y\\׬HHH,X)ʄ <<}QQQ:::LfhhhnnˇB%%%...v***nnnX}AAUVܹJ--Geff*++/Zd W.dzTWW׮]|||_ aaaGׯ_,X@@@@FFf岲SD"_ҥKx^zuk׮͛7Jxyymذ#Gtuu%%%W\+PDDh```BBBQQBHOOqL77U222ܺvWW˗cBϊtÆ <q℣!qqӧXYKK:##PVV۷oB˗߿?x `0ɑegg}UZWWBCCIOMM-33[铚Z?1XZZOn 0ÒyxxCJHH}O.))ikk{Ett~:Xo߾MMMMMM}MGGǽ{X888>|˗/Ǐ?z(k+WVWW_p !dgg?c yyy~kk+v{kk+r7nlmmmmmsudݺu***c zVJJݻFFF...=.8'Om...O>??8==}Ŭ***\@DDDDDDd?~K.cEDD DDDLMMB<:t߰PllMU2..dcAgggmm-NNTT***7j89orA.rA.rA.rA.' Ivvv]]Y_OBBJ}Ye_UpT#ICyor@`{NUUGy׍ZJT#i{dҡ{o6lGG]]ݬYF; QT*ӧ˳>];FN[Õ'rYcO+B"Ǽ|c hr9|X{֮]{1<9?.WVVǖ ӦM!8ߡ{YZZy{tqqh#o,X設sθq_x 577q:oBHHhڵiaO#o8sϱ̞=FȬ^!G)) mmUVuvvh4kk뢢)**8p@SS3%%ECCÇ{^k333ٍ7)++?!t---))y eddRSSBUUUrrrO| #>>o޼P۷o9"###''wӖ+VPUU} (njj$ubbb,- =$H'O޾};V+111uuG!/^ǶyfOOOlΝ; , v<<<B?~;w˗ʍrrr۶mwEEEE'Ms.B...gYzqձQ;{ttɓEEEtzHHTTTN81n8Gu`/ܒ'N?gIDAT{=nf666 gϞ~|}}=BÇ4ƦMAAAAAa0S FKrĉű0u,XPYYYTT}"뛛_zuy&p2׳+>>>>>>33s>}deevcǎVTTV8|ldddnn+}:q℣cyyBW^ѦoaaS/^WUUu̙5k`;g֭V$''_S0o߾=}7o>|('';찰CԴO>¢v=uuu3g ccc޿=nܸ+W_v߿S6$##@_9v옜\TTTaaʕ+.]:k,///_\?hQQQd2pXgA2Yv kߜSRR( ۟/--7oތ"~~~Ϟ=cۑ7LkצMfooO$gϞ}޽~:ukڴiD"qeee!777555={^D"mmm2<`lϞ=SSS/1钒K,yB(,,lAAAo2 _p!DZv)vɓ'ׯ_M$?{!SNuwwǵFFFya!aQz{{#Ubbbp{---mmmW^qpp 3f077{oܸ'Hϟh۷|||NNNX ߳g/r%ֵH$RKK۷o F.\\\lmm-[1HoՉN:+"jjj<< MBYlYff-[/߀CayXrVO.--=x𠇇i: Ǧ[|!!!d}b0{CNN_RR 6mguE2ݻׯ߼y}mt6YYŶ*++ɶG}Ⱥ[[[--..ƿj-ǎ¿L4IYYΝ;=5k_{~ ٳKJJk[[ׯg}Etzyy9~AaaB*V# 77]v޽{>}3gVz;99}hٲeYaiXB(##ԩSNNN6lQ,;bc {СCx7nIIIO444HKKKJJ"~SRRjllBݺurIIIW^hkk2A:RRR4B#88855d644ܸq!uVŋ\d2T*__ WRRЀϻx񜜜f++ANc|ۊk׮uwwգv`ssSr۷gee;wnٲe}uvʕӶNNNǏv';;;233cbbcooxeUKHH^Y*j?iPSWbc"1X c,"#B-,Rb;jRmCQG*(XP).q{DJ}͛5 ^'7g˕]rٳR)}ք .666Ϟ=;|FF "66ѣGm!ݹs@Vg(ʬ,Rb $\\\\nYYYWǏ䔔~}˖-t+ٳkܹӿJU^^tWt2sgSo{~;N^[}{XYncc{|DP(o߮;[jG9yNB͛M6a„SN}D"9rHZZ۟^|yLLD"ٶm ,X`iiꚛp~dBȺu 鋬 KJJ,--=<<'Ȍ=q?xž666J2--B8qƍΝ۷o_ggCq8dɒ'O?_re :YYYaaa2L&aCBB󽼼/Quq9x~]xqȑ++ǏرJ&%$$צO;({.ǣOBf͚u~rK$3g&''ӝ'%%IҡC9rҤIyɒ%bX**ʘ6CtEQ)))RT,ٳ'55}Poo+Wv#~ǩSzzzCՌvbψ,f7X7"xҥKLfĈ>aT&B_NNҥKX[[YMbϯ-Y5|p L/s[ݳ0:#,/~,Z-h8皙ZA?,bʕ+ ]|Y,jk/7NztO2)j7;wN> _o.ŇÓ\]ՒOdYdԯ2,6vx-w)b`7d9! YnrvCݘe[/Ak4d9QQh5aZV*Eq8oz݄(VT*U* \"(J.pz9FhbBP(2c`7d9! YnrvCݐ,߻w|`zEv`2 ݽt׮]ڮ())tb+!d֭aaatـ=hαx<!SNߍ0aBQQъ+Μ9#!**&&O?mnn&YXXlذ5"""55o֬Ynnnӧ>srrD"\.߲eKs jmmrOjԨѣGB|}}KKK!fffSN={l_O&EGGB~gOOOp¾}ѣǍ7D"Ѱan*5k鲹9]pww2dHyyBhDT?3gΤS3VKӧjtx'ODK.޿ te͍AUTTB*++ڰ{~/=))ãK0;.si}7n߾vvv۩u+bqfffaaaaa۷+++ xBBB #effFGGK$={[EQ[nLOO߱c-8 t.;;Gkݐ,`7d91 usssMM 5@Gh4KKKFOfUUU}fff]Y (VR]]moo ž={2t Q*0^rq8`Tlf!_D-c`7d9! YnrvCݐ,`7wƍMMMVӧx2˛Fi˗/pPϱ}RVKu8$N=}?ywLq\ф8;;vP^^^"H*;wΨڽ{B1`,̼sNhh(Y;wFDDΚ5+==s;vM9"A+) fڵzZ|*Zo7oT*DSLINN֯s=ooo+++LhѢO퓒ƌ3lذYfT*z5kd2\< AαSOݷo]AAA˖-| UUU7n_ .zYׯN6McR[^^~k׮' N:uB2227o8q֭믿\ցSPP;vިP(N˅BaBBBSSS\\\W !b:NNNr|Ŋ!9]իWss3!R&ubsjeUVV4.Ϙ1###o>(?wwBA9!M,gffo]qEGG +WmmctYWşں.WWWݻuVnn;D&c,733r>jժ;vTVV666,ZHNcc!CAKKKjjj<|PT&&&D")++GO----[O2888< is Y2038-unsafe This reflects the fact that the user code is referring to a symbol which, when glibc defaults to 32-bit time support, might fail Y2038. General note: y2038.py will handle multiple configurations, and will emit diagnostics for each configuration in turn. 4. How to use the Y2038 cppcheck addon The Y2038 cppcheck addon is used like any other cppcheck addon: cppcheck --dump file1.c [ file2.c [...]]] y2038.py file1.c [ file2.c [...]]] Sample test C file is provided: test/y2038-test-1-bad-time-bits.c test/y2038-test-2-no-time-bits.c test/y2038-test-3-no-use-time-bits.c test/y2038-test-4-good.c These cover the cases described above. You can run them through cppcheck and y2038.py to see for yourself how the addon diagnostics look like. If this README is not outdated (and if it is, feel free to submit a patch), you can run cppcheck on these files as on any others: cppcheck --dump addons/y2038/test/y2038-*.c y2038.py addons/y2038/test/y2038-*.dump If you have not installed cppcheck yet, you will have to run these commands from the root of the cppcheck repository: make sudo make install ./cppcheck --dump addons/y2038/test/y2038-*.c PYTHONPATH=addons python addons/y2038/y2038.py addons/y2038/test/y2038-*.c.dump In both cases, y2038.py execution should result in the following: Checking addons/y2038/test/y2038-test-1-bad-time-bits.c.dump... Checking addons/y2038/test/y2038-test-1-bad-time-bits.c.dump, config ""... Checking addons/y2038/test/y2038-test-2-no-time-bits.c.dump... Checking addons/y2038/test/y2038-test-2-no-time-bits.c.dump, config ""... Checking addons/y2038/test/y2038-test-3-no-use-time-bits.c.dump... Checking addons/y2038/test/y2038-test-3-no-use-time-bits.c.dump, config ""... Checking addons/y2038/test/y2038-test-4-good.c.dump... Checking addons/y2038/test/y2038-test-4-good.c.dump, config ""... # Configuration "": # Configuration "": [addons/y2038/test/y2038-test-1-bad-time-bits.c:8]: (error) _TIME_BITS must be defined equal to 64 [addons/y2038/test/y2038-inc.h:9]: (warning) _USE_TIME_BITS64 is defined but _TIME_BITS was not [addons/y2038/test/y2038-test-1-bad-time-bits.c:10]: (information) addons/y2038/test/y2038-inc.h was included from here [addons/y2038/test/y2038-inc.h:9]: (warning) _USE_TIME_BITS64 is defined but _TIME_BITS was not [addons/y2038/test/y2038-test-2-no-time-bits.c:8]: (information) addons/y2038/test/y2038-inc.h was included from here [addons/y2038/test/y2038-test-3-no-use-time-bits.c:13]: (warning) timespec is Y2038-unsafe [addons/y2038/test/y2038-test-3-no-use-time-bits.c:15]: (warning) clock_gettime is Y2038-unsafe Note: y2038.py recognizes option --template as cppcheck does, including pre-defined templates 'gcc', 'vs' and 'edit'. The short form -t is also recognized. cppcheck-2.7/addons/findcasts.py000077500000000000000000000015221417746362400167630ustar00rootroot00000000000000#!/usr/bin/env python3 # # Locate casts in the code # import cppcheck import sys @cppcheck.checker def cast(cfg, data): for token in cfg.tokenlist: if token.str != '(' or not token.astOperand1 or token.astOperand2: continue # Is it a lambda? if token.astOperand1.str == '{': continue # we probably have a cast.. if there is something inside the parentheses # there is a cast. Otherwise this is a function call. typetok = token.next if not typetok.isName: continue # cast number => skip output if token.astOperand1.isNumber: continue # void cast => often used to suppress compiler warnings if typetok.str == 'void': continue cppcheck.reportError(token, 'information', 'found a cast') cppcheck-2.7/addons/misc.py000066400000000000000000000136661417746362400157510ustar00rootroot00000000000000#!/usr/bin/env python3 # # Misc: Uncategorized checks that might be moved to some better addon later # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python misc.py main.cpp.dump import cppcheckdata import sys import re DEBUG = ('-debug' in sys.argv) VERIFY = ('-verify' in sys.argv) VERIFY_EXPECTED = [] VERIFY_ACTUAL = [] def reportError(token, severity, msg, id): if id == 'debug' and DEBUG == False: return if VERIFY: VERIFY_ACTUAL.append(str(token.linenr) + ':' + id) else: cppcheckdata.reportError(token, severity, msg, 'misc', id) def simpleMatch(token, pattern): return cppcheckdata.simpleMatch(token, pattern) # Get function arguments def getArgumentsRecursive(tok, arguments): if tok is None: return if tok.str == ',': getArgumentsRecursive(tok.astOperand1, arguments) getArgumentsRecursive(tok.astOperand2, arguments) else: arguments.append(tok) def getArguments(ftok): arguments = [] getArgumentsRecursive(ftok.astOperand2, arguments) return arguments def isStringLiteral(tokenString): return tokenString.startswith('"') # check data def stringConcatInArrayInit(data): # Get all string macros stringMacros = [] for cfg in data.iterconfigurations(): for directive in cfg.directives: res = re.match(r'#define[ ]+([A-Za-z0-9_]+)[ ]+".*', directive.str) if res: macroName = res.group(1) if macroName not in stringMacros: stringMacros.append(macroName) # Check code arrayInit = False for i in range(len(data.rawTokens)): if i < 2: continue tok1 = data.rawTokens[i-2].str tok2 = data.rawTokens[i-1].str tok3 = data.rawTokens[i-0].str if tok3 == '}': arrayInit = False elif tok1 == ']' and tok2 == '=' and tok3 == '{': arrayInit = True elif arrayInit and (tok1 in [',', '{']): isString2 = (isStringLiteral(tok2) or (tok2 in stringMacros)) isString3 = (isStringLiteral(tok3) or (tok3 in stringMacros)) if isString2 and isString3: reportError(data.rawTokens[i], 'style', 'String concatenation in array initialization, missing comma?', 'stringConcatInArrayInit') def implicitlyVirtual(data): for cfg in data.iterconfigurations(): for function in cfg.functions: if function.isImplicitlyVirtual is None: continue if not function.isImplicitlyVirtual: continue reportError(function.tokenDef, 'style', 'Function \'' + function.name + '\' overrides base class function but is not marked with \'virtual\' keyword.', 'implicitlyVirtual') def ellipsisStructArg(data): for cfg in data.iterconfigurations(): for tok in cfg.tokenlist: if tok.str != '(': continue if tok.astOperand1 is None or tok.astOperand2 is None: continue if tok.astOperand2.str != ',': continue if tok.scope.type in ['Global', 'Class']: continue if tok.astOperand1.function is None: continue for argnr, argvar in tok.astOperand1.function.argument.items(): if argnr < 1: continue if not simpleMatch(argvar.typeStartToken, '...'): continue callArgs = getArguments(tok) for i in range(argnr-1, len(callArgs)): valueType = callArgs[i].valueType if valueType is None: argStart = callArgs[i].previous while argStart.str != ',': if argStart.str == ')': argStart = argStart.link argStart = argStart.previous argEnd = callArgs[i] while argEnd.str != ',' and argEnd.str != ')': if argEnd.str == '(': argEnd = argEnd.link argEnd = argEnd.next expression = '' argStart = argStart.next while argStart != argEnd: expression = expression + argStart.str argStart = argStart.next reportError(tok, 'debug', 'Bailout, unknown argument type for argument \'' + expression + '\'.', 'debug') continue if valueType.pointer > 0: continue if valueType.type != 'record' and valueType.type != 'container': continue reportError(tok, 'style', 'Passing record to ellipsis function \'' + tok.astOperand1.function.name + '\'.', 'ellipsisStructArg') break for arg in sys.argv[1:]: if arg in ['-debug', '-verify', '--cli']: continue print("Checking %s..." % arg) data = cppcheckdata.CppcheckData(arg) if VERIFY: VERIFY_ACTUAL = [] VERIFY_EXPECTED = [] for tok in data.rawTokens: if tok.str.startswith('//'): for word in tok.str[2:].split(' '): if word in ['stringConcatInArrayInit', 'implicitlyVirtual', 'ellipsisStructArg']: VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word) stringConcatInArrayInit(data) implicitlyVirtual(data) ellipsisStructArg(data) if VERIFY: for expected in VERIFY_EXPECTED: if expected not in VERIFY_ACTUAL: print('Expected but not seen: ' + expected) sys.exit(1) for actual in VERIFY_ACTUAL: if actual not in VERIFY_EXPECTED: print('Not expected: ' + actual) sys.exit(1) sys.exit(cppcheckdata.EXIT_CODE) cppcheck-2.7/addons/misra.py000077500000000000000000005575221417746362400161400ustar00rootroot00000000000000#!/usr/bin/env python3 # # MISRA C 2012 checkers # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python misra.py --rule-texts= main.cpp.dump # # Limitations: This addon is released as open source. Rule texts can't be freely # distributed. https://www.misra.org.uk/forum/viewtopic.php?f=56&t=1189 # # The MISRA standard documents may be obtained from https://www.misra.org.uk # # Total number of rules: 143 from __future__ import print_function import cppcheckdata import itertools import json import sys import re import os import argparse import codecs import string import copy try: from itertools import izip as zip except ImportError: pass import misra_9 def grouped(iterable, n): """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ...""" return zip(*[iter(iterable)] * n) INT_TYPES = ['bool', 'char', 'short', 'int', 'long', 'long long'] STDINT_TYPES = ['%s%d_t' % (n, v) for n, v in itertools.product( ['int', 'uint', 'int_least', 'uint_least', 'int_fast', 'uint_fast'], [8, 16, 32, 64])] typeBits = { 'CHAR': None, 'SHORT': None, 'INT': None, 'LONG': None, 'LONG_LONG': None, 'POINTER': None } def isUnsignedType(ty): return ty == 'unsigned' or ty.startswith('uint') def simpleMatch(token, pattern): return cppcheckdata.simpleMatch(token, pattern) def rawlink(rawtoken): if rawtoken.str == '}': indent = 0 while rawtoken: if rawtoken.str == '}': indent = indent + 1 elif rawtoken.str == '{': indent = indent - 1 if indent == 0: break rawtoken = rawtoken.previous else: rawtoken = None return rawtoken # Identifiers described in Section 7 "Library" of C90 Standard # Based on ISO/IEC9899:1990 Annex D -- Library summary and # Annex E -- Implementation limits. C90_STDLIB_IDENTIFIERS = { # D.1 Errors 'errno.h': ['EDOM', 'ERANGE', 'errno'], # D.2 Common definitions 'stddef.h': ['NULL', 'offsetof', 'ptrdiff_t', 'size_t', 'wchar_t'], # D.3 Diagnostics 'assert.h': ['NDEBUG', 'assert'], # D.4 Character handling 'ctype.h': [ 'isalnum', 'isalpha', 'isblank', 'iscntrl', 'isdigit', 'isgraph', 'islower', 'isprint', 'ispunct', 'isspace', 'isupper', 'isxdigit', 'tolower', 'toupper', ], # D.5 Localization 'locale.h': [ 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'NULL', 'lconv', 'setlocale', 'localeconv', ], # D.6 Mathematics 'math.h': [ 'HUGE_VAL', 'acos', 'asin' , 'atan2', 'cos', 'sin', 'tan', 'cosh', 'sinh', 'tanh', 'exp', 'frexp', 'ldexp', 'log', 'loglO', 'modf', 'pow', 'sqrt', 'ceil', 'fabs', 'floor', 'fmod', ], # D.7 Nonlocal jumps 'setjmp.h': ['jmp_buf', 'setjmp', 'longjmp'], # D.8 Signal handling 'signal.h': [ 'sig_atomic_t', 'SIG_DFL', 'SIG_ERR', 'SIG_IGN', 'SIGABRT', 'SIGFPE', 'SIGILL', 'SIGINT', 'SIGSEGV', 'SIGTERM', 'signal', 'raise', ], # D.9 Variable arguments 'stdarg.h': ['va_list', 'va_start', 'va_arg', 'va_end'], # D.10 Input/output 'stdio.h': [ '_IOFBF', '_IOLBF', '_IONBF', 'BUFSIZ', 'EOF', 'FILE', 'FILENAME_MAX', 'FOPEN_MAX', 'fpos_t', 'L_tmpnam', 'NULL', 'SEEK_CUR', 'SEEK_END', 'SEEK_SET', 'size_t', 'stderr', 'stdin', 'stdout', 'TMP_MAX', 'remove', 'rename', 'tmpfile', 'tmpnam', 'fclose', 'fflush', 'fopen', 'freopen', 'setbuf', 'setvbuf', 'fprintf', 'fscanf', 'printf', 'scanf', 'sprintf', 'sscanf', 'vfprintf', 'vprintf', 'vsprintf', 'fgetc', 'fgets', 'fputc', 'fputs', 'getc', 'getchar', 'gets', 'putc', 'putchar', 'puts', 'ungetc', 'fread', 'fwrite', 'fgetpos', 'fseek', 'fsetpos', 'rewind', 'clearerr', 'feof', 'ferror', 'perror', ], # D.11 General utilities 'stdlib.h': [ 'EXIT_FAILURE', 'EXIT_SUCCESS', 'MB_CUR_MAX', 'NULL', 'RAND_MAX', 'div_t', 'ldiv_t', 'wchar_t', 'atof', 'atoi', 'strtod', 'rand', 'srand', 'calloc', 'free', 'malloc', 'realloc', 'abort', 'atexit', 'exit', 'getenv', 'system', 'bsearch', 'qsort', 'abs', 'div', 'ldiv', 'mblen', 'mbtowc', 'wctomb', 'mbstowcs', 'wcstombs', ], # D.12 String handling 'string.h': [ 'NULL', 'size_t', 'memcpy', 'memmove', 'strcpy', 'strncpy', 'strcat', 'strncat', 'memcmp', 'strcmp', 'strcoll', 'strncmp', 'strxfrm', 'memchr', 'strchr', 'strcspn', 'strpbrk', 'strrchr', 'strspn', 'strstr', 'strtok', 'memset', 'strerror', 'strlen', ], # D.13 Date and time 'time.h': [ 'CLK_TCK', 'NULL', 'clock_t', 'time_t', 'size_t', 'tm', 'clock', 'difftime', 'mktime', 'time', 'asctime', 'ctime', 'gmtime', 'localtime', 'strftime', ], # Annex E: Implementation limits 'limits.h': [ 'CHAR_BIT', 'SCHAR_MIN', 'SCHAR_MAX', 'UCHAR_MAX', 'CHAR_MIN', 'CHAR_MAX', 'MB_LEN_MAX', 'SHRT_MIN', 'SHRT_MAX', 'USHRT_MAX', 'INT_MIN', 'INT_MAX', 'UINT_MAX', 'LONG_MIN', 'LONG_MAX', 'ULONG_MAX', ], 'float.h': [ 'FLT_ROUNDS', 'FLT_RADIX', 'FLT_MANT_DIG', 'DBL_MANT_DIG', 'LDBL_MANT_DIG', 'DECIMAL_DIG', 'FLT_DIG', 'DBL_DIG', 'LDBL_DIG', 'DBL_MIN_EXP', 'LDBL_MIN_EXP', 'FLT_MIN_10_EXP', 'DBL_MIN_10_EXP', 'LDBL_MIN_10_EXP', 'FLT_MAX_EXP', 'DBL_MAX_EXP', 'LDBL_MAX_EXP', 'FLT_MAX_10_EXP', 'DBL_MAX_10_EXP', 'LDBL_MAX_10_EXP', 'FLT_MAX', 'DBL_MAX', 'LDBL_MAX', 'FLT_MIN', 'DBL_MIN', 'LDBL_MIN', 'FLT_EPSILON', 'DBL_EPSILON', 'LDBL_EPSILON' ], } # Identifiers described in Section 7 "Library" of C99 Standard # Based on ISO/IEC 9899 WF14/N1256 Annex B -- Library summary C99_STDLIB_IDENTIFIERS = { # B.1 Diagnostics 'assert.h': C90_STDLIB_IDENTIFIERS['assert.h'], # B.2 Complex 'complex.h': [ 'complex', 'imaginary', 'I', '_Complex_I', '_Imaginary_I', 'CX_LIMITED_RANGE', 'cacos', 'cacosf', 'cacosl', 'casin', 'casinf', 'casinl', 'catan', 'catanf', 'catanl', 'ccos', 'ccosf', 'ccosl', 'csin', 'csinf', 'csinl', 'ctan', 'ctanf', 'ctanl', 'cacosh', 'cacoshf', 'cacoshl', 'casinh', 'casinhf', 'casinhl', 'catanh', 'catanhf', 'catanhl', 'ccosh', 'ccoshf', 'ccoshl', 'csinh', 'csinhf', 'csinhl', 'ctanh', 'ctanhf', 'ctanhl', 'cexp', 'cexpf', 'cexpl', 'clog', 'clogf', 'clogl', 'cabs', 'cabsf', 'cabsl', 'cpow', 'cpowf', 'cpowl', 'csqrt', 'csqrtf', 'csqrtl', 'carg', 'cargf', 'cargl', 'cimag', 'cimagf', 'cimagl', 'conj', 'conjf', 'conjl', 'cproj', 'cprojf', 'cprojl', 'creal', 'crealf', 'creall', ], # B.3 Character handling 'ctype.h': C90_STDLIB_IDENTIFIERS['ctype.h'], # B.4 Errors 'errno.h': C90_STDLIB_IDENTIFIERS['errno.h'] + ['EILSEQ'], # B.5 Floating-point environment 'fenv.h': [ 'fenv_t', 'FE_OVERFLOW', 'FE_TOWARDZERO', 'fexcept_t', 'FE_UNDERFLOW', 'FE_UPWARD', 'FE_DIVBYZERO', 'FE_ALL_EXCEPT', 'FE_DFL_ENV', 'FE_INEXACT', 'FE_DOWNWARD', 'FE_INVALID', 'FE_TONEAREST', 'FENV_ACCESS', 'feclearexcept', 'fegetexceptflag', 'fegetround', 'fesetround', 'fegetenv', 'feholdexcept', 'fesetenv', 'feupdateenv', ], # B.6 Characteristics of floating types 'float.h': C90_STDLIB_IDENTIFIERS['float.h'] + ['FLT_EVAL_METHOD'], # B.7 Format conversion of integer types 'inttypes.h': [ 'imaxdiv_t', 'imaxabs', 'imaxdiv', 'strtoimax', 'strtoumax', 'wcstoimax', 'wcstoumax', ], # B.8 Alternative spellings 'iso646.h': [ 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq', ], # B.9 Size of integer types 'limits.h': C90_STDLIB_IDENTIFIERS['limits.h'] + ['LLONG_MIN', 'LLONG_MAX', 'ULLONG_MAX'], # B.10 Localization 'locale.h': C90_STDLIB_IDENTIFIERS['locale.h'], # B.11 Mathematics 'math.h': C90_STDLIB_IDENTIFIERS['math.h'] + [ 'float_t', 'double_t', 'HUGE_VAL', 'HUGE_VALF', 'HUGE_VALL', 'INFINITY', 'NAN', 'FP_INFINITE', 'FP_NAN', 'FP_NORMAL', 'FP_SUBNORMAL', 'FP_ZERO', 'FP_FAST_FMA', 'FP_FAST_FMAF', 'FP_FAST_FMAL', 'FP_ILOGB0', 'FP_ILOGBNAN', 'MATH_ERRNO', 'MATH_ERREXCEPT', 'math_errhandling', 'FP_CONTRACT', 'fpclassify', 'isfinite', 'isinf', 'isnan', 'isnormal', 'signbit', 'acosf', 'acosl', 'asinf', 'asinl', 'atanf', 'atanl', 'atan2', 'atan2f', 'atan2l', 'cosf', 'cosl', 'sinf', 'sinl', 'tanf', 'tanl', 'acosh', 'acoshf', 'acoshl', 'asinh', 'asinhf', 'asinhl', 'atanh', 'atanhf', 'atanhl', 'cosh', 'coshf', 'coshl', 'sinh', 'sinhf', 'sinhl', 'tanh', 'tanhf', 'tanhl', 'expf', 'expl', 'exp2', 'exp2f', 'exp2l', 'expm1', 'expm1f', 'expm1l', 'frexpf', 'frexpl', 'ilogb', 'ilogbf', 'ilogbl', 'float', 'ldexpl', 'logf', 'logl', 'log10f', 'log10l', 'log1p', 'log1pf', 'log1pl', 'log2', 'log2f', 'log2l', 'logb', 'logbf', 'logbl', 'modff', 'modfl', 'scalbn', 'scalbnf', 'scalbnl', 'scalbln', 'scalblnf', 'scalblnl', 'hypotl', 'powf', 'powl', 'sqrtf', 'sqrtl', 'erf', 'erff', 'erfl', 'erfc', 'erfcf', 'erfcl', 'lgamma', 'lgammaf', 'lgammal', 'tgamma', 'tgammaf', 'tgammal', 'ceilf', 'ceill', 'floorf', 'floorl', 'nearbyint', 'nearbyintf', 'nearbyintl', 'rint', 'rintf', 'rintl', 'lrint', 'lrintf', 'lrintl', 'llrint', 'llrintf', 'llrintl', 'round', 'roundf', 'roundl', 'lround', 'lroundf', 'lroundl', 'llround', 'llroundf', 'llroundl', 'trunc', 'truncf', 'truncl', 'fmodf', 'fmodl', 'remainder', 'remainderf', 'remainderl', 'remquo', 'remquof', 'remquol', 'copysign', 'copysignf', 'copysignl', 'nan', 'nanf', 'nanl', 'nextafter', 'nextafterf', 'nextafterl', 'nexttoward', 'nexttowardf', 'nexttowardl', 'fdim', 'fdimf', 'fdiml', 'fmax', 'fmaxf', 'fmaxl', 'fmin', 'fminf', 'fminl', 'fmal', 'isgreater', 'isgreaterequal', 'isless', 'islessequal', 'islessgreater', 'isunordered', ], # B.12 Nonlocal jumps 'setjmp.h': C90_STDLIB_IDENTIFIERS['setjmp.h'], # B.13 Signal handling 'signal.h': C90_STDLIB_IDENTIFIERS['signal.h'], # B.14 Variable arguments 'stdarg.h': C90_STDLIB_IDENTIFIERS['stdarg.h'] + ['va_copy'], # B.15 Boolean type and values 'stdbool.h': ['bool', 'true', 'false', '__bool_true_false_are_defined'], # B.16 Common definitions 'stddef.h': C90_STDLIB_IDENTIFIERS['stddef.h'], # B.17 Integer types 'stdint.h': [ 'intptr_t', 'uintptr_t', 'intmax_t', 'uintmax_t', 'INTN_MIN', 'INTN_MAX', 'UINTN_MAX', 'INT_LEASTN_MIN', 'INT_LEASTN_MAX', 'UINT_LEASTN_MAX', 'INT_FASTN_MIN', 'INT_FASTN_MAX', 'UINT_FASTN_MAX', 'INTPTR_MIN', 'INTPTR_MAX', 'UINTPTR_MAX', 'INTMAX_MIN', 'INTMAX_MAX', 'UINTMAX_MAX', 'PTRDIFF_MIN', 'PTRDIFF_MAX', 'SIG_ATOMIC_MIN', 'SIG_ATOMIC_MAX', 'SIZE_MAX', 'WCHAR_MIN', 'WCHAR_MAX', 'WINT_MIN', 'WINT_MAX', 'INTN_C', 'UINTN_C', 'INTMAX_C', 'UINTMAX_C', ] + STDINT_TYPES, # B.18 Input/output 'stdio.h': C90_STDLIB_IDENTIFIERS['stdio.h'] + [ 'mode', 'restrict', 'snprintf', 'vfscanf', 'vscanf', 'vsnprintf', 'vsscanf', ], # B.19 General utilities 'stdlib.h': C90_STDLIB_IDENTIFIERS['stdlib.h'] + [ '_Exit', 'labs', 'llabs', 'lldiv', 'lldiv_t', 'strtof', 'strtol', 'strtold', 'strtoll', 'strtoul', 'strtoull' ], # B.20 String handling 'string.h': C90_STDLIB_IDENTIFIERS['string.h'], # B.21 Type-generic math 'tgmath.h': [ 'acos', 'asin', 'atan', 'acosh', 'asinh', 'atanh', 'cos', 'sin', 'tan', 'cosh', 'sinh', 'tanh', 'exp', 'log', 'pow', 'sqrt', 'fabs', 'atan2', 'cbrt', 'ceil', 'copysign', 'erf', 'erfc', 'exp2', 'expm1', 'fdim', 'floor', 'fma', 'fmax', 'fmin', 'fmod', 'frexp', 'hypot', 'ilogb', 'ldexp', 'lgamma', 'llrint', 'llround', 'log10', 'log1p', 'log2', 'logb', 'lrint', 'lround', 'nearbyint', 'nextafter', 'nexttoward', 'remainder', 'remquo', 'rint', 'round', 'scalbn', 'scalbln', 'tgamma', 'trunc', 'carg', 'cimag', 'conj', 'cproj', 'creal', ], # B.22 Date and time 'time.h': C90_STDLIB_IDENTIFIERS['time.h'] + ['CLOCKS_PER_SEC'], # B.23 Extended multibyte/wide character utilities 'wchar.h': [ 'wchar_t', 'size_t', 'mbstate_t', 'wint_t', 'tm', 'NULL', 'WCHAR_MAX', 'WCHAR_MIN', 'WEOF', 'fwprintf', 'fwscanf', 'swprintf', 'swscanf', 'vfwprintf', 'vfwscanf', 'vswprintf', 'vswscanf', 'vwprintf', 'vwscanf', 'wprintf', 'wscanf', 'fgetwc', 'fgetws', 'fputwc', 'fputws', 'fwide', 'getwc', 'getwchar', 'putwc', 'putwchar', 'ungetwc', 'wcstod', 'wcstof', 'double', 'int', 'long', 'long', 'long', 'wcscpy', 'wcsncpy', 'wmemcpy', 'wmemmove', 'wcscat', 'wcsncat', 'wcscmp', 'wcscoll', 'wcsncmp', 'wcsxfrm', 'wmemcmp', 'wcschr', 'wcscspn', 'wcspbrk', 'wcsrchr', 'wcsspn', 'wcsstr', 'wcstok', 'wmemchr', 'wcslen', 'wmemset', 'wcsftime', 'btowc', 'wctob', 'mbsinit', 'mbrlen', 'mbrtowc', 'wcrtomb', 'mbsrtowcs', 'wcsrtombs', ], } def isStdLibId(id_, standard='c99'): id_lists = [] if standard == 'c89': id_lists = C90_STDLIB_IDENTIFIERS.values() elif standard in ('c99', 'c11'): id_lists = C99_STDLIB_IDENTIFIERS.values() for l in id_lists: if id_ in l: return True return False # Reserved keywords defined in ISO/IEC9899:1990 -- ch 6.1.1 C90_KEYWORDS = { 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while' } # Reserved keywords defined in ISO/IEC 9899 WF14/N1256 -- ch. 6.4.1 C99_ADDED_KEYWORDS = { 'inline', 'restrict', '_Bool', '_Complex', '_Imaginary', 'bool', 'complex', 'imaginary' } C11_ADDED_KEYWORDS = { '_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', '_Statis_assert', '_Thread_local' , 'alignas', 'alignof', 'noreturn', 'static_assert' } def isKeyword(keyword, standard='c99'): kw_set = {} if standard == 'c89': kw_set = C90_KEYWORDS elif standard == 'c99': kw_set = copy.copy(C90_KEYWORDS) kw_set.update(C99_ADDED_KEYWORDS) else: kw_set = copy.copy(C90_KEYWORDS) kw_set.update(C99_ADDED_KEYWORDS) kw_set.update(C11_ADDED_KEYWORDS) return keyword in kw_set def is_source_file(file): return file.endswith('.c') def is_header(file): return file.endswith('.h') def is_errno_setting_function(function_name): return function_name and \ function_name in ('ftell', 'fgetpos', 'fsetpos', 'fgetwc', 'fputwc' 'strtoimax', 'strtoumax', 'strtol', 'strtoul', 'strtoll', 'strtoull', 'strtof', 'strtod', 'strtold' 'wcstoimax', 'wcstoumax', 'wcstol', 'wcstoul', 'wcstoll', 'wcstoull', 'wcstof', 'wcstod', 'wcstold' 'wcrtomb', 'wcsrtombs', 'mbrtowc') def get_type_conversion_to_from(token): def get_vartok(expr): while expr: if isCast(expr): if expr.astOperand2 is None: expr = expr.astOperand1 else: expr = expr.astOperand2 elif expr.str in ('.', '::'): expr = expr.astOperand2 elif expr.str == '[': expr = expr.astOperand1 else: break return expr if (expr and expr.variable) else None if isCast(token): vartok = get_vartok(token) if vartok: return (token.next, vartok.variable.typeStartToken) elif token.str == '=': lhs = get_vartok(token.astOperand1) rhs = get_vartok(token.astOperand2) if lhs and rhs: return (lhs.variable.typeStartToken, rhs.variable.typeStartToken) return None def getEssentialTypeCategory(expr): if not expr: return None if expr.str == ',': return getEssentialTypeCategory(expr.astOperand2) if expr.str in ('<', '<=', '==', '!=', '>=', '>', '&&', '||', '!'): return 'bool' if expr.str in ('<<', '>>'): # TODO this is incomplete return getEssentialTypeCategory(expr.astOperand1) if len(expr.str) == 1 and expr.str in '+-*/%&|^': # TODO this is incomplete e1 = getEssentialTypeCategory(expr.astOperand1) e2 = getEssentialTypeCategory(expr.astOperand2) # print('{0}: {1} {2}'.format(expr.str, e1, e2)) if e1 and e2 and e1 == e2: return e1 if expr.valueType: return expr.valueType.sign if expr.valueType and expr.valueType.typeScope and expr.valueType.typeScope.className: return "enum<" + expr.valueType.typeScope.className + ">" vartok = expr while simpleMatch(vartok, '[') or (vartok and vartok.str == '*' and vartok.astOperand2 is None): vartok = vartok.astOperand1 if vartok and vartok.variable: typeToken = vartok.variable.typeStartToken while typeToken and typeToken.isName: if typeToken.str == 'char' and not typeToken.isSigned and not typeToken.isUnsigned: return 'char' if typeToken.valueType: if typeToken.valueType.type == 'bool': return typeToken.valueType.type if typeToken.valueType.type in ('float', 'double', 'long double'): return "float" if typeToken.valueType.sign: return typeToken.valueType.sign typeToken = typeToken.next # See Appendix D, section D.6, Character constants if expr.str[0] == "'" and expr.str[-1] == "'": if len(expr.str) == 3 or (len(expr.str) == 4 and expr.str[1] == '\\'): return 'char' return expr.valueType.sign if expr.valueType: return expr.valueType.sign return None def getEssentialCategorylist(operand1, operand2): if not operand1 or not operand2: return None, None if (operand1.str in ('++', '--') or operand2.str in ('++', '--')): return None, None if ((operand1.valueType and operand1.valueType.pointer) or (operand2.valueType and operand2.valueType.pointer)): return None, None e1 = getEssentialTypeCategory(operand1) e2 = getEssentialTypeCategory(operand2) return e1, e2 def get_essential_type_from_value(value, is_signed): if value is None: return None for t in ('char', 'short', 'int', 'long', 'long long'): bits = bitsOfEssentialType(t) if bits >= 64: continue if is_signed: range_min = -(1 << (bits - 1)) range_max = (1 << (bits - 1)) - 1 else: range_min = 0 range_max = (1 << bits) - 1 sign = 'signed' if is_signed else 'unsigned' if is_signed and value < 0 and value >= range_min: return '%s %s' % (sign, t) if value >= 0 and value <= range_max: return '%s %s' % (sign, t) return None def getEssentialType(expr): if not expr: return None # See Appendix D, section D.6, Character constants if expr.str[0] == "'" and expr.str[-1] == "'": if len(expr.str) == 3 or (len(expr.str) == 4 and expr.str[1] == '\\'): return 'char' return '%s %s' % (expr.valueType.sign, expr.valueType.type) if expr.variable or isCast(expr): typeToken = expr.variable.typeStartToken if expr.variable else expr.next while typeToken and typeToken.isName: if typeToken.str == 'char' and not typeToken.isSigned and not typeToken.isUnsigned: return 'char' typeToken = typeToken.next if expr.valueType: if expr.valueType.type == 'bool': return 'bool' if expr.valueType.isFloat(): return expr.valueType.type if expr.valueType.isIntegral(): if (expr.valueType.sign is None) and expr.valueType.type == 'char': return 'char' return '%s %s' % (expr.valueType.sign, expr.valueType.type) elif expr.isNumber: # Appendix D, D.6 The essential type of literal constants # Integer constants if expr.valueType.type == 'bool': return 'bool' if expr.valueType.isFloat(): return expr.valueType.type if expr.valueType.isIntegral(): if expr.valueType.type != 'int': return '%s %s' % (expr.valueType.sign, expr.valueType.type) return get_essential_type_from_value(expr.getKnownIntValue(), expr.valueType.sign == 'signed') elif expr.str in ('<', '<=', '>=', '>', '==', '!=', '&&', '||', '!'): return 'bool' elif expr.astOperand1 and expr.astOperand2 and expr.str in ( '+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":"): if expr.astOperand1.valueType and expr.astOperand1.valueType.pointer > 0: return None if expr.astOperand2.valueType and expr.astOperand2.valueType.pointer > 0: return None e1 = getEssentialType(expr.astOperand1) e2 = getEssentialType(expr.astOperand2) if e1 is None or e2 is None: return None if is_constant_integer_expression(expr): sign1 = e1.split(' ')[0] sign2 = e2.split(' ')[0] if sign1 == sign2 and sign1 in ('signed', 'unsigned'): e = get_essential_type_from_value(expr.getKnownIntValue(), sign1 == 'signed') if e: return e if bitsOfEssentialType(e2) >= bitsOfEssentialType(e1): return e2 else: return e1 elif expr.str == "~": e1 = getEssentialType(expr.astOperand1) return e1 return None def bitsOfEssentialType(ty): if ty is None: return 0 last_type = ty.split(' ')[-1] if last_type == 'Boolean': return 1 if last_type == 'char': return typeBits['CHAR'] if last_type == 'short': return typeBits['SHORT'] if last_type == 'int': return typeBits['INT'] if ty.endswith('long long'): return typeBits['LONG_LONG'] if last_type == 'long': return typeBits['LONG'] for sty in STDINT_TYPES: if ty == sty: return int(''.join(filter(str.isdigit, sty))) return 0 def get_function_pointer_type(tok): ret = '' par = 0 while tok and (tok.isName or tok.str == '*'): ret += ' ' + tok.str tok = tok.next if tok is None or tok.str != '(': return None tok = tok.link if not simpleMatch(tok, ') ('): return None ret += '(' tok = tok.next.next while tok and (tok.str not in '()'): ret += ' ' + tok.str tok = tok.next if (tok is None) or tok.str != ')': return None return ret[1:] + ')' def isCast(expr): if not expr or expr.str != '(' or not expr.astOperand1 or expr.astOperand2: return False if simpleMatch(expr, '( )'): return False return True def is_constant_integer_expression(expr): if expr is None: return False if expr.isInt: return True if not expr.isArithmeticalOp: return False if expr.astOperand1 and not is_constant_integer_expression(expr.astOperand1): return False if expr.astOperand2 and not is_constant_integer_expression(expr.astOperand2): return False return True def isFunctionCall(expr, std='c99'): if not expr: return False if expr.str != '(' or not expr.astOperand1: return False if expr.astOperand1 != expr.previous: return False if isKeyword(expr.astOperand1.str, std): return False return True def hasExternalLinkage(var): return var.isGlobal and not var.isStatic def countSideEffects(expr): if not expr or expr.str in (',', ';'): return 0 ret = 0 if expr.str in ('++', '--', '='): ret = 1 return ret + countSideEffects(expr.astOperand1) + countSideEffects(expr.astOperand2) def getForLoopExpressions(forToken): if not forToken or forToken.str != 'for': return None lpar = forToken.next if not lpar or lpar.str != '(': return None if not lpar.astOperand2 or lpar.astOperand2.str != ';': return None if not lpar.astOperand2.astOperand2 or lpar.astOperand2.astOperand2.str != ';': return None return [lpar.astOperand2.astOperand1, lpar.astOperand2.astOperand2.astOperand1, lpar.astOperand2.astOperand2.astOperand2] def getForLoopCounterVariables(forToken): """ Return a set of Variable objects defined in ``for`` statement and satisfy requirements to loop counter term from section 8.14 of MISRA document. """ if not forToken or forToken.str != 'for': return None tn = forToken.next if not tn or tn.str != '(': return None vars_defined = set() vars_exit = set() vars_modified = set() cur_clause = 1 te = tn.link while tn and tn != te: if tn.variable: if cur_clause == 1 and tn.variable.nameToken == tn: vars_defined.add(tn.variable) elif cur_clause == 2: vars_exit.add(tn.variable) elif cur_clause == 3: if tn.next and hasSideEffectsRecursive(tn.next): vars_modified.add(tn.variable) elif tn.previous and tn.previous.str in ('++', '--'): vars_modified.add(tn.variable) if tn.str == ';': cur_clause += 1 tn = tn.next return vars_defined & vars_exit & vars_modified def findCounterTokens(cond): if not cond: return [] if cond.str in ['&&', '||']: c = findCounterTokens(cond.astOperand1) c.extend(findCounterTokens(cond.astOperand2)) return c ret = [] if ((cond.isArithmeticalOp and cond.astOperand1 and cond.astOperand2) or (cond.isComparisonOp and cond.astOperand1 and cond.astOperand2)): if cond.astOperand1.isName: ret.append(cond.astOperand1) if cond.astOperand2.isName: ret.append(cond.astOperand2) if cond.astOperand1.isOp: ret.extend(findCounterTokens(cond.astOperand1)) if cond.astOperand2.isOp: ret.extend(findCounterTokens(cond.astOperand2)) return ret def isFloatCounterInWhileLoop(whileToken): if not simpleMatch(whileToken, 'while ('): return False lpar = whileToken.next rpar = lpar.link counterTokens = findCounterTokens(lpar.astOperand2) whileBodyStart = None if simpleMatch(rpar, ') {'): whileBodyStart = rpar.next elif simpleMatch(whileToken.previous, '} while') and simpleMatch(whileToken.previous.link.previous, 'do {'): whileBodyStart = whileToken.previous.link else: return False token = whileBodyStart while token != whileBodyStart.link: token = token.next for counterToken in counterTokens: if not counterToken.valueType or not counterToken.valueType.isFloat(): continue if token.isAssignmentOp and token.astOperand1.str == counterToken.str: return True if token.str == counterToken.str and token.astParent and token.astParent.str in ('++', '--'): return True return False def hasSideEffectsRecursive(expr): if not expr or expr.str == ';': return False if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '[': prev = expr.astOperand1.previous if prev and (prev.str == '{' or prev.str == '{'): return hasSideEffectsRecursive(expr.astOperand2) if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '.': e = expr.astOperand1 while e and e.str == '.' and e.astOperand2: e = e.astOperand1 if e and e.str == '.': return False if expr.isAssignmentOp or expr.str in {'++', '--'}: return True # Todo: Check function calls return hasSideEffectsRecursive(expr.astOperand1) or hasSideEffectsRecursive(expr.astOperand2) def isBoolExpression(expr): if not expr: return False if expr.valueType and (expr.valueType.type == 'bool' or expr.valueType.bits == 1): return True return expr.str in ['!', '==', '!=', '<', '<=', '>', '>=', '&&', '||', '0', '1', 'true', 'false'] def isEnumConstant(expr): if not expr or not expr.values: return False values = expr.values return len(values) == 1 and values[0].valueKind == 'known' def isConstantExpression(expr): if expr.isNumber: return True if expr.isName and not isEnumConstant(expr): return False if simpleMatch(expr.previous, 'sizeof ('): return True if expr.astOperand1 and not isConstantExpression(expr.astOperand1): return False if expr.astOperand2 and not isConstantExpression(expr.astOperand2): return False return True def isUnsignedInt(expr): return expr and expr.valueType and expr.valueType.type in ('short', 'int') and expr.valueType.sign == 'unsigned' def getPrecedence(expr): if not expr: return 16 if not expr.astOperand1 or not expr.astOperand2: return 16 if expr.str in ('*', '/', '%'): return 12 if expr.str in ('+', '-'): return 11 if expr.str in ('<<', '>>'): return 10 if expr.str in ('<', '>', '<=', '>='): return 9 if expr.str in ('==', '!='): return 8 if expr.str == '&': return 7 if expr.str == '^': return 6 if expr.str == '|': return 5 if expr.str == '&&': return 4 if expr.str == '||': return 3 if expr.str in ('?', ':'): return 2 if expr.isAssignmentOp: return 1 if expr.str == ',': return 0 return -1 def findRawLink(token): tok1 = None tok2 = None forward = False if token.str in '{([': tok1 = token.str tok2 = '})]'['{(['.find(token.str)] forward = True elif token.str in '})]': tok1 = token.str tok2 = '{(['['})]'.find(token.str)] forward = False else: return None # try to find link indent = 0 while token: if token.str == tok1: indent = indent + 1 elif token.str == tok2: if indent <= 1: return token indent = indent - 1 if forward is True: token = token.next else: token = token.previous # raw link not found return None def numberOfParentheses(tok1, tok2): while tok1 and tok1 != tok2: if tok1.str == '(' or tok1.str == ')': return False tok1 = tok1.next return tok1 == tok2 def findGotoLabel(gotoToken): label = gotoToken.next.str tok = gotoToken.next.next while tok: if tok.str == '}' and tok.scope.type == 'Function': break if tok.str == label and tok.next.str == ':': return tok tok = tok.next return None def findInclude(directives, header): for directive in directives: if directive.str == '#include ' + header: return directive return None # Get function arguments def getArgumentsRecursive(tok, arguments): if tok is None: return if tok.str == ',': getArgumentsRecursive(tok.astOperand1, arguments) getArgumentsRecursive(tok.astOperand2, arguments) else: arguments.append(tok) def getArguments(ftok): arguments = [] getArgumentsRecursive(ftok.astOperand2, arguments) return arguments def isalnum(c): return c in string.digits or c in string.ascii_letters def isHexEscapeSequence(symbols): """Checks that given symbols are valid hex escape sequence. hexadecimal-escape-sequence: \\x hexadecimal-digit hexadecimal-escape-sequence hexadecimal-digit Reference: n1570 6.4.4.4""" if len(symbols) < 3 or symbols[:2] != '\\x': return False return all([s in string.hexdigits for s in symbols[2:]]) def isOctalEscapeSequence(symbols): r"""Checks that given symbols are valid octal escape sequence: octal-escape-sequence: \ octal-digit \ octal-digit octal-digit \ octal-digit octal-digit octal-digit Reference: n1570 6.4.4.4""" if len(symbols) not in range(2, 5) or symbols[0] != '\\': return False return all([s in string.octdigits for s in symbols[1:]]) def isSimpleEscapeSequence(symbols): """Checks that given symbols are simple escape sequence. Reference: n1570 6.4.4.4""" if len(symbols) != 2 or symbols[0] != '\\': return False return symbols[1] in ("'", '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v') def isTernaryOperator(token): if not token: return False if not token.astOperand2: return False return token.str == '?' and token.astOperand2.str == ':' def getTernaryOperandsRecursive(token): """Returns list of ternary operands including nested ones.""" if not isTernaryOperator(token): return [] result = [] result += getTernaryOperandsRecursive(token.astOperand2.astOperand1) if token.astOperand2.astOperand1 and not isTernaryOperator(token.astOperand2.astOperand1): result += [token.astOperand2.astOperand1] result += getTernaryOperandsRecursive(token.astOperand2.astOperand2) if token.astOperand2.astOperand2 and not isTernaryOperator(token.astOperand2.astOperand2): result += [token.astOperand2.astOperand2] return result def hasNumericEscapeSequence(symbols): """Check that given string contains octal or hexadecimal escape sequences.""" if '\\' not in symbols: return False for c, cn in grouped(symbols, 2): if c == '\\' and cn in ('x' + string.octdigits): return True return False def isNoReturnScope(tok): if tok is None or tok.str != '}': return False if tok.previous is None or tok.previous.str != ';': return False if simpleMatch(tok.previous.previous, 'break ;'): return True prev = tok.previous.previous while prev and prev.str not in ';{}': if prev.str in '])': prev = prev.link prev = prev.previous if prev and prev.next.str in ['throw', 'return']: return True return False # Return the token which the value is assigned to def getAssignedVariableToken(valueToken): if not valueToken: return None if not valueToken.astParent: return None operator = valueToken.astParent if operator.isAssignmentOp: return operator.astOperand1 if operator.isArithmeticalOp: return getAssignedVariableToken(operator) return None # If the value is used as a return value, return the function definition def getFunctionUsingReturnValue(valueToken): if not valueToken: return None if not valueToken.astParent: return None operator = valueToken.astParent if operator.str == 'return': return operator.scope.function if operator.isArithmeticalOp: return getFunctionUsingReturnValue(operator) return None # Return true if the token follows a specific sequence of token str values def tokenFollowsSequence(token, sequence): if not token: return False for i in reversed(sequence): prev = token.previous if not prev: return False if prev.str != i: return False token = prev return True class Define: def __init__(self, directive): self.name = '' self.args = [] self.expansionList = '' res = re.match(r'#define ([A-Za-z0-9_]+)\(([A-Za-z0-9_, ]+)\)[ ]+(.*)', directive.str) if res: self.name = res.group(1) self.args = res.group(2).strip().split(',') self.expansionList = res.group(3) else: res = re.match(r'#define ([A-Za-z0-9_]+)[ ]+(.*)', directive.str) if res: self.name = res.group(1) self.expansionList = res.group(2) def __repr__(self): attrs = ["name", "args", "expansionList"] return "{}({})".format( "Define", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def getAddonRules(): """Returns dict of MISRA rules handled by this addon.""" addon_rules = [] compiled = re.compile(r'.*def[ ]+misra_([0-9]+)_([0-9]+)[(].*') for line in open(__file__): res = compiled.match(line) if res is None: continue addon_rules.append(res.group(1) + '.' + res.group(2)) return addon_rules def getCppcheckRules(): """Returns list of rules handled by cppcheck.""" return ['1.3', # '2.1', # alwaysFalse, duplicateBreak '2.2', # alwaysTrue, redundantCondition, redundantAssignment, redundantAssignInSwitch, unreadVariable '2.6', # unusedLabel '5.3', # shadowVariable '8.3', # funcArgNamesDifferent '8.13', # constPointer '9.1', # uninitvar '14.3', # alwaysTrue, alwaysFalse, compareValueOutOfTypeRangeError '13.2', # unknownEvaluationOrder '13.6', # sizeofCalculation '17.4', # missingReturn '17.5', # argumentSize '18.1', # pointerOutOfBounds '18.2', # comparePointers '18.3', # comparePointers '18.6', # danglingLifetime '19.1', # overlappingWriteUnion, overlappingWriteFunction '20.6', # preprocessorErrorDirective '21.13', # invalidFunctionArg '21.17', # bufferAccessOutOfBounds '21.18', # bufferAccessOutOfBounds '22.1', # memleak, resourceLeak, memleakOnRealloc, leakReturnValNotUsed, leakNoVarFunctionCall '22.2', # autovarInvalidDeallocation '22.3', # incompatibleFileOpen '22.4', # writeReadOnlyFile '22.6' # useClosedFile ] def generateTable(): # print table numberOfRules = {} numberOfRules[1] = 3 numberOfRules[2] = 7 numberOfRules[3] = 2 numberOfRules[4] = 2 numberOfRules[5] = 9 numberOfRules[6] = 2 numberOfRules[7] = 4 numberOfRules[8] = 14 numberOfRules[9] = 5 numberOfRules[10] = 8 numberOfRules[11] = 9 numberOfRules[12] = 4 numberOfRules[13] = 6 numberOfRules[14] = 4 numberOfRules[15] = 7 numberOfRules[16] = 7 numberOfRules[17] = 8 numberOfRules[18] = 8 numberOfRules[19] = 2 numberOfRules[20] = 14 numberOfRules[21] = 21 numberOfRules[22] = 10 # Rules that can be checked with compilers: # compiler = ['1.1', '1.2'] addon = getAddonRules() cppcheck = getCppcheckRules() for i1 in range(1, 23): for i2 in range(1, numberOfRules[i1] + 1): num = str(i1) + '.' + str(i2) s = '' if num in addon: s = 'X (Addon)' elif num in cppcheck: s = 'X (Cppcheck)' num = num + ' ' print(num[:8] + s) def remove_file_prefix(file_path, prefix): """ Remove a file path prefix from a give path. leftover directory separators at the beginning of a file after the removal are also stripped. Example: '/remove/this/path/file.c' with a prefix of: '/remove/this/path' becomes: file.c """ result = None if file_path.startswith(prefix): result = file_path[len(prefix):] # Remove any leftover directory separators at the # beginning result = result.lstrip('\\/') else: result = file_path return result class Rule(object): """Class to keep rule text and metadata""" MISRA_SEVERITY_LEVELS = ['Required', 'Mandatory', 'Advisory'] def __init__(self, num1, num2): self.num1 = num1 self.num2 = num2 self.text = '' self.misra_severity = '' @property def num(self): return self.num1 * 100 + self.num2 @property def misra_severity(self): return self._misra_severity @misra_severity.setter def misra_severity(self, val): if val in self.MISRA_SEVERITY_LEVELS: self._misra_severity = val else: self._misra_severity = '' @property def cppcheck_severity(self): return 'style' def __repr__(self): return "%d.%d (%s)" % (self.num1, self.num2, self.misra_severity) class MisraSettings(object): """Hold settings for misra.py script.""" __slots__ = ["verify", "quiet", "show_summary"] def __init__(self, args): """ :param args: Arguments given by argparse. """ self.verify = False self.quiet = False self.show_summary = True if args.verify: self.verify = True if args.cli: self.quiet = True self.show_summary = False if args.quiet: self.quiet = True if args.no_summary: self.show_summary = False def __repr__(self): attrs = ["verify", "quiet", "show_summary", "verify"] return "{}({})".format( "MisraSettings", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class MisraChecker: def __init__(self, settings, stdversion="c89"): """ :param settings: misra.py script settings. """ self.settings = settings # Test validation rules lists self.verify_expected = list() self.verify_actual = list() # List of formatted violation messages self.violations = dict() # if --rule-texts is specified this dictionary # is loaded with descriptions of each rule # by rule number (in hundreds). # ie rule 1.2 becomes 102 self.ruleTexts = dict() # Dictionary of dictionaries for rules to suppress # Dict1 is keyed by rule number in the hundreds format of # Major * 100 + minor. ie Rule 5.2 = (5*100) + 2 # Dict 2 is keyed by filename. An entry of None means suppress globally. # Each file name entry contains a list of tuples of (lineNumber, symbolName) # or an item of None which indicates suppress rule for the entire file. # The line and symbol name tuple may have None as either of its elements but # should not be None for both. self.suppressedRules = dict() # Prefix to ignore when matching suppression files. self.filePrefix = None # Number of all violations suppressed per rule self.suppressionStats = dict() self.stdversion = stdversion self.severity = None self.existing_violations = set() self._ctu_summary_typedefs = False self._ctu_summary_tagnames = False self._ctu_summary_identifiers = False self._ctu_summary_usage = False def __repr__(self): attrs = ["settings", "verify_expected", "verify_actual", "violations", "ruleTexts", "suppressedRules", "filePrefix", "suppressionStats", "stdversion", "severity"] return "{}({})".format( "MisraChecker", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def get_num_significant_naming_chars(self, cfg): if cfg.standards and cfg.standards.c == "c89": return 31 else: return 63 def _save_ctu_summary_typedefs(self, dumpfile, typedef_info): if self._ctu_summary_typedefs: return self._ctu_summary_typedefs = True summary = [] for ti in typedef_info: summary.append({ 'name': ti.name, 'file': ti.file, 'line': ti.linenr, 'column': ti.column, 'used': ti.used }) if len(summary) > 0: cppcheckdata.reportSummary(dumpfile, 'MisraTypedefInfo', summary) def _save_ctu_summary_tagnames(self, dumpfile, cfg): if self._ctu_summary_tagnames: return self._ctu_summary_tagnames = True summary = [] # structs/enums for scope in cfg.scopes: if scope.className is None: continue if scope.type not in ('Struct', 'Enum'): continue used = False tok = scope.bodyEnd while tok: if tok.str == scope.className: used = True break tok = tok.next summary.append({'name': scope.className, 'used':used, 'file': scope.bodyStart.file, 'line': scope.bodyStart.linenr, 'column': scope.bodyStart.column}) if len(summary) > 0: cppcheckdata.reportSummary(dumpfile, 'MisraTagName', summary) def _save_ctu_summary_identifiers(self, dumpfile, cfg): if self._ctu_summary_identifiers: return self._ctu_summary_identifiers = True external_identifiers = [] internal_identifiers = [] local_identifiers = [] def identifier(nameToken): return {'name':nameToken.str, 'file':nameToken.file, 'line':nameToken.linenr, 'column':nameToken.column} names = [] for var in cfg.variables: if var.nameToken is None: continue if var.access != 'Global': if var.nameToken.str in names: continue names.append(var.nameToken.str) local_identifiers.append(identifier(var.nameToken)) elif var.isStatic: names.append(var.nameToken.str) internal_identifiers.append(identifier(var.nameToken)) else: names.append(var.nameToken.str) i = identifier(var.nameToken) i['decl'] = var.isExtern external_identifiers.append(i) for func in cfg.functions: if func.tokenDef is None: continue if func.isStatic: internal_identifiers.append(identifier(func.tokenDef)) else: i = identifier(func.tokenDef) i['decl'] = func.token is None external_identifiers.append(i) cppcheckdata.reportSummary(dumpfile, 'MisraExternalIdentifiers', external_identifiers) cppcheckdata.reportSummary(dumpfile, 'MisraInternalIdentifiers', internal_identifiers) cppcheckdata.reportSummary(dumpfile, 'MisraLocalIdentifiers', local_identifiers) def _save_ctu_summary_usage(self, dumpfile, cfg): if self._ctu_summary_usage: return self._ctu_summary_usage = True names = [] for token in cfg.tokenlist: if not token.isName: continue if token.function and token.scope.isExecutable: if (not token.function.isStatic) and (token.str not in names): names.append(token.str) elif token.variable: if token == token.variable.nameToken: continue if token.variable.access == 'Global' and (not token.variable.isStatic) and (token.str not in names): names.append(token.str) if len(names) > 0: cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names) def misra_1_4(self, cfg): for token in cfg.tokenlist: if token.str in ('_Atomic', '_Noreturn', '_Generic', '_Thread_local', '_Alignas', '_Alignof'): self.reportError(token, 1, 4) if token.str.endswith('_s') and isFunctionCall(token.next): # See C specification C11 - Annex K, page 578 if token.str in ('tmpfile_s', 'tmpnam_s', 'fopen_s', 'freopen_s', 'fprintf_s', 'fscanf_s', 'printf_s', 'scanf_s', 'snprintf_s', 'sprintf_s', 'sscanf_s', 'vfprintf_s', 'vfscanf_s', 'vprintf_s', 'vscanf_s', 'vsnprintf_s', 'vsprintf_s', 'vsscanf_s', 'gets_s', 'set_constraint_handler_s', 'abort_handler_s', 'ignore_handler_s', 'getenv_s', 'bsearch_s', 'qsort_s', 'wctomb_s', 'mbstowcs_s', 'wcstombs_s', 'memcpy_s', 'memmove_s', 'strcpy_s', 'strncpy_s', 'strcat_s', 'strncat_s', 'strtok_s', 'memset_s', 'strerror_s', 'strerrorlen_s', 'strnlen_s', 'asctime_s', 'ctime_s', 'gmtime_s', 'localtime_s', 'fwprintf_s', 'fwscanf_s', 'snwprintf_s', 'swprintf_s', 'swscanf_s', 'vfwprintf_s', 'vfwscanf_s', 'vsnwprintf_s', 'vswprintf_s', 'vswscanf_s', 'vwprintf_s', 'vwscanf_s', 'wprintf_s', 'wscanf_s', 'wcscpy_s', 'wcsncpy_s', 'wmemcpy_s', 'wmemmove_s', 'wcscat_s', 'wcsncat_s', 'wcstok_s', 'wcsnlen_s', 'wcrtomb_s', 'mbsrtowcs_s', 'wcsrtombs_s'): self.reportError(token, 1, 4) def misra_2_2(self, cfg): for token in cfg.tokenlist: if (token.str in '+-') and token.astOperand2: if simpleMatch(token.astOperand1, '0'): self.reportError(token.astOperand1, 2, 2) elif simpleMatch(token.astOperand2, '0'): self.reportError(token.astOperand2, 2, 2) if token.str == '*' and token.astOperand2: if simpleMatch(token.astOperand2, '0'): self.reportError(token.astOperand1, 2, 2) elif simpleMatch(token.astOperand1, '0'): self.reportError(token.astOperand2, 2, 2) elif simpleMatch(token.astOperand1, '1'): self.reportError(token.astOperand1, 2, 2) elif simpleMatch(token.astOperand2, '1'): self.reportError(token.astOperand2, 2, 2) def misra_2_3(self, dumpfile, typedefInfo): self._save_ctu_summary_typedefs(dumpfile, typedefInfo) def misra_2_4(self, dumpfile, cfg): self._save_ctu_summary_tagnames(dumpfile, cfg) def misra_2_5(self, dumpfile, cfg): used_macros = list() for m in cfg.macro_usage: used_macros.append(m.name) summary = [] for directive in cfg.directives: res = re.match(r'#define[ \t]+([a-zA-Z_][a-zA-Z_0-9]*).*', directive.str) if res: macro_name = res.group(1) summary.append({'name': macro_name, 'used': (macro_name in used_macros), 'file': directive.file, 'line': directive.linenr, 'column': directive.column}) if len(summary) > 0: cppcheckdata.reportSummary(dumpfile, 'MisraMacro', summary) def misra_2_7(self, data): for func in data.functions: # Skip function with no parameter if len(func.argument) == 0: continue # Setup list of function parameters func_param_list = list() for arg in func.argument: func_arg = func.argument[arg] if func_arg.typeStartToken and func_arg.typeStartToken.str == '...': continue func_param_list.append(func_arg) # Search for scope of current function for scope in data.scopes: if (scope.type == "Function") and (scope.function == func): # Search function body: remove referenced function parameter from list token = scope.bodyStart while token.next is not None and token != scope.bodyEnd and len(func_param_list) > 0: if token.variable is not None and token.variable in func_param_list: func_param_list.remove(token.variable) token = token.next # Emit a warning for each unused variable, but no more that one warning per line reported_linenrs = set() for func_param in func_param_list: if func_param.nameToken: linenr = func_param.nameToken if linenr not in reported_linenrs: self.reportError(func_param.nameToken, 2, 7) reported_linenrs.add(linenr) else: linenr = func.tokenDef.linenr if linenr not in reported_linenrs: self.reportError(func.tokenDef, 2, 7) reported_linenrs.add(linenr) def misra_3_1(self, rawTokens): for token in rawTokens: starts_with_double_slash = token.str.startswith('//') if token.str.startswith('/*') or starts_with_double_slash: s = token.str.lstrip('/') if ((not starts_with_double_slash) and '//' in s) or '/*' in s: self.reportError(token, 3, 1) def misra_3_2(self, rawTokens): for token in rawTokens: if token.str.startswith('//'): # Check for comment ends with trigraph which might be replaced # by a backslash. if token.str.endswith('??/'): self.reportError(token, 3, 2) # Check for comment which has been merged with subsequent line # because it ends with backslash. # The last backslash is no more part of the comment token thus # check if next token exists and compare line numbers. elif (token.next is not None) and (token.linenr == token.next.linenr): self.reportError(token, 3, 2) def misra_4_1(self, rawTokens): for token in rawTokens: if (token.str[0] != '"') and (token.str[0] != '\''): continue if len(token.str) < 3: continue delimiter = token.str[0] symbols = token.str[1:-1] # No closing delimiter. This will not compile. if token.str[-1] != delimiter: continue if len(symbols) < 2: continue if not hasNumericEscapeSequence(symbols): continue # String literals that contains one or more escape sequences. All of them should be # terminated. for sequence in ['\\' + t for t in symbols.split('\\')][1:]: if (isHexEscapeSequence(sequence) or isOctalEscapeSequence(sequence) or isSimpleEscapeSequence(sequence)): continue else: self.reportError(token, 4, 1) def misra_4_2(self, rawTokens): for token in rawTokens: if (token.str[0] != '"') or (token.str[-1] != '"'): continue # Check for trigraph sequence as defined by ISO/IEC 9899:1999 for sequence in ['??=', '??(', '??/', '??)', '??\'', '??<', '??!', '??>', '??-']: if sequence in token.str[1:-1]: # First trigraph sequence match, report error and leave loop. self.reportError(token, 4, 2) break def misra_5_1(self, data): long_vars = {} num_sign_chars = self.get_num_significant_naming_chars(data) for var in data.variables: if var.nameToken is None: continue if len(var.nameToken.str) <= num_sign_chars: continue if not hasExternalLinkage(var): continue long_vars.setdefault(var.nameToken.str[:num_sign_chars], []).append(var.nameToken) for name_prefix in long_vars: tokens = long_vars[name_prefix] if len(tokens) < 2: continue for tok in sorted(tokens, key=lambda t: (t.linenr, t.column))[1:]: self.reportError(tok, 5, 1) def misra_5_2(self, data): scopeVars = {} num_sign_chars = self.get_num_significant_naming_chars(data) for var in data.variables: if var.nameToken is None: continue if len(var.nameToken.str) <= num_sign_chars: continue if var.nameToken.scope not in scopeVars: scopeVars.setdefault(var.nameToken.scope, {})["varlist"] = [] scopeVars.setdefault(var.nameToken.scope, {})["scopelist"] = [] scopeVars[var.nameToken.scope]["varlist"].append(var) for scope in data.scopes: if scope.nestedIn and scope.className: if scope.nestedIn not in scopeVars: scopeVars.setdefault(scope.nestedIn, {})["varlist"] = [] scopeVars.setdefault(scope.nestedIn, {})["scopelist"] = [] scopeVars[scope.nestedIn]["scopelist"].append(scope) for scope in scopeVars: if len(scopeVars[scope]["varlist"]) <= 1: continue for i, variable1 in enumerate(scopeVars[scope]["varlist"]): for variable2 in scopeVars[scope]["varlist"][i + 1:]: if variable1.isArgument and variable2.isArgument: continue if hasExternalLinkage(variable1) or hasExternalLinkage(variable2): continue if (variable1.nameToken.str[:num_sign_chars] == variable2.nameToken.str[:num_sign_chars] and variable1 is not variable2): if int(variable1.nameToken.linenr) > int(variable2.nameToken.linenr): self.reportError(variable1.nameToken, 5, 2) else: self.reportError(variable2.nameToken, 5, 2) for innerscope in scopeVars[scope]["scopelist"]: if variable1.nameToken.str[:num_sign_chars] == innerscope.className[:num_sign_chars]: if int(variable1.nameToken.linenr) > int(innerscope.bodyStart.linenr): self.reportError(variable1.nameToken, 5, 2) else: self.reportError(innerscope.bodyStart, 5, 2) if len(scopeVars[scope]["scopelist"]) <= 1: continue for i, scopename1 in enumerate(scopeVars[scope]["scopelist"]): for scopename2 in scopeVars[scope]["scopelist"][i + 1:]: if scopename1.className[:num_sign_chars] == scopename2.className[:num_sign_chars]: if int(scopename1.bodyStart.linenr) > int(scopename2.bodyStart.linenr): self.reportError(scopename1.bodyStart, 5, 2) else: self.reportError(scopename2.bodyStart, 5, 2) def misra_5_4(self, data): num_sign_chars = self.get_num_significant_naming_chars(data) macro = {} compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)') compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]') short_names = {} macro_w_arg = [] for dir in data.directives: res1 = compile_name.match(dir.str) if res1: if dir not in macro: macro.setdefault(dir, {})["name"] = [] macro.setdefault(dir, {})["params"] = [] full_name = res1.group(1) macro[dir]["name"] = full_name short_name = full_name[:num_sign_chars] if short_name in short_names: _dir = short_names[short_name] if full_name != macro[_dir]["name"]: self.reportError(dir, 5, 4) else: short_names[short_name] = dir res2 = compile_param.match(dir.str) if res2: res_gp2 = res2.group(2).split(",") res_gp2 = [macroname.replace(" ", "") for macroname in res_gp2] macro[dir]["params"].extend(res_gp2) macro_w_arg.append(dir) for mvar in macro_w_arg: for i, macroparam1 in enumerate(macro[mvar]["params"]): for j, macroparam2 in enumerate(macro[mvar]["params"]): if j > i and macroparam1[:num_sign_chars] == macroparam2[:num_sign_chars]: self.reportError(mvar, 5, 4) param = macroparam1 if param[:num_sign_chars] in short_names: m_var1 = short_names[param[:num_sign_chars]] if m_var1.linenr > mvar.linenr: self.reportError(m_var1, 5, 4) else: self.reportError(mvar, 5, 4) def misra_5_5(self, data): num_sign_chars = self.get_num_significant_naming_chars(data) macroNames = {} compiled = re.compile(r'#define ([A-Za-z0-9_]+)') for dir in data.directives: res = compiled.match(dir.str) if res: macroNames[res.group(1)[:num_sign_chars]] = dir for var in data.variables: if var.nameToken and var.nameToken.str[:num_sign_chars] in macroNames: self.reportError(var.nameToken, 5, 5) for scope in data.scopes: if scope.className and scope.className[:num_sign_chars] in macroNames: self.reportError(scope.bodyStart, 5, 5) def misra_5_6(self, dumpfile, typedefInfo): self._save_ctu_summary_typedefs(dumpfile, typedefInfo) def misra_5_7(self, dumpfile, cfg): self._save_ctu_summary_tagnames(dumpfile, cfg) def misra_5_8(self, dumpfile, cfg): self._save_ctu_summary_identifiers(dumpfile, cfg) def misra_5_9(self, dumpfile, cfg): self._save_ctu_summary_identifiers(dumpfile, cfg) def misra_6_1(self, data): # Bitfield type must be bool or explicitly signed/unsigned int for token in data.tokenlist: if not token.valueType: continue if token.valueType.bits == 0: continue if not token.variable: continue if not token.scope: continue if token.scope.type not in 'Struct': continue if data.standards.c == 'c89': if token.valueType.type != 'int' and not isUnsignedType(token.variable.typeStartToken.str): self.reportError(token, 6, 1) elif data.standards.c == 'c99': if token.valueType.type == 'bool': continue isExplicitlySignedOrUnsigned = False typeToken = token.variable.typeStartToken while typeToken: if typeToken.isUnsigned or typeToken.isSigned or isUnsignedType(typeToken.str): isExplicitlySignedOrUnsigned = True break if typeToken is token.variable.typeEndToken: break typeToken = typeToken.next if not isExplicitlySignedOrUnsigned: self.reportError(token, 6, 1) def misra_6_2(self, data): # Bitfields of size 1 can not be signed for token in data.tokenlist: if not token.valueType: continue if not token.scope: continue if token.scope.type not in 'Struct': continue if token.valueType.bits == 1 and token.valueType.sign == 'signed': self.reportError(token, 6, 2) def misra_7_1(self, rawTokens): compiled = re.compile(r'^0[0-7]+$') for tok in rawTokens: if compiled.match(tok.str): self.reportError(tok, 7, 1) def misra_7_2(self, data): # Large constant numbers that are assigned to a variable should have an # u/U suffix if the variable type is unsigned. def reportErrorIfMissingSuffix(variable, value): if 'U' in value.str.upper(): return if value and value.isNumber: if variable and variable.valueType and variable.valueType.sign == 'unsigned': if variable.valueType.type in ['char', 'short', 'int', 'long', 'long long']: limit = 1 << (bitsOfEssentialType(variable.valueType.type) -1) v = value.getKnownIntValue() if v is not None and v >= limit: self.reportError(value, 7, 2) for token in data.tokenlist: # Check normal variable assignment if token.valueType and token.isNumber: variable = getAssignedVariableToken(token) reportErrorIfMissingSuffix(variable, token) # Check use as function parameter if isFunctionCall(token) and token.astOperand1 and token.astOperand1.function: functionDeclaration = token.astOperand1.function if functionDeclaration.tokenDef: if functionDeclaration.tokenDef is token.astOperand1: # Token is not a function call, but it is the definition of the function continue parametersUsed = getArguments(token) for i in range(len(parametersUsed)): usedParameter = parametersUsed[i] if usedParameter.isNumber: parameterDefinition = functionDeclaration.argument.get(i+1) if parameterDefinition and parameterDefinition.nameToken: reportErrorIfMissingSuffix(parameterDefinition.nameToken, usedParameter) def misra_7_3(self, rawTokens): compiled = re.compile(r'^[0-9.]+[Uu]*l+[Uu]*$') for tok in rawTokens: if compiled.match(tok.str): self.reportError(tok, 7, 3) def misra_7_4(self, data): # A string literal shall not be assigned to an object unless the object's type # is constant. def reportErrorIfVariableIsNotConst(variable, stringLiteral): if variable.valueType: if (variable.valueType.constness % 2) != 1: self.reportError(stringLiteral, 7, 4) for token in data.tokenlist: if token.isString: # Check normal variable assignment variable = getAssignedVariableToken(token) if variable: reportErrorIfVariableIsNotConst(variable, token) # Check use as return value function = getFunctionUsingReturnValue(token) if function: # "Primitive" test since there is no info available on return value type if not tokenFollowsSequence(function.tokenDef, ['const', 'char', '*']): self.reportError(token, 7, 4) # Check use as function parameter if isFunctionCall(token) and token.astOperand1 and token.astOperand1.function: functionDeclaration = token.astOperand1.function if functionDeclaration.tokenDef: if functionDeclaration.tokenDef is token.astOperand1: # Token is not a function call, but it is the definition of the function continue parametersUsed = getArguments(token) for i in range(len(parametersUsed)): usedParameter = parametersUsed[i] parameterDefinition = functionDeclaration.argument.get(i+1) if usedParameter.isString and parameterDefinition.nameToken: reportErrorIfVariableIsNotConst(parameterDefinition.nameToken, usedParameter) def misra_8_1(self, cfg): for token in cfg.tokenlist: if token.isImplicitInt: self.reportError(token, 8, 1) def misra_8_2(self, data, rawTokens): def getFollowingRawTokens(rawTokens, token, count): following =[] for rawToken in rawTokens: if (rawToken.file == token.file and rawToken.linenr == token.linenr and rawToken.column == token.column): for _ in range(count): rawToken = rawToken.next # Skip comments while rawToken and (rawToken.str.startswith('/*') or rawToken.str.startswith('//')): rawToken = rawToken.next if rawToken is None: break following.append(rawToken) return following # Zero arguments should be in form ( void ) def checkZeroArguments(func, startCall, endCall): if (len(func.argument) == 0): voidArg = startCall.next while voidArg is not endCall: if voidArg.str == 'void': break voidArg = voidArg.next if not voidArg.str == 'void': if func.tokenDef.next: self.reportError(func.tokenDef.next, 8, 2) else: self.reportError(func.tokenDef, 8, 2) def checkDeclarationArgumentsViolations(func, startCall, endCall): # Collect the tokens for the arguments in function definition argNameTokens = set() for arg in func.argument: argument = func.argument[arg] typeStartToken = argument.typeStartToken if typeStartToken is None: continue nameToken = argument.nameToken if nameToken is None: continue argNameTokens.add(nameToken) # Check if we have the same number of variables in both the # declaration and the definition. # # TODO: We actually need to check if the names of the arguments are # the same. But we can't do this because we have no links to # variables in the arguments in function definition in the dump file. foundVariables = 0 while startCall and startCall != endCall: if startCall.varId: foundVariables += 1 startCall = startCall.next if len(argNameTokens) != foundVariables: if func.tokenDef.next: self.reportError(func.tokenDef.next, 8, 2) else: self.reportError(func.tokenDef, 8, 2) def checkDefinitionArgumentsViolations(func, startCall, endCall): for arg in func.argument: argument = func.argument[arg] typeStartToken = argument.typeStartToken if typeStartToken is None: continue # Arguments should have a name unless variable length arg nameToken = argument.nameToken if nameToken is None and typeStartToken.str != '...': self.reportError(typeStartToken, 8, 2) # Type declaration on next line (old style declaration list) is not allowed if typeStartToken.linenr > endCall.linenr: self.reportError(typeStartToken, 8, 2) # Check arguments in function declaration for func in data.functions: # Check arguments in function definition tokenImpl = func.token if tokenImpl: startCall = tokenImpl.next if startCall is None or startCall.str != '(': continue endCall = startCall.link if endCall is None or endCall.str != ')': continue checkZeroArguments(func, startCall, endCall) checkDefinitionArgumentsViolations(func, startCall, endCall) # Check arguments in function declaration tokenDef = func.tokenDef if tokenDef: startCall = func.tokenDef.next if startCall is None or startCall.str != '(': continue endCall = startCall.link if endCall is None or endCall.str != ')': continue checkZeroArguments(func, startCall, endCall) if tokenImpl: checkDeclarationArgumentsViolations(func, startCall, endCall) else: # When there is no function definition, we should execute # its checks for the declaration token. The point is that without # a known definition we have no Function.argument list required # for declaration check. checkDefinitionArgumentsViolations(func, startCall, endCall) # Check arguments in pointer declarations for var in data.variables: if not var.isPointer: continue if var.nameToken is None: continue rawTokensFollowingPtr = getFollowingRawTokens(rawTokens, var.nameToken, 3) if len(rawTokensFollowingPtr) != 3: continue # Compliant: returnType (*ptrName) ( ArgType ) # Non-compliant: returnType (*ptrName) ( ) if (rawTokensFollowingPtr[0].str == ')' and rawTokensFollowingPtr[1].str == '(' and rawTokensFollowingPtr[2].str == ')'): self.reportError(var.nameToken, 8, 2) def misra_8_4(self, cfg): for func in cfg.functions: if func.isStatic: continue if func.token is None: continue if not is_source_file(func.token.file): continue if func.token != func.tokenDef: continue if func.tokenDef.str == 'main': continue self.reportError(func.tokenDef, 8, 4) extern_vars = [] var_defs = [] for var in cfg.variables: if not var.isGlobal: continue if var.isStatic: continue if var.nameToken is None: continue if var.isExtern: extern_vars.append(var.nameToken.str) else: var_defs.append(var.nameToken) for vartok in var_defs: if vartok.str not in extern_vars: self.reportError(vartok, 8, 4) def misra_8_5(self, dumpfile, cfg): self._save_ctu_summary_identifiers(dumpfile, cfg) def misra_8_6(self, dumpfile, cfg): self._save_ctu_summary_identifiers(dumpfile, cfg) def misra_8_7(self, dumpfile, cfg): self._save_ctu_summary_usage(dumpfile, cfg) def misra_8_8(self, cfg): vars = {} for var in cfg.variables: if var.access != 'Global': continue if var.nameToken is None: continue varname = var.nameToken.str if varname in vars: vars[varname].append(var) else: vars[varname] = [var] for varname, varlist in vars.items(): static_var = None extern_var = None for var in varlist: if var.isStatic: static_var = var elif var.isExtern: extern_var = var if static_var and extern_var: self.reportError(extern_var.nameToken, 8, 8) def misra_8_9(self, cfg): variables = {} for scope in cfg.scopes: if scope.type != 'Function': continue variables_used_in_scope = [] tok = scope.bodyStart while tok != scope.bodyEnd: if tok.variable and tok.variable.access == 'Global' and tok.variable.isStatic: if tok.variable not in variables_used_in_scope: variables_used_in_scope.append(tok.variable) tok = tok.next for var in variables_used_in_scope: if var in variables: variables[var] += 1 else: variables[var] = 1 for var, count in variables.items(): if count == 1: self.reportError(var.nameToken, 8, 9) def misra_8_10(self, cfg): for func in cfg.functions: if func.isInlineKeyword and not func.isStatic: self.reportError(func.tokenDef, 8, 10) def misra_8_11(self, data): for var in data.variables: if var.isExtern and simpleMatch(var.nameToken.next, '[ ]') and var.nameToken.scope.type == 'Global': self.reportError(var.nameToken, 8, 11) def misra_8_12(self, data): for scope in data.scopes: if scope.type != 'Enum': continue enum_values = [] implicit_enum_values = [] e_token = scope.bodyStart.next while e_token != scope.bodyEnd: if e_token.str == '(': e_token = e_token.link continue if e_token.previous.str not in ',{': e_token = e_token.next continue if e_token.isName and e_token.values and e_token.valueType and e_token.valueType.typeScope == scope: token_values = [v.intvalue for v in e_token.values] enum_values += token_values if e_token.next.str != "=": implicit_enum_values += token_values e_token = e_token.next for implicit_enum_value in implicit_enum_values: if enum_values.count(implicit_enum_value) != 1: self.reportError(scope.bodyStart, 8, 12) def misra_8_14(self, rawTokens): for token in rawTokens: if token.str == 'restrict': self.reportError(token, 8, 14) def misra_9_2(self, data): misra_9.misra_9_x(self, data, 902) def misra_9_3(self, data): misra_9.misra_9_x(self, data, 903) def misra_9_4(self, data): misra_9.misra_9_x(self, data, 904) def misra_9_5(self, data, rawTokens): misra_9.misra_9_x(self, data, 905, rawTokens) #for token in rawTokens: # if simpleMatch(token, '[ ] = { ['): # self.reportError(token, 9, 5) def misra_10_1(self, data): for token in data.tokenlist: if not token.isOp: continue for t1, t2 in itertools.product( list(getTernaryOperandsRecursive(token.astOperand1) or [token.astOperand1]), list(getTernaryOperandsRecursive(token.astOperand2) or [token.astOperand2]), ): e1 = getEssentialTypeCategory(t1) e2 = getEssentialTypeCategory(t2) if not e1 or not e2: continue if token.str in ('<<', '>>'): if not isUnsignedType(e1): self.reportError(token, 10, 1) elif not isUnsignedType(e2) and not token.astOperand2.isNumber: self.reportError(token, 10, 1) elif token.str in ('~', '&', '|', '^'): e1_et = getEssentialType(token.astOperand1) e2_et = getEssentialType(token.astOperand2) if e1_et == 'char' or e2_et == 'char': self.reportError(token, 10, 1) def misra_10_2(self, data): def isEssentiallySignedOrUnsigned(op): e = getEssentialType(op) return e and (e.split(' ')[0] in ('unsigned', 'signed')) def isEssentiallyChar(op): if op is None: return False if op.str == '+': return isEssentiallyChar(op.astOperand1) or isEssentiallyChar(op.astOperand2) return op.isChar for token in data.tokenlist: if token.str not in ('+', '-'): continue if (not isEssentiallyChar(token.astOperand1)) and (not isEssentiallyChar(token.astOperand2)): continue if token.str == '+': if isEssentiallyChar(token.astOperand1) and not isEssentiallySignedOrUnsigned(token.astOperand2): self.reportError(token, 10, 2) if isEssentiallyChar(token.astOperand2) and not isEssentiallySignedOrUnsigned(token.astOperand1): self.reportError(token, 10, 2) if token.str == '-': e1 = getEssentialType(token.astOperand1) if e1 and e1.split(' ')[-1] != 'char': self.reportError(token, 10, 2) if not isEssentiallyChar(token.astOperand2) and not isEssentiallySignedOrUnsigned(token.astOperand2): self.reportError(token, 10, 2) def misra_10_3(self, cfg): def get_category(essential_type): if essential_type: if essential_type in ('bool', 'char'): return essential_type if essential_type.split(' ')[-1] in ('float', 'double'): return 'floating' if essential_type.split(' ')[0] in ('unsigned', 'signed'): return essential_type.split(' ')[0] return None for tok in cfg.tokenlist: if tok.isAssignmentOp: lhs = getEssentialType(tok.astOperand1) rhs = getEssentialType(tok.astOperand2) #print(lhs) #print(rhs) if lhs is None or rhs is None: continue lhs_category = get_category(lhs) rhs_category = get_category(rhs) if lhs_category and rhs_category and lhs_category != rhs_category and rhs_category not in ('signed','unsigned'): self.reportError(tok, 10, 3) if bitsOfEssentialType(lhs) < bitsOfEssentialType(rhs): self.reportError(tok, 10, 3) def misra_10_4(self, data): op = {'+', '-', '*', '/', '%', '&', '|', '^', '+=', '-=', ':'} for token in data.tokenlist: if token.str not in op and not token.isComparisonOp: continue if not token.astOperand1 or not token.astOperand2: continue if not token.astOperand1.valueType or not token.astOperand2.valueType: continue if ((token.astOperand1.str in op or token.astOperand1.isComparisonOp) and (token.astOperand2.str in op or token.astOperand1.isComparisonOp)): e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2.astOperand1) elif token.astOperand1.str in op or token.astOperand1.isComparisonOp: e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2) elif token.astOperand2.str in op or token.astOperand2.isComparisonOp: e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2.astOperand1) else: e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2) if token.str == "+=" or token.str == "+": if e1 == "char" and (e2 == "signed" or e2 == "unsigned"): continue if e2 == "char" and (e1 == "signed" or e1 == "unsigned"): continue if token.str == "-=" or token.str == "-": if e1 == "char" and (e2 == "signed" or e2 == "unsigned"): continue if e1 and e2 and (e1.find('Anonymous') != -1 and (e2 == "signed" or e2 == "unsigned")): continue if e1 and e2 and (e2.find('Anonymous') != -1 and (e1 == "signed" or e1 == "unsigned")): continue if e1 and e2 and e1 != e2: self.reportError(token, 10, 4) def misra_10_5(self, cfg): def _get_essential_category(token): essential_type = getEssentialType(token) #print(essential_type) if essential_type: if essential_type in ('bool', 'char'): return essential_type if essential_type.split(' ')[-1] in ('float', 'double'): return 'floating' if essential_type.split(' ')[0] in ('unsigned', 'signed'): return essential_type.split(' ')[0] return None for token in cfg.tokenlist: if not isCast(token): continue to_type = _get_essential_category(token) #print(to_type) if to_type is None: continue from_type = _get_essential_category(token.astOperand1) #print(from_type) if from_type is None: continue if to_type == from_type: continue if to_type == 'bool' or from_type == 'bool': if token.astOperand1.isInt and token.astOperand1.getKnownIntValue() == 1: # Exception continue self.reportError(token, 10, 5) continue if to_type == 'enum': self.reportError(token, 10, 5) continue if from_type == 'float' and to_type == 'char': self.reportError(token, 10, 5) continue if from_type == 'char' and to_type == 'float': self.reportError(token, 10, 5) continue def misra_10_6(self, data): for token in data.tokenlist: if token.str != '=' or not token.astOperand1 or not token.astOperand2: continue if (token.astOperand2.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~') and not isCast(token.astOperand2)): continue vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if not vt1 or vt1.pointer > 0: continue if not vt2 or vt2.pointer > 0: continue try: if isCast(token.astOperand2): e = vt2.type else: e = getEssentialType(token.astOperand2) if not e: continue lhsbits = vt1.bits if vt1.bits else bitsOfEssentialType(vt1.type) if lhsbits > bitsOfEssentialType(e): self.reportError(token, 10, 6) except ValueError: pass def misra_10_7(self, cfg): for token in cfg.tokenlist: if token.astOperand1 is None or token.astOperand2 is None: continue if not token.isArithmeticalOp: continue parent = token.astParent if parent is None: continue if not parent.isArithmeticalOp: if not parent.isAssignmentOp: continue if parent.str == '=': continue token_type = getEssentialType(token) if token_type is None: continue sibling = parent.astOperand1 if (token == parent.astOperand2) else parent.astOperand2 sibling_type = getEssentialType(sibling) if sibling_type is None: continue b1 = bitsOfEssentialType(token_type) b2 = bitsOfEssentialType(sibling_type) if b1 > 0 and b1 < b2: self.reportError(token, 10, 7) def misra_10_8(self, data): for token in data.tokenlist: if not isCast(token): continue if not token.valueType or token.valueType.pointer > 0: continue if not token.astOperand1.valueType or token.astOperand1.valueType.pointer > 0: continue if not token.astOperand1.astOperand1: continue if token.astOperand1.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~'): continue if token.astOperand1.str != '~' and not token.astOperand1.astOperand2: continue if token.astOperand1.str == '~': e2 = getEssentialTypeCategory(token.astOperand1.astOperand1) else: e2, e3 = getEssentialCategorylist(token.astOperand1.astOperand1, token.astOperand1.astOperand2) if e2 != e3: continue e1 = getEssentialTypeCategory(token) if e1 != e2: self.reportError(token, 10, 8) else: try: e = getEssentialType(token.astOperand1) if not e: continue if bitsOfEssentialType(token.valueType.type) > bitsOfEssentialType(e): self.reportError(token, 10, 8) except ValueError: pass def misra_11_1(self, data): for token in data.tokenlist: to_from = get_type_conversion_to_from(token) if to_from is None: continue from_type = get_function_pointer_type(to_from[1]) if from_type is None: continue to_type = get_function_pointer_type(to_from[0]) if to_type is None or to_type != from_type: self.reportError(token, 11, 1) def misra_11_2(self, data): def get_pointer_type(type_token): while type_token and (type_token.str in ('const', 'struct')): type_token = type_token.next if type_token is None: return None if not type_token.isName: return None return type_token if (type_token.next and type_token.next.str == '*') else None incomplete_types = [] for token in data.tokenlist: if token.str == 'struct' and token.next and token.next.next and token.next.isName and token.next.next.str == ';': incomplete_types.append(token.next.str) to_from = get_type_conversion_to_from(token) if to_from is None: continue to_pointer_type_token = get_pointer_type(to_from[0]) if to_pointer_type_token is None: continue from_pointer_type_token = get_pointer_type(to_from[1]) if from_pointer_type_token is None: continue if to_pointer_type_token.str == from_pointer_type_token.str: continue if from_pointer_type_token.typeScope is None and (from_pointer_type_token.str in incomplete_types): self.reportError(token, 11, 2) elif to_pointer_type_token.typeScope is None and (to_pointer_type_token.str in incomplete_types): self.reportError(token, 11, 2) def misra_11_3(self, data): for token in data.tokenlist: if not isCast(token): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt1.type == 'void' or vt2.type == 'void': continue if (vt1.pointer > 0 and vt1.type == 'record' and vt2.pointer > 0 and vt2.type == 'record' and vt1.typeScopeId != vt2.typeScopeId): self.reportError(token, 11, 3) elif (vt1.pointer == vt2.pointer and vt1.pointer > 0 and vt1.type != vt2.type and vt1.type != 'char'): self.reportError(token, 11, 3) def misra_11_4(self, data): for token in data.tokenlist: if not isCast(token): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt2.pointer > 0 and vt1.pointer == 0 and (vt1.isIntegral() or vt1.isEnum()) and vt2.type != 'void': self.reportError(token, 11, 4) elif vt1.pointer > 0 and vt2.pointer == 0 and (vt2.isIntegral() or vt2.isEnum()) and vt1.type != 'void': self.reportError(token, 11, 4) def misra_11_5(self, data): for token in data.tokenlist: if not isCast(token): if token.astOperand1 and token.astOperand2 and token.str == "=" and token.next.str != "(": vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if not vt1 or not vt2: continue if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': self.reportError(token, 11, 5) continue if token.astOperand1.astOperand1 and token.astOperand1.astOperand1.str in ( 'malloc', 'calloc', 'realloc', 'free'): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': self.reportError(token, 11, 5) def misra_11_6(self, data): for token in data.tokenlist: if not isCast(token): continue if token.astOperand1.astOperand1: continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt1.pointer == 1 and vt1.type == 'void' and vt2.pointer == 0 and token.astOperand1.str != "0": self.reportError(token, 11, 6) elif vt1.pointer == 0 and vt1.type != 'void' and vt2.pointer == 1 and vt2.type == 'void': self.reportError(token, 11, 6) def misra_11_7(self, data): for token in data.tokenlist: if not isCast(token): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if token.astOperand1.astOperand1: continue if (vt2.pointer > 0 and vt1.pointer == 0 and not vt1.isIntegral() and not vt1.isEnum() and vt1.type != 'void'): self.reportError(token, 11, 7) elif (vt1.pointer > 0 and vt2.pointer == 0 and not vt2.isIntegral() and not vt2.isEnum() and vt1.type != 'void'): self.reportError(token, 11, 7) def misra_11_8(self, data): # TODO: reuse code in CERT-EXP05 for token in data.tokenlist: if isCast(token): # C-style cast if not token.valueType: continue if not token.astOperand1.valueType: continue if token.valueType.pointer == 0: continue if token.astOperand1.valueType.pointer == 0: continue const1 = token.valueType.constness const2 = token.astOperand1.valueType.constness if (const1 % 2) < (const2 % 2): self.reportError(token, 11, 8) elif token.str == '(' and token.astOperand1 and token.astOperand2 and token.astOperand1.function: # Function call function = token.astOperand1.function arguments = getArguments(token) for argnr, argvar in function.argument.items(): if argnr < 1 or argnr > len(arguments): continue if not argvar.isPointer: continue argtok = arguments[argnr - 1] if not argtok.valueType: continue if argtok.valueType.pointer == 0: continue const1 = argvar.constness const2 = arguments[argnr - 1].valueType.constness if (const1 % 2) < (const2 % 2): self.reportError(token, 11, 8) def misra_11_9(self, data): for token in data.tokenlist: if token.astOperand1 and token.astOperand2 and token.str in ["=", "==", "!=", "?", ":"]: vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if not vt1 or not vt2: continue if vt1.pointer > 0 and vt2.pointer == 0 and token.astOperand2.str == "NULL": continue if (token.astOperand2.values and vt1.pointer > 0 and vt2.pointer == 0 and token.astOperand2.values): if token.astOperand2.getValue(0): self.reportError(token, 11, 9) def misra_12_1_sizeof(self, rawTokens): state = 0 compiled = re.compile(r'^[a-zA-Z_]') for tok in rawTokens: if tok.str.startswith('//') or tok.str.startswith('/*'): continue if tok.str == 'sizeof': state = 1 elif state == 1: if compiled.match(tok.str): state = 2 else: state = 0 elif state == 2: if tok.str in ('+', '-', '*', '/', '%'): self.reportError(tok, 12, 1) else: state = 0 def misra_12_1(self, data): for token in data.tokenlist: p = getPrecedence(token) if p < 2 or p > 12: continue p1 = getPrecedence(token.astOperand1) if p < p1 <= 12 and numberOfParentheses(token.astOperand1, token): self.reportError(token, 12, 1) continue p2 = getPrecedence(token.astOperand2) if p < p2 <= 12 and numberOfParentheses(token, token.astOperand2): self.reportError(token, 12, 1) continue def misra_12_2(self, data): for token in data.tokenlist: if not (token.str in ('<<', '>>')): continue if (not token.astOperand2) or (not token.astOperand2.values): continue maxval = 0 for val in token.astOperand2.values: if val.intvalue and val.intvalue > maxval: maxval = val.intvalue if maxval == 0: continue sz = bitsOfEssentialType(getEssentialType(token.astOperand1)) if sz <= 0: continue if maxval >= sz: self.reportError(token, 12, 2) def misra_12_3(self, data): for token in data.tokenlist: if token.str == ';' and (token.isSplittedVarDeclComma is True): self.reportError(token, 12, 3) if token.str == ',' and token.astParent and token.astParent.str == ';': self.reportError(token, 12, 3) if token.str == ',' and token.astParent is None: if token.scope.type in ('Class', 'Struct'): # Is this initlist.. tok = token while tok and tok.str == ',': tok = tok.next if tok and tok.next and tok.isName and tok.next.str == '(': tok = tok.next.link.next if tok.str == '{': # This comma is used in initlist, do not warn continue prev = token.previous while prev: if prev.str == ';': self.reportError(token, 12, 3) break elif prev.str in ')}]': prev = prev.link elif prev.str in '({[': break prev = prev.previous def misra_12_4(self, cfg): for expr in cfg.tokenlist: if not expr.astOperand2 or not expr.astOperand1: continue if expr.valueType is None: continue if expr.valueType.sign is None or expr.valueType.sign != 'unsigned': continue if expr.valueType.pointer > 0: continue if not expr.valueType.isIntegral(): continue op1 = expr.astOperand1.getKnownIntValue() if op1 is None: continue op2 = expr.astOperand2.getKnownIntValue() if op2 is None: continue bits = bitsOfEssentialType('unsigned ' + expr.valueType.type) if bits <= 0 or bits >= 64: continue max_value = (1 << bits) - 1 if not is_constant_integer_expression(expr): continue if expr.str == '+' and op1 + op2 > max_value: self.reportError(expr, 12, 4) elif expr.str == '-' and op1 - op2 < 0: self.reportError(expr, 12, 4) elif expr.str == '*' and op1 * op2 > max_value: self.reportError(expr, 12, 4) def misra_13_1(self, data): for token in data.tokenlist: if simpleMatch(token, ") {") and token.next.astParent == token.link: pass elif not simpleMatch(token, '= {'): continue init = token.next end = init.link if not end: continue # syntax is broken tn = init while tn and tn != end: if tn.str == '[' and tn.link: tn = tn.link if tn and tn.next and tn.next.str == '=': tn = tn.next.next continue else: break if tn.str == '.' and tn.next and tn.next.isName: tn = tn.next if tn.next and tn.next.str == '=': tn = tn.next.next continue if tn.str in {'++', '--'} or tn.isAssignmentOp: self.reportError(init, 13, 1) tn = tn.next def misra_13_3(self, data): for token in data.tokenlist: if token.str not in ('++', '--'): continue astTop = token while astTop.astParent and astTop.astParent.str not in (',', ';'): astTop = astTop.astParent if countSideEffects(astTop) >= 2: self.reportError(astTop, 13, 3) def misra_13_4(self, data): for token in data.tokenlist: if token.str != '=': continue if not token.astParent: continue if token.astOperand1.str == '[' and token.astOperand1.previous.str in ('{', ','): continue if not (token.astParent.str in [',', ';', '{']): self.reportError(token, 13, 4) def misra_13_5(self, data): for token in data.tokenlist: if token.isLogicalOp and hasSideEffectsRecursive(token.astOperand2): self.reportError(token, 13, 5) def misra_13_6(self, data): for token in data.tokenlist: if token.str == 'sizeof' and hasSideEffectsRecursive(token.next): self.reportError(token, 13, 6) def misra_14_1(self, data): for token in data.tokenlist: if token.str == 'for': exprs = getForLoopExpressions(token) if not exprs: continue for counter in findCounterTokens(exprs[1]): if counter.valueType and counter.valueType.isFloat(): self.reportError(token, 14, 1) elif token.str == 'while': if isFloatCounterInWhileLoop(token): self.reportError(token, 14, 1) def misra_14_2(self, data): for token in data.tokenlist: expressions = getForLoopExpressions(token) if not expressions: continue if expressions[0] and not expressions[0].isAssignmentOp: self.reportError(token, 14, 2) elif hasSideEffectsRecursive(expressions[1]): self.reportError(token, 14, 2) # Inspect modification of loop counter in loop body counter_vars = getForLoopCounterVariables(token) outer_scope = token.scope body_scope = None tn = token.next while tn and tn.next != outer_scope.bodyEnd: if tn.scope and tn.scope.nestedIn == outer_scope: body_scope = tn.scope break tn = tn.next if not body_scope: continue tn = body_scope.bodyStart while tn and tn != body_scope.bodyEnd: if tn.variable and tn.variable in counter_vars: if tn.next: # TODO: Check modifications in function calls if hasSideEffectsRecursive(tn.next): self.reportError(tn, 14, 2) tn = tn.next def misra_14_4(self, data): for token in data.tokenlist: if token.str != '(': continue if not token.astOperand1 or not (token.astOperand1.str in ['if', 'while']): continue if not isBoolExpression(token.astOperand2): self.reportError(token, 14, 4) def misra_15_1(self, data): for token in data.tokenlist: if token.str == "goto": self.reportError(token, 15, 1) def misra_15_2(self, data): for token in data.tokenlist: if token.str != 'goto': continue if (not token.next) or (not token.next.isName): continue if not findGotoLabel(token): self.reportError(token, 15, 2) def misra_15_3(self, data): for token in data.tokenlist: if token.str != 'goto': continue if (not token.next) or (not token.next.isName): continue tok = findGotoLabel(token) if not tok: continue scope = token.scope while scope and scope != tok.scope: scope = scope.nestedIn if not scope: self.reportError(token, 15, 3) # Jump crosses from one switch-clause to another is non-compliant elif scope.type == 'Switch': # Search for start of a current case block tcase_start = token while tcase_start and tcase_start.str not in ('case', 'default'): tcase_start = tcase_start.previous # Make sure that goto label doesn't occurs in the other # switch-clauses if tcase_start: t = scope.bodyStart in_this_case = False while t and t != scope.bodyEnd: if t == tcase_start: in_this_case = True if in_this_case and t.str not in ('case', 'default'): in_this_case = False if t == tok and not in_this_case: self.reportError(token, 15, 3) break t = t.next def misra_15_4(self, data): # Return a list of scopes affected by a break or goto def getLoopsAffectedByBreak(knownLoops, scope, isGoto): if scope and scope.type and scope.type not in ['Global', 'Function']: if not isGoto and scope.type == 'Switch': return if scope.type in ['For', 'While', 'Do']: knownLoops.append(scope) if not isGoto: return getLoopsAffectedByBreak(knownLoops, scope.nestedIn, isGoto) loopWithBreaks = {} for token in data.tokenlist: if token.str not in ['break', 'goto']: continue affectedLoopScopes = [] getLoopsAffectedByBreak(affectedLoopScopes, token.scope, token.str == 'goto') for scope in affectedLoopScopes: if scope in loopWithBreaks: loopWithBreaks[scope] += 1 else: loopWithBreaks[scope] = 1 for scope, breakCount in loopWithBreaks.items(): if breakCount > 1: self.reportError(scope.bodyStart, 15, 4) def misra_15_5(self, data): for token in data.tokenlist: if token.str == 'return' and token.scope.type != 'Function': self.reportError(token, 15, 5) def misra_15_6(self, rawTokens): state = 0 indent = 0 tok1 = None def tokAt(tok,i): while i < 0 and tok: tok = tok.previous if tok.str.startswith('//') or tok.str.startswith('/*'): continue i += 1 while i > 0 and tok: tok = tok.next if tok.str.startswith('//') or tok.str.startswith('/*'): continue i -= 1 return tok def strtokens(tok, i1, i2): tok1 = tokAt(tok, i1) tok2 = tokAt(tok, i2) tok = tok1 s = '' while tok != tok2: if tok.str.startswith('//') or tok.str.startswith('/*'): tok = tok.next continue s += ' ' + tok.str tok = tok.next s += ' ' + tok.str return s[1:] for token in rawTokens: if token.str in ['if', 'for', 'while']: if strtokens(token,-1,0) == '# if': continue if strtokens(token,-1,0) == "} while": # is there a 'do { .. } while'? start = rawlink(tokAt(token,-1)) if start and strtokens(start, -1, 0) == 'do {': continue if state == 2: self.reportError(tok1, 15, 6) state = 1 indent = 0 tok1 = token elif token.str == 'else': if strtokens(token,-1,0) == '# else': continue if strtokens(token,0,1) == 'else if': continue if state == 2: self.reportError(tok1, 15, 6) state = 2 indent = 0 tok1 = token elif state == 1: if indent == 0 and token.str != '(': state = 0 continue if token.str == '(': indent = indent + 1 elif token.str == ')': if indent == 0: state = 0 elif indent == 1: state = 2 indent = indent - 1 elif state == 2: if token.str.startswith('//') or token.str.startswith('/*'): continue state = 0 if token.str not in ('{', '#'): self.reportError(tok1, 15, 6) def misra_15_7(self, data): for scope in data.scopes: if scope.type != 'Else': continue if not simpleMatch(scope.bodyStart, '{ if ('): continue if scope.bodyStart.column > 0: continue tok = scope.bodyStart.next.next.link if not simpleMatch(tok, ') {'): continue tok = tok.next.link if not simpleMatch(tok, '} else'): self.reportError(tok, 15, 7) def misra_16_1(self, cfg): for scope in cfg.scopes: if scope.type != 'Switch': continue in_case_or_default = False tok = scope.bodyStart.next while tok != scope.bodyEnd: if not in_case_or_default: if tok.str not in ('case', 'default'): self.reportError(tok, 16, 1) else: in_case_or_default = True else: if simpleMatch(tok, 'break ;'): in_case_or_default = False tok = tok.next if tok.str == '{': tok = tok.link if tok.scope.type == 'Unconditional' and simpleMatch(tok.previous.previous, 'break ;'): in_case_or_default = False tok = tok.next def misra_16_2(self, data): for token in data.tokenlist: if token.str == 'case' and token.scope.type != 'Switch': self.reportError(token, 16, 2) def misra_16_3(self, rawTokens): STATE_NONE = 0 # default state, not in switch case/default block STATE_BREAK = 1 # break/comment is seen but not its ';' STATE_OK = 2 # a case/default is allowed (we have seen 'break;'/'comment'/'{'/attribute) STATE_SWITCH = 3 # walking through switch statement scope state = STATE_NONE end_swtich_token = None # end '}' for the switch scope for token in rawTokens: # Find switch scope borders if token.str == 'switch': state = STATE_SWITCH if state == STATE_SWITCH: if token.str == '{': end_swtich_token = findRawLink(token) else: continue if token.str == 'break' or token.str == 'return' or token.str == 'throw': state = STATE_BREAK elif token.str == ';': if state == STATE_BREAK: state = STATE_OK elif token.next and token.next == end_swtich_token: self.reportError(token.next, 16, 3) else: state = STATE_NONE elif token.str.startswith('/*') or token.str.startswith('//'): if 'fallthrough' in token.str.lower(): state = STATE_OK elif simpleMatch(token, '[ [ fallthrough ] ] ;'): state = STATE_BREAK elif token.str == '{': state = STATE_OK elif token.str == '}' and state == STATE_OK: # is this {} an unconditional block of code? prev = findRawLink(token) if prev: prev = prev.previous while prev and prev.str[:2] in ('//', '/*'): prev = prev.previous if (prev is None) or (prev.str not in ':;{}'): state = STATE_NONE elif token.str == 'case' or token.str == 'default': if state != STATE_OK: self.reportError(token, 16, 3) state = STATE_OK def misra_16_4(self, data): for token in data.tokenlist: if token.str != 'switch': continue if not simpleMatch(token, 'switch ('): continue if not simpleMatch(token.next.link, ') {'): continue startTok = token.next.link.next tok = startTok.next while tok and tok.str != '}': if tok.str == '{': tok = tok.link elif tok.str == 'default': break tok = tok.next if tok and tok.str != 'default': self.reportError(token, 16, 4) def misra_16_5(self, data): for token in data.tokenlist: if token.str != 'default': continue if token.previous and token.previous.str == '{': continue tok2 = token while tok2: if tok2.str in ('}', 'case'): break if tok2.str == '{': tok2 = tok2.link tok2 = tok2.next if tok2 and tok2.str == 'case': self.reportError(token, 16, 5) def misra_16_6(self, data): for token in data.tokenlist: if not (simpleMatch(token, 'switch (') and simpleMatch(token.next.link, ') {')): continue tok = token.next.link.next.next count = 0 while tok: if tok.str in ['break', 'return', 'throw']: count = count + 1 elif tok.str == '{': tok = tok.link if isNoReturnScope(tok): count = count + 1 elif tok.str == '}': break tok = tok.next if count < 2: self.reportError(token, 16, 6) def misra_16_7(self, data): for token in data.tokenlist: if simpleMatch(token, 'switch (') and isBoolExpression(token.next.astOperand2): self.reportError(token, 16, 7) def misra_17_1(self, data): for token in data.tokenlist: if isFunctionCall(token) and token.astOperand1.str in ( 'va_list', 'va_arg', 'va_start', 'va_end', 'va_copy'): self.reportError(token, 17, 1) elif token.str == 'va_list': self.reportError(token, 17, 1) def misra_17_2(self, data): # find recursions.. def find_recursive_call(search_for_function, direct_call, calls_map, visited=None): if visited is None: visited = set() if direct_call == search_for_function: return True for indirect_call in calls_map.get(direct_call, []): if indirect_call == search_for_function: return True if indirect_call in visited: # This has already been handled continue visited.add(indirect_call) if find_recursive_call(search_for_function, indirect_call, calls_map, visited): return True return False # List functions called in each function function_calls = {} for scope in data.scopes: if scope.type != 'Function': continue calls = [] tok = scope.bodyStart while tok != scope.bodyEnd: tok = tok.next if not isFunctionCall(tok, data.standards.c): continue f = tok.astOperand1.function if f is not None and f not in calls: calls.append(f) function_calls[scope.function] = calls # Report warnings for all recursions.. for func in function_calls: for call in function_calls[func]: if not find_recursive_call(func, call, function_calls): # Function call is not recursive continue # Warn about all functions calls.. for scope in data.scopes: if scope.type != 'Function' or scope.function != func: continue tok = scope.bodyStart while tok != scope.bodyEnd: if tok.function and tok.function == call: self.reportError(tok, 17, 2) tok = tok.next def misra_17_3(self, cfg): for w in cfg.clang_warnings: if w['message'].endswith('[-Wimplicit-function-declaration]'): self.reportError(cppcheckdata.Location(w), 17, 3) def misra_17_6(self, rawTokens): for token in rawTokens: if simpleMatch(token, '[ static'): self.reportError(token, 17, 6) def misra_17_7(self, data): for token in data.tokenlist: if not token.scope.isExecutable: continue if token.str != '(' or token.astParent: continue if not token.previous.isName or token.previous.varId: continue if token.valueType is None: continue if token.valueType.type == 'void' and token.valueType.pointer == 0: continue self.reportError(token, 17, 7) def misra_17_8(self, data): for token in data.tokenlist: if not (token.isAssignmentOp or (token.str in ('++', '--'))): continue if not token.astOperand1: continue var = token.astOperand1.variable if var and var.isArgument: self.reportError(token, 17, 8) def misra_18_4(self, data): for token in data.tokenlist: if token.str not in ('+', '-', '+=', '-='): continue if token.astOperand1 is None or token.astOperand2 is None: continue vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if vt1 and vt1.pointer > 0: self.reportError(token, 18, 4) elif vt2 and vt2.pointer > 0: self.reportError(token, 18, 4) def misra_18_5(self, data): for var in data.variables: if not var.isPointer: continue typetok = var.nameToken count = 0 while typetok: if typetok.str == '*': count = count + 1 elif not typetok.isName: break typetok = typetok.previous if count > 2: self.reportError(var.nameToken, 18, 5) def misra_18_7(self, data): for scope in data.scopes: if scope.type != 'Struct': continue token = scope.bodyStart.next while token != scope.bodyEnd and token is not None: # Handle nested structures to not duplicate an error. if token.str == '{': token = token.link if cppcheckdata.simpleMatch(token, "[ ]"): self.reportError(token, 18, 7) break token = token.next def misra_18_8(self, data): for var in data.variables: if not var.isArray or not var.isLocal: continue # TODO Array dimensions are not available in dump, must look in tokens typetok = var.nameToken.next if not typetok or typetok.str != '[': continue # Unknown define or syntax error if not typetok.astOperand2: continue if not isConstantExpression(typetok.astOperand2): self.reportError(var.nameToken, 18, 8) def misra_19_2(self, data): for token in data.tokenlist: if token.str == 'union': self.reportError(token, 19, 2) def misra_20_1(self, data): token_in_file = {} for token in data.tokenlist: if token.file not in token_in_file: token_in_file[token.file] = int(token.linenr) else: token_in_file[token.file] = min(token_in_file[token.file], int(token.linenr)) for directive in data.directives: if not directive.str.startswith('#include'): continue if directive.file not in token_in_file: continue if token_in_file[directive.file] < int(directive.linenr): self.reportError(directive, 20, 1) def misra_20_2(self, data): for directive in data.directives: if not directive.str.startswith('#include '): continue for pattern in ('\\', '//', '/*', ',', "'"): if pattern in directive.str: self.reportError(directive, 20, 2) break def misra_20_3(self, data): for directive in data.directives: if not directive.str.startswith('#include '): continue words = directive.str.split(' ') # If include directive contains more than two words, here would be # violation anyway. if len(words) > 2: self.reportError(directive, 20, 3) # Handle include directives with not quoted argument elif len(words) > 1: filename = words[1] if not ((filename.startswith('"') and filename.endswith('"')) or (filename.startswith('<') and filename.endswith('>'))): # We are handle only directly included files in the # following format: #include file.h # Cases with macro expansion provided by MISRA document are # skipped because we don't always have access to directive # definition. if '.' in filename: self.reportError(directive, 20, 3) def misra_20_4(self, data): for directive in data.directives: res = re.search(r'#define ([a-z][a-z0-9_]+)', directive.str) if res and isKeyword(res.group(1), data.standards.c): self.reportError(directive, 20, 4) def misra_20_5(self, data): for directive in data.directives: if directive.str.startswith('#undef '): self.reportError(directive, 20, 5) def misra_20_7(self, data): def find_string_concat(exp, arg, directive_args): # Handle concatenation of string literals, e.g.: # #define MACRO(A, B) (A " " B) # Addon should not report errors for both macro arguments. arg_pos = exp.find(arg, 0) need_check = False skip_next = False state_in_string = False pos_search = arg_pos + 1 directive_args = [a.strip() for a in directive_args if a != arg] arg = arg.strip() while pos_search < len(exp): if exp[pos_search] == '"': if state_in_string: state_in_string = False else: state_in_string = True pos_search += 1 elif exp[pos_search].isalnum(): word = "" while pos_search < len(exp) and exp[pos_search].isalnum(): word += exp[pos_search] pos_search += 1 if word == arg: pos_search += 1 elif word in directive_args: skip_next = True break elif exp[pos_search] == ' ': pos_search += 1 elif state_in_string: pos_search += 1 else: need_check = True break return need_check, skip_next for directive in data.directives: d = Define(directive) exp = '(' + d.expansionList + ')' skip_next = False for arg in d.args: if skip_next: _, skip_next = find_string_concat(exp, arg, d.args) continue need_check, skip_next = find_string_concat(exp, arg, d.args) if not need_check: continue pos = 0 while pos < len(exp): pos = exp.find(arg, pos) if pos < 0: break # is 'arg' used at position pos pos1 = pos - 1 pos2 = pos + len(arg) pos = pos2 if pos1 >= 0 and (isalnum(exp[pos1]) or exp[pos1] == '_'): continue if pos2 < len(exp) and (isalnum(exp[pos2]) or exp[pos2] == '_'): continue while pos1 >= 0 and exp[pos1] == ' ': pos1 -= 1 if exp[pos1] == '#': continue if exp[pos1] not in '([,.': self.reportError(directive, 20, 7) break while pos2 < len(exp) and exp[pos2] == ' ': pos2 += 1 if pos2 < len(exp) and exp[pos2] not in ')]#,': self.reportError(directive, 20, 7) break def misra_20_8(self, cfg): for cond in cfg.preprocessor_if_conditions: #print(cond) if cond.result and cond.result not in (0,1): self.reportError(cond, 20, 8) def misra_20_9(self, cfg): for cond in cfg.preprocessor_if_conditions: if cond.E is None: continue defined = [] for directive in cfg.directives: if directive.file == cond.file and directive.linenr == cond.linenr: for name in re.findall(r'[^_a-zA-Z0-9]defined[ ]*\([ ]*([_a-zA-Z0-9]+)[ ]*\)', directive.str): defined.append(name) for name in re.findall(r'[^_a-zA-Z0-9]defined[ ]*([_a-zA-Z0-9]+)', directive.str): defined.append(name) break for s in cond.E.split(' '): if (s[0] >= 'A' and s[0] <= 'Z') or (s[0] >= 'a' and s[0] <= 'z'): if isKeyword(s): continue if s in defined: continue self.reportError(cond, 20, 9) def misra_20_10(self, data): for directive in data.directives: d = Define(directive) if d.expansionList.find('#') >= 0: self.reportError(directive, 20, 10) def misra_20_11(self, cfg): for directive in cfg.directives: d = Define(directive) for arg in d.args: res = re.search(r'[^#]#[ ]*%s[ ]*##' % arg, ' ' + d.expansionList) if res: self.reportError(directive, 20, 11) def misra_20_12(self, cfg): def _is_hash_hash_op(expansion_list, arg): return re.search(r'##[ ]*%s[^a-zA-Z0-9_]' % arg, expansion_list) or \ re.search(r'[^a-zA-Z0-9_]%s[ ]*##' % arg, expansion_list) def _is_other_op(expansion_list, arg): pos = expansion_list.find(arg) while pos >= 0: pos1 = pos - 1 pos2 = pos + len(arg) pos = expansion_list.find(arg, pos2) if isalnum(expansion_list[pos1]) or expansion_list[pos1] == '_': continue if isalnum(expansion_list[pos2]) or expansion_list[pos2] == '_': continue while expansion_list[pos1] == ' ': pos1 = pos1 - 1 if expansion_list[pos1] == '#': continue while expansion_list[pos2] == ' ': pos2 = pos2 + 1 if expansion_list[pos2] == '#': continue return True return False def _is_arg_macro_usage(directive, arg): for macro_usage in cfg.macro_usage: if macro_usage.file == directive.file and macro_usage.linenr == directive.linenr: for macro_usage_arg in cfg.macro_usage: if macro_usage_arg == macro_usage: continue if (macro_usage.usefile == macro_usage_arg.usefile and macro_usage.uselinenr == macro_usage_arg.uselinenr and macro_usage.usecolumn == macro_usage_arg.usecolumn): # TODO: check arg better return True return False for directive in cfg.directives: define = Define(directive) expansion_list = '(%s)' % define.expansionList for arg in define.args: if not _is_hash_hash_op(expansion_list, arg): continue if not _is_other_op(expansion_list, arg): continue if _is_arg_macro_usage(directive, arg): self.reportError(directive, 20, 12) break def misra_20_13(self, data): dir_pattern = re.compile(r'#[ ]*([^ (<]*)') for directive in data.directives: dir = directive.str mo = dir_pattern.match(dir) if mo: dir = mo.group(1) if dir not in ['define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 'include', 'pragma', 'undef', 'warning']: self.reportError(directive, 20, 13) def misra_20_14(self, data): # stack for #if blocks. contains the #if directive until the corresponding #endif is seen. # the size increases when there are inner #if directives. ifStack = [] for directive in data.directives: if directive.str.startswith('#if ') or directive.str.startswith('#ifdef ') or directive.str.startswith( '#ifndef '): ifStack.append(directive) elif directive.str == '#else' or directive.str.startswith('#elif '): if len(ifStack) == 0: self.reportError(directive, 20, 14) ifStack.append(directive) elif directive.file != ifStack[-1].file: self.reportError(directive, 20, 14) elif directive.str == '#endif': if len(ifStack) == 0: self.reportError(directive, 20, 14) elif directive.file != ifStack[-1].file: self.reportError(directive, 20, 14) ifStack.pop() def misra_21_1(self, data): re_forbidden_macro = re.compile(r'#(?:define|undef) _[_A-Z]+') re_macro_name = re.compile(r'#(?:define|undef) (.+)[ $]') for d in data.directives: # Search for forbidden identifiers m = re.search(re_forbidden_macro, d.str) if m: self.reportError(d, 21, 1) continue # Search standard library identifiers in macro names m = re.search(re_macro_name, d.str) if not m: continue name = m.group(1) if isStdLibId(name, data.standards.c): self.reportError(d, 21, 1) def misra_21_2(self, cfg): for directive in cfg.directives: define = Define(directive) if re.match(r'_+BUILTIN_.*', define.name.upper()): self.reportError(directive, 21, 2) for func in cfg.functions: if isStdLibId(func.name, cfg.standards.c): tok = func.tokenDef if func.tokenDef else func.token self.reportError(tok, 21, 2) def misra_21_3(self, data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in ('malloc', 'calloc', 'realloc', 'free')): self.reportError(token, 21, 3) def misra_21_4(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 4) def misra_21_5(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 5) def misra_21_6(self, data): dir_stdio = findInclude(data.directives, '') dir_wchar = findInclude(data.directives, '') if dir_stdio: self.reportError(dir_stdio, 21, 6) if dir_wchar: self.reportError(dir_wchar, 21, 6) def misra_21_7(self, data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in ('atof', 'atoi', 'atol', 'atoll')): self.reportError(token, 21, 7) def misra_21_8(self, data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in ('abort', 'exit', 'getenv')): self.reportError(token, 21, 8) def misra_21_9(self, data): for token in data.tokenlist: if (token.str in ('bsearch', 'qsort')) and token.next and token.next.str == '(': self.reportError(token, 21, 9) def misra_21_10(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 10) for token in data.tokenlist: if (token.str == 'wcsftime') and token.next and token.next.str == '(': self.reportError(token, 21, 10) def misra_21_11(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 11) def misra_21_12(self, data): if findInclude(data.directives, ''): for token in data.tokenlist: if token.str == 'fexcept_t' and token.isName: self.reportError(token, 21, 12) if isFunctionCall(token) and (token.astOperand1.str in ( 'feclearexcept', 'fegetexceptflag', 'feraiseexcept', 'fesetexceptflag', 'fetestexcept')): self.reportError(token, 21, 12) def misra_21_14(self, data): # buffers used in strcpy/strlen/etc function calls string_buffers = [] for token in data.tokenlist: if token.str[0] == 's' and isFunctionCall(token.next): name, args = cppcheckdata.get_function_call_name_args(token) if name is None: continue def _get_string_buffers(match, args, argnum): if not match: return [] ret = [] for a in argnum: if a < len(args): arg = args[a] while arg and arg.str in ('.', '::'): arg = arg.astOperand2 if arg and arg.varId != 0 and arg.varId not in ret: ret.append(arg.varId) return ret string_buffers += _get_string_buffers(name == 'strcpy', args, [0, 1]) string_buffers += _get_string_buffers(name == 'strncpy', args, [0, 1]) string_buffers += _get_string_buffers(name == 'strlen', args, [0]) string_buffers += _get_string_buffers(name == 'strcmp', args, [0, 1]) string_buffers += _get_string_buffers(name == 'sprintf', args, [0]) string_buffers += _get_string_buffers(name == 'snprintf', args, [0, 3]) for token in data.tokenlist: if token.str != 'memcmp': continue name, args = cppcheckdata.get_function_call_name_args(token) if name is None: continue if len(args) != 3: continue for arg in args[:2]: if arg.str[-1] == '\"': self.reportError(arg, 21, 14) continue while arg and arg.str in ('.', '::'): arg = arg.astOperand2 if arg and arg.varId and arg.varId in string_buffers: self.reportError(arg, 21, 14) def misra_21_15(self, data): for token in data.tokenlist: if token.str not in ('memcpy', 'memmove', 'memcmp'): continue name, args = cppcheckdata.get_function_call_name_args(token) if name is None: continue if len(args) != 3: continue if args[0].valueType is None or args[1].valueType is None: continue if args[0].valueType.type == args[1].valueType.type: continue if args[0].valueType.type == 'void' or args[1].valueType.type == 'void': continue self.reportError(token, 21, 15) def misra_21_16(self, cfg): for token in cfg.tokenlist: if token.str != 'memcmp': continue name, args = cppcheckdata.get_function_call_name_args(token) if name is None: continue if len(args) != 3: continue for arg in args[:2]: if arg.valueType is None: continue if arg.valueType.pointer > 1: continue if arg.valueType.sign in ('unsigned', 'signed'): continue if arg.valueType.isEnum(): continue self.reportError(token, 21, 16) def misra_21_19(self, cfg): for token in cfg.tokenlist: if token.str in ('localeconv', 'getenv', 'setlocale', 'strerror') and simpleMatch(token.next, '('): name, _ = cppcheckdata.get_function_call_name_args(token) if name is None or name != token.str: continue parent = token.next while simpleMatch(parent.astParent, '+'): parent = parent.astParent # x = f() if simpleMatch(parent.astParent, '=') and parent == parent.astParent.astOperand2: lhs = parent.astParent.astOperand1 if lhs and lhs.valueType and lhs.valueType.pointer > 0 and lhs.valueType.constness == 0: self.reportError(token, 21, 19) if token.str == '=': lhs = token.astOperand1 while simpleMatch(lhs, '*') and lhs.astOperand2 is None: lhs = lhs.astOperand1 if not simpleMatch(lhs, '.'): continue while simpleMatch(lhs, '.'): lhs = lhs.astOperand1 if lhs and lhs.variable and simpleMatch(lhs.variable.typeStartToken, 'lconv'): self.reportError(token, 21, 19) def misra_21_20(self, cfg): assigned = {} invalid = [] for token in cfg.tokenlist: # No sophisticated data flow analysis, bail out if control flow is "interrupted" if token.str in ('{', '}', 'break', 'continue', 'return'): assigned = {} invalid = [] continue # When pointer is assigned, remove it from 'assigned' and 'invalid' if token.varId and token.varId > 0 and simpleMatch(token.next, '='): for name in assigned.keys(): while token.varId in assigned[name]: assigned[name].remove(token.varId) while token.varId in invalid: invalid.remove(token.varId) continue # Calling dangerous function if token.str in ('asctime', 'ctime', 'gmtime', 'localtime', 'localeconv', 'getenv', 'setlocale', 'strerror'): name, args = cppcheckdata.get_function_call_name_args(token) if name and name == token.str: # make assigned pointers invalid for varId in assigned.get(name, ()): if varId not in invalid: invalid.append(varId) # assign pointer parent = token.next while parent.astParent and (parent.astParent.str == '+' or isCast(parent.astParent)): parent = parent.astParent if simpleMatch(parent.astParent, '='): eq = parent.astParent vartok = eq.previous if vartok and vartok.varId and vartok.varId > 0: if name not in assigned: assigned[name] = [vartok.varId] elif vartok.varId not in assigned[name]: assigned[name].append(vartok.varId) continue # taking value of invalid pointer.. if token.astParent and token.varId: if token.varId in invalid: self.reportError(token, 21, 20) def misra_21_21(self, cfg): for token in cfg.tokenlist: if token.str == 'system': name, args = cppcheckdata.get_function_call_name_args(token) if name == 'system' and len(args) == 1: self.reportError(token, 21, 21) def misra_22_5(self, cfg): for token in cfg.tokenlist: if token.isUnaryOp("*") or (token.isBinaryOp() and token.str == '.'): fileptr = token.astOperand1 if fileptr.variable and cppcheckdata.simpleMatch(fileptr.variable.typeStartToken, 'FILE *'): self.reportError(token, 22, 5) def misra_22_7(self, cfg): for eofToken in cfg.tokenlist: if eofToken.str != 'EOF': continue if eofToken.astParent is None or not eofToken.astParent.isComparisonOp: continue if eofToken.astParent.astOperand1 == eofToken: eofTokenSibling = eofToken.astParent.astOperand2 else: eofTokenSibling = eofToken.astParent.astOperand1 while isCast(eofTokenSibling) and eofTokenSibling.valueType and eofTokenSibling.valueType.type and eofTokenSibling.valueType.type == 'int': eofTokenSibling = eofTokenSibling.astOperand2 if eofTokenSibling.astOperand2 else eofTokenSibling.astOperand1 if eofTokenSibling is not None and eofTokenSibling.valueType and eofTokenSibling.valueType and eofTokenSibling.valueType.type in ('bool', 'char', 'short'): self.reportError(eofToken, 22, 7) def misra_22_8(self, cfg): is_zero = False for token in cfg.tokenlist: if simpleMatch(token, 'errno = 0'): is_zero = True if token.str == '(' and not simpleMatch(token.link, ') {'): name, _ = cppcheckdata.get_function_call_name_args(token.previous) if name is None: continue if is_errno_setting_function(name): if not is_zero: self.reportError(token, 22, 8) else: is_zero = False def misra_22_9(self, cfg): errno_is_set = False for token in cfg.tokenlist: if token.str == '(' and not simpleMatch(token.link, ') {'): name, args = cppcheckdata.get_function_call_name_args(token.previous) if name is None: continue errno_is_set = is_errno_setting_function(name) if errno_is_set and token.str in '{};': errno_is_set = False tok = token.next while tok and tok.str not in ('{','}',';','errno'): tok = tok.next if tok is None or tok.str != 'errno': self.reportError(token, 22, 9) elif (tok.astParent is None) or (not tok.astParent.isComparisonOp): self.reportError(token, 22, 9) def misra_22_10(self, cfg): last_function_call = None for token in cfg.tokenlist: if token.str == '(' and not simpleMatch(token.link, ') {'): name, args = cppcheckdata.get_function_call_name_args(token.previous) last_function_call = name if token.str == '}': last_function_call = None if token.str == 'errno' and token.astParent and token.astParent.isComparisonOp: if last_function_call is None: self.reportError(token, 22, 10) elif not is_errno_setting_function(last_function_call): self.reportError(token, 22, 10) def get_verify_expected(self): """Return the list of expected violations in the verify test""" return self.verify_expected def get_verify_actual(self): """Return the list of actual violations in for the verify test""" return self.verify_actual def get_violations(self, violation_type=None): """Return the list of violations for a normal checker run""" if violation_type is None: return self.violations.items() else: return self.violations[violation_type] def get_violation_types(self): """Return the list of violations for a normal checker run""" return self.violations.keys() def addSuppressedRule(self, ruleNum, fileName=None, lineNumber=None, symbolName=None): """ Add a suppression to the suppressions data structure Suppressions are stored in a dictionary of dictionaries that contains a list of tuples. The first dictionary is keyed by the MISRA rule in hundreds format. The value of that dictionary is a dictionary of filenames. If the value is None then the rule is assumed to be suppressed for all files. If the filename exists then the value of that dictionary contains a list with the scope of the suppression. If the list contains an item of None then the rule is assumed to be suppressed for the entire file. Otherwise the list contains line number, symbol name tuples. For each tuple either line number or symbol name can can be none. """ normalized_filename = None if fileName is not None: normalized_filename = os.path.expanduser(fileName) normalized_filename = os.path.normpath(normalized_filename) if lineNumber is not None or symbolName is not None: line_symbol = (lineNumber, symbolName) else: line_symbol = None # If the rule is not in the dict already then add it if ruleNum not in self.suppressedRules: ruleItemList = list() ruleItemList.append(line_symbol) fileDict = dict() fileDict[normalized_filename] = ruleItemList self.suppressedRules[ruleNum] = fileDict # Rule is added. Done. return # Rule existed in the dictionary. Check for # filename entries. # Get the dictionary for the rule number fileDict = self.suppressedRules[ruleNum] # If the filename is not in the dict already add it if normalized_filename not in fileDict: ruleItemList = list() ruleItemList.append(line_symbol) fileDict[normalized_filename] = ruleItemList # Rule is added with a file scope. Done return # Rule has a matching filename. Get the rule item list. # Check the lists of rule items # to see if this (lineNumber, symbolName) combination # or None already exists. ruleItemList = fileDict[normalized_filename] if line_symbol is None: # is it already in the list? if line_symbol not in ruleItemList: ruleItemList.append(line_symbol) else: # Check the list looking for matches matched = False for each in ruleItemList: if each is not None: if (each[0] == line_symbol[0]) and (each[1] == line_symbol[1]): matched = True # Append the rule item if it was not already found if not matched: ruleItemList.append(line_symbol) def isRuleSuppressed(self, file_path, linenr, ruleNum): """ Check to see if a rule is suppressed. :param ruleNum: is the rule number in hundreds format :param file_path: File path of checked location :param linenr: Line number of checked location If the rule exists in the dict then check for a filename If the filename is None then rule is suppressed globally for all files. If the filename exists then look for list of line number, symbol name tuples. If the list is None then the rule is suppressed for the entire file If the list of tuples exists then search the list looking for matching line numbers. Symbol names are currently ignored because they can include regular expressions. TODO: Support symbol names and expression matching. """ ruleIsSuppressed = False # Remove any prefix listed in command arguments from the filename. filename = None if file_path is not None: if self.filePrefix is not None: filename = remove_file_prefix(file_path, self.filePrefix) else: filename = os.path.basename(file_path) if ruleNum in self.suppressedRules: fileDict = self.suppressedRules[ruleNum] # a file name entry of None means that the rule is suppressed # globally if None in fileDict: ruleIsSuppressed = True else: # Does the filename match one of the names in # the file list if filename in fileDict: # Get the list of ruleItems ruleItemList = fileDict[filename] if None in ruleItemList: # Entry of None in the ruleItemList means the rule is # suppressed for all lines in the filename ruleIsSuppressed = True else: # Iterate though the the list of line numbers # and symbols looking for a match of the line # number. Matching the symbol is a TODO: for each in ruleItemList: if each is not None: if each[0] == linenr: ruleIsSuppressed = True return ruleIsSuppressed def isRuleGloballySuppressed(self, rule_num): """ Check to see if a rule is globally suppressed. :param rule_num: is the rule number in hundreds format """ if rule_num not in self.suppressedRules: return False return None in self.suppressedRules[rule_num] def showSuppressedRules(self): """ Print out rules in suppression list sorted by Rule Number """ print("Suppressed Rules List:") outlist = list() for ruleNum in self.suppressedRules: fileDict = self.suppressedRules[ruleNum] for fname in fileDict: ruleItemList = fileDict[fname] for item in ruleItemList: if item is None: item_str = "None" else: item_str = str(item[0]) outlist.append("%s: %s: %s (%d locations suppressed)" % ( float(ruleNum) / 100, fname, item_str, self.suppressionStats.get(ruleNum, 0))) for line in sorted(outlist, reverse=True): print(" %s" % line) def setFilePrefix(self, prefix): """ Set the file prefix to ignore from files when matching suppression files """ self.filePrefix = prefix def setSeverity(self, severity): """ Set the severity for all errors. """ self.severity = severity def setSuppressionList(self, suppressionlist): num1 = 0 num2 = 0 rule_pattern = re.compile(r'([0-9]+).([0-9]+)') strlist = suppressionlist.split(",") # build ignore list for item in strlist: res = rule_pattern.match(item) if res: num1 = int(res.group(1)) num2 = int(res.group(2)) ruleNum = (num1 * 100) + num2 self.addSuppressedRule(ruleNum) def reportError(self, location, num1, num2): ruleNum = num1 * 100 + num2 if self.isRuleGloballySuppressed(ruleNum): return if self.settings.verify: self.verify_actual.append('%s:%d %d.%d' % (location.file, location.linenr, num1, num2)) elif self.isRuleSuppressed(location.file, location.linenr, ruleNum): # Error is suppressed. Ignore self.suppressionStats.setdefault(ruleNum, 0) self.suppressionStats[ruleNum] += 1 return else: errorId = 'c2012-' + str(num1) + '.' + str(num2) misra_severity = 'Undefined' cppcheck_severity = 'style' if ruleNum in self.ruleTexts: errmsg = self.ruleTexts[ruleNum].text if self.ruleTexts[ruleNum].misra_severity: misra_severity = self.ruleTexts[ruleNum].misra_severity cppcheck_severity = self.ruleTexts[ruleNum].cppcheck_severity elif len(self.ruleTexts) == 0: errmsg = 'misra violation (use --rule-texts= to get proper output)' else: errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum) if self.severity: cppcheck_severity = self.severity this_violation = '{}-{}-{}-{}'.format(location.file, location.linenr, location.column, ruleNum) # If this is new violation then record it and show it. If not then # skip it since it has already been displayed. if not this_violation in self.existing_violations: self.existing_violations.add(this_violation) cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId, misra_severity) if misra_severity not in self.violations: self.violations[misra_severity] = [] self.violations[misra_severity].append('misra-' + errorId) def loadRuleTexts(self, filename): num1 = 0 num2 = 0 appendixA = False ruleText = False expect_more = False Rule_pattern = re.compile(r'^Rule ([0-9]+).([0-9]+)') severity_pattern = re.compile(r'.*[ ]*(Advisory|Required|Mandatory)$') xA_Z_pattern = re.compile(r'^[#A-Z].*') a_z_pattern = re.compile(r'^[a-z].*') # Try to detect the file encoding file_stream = None encodings = ['ascii', 'utf-8', 'windows-1250', 'windows-1252'] for e in encodings: try: file_stream = codecs.open(filename, 'r', encoding=e) file_stream.readlines() file_stream.seek(0) except UnicodeDecodeError: file_stream = None else: break if not file_stream: print('Could not find a suitable codec for "' + filename + '".') print('If you know the codec please report it to the developers so the list can be enhanced.') print('Trying with default codec now and ignoring errors if possible ...') try: file_stream = open(filename, 'rt', errors='ignore') except TypeError: # Python 2 does not support the errors parameter file_stream = open(filename, 'rt') rule = None have_severity = False severity_loc = 0 for line in file_stream: line = line.replace('\r', '').replace('\n', '') if not appendixA: if line.find('Appendix A') >= 0 and line.find('Summary of guidelines') >= 10: appendixA = True continue if line.find('Appendix B') >= 0: break if len(line) == 0: continue # Parse rule declaration. res = Rule_pattern.match(line) if res: have_severity = False expect_more = False severity_loc = 0 num1 = int(res.group(1)) num2 = int(res.group(2)) rule = Rule(num1, num2) if not have_severity and rule is not None: res = severity_pattern.match(line) if res: rule.misra_severity = res.group(1) have_severity = True else: severity_loc += 1 # Only look for severity on the Rule line # or the next non-blank line after # If it's not in either of those locations then # assume a severity was not provided. if severity_loc < 2: continue else: rule.misra_severity = '' have_severity = True if rule is None: continue # Parse continuing of rule text. if expect_more: if a_z_pattern.match(line): self.ruleTexts[rule.num].text += ' ' + line continue expect_more = False continue # Parse beginning of rule text. if xA_Z_pattern.match(line): rule.text = line self.ruleTexts[rule.num] = rule expect_more = True def verifyRuleTexts(self): """Prints rule numbers without rule text.""" rule_texts_rules = [] for rule_num in self.ruleTexts: rule = self.ruleTexts[rule_num] rule_texts_rules.append(str(rule.num1) + '.' + str(rule.num2)) all_rules = list(getAddonRules() + getCppcheckRules()) missing_rules = list(set(all_rules) - set(rule_texts_rules)) if len(missing_rules) == 0: print("Rule texts are correct.") else: print("Missing rule texts: " + ', '.join(missing_rules)) def printStatus(self, *args, **kwargs): if not self.settings.quiet: print(*args, **kwargs) def executeCheck(self, rule_num, check_function, *args): """Execute check function for a single MISRA rule. :param rule_num: Number of rule in hundreds format :param check_function: Check function to execute :param args: Check function arguments """ if not self.isRuleGloballySuppressed(rule_num): check_function(*args) def parseDump(self, dumpfile): def fillVerifyExpected(verify_expected, tok): """Add expected suppressions to verify_expected list.""" rule_re = re.compile(r'[0-9]+\.[0-9]+') if tok.str.startswith('//') and 'TODO' not in tok.str: for word in tok.str[2:].split(' '): if rule_re.match(word): verify_expected.append('%s:%d %s' % (tok.file, tok.linenr, word)) data = cppcheckdata.parsedump(dumpfile) typeBits['CHAR'] = data.platform.char_bit typeBits['SHORT'] = data.platform.short_bit typeBits['INT'] = data.platform.int_bit typeBits['LONG'] = data.platform.long_bit typeBits['LONG_LONG'] = data.platform.long_long_bit typeBits['POINTER'] = data.platform.pointer_bit if self.settings.verify: # Add suppressions from the current file for tok in data.rawTokens: fillVerifyExpected(self.verify_expected, tok) # Add suppressions from the included headers include_re = re.compile(r'^#include [<"]([a-zA-Z0-9]+[a-zA-Z\-_./\\0-9]*)[">]$') dump_dir = os.path.dirname(data.filename) for conf in data.configurations: for directive in conf.directives: m = re.match(include_re, directive.str) if not m: continue header_dump_path = os.path.join(dump_dir, m.group(1) + '.dump') if not os.path.exists(header_dump_path): continue header_data = cppcheckdata.parsedump(header_dump_path) for tok in header_data.rawTokens: fillVerifyExpected(self.verify_expected, tok) else: self.printStatus('Checking ' + dumpfile + '...') for cfgNumber, cfg in enumerate(data.iterconfigurations()): if not self.settings.quiet: self.printStatus('Checking %s, config %s...' % (dumpfile, cfg.name)) self.executeCheck(104, self.misra_1_4, cfg) self.executeCheck(202, self.misra_2_2, cfg) self.executeCheck(203, self.misra_2_3, dumpfile, cfg.typedefInfo) self.executeCheck(204, self.misra_2_4, dumpfile, cfg) self.executeCheck(205, self.misra_2_5, dumpfile, cfg) self.executeCheck(207, self.misra_2_7, cfg) # data.rawTokens is same for all configurations if cfgNumber == 0: self.executeCheck(301, self.misra_3_1, data.rawTokens) self.executeCheck(302, self.misra_3_2, data.rawTokens) self.executeCheck(401, self.misra_4_1, data.rawTokens) self.executeCheck(402, self.misra_4_2, data.rawTokens) self.executeCheck(501, self.misra_5_1, cfg) self.executeCheck(502, self.misra_5_2, cfg) self.executeCheck(504, self.misra_5_4, cfg) self.executeCheck(505, self.misra_5_5, cfg) self.executeCheck(506, self.misra_5_6, dumpfile, cfg.typedefInfo) self.executeCheck(507, self.misra_5_7, dumpfile, cfg) self.executeCheck(508, self.misra_5_8, dumpfile, cfg) self.executeCheck(509, self.misra_5_9, dumpfile, cfg) self.executeCheck(601, self.misra_6_1, cfg) self.executeCheck(602, self.misra_6_2, cfg) if cfgNumber == 0: self.executeCheck(701, self.misra_7_1, data.rawTokens) self.executeCheck(702, self.misra_7_2, cfg) if cfgNumber == 0: self.executeCheck(703, self.misra_7_3, data.rawTokens) self.executeCheck(704, self.misra_7_4, cfg) self.executeCheck(801, self.misra_8_1, cfg) if cfgNumber == 0: self.executeCheck(802, self.misra_8_2, cfg, data.rawTokens) self.executeCheck(804, self.misra_8_4, cfg) self.executeCheck(805, self.misra_8_5, dumpfile, cfg) self.executeCheck(806, self.misra_8_6, dumpfile, cfg) self.executeCheck(807, self.misra_8_7, dumpfile, cfg) self.executeCheck(808, self.misra_8_8, cfg) self.executeCheck(809, self.misra_8_9, cfg) self.executeCheck(810, self.misra_8_10, cfg) self.executeCheck(811, self.misra_8_11, cfg) self.executeCheck(812, self.misra_8_12, cfg) if cfgNumber == 0: self.executeCheck(814, self.misra_8_14, data.rawTokens) self.executeCheck(902, self.misra_9_2, cfg) self.executeCheck(903, self.misra_9_3, cfg) self.executeCheck(904, self.misra_9_4, cfg) if cfgNumber == 0: self.executeCheck(905, self.misra_9_5, cfg, data.rawTokens) self.executeCheck(1001, self.misra_10_1, cfg) self.executeCheck(1002, self.misra_10_2, cfg) self.executeCheck(1003, self.misra_10_3, cfg) self.executeCheck(1004, self.misra_10_4, cfg) self.executeCheck(1005, self.misra_10_5, cfg) self.executeCheck(1006, self.misra_10_6, cfg) self.executeCheck(1007, self.misra_10_7, cfg) self.executeCheck(1008, self.misra_10_8, cfg) self.executeCheck(1101, self.misra_11_1, cfg) self.executeCheck(1102, self.misra_11_2, cfg) self.executeCheck(1103, self.misra_11_3, cfg) self.executeCheck(1104, self.misra_11_4, cfg) self.executeCheck(1105, self.misra_11_5, cfg) self.executeCheck(1106, self.misra_11_6, cfg) self.executeCheck(1107, self.misra_11_7, cfg) self.executeCheck(1108, self.misra_11_8, cfg) self.executeCheck(1109, self.misra_11_9, cfg) if cfgNumber == 0: self.executeCheck(1201, self.misra_12_1_sizeof, data.rawTokens) self.executeCheck(1201, self.misra_12_1, cfg) self.executeCheck(1202, self.misra_12_2, cfg) self.executeCheck(1203, self.misra_12_3, cfg) self.executeCheck(1204, self.misra_12_4, cfg) self.executeCheck(1301, self.misra_13_1, cfg) self.executeCheck(1303, self.misra_13_3, cfg) self.executeCheck(1304, self.misra_13_4, cfg) self.executeCheck(1305, self.misra_13_5, cfg) self.executeCheck(1306, self.misra_13_6, cfg) self.executeCheck(1401, self.misra_14_1, cfg) self.executeCheck(1402, self.misra_14_2, cfg) self.executeCheck(1404, self.misra_14_4, cfg) self.executeCheck(1501, self.misra_15_1, cfg) self.executeCheck(1502, self.misra_15_2, cfg) self.executeCheck(1503, self.misra_15_3, cfg) self.executeCheck(1504, self.misra_15_4, cfg) self.executeCheck(1505, self.misra_15_5, cfg) if cfgNumber == 0: self.executeCheck(1506, self.misra_15_6, data.rawTokens) self.executeCheck(1507, self.misra_15_7, cfg) self.executeCheck(1601, self.misra_16_1, cfg) self.executeCheck(1602, self.misra_16_2, cfg) if cfgNumber == 0: self.executeCheck(1603, self.misra_16_3, data.rawTokens) self.executeCheck(1604, self.misra_16_4, cfg) self.executeCheck(1605, self.misra_16_5, cfg) self.executeCheck(1606, self.misra_16_6, cfg) self.executeCheck(1607, self.misra_16_7, cfg) self.executeCheck(1701, self.misra_17_1, cfg) self.executeCheck(1702, self.misra_17_2, cfg) self.executeCheck(1703, self.misra_17_3, cfg) if cfgNumber == 0: self.executeCheck(1706, self.misra_17_6, data.rawTokens) self.executeCheck(1707, self.misra_17_7, cfg) self.executeCheck(1708, self.misra_17_8, cfg) self.executeCheck(1804, self.misra_18_4, cfg) self.executeCheck(1805, self.misra_18_5, cfg) self.executeCheck(1807, self.misra_18_7, cfg) self.executeCheck(1808, self.misra_18_8, cfg) self.executeCheck(1902, self.misra_19_2, cfg) self.executeCheck(2001, self.misra_20_1, cfg) self.executeCheck(2002, self.misra_20_2, cfg) self.executeCheck(2003, self.misra_20_3, cfg) self.executeCheck(2004, self.misra_20_4, cfg) self.executeCheck(2005, self.misra_20_5, cfg) self.executeCheck(2007, self.misra_20_7, cfg) self.executeCheck(2008, self.misra_20_8, cfg) self.executeCheck(2009, self.misra_20_9, cfg) self.executeCheck(2010, self.misra_20_10, cfg) self.executeCheck(2011, self.misra_20_11, cfg) self.executeCheck(2012, self.misra_20_12, cfg) self.executeCheck(2013, self.misra_20_13, cfg) self.executeCheck(2014, self.misra_20_14, cfg) self.executeCheck(2101, self.misra_21_1, cfg) self.executeCheck(2102, self.misra_21_2, cfg) self.executeCheck(2103, self.misra_21_3, cfg) self.executeCheck(2104, self.misra_21_4, cfg) self.executeCheck(2105, self.misra_21_5, cfg) self.executeCheck(2106, self.misra_21_6, cfg) self.executeCheck(2107, self.misra_21_7, cfg) self.executeCheck(2108, self.misra_21_8, cfg) self.executeCheck(2109, self.misra_21_9, cfg) self.executeCheck(2110, self.misra_21_10, cfg) self.executeCheck(2111, self.misra_21_11, cfg) self.executeCheck(2112, self.misra_21_12, cfg) self.executeCheck(2114, self.misra_21_14, cfg) self.executeCheck(2115, self.misra_21_15, cfg) self.executeCheck(2116, self.misra_21_16, cfg) self.executeCheck(2119, self.misra_21_19, cfg) self.executeCheck(2120, self.misra_21_20, cfg) self.executeCheck(2121, self.misra_21_21, cfg) # 22.4 is already covered by Cppcheck writeReadOnlyFile self.executeCheck(2205, self.misra_22_5, cfg) self.executeCheck(2207, self.misra_22_7, cfg) self.executeCheck(2208, self.misra_22_8, cfg) self.executeCheck(2209, self.misra_22_9, cfg) self.executeCheck(2210, self.misra_22_10, cfg) def analyse_ctu_info(self, ctu_info_files): all_typedef_info = [] all_tagname_info = [] all_macro_info = [] all_external_identifiers_decl = {} all_external_identifiers_def = {} all_internal_identifiers = {} all_local_identifiers = {} all_usage_count = {} from cppcheckdata import Location def is_different_location(loc1, loc2): return loc1['file'] != loc2['file'] or loc1['line'] != loc2['line'] for filename in ctu_info_files: for line in open(filename, 'rt'): if not line.startswith('{'): continue s = json.loads(line) summary_type = s['summary'] summary_data = s['data'] if summary_type == 'MisraTypedefInfo': for new_typedef_info in summary_data: found = False for old_typedef_info in all_typedef_info: if old_typedef_info['name'] == new_typedef_info['name']: found = True if is_different_location(old_typedef_info, new_typedef_info): self.reportError(Location(old_typedef_info), 5, 6) self.reportError(Location(new_typedef_info), 5, 6) else: if new_typedef_info['used']: old_typedef_info['used'] = True break if not found: all_typedef_info.append(new_typedef_info) if summary_type == 'MisraTagName': for new_tagname_info in summary_data: found = False for old_tagname_info in all_tagname_info: if old_tagname_info['name'] == new_tagname_info['name']: found = True if is_different_location(old_tagname_info, new_tagname_info): self.reportError(Location(old_tagname_info), 5, 7) self.reportError(Location(new_tagname_info), 5, 7) else: if new_tagname_info['used']: old_tagname_info['used'] = True break if not found: all_tagname_info.append(new_tagname_info) if summary_type == 'MisraMacro': for new_macro in summary_data: found = False for old_macro in all_macro_info: if old_macro['name'] == new_macro['name']: found = True if new_macro['used']: old_macro['used'] = True break if not found: all_macro_info.append(new_macro) if summary_type == 'MisraExternalIdentifiers': for s in summary_data: is_declaration = s['decl'] if is_declaration: all_external_identifiers = all_external_identifiers_decl else: all_external_identifiers = all_external_identifiers_def name = s['name'] if name in all_external_identifiers and is_different_location(s, all_external_identifiers[name]): num = 5 if is_declaration else 6 self.reportError(Location(s), 8, num) self.reportError(Location(all_external_identifiers[name]), 8, num) all_external_identifiers[name] = s if summary_type == 'MisraInternalIdentifiers': for s in summary_data: if s['name'] in all_internal_identifiers: self.reportError(Location(s), 5, 9) self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9) all_internal_identifiers[s['name']] = s if summary_type == 'MisraLocalIdentifiers': for s in summary_data: all_local_identifiers[s['name']] = s if summary_type == 'MisraUsage': for s in summary_data: if s in all_usage_count: all_usage_count[s] += 1 else: all_usage_count[s] = 1 for ti in all_typedef_info: if not ti['used']: self.reportError(Location(ti), 2, 3) for ti in all_tagname_info: if not ti['used']: self.reportError(Location(ti), 2, 4) for m in all_macro_info: if not m['used']: self.reportError(Location(m), 2, 5) all_external_identifiers = all_external_identifiers_decl all_external_identifiers.update(all_external_identifiers_def) for name, external_identifier in all_external_identifiers.items(): internal_identifier = all_internal_identifiers.get(name) if internal_identifier: self.reportError(Location(internal_identifier), 5, 8) self.reportError(Location(external_identifier), 5, 8) local_identifier = all_local_identifiers.get(name) if local_identifier: self.reportError(Location(local_identifier), 5, 8) self.reportError(Location(external_identifier), 5, 8) for name, count in all_usage_count.items(): #print('%s:%i' % (name, count)) if count != 1: continue if name in all_external_identifiers: self.reportError(Location(all_external_identifiers[name]), 8, 7) RULE_TEXTS_HELP = '''Path to text file of MISRA rules If you have the tool 'pdftotext' you might be able to generate this textfile with such command: pdftotext MISRA_C_2012.pdf MISRA_C_2012.txt Otherwise you can more or less copy/paste the chapter Appendix A Summary of guidelines from the MISRA pdf. You can buy the MISRA pdf from http://www.misra.org.uk/ Format: <..arbitrary text..> Appendix A Summary of guidelines Rule 1.1 Required Rule text for 1.1 continuation of rule text for 1.1 Rule 1.2 Mandatory Rule text for 1.2 continuation of rule text for 1.2 <...> ''' SUPPRESS_RULES_HELP = '''MISRA rules to suppress (comma-separated) For example, if you'd like to suppress rules 15.1, 11.3, and 20.13, run: python misra.py --suppress-rules 15.1,11.3,20.13 ... ''' def get_args_parser(): """Generates list of command-line arguments acceptable by misra.py script.""" parser = cppcheckdata.ArgumentParser() parser.add_argument("--rule-texts", type=str, help=RULE_TEXTS_HELP) parser.add_argument("--verify-rule-texts", help="Verify that all supported rules texts are present in given file and exit.", action="store_true") parser.add_argument("--suppress-rules", type=str, help=SUPPRESS_RULES_HELP) parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true") parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules") parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true") parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true") parser.add_argument("--severity", type=str, help="Set a custom severity string, for example 'error' or 'warning'. ") return parser def main(): parser = get_args_parser() args = parser.parse_args() settings = MisraSettings(args) checker = MisraChecker(settings) if args.generate_table: generateTable() sys.exit(0) if args.rule_texts: filename = os.path.expanduser(args.rule_texts) filename = os.path.normpath(filename) if not os.path.isfile(filename): print('Fatal error: file is not found: ' + filename) sys.exit(1) checker.loadRuleTexts(filename) if args.verify_rule_texts: checker.verifyRuleTexts() sys.exit(0) if args.verify_rule_texts and not args.rule_texts: print("Error: Please specify rule texts file with --rule-texts=") sys.exit(1) if args.suppress_rules: checker.setSuppressionList(args.suppress_rules) if args.file_prefix: checker.setFilePrefix(args.file_prefix) dump_files, ctu_info_files = cppcheckdata.get_files(args) if (not dump_files) and (not ctu_info_files): if not args.quiet: print("No input files.") sys.exit(0) if args.severity: checker.setSeverity(args.severity) for item in dump_files: checker.parseDump(item) if settings.verify: verify_expected = checker.get_verify_expected() verify_actual = checker.get_verify_actual() exitCode = 0 for expected in verify_expected: if expected not in verify_actual: print('Expected but not seen: ' + expected) exitCode = 1 for actual in verify_actual: if actual not in verify_expected: print('Not expected: ' + actual) exitCode = 1 # Existing behavior of verify mode is to exit # on the first un-expected output. # TODO: Is this required? or can it be moved to after # all input files have been processed if exitCode != 0: sys.exit(exitCode) checker.analyse_ctu_info(ctu_info_files) if settings.verify: sys.exit(exitCode) number_of_violations = len(checker.get_violations()) if number_of_violations > 0: if settings.show_summary: print("\nMISRA rules violations found:\n\t%s\n" % ( "\n\t".join(["%s: %d" % (viol, len(checker.get_violations(viol))) for viol in checker.get_violation_types()]))) rules_violated = {} for severity, ids in checker.get_violations(): for misra_id in ids: rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1 print("MISRA rules violated:") convert = lambda text: int(text) if text.isdigit() else text misra_sort = lambda key: [convert(c) for c in re.split(r'[\.-]([0-9]*)', key)] for misra_id in sorted(rules_violated.keys(), key=misra_sort): res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id) if res is None: num = 0 else: num = int(res.group(1)) * 100 + int(res.group(2)) severity = '-' if num in checker.ruleTexts: severity = checker.ruleTexts[num].cppcheck_severity print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id])) if args.show_suppressed_rules: checker.showSuppressedRules() if __name__ == '__main__': main() sys.exit(cppcheckdata.EXIT_CODE) cppcheck-2.7/addons/misra_9.py000066400000000000000000000461041417746362400163520ustar00rootroot00000000000000# Holds information about an array, struct or union's element definition. class ElementDef: def __init__(self, elementType, name, valueType, dimensions = None): self.elementType = elementType # 'array', 'record' or 'value' self.name = str(name) self.valueType = valueType self.children = [] self.dimensions = dimensions self.parent = None self.isDesignated = False self.isPositional = False self.numInits = 0 self.childIndex = -1 self.flexibleToken = None self.isFlexible = False self.structureViolationToken = None def __repr__(self): inits = "" if self.isPositional: inits += 'P' if self.isDesignated: inits += 'D' if not (self.isPositional or self.isDesignated) and self.numInits == 0: inits += '_' if self.numInits > 1: inits += str(self.numInits) attrs = ["childIndex", "elementType", "valueType"] return "{}({}, {}, {})".format( "ED", self.getLongName(), inits, ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) @property def isArray(self): return self.elementType == 'array' @property def isRecord(self): return self.elementType == 'record' @property def isValue(self): return self.elementType == 'value' def getLongName(self): return self.parent.getLongName() + "." + self.name if self.parent else self.name def getInitDump(self): t = [] if self.isPositional: t.append('P') if self.isDesignated: t.append('D') if self.numInits == 0: t.append('_') if self.numInits > 1: t.append(str(self.numInits)) myDump = "".join(t) if len(self.children): childDumps = [] for c in self.children: childDumps.append(c.getInitDump()) if self.structureViolationToken is not None: myDump += "!" myDump += "{ " + ", ".join(childDumps) + " }" return myDump def addChild(self, child): self.children.append(child) child.parent = self def getNextChild(self): self.childIndex += 1 return self.getChildByIndex(self.childIndex) def getChildByIndex(self, index): if self.isFlexible: while len(self.children) <= index: createChild(self, self.flexibleToken, len(self.children)) return self.children[index] if 0 <= index < len(self.children) else None def getChildByName(self, name): for c in self.children: if c.name == name: return c return None def getNextValueElement(self, root): current = self while current != root: if not current.parent: return None # Get next index of parent i = current.parent.children.index(current) + 1 # Next index of parent exists if i < len(current.parent.children): current = current.parent.children[i] return current.getFirstValueElement() # Next index of parent doesn't exist. Move up current = current.parent return None def getFirstValueElement(self): current = self # Move to first child as long as children exists next_child = current.getChildByIndex(0) while next_child: current = next_child next_child = current.getChildByIndex(0) return current def getLastValueElement(self): current = self # Move to last child as long as children exists while len(current.children) > 0: current = current.children[-1] return current def getChildByValueElement(self, ed): potentialChild = ed while potentialChild and potentialChild not in self.children: potentialChild = potentialChild.parent return self.children[self.children.index(potentialChild)] if potentialChild else None def getEffectiveLevel(self): if self.parent and self.parent.elementType == "array": return self.parent.getEffectiveLevel() + 1 else: return 0 def setInitialized(self, designated=False, positional=False): if designated: self.isDesignated = True if positional or not designated: self.isPositional = True self.numInits += 1 def initializeChildren(self): for child in self.children: child.setInitialized(positional=True) child.initializeChildren() def unset(self): self.isDesignated = False self.isPositional = False # Unset is always recursive for child in self.children: child.unset() def markStuctureViolation(self, token): if self.name == '->': self.children[0].markStuctureViolation(token) elif not self.structureViolationToken: self.structureViolationToken = token def markAsFlexibleArray(self, token): self.flexibleToken = token self.isFlexible = True def markAsCurrent(self): if self.parent: if self.name == '<-': self.parent.childIndex = self.parent.children.index(self.children[0]) else: self.parent.childIndex = self.parent.children.index(self) self.parent.markAsCurrent() def isAllChildrenSet(self): myself = len(self.children) == 0 and (self.isDesignated or self.isPositional) mychildren = len(self.children) > 0 and all([child.isAllChildrenSet() for child in self.children]) return myself or mychildren def isAllSet(self): return all([child.isPositional or child.isDesignated for child in self.children]) def isOnlyDesignated(self): return all([not child.isPositional for child in self.children]) def isMisra92Compliant(self): return self.structureViolationToken is None and all([child.isMisra92Compliant() for child in self.children]) def isMisra93Compliant(self): if self.elementType == 'array': result = self.isAllChildrenSet() or \ ((self.isAllSet() or self.isOnlyDesignated()) and all([not (child.isDesignated or child.isPositional) or child.isMisra93Compliant() for child in self.children])) return result elif self.elementType == 'record': result = all([child.isMisra93Compliant() for child in self.children]) return result else: return True def isMisra94Compliant(self): return self.numInits <= 1 and all([child.isMisra94Compliant() for child in self.children]) def isMisra95Compliant(self): return not self.isFlexible or all([not child.isDesignated for child in self.children]) # Parses the initializers and update the ElementDefs status accordingly class InitializerParser: def __init__(self): self.token = None self.root = None self.ed = None self.rootStack = [] def parseInitializer(self, root, token): self.root = root self.token = token dummyRoot = ElementDef('array', '->', self.root.valueType) dummyRoot.children = [self.root] self.rootStack = [] self.root = dummyRoot self.ed = self.root.getFirstValueElement() isFirstElement = False isDesignated = False while self.token: if self.token.str == ',': self.token = self.token.astOperand1 isFirstElement = False # Designated initializer ( [2]=... or .name=... ) elif self.token.isAssignmentOp and not self.token.valueType: self.popFromStackIfExitElement() self.ed = getElementByDesignator(self.root, self.token.astOperand1) if self.ed: # Update root self.pushToRootStackAndMarkAsDesignated() # Make sure ed points to valueElement self.ed = self.ed.getFirstValueElement() self.token = self.token.astOperand2 isFirstElement = False isDesignated = True elif self.token.str == '{': nextChild = self.root.getNextChild() if self.root is not None else None if nextChild: if nextChild.isArray or nextChild.isRecord: nextChild.unset() nextChild.setInitialized(isDesignated) self.ed = nextChild.getFirstValueElement() isDesignated = False elif nextChild.valueType is None: # No type information available - unable to check structure - assume correct initialization nextChild.setInitialized(isDesignated) self.unwindAndContinue() continue elif self.token.astOperand1: # Create dummy nextChild to represent excess levels in initializer dummyRoot = ElementDef('array', '<-', self.root.valueType) dummyRoot.parent = self.root dummyRoot.childIndex = 0 dummyRoot.children = [nextChild] nextChild.parent = dummyRoot self.root.markStuctureViolation(self.token) # Fake dummy as nextChild (of current root) nextChild = dummyRoot if self.token.astOperand1: self.root = nextChild self.token = self.token.astOperand1 isFirstElement = True else: if self.root: # {} if self.root.name == '<-': self.root.parent.markStuctureViolation(self.token) else: self.root.markStuctureViolation(self.token) self.ed = None self.unwindAndContinue() else: if self.ed and self.ed.isValue: if not isDesignated and len(self.rootStack) > 0 and self.rootStack[-1][1] == self.root: self.rootStack[-1][0].markStuctureViolation(self.token) if isFirstElement and self.token.str == '0' and self.token.next.str == '}': # Zero initializer causes recursive initialization self.root.initializeChildren() elif self.token.isString and self.ed.valueType and self.ed.valueType.pointer > 0: if self.ed.valueType.pointer - self.ed.getEffectiveLevel() == 1: if self.ed.parent != self.root: self.root.markStuctureViolation(self.token) self.ed.setInitialized(isDesignated) elif self.ed.valueType.pointer == self.ed.getEffectiveLevel(): if(self.root.name != '->' and self.ed.parent.parent != self.root) or (self.root.name == '->' and self.root.children[0] != self.ed.parent): self.root.markStuctureViolation(self.token) else: self.ed.parent.setInitialized(isDesignated) self.ed.parent.initializeChildren() else: if self.ed.parent != self.root: # Check if token is correct value type for self.root.children[?] child = self.root.getChildByValueElement(self.ed) if self.token.valueType: if child.elementType != 'record' or self.token.valueType.type != 'record' or child.valueType.typeScope != self.token.valueType.typeScope: self.root.markStuctureViolation(self.token) self.ed.setInitialized(isDesignated) # Mark all elements up to root with positional or designated # (for complex designators, or missing structure) parent = self.ed.parent while parent and parent != self.root: parent.isDesignated = isDesignated if isDesignated and not parent.isPositional else parent.isDesignated parent.isPositional = not isDesignated if not isDesignated and not parent.isDesignated else parent.isPositional parent = parent.parent isDesignated = False self.unwindAndContinue() def pushToRootStackAndMarkAsDesignated(self): new = self.ed.parent if new != self.root: # Mark all elements up to self.root root as designated parent = new while parent and parent != self.root: parent.isDesignated = True parent = parent.parent self.rootStack.append((self.root, new)) new.markAsCurrent() new.childIndex = new.children.index(self.ed) - 1 self.root = new def popFromStackIfExitElement(self): if len(self.rootStack) > 0 and self.rootStack[-1][1] == self.root: old = self.rootStack.pop()[0] old.markAsCurrent() self.root = old def unwindAndContinue(self): while self.token: if self.token.astParent.astOperand1 == self.token and self.token.astParent.astOperand2: if self.ed: self.ed.markAsCurrent() self.ed = self.ed.getNextValueElement(self.root) self.token = self.token.astParent.astOperand2 break else: self.token = self.token.astParent if self.token.str == '{': if self.root: self.ed = self.root.getLastValueElement() self.ed.markAsCurrent() # Cleanup if root is dummy node representing excess levels in initializer if self.root.name == '<-': self.root.children[0].parent = self.root.parent self.root = self.root.parent if self.token.astParent is None: self.token = None break def misra_9_x(self, data, rule, rawTokens = None): parser = InitializerParser() for variable in data.variables: if not variable.nameToken: continue nameToken = variable.nameToken # Check if declaration and initialization is # split into two separate statements in ast. if nameToken.next and nameToken.next.isSplittedVarDeclEq: nameToken = nameToken.next.next # Find declarations with initializer assignment eq = nameToken while not eq.isAssignmentOp and eq.astParent: eq = eq.astParent # We are only looking for initializers if not eq.isAssignmentOp or eq.astOperand2.isName: continue if variable.isArray or variable.isClass: ed = getElementDef(nameToken, rawTokens) # No need to check non-arrays if valueType is missing, # since we can't say anything useful about the structure # without it. if ed.valueType is None and not variable.isArray: continue parser.parseInitializer(ed, eq.astOperand2) # print(rule, nameToken.str + '=', ed.getInitDump()) if rule == 902 and not ed.isMisra92Compliant(): self.reportError(nameToken, 9, 2) if rule == 903 and not ed.isMisra93Compliant(): self.reportError(nameToken, 9, 3) if rule == 904 and not ed.isMisra94Compliant(): self.reportError(nameToken, 9, 4) if rule == 905 and not ed.isMisra95Compliant(): self.reportError(nameToken, 9, 5) def getElementDef(nameToken, rawTokens = None): if nameToken.variable.isArray: ed = ElementDef("array", nameToken.str, nameToken.valueType) createArrayChildrenDefs(ed, nameToken.astParent, rawTokens) elif nameToken.variable.isClass: ed = ElementDef("record", nameToken.str, nameToken.valueType) createRecordChildrenDefs(ed) else: ed = ElementDef("value", nameToken.str, nameToken.valueType) return ed def createArrayChildrenDefs(ed, token, rawTokens = None): if token.str == '[': if rawTokens is not None: foundToken = next((rawToken for rawToken in rawTokens if rawToken.file == token.file and rawToken.linenr == token.linenr and rawToken.column == token.column ), None) if foundToken and foundToken.next and foundToken.next.str == ']': ed.markAsFlexibleArray(token) if (token.astOperand2 is not None) and (token.astOperand2.getKnownIntValue() is not None): for i in range(token.astOperand2.getKnownIntValue()): createChild(ed, token, i) else: ed.markAsFlexibleArray(token) def createChild(ed, token, name): if token.astParent and token.astParent.str == '[': child = ElementDef("array", name, ed.valueType) createArrayChildrenDefs(child, token.astParent) else: if ed.valueType and ed.valueType.type == "record": child = ElementDef("record", name, ed.valueType) createRecordChildrenDefs(child) else: child = ElementDef("value", name, ed.valueType) ed.addChild(child) def createRecordChildrenDefs(ed): valueType = ed.valueType if not valueType or not valueType.typeScope: return for variable in valueType.typeScope.varlist: child = getElementDef(variable.nameToken) ed.addChild(child) def getElementByDesignator(ed, token): if not token.str in [ '.', '[' ]: return None while token.str in [ '.', '[' ]: token = token.astOperand1 while ed and not token.isAssignmentOp: token = token.astParent if token.str == '[': if not ed.isArray: ed.markStuctureViolation(token) chIndex = -1 if token.astOperand2 is not None: chIndex = token.astOperand2.getKnownIntValue() elif token.astOperand1 is not None: chIndex = token.astOperand1.getKnownIntValue() ed = ed.getChildByIndex(chIndex) if chIndex is not None else None elif token.str == '.': if not ed.isRecord: ed.markStuctureViolation(token) name = "" if token.astOperand2 is not None: name = token.astOperand2.str elif token.astOperand1 is not None: name = token.astOperand1.str ed = ed.getChildByName(name) return ed cppcheck-2.7/addons/naming.json000066400000000000000000000005271417746362400166000ustar00rootroot00000000000000{ "RE_VARNAME": ["[a-z]*[a-zA-Z0-9_]*\\Z"], "RE_PRIVATE_MEMBER_VARIABLE": null, "RE_FUNCTIONNAME": ["[a-z0-9A-Z]*\\Z"], "var_prefixes": {"uint32_t": "ui32", "int*": "intp"}, "function_prefixes": {"uint16_t": "ui16", "uint32_t": "ui32"}, "skip_one_char_variables": false } cppcheck-2.7/addons/naming.py000077500000000000000000000066421417746362400162660ustar00rootroot00000000000000#!/usr/bin/env python3 # # cppcheck addon for naming conventions # # Example usage (variable name must start with lowercase, function name must start with uppercase): # $ cppcheck --dump path-to-src/ # $ python addons/naming.py --var='[a-z].*' --function='[A-Z].*' path-to-src/*.dump # import cppcheckdata import sys import re def validate_regex(expr): try: re.compile(expr) except re.error: print('Error: "{}" is not a valid regular expression.'.format(expr)) exit(1) RE_VARNAME = None RE_CONSTNAME = None RE_PRIVATE_MEMBER_VARIABLE = None RE_FUNCTIONNAME = None for arg in sys.argv[1:]: if arg[:6] == '--var=': RE_VARNAME = arg[6:] validate_regex(RE_VARNAME) elif arg.startswith('--const='): RE_CONSTNAME = arg[arg.find('=')+1:] validate_regex(RE_CONSTNAME) elif arg.startswith('--private-member-variable='): RE_PRIVATE_MEMBER_VARIABLE = arg[arg.find('=')+1:] validate_regex(RE_PRIVATE_MEMBER_VARIABLE) elif arg[:11] == '--function=': RE_FUNCTIONNAME = arg[11:] validate_regex(RE_FUNCTIONNAME) def reportError(token, severity, msg, errorId): cppcheckdata.reportError(token, severity, msg, 'naming', errorId) for arg in sys.argv[1:]: if not arg.endswith('.dump'): continue print('Checking ' + arg + '...') data = cppcheckdata.CppcheckData(arg) for cfg in data.iterconfigurations(): print('Checking %s, config %s...' % (arg, cfg.name)) if RE_VARNAME: for var in cfg.variables: if var.access == 'Private': continue if var.nameToken and not var.isConst: res = re.match(RE_VARNAME, var.nameToken.str) if not res: reportError(var.typeStartToken, 'style', 'Variable ' + var.nameToken.str + ' violates naming convention', 'varname') if RE_CONSTNAME: for var in cfg.variables: if var.access == 'Private': continue if var.nameToken and var.isConst: res = re.match(RE_CONSTNAME, var.nameToken.str) if not res: reportError(var.typeStartToken, 'style', 'Constant ' + var.nameToken.str + ' violates naming convention', 'constname') if RE_PRIVATE_MEMBER_VARIABLE: for var in cfg.variables: if (var.access is None) or var.access != 'Private': continue res = re.match(RE_PRIVATE_MEMBER_VARIABLE, var.nameToken.str) if not res: reportError(var.typeStartToken, 'style', 'Private member variable ' + var.nameToken.str + ' violates naming convention', 'privateMemberVariable') if RE_FUNCTIONNAME: for scope in cfg.scopes: if scope.type == 'Function': function = scope.function if function is not None and function.type in ('Constructor', 'Destructor'): continue res = re.match(RE_FUNCTIONNAME, scope.className) if not res: reportError( scope.bodyStart, 'style', 'Function ' + scope.className + ' violates naming convention', 'functionName') sys.exit(cppcheckdata.EXIT_CODE) cppcheck-2.7/addons/namingng.py000077500000000000000000000265351417746362400166160ustar00rootroot00000000000000#!/usr/bin/env python3 # # cppcheck addon for naming conventions # An enhanced version. Configuration is taken from a json file # It supports to check for type-based prefixes in function or variable names. # # Example usage (variable name must start with lowercase, function name must start with uppercase): # $ cppcheck --dump path-to-src/ # $ python namingng.py test.c.dump # # JSON format: # # { # "RE_VARNAME": "[a-z]*[a-zA-Z0-9_]*\\Z", # "RE_PRIVATE_MEMBER_VARIABLE": null, # "RE_FUNCTIONNAME": "[a-z0-9A-Z]*\\Z", # "var_prefixes": {"uint32_t": "ui32"}, # "function_prefixes": {"uint16_t": "ui16", # "uint32_t": "ui32"} # } # # RE_VARNAME, RE_PRIVATE_MEMBER_VARIABLE and RE_FUNCTIONNAME are regular expressions to cover the basic names # In var_prefixes and function_prefixes there are the variable-type/prefix pairs import cppcheckdata import sys import re import argparse import json # Auxiliary class class DataStruct: def __init__(self, file, linenr, string): self.file = file self.linenr = linenr self.str = string def reportError(filename, linenr, severity, msg): message = "[{filename}:{linenr}] ( {severity} ) naming.py: {msg}\n".format( filename=filename, linenr=linenr, severity=severity, msg=msg ) sys.stderr.write(message) return message def loadConfig(configfile): with open(configfile) as fh: data = json.load(fh) return data def checkTrueRegex(data, expr, msg, errors): res = re.match(expr, data.str) if res: errors.append(reportError(data.file, data.linenr, 'style', msg)) def checkFalseRegex(data, expr, msg, errors): res = re.match(expr, data.str) if not res: errors.append(reportError(data.file, data.linenr, 'style', msg)) def evalExpr(conf, exp, mockToken, msgType, errors): if isinstance(conf, dict): if conf[exp][0]: msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1] checkTrueRegex(mockToken, exp, msg, errors) elif ~conf[exp][0]: msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1] checkFalseRegex(mockToken, exp, msg, errors) else: msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][0] checkFalseRegex(mockToken, exp, msg, errors) else: msg = msgType + ' ' + mockToken.str + ' violates naming convention' checkFalseRegex(mockToken, exp, msg, errors) def process(dumpfiles, configfile, debugprint=False): errors = [] conf = loadConfig(configfile) for afile in dumpfiles: if not afile[-5:] == '.dump': continue print('Checking ' + afile + '...') data = cppcheckdata.CppcheckData(afile) # Check File naming if "RE_FILE" in conf and conf["RE_FILE"]: mockToken = DataStruct(afile[:-5], "0", afile[afile.rfind('/') + 1:-5]) msgType = 'File name' for exp in conf["RE_FILE"]: evalExpr(conf["RE_FILE"], exp, mockToken, msgType, errors) # Check Namespace naming if "RE_NAMESPACE" in conf and conf["RE_NAMESPACE"]: for tk in data.rawTokens: if tk.str == 'namespace': mockToken = DataStruct(tk.next.file, tk.next.linenr, tk.next.str) msgType = 'Namespace' for exp in conf["RE_NAMESPACE"]: evalExpr(conf["RE_NAMESPACE"], exp, mockToken, msgType, errors) for cfg in data.configurations: print('Checking %s, config %s...' % (afile, cfg.name)) if "RE_VARNAME" in conf and conf["RE_VARNAME"]: for var in cfg.variables: if var.nameToken and var.access != 'Global' and var.access != 'Public' and var.access != 'Private': prev = var.nameToken.previous varType = prev.str while "*" in varType and len(varType.replace("*", "")) == 0: prev = prev.previous varType = prev.str + varType if debugprint: print("Variable Name: " + str(var.nameToken.str)) print("original Type Name: " + str(var.nameToken.valueType.originalTypeName)) print("Type Name: " + var.nameToken.valueType.type) print("Sign: " + str(var.nameToken.valueType.sign)) print("variable type: " + varType) print("\n") print("\t-- {} {}".format(varType, str(var.nameToken.str))) if conf["skip_one_char_variables"] and len(var.nameToken.str) == 1: continue if varType in conf["var_prefixes"]: if not var.nameToken.str.startswith(conf["var_prefixes"][varType]): errors.append(reportError( var.typeStartToken.file, var.typeStartToken.linenr, 'style', 'Variable ' + var.nameToken.str + ' violates naming convention')) mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Variable' for exp in conf["RE_VARNAME"]: evalExpr(conf["RE_VARNAME"], exp, mockToken, msgType, errors) # Check Private Variable naming if "RE_PRIVATE_MEMBER_VARIABLE" in conf and conf["RE_PRIVATE_MEMBER_VARIABLE"]: # TODO: Not converted yet for var in cfg.variables: if (var.access is None) or var.access != 'Private': continue mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Private member variable' for exp in conf["RE_PRIVATE_MEMBER_VARIABLE"]: evalExpr(conf["RE_PRIVATE_MEMBER_VARIABLE"], exp, mockToken, msgType, errors) # Check Public Member Variable naming if "RE_PUBLIC_MEMBER_VARIABLE" in conf and conf["RE_PUBLIC_MEMBER_VARIABLE"]: for var in cfg.variables: if (var.access is None) or var.access != 'Public': continue mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Public member variable' for exp in conf["RE_PUBLIC_MEMBER_VARIABLE"]: evalExpr(conf["RE_PUBLIC_MEMBER_VARIABLE"], exp, mockToken, msgType, errors) # Check Global Variable naming if "RE_GLOBAL_VARNAME" in conf and conf["RE_GLOBAL_VARNAME"]: for var in cfg.variables: if (var.access is None) or var.access != 'Global': continue mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Public member variable' for exp in conf["RE_GLOBAL_VARNAME"]: evalExpr(conf["RE_GLOBAL_VARNAME"], exp, mockToken, msgType, errors) # Check Functions naming if "RE_FUNCTIONNAME" in conf and conf["RE_FUNCTIONNAME"]: for token in cfg.tokenlist: if token.function: if token.function.type == 'Constructor' or token.function.type == 'Destructor': continue retval = token.previous.str prev = token.previous while "*" in retval and len(retval.replace("*", "")) == 0: prev = prev.previous retval = prev.str + retval if debugprint: print("\t:: {} {}".format(retval, token.function.name)) if retval and retval in conf["function_prefixes"]: if not token.function.name.startswith(conf["function_prefixes"][retval]): errors.append(reportError( token.file, token.linenr, 'style', 'Function ' + token.function.name + ' violates naming convention')) mockToken = DataStruct(token.file, token.linenr, token.function.name) msgType = 'Function' for exp in conf["RE_FUNCTIONNAME"]: evalExpr(conf["RE_FUNCTIONNAME"], exp, mockToken, msgType, errors) # Check Class naming if "RE_CLASS_NAME" in conf and conf["RE_CLASS_NAME"]: for fnc in cfg.functions: # Check if it is Constructor/Destructor if fnc.type == 'Constructor' or fnc.type == 'Destructor': mockToken = DataStruct(fnc.tokenDef.file, fnc.tokenDef.linenr, fnc.name) msgType = 'Class ' + fnc.type for exp in conf["RE_CLASS_NAME"]: evalExpr(conf["RE_CLASS_NAME"], exp, mockToken, msgType, errors) return errors if __name__ == "__main__": parser = argparse.ArgumentParser(description='Naming verification') parser.add_argument('dumpfiles', type=str, nargs='+', help='A set of dumpfiles to process') parser.add_argument("--debugprint", action="store_true", default=False, help="Add debug prints") parser.add_argument("--configfile", type=str, default="naming.json", help="Naming check config file") parser.add_argument("--verify", action="store_true", default=False, help="verify this script. Must be executed in test folder !") args = parser.parse_args() errors = process(args.dumpfiles, args.configfile, args.debugprint) if args.verify: print(errors) if len(errors) < 6: print("Not enough errors found") sys.exit(1) target = [ '[namingng_test.c:8] ( style ) naming.py: Variable badui32 violates naming convention\n', '[namingng_test.c:11] ( style ) naming.py: Variable a violates naming convention\n', '[namingng_test.c:29] ( style ) naming.py: Variable badui32 violates naming convention\n', '[namingng_test.c:20] ( style ) naming.py: Function ui16bad_underscore violates naming convention\n', '[namingng_test.c:25] ( style ) naming.py: Function u32Bad violates naming convention\n', '[namingng_test.c:37] ( style ) naming.py: Function Badui16 violates naming convention\n'] diff = set(errors) - set(target) if len(diff): print("Not the right errors found {}".format(str(diff))) sys.exit(1) print("Verification done\n") sys.exit(0) if len(errors): print('Found errors: {}'.format(len(errors))) sys.exit(1) sys.exit(0) cppcheck-2.7/addons/runaddon.py000066400000000000000000000005061417746362400166150ustar00rootroot00000000000000import cppcheckdata, cppcheck, runpy, sys, os if __name__ == '__main__': addon = sys.argv[1] __addon_name__ = os.path.splitext(os.path.basename(addon))[0] sys.argv.pop(0) runpy.run_path(addon, run_name='__main__') # Run registered checkers cppcheck.runcheckers() sys.exit(cppcheckdata.EXIT_CODE)cppcheck-2.7/addons/test/000077500000000000000000000000001417746362400154075ustar00rootroot00000000000000cppcheck-2.7/addons/test/__init__.py000066400000000000000000000000001417746362400175060ustar00rootroot00000000000000cppcheck-2.7/addons/test/cert-test.c000066400000000000000000000072331417746362400174720ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump cert-test.c && python ../cert.py -verify cert-test.c.dump #include #include #include #include #include struct S { short a; short b; }; #pragma pack() struct PackedStruct { short a; short b; }; void api01() { const size_t String_Size = 20; struct bad_node_s { char name[String_Size]; struct bad_node_s* next; // cert-API01-C }; struct good_node_s { struct good_node_s* next; char name[String_Size]; }; struct also_good_node_s { struct also_good_node_s* next; char *name; }; } void dostuff(int *data); void exp05() { const int x = 42; int y = (int)x; int *p; p = (int *)&x; // cert-EXP05-C const int data[] = {1,2,3,4}; dostuff(data); // cert-EXP05-C } void print(const char *p); void exp05_fp() { print("hello"); } void exp42() { struct S s1 = {1,2}; struct S s2 = {1,2}; memcmp(&s1, &s2, sizeof(struct S)); // cert-EXP42-C struct PackedStruct s3 = {1,2}; struct PackedStruct s4 = {1,2}; memcmp(&s3, &s4, sizeof(struct S)); } void exp46(int x, int y, int z) { if ((x == y) & z) {} // cert-EXP46-c } void int31(int x) { x = (unsigned char)1000; // cert-INT31-c x = (signed char)0xff; // cert-INT31-c x = (unsigned char)-1; // cert-INT31-c x = (unsigned long long)-1; // cert-INT31-c unsigned char c; c = 256; c = -1; } void env33() { system("chmod -x $(which chmod)"); // cert-ENV33-C system(""); // cert-ENV33-C system(NULL); // no-warning system(0); // no-warning const int *np = NULL; system(np); // no-warning int system; } void msc24() { struct S { int x; int fopen; }; struct S s; time_t rawtime; struct tm *timeinfo; char buffer[256]; int i; long int li; long long int lli; FILE *f; s.fopen = 123; f = fopen ("myfile.txt","w+"); //cert-MSC24-C setbuf ( f , buffer ); //cert-MSC24-C for ( i='A' ; i<='Z' ; i++) fputc ( i, f); rewind (f); //cert-MSC24-C fclose (f); time ( &rawtime ); timeinfo = localtime ( &rawtime ); printf ( "The current date/time is: %s", asctime (timeinfo) ); //cert-MSC24-C float n = atof (buffer); //cert-MSC24-C float m = sin (n*M_PI/180); i = atoi (buffer); //cert-MSC24-C li = atol(buffer); //cert-MSC24-C lli = atoll(buffer); //cert-MSC24-C time (&rawtime); printf ("The current local time is: %s", ctime (&rawtime)); //cert-MSC24-C freopen ("myfile.txt","w",stdout); //cert-MSC24-C printf ("This sentence is redirected to a file."); fclose (stdout); } void msc30() { unsigned int num = rand(); // cert-MSC30-c int rand = 5; int a = rand; } void exp15() { int x=5, y=7; if(x==y); //cert-EXP15-C { printf("not working\n"); } if(x) ; } void str03() { char *string_data=(char*)malloc(16); char a[16]; int d; strncpy(a, string_data, sizeof(a)); //cert-STR03-C strncpy(a, string_data, 5); d=sizeof(int); } void str05() { int x=5, y=7; if(x==y); //cert-EXP15-C { printf("not working\n"); } if(x) ; } void str07(char *buf, const char *newBuf) { const char *str = "test"; strcat(buf,"bla"); strcat(buf, str); //cert-STR07-C strcat(buf, newBuf); //cert-STR07-C strcpy(buf, newBuf); //cert-STR07-C } void str11() { const char str[3]="abc"; //cert-STR11-C const char *x[10]; x[3]="def"; } cppcheck-2.7/addons/test/cert-test.cpp000066400000000000000000000011161417746362400200240ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump cert-test.cpp && python ../cert.py -verify cert-test.cpp.dump #include class msc30TestClass { public: static int rand(); }; namespace exp05c { using uint32 = std::uint32_t; static const uint32 a = static_cast(0xFFFFFFFF); } void msc30(msc30TestClass & testClass) { unsigned int num = rand(); // cert-MSC30-c num = std::rand(); // cert-MSC30-c num = msc30TestClass::rand(); num = unknownClass::rand(); num = testClass.rand(); num = unknownClass.rand(); int rand = 5; int a = rand; } cppcheck-2.7/addons/test/misc-test.cpp000066400000000000000000000014061417746362400200240ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump misc-test.cpp && python ../misc.py -verify misc-test.cpp.dump #include #include // Warn about string concatenation in array initializers.. const char *a[] = {"a" "b"}; // stringConcatInArrayInit const char *b[] = {"a","b" "c"}; // stringConcatInArrayInit #define MACRO "MACRO" const char *c[] = { MACRO "text" }; // stringConcatInArrayInit // Function is implicitly virtual class base { virtual void dostuff(int); }; class derived : base { void dostuff(int); // implicitlyVirtual }; // Pass struct to ellipsis function struct {int x;int y;} s; void ellipsis(int x, ...); void foo(std::vector v) { ellipsis(321, s); // ellipsisStructArg ellipsis(321, v[0]); // ellipsisStructArg } cppcheck-2.7/addons/test/misra/000077500000000000000000000000001417746362400165225ustar00rootroot00000000000000cppcheck-2.7/addons/test/misra/misra-ctu-1-test.c000066400000000000000000000017561417746362400217160ustar00rootroot00000000000000// Test with command: // ./cppcheck --enable=information --addon=misra --inline-suppr addons/test/misra/misra-ctu-*-test.c #include "misra-ctu-test.h" extern MISRA_2_3_A misra_2_3_a; x = MISRA_2_5_OK_1; // cppcheck-suppress misra-c2012-2.3 // cppcheck-suppress misra-c2012-5.6 typedef int MISRA_5_6_VIOLATION; // cppcheck-suppress misra-c2012-5.7 struct misra_5_7_violation_t { int x; }; static misra_5_7_violation_t misra_5_7_use_type_1; // cppcheck-suppress misra-c2012-8.4 // cppcheck-suppress misra-c2012-5.8 int misra_5_8_var1; // cppcheck-suppress misra-c2012-8.4 // cppcheck-suppress misra-c2012-5.8 int misra_5_8_var2; // cppcheck-suppress misra-c2012-5.8 static void misra_5_8_f(void) {} // cppcheck-suppress misra-c2012-5.9 static int misra_5_9_count; // cppcheck-suppress misra-c2012-5.9 static void misra_5_8_foo(void) {} // cppcheck-suppress misra-c2012-8.5 extern int misra_8_5; // cppcheck-suppress misra-c2012-8.4 // cppcheck-suppress misra-c2012-8.6 int32_t misra_8_6 = 1; cppcheck-2.7/addons/test/misra/misra-ctu-2-test.c000066400000000000000000000021541417746362400217100ustar00rootroot00000000000000// Test with command: // ./cppcheck --enable=information --addon=misra --inline-suppr addons/test/misra/misra-ctu-*-test.c #include "misra-ctu-test.h" extern MISRA_2_3_B misra_2_3_b; x = MISRA_2_5_OK_2; // cppcheck-suppress misra-c2012-5.6 typedef int MISRA_5_6_VIOLATION; static MISRA_5_6_VIOLATION misra_5_6_x; // cppcheck-suppress misra-c2012-5.7 struct misra_5_7_violation_t { int x; }; static misra_5_7_violation_t misra_5_7_use_type_2; // cppcheck-suppress misra-c2012-5.8 static int misra_5_8_var1; // cppcheck-suppress misra-c2012-8.4 // cppcheck-suppress misra-c2012-5.8 void misra_5_8_f(void) { // cppcheck-suppress misra-c2012-5.8 char misra_5_8_var2; } // cppcheck-suppress misra-c2012-5.9 static int misra_5_9_count; // cppcheck-suppress misra-c2012-5.9 static void misra_5_8_foo(void) {} // cppcheck-suppress misra-c2012-8.5 extern int misra_8_5; // cppcheck-suppress misra-c2012-8.4 // cppcheck-suppress misra-c2012-8.6 int32_t misra_8_6 = 2; // cppcheck-suppress misra-c2012-8.4 // cppcheck-suppress misra-c2012-8.7 void misra_8_7(void) {} static void misra_8_7_caller(void) { misra_8_7(); } cppcheck-2.7/addons/test/misra/misra-ctu-test.h000066400000000000000000000005051417746362400215540ustar00rootroot00000000000000 typedef int MISRA_2_3_A; typedef int MISRA_2_3_B; typedef int MISRA_2_3_VIOLATION; // cppcheck-suppress misra-c2012-2.3 // cppcheck-suppress misra-c2012-2.4 struct misra_2_4_violation_t { int x; }; #define MISRA_2_5_OK_1 1 #define MISRA_2_5_OK_2 2 // cppcheck-suppress misra-c2012-2.5 #define MISRA_2_5_VIOLATION 0 cppcheck-2.7/addons/test/misra/misra-suppressions1-test.c000066400000000000000000000016601417746362400236150ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported // This needs to stay at line number 7 to make the test pass // If it is changed update suppressions.txt with the new line number #include //21.6 extern int misra_5_2_var_hides_var______31x;//8.4 static int misra_5_2_var_hides_var______31y;//5.2 static int misra_5_2_function_hides_var_31x; static void misra_5_2_function_hides_var_31y(void) {}//5.2 static void foo(void) { int i; switch(misra_5_2_func1()) //16.4 16.6 { case 1: { do { for(i = 0; i < 10; i++) { if(misra_5_2_func3()) //14.4 { int misra_5_2_var_hides_var_1____31x; int misra_5_2_var_hides_var_1____31y;//5.2 } } } while(misra_5_2_func2()); //14.4 } } } cppcheck-2.7/addons/test/misra/misra-suppressions2-test.c000066400000000000000000000011601417746362400236110ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported union misra_5_2_field_hides_field__63x { //19.2 int misra_5_2_field_hides_field__31x; int misra_5_2_field_hides_field__31y;//5.2 }; struct misra_5_2_field_hides_field__63y { //5.2 int misra_5_2_field_hides_field1_31x; int misra_5_2_field_hides_field1_31y;//5.2 }; const char *s41_1 = "\x41g"; // 4.1 8.4 const char *s41_2 = "\x41\x42"; // 8.4 // cppcheck-suppress misra-c2012-5.7 struct misra_5_7_violation_t { int x; }; cppcheck-2.7/addons/test/misra/misra-test-avr8.c000066400000000000000000000004261417746362400216360ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --addon=misra --platform=avr8 misra-test-avr8.c static void misra_10_4(void) { // #10480 char buf[1] = {'f'}; const char c = '0'; signed int x = buf[0] - c; } static void misra_12_2(void) { a = (((uint64_t)0xFF) << 32); } cppcheck-2.7/addons/test/misra/misra-test.c000066400000000000000000001634321417746362400207670ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump misra/misra-test.h --std=c89 // ~/cppcheck/cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra/misra-test.c --std=c89 --platform=unix64 && python3 ../misra.py -verify misra/misra-test.c.dump #include "path\file.h" // 20.2 #include "file//.h" // 20.2 #include "file/*.h" // 20.2 #include "file'.h" // 20.2 #include // 20.2 #include "file,.h" // 20.2 #include "misra-test.h" #include /*abc*/ "file.h" // no warning /*foo*/#include "file.h" // no warning #include "./file.h" // no warning #include \ "file.h" #include /*abc*/ \ "file.h" #include "fi" "le.h" // 20.3 (strings are concatenated after preprocessing) #include "fi" // 20.3 #include // 20.3 #include PATH "file.h" // 20.3 #define H_20_3_ok "file.h" #include H_20_3_ok #include file.h // 20.3 #define H_20_3_bad file.h #include H_20_3_bad // TODO: 20.3 Trac #9606 #include "//file.h" // 20.2 #include "//file.h" H_20_3_bad // 20.2 20.3 //#include H_20_3_bad // no warning #include H_20_3_ok H_20_3_ok // 20.3 #include // no warning #include // 21.4 #include // 21.5 #include //21.6 #include //21.6 #include // 21.10 #include // 21.11 #include // Check that the addon doesn't crash typedef struct { union { // 19.2 struct { unsigned a : 2; // 8.1 unsigned : 14; }; uint16_t value; }; } STRUCT_BITS; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef signed int s32; typedef unsigned long long u64; static _Atomic int misra_1_4_var; // 1.4 static _Noreturn void misra_1_4_func(void) // 1.4 { if (0 != _Generic(misra_1_4_var)) {} // 1.4 printf_s("hello"); // 1.4 } static void misra_2_2(int x) { int a; a = x + 0; // 2.2 a = 0 + x; // 2.2 a = x * 0; // 2.2 a = 0 * x; // 2.2 a = x * 1; // 2.2 a = 1 * x; // 2.2 (void)a; } /* // */ // 3.1 /* /* */ // 3.1 //// // http://example.com // no warning static void misra_2_7_unused_param (int *param1, int unused_param) // 2.7 { *param1 = 42U; } static void misra_2_7_used_params (int *param1, int param2, int param3) { (void)param3; *param1 = param2; } static void misra_2_7_a(int a, int b, // 2.7 int c, int d) // 2.7 { (void)a; (void)c; } static void misra_2_7_b(int a, int b, int c, // 2.7 int d) // 2.7 { (void)a; } static void misra_2_7_c(int a, ...) { (void)a; } static void misra_2_7_d(int) { } // 2.7 8.2 static void misra_3_2(int enable) { // This won't generate a violation because of subsequent blank line \ int y = 0; int x = 0; // 3.2 non-compliant comment ends with backslash \ if (enable != 0) { ++x; // This is always executed // 3.2 potentially non-compliant comment ends with trigraph resolved to backslash ??/ ++y; // This is hidden if trigraph replacement is active } (void)printf("x=%i, y=%i\n", x, y); } extern int misra_5_1_extern_var_hides_var_x; extern int misra_5_1_extern_var_hides_var_y; //5.1 int misra_5_1_var_hides_var________a; // 8.4 int misra_5_1_var_hides_var________b; int misra_5_1_var_hides_var________b1; int misra_5_1_var_hides_var________b2; //5.1 8.4 int misra_5_1_var_hides_var________c; //5.1 8.4 int misra_5_1_var_hides_var________d; //5.1 8.4 int misra_5_1_var_hides_var________e; //5.1 8.4 extern const uint8_t misra_5_2_var1; const uint8_t misra_5_2_var1 = 3; static int misra_5_2_var_hides_var______31x; static int misra_5_2_var_hides_var______31y;//5.2 static int misra_5_2_function_hides_var_31x; static void misra_5_2_function_hides_var_31y(void) {}//5.2 static void foo(void) { int i; switch(misra_5_2_func1()) //16.4 16.6 { case 1: { do { for(i = 0; i < 10; i++) { if(misra_5_2_func3()) //14.4 { int misra_5_2_var_hides_var_1____31x; int misra_5_2_var_hides_var_1____31y;//5.2 } } } while(misra_5_2_func2()); //14.4 } break; } } union misra_5_2_field_hides_field__63x { //19.2 int misra_5_2_field_hides_field__31x; int misra_5_2_field_hides_field__31y;//5.2 }; struct misra_5_2_field_hides_field__63y { //5.2 int misra_5_2_field_hides_field1_31x; int misra_5_2_field_hides_field1_31y;//5.2 }; const char *s41_1 = "\x41g"; // 4.1 8.4 const char *s41_2 = "\x41\x42"; // 8.4 const char *s41_3 = "\x41" "\x42"; // 8.4 const char *s41_4 = "\x41" "g"; // 8.4 const char *s41_5 = "\x41\xA"; // 8.4 const char *s41_6 = "\xA\x41"; // 8.4 const char *s41_7 = "\xAA\xg\x41"; // 4.1 8.4 const char *s41_8 = "\xAA\x\x41"; // 4.1 8.4 const char *s41_9 = "unknown\gsequence"; // 8.4 const char *s41_10 = "simple\nsequence"; // 8.4 const char *s41_11 = "string"; // 8.4 int c41_3 = '\141t'; // 4.1 8.4 int c41_4 = '\141\t'; // 8.4 int c41_5 = '\0'; // 10.3 8.4 int c41_6 = '\0\t'; // 8.4 int c41_7 = '\12\t'; // 8.4 int c41_8 = '\0t'; // 4.1 8.4 int c41_9 = '\12'; // 8.4 int c41_10 = '\12\n'; // 8.4 int c41_11 = '\12n'; // 4.1 8.4 int c41_12 = '\12323'; // 4.1 8.4 int c41_13 = '\123\3'; // 8.4 // TODO int c41_14 = '\777\777'; int c41_15 = 'a'; // 10.3 8.4 static void misra_4_1(void) { (void)printf("\x41g"); // 4.1 (void)printf("\x41\x42"); (void)printf("\x41" "g"); } const char *s42_1 = "String containing trigraphs ??-??-??"; // 4.2 8.4 const char *s42_2 = "String containing trigraph???=preceded by question mark"; // 4.2 8.4 const char *s42_3 = "No trigraph?(?'?)"; // 8.4 static void misra_4_2(void) { (void)printf("??=Trigraph\n"); // 4.2 (void)printf("No?/Trigraph\n"); } #define misra_5_4_macro_hides_macro__31x 1 #define misra_5_4_param_hides_macro__31x 1 #define misra_5_4_macro_hides_macro__31y 2 //5.4 #define m1(misra_5_4_param_hides_macro__31y) 1 //5.4 #define m2(misra_5_4_param_hides_param__31x,misra_5_4_param_hides_param__31y) 1 //5.4 #ifdef misra_5_4_macro_hides_macro__31x #define misra_5_4_macro 1 // no warning #else #define misra_5_4_macro 2 // no warning #endif #define misra_5_5_var_hides_macro____31x 1 #define misra_5_5_functionhides_macro31x 1 #define misra_5_5_param_hides_macro__31x 1 #define misra_5_5_tag_hides_macro____31x 1 #define misra_5_5_hides_macro________31x 1 int misra_5_5_var_hides_macro____31y; //5.5 8.4 static void misra_5_5_functionhides_macro31y(int misra_5_5_param_hides_macro__31y){(void)misra_5_5_param_hides_macro__31y;} //5.5 struct misra_5_5_tag_hides_macro____31y { //5.5 int x; }; static void misra_5_5_func1(void) { switch(misra_5_5_func2()) //16.4 16.6 { case 1: { do { if(misra_5_5_func3()) //14.4 { int misra_5_5_hides_macro________31y; //5.5 } } while(misra_5_5_func2()); //14.4 } break; } } typedef unsigned int UINT_TYPEDEF; struct struct_with_bitfields { unsigned int a:2; // Compliant signed int b:2; // Compliant UINT_TYPEDEF c:2; // Compliant int d:2; // 6.1 - plain int not compliant signed long f:2; // 6.1 - signed long not compliant unsigned int g:1; // Compliant signed int h:1; // 6.2 - signed int with size 1 is not compliant uint16_t i:1; // Compliant }; static void misra6_1_fn(void) { // "Use" occurrence should not generate warnings struct_with_bitfields s; s.h = 61; } static void misra_7_1(void) { int x = 066; // 7.1 } static void misra_7_2_call_test(int a, unsigned int b, unsigned int c) { } // 2.7 static void misra_7_2_call_va_test(int a, ...) { } // 2.7 static void misra_7_2(void) { uint32_t a = 2147483647; const uint32_t b = 2147483648U; const uint32_t c = 2147483648; // 7.2 10.3 uint32_t d = 2147483649; // 7.2 10.3 uint8_t e = 0x80; // 7.2 10.3 uint8_t f = 0x80U; uint16_t g = 0x8000; // 7.2 10.3 uint16_t h = 0x8000U; uint32_t i = 0x80000000; // 7.2 uint32_t j = 0x80000000U; uint64_t k = 0x8000000000000000; // TODO 7.2 uint64_t l = 0x8000000000000000ULL; uint32_t m = 1 + 0x80000000; // 7.2 10.4 misra_7_2_call_test(1, 2, 2147483648U); misra_7_2_call_test(1, 2, 2147483648); // 7.2 misra_7_2_call_test(1, 0x80000000, 3); // 7.2 misra_7_2_call_va_test(1, 2, 3); } // The addon should not generate false positives for the identifiers. struct misra_7_3_s { uint32_t ul_clka; uint32_t test123l; }; static void misra_7_3(void) { long misra_7_3_a = 0l; //7.3 long misra_7_3_b = 0lU; //7.3 long long misra_7_3_c = 0Ull; //7.3 long long misra_7_3_d = 0ll; //7.3 long double misra_7_3_e = 7.3l; //7.3 struct misra_7_3_s misra_7_3_f = { .ul_clka = 19U, .test123l = 23U }; } typedef const char* MISRA_7_4_CHAR_CONST; static MISRA_7_4_CHAR_CONST misra_7_4_return_const_type_def (void) { return "return_typedef_const"; } static char *misra_7_4_return_non_const (void) { return 1 + "return_non_const"; } // 7.4 18.4 static const char *misra_7_4_return_const (void) { return 1 + "return_const"; } // 18.4 static void misra_7_4_const_call(int a, const char* b) { } // 2.7 static void misra_7_4_const_ptr_call(int a, const char const* b) { } // 2.7 static void misra_7_4_call(int a, char* b) { } // 2.7 static void misra_7_4(void) { const char *a = "text a"; char* const b = "text_b"; // 7.4 char *c = "text c"; // 7.4 char *d = 1 + "text d"; // 7.4 18.4 char *e = "text e" + 1 + 2; // 7.4 18.4 char *f = 1 + "text f" + 2; // 7.4 18.4 const wchar_t *g = "text_g"; wchar_t *h = "text_h"; // 7.4 misra_7_4_const_call(1, ("text_const_call")); misra_7_4_const_ptr_call(1, ("text_const_call")); misra_7_4_call(1, "text_call"); // 7.4 11.8 } const misra_8_1_a; // 8.1 8.4 static int misra_8_2_a (int n, ...); extern int misra_8_2_b (int n); extern int misra_8_2_c (int); // 8.2 static int misra_8_2_d (); // 8.2 static int misra_8_2_e (void); static int misra_8_2_f (vec, n ) int *vec; // 8.2 int n; // 8.2 { return vec[ n - 1 ]; } static int misra_8_2_g ( /* comment */ ); // 8.2 static int misra_8_2_h ( /* comment 1 */ /* comment 2 */ ); // 8.2 static int misra_8_2_i ( /* comment */ void); static int misra_8_2_j ( /* comment */ void /* comment */); static int misra_8_2_k ( // void); static int misra_8_2_l ( // 8.2 ); static void misra_8_2_m(uint8_t * const x); static void misra_8_2_m(uint8_t * const x) { (void)x; } int16_t ( *misra_8_2_p_a ) (); // 8.2 8.4 int16_t ( *misra_8_2_p_b ) (void); // 8.4 int16_t ( *misra_8_2_p_c ) (int); // 8.4 static int misra_8_2_n(int a) { return a + 42; } static int misra_8_2_o( const uint32_t a1, const uint8_t *const a2 ) { return *a2 + a1; } static int misra_8_2_p( const uint32_t a1, const uint8_t *const a2 ); static int misra_8_2_q (); // 8.2 void misra_8_4_foo(void) {} // 8.4 extern void misra_8_4_func(void); void misra_8_4_func(void) {} static void misra_8_4_bar(void) {} // Declared in header extern int16_t misra_8_4_count; // no-warning int16_t misra_8_4_count = 0; // Compliant extern uint8_t misra_8_4_buf1[13]; // no-warning uint8_t misra_8_4_buf2[24]; // 8.4 typedef struct { uint16_t a; uint16_t b; } misra_8_4_struct; extern misra_8_4_struct bar[42]; misra_8_4_struct bar[42]; // compliant static int32_t misra_8_8 = 123; extern int32_t misra_8_8; // 8.8 static int32_t misra_8_9_i; // 8.9 static int32_t misra_8_9_foo(void) { return misra_8_9_i++; } inline int32_t misra_8_10_value(void) { return 123; } // 8.10 8.4 extern int a811[]; // 8.11 enum misra_8_12_a { misra_a1 = 1, misra_a2 = 2, misra_a3, misra_a4 = 3 }; //8.12 enum misra_8_12_b { misra_b1, misra_b2, misra_b3 = 3, misra_b4 = 3 }; // no-warning enum misra_8_12_c { misra_c1 = misra_a1, misra_c2 = 1 }; // no-warning enum misra_8_12_d { misra_d1 = 1, misra_d2 = 2, misra_d3 = misra_d1 }; // no-warning enum misra_8_12_e { misra_e1 = sizeof(int), misra_e2}; // no-crash static void misra_8_14(char * restrict str) {(void)str;} // 8.14 static void misra_9_empty_or_zero_initializers(void) { int a[2] = {}; // 9.2 int b[2][2] = {}; // 9.2 int c[2][2] = { {} }; // 9.2 9.3 int d[2][2] = { {}, {} }; // 9.2 int e[2][2] = { { 1 , 2 }, {} }; // 9.2 int f[5] = { 0 }; int g[5][2] = { 0 }; int h[2][2] = { { 0 } }; // 9.3 int i[2][2] = { { 0 }, { 0 } }; int j[2][2] = { { 1, 2 }, { 0 } }; int k[2][2] = { [0] = { 1 , 2 }, { 0 } }; int l[1][2] = { { 0 }, [0] = { 1 } }; // 9.3 9.4 typedef struct { int a; int b; } struct1; struct1 m = { }; // 9.2 struct1 n = { 0 }; } static void misra_9_string_initializers(void) { const char a[12] = { "Hello world" }; // 9.2 const char b[2][20] = "Hello world"; // 9.2 9.3 const char c[] = "Hello world"; const char d[15] = "Hello world"; const char e[1][12] = { "Hello world" }; const char *f[2] = { "Hello", [1] = "world" }; const char *g[1] = "Hello world"; // 9.2 const char h[2][15] = { { 0 }, "Hello world" }; char **str_p = &f[0]; char **i[1] = { str_p }; char **j[1] = { { str_p } }; // 9.2 } static void misra_9_array_initializers(void) { char a[4] = { 1, 2, 3, 4 }; char b[2][2] = { {1, 2}, {3, 4} }; char c[2][2] = { 1, 2, 3, 4 }; // 9.2 char d[6] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }; // 9.2 9.3 char e[2][2] = { {1, 2}, {4} }; // 9.3 char f[2][2] = { 1, 2, 3 }; // 9.2 9.3 char g[2][2] = { {1, 2, 3, 4} }; // 9.3 char h[2][2] = { { 1, { 2 } }, { 3, { 5 } } }; // 9.2 char i[2][2] = { { 1, { 2 } }, { 3 } }; // 9.2 9.3 char j[2][3] = { { 1, { 2 }, 3 }, { 4, { 5 }, 6 } }; // 9.2 char k[2][3] = { { 1, { 2 }, 3 }, { 4, { 5 } } }; // 9.2 9.3 char l[3] = { 1, { 2, 3 } }; // 9.2 9.3 } static void misra_9_array_initializers_with_designators(void) { char a[1] = { [0][1] = 1 }; // 9.2 char b[1] = { [0] = { 1, 2 } }; // 9.2 char c[2][2] = { [0] = {1, 2, 3} }; char d[1][2] = { [0] = 1 }; // 9.2 char e[2][2] = { { 1, 2 }, [1][0] = {3, 4} }; // 9.2 char f[2] = { [0] = 1, 2 }; char g[2] = { [1] = 2, [0] = 1 }; char h[2][2] = { { 1, 2 }, [1] = { 3 } }; // 9.3 char i[2][2] = { { 1, 2 }, [1] = { 3, 4 } }; char j[2][2] = { { 1, 2 }, [1] = { [0] = 3 } }; char k[2][2] = { { 1, 2 }, [1][0] = 3 }; char l[2][2] = { { 1, 2 }, [1][0] = 3, 4}; // 9.2 char m[2][2] = { [0] = { [2] = 2 }, [1][5] = 4 }; char n[2][2] = { [0] = { 1 } }; // 9.3 char o[2][2] = { { 1 }, [1][0] = 3 }; // 9.3 char p[2][2] = { { 1, 2 }, { 3, 4 }, [1] = { 3 } }; // 9.3 9.4 // cppcheck-suppress unknownEvaluationOrder char q[2][2] = { { 1, 2 }, { 1 }, [1] = { [1] = 3 } }; // 9.4 char r[2][2][2] = { [0][0] = { 1, 2 }, [1] = { [0] = {5, 6} } }; char s[2][2][2] = { [0][0] = { 1, 2 }, [1] = {5, 6, 7, 8}}; // 9.2 char t[2][2][2] = { [0][0] = { 1, 2 }, {3, 4}, [1] = {5, 6}}; // 9.2 9.3 char u[2][2][2] = { [0] = { 1, 2, {3, 4} } }; // 9.2 char v[2][2][2] = { [0] = { 1, 2, [1] = {3, 4} }}; // 9.2 } static void misra_9_struct_initializers(void) { typedef struct { int i1; int i2; } struct1; typedef struct { char c1; struct1 is1; char c2[4]; } struct2; typedef struct { struct1 s[2][2]; } struct3; typedef struct { unknown_field_type f1; unknown_field_type f2[2]; int f3[2]; } struct_with_unknown_fields; struct3 sa[2] = { [1].s[1][0].i1 = 3, 4 }; // 9.2 struct1 sa = 1; // 9.2 struct1 sb = { 1, 2 }; struct2 sc = { 1, { 2 }, {4, 5, 6, 7} }; struct2 sd = { 1, { 2, 3 }, {4, 5, 6} }; // 9.3 struct2 se = { 1, 2, 3, 4, 5, 6, 7 }; // 9.2 struct2 sf = { 1, { 2, 3 }, 4, 5, 6, 7 }; // 9.2 struct2 sg = { 1, { 2 }, 4, 5, 6, 7 }; // 9.2 struct2 sh = { 1, { 2, 3 }, 4, 5, 6 }; // 9.2 9.3 struct2 si = { 1, 2, 3, {4,5,6,7} }; // 9.2 int a; struct1 sj = { a = 1, 2 }; // 13.1 // Struct types struct2 sta = { .is1 = sc }; // 9.2 struct2 stb = { .is1 = sb }; struct1 stc[1] = { sc }; // 9.2 struct1 std[1] = { sb }; // Struct designators struct1 sda = { 1, .i2 = 2 }; struct2 sdb = { 1, { 2, .i2=3 }, .c2[1]=5 }; struct2 sdc = { 1, { 2, .i2=3 }, .c2 = { 5 } }; // 9.3 struct2 sdd = { 1, { 2, .i2=3 }, .c2 = 5 }; // 9.2 struct2 sde = { .is1 = { 2, 3 }, { 4, 5, 6, 7 } }; // Struct arrays struct1 asa[2] = { {1,2}, {3,4} }; struct1 asb[2] = { {1}, {3,4} }; struct1 asc[2] = { {1,2} }; // 9.3 struct1 asd[2] = { 1,2, 3,4 }; // 9.2 struct1 ase[2] = { 1,2, 3 }; // 9.2 struct1 asf[2] = { 1,2 }; // 9.2 9.3 struct1 asg[2] = { [1].i1 = 3 }; struct3 ash[2] = { [1].s[1][0].i1 = 3 }; struct3 asi[2] = { [0] = { .s[0] = { { 1, 2 } }}}; // 9.3 struct3 asj[2] = { [0] = { .s[0] = { 1, 2 }}}; // 9.2 9.3 // Missing type information dummy_struct dsa = { 1, .a = 2 }; dummy_struct dsb[2] = { {1,2}, {3,4} }; dummy_struct dsc[2][2] = { {1,2}, {3,4} }; dummy_struct dsd[2][2] = { 1, 2, 3, 4 }; // 9.2 dummy_struct dse[3] = { {1,2}, {3,4}, [1] = {5,6} }; // 9.3 9.4 dummy_struct dsf[] = { [0] = 1 }; // 9.5 dummy_struct dsg = { .a = {0}, .b = {0} }; dummy_struct dsh[2][2] = { { {.a = 0, .b = {0}}, { 0 } }, { { 0 }, {.a = 0, .b = {0}}} }; // Struct with fields of unknown type struct_with_unknown_fields ufa = { 1, { 1, 2 }, { 1, 2 } }; struct_with_unknown_fields ufb = { 1, 1, 2 }; // 9.2 struct_with_unknown_fields[2] ufc = { {1, { 1, 2 }, { 1, 2 } }, { 2, { 1, 2 }, { 1, 2 } } }; struct_with_unknown_fields[2][2] ufd = { {1, { 1, 2 }, { 1, 2 } }, { 2, { 1, 2 }, { 1, 2 } } }; struct_with_unknown_fields[2] ufe = { 1, { 1, 2 }, { 1, 2 }, // TODO: 9.2 2, { 1, 2 }, { 1, 2 } }; struct_with_unknown_fields[3] uff = { { 1, { 1, 2 }, { 1, 2 }}, // TODO: 9.3 9.4 {2, { 1, 2 }, { 1, 2 }}, [1] = { 2, { 1, 2 }, { 1, 2 }} }; // Obsolete initialization syntax for GCC struct1 os1 = { i1: 1, i2: 2 }; // 10.4 13.4 } static void misra_9_broken_initializers(void) { char a[UNKNOWN_MACRO] = { 19, 23, 0 }; // 18.8 } static void misra_9_2(void) { union misra_9_2_union { // 19.2 char c; struct1 i; } u = { 3 }; // 19.2 } static void misra_9_5(void) { char a[] = { 1, 2, 3 }; char b[] = { [2] = 5 }; // 9.5 char c[] = { 1, [1] = 5 }; // 9.5 char d[] = { [1] = 2, [0] = 1 }; // 9.5 char e[][2] = { { 1, 2 }, { 3, 4 } }; char f[][2] = { [1] = { 3, 4 } }; // 9.5 char g[][2] = { { 1, 2 }, [1] = { 3, 4 } }; // 9.5 char h[][2] = { [1] = { 1, 2 }, [0] = { 3, 4 } }; // 9.5 } typedef char misra_10_1_char_t; #define MISRA_10_1_CHAR char static void misra_10_1(uint32_t u, char c1, char c2, uint8_t u8) { int32_t i; char c; enum { E1 = 1 }; i = 3 << 1; // 10.1 10.6 i = (u & u) << 4; // no-warning c = c1 & c2; // 10.1 c = c1 << 1; // 10.1 i = c1 > c2; // 10.3 i = E1 + i; // no-warning char ch1 = 'a'; char ch2 = 'b'; char ch3; ch3 = ch1 & ch2; // 10.1 misra_10_1_char_t ct1 = 'a'; misra_10_1_char_t ct2 = 'b'; misra_10_1_char_t ct3; ct3 = ct1 & ct2; // 10.1 MISRA_10_1_CHAR cd1 = 'a'; MISRA_10_1_CHAR cd2 = 'b'; MISRA_10_1_CHAR cd3; cd3 = cd1 & cd2; // 10.1 uint8_t temp1 = u8 & 0x42U; // no-warning } static void misra_10_1_ternary(void) { int a; uint8_t ui8; uint16_t ui16; int8_t i8; int16_t i16; a = ui16 << ui16; // 10.6 a = ui16 << (get_bool(42) ? ui16 : ui16); a = ui16 << (get_bool(42) ? ui16 : (get_bool(34) ? ui16 : ui16)); // 10.4 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui16) : ui16); // 10.4 a = ui16 << (get_bool(42) ? i16 : (get_bool(34) ? ui16 : ui16)); // 10.1 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : i16) : ui16); // 10.1 10.4 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui16) : i16); // 10.1 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8); // 10.4 a = ui16 << (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8); // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << ui16; // 10.4 a = (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8) << ui16; // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : i8) : ui8) << ui16; // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : i8) << ui16; // 10.1 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << (get_bool(19) ? ui16 : ui8); // 10.4 a = (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8) << (get_bool(19) ? ui16 : ui8); // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << (get_bool(19) ? i16 : ui8); // 10.1 10.4 } static void misra_10_2(void) { uint8_t u8a = 0; char cha = 0; int8_t s8a = 0; int16_t s16a = 0; float f32a = 0.0; char res; res = '0' + u8a; // Convert u8a to digit res = s8a + '0'; res = cha - '0'; res = '0' - s8a; res = cha + ':'; // 10.2 res = s16a - 'a'; // 10.2 10.3 10.4 res = '0' + f32a; // 10.2 10.4 // 10481 - crash char buf[1] = {'f'}; x = buf[0] - '0'; } static void misra_10_3(uint32_t u32a, uint32_t u32b) { uint8_t res; res = u32a + u32b; // 10.3 res = (uint16_t)(2U + 3U); // 10.3 10.8 res = 2U + 3U; // no warning, utlr=unsigned char res = 0.1f; // 10.3 const char c = '0'; // no-warning } static void misra_10_4(u32 x, s32 y) { z = x + 3; // 10.4 enum misra_10_4_enuma { misra_10_4_A1, misra_10_4_A2, misra_10_4_A3 } a; enum misra_10_4_enumb { misra_10_4_B1, misra_10_4_B2, misra_10_4_B3 }; if ( misra_10_4_B1 > misra_10_4_A1 ) //10.4 { ; } z = x + y; //10.4 z = (a == misra_10_4_A3) ? x : y; //10.4 z = (a == misra_10_4_A3) ? y : y; // no-warning // #10499 const char buf[10] = {0}; if ('0' == buf[x]) // no-warning { } } static void misra_10_5(uint16_t x) { // bool res = (uint16_t) (x > 10u); // 10.5 res = (bool) 1u; // no-warning // char <=> float res = (char) 0.1f; res = (float) 'x'; } struct misra_10_6_s { unsigned int a:4; }; static void misra_10_6(u8 x, u32 a, u32 b, char c1, char c2) { u16 y = x+x; // 10.6 u16 z = ~u8 x ;//10.6 u32 c = ( u16) ( u32 a + u32 b ); //10.6 s32 i = c1 - c2; // 10.3 FIXME: False positive for 10.6 (this is compliant). Trac #9488 struct misra_10_6_s s; s.a = x & 1U; // no-warning (#10487) } static void misra_10_6_1(uint32_t *a, uint16_t b, uint16_t c) { *a = b + c ; // 10.6 } static void misra_10_7(uint16_t u16a, uint16_t u16b) { uint32_t u32a = 100u; res = u32a * u16a + u16b; // 12.1 no-warning res = (u32a * u16a) + u16b; // no-warning res = u32a * ( ( uint32_t ) u16a + u16b ); // no-warning res = u32a * (u16a + u16b); // 10.7 u32a *= u16a + u16b; // 10.7 u32a = ((uint32_t)4 * (uint32_t)2 * (uint32_t)4 ); // no-warning (#10488) } static void misra_10_8(u8 x, s32 a, s32 b) { y = (u16)x; y = (u16)(x+x); // 10.8 y = (u16) (a + b) //10.8 } int (*misra_11_1_p)(void); // 8.4 void *misra_11_1_bad1 = (void*)misra_11_1_p; // 11.1 8.4 struct misra_11_2_s; struct misra_11_2_t; static struct misra_11_2_s * sp; static struct misra_11_2_t * tp = sp; // 11.2 struct Fred {}; struct Wilma {}; static void misra_11_3(u8* p, struct Fred *fred) { x = (u64*)p; // 11.3 struct Wilma *wilma = (struct Wilma *)fred; // 11.3 } static void misra_11_4(u8*p) { u64 y = (u64)p; // 11.4 u8 *misra_11_4_A = ( u8 * ) 0x0005;// 11.4 s32 misra_11_4_B; u8 *q = ( u8 * ) misra_11_4_B; // 11.4 } static void misra_11_5(void *p) { u16 *p16; x = (u8 *)p; // 11.5 p16 = p; // 11.5 } static void misra_11_6(void) { void *p; p = (void*)123; // 11.6 x = (u64)p; // 11.6 p = ( void * )0; // no-warning (void)p; // no-warning } static void misra_11_7(int *p, float f) { x = ( float ) p; //11.7 y = ( int * ) f; //11.7 } static void misra_11_7_extra(int *p, float f, bool b) { (void) p; // no-warning (void) f; // no-warning (void) b; // no-warning } static void misra_11_8_const(const char *str) {(void)str;} static char * misra_11_8(const char *str) { (void)misra_11_8_const(str); // no-warning return (char *)str; // 11.8 } #define MISRA_11_9_NULL_1 (1-1) #define MISRA_11_9_NULL_2 ( void * ) 0 #define MISRA_11_9_NULL_3 NULL static void misra_11_9(void) { int *p1 = (5-5); //11.9 int *p2 = MISRA_11_9_NULL_2 ; // no-warning int *p3 = MISRA_11_9_NULL_3 ; // no-warning if ( p1 == MISRA_11_9_NULL_1 ) //11.9 { ; } } static void misra_12_1(void) { sz = sizeof x + y; // 12.1 a = (b * c) + d; a = b << c + d; // 12.1 } static void misra_12_2(u8 x) { a = x << 8; // 12.2 } static int misra_12_3_v1 = 0, misra_12_3_v2; // 12.3 static int misra_12_3_v3, misra_12_3_v4; // 12.3 enum misra_12_3_e1 { M123A1, M123B1, M123C1 }; enum misra_12_3_e2 { M123A2 = 3, M123B2 = 4, M123C2 }; typedef enum misra_12_3_e3 { M123A3 , M123B3, M123C3 } misra_12_3_e3_t; typedef enum { M123A4 , M123B4, M123C4 } misra_12_3_e4_t; struct misra_12_3_s1 { int a; int b; int c, d; }; // 12.3 static struct misra_12_3_s1 misra_12_3_s1_inst = { 3, 4, 5, 6, // no warning }; typedef struct misra_12_3_s2 { int a; int b; int c, d; } misra_12_3_s2_t; // 12.3 typedef struct { int a; int b; int c, d; } misra_12_3_s3_t; // 12.3 static void misra_12_3_fn1(int, int); static int misra_12_3_v5, misra_12_4_v6; // 12.3 8.2 static void misra_12_3_fn2(int a, int b) // 2.7 { int d, e; } // 12.3 static int misra_12_3_fn3(int a, int b) { return a+b;} static int misra_12_3_v5, misra_12_4_v6; // 12.3 static void misra_12_3_fn4(const uint32_t value, uint8_t * const y) {} // 2.7 static void misra_12_3_fn5(const uint32_t * const, const uint8_t) {} // 2.7 8.2 extern void misra_12_3_fn6(const uint32_t value, uint8_t * const y); extern uint32_t misra_12_3_fn7(const uint32_t * const, const uint8_t); // 8.2 #define MISRA_12_3_FN3_1(A, B) (misra_12_3_fn3(A, B)) #define MISRA_12_3_FN3_2(A, B) (misra_12_3_fn3(A, \ B)) #define MISRA_12_3_FN3_2_MSG(x) x, fflush(stderr) static void misra_12_3(int, int, int); // 8.2 void misra_12_3(int a, int b, int c) { int a1, a2; // 12.3 int a3; int a4; // no warning int a5 = 9, a6; // 12.3 int a7, a8 = 11; // 12.3 int a9 = foo(), a10; // 12.3 int a11 = a = b = c; // 17.8 struct s1 {int a, b;}; int a12, a13; // 12.3 int a14, a15; misra_12_3_fn3(a14, a15); // 12.3 17.7 ; int a16, a17; // 12.3 int a18; int a19, a20; // 12.3 int a21, a22; int a23; // 12.3 int a24, // 12.3 a25; int a26 , a27; // 12.3 int a28 , // 12.3 a29; struct misra_12_3_s2 a30 = {1, 2}, a31; // 12.3 struct misra_12_3_s2 a32, a33; // 12.3 struct misra_12_3_s2 a34, a35 = {1, 2}, a36; // 12.3 // cppcheck-suppress uninitStructMember int a37 = MISRA_12_3_FN3_1(a34, a35), a38; // 12.3 int a39, a40 = MISRA_12_3_FN3_1(a34, a35); // 12.3 int a41 = MISRA_12_3_FN3_2(a34, a35), a42; // 12.3 int a43, a44 = MISRA_12_3_FN3_2(a34, a35); // 12.3 MISRA_12_3_FN3_2_MSG(fprintf(stderr, "test\n")); // 12.3 f((1,2),3); // TODO for (i=0; i<10; i++, j++){} // 12.3 for (int i = 0, p = &a1; // 12.3 14.2 i < 42; ++i, ++p ) // 12.3 {} // No false positives in local and extern function calls misra_12_3_fn4(misra_12_3_fn5(&a1, 32), &a1); misra_12_3_fn4(misra_12_3_fn7(&a1, 32), &a1); misra_12_3_fn6(misra_12_3_fn5(&a1, 32), &a1); misra_12_3_fn6(misra_12_3_fn7(&a1, 32), &a1); misra_12_3_fn7(maxlen, fn(va, unsigned long), false); misra_12_3_fn8(maxlen, (unsigned long)((uintptr_t)fn(va, void*)), false); const struct fun_t { int64_t x; uint32_t y; } moreFun[2U] = { { 900000000000000LL, 0x20000UL }, { 450000000000000LL, 0x10000UL } }; } #define MISRA12_4a 2000000000u #define MISRA12_4b 4000000000u static void misra_12_4(uint8_t t) { x = 123456u * 123456u; // 12.4 x = MISRA12_4a + MISRA12_4b; // 12.4 x = 0u - 1u; // 12.4 x = t ? 0u : (0u-1u); // 12.4 } struct misra_13_1_t { int a; int b; }; uint8_t misra_13_1_x = 0; // 8.4 static void misra_13_1_bar(uint8_t a[2]); static void misra_13_1(int *p) { volatile int v; int a1[3] = {0, (*p)++, 2}; // 13.1 int a2[3] = {0, ((*p) += 1), 2}; // 13.1 int a3[3] = {0, ((*p) = 19), 2}; // 13.1 misra_13_1_bar((uint8_t[2]){ misra_13_1_x++, misra_13_1_x++ } ); // 13.1 int b[2] = {v,1}; struct misra_13_1_t c = { .a=4, .b=5 }; // no fp volatile int vv; int v = 42; int a1[3] = { 0, (*p)++, 2 }; // 13.1 int a2[2] = { [0]=19, [1]=42 }; int a3[2] = { [0]=v, [1]=42 }; int a4[2] = { [0]=0, [1]=(v+=1) }; // 13.1 int a5[2] = { [0]=0, [1]=(v+1) }; int a6[2] = { v, 1 }; int a6[2] = { v >>= 3 }; // 13.1 9.3 int a7[2] = { v, ++v }; // 13.1 int a8[1] = { vv }; // TODO: 13.1 Trac #9504 struct misra_13_1_t c01 = { 4, 5 }; struct misra_13_1_t c02 = { 16 == 1, 5+1 }; struct misra_13_1_t c03 = { (v += 1), 5+1 }; // 13.1 struct misra_13_1_t c04 = { v <<= 1, 5+1 }; // 13.1 struct misra_13_1_t c05 = { v += 1, 5+1 }; // 13.1 struct misra_13_1_t c06 = { (4.5 + 0.5), 1 }; struct misra_13_1_t c07 = { (4.5 + 0.5), ++v }; // 13.1 struct misra_13_1_t c08 = { (int)4.5, 5 }; struct misra_13_1_t c09 = { (int)4.5+(*p)++, 5 }; // 13.1 struct misra_13_1_t c10 = { (int)4.5, (*p)++ }; // 13.1 struct misra_13_1_t c11 = { .a=4+1, .b=3/3 }; struct misra_13_1_t c12 = { .a=4, .b=5 }; struct misra_13_1_t c13 = { (*v)<<=(int)(4.5), .b=5 }; // 13.1 struct misra_13_1_t c14 = { (*p)/=(int)(4.5) }; // 13.1 } // Large arrays for R13.1. Size exceeds default Python's max recursion depth. static uint8_t misra_13_1_large_ok[1024] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static uint8_t misra_13_1_large_bad[1024] = { // 13.1 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, i++, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static void misra_13_3(void) { x = y++; // 13.3 } #define STRING_DEF_13_4 "This is a string" typedef struct { char string[sizeof(STRING_DEF_13_4)]; } s13_4_t; static s13_4_t s13_4 = { .string = STRING_DEF_13_4 // no-warning }; static void misra_13_4(void) { if (x != (y = z)) {} // 13.4 else {} } static void misra_13_5(void) { if (x && (y++ < 123)){} // 13.5 if (x || ((y += 19) > 33)){} // 13.5 if (x || ((y = 25) > 33)){} // 13.5 13.4 if (x || ((--y) > 33)){} // 13.5 else {} } static void misra_13_6(void) { int a = sizeof(x|=42); // 13.6 a = sizeof(--x); // 13.6 13.3 return sizeof(x++); // 13.6 } static void misra_14_1(void) { for (float f=0.1f; f<1.0f; f += 0.1f){} // 14.1 float a = 0.0f; int b = 10; while ((a<100.0f) || (b > 100)) //14.1 { a++; } do { ; } while ( a < 10.0f ); // no-warning } static void misra_14_2_init_value(int32_t *var) { *var = 0; } static void misra_14_2_fn1(bool b) { for (;i++<10;) {} // 14.2 for (;i<10;dostuff()) {} // TODO int32_t g = 0; int g_arr[42]; g += 2; // no-warning for (int32_t i2 = 0; i2 < 8; ++i2) { i2 += 2; // 14.2 i2 |= 2; // 14.2 g += 2; i2 ^= 2; // 14.2 if (i2 == 2) { g += g_arr[i2]; } misra_14_2_init_value(&i2); // TODO: Fix false negative in function call } for (misra_14_2_init_value(&i); i < 10; ++i) {} // no-warning FIXME: False positive for 14.2 Trac #9491 bool abort = false; for (i = 0; (i < 10) && !abort; ++i) { // no-warning if (b) { abort = true; } } for (;;) {} // no-warning int x = 10; for (int i = x; i < 42; i++) { x++; // no warning } for (int i = (x - 3); i < 42; i++) { x ^= 3; // no warning } for (int i = 0, j = 19; i < 42; i++) { // 12.3 14.2 i += 12; // 14.2 j /= 3; // TODO: 14.2 } for (int i = 0; i < 19; i++) { for (int j = 0; j < 42; j++) { i--; // 14.2 for (int k = j; k > 5; k--) { i++; // 14.2 for (int h = 35; h > 5; k++) // 14.2 {} } } } } static void misra_14_2_fn2(void) { int y = 0; // Handle cases when i is not treated as loop counter according MISRA // definition. for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3 i++; // no warning } for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3 i++; // no warning } for (int i = 0; y < 10; y++) { // TODO: 14.2 i++; // no warning } for (int i = 0; i < 10; y++) { // TODO: 14.2 i++; // no warning } for (int i = 0; y < 10; i++) { // TODO: 14.2 i++; // no warning } for (int i = 0; i < 10; (y+=i)) { i++; // no warning } // i is a loop counter according MISRA definition for (int i = 0; i < 10; i++) { i++; // 14.2 if (++i > 5) { // 14.2 break; } } for (int i = 0; i < 10; (i+=42)) { i++; // 14.2 } for (int i = 0; i < 10; (i|=y)) { i++; // 14.2 } return 0; } struct { unsigned int x:1; unsigned int y:1; } r14_4_struct; // 8.4 static void misra_14_4(bool b) { if (x+4){} // 14.4 else {} if (b) {} else {} if (r14_4_struct.x) {} } static void misra_15_1(void) { goto a1; // 15.1 a1: } static void misra_15_2(void) { label: goto label; // 15.2 15.1 } static void misra_15_3(void) { if (x!=0) { goto L1; // 15.3 15.1 if (y!=0) { L1: } else {} } else {} switch (x) { case 0: if (x == y) { goto L2; // 15.3 15.1 } goto L2; // 15.3 15.1 L3: foo(); if (a == 0x42) { // Compliant: goto L3; // 15.1 15.2 } break; case 1: y = x; L2: ++x; break; default: break; } } static void misra_15_4(void) { misra_15_4_label: return; int x = 0; int y = 0; int z = 0; // Break on different loop scopes for (x = 0; x < 42; ++x) { if (x==1) { break; } for (y = 0; y < 42; ++y) { // 15.4 if (y==1) { break; } if (y==2) { break; } for (z = 0; y < 42; ++z) { if (z==1) { break; } } } } // Break in while loop do { // 15.4 if(x == 1) { break; } if(x == 2) { break } x++; } while(x != 42); // Break and goto in same loop for (int x = 0; x < 10; ++x) { // 15.4 if (x == 1) { break; } if (x == 2) { goto misra_15_4_label; // 15.1 15.2 } } // Inner loop uses goto for (x = 0; x < 42; ++x) { // 15.4 if (x==1) { break; } for (y = 0; y < 42; ++y) { if (y == 1) { goto misra_15_4_label; // 15.1 15.2 } } } // Allow switch with multiple breaks inside loop for (x = 0; x < 42; ++x) { switch (x) { case 1: break; default: break; } } // Do not allow switch with multiple gotos inside loop for (x = 0; x < 42; ++x) { // 15.4 switch (x) { case 1: goto misra_15_4_label; // 15.1 15.2 break; default: goto misra_15_4_label; // 15.1 15.2 break; } } } static int misra_15_5(void) { if (x!=0) { return 1; // 15.5 } else {} return 2; } static void misra_15_6(void) { if (x!=0); // 15.6 else{} #if A>1 // 20.9 (void)0; #endif #if A > 0x42 // 20.9 if (true) { (void)0; } if (true) #endif { (void)0; } // no-warning do {} while (x<0); // no-warning } static void misra_15_6_fp(void) { uint8_t value = 0U; do // Test { value++; } while (value < 2U); } #if defined(M_20_9) && M_20_9 > 1 // no-warning (#10380) #endif static void misra_15_7(void) { uint32_t var = 0; uint32_t var2 = 0; if (x!=0){} // no-warning if (x!=0){} else if(x==1){} // 15.7 if (x!=0){} else if(x==1){}else{;} // no-warning if (x!=0) { } else { var = 5u; if (var != 5u) { var2 = 10u; } // no-warning } if (a==2) {} else if (b==4) {} // 15.7 if (a==2) {} else { if (b==4) {} } // no-warning } static void misra_16_1(int32_t i) { switch (i) { int8_t x; // 16.1 default: // 16.3 16.5 break; if (i != 18) {} // 16.1 case 1: // 16.3 break; } } static void misra_16_2(void) { switch (x) { default: break; case 1: while (y>4) { case 2: break; // 16.2 } break; } } static void misra_16_3(void) { switch (x) { case 1: case 2: a=1; case 3: // 16.3 a=2; // fallthrough case 5: break; case 7: a=3; [[fallthrough]]; case 8: a=4; break; case 9: if (a==b) { break; } case 10: // 16.3 return; // 15.5 case 11: { break; } case 12: default: break; } switch (x) { case 1: // comment 1 { a = 1; break; } case 2: // comment 2 { a = 2; break; } default: { break; } } switch (x) { case 1: break; default: // 16.5 x++; case 19: // 16.3 break; case 20: x + 2; x + 3; break; } switch (x) { // 16.6 default:; } // 16.3 switch (x) { default:; } // 16.3 16.6 switch (x) { case 20: x + 2; x + 3; break; case 21: x + 2; x + 3; break; default: ; } // 16.3 switch (x) { // 16.4 16.6 case 1: x++; break; case 2: x++; } // 16.3 } static void misra_16_4(void) { switch (x) { // 16.4 case 1: break; case 2: break; } } static void misra_16_5(void) { switch (x) { case 1: break; default: // 16.5 break; case 2: break; } } static void misra_16_6(void) { switch (x) { // 16.6 default: break; } switch (x) { case 1: break; case 2: break; default: break; } // No 16 6 in this switch: switch (x) { case A: return 1; // 15.5 case B: return 1; // 15.5 case C: return 1; // 15.5 default: return 2; // 15.5 } } static void misra_16_7(void) { switch (x != 123) { // 16.7 case 1: break; default: break; } } static void misra_17_1(void) { va_list(); // 17.1 va_arg(); // 17.1 va_start(); // 17.1 va_end(); // 17.1 va_copy(); // 17.1 } static void misra_17_2_ok_1(void) { ; } static void misra_17_2_ok_2(void) { misra_17_2_ok_1(); // no-warning } static void misra_17_2_1(void) { misra_17_2_ok_1(); // no-warning misra_17_2_1(); // 17.2 misra_17_2_ok_2(); // no-warning misra_17_2_1(); // 17.2 } static void misra_17_2_2(void) { misra_17_2_3(); // 17.2 } static void misra_17_2_3(void) { misra_17_2_4(); // 17.2 } static void misra_17_2_4(void) { misra_17_2_2(); // 17.2 misra_17_2_3(); // 17.2 } static void misra_17_2_5(void) { misra_17_2_ok_1(); // no-warning misra_17_2_5(); // 17.2 misra_17_2_1(); // no-warning } static void misra_17_6(int x[static 20]) {(void)x;} // 17.6 static int calculation(int x) { return x + 1; } static void misra_17_7(void) { calculation(123); // 17.7 } static void misra_17_8(int x) { x = 3; // 17.8 } static void misra_18_4(void) { int b = 42; int *bp = &b; bp += 1; // 18.4 bp -= 2; // 18.4 int *p = bp - 2; // 18.4 int *ab = &b + 1; // 18.4 p = bp + p; // 18.4 bp = 1 + p + 1; // 18.4 b += 19; // no-warning b = b + 9; // no-warning } static void misra_18_5(void) { int *** p; // 18.5 } struct { uint16_t len; struct { uint8_t data_1[]; // 18.7 } nested_1; struct named { struct { uint8_t len_1; uint32_t data_2[]; // 18.7 } nested_2; uint8_t data_3[]; // 18.7 } nested_3; } r18_7_struct; // 8.4 struct { uint16_t len; uint8_t data_1[ 19 ]; uint8_t data_2[ ]; // 18.7 } r18_7_struct; // 8.4 typedef enum { R18_8_ENUM_CONSTANT_0, R18_8_ENUM_CONSTANT_1, } r18_8_enum; static void misra_18_8(int x) { int buf1[10]; int buf2[sizeof(int)]; int vla[x]; // 18.8 static const unsigned char arr18_8_1[] = UNDEFINED_ID; static uint32_t enum_test_0[R18_8_ENUM_CONSTANT_0] = {0}; } union misra_19_2 { }; // 19.2 #include "notfound.h" // 20.1 #define int short // 20.4 #define inline "foo" // no warning in C90 standard #undef X // 20.5 #define M_20_7_1(A) (A+1) // 20.7 #define M_20_7_2(A,B) (1+AB+2) // no warning #define M_20_7_3(A) ((A)+A) // 20.7 #define M_20_7_4(A) x##A // 20.10 this test was written to see there are not FPs #define M_20_7_5(A,B) f(A, B) // no warning #define M_20_7_6(x) a ## x = ( x ) // 20.10 #define M_20_7_7(x) a = # x // 20.10 #define M_20_7_8(x, fn) a = fn ( # x ) // 20.7 20.10 #define M_20_7_9(x, fn) a = (fn) ( # x ) // 20.10 #define M_20_7_10(A, B) (A " " B) #define M_20_7_11(A, B, C) (A " " B " " C) #define M_20_7_12(A, B, C) (A " " B + C) // 20.7 #define M_20_7_13(A, B, C) (A + B " " C) // 20.7 #define M_20_7_14(STRING1, STRING2) (STRING1 " " STRING2) #define M_20_7_15(STRING1, STRING2, STRING3) (STRING1 " " STRING2 " " STRING3) #define M_20_7_16(STRING1, STRING2, STRING3) (STRING1 " " STRING2 + STRING3) // 20.7 #define M_20_7_17(STRING1, STRING2, STRING3) (STRING1 + STRING2 " " STRING3) // 20.7 // Compliant: M is a structure member name, not an expression struct { int a; } struct_20_7_s; // 8.4 #define M_20_7_6(M) struct_20_7.M #define M_20_7_7(M) (struct_20_7).M #define MUL(a ,b ) ( a * b ) // 20.7 #if __LINE__ // 20.8 #elif 2+5 // 20.8 #elif 2-2 #endif #if A // 20.9 #elif B || C // 20.9 #endif #define M_20_10(a) (#a) // 20.10 #define M_20_11(a) # a ## 1 // 20.11 20.10 #define M_20_12_AA 0xffff #define M_20_12_BB(x) (x) + wow ## x // 20.12 20.10 misra_20_12 = M_20_12_BB(M_20_12_AA); #else1 // 20.13 #ifdef A # define somethingis 5 // no warning # define func_20_13(v) (v) // no warning #else # definesomethingis 6 // 20.13 # def fun_2013(v) () // 20.13 #endif #define _Incompatible 0xdeadbeef // 21.1 #define __Incompatible 0xdeadbeef // 21.1 #define __starts_with_lower 0xdeadbeef // 21.1 #define __MY_HEADER_ // 21.1 #define _macro_starts_with_lower 1 // no warning static int _file_scope_id_21_1 = 42; // no warning static int _file_scope_id_21_1_fn(void) { return 42; } // no warning static int misra_21_1(void) { int _a = 42; // no warning: only directives affected errno = EINVAL; // no warning _a ++; // no warning _exit(1); // no warning return _a; // no warning } static int _misra_21_1_2(void); // no warning #define errno 11 // 21.1 #undef errno // 20.5 #define __BUILTIN_SOMETHING 123 // 21.2 21.1 extern void *memcpy ( void *restrict s1, const void *restrict s2, size_t n ); // 21.2 8.14 static void misra_21_3(void) { p1=malloc(10); // 21.3 p2=calloc(10); // 21.3 realloc(10); // 21.3 free(p1); // 21.3 } static void misra_21_7(void) { (void)atof(str); // 21.7 (void)atoi(str); // 21.7 (void)atol(str); // 21.7 (void)atoll(str); // 21.7 } static void misra_21_8(void) { abort(); // 21.8 (void)getenv("foo"); // 21.8 exit(-1); // 21.8 } static void misra_21_9(void) { (void)bsearch(key,base,num,size,cmp); // 21.9 qsort(base,num,size,cmp); // 21.9 } static void misra_21_12(void) { int rc; fexcept_t f; // 21.12 rc = feclearexcept(1); // 21.12 rc = fegetexceptflag(&f, 1); // 21.12 rc = feraiseexcept(1); // 21.12 rc = fesetexceptflag(&f, 1); // 21.12 rc = fetestexcept(1); // 21.12 } static void misra_21_14(uint8_t *x) { (void)strcpy(x, "123"); (void)memcmp(x, y, 100); // 21.14 (void)memcmp("abc", y, 100); // 21.14 21.16 } static void misra_21_15(uint8_t *x, uint16_t *y) { (void)memcpy(x, y, 10); // 21.15 (void)memmove(x, y, 10); // 21.15 (void)memcmp(x, y, 10); // 21.15 } struct misra_21_16_S { int a; int b; }; static void misra_21_16_f1(struct misra_21_16_S *s1, struct misra_21_16_S *s2) { (void)memcmp(s1, s2, 10); // 21.16 } static void misra_21_16_f2(char *x, char *y) { (void)memcmp(x, y, 10); // 21.16 } static void misra_21_19(void) { char *s = setlocale(LC_ALL,0); // 21.19 const struct lconv *conv = localeconv (); conv->decimal_point = "^"; // 21.19 } static void misra_21_20(void) { const char *res1 = setlocale ( LC_ALL, 0 ); (void) setlocale ( LC_MONETARY, "French" ); if (res1) {} // 21.20 14.4 } static void misra_21_21(void) { (void)system("ls"); // 21.21 } static void misra_22_5(FILE *f) { int x = *f; // 22.5 int y = f->pos; // 22.5 } static void misra_22_7(char ch) { if (EOF == ch) {} // 22.7 } static void misra_22_8(void) { (void)strtoll("123", NULL, 10); // 22.8 if (errno == 0) {} } static void misra_22_9(void) { errno = 0; (void)strtoll("123", NULL, 10); // 22.9 } static void misra_22_10(void) { errno = 0; f = atof ( "A.12" ); // 21.7 if ( 0 == errno ) {} // 22.10 errno = 0; f = strtod ( "A.12", NULL ); if ( 0 == errno ) {} } cppcheck-2.7/addons/test/misra/misra-test.cpp000066400000000000000000000005301417746362400213140ustar00rootroot00000000000000// #8441 class C { int a; int b; C(void) : a(1), b(1) { c; } }; class misra_21_1_C { public: misra_21_1_C operator=(const misra_21_1_C &); // 8.2 }; class C2 { public: C2(void); private: void* f; }; C2::C2(void) : f(NULL) {} static void test_misra_21_1_crash(void) { auto misra_21_1_C a, b; // 12.3 a = b; } cppcheck-2.7/addons/test/misra/misra-test.h000066400000000000000000000002601417746362400207610ustar00rootroot00000000000000#ifndef MISRA_TEST_H #define MISRA_TEST_H struct misra_h_s { int foo; }; bool test(char *a); // OK int misra_8_2_no_fp(int a); void misra_8_4_bar(void); #endif // MISRA_TEST_H cppcheck-2.7/addons/test/misra/misra2012_rules_dummy_ascii.txt000066400000000000000000000001251417746362400244760ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Text of rule 1.1 Rule 1.2 Text of rule 1.2 cppcheck-2.7/addons/test/misra/misra2012_rules_dummy_utf8.txt000066400000000000000000000001451417746362400242760ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Text of rule 1.1, utf8 test: ∑ Rule 1.2 Text of rule 1.2 cppcheck-2.7/addons/test/misra/misra2012_rules_dummy_windows1250.txt000066400000000000000000000001521417746362400254100ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Text of rule 1.1, windows1250 test: Rule 1.2 Text of rule 1.2 cppcheck-2.7/addons/test/misra/misra_rules_dummy.txt000066400000000000000000000005141417746362400230230ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 3.1 Required R3.1 text. Rule 4.1 Required R4.1 text. Rule 10.4 Mandatory R10.4 text. Rule 11.5 Advisory R11.5 text. Rule 15.5 Advisory R15.5 text. Rule 15.6 Required R15.6 text. Rule 17.7 Required R17.7 text. Rule 20.1 Advisory R20.1 text. Rule 21.3 Required R21.3 text. Rule 21.4 R21.4 text. cppcheck-2.7/addons/test/misra/misra_rules_empty_lines.txt000066400000000000000000000003231417746362400242160ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Add this rule and parse to next, skipping empty lines. Rule 1.2 Rule text. Rule 1.3 There is 3 rules. cppcheck-2.7/addons/test/misra/misra_rules_multiple_lines.txt000066400000000000000000000004061417746362400247150ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Multiple lines text. Rule 1.2 Multiple lines text. Rule 1.3 Required Multiple lines text. Rule 1.4 Should Starts from lowercase letter. Rule 1.5 Should starts from lowercase letter. Rule 1.6 Can contain empty lines. cppcheck-2.7/addons/test/misra/misra_rules_structure.txt000066400000000000000000000003341417746362400237300ustar00rootroot00000000000000Here can be any text. Incorrect definitions: Appendix A Appendix A Summary: Rule 1.1 Error! Here we go: Appendix A Summary of guidelines Rule 1.2 Rule text. Stop parsing after this line: Appendix B Rule 1.3 Error! cppcheck-2.7/addons/test/misra/suppressions.txt000066400000000000000000000005421417746362400220410ustar00rootroot00000000000000misra-c2012-21.6:*/misra-suppressions1-test.c:7 misra-c2012-14.4 misra-c2012-5.2 misra-c2012-8.4:*/misra-suppressions1-test.c misra-c2012-16.4:*/misra-suppressions1-test.c misra-c2012-16.6:*/misra-suppressions1-test.c misra-c2012-4.1:*/misra-suppressions2-test.c misra-c2012-8.4:*/misra-suppressions2-test.c misra-c2012-19.2:*/misra-suppressions2-test.c cppcheck-2.7/addons/test/naming_test.c000066400000000000000000000003231417746362400200610ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump naming_test.c && python ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump // Should not crash when there is no name void func(int number, int); cppcheck-2.7/addons/test/naming_test.cpp000066400000000000000000000004701417746362400204240ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump naming_test.cpp && python ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump // No error for mismatching Constructor/Destructor names should be issued, they can not be changed. class TestClass1 { TestClass1() {} ~TestClass1() {} }; cppcheck-2.7/addons/test/namingng_test.c000066400000000000000000000012001417746362400204010ustar00rootroot00000000000000#include #include uint32_t ui32Good (int abc) { uint32_t ui32good; int32_t i32good; uint32_t badui32; int32_t badi32; uint32_t a; // Short return 5; } uint16_t ui16Good (int a) { return 5; } uint16_t ui16bad_underscore (int a) { return 5; } uint32_t u32Bad (int a) { uint32_t ui32good; int32_t i32good; uint32_t badui32; int32_t badi32; int * intpointer=NULL; int ** intppointer=NULL; int *** intpppointer=NULL; return 5; } uint16_t Badui16 (int a) { return 5; } void * Pointer() { return NULL; } void ** PPointer() { return NULL; } cppcheck-2.7/addons/test/path1/000077500000000000000000000000001417746362400164245ustar00rootroot00000000000000cppcheck-2.7/addons/test/path1/misra-suppressions1-test.c000066400000000000000000000016351417746362400235210ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported // This needs to stay at line number 7 to make the test pass // If it is changed update suppressions.txt with the new line number #include //21.6 extern int misra_5_2_var_hides_var______31x; static int misra_5_2_var_hides_var______31y;//5.2 static int misra_5_2_function_hides_var_31x; void misra_5_2_function_hides_var_31y(void) {}//5.2 void foo(void) { int i; switch(misra_5_2_func1()) //16.4 16.6 { case 1: { do { for(i = 0; i < 10; i++) { if(misra_5_2_func3()) //14.4 { int misra_5_2_var_hides_var_1____31x; int misra_5_2_var_hides_var_1____31y;//5.2 } } } while(misra_5_2_func2()); //14.4 } } } cppcheck-2.7/addons/test/path1/misra-suppressions2-test.c000066400000000000000000000010221417746362400235100ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported union misra_5_2_field_hides_field__63x { //19.2 int misra_5_2_field_hides_field__31x; int misra_5_2_field_hides_field__31y;//5.2 }; struct misra_5_2_field_hides_field__63y { //5.2 int misra_5_2_field_hides_field1_31x; int misra_5_2_field_hides_field1_31y;//5.2 }; const char *s41_1 = "\x41g"; // 4.1 const char *s41_2 = "\x41\x42"; cppcheck-2.7/addons/test/test-cert.py000066400000000000000000000020561417746362400176760ustar00rootroot00000000000000# Running the test with Python 2: # Be sure to install pytest version 4.6.4 (newer should also work) # Command in cppcheck directory: # python -m pytest addons/test/test-cert.py # # Running the test with Python 3: # Command in cppcheck directory: # PYTHONPATH=./addons python3 -m pytest addons/test/test-cert.py import sys import pytest def test_arguments_regression(): args_ok = ["-q", "--quiet", "-verify", "--cli"] # Arguments with expected SystemExit args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] from addons.cert import get_args_parser for arg in args_exit: sys.argv.append(arg) with pytest.raises(SystemExit): parser = get_args_parser() parser.parse_args() sys.argv.remove(arg) for arg in args_ok: sys.argv.append(arg) try: parser = get_args_parser() parser.parse_args() except SystemExit: pytest.fail("Unexpected SystemExit with '%s'" % arg) sys.argv.remove(arg) cppcheck-2.7/addons/test/test-misra.py000066400000000000000000000126501417746362400200550ustar00rootroot00000000000000# Running the test with Python 2: # Be sure to install pytest version 4.6.4 (newer should also work) # Command in cppcheck directory: # python -m pytest addons/test/test-misra.py # # Running the test with Python 3: # Command in cppcheck directory: # PYTHONPATH=./addons python3 -m pytest addons/test/test-misra.py import pytest import re import sys from .util import dump_create, dump_remove, convert_json_output TEST_SOURCE_FILES = ['./addons/test/misra/misra-test.c'] def setup_module(module): for f in TEST_SOURCE_FILES: dump_create(f) def teardown_module(module): for f in TEST_SOURCE_FILES: dump_remove(f) @pytest.fixture(scope="function") def checker(): from addons.misra import MisraChecker, MisraSettings, get_args_parser parser = get_args_parser() args = parser.parse_args([]) settings = MisraSettings(args) return MisraChecker(settings) def test_loadRuleTexts_structure(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_structure.txt") assert(checker.ruleTexts.get(101, None) is None) assert(checker.ruleTexts[102].text == "Rule text.") assert(checker.ruleTexts.get(103, None) is None) def test_loadRuleTexts_empty_lines(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_empty_lines.txt") assert(len(checker.ruleTexts) == 3) assert(len(checker.ruleTexts[102].text) == len("Rule text.")) def test_loadRuleTexts_mutiple_lines(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_multiple_lines.txt") assert(checker.ruleTexts[101].text == "Multiple lines text.") assert(checker.ruleTexts[102].text == "Multiple lines text.") assert(checker.ruleTexts[103].text == "Multiple lines text.") assert(checker.ruleTexts[104].text == "Should") assert(checker.ruleTexts[105].text == "Should") assert(checker.ruleTexts[106].text == "Can contain empty lines.") def test_verifyRuleTexts(checker, capsys): checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") checker.verifyRuleTexts() captured = capsys.readouterr().out assert("21.3" not in captured) assert("1.3" in captured) def test_rules_misra_severity(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") assert(checker.ruleTexts[1004].misra_severity == 'Mandatory') assert(checker.ruleTexts[401].misra_severity == 'Required') assert(checker.ruleTexts[1505].misra_severity == 'Advisory') assert(checker.ruleTexts[2104].misra_severity == '') def test_json_out(checker, capsys): sys.argv.append("--cli") checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") checker.parseDump("./addons/test/misra/misra-test.c.dump") captured = capsys.readouterr() captured = captured.out.splitlines() sys.argv.remove("--cli") json_output = convert_json_output(captured) assert("Mandatory" in json_output['c2012-10.4'][0]['extra']) assert("Required" in json_output['c2012-21.3'][0]['extra']) assert("Advisory" in json_output['c2012-20.1'][0]['extra']) def test_rules_cppcheck_severity(checker, capsys): checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") checker.parseDump("./addons/test/misra/misra-test.c.dump") captured = capsys.readouterr().err assert("(error)" not in captured) assert("(warning)" not in captured) assert("(style)" in captured) def test_rules_cppcheck_severity_custom(checker, capsys): checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") checker.setSeverity("custom-severity") checker.parseDump("./addons/test/misra/misra-test.c.dump") captured = capsys.readouterr().err assert("(error)" not in captured) assert("(warning)" not in captured) assert("(style)" not in captured) assert("(custom-severity)" in captured) def test_rules_suppression(checker, capsys): test_sources = ["addons/test/misra/misra-suppressions1-test.c", "addons/test/misra/misra-suppressions2-test.c"] for src in test_sources: re_suppressed= r"\[%s\:[0-9]+\]" % src dump_remove(src) dump_create(src, "--suppressions-list=addons/test/misra/suppressions.txt") checker.parseDump(src + ".dump") captured = capsys.readouterr().err found = re.search(re_suppressed, captured) assert found is None, 'Unexptected output:\n' + captured dump_remove(src) def test_arguments_regression(): args_ok = ["-generate-table", "--rule-texts=./addons/test/assets/misra_rules_multiple_lines.txt", "--verify-rule-texts", "-t=foo", "--template=foo", "--suppress-rules=15.1", "--quiet", "--cli", "--no-summary", "--show-suppressed-rules", "-P=src/", "--file-prefix=src/", "--severity=misra-warning"] # Arguments with expected SystemExit args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] from addons.misra import get_args_parser for arg in args_exit: sys.argv.append(arg) with pytest.raises(SystemExit): parser = get_args_parser() parser.parse_args() sys.argv.remove(arg) for arg in args_ok: sys.argv.append(arg) try: parser = get_args_parser() parser.parse_args() except SystemExit: pytest.fail("Unexpected SystemExit with '%s'" % arg) sys.argv.remove(arg) cppcheck-2.7/addons/test/test-y2038.py000066400000000000000000000112311417746362400175210ustar00rootroot00000000000000# Running the test with Python 2: # Be sure to install pytest version 4.6.4 (newer should also work) # Command in cppcheck directory: # python -m pytest addons/test/test-y2038.py # # Running the test with Python 3: # Command in cppcheck directory: # PYTHONPATH=./addons python3 -m pytest addons/test/test-y2038.py import sys import pytest from addons.y2038 import check_y2038_safe from .util import dump_create, dump_remove, convert_json_output TEST_SOURCE_FILES = ['./addons/test/y2038/y2038-test-1-bad-time-bits.c', './addons/test/y2038/y2038-test-2-no-time-bits.c', './addons/test/y2038/y2038-test-3-no-use-time-bits.c', './addons/test/y2038/y2038-test-4-good.c', './addons/test/y2038/y2038-test-5-good-no-time-used.c'] def setup_module(module): sys.argv.append("--cli") for f in TEST_SOURCE_FILES: dump_create(f) def teardown_module(module): sys.argv.remove("--cli") for f in TEST_SOURCE_FILES: dump_remove(f) def test_1_bad_time_bits(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-1-bad-time-bits.c.dump', quiet=True) assert(is_safe is False) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # Has exactly one warnings of _TIME_BITS and _USE_TIME_BITS64 kind. assert(len(json_output['type-bits-undef']) == 1) assert(len(json_output['type-bits-not-64']) == 1) # There are 2 unsafe calls in test source and 3 in y2038-in.h unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('h')]) == 3) assert(len([c for c in unsafe_calls if c['file'].endswith('c')]) == 0) def test_2_no_time_bits(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-2-no-time-bits.c.dump', quiet=True) assert(is_safe is False) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # _USE_TIME_BITS64 defined in y2038-inc.h header, but there is not # _TIME_BITS definition. Here must be appropriate warning. assert(len(json_output['type-bits-undef']) == 1) assert(json_output.get('type-bits-not-64') is None) # y2038-in.h still has y2038-unsafe calls. unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('h')]) == 3) def test_3_no_use_time_bits(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-3-no-use-time-bits.c.dump', quiet=True) assert(is_safe is False) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # Included bad _USE_TIME_BITS64 definition must trigger the errors. unsafe_calls = json_output['unsafe-call'] assert(len(unsafe_calls) == 2) def test_4_good(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-4-good.c.dump', quiet=True) # assert(is_safe is True) # FIXME: This should be a "good" example returning "True" instead of "False" captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # Defined _TIME_BITS equal to 64 so that glibc knows we want Y2038 support. # There are no warnings from C sources. unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('.c')]) == 0) def test_5_good(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-5-good-no-time-used.c.dump', quiet=True) assert(is_safe is True) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # There are no warnings from C sources. if 'unsafe-call' in json_output: unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('.c')]) == 0) def test_arguments_regression(): args_ok = ["-t=foo", "--template=foo", "-q", "--quiet", "--cli"] # Arguments with expected SystemExit args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] from addons.y2038 import get_args_parser for arg in args_exit: sys.argv.append(arg) with pytest.raises(SystemExit): parser = get_args_parser() parser.parse_args() sys.argv.remove(arg) for arg in args_ok: sys.argv.append(arg) try: parser = get_args_parser() parser.parse_args() except SystemExit: pytest.fail("Unexpected SystemExit with '%s'" % arg) sys.argv.remove(arg) cppcheck-2.7/addons/test/threadsafety/000077500000000000000000000000001417746362400200725ustar00rootroot00000000000000cppcheck-2.7/addons/test/threadsafety/local_static.cpp000066400000000000000000000001001417746362400232260ustar00rootroot00000000000000struct Dummy { int x; }; void func() { static Dummy dummy; } cppcheck-2.7/addons/test/threadsafety/local_static_const.cpp000066400000000000000000000001061417746362400244420ustar00rootroot00000000000000struct Dummy { int x; }; void func() { static const Dummy dummy; } cppcheck-2.7/addons/test/util.py000066400000000000000000000023421417746362400167370ustar00rootroot00000000000000# Helpers for pytest tests import subprocess import json import os def find_cppcheck_binary(): possible_locations = [ "./cppcheck", "./build/bin/cppcheck", r".\bin\cppcheck.exe", ] for location in possible_locations: if os.path.exists(location): break else: raise RuntimeError("Could not find cppcheck binary") return location def dump_create(fpath, *argv): cppcheck_binary = find_cppcheck_binary() cmd = [cppcheck_binary, "--dump", "-DDUMMY", "--quiet", fpath] + list(argv) p = subprocess.Popen(cmd) p.communicate() if p.returncode != 0: raise OSError("cppcheck returns error code: %d" % p.returncode) subprocess.Popen(["sync"]) def dump_remove(fpath): subprocess.Popen(["rm", "-f", fpath + ".dump"]) def convert_json_output(raw_json_strings): """Convert raw stdout/stderr cppcheck JSON output to python dict.""" json_output = {} for line in raw_json_strings: try: json_line = json.loads(line) # json_output[json_line['errorId']] = json_line json_output.setdefault(json_line['errorId'], []).append(json_line) except ValueError: pass return json_output cppcheck-2.7/addons/test/y2038/000077500000000000000000000000001417746362400161745ustar00rootroot00000000000000cppcheck-2.7/addons/test/y2038/y2038-inc.h000066400000000000000000000007771417746362400177140ustar00rootroot00000000000000#ifndef __INC2038 #define _INC2038 /* * This file defines _USE_TIME_BITS64. * It plays the role of a Y2038-proof glibc. */ #define _USE_TIME_BITS64 /* * Declare just enough for clock_gettime */ typedef int clockid_t; typedef int __time_t; typedef long int __syscall_slong_t; struct timespec { __time_t tv_sec; /* Seconds. */ __syscall_slong_t tv_nsec; /* Nanoseconds. */ }; extern int clock_gettime(clockid_t clk_id, struct timespec *tp); #define CLOCK_REALTIME 0 #endif /* INC2038 */ cppcheck-2.7/addons/test/y2038/y2038-test-1-bad-time-bits.c000066400000000000000000000004521417746362400226600ustar00rootroot00000000000000#include #include /* * Define _TIME_BITS unequal to 64 to trigger error */ #define _TIME_BITS 62 #include "y2038-inc.h" int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-2.7/addons/test/y2038/y2038-test-2-no-time-bits.c000066400000000000000000000004351417746362400225500ustar00rootroot00000000000000#include #include /* * Do not define _TIME_BITS but have _USE_TIME_BITS64 defined */ #include "y2038-inc.h" int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-2.7/addons/test/y2038/y2038-test-3-no-use-time-bits.c000066400000000000000000000004321417746362400233400ustar00rootroot00000000000000#include #include /* * Include bad _USE_TIME_BITS64 definition to trigger error */ #define _TIME_BITS 64 int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-2.7/addons/test/y2038/y2038-test-4-good.c000066400000000000000000000004331417746362400211710ustar00rootroot00000000000000/* * Define _TIME_BITS equal to 64 so that glibc knows we want Y2038 support. */ #define _TIME_BITS 64 #include "y2038-inc.h" int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-2.7/addons/test/y2038/y2038-test-5-good-no-time-used.c000066400000000000000000000003301417746362400234720ustar00rootroot00000000000000/* * C file that does not use any time functionality -> no errors should * be reported. */ #include int main(int argc, char **argv) { if (argc > 1) { printf("Hello"); } return 0; } cppcheck-2.7/addons/threadsafety.py000077500000000000000000000023521417746362400174720ustar00rootroot00000000000000#!/usr/bin/env python3 # # This script analyses Cppcheck dump files to locate threadsafety issues # - warn about static local objects # import cppcheckdata import sys def reportError(token, severity, msg, id): cppcheckdata.reportError(token, severity, msg, 'threadsafety', id) def checkstatic(data): for var in data.variables: if var.isStatic and var.isLocal: type = None if var.isClass: type = 'object' else: type = 'variable' if var.isConst: if data.standards.cpp == 'c++03': reportError(var.typeStartToken, 'warning', 'Local constant static ' + type + ' \'' + var.nameToken.str + '\', dangerous if it is initialized in parallel threads', 'threadsafety-const') else: reportError(var.typeStartToken, 'warning', 'Local static ' + type + ': ' + var.nameToken.str, 'threadsafety') for arg in sys.argv[1:]: if arg.startswith('-'): continue print('Checking %s...' % arg) data = cppcheckdata.CppcheckData(arg) for cfg in data.iterconfigurations(): print('Checking %s, config %s...' % (arg, cfg.name)) checkstatic(cfg) sys.exit(cppcheckdata.EXIT_CODE) cppcheck-2.7/addons/y2038.py000077500000000000000000000157461417746362400156070ustar00rootroot00000000000000#!/usr/bin/env python3 # # cppcheck addon for Y2038 safeness detection # # Detects: # # 1. _TIME_BITS being defined to something else than 64 bits # 2. _USE_TIME_BITS64 being defined when _TIME_BITS is not # 3. Any Y2038-unsafe symbol when _USE_TIME_BITS64 is not defined. # # Example usage: # $ cppcheck --dump path-to-src/test.c # $ y2038.py path-to-src/test.c.dump # # y2038.py will walk the source tree for .dump files. from __future__ import print_function import cppcheckdata import sys import os import re # -------------------------------------------- # #define/#undef detection regular expressions # -------------------------------------------- # test for '#define _TIME_BITS 64' re_define_time_bits_64 = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+64\s*$') # test for '#define _TIME_BITS ...' (combine w/ above to test for 'not 64') re_define_time_bits = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+.*$') # test for '#undef _TIME_BITS' (if it ever happens) re_undef_time_bits = re.compile(r'^\s*#\s*undef\s+_TIME_BITS\s*$') # test for '#define _USE_TIME_BITS64' re_define_use_time_bits64 = re.compile(r'^\s*#\s*define\s+_USE_TIME_BITS64\s*$') # test for '#undef _USE_TIME_BITS64' (if it ever happens) re_undef_use_time_bits64 = re.compile(r'^\s*#\s*undef\s+_USE_TIME_BITS64\s*$') # -------------------------------- # List of Y2038-unsafe identifiers # -------------------------------- # This is WIP. Eventually it should contain all identifiers (types # and functions) which would be affected by the Y2038 bug. id_Y2038 = { # Y2038-unsafe types by definition 'time_t' # Types using Y2038-unsafe types 'lastlog', 'msqid_ds', 'semid_ds', 'timeb', 'timespec', 'timeval', 'utimbuf', 'itimerspec', 'stat', 'clnt_ops', 'elf_prstatus', 'itimerval', 'ntptimeval', 'rusage', 'timex', 'utmp', 'utmpx', # APIs using 2038-unsafe types 'ctime', 'ctime_r', 'difftime', 'gmtime', 'gmtime_r', 'localtime', 'localtime_r', 'mktime', 'stime', 'timegm', 'timelocal', 'time', 'msgctl', 'ftime', 'aio_suspend', 'clock_getres', 'clock_gettime', 'clock_nanosleep', 'clock_settime', 'futimens', 'mq_timedreceive', 'mq_timedsend', 'nanosleep', 'pselect', 'pthread_cond_timedwait', 'pthread_mutex_timedlock', 'pthread_rwlock_timedrdlock', 'pthread_rwlock_timedwrlock', 'sched_rr_get_interval', 'sem_timedwait', 'sigtimedwait', 'timespec_get', 'utimensat', 'adjtime', 'pmap_rmtcall', 'clntudp_bufcreate', 'clntudp_create', 'futimes', 'gettimeofday', 'lutimes', 'select', 'settimeofday', 'utimes', 'utime', 'timerfd_gettime', 'timerfd_settime', 'timer_gettime', 'timer_settime', 'fstatat', 'fstat', '__fxstatat', '__fxstat', 'lstat', '__lxstat', 'stat', '__xstat', 'struct itimerval', 'setitimer', 'getitimer', 'ntp_gettime', 'getrusage', 'wait3', 'wait4', 'adjtimex', 'ntp_adjtime', 'getutent_r', 'getutent', 'getutid_r', 'getutid', 'getutline_r', 'getutline', 'login', 'pututline', 'updwtmp', 'getutxent', 'getutxid', 'getutxline', 'pututxline' } def check_y2038_safe(dumpfile, quiet=False): # Assume that the code is Y2038 safe until proven otherwise y2038safe = True # load XML from .dump file data = cppcheckdata.CppcheckData(dumpfile) # Convert dump file path to source file in format generated by cppcheck. # For example after the following call: # cppcheck ./src/my-src.c --dump # We got 'src/my-src.c' value for 'file' field in cppcheckdata. srcfile = dumpfile.rstrip('.dump') srcfile = os.path.expanduser(srcfile) srcfile = os.path.normpath(srcfile) # go through each configuration for cfg in data.iterconfigurations(): if not quiet: print('Checking %s, config %s...' % (srcfile, cfg.name)) safe_ranges = [] safe = -1 time_bits_defined = False srclinenr = '0' for directive in cfg.directives: # track source line number if directive.file == srcfile: srclinenr = directive.linenr # check for correct _TIME_BITS if present if re_define_time_bits_64.match(directive.str): time_bits_defined = True elif re_define_time_bits.match(directive.str): cppcheckdata.reportError(directive, 'error', '_TIME_BITS must be defined equal to 64', 'y2038', 'type-bits-not-64') time_bits_defined = False y2038safe = False elif re_undef_time_bits.match(directive.str): time_bits_defined = False # check for _USE_TIME_BITS64 (un)definition if re_define_use_time_bits64.match(directive.str): safe = int(srclinenr) # warn about _TIME_BITS not being defined if not time_bits_defined: cppcheckdata.reportError(directive, 'warning', '_USE_TIME_BITS64 is defined but _TIME_BITS was not', 'y2038', 'type-bits-undef') elif re_undef_use_time_bits64.match(directive.str): unsafe = int(srclinenr) # do we have a safe..unsafe area? if unsafe > safe > 0: safe_ranges.append((safe, unsafe)) safe = -1 # check end of source beyond last directive if len(cfg.tokenlist) > 0: unsafe = int(cfg.tokenlist[-1].linenr) if unsafe > safe > 0: safe_ranges.append((safe, unsafe)) # go through all tokens for token in cfg.tokenlist: if token.str in id_Y2038: if not any(lower <= int(token.linenr) <= upper for (lower, upper) in safe_ranges): cppcheckdata.reportError(token, 'warning', token.str + ' is Y2038-unsafe', 'y2038', 'unsafe-call') y2038safe = False token = token.next return y2038safe def get_args_parser(): parser = cppcheckdata.ArgumentParser() return parser if __name__ == '__main__': parser = get_args_parser() args = parser.parse_args() exit_code = 0 quiet = not any((args.quiet, args.cli)) if not args.dumpfile: if not args.quiet: print("no input files.") sys.exit(0) for dumpfile in args.dumpfile: if not args.quiet: print('Checking ' + dumpfile + '...') check_y2038_safe(dumpfile, quiet) sys.exit(cppcheckdata.EXIT_CODE) cppcheck-2.7/benchmarks.txt000066400000000000000000000007531417746362400160430ustar00rootroot00000000000000 ========== Benchmarks ========== In this file we can document some good code repos / code samples to use when working on optimisations. Trac tickets ------------ http://trac.cppcheck.net/ticket/2435 -- Tokenizer::simplifyTypedef http://trac.cppcheck.net/ticket/8355 -- TokenList::createAst http://trac.cppcheck.net/ticket/9007 -- Unused types Repos ----- Small C++ library with lots of templates: https://framagit.org/dtschump/CImg Just check the file examples/use_tinymatwriter.cpp cppcheck-2.7/build-pcre.txt000066400000000000000000000037131417746362400157530ustar00rootroot00000000000000PCRE is a library that is used by the optional "rules" feature for the command line version of cppcheck. It is readily available on Linux and Mac OS X, but must be obtained separately for Windows. If you're using qmake to generate makefiles, the following behavior applies: - If you're not on Windows, it assumes by default that you have PCRE and want to enable rules support. You can disable rules support (removing the PCRE dependency) by passing HAVE_RULES=no to qmake. - If you are on Windows, but have PCRE available, you can enable rules support by passing HAVE_RULES=yes to qmake. - Note: This includes using build.bat since it calls qmake - to use PCRE and build.bat, you need to run set HAVE_RULES=yes before each run of build.bat Build instructions ------------------ Windows ------- Visual Studio To build PCRE, download the source code from www.pcre.org and CMake (https://cmake.org/download/). We assume you use Visual Studio 2015 - otherwise adapt the commands for your version. VS Solution file cmake . -G "Visual Studio 14 2015" Open PCRE.sln with VS IDE or via cmd: call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" x86 MSBuild PCRE.sln /target:Build /property:Configuration="Release" For 64-bit target: cmake . -G "Visual Studio 14 2015 Win64" or using NMake call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" x86 cmake . -G "NMake Makefiles" nmake or using MSYS cmake . -G "MSYS Makefiles" make Linux ----- The normal Makefile should work. Install PCRE on Ubuntu might be needed: sudo apt-get install libpcre3 libpcre3-dev Mac OSX ------- Install PCRE: homebre brew install pcre or macport sudo port install pcre Ensure /path/to/pcre.h is in CXXFLAGS, e.g: for homebrew export CXXFLAGS=${CXXFLAGS}:/usr/local/include or macport export CXXFLAGS=${CXXFLAGS}:/opt/local/include Or for MSVC copy pcre.lib and pcre.h in /externals directory. cppcheck-2.7/cfg/000077500000000000000000000000001417746362400137175ustar00rootroot00000000000000cppcheck-2.7/cfg/avr.cfg000066400000000000000000000306561417746362400152020ustar00rootroot00000000000000 false 0:255 false false false false false false false false false 0: false false false false false false false false false false false false false false false false cppcheck-2.7/cfg/bento4.cfg000066400000000000000000000205211417746362400155730ustar00rootroot00000000000000 false cppcheck-2.7/cfg/boost.cfg000066400000000000000000001343511417746362400155350ustar00rootroot00000000000000 false false false false false arg1==0?0:(arg1<0?-1:1) false arg1<0?1:0 false false false 5.000000000000000000000000000000000000e-01 false 3.333333333333333333333333333333333333e-01 false 6.666666666666666666666666666666666666e-01 false 6.666666666666666666666666666666666666e-01 false 1.66666666666666666666666666666666666666666e-01 false 7.500000000000000000000000000000000000e-01 false 1.414213562373095048801688724209698078e+00 false 1.732050807568877293527446341505872366e+00 false 7.071067811865475244008443621048490392e-01 false 6.931471805599453094172321214581765680e-01 false -3.665129205816643270124391582326694694e-01 false 1.177410022515474691011569326459699637e+00 false 7.071067811865475244008443621048490392e-01 false 3.141592653589793238462643383279502884e+00 false 1.570796326794896619231321691639751442e+00 false 1.047197551196597746154214461093167628e+00 false 5.235987755982988730771072305465838140e-01 false 6.283185307179586476925286766559005768e+00 false 2.094395102393195492308428922186335256e+00 false 2.356194490192344928846982537459627163e+00 false 4.188790204786390984616857844372670512e+00 false 1.591549430918953357688837633725143620e-01 false 3.989422804014326779399460599343818684e-01 false 1.772453850905516027298167483341145182e+00 false 1.253314137315500251207882642405522626e+00 false 2.506628274631000502415765284811045253e+00 false 9.189385332046727417803297364056176398e-01 false 5.641895835477562869480794515607725858e-01 false 5.641895835477562869480794515607725858e-01 false 1.415926535897932384626433832795028841e-01 false 8.584073464102067615373566167204971158e-01 false 7.953167673715975443483953350568065807e-01 false 2.245915771836104547342715220454373502e+01 false 9.869604401089358618834490999876151135e+00 false 1.644934066848226436472415166646025189e+00 false 3.100627668029982017547631506710139520e+01 false 1.464591887561523263020142527263790391e+00 false 6.827840632552956814670208331581645981e-01 false 2.718281828459045235360287471352662497e+00 false 6.065306597126334236037995349911804534e-01 false 2.314069263277926900572908636794854738e+01 false 1.648721270700128146848650787814163571e+00 false 4.342944819032518276511289189166050822e-01 false 2.302585092994045684017991454684364207e+00 false 2.302585092994045684017991454684364207e+00 false 1.745329251994329576923690768488612713e-02 false 5.729577951308232087679815481410517033e+01 false 8.414709848078965066525023216302989996e-01 false 5.403023058681397174009366074429766037e-01 false 1.175201193643801456882381850595600815e+00 false 1.543080634815243778477905620757061682e+00 false 1.618033988749894848204586834365638117e+00 false 4.812118250596034474977589134243684231e-01 false 2.078086921235027537601322606117795767e+00 false 5.772156649015328606065120900824024310e-01 false 1.732454714600633473583025315860829681e+00 false 3.331779238077186743183761363552442266e-01 false 1.644934066848226436472415166646025189e+00 false 1.202056903159594285399738161511449990e+00 false 9.159655941772190150546035149323841107e-01 false 1.282427129100622636875342568869791727e+00 false 2.685452001065306445309714835481795693e+00 false 1.139547099404648657492793019389846112e+00 false 6.311106578189371381918993515442277798e-01 false 3.245089300687638062848660410619754415e+00 false 2.450893006876380628486604106197544154e-01 false 6.366197723675813430755350534900574481e-01 false 7.978845608028653558798921198687637369e-01 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false true cppcheck-2.7/cfg/bsd.cfg000066400000000000000000000242331417746362400151540ustar00rootroot00000000000000 false false false false false false false false false false false false false false false 0: false 0: false false 1: false 2: false false 0: false 1: -1: true true true true cppcheck-2.7/cfg/cairo.cfg000066400000000000000000000064101417746362400154760ustar00rootroot00000000000000 false false false false false false cppcheck-2.7/cfg/cppcheck-cfg.rng000066400000000000000000000550611417746362400167530ustar00rootroot00000000000000 2 true false maybe error-code (::)?([a-zA-Z_:][a-zA-Z_0-9:]*[ ])*([a-zA-Z_][a-zA-Z_0-9]*::)*([a-zA-Z_][a-zA-Z_0-9]*([ ]?[*& ])*)+ all|([0-9]*:[0-9]*) error warning style performance portability information Obsolescent Obsolete c99 any variadic (-?[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?[,:])*([-]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?)? strlen argvalue sizeof mul value first middle last [.][a-z]+ std-like std-like erase insert array-like bool char short int long long long 1 2 4 8 s u 1 40 0 2 in out inout true false [a-zA-Z_][a-zA-Z_0-9]* [a-zA-Z_][a-zA-Z_0-9:]* [a-zA-Z_][a-zA-Z_0-9:,]* malloc(:[1-5])?|calloc(:[1-5],[1-5])?|strdup(:[1-5])? resize clear push pop find insert erase change-content change-internal change at_index item buffer buffer-nt start-iterator end-iterator iterator size empty 1 9223372036854775807 cppcheck-2.7/cfg/cppcheck-lib.cfg000066400000000000000000000040221417746362400167220ustar00rootroot00000000000000 false false false false -50:50 false -50:50 false cppcheck-2.7/cfg/cppunit.cfg000066400000000000000000000052021417746362400160610ustar00rootroot00000000000000 false false false cppcheck-2.7/cfg/daca.cfg000066400000000000000000000016141417746362400152720ustar00rootroot00000000000000 false arg1<arg2?arg1:arg2 false arg1>arg2?arg1:arg2 cppcheck-2.7/cfg/dpdk.cfg000066400000000000000000000003331417746362400153210ustar00rootroot00000000000000 true cppcheck-2.7/cfg/embedded_sql.cfg000066400000000000000000000001551417746362400170110ustar00rootroot00000000000000 cppcheck-2.7/cfg/emscripten.cfg000066400000000000000000000003461417746362400165540ustar00rootroot00000000000000 cppcheck-2.7/cfg/ginac.cfg000066400000000000000000014217321417746362400154730ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-2.7/cfg/gnu.cfg000066400000000000000000001210451417746362400151740ustar00rootroot00000000000000 free get_current_dir_name asprintf free xmalloc xcalloc xstrdup xrealloc free xfree backtrace_symbols free pvalloc free false ((arg1 & 0xff00u) >> 8) | ((arg1 & 0x00ffu) << 8) false ((arg1 & 0xff000000ul) >> 24) | ((arg1 & 0x00ff0000ul) >> 8) | ((arg1 & 0x0000ff00ul) << 8) | ((arg1 & 0x000000fful) << 24) false ((arg1 & 0xff00000000000000ull) >> 56) | ((arg1 & 0x00ff000000000000ull) >> 40) | ((arg1 & 0x0000ff0000000000ull) >> 24) | ((arg1 & 0x000000ff00000000ull) >> 8) | ((arg1 & 0x00000000ff000000ull) << 8) | ((arg1 & 0x0000000000ff0000ull) << 24) | ((arg1 & 0x000000000000ff00ull) << 40) | ((arg1 & 0x00000000000000ffull) << 56) false 0: false arg1 false 0: false false false false false false true false true 0: false 0: 0: 1: false 0: 0: 1: false 1: 0: 0: false 0: false arg1>=0 && arg1<=0x7F false 0:255 false false 0: false 0: false false 0: false 0: 0: false false false false false 0: false false false false 0: false 0: false 0: false 0: false false false false false false false false 0:4294967295 0:4294967295 false false false false false false false false false 0: 0: false 0: 0: false 0: false false false false 1: false false 0: 0: false 0: 1: false 0: 1: false 0: false false false false false false true false false false false 1: -1: false 0: false 0: mkostemp mkstemps mkostemps close close epoll_create close epoll_create1 cppcheck-2.7/cfg/googletest.cfg000066400000000000000000000162511417746362400165610ustar00rootroot00000000000000 cppcheck-2.7/cfg/gtk.cfg000066400000000000000000022125361417746362400152000ustar00rootroot00000000000000 g_thread_new g_thread_try_new g_thread_ref g_thread_unref g_thread_join g_variant_iter_copy g_variant_iter_new g_variant_iter_free g_source_new g_idle_source_new g_timeout_source_new g_timeout_source_new_seconds g_child_watch_source_new g_cancellable_source_new g_io_create_watch g_source_ref g_source_unref g_date_time_new g_date_time_new_now g_date_time_new_now_local g_date_time_new_now_utc g_date_time_new_from_unix_local g_date_time_new_from_unix_utc g_date_time_new_from_timeval_local g_date_time_new_from_timeval_utc g_date_time_new_local g_date_time_new_utc g_date_time_add g_date_time_add_years g_date_time_add_months g_date_time_add_weeks g_date_time_add_days g_date_time_add_hours g_date_time_add_minutes g_date_time_add_seconds g_date_time_add_full g_date_time_to_timezone g_date_time_to_local g_date_time_to_utc g_date_time_ref g_date_time_unref g_dir_open g_dir_rewind g_dir_close g_timer_new g_timer_destroy g_file_attribute_info_list_new g_file_attribute_info_list_dup g_file_attribute_info_list_ref g_file_attribute_info_list_unref g_slist_alloc g_slist_copy g_slist_copy_deep g_slist_free g_slist_free_1 g_slist_free_full g_variant_new g_variant_new_va g_variant_new_boolean g_variant_new_byte g_variant_new_int16 g_variant_new_uint16 g_variant_new_int32 g_variant_new_uint32 g_variant_new_int64 g_variant_new_uint64 g_variant_new_handle g_variant_new_double g_variant_new_string g_variant_new_take_string g_variant_new_printf g_variant_new_signature g_variant_new_object_path g_variant_new_variant g_variant_new_objv g_variant_new_strv g_variant_new_bytestring g_variant_new_bytestring_array g_variant_new_maybe g_variant_new_array g_variant_new_tuple g_variant_new_dict_entry g_variant_new_fixed_array g_variant_new_from_data g_variant_new_from_bytes g_variant_builder_end g_variant_new_parsed_va g_variant_new_parsed g_variant_byteswap g_variant_get_child_value g_variant_get_normal_form g_variant_parse g_variant_ref g_variant_take_ref g_variant_ref_sink g_variant_unref g_variant_iter_new g_variant_iter_free g_variant_type_new g_variant_type_copy g_variant_type_new_array g_variant_type_new_dict_entry g_variant_type_new_maybe g_variant_type_new_tuple g_variant_type_free g_allocator_new g_allocator_free g_bookmark_file_new g_bookmark_file_free g_srv_target_new g_srv_target_free g_string_chunk_new g_string_chunk_free g_test_log_buffer_new g_test_log_buffer_free g_value_array_new g_value_array_free g_cache_new g_cache_destroy g_cclosure_new g_cclosure_new_swap g_cclosure_new_object g_cclosure_new_object_swap g_closure_new_object g_closure_new_simple g_closure_ref g_closure_unref g_array_new g_array_sized_new g_array_ref g_array_free g_array_unref g_async_queue_new g_async_queue_new_full g_async_queue_ref g_async_queue_unref g_byte_array_new g_byte_array_sized_new g_byte_array_new_take g_byte_array_sized_new g_bytes_unref_to_array g_byte_array_ref g_byte_array_free g_byte_array_unref g_checksum_new g_checksum_copy g_checksum_free g_main_loop_new g_main_new g_main_loop_ref g_main_loop_unref g_main_destroy g_main_context_new g_main_context_ref g_main_context_unref g_main_destroy g_thread_pool_new g_thread_pool_free g_error_copy g_error_new_valist g_error_new_literal g_error_new g_error_free g_string_new g_string_new_len g_string_sized_new g_variant_print_string g_string_free g_ptr_array_new g_ptr_array_new_full g_ptr_array_new_with_free_func g_ptr_array_ref g_ptr_array_free g_ptr_array_unref g_pattern_spec_new g_pattern_spec_free g_key_file_new g_key_file_ref g_key_file_free g_key_file_unref g_io_module_scope_new g_io_module_scope_free g_ascii_strdown g_ascii_strup g_base64_decode g_base64_encode g_bookmark_file_get_description g_bookmark_file_get_mime_type g_bookmark_file_get_title g_bookmark_file_to_data g_build_filename g_build_filenamev g_build_path g_build_pathv g_bytes_unref_to_data g_compute_checksum_for_bytes g_compute_checksum_for_data g_compute_checksum_for_string g_compute_hmac_for_data g_compute_hmac_for_string g_convert g_convert_with_fallback g_convert_with_iconv g_credentials_to_string g_date_time_format g_filename_display_basename g_filename_display_name g_filename_from_uri g_filename_to_uri g_get_codeset g_get_current_dir g_get_locale_variants g_key_file_get_start_group g_key_file_to_data g_malloc g_realloc g_malloc0 g_malloc0_n g_malloc_n g_realloc_n g_memdup g_path_get_basename g_path_get_dirname g_slice_alloc g_slice_alloc0 g_slice_copy g_strcompress g_strconcat g_strdup g_strdup_printf g_strdup_vprintf g_strescape g_strjoin g_strjoinv g_strndup g_strnfill g_time_val_to_iso8601 g_try_malloc g_try_realloc g_try_malloc0 g_try_malloc0_n g_try_malloc_n g_try_realloc_n g_ucs4_to_utf16 g_ucs4_to_utf8 g_unicode_canonical_decomposition g_utf16_to_ucs4 g_utf16_to_utf8 g_utf8_casefold g_utf8_collate_key g_utf8_collate_key_for_filename g_utf8_normalize g_utf8_strdown g_utf8_strreverse g_utf8_strup g_utf8_substring g_utf8_to_ucs4 g_utf8_to_ucs4_fast g_utf8_to_ucs4_fast g_utf8_to_utf16 g_key_file_get_locale_string g_key_file_get_value g_key_file_get_string g_key_file_get_boolean_list g_key_file_get_integer_list g_key_file_get_double_list g_key_file_get_comment g_dbus_proxy_get_name_owner g_file_info_get_attribute_as_string g_file_attribute_matcher_to_string g_app_launch_context_get_environment g_app_launch_context_get_startup_notify_id g_filename_completer_get_completion_suffix g_inet_address_mask_to_string g_variant_dup_string g_variant_dup_bytestring g_variant_get_objv g_variant_get_strv g_variant_print g_datalist_id_dup_data g_dir_make_tmp g_filename_from_utf8 g_filename_to_utf8 g_file_read_link g_find_program_in_path g_format_size g_format_size_for_display g_format_size_full g_hostname_to_ascii g_hostname_to_unicode g_locale_from_utf8 g_locale_to_utf8 g_markup_escape_text g_markup_printf_escaped g_markup_vprintf_escaped g_match_info_expand_references g_match_info_fetch g_match_info_fetch_named g_option_context_get_help g_regex_escape_nul g_regex_escape_string g_regex_replace g_regex_replace_eval g_regex_replace_literal g_shell_quote g_shell_unquote g_uri_escape_string g_uri_parse_scheme g_uri_unescape_segment g_uri_unescape_string g_variant_type_dup_string g_value_dup_string g_register_data g_free g_hash_table_new_full g_hash_table_new g_hash_table_ref g_hash_table_destroy g_hash_table_unref g_io_channel_unix_new g_io_channel_win32_new_fd g_io_channel_win32_new_socket g_io_channel_win32_new_messages g_io_channel_new_file g_io_channel_ref g_io_channel_close g_io_channel_shutdown g_io_channel_unref g_emblemed_icon_get_emblems g_list_alloc g_list_copy g_list_copy_deep g_app_info_get_all g_app_info_get_all_for_type g_app_info_get_fallback_for_type g_app_info_get_recommended_for_type g_io_modules_load_all_in_directory g_io_modules_load_all_in_directory_with_scope g_hash_table_get_keys g_hash_table_get_values g_list_free g_list_free_1 g_list_free_full g_regex_new g_regex_ref g_regex_unref g_node_new g_node_copy g_node_copy_deep g_node_destroy g_time_zone_new g_time_zone_new_local g_time_zone_new_utc g_time_zone_ref g_time_zone_unref g_markup_parse_context_new g_markup_parse_context_free g_mapped_file_new g_mapped_file_new_from_fd g_mapped_file_ref g_mapped_file_free g_mapped_file_unref g_mutex_new g_mutex_free g_mem_chunk_new g_mem_chunk_free g_option_group_new g_option_group_free g_option_context_new g_option_context_free g_rand_new g_rand_copy g_rand_new_with_seed g_rand_new_with_seed_array g_rand_free g_queue_new g_queue_copy g_queue_free g_slice_new g_slice_free g_slice_free1 g_sequence_new g_sequence_free g_completion_new g_completion_free g_chunk_new g_chunk_free g_bytes_new g_bytes_new_take g_bytes_new_static g_bytes_new_with_free_func g_bytes_new_from_bytes g_byte_array_free_to_bytes g_memory_output_stream_steal_as_bytes g_variant_get_data_as_bytes g_mapped_file_get_bytes g_bytes_ref g_bytes_unref g_bookmark_file_get_uris g_bookmark_file_get_groups g_bookmark_file_get_applications g_key_file_get_groups g_key_file_get_keys g_strdupv g_strsplit g_strsplit_set g_uri_list_extract_uris g_key_file_get_string_list g_key_file_get_locale_string_list g_file_info_list_attributes g_file_info_get_attribute_stringv g_app_launch_context_get_environment g_filename_completer_get_completions g_io_module_query g_variant_dup_objv g_variant_dup_bytestring_array g_environ_setenv g_environ_unsetenv g_get_environ g_listenv g_match_info_fetch_all g_regex_split g_regex_split_full g_regex_split_simple g_regex_split_simple g_variant_dup_strv g_strfreev g_hmac_new g_hmac_copy g_hmac_ref g_hmac_unref g_hook_alloc g_hook_ref g_hook_unref g_hook_destroy g_hook_free g_date_new g_date_new_dmy g_date_new_julian g_date_free g_variant_builder_new g_variant_builder_ref g_variant_builder_unref g_cond_new g_cond_free g_app_launch_context_new g_app_info_create_from_commandline g_app_info_dup g_app_info_get_default_for_type g_app_info_get_default_for_uri_scheme g_application_new g_application_get_dbus_connection g_application_get_default g_buffered_input_stream_new g_buffered_output_stream_new g_cancellable_new g_charset_converter_new g_converter_input_stream_new g_converter_output_stream_new g_credentials_new g_data_input_stream_new g_data_output_stream_new g_dbus_auth_observer_new g_dbus_connection_new_finish g_dbus_connection_new_sync g_dbus_connection_new_for_address_finish g_dbus_connection_new_for_address_sync g_dbus_message_new g_dbus_message_new_signal g_dbus_message_new_method_call g_dbus_message_new_method_reply g_dbus_message_new_method_error g_dbus_message_new_method_error_valist g_dbus_message_new_method_error_literal g_dbus_object_manager_client_new_finish g_dbus_object_manager_client_new_sync g_dbus_object_manager_client_new_for_bus_finish g_dbus_object_manager_client_new_for_bus_sync g_dbus_object_manager_server_new g_dbus_object_manager_server_get_connection g_dbus_object_proxy_new g_dbus_object_skeleton_new g_dbus_proxy_new_finish g_dbus_proxy_new_sync g_dbus_proxy_new_for_bus_finish g_dbus_proxy_new_for_bus_sync g_emblemed_icon_new g_emblem_new g_emblem_new_with_origin g_file_icon_new g_file_icon_get_file g_file_info_new g_file_info_dup g_file_info_get_icon g_file_info_get_symbolic_icon g_file_info_get_attribute_object g_file_info_get_deletion_date g_filename_completer_new g_inet_address_mask_new g_inet_address_mask_new_from_string g_inet_address_mask_get_address g_inet_socket_address_new g_inet_socket_address_get_address g_initable_new g_initable_new_valist g_initable_newv g_io_module_new g_io_module_scope_new g_keyfile_settings_backend_new g_memory_input_stream_new g_memory_input_stream_new_from_data g_memory_input_stream_new_from_bytes g_memory_output_stream_new g_memory_output_stream_new_resizable g_memory_settings_backend_new g_null_settings_backend_new g_menu_item_new g_menu_item_new_section g_menu_item_new_submenu g_menu_item_new_from_model g_menu_new g_mount_operation_new g_network_address_new g_network_service_new g_object_new g_param_spec_pool_new g_pollable_source_new g_private_new g_proxy_address_new g_ptr_array_sized_new g_relation_new g_scanner_new g_settings_new g_signal_type_cclosure_new g_simple_action_group_new g_simple_action_new g_simple_async_result_new g_simple_permission_new g_socket_client_new g_socket_listener_new g_socket_new g_socket_service_new g_tcp_wrapper_connection_new g_test_dbus_new g_themed_icon_new g_threaded_socket_service_new g_tls_client_connection_new g_tls_file_database_new g_tls_password_new g_tls_server_connection_new g_unix_signal_source_new g_zlib_compressor_new g_zlib_decompressor_new g_object_ref g_object_unref gtk_widget_destroy g_tree_new g_tree_new_full g_tree_new_with_data g_tree_ref g_tree_unref g_file_attribute_matcher_new g_file_attribute_matcher_subtract g_file_attribute_matcher_ref g_file_attribute_matcher_unref true true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false arg1 false false false false false g_value_set_object_take_ownership has been deprecated since version 2.4 and should not be used in newly-written code. Use g_value_take_object() instead. false false false g_value_set_string_take_ownership has been deprecated since version 2.4 and should not be used in newly-written code. Use g_value_take_string() instead. false false false false false false false false false false false false false false false 0:255 false false false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false false false false false 0,2:36 false 0,2:36 false false 0:255 false 0:255 false 0:255 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false g_basename has been deprecated since version 2.2 and should not be used in newly-written code. Use g_path_get_basename() instead, but notice that g_path_get_basename() allocates new memory for the returned string, unlike this function which returns a pointer into the argument. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false g_dirname is deprecated and should not be used in newly-written code. Use g_path_get_dirname() instead. false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false 1: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: 0: 0: false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false g_strcasecmp has been deprecated since version 2.2 and should not be used in newly-written code. false arg1 false arg1 false false false false false false false false false g_strncasecmp has been deprecated since version 2.2 and should not be used in newly-written code. 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: 0: false 0: false 0: 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: 0: 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false g_string_down has been deprecated since version 2.2 and should not be used in newly-written code. This function uses the locale-specific tolower() function, which is almost never the right thing. Use g_string_ascii_down() or g_utf8_strdown() instead. false false false false false false false false false false false false false false false false false false false false 0: false g_string_sprintf is deprecated and should not be used in newly-written code. This function has been renamed to g_string_printf(). false g_string_sprintfa is deprecated and should not be used in newly-written code. This function has been renamed to g_string_append_printf() false false g_string_up has been deprecated since version 2.2 and should not be used in newly-written code. This function uses the locale-specific toupper() function, which is almost never the right thing. Use g_string_ascii_up() or g_utf8_strup() instead. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false g_type_class_add_private has been deprecated since version 2.58 and should not be used in newly-written code. Use the G_ADD_PRIVATE() macro with the G_DEFINE_* family of macros to add instance private data to a type. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_hbox_new has been deprecated since version 3.2 and should not be used in newly-written code. You can use gtk_box_new() with GTK_ORIENTATION_HORIZONTAL instead, which is a quick and easy change. But the recommendation is to switch to GtkGrid, since GtkBox is going to go away eventually. See Migrating from other containers to GtkGrid. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_vbox_new has been deprecated since version 3.2 and should not be used in newly-written code. You can use gtk_box_new() with GTK_ORIENTATION_VERTICAL instead, which is a quick and easy change. But the recommendation is to switch to GtkGrid, since GtkBox is going to go away eventually. See Migrating from other containers to GtkGrid. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false true gtk_exit is deprecated and should not be used in newly-written code. Use the standard exit() function instead. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_signal_connect_object_while_alive is deprecated and should not be used in newly-written code. Use g_signal_connect_object() instead, passing G_CONNECT_SWAPPED as connect_flags. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_label_get is deprecated and should not be used in newly-written code. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-2.7/cfg/icu.cfg000066400000000000000000000003671417746362400151660ustar00rootroot00000000000000 cppcheck-2.7/cfg/kde.cfg000066400000000000000000000063521417746362400151510ustar00rootroot00000000000000 false cppcheck-2.7/cfg/libcerror.cfg000066400000000000000000000071111417746362400163630ustar00rootroot00000000000000 false false false false false false 0: false cppcheck-2.7/cfg/libcurl.cfg000066400000000000000000000343441417746362400160440ustar00rootroot00000000000000 curl_easy_init curl_easy_duphandle curl_easy_cleanup curl_easy_escape curl_easy_unescape curl_escape curl_unescape curl_free curl_getenv curl_maprintf curl_mvaprintf free false false false 0: false false false false false 0: false false 0: false false false 0: false false 0: false false This function will be removed from the public libcurl API in a near future. It will instead be made "available" by source code access only, and then as curlx_getenv(). false These functions will be removed from the public libcurl API in the future. Do not use them in any new programs or projects. false false false 0: false false Usage of curl_multi_socket is deprecated, whereas the function is equivalent to curl_multi_socket_action with ev_bitmask set to 0. false false These functions will be removed from the public libcurl API in the future. Do not use them in any new programs or projects. false false false 0: false false These functions will be removed from the public libcurl API in a near future. They will instead be made "available" by source code access only, and then as curlx_strequal() and curlx_strenqual(). false These functions will be removed from the public libcurl API in a near future. They will instead be made "available" by source code access only, and then as curlx_strequal() and curlx_strenqual(). 0: false 0: cppcheck-2.7/cfg/libsigc++.cfg000066400000000000000000000047711417746362400161530ustar00rootroot00000000000000 false cppcheck-2.7/cfg/lua.cfg000066400000000000000000000223651417746362400151710ustar00rootroot00000000000000 true false false false false false 1: false false false false false false 0: false false false false false false false false false false false false false false false true cppcheck-2.7/cfg/mfc.cfg000066400000000000000000000301731417746362400151510ustar00rootroot00000000000000 cppcheck-2.7/cfg/microsoft_atl.cfg000066400000000000000000000031221417746362400172430ustar00rootroot00000000000000 cppcheck-2.7/cfg/microsoft_sal.cfg000066400000000000000000000644571417746362400172640ustar00rootroot00000000000000 cppcheck-2.7/cfg/microsoft_unittest.cfg000066400000000000000000000024741417746362400203530ustar00rootroot00000000000000 cppcheck-2.7/cfg/motif.cfg000066400000000000000000000166011417746362400155220ustar00rootroot00000000000000 false false false false false false false false false false MrmCloseHierarchy MrmOpenHierarchy MrmOpenHierarchyPerDisplay XmStringFree XmStringCreateLocalized XmStringCreateSimple XmStringGenerate XmCvtCTToXmString XtFree XmFontListEntryGetTag XmTextGetString XmCvtXmStringToCT XmWidgetGetBaselines XmFontListEntryFree XmFontListCreate XmFontListAppendEntry false false false false false false false XtFree XtMalloc XtCalloc XtRealloc XtNew XtNewString false false false false false false XOpenDisplay XCloseDisplay cppcheck-2.7/cfg/nspr.cfg000066400000000000000000000035441417746362400153700ustar00rootroot00000000000000 cppcheck-2.7/cfg/ntl.cfg000066400000000000000000005767721417746362400152250ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false cppcheck-2.7/cfg/opencv2.cfg000066400000000000000000000122411417746362400157540ustar00rootroot00000000000000 cv::fastMalloc cv::fastFree true false false 0: false false false false cppcheck-2.7/cfg/opengl.cfg000066400000000000000000000530631417746362400156730ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-2.7/cfg/openmp.cfg000066400000000000000000000113651417746362400157040ustar00rootroot00000000000000 omp_target_alloc omp_target_free omp_alloc omp_free false false false false This routine has been deprecated. See OpenMP specification. false false false false false false false false This routine has been deprecated. See OpenMP specification. false 1: false 0: false 0: cppcheck-2.7/cfg/openssl.cfg000066400000000000000000000114211417746362400160620ustar00rootroot00000000000000 EVP_CIPHER_CTX_new EVP_CIPHER_CTX_free false false false false 0: false false false 0: false false false false false false 0: cppcheck-2.7/cfg/pcre.cfg000066400000000000000000000204521417746362400153340ustar00rootroot00000000000000 cppcheck-2.7/cfg/posix.cfg000066400000000000000000004640141417746362400155530ustar00rootroot00000000000000 false false false 0: arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A false 0:255 arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' false 0:255 arg1==' ' || arg1=='\t' false 0:255 arg1==0x7F || arg1<=0x1F false 0:255 arg1>='0' && arg1<='9' false 0:255 arg1>=0x21 && arg1<=0x7E false 0:255 arg1>=0x61 && arg1<=0x7A false 0:255 arg1>=0x20 && arg1<=0x7E false 0:255 arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E false 0:255 arg1>=0x09 && arg1<=0x0D || arg1==0x20 false 0:255 arg1>=0 && arg1<=0x7F false 0:255 arg1>=0x41 && arg1<=0x5A false 0:255 arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 false 0:255 false 0: false false dlopen dlclose false 0: 0: false 0: false 0: false 0: false false false false 0: false 0: 0: false false false 0: false 0: false 0: false false 0: false false 0: false false false 0: false 0: false false false 0: false false false false false 0: false 0:999999 Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead. The obsolescent function 'usleep' is called. POSIX.1-2001 declares usleep() function obsolescent and POSIX.1-2008 removes it. It is recommended that new applications use the 'nanosleep' or 'setitimer' function. true false false false false 0: false Non reentrant function 'getrpcent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcent_r'. false Non reentrant function 'getrpcbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcbyname_r'. false Non reentrant function 'getrpcbynumber' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcbynumber_r'. false Non reentrant function 'getprotoent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotoent_r'. false Non reentrant function 'getprotobyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotobyname_r'. false Non reentrant function 'getprotobynumber' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotobynumber_r'. false Non reentrant function 'getservent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservent_r'. false Non reentrant function 'getservbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservbyname_r'. false Non reentrant function 'getservbyport' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservbyport_r'. false Non reentrant function 'getnetent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetent_r'. false Non reentrant function 'getnetbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetbyname_r'. false Non reentrant function 'getnetbyaddr' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetbyaddr_r'. false Non reentrant function 'gethostent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gethostent_r'. false false Non reentrant function 'gethostbyname2' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gethostbyname2_r'. false false false false false false false false 0: false false false false Obsolete function 'mktemp' called. It is recommended to use 'mkstemp' or 'mkdtemp' instead. The function 'mktemp' is considered to be dangerous due to race conditions and some implementations generating only up to 26 different filenames out of each template. This function has been removed in POSIX.1-2008. Use 'mkstemp' or 'mkdtemp' instead. false 0: false false false false false false false false false false false 0: 0: false 0: false false false false 0: false 0: false false false 0: false false false 0: false false false 0: false false false false 0: false 0: false false false false 0: false false false false 0: false 0: false 0: false false false 0: false false false false false false false 0: 0: 0: 1: 0: 0: false 0: 0: 0: 0: 0: 0: 0: 0: false 1: 0: false 1: 0: false 1: false false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false 0: 0: 0: false false false false false false Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'. false Non reentrant function 'getpwnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwnam_r'. Non reentrant function 'strtok' called. For threadsafe applications it is recommended to use the reentrant replacement function 'strtok_r'. false false 0: false Non reentrant function 'getpwuid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwuid_r'. false 0: false false false false false false false 0: false 0: false false false false 0: false 0: false false 0: false 0: false false false false false 0: false false false false false false false false Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'. false false Non reentrant function 'readdir' called. For threadsafe applications it is recommended to use the reentrant replacement function 'readdir_r'. false false 0: false false 0: false false false false Non reentrant function 'gmtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gmtime_r'. false false false false false false false Obsolescent function 'makecontext' called. Applications are recommended to be rewritten to use POSIX threads. false Obsolescent function 'swapcontext' called. Applications are recommended to be rewritten to use POSIX threads. false Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads. false false false 0: false 0: false 0: false false false false false false false false false false false false false true false false false false false false false false 0: false false false false false false false Non reentrant function 'tempnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'tempnam_r'. false Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'. false 0: Non reentrant function 'ttyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'ttyname_r'. false 0: 0: false Non reentrant function 'getspnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getspnam_r'. false Non reentrant function 'getspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getspent_r'. false Non reentrant function 'fgetspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetspent_r'. false Non reentrant function 'sgetspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'sgetspent_r'. false Non reentrant function 'fgetpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetpwent_r'. false Non reentrant function 'getgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrent_r'. false 0: false Non reentrant function 'fgetgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetgrent_r'. false Non reentrant function 'getnetgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetgrent_r'. false Non reentrant function 'getgrnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrnam_r'. false Non reentrant function 'getgrgid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrgid_r'. false Non reentrant function 'getlogin' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getlogin_r'. false Non reentrant function 'ctermid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'ctermid_r'. false false false false false false false false false false 0: false false false 0: false false false false false false false 0: false 0: false 0: 0: false 0: false false false false true false false false false false false false false false false false false false false false 0: false 0: false 0: false 0: false 0: false 0: false 0: false false 0: false false 0: false false 1: false 0: false 0: valloc free posix_memalign free scandir free strdup strndup wcsdup free mmap mmap64 munmap open mkstemp creat openat socket close opendir fdopendir closedir fdopen fclose popen pclose mq_open mq_close getaddrinfo freeaddrinfo cppcheck-2.7/cfg/python.cfg000066400000000000000000000471641417746362400157350ustar00rootroot00000000000000 PyMem_Malloc PyMem_Calloc PyMem_Realloc PyMem_Free PyMem_RawMalloc PyMem_RawCalloc PyMem_RawRealloc PyMem_RawFree PyObject_Malloc PyObject_Calloc PyObject_Realloc PyObject_Free false false true true false false false false false false 0: false false false false false false false false false NULL false NULL false false false NULL false false false false false false false false false false false false false false 1: 0: false false 0: false 0: false false false false false false false false cppcheck-2.7/cfg/qt.cfg000066400000000000000000005341331417746362400150350ustar00rootroot00000000000000 READ READ WRITE NOTIFY connect invokeMethod false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: false 0: false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false 0: false false false false false false false false false false false false false false false false 0,2:36 false 0,2:36 false 0,2:36 false false 0,2:36 false false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false false false false :-1,1: 1:12 1:31 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false QApplication QMutexLocker cppcheck-2.7/cfg/ruby.cfg000066400000000000000000000102111417746362400153540ustar00rootroot00000000000000 false false false false false false false false false false false true cppcheck-2.7/cfg/sdl.cfg000066400000000000000000000245311417746362400151670ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false -1 false false false SDL_FreeSurface SDL_CreateRGBSurface SDL_CreateRGBSurfaceFrom SDL_ConvertSurface TTF_RenderUTF8_Blended IMG_LoadPNG_RW IMG_LoadJPG_RW IMG_Load SDL_DestroyMutex SDL_CreateMutex SDL_WaitThread SDL_CreateThread SDL_RWclose SDL_RWFromFile SDL_FreeRW SDL_AllocRW Mix_FreeMusic Mix_LoadMUSType_RW cppcheck-2.7/cfg/sfml.cfg000066400000000000000000000220141417746362400153400ustar00rootroot00000000000000 false false false false false false false false false 0: false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false 0: false 0:100 false false false false cppcheck-2.7/cfg/sqlite3.cfg000066400000000000000000001630521417746362400157730ustar00rootroot00000000000000 sqlite3_malloc sqlite3_malloc64 sqlite3_free sqlite3_str_new sqlite3_str_finish sqlite3_str_finish sqlite3_free sqlite3_mprintf sqlite3_vmprintf sqlite3_free sqlite3_expanded_sql sqlite3_free sqlite3_open sqlite3_open16 sqlite3_open_v2 sqlite3_close sqlite3_close_v2 false 1: 0: false 1: false 1: false 1: false 1: false false false 0: false false 0: false 0: false 0: false 0: false 0: false 0: false 0: false false -1:127 false false false false false false false false false 1: false 1: false false false false false false false false false false false The sqlite3_prepare() interface is legacy and should be avoided false The sqlite3_prepare16() interface is legacy and should be avoided false false false false false false false false false false false false false 0: false false false 0: false false false false false false false false false false false false cppcheck-2.7/cfg/std.cfg000066400000000000000000007667041417746362400152160ustar00rootroot00000000000000 true arg1>0?arg1:-arg1 false arg1>0?arg1:-arg1 false false false false false -1.0:1.0 false -1.0:1.0 false -1.0:1.0 false 1.0: false 1.0: false 1.0: false false 26: false 0.0: false 0.0: false 0.0: false false false false false false false false false false -1.0:1.0 false -1.0:1.0 false -1.0:1.0 false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.0:1.0 false -1.0:1.0 false -1.0:1.0 false false false false false false false false false false 0: 0: false false false false false false false false false false false false false false false false false false false false false 26: false false :-1,1: false :-1,1: true false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false 0: 0: false false false false false false false false false false false false false false false 0: false 0: false false false 0: 0: false 0: false 0: false 0: false 0:255 false false false 0: false 0: false false false 0: false false 0:255 false false false Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun if the input data exceeds the size of the buffer. It is recommended to use the functions 'fgets' or 'gets_s' instead. false 0: false arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A false 0:255 arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A false arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' false 0:255 arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' false arg1==' ' || arg1=='\t' false 0:255 arg1==' ' || arg1=='\t' false arg1==0x7F || arg1<=0x1F false 0:255 arg1==0x7F || arg1<=0x1F false false arg1>='0' && arg1<='9' false 0:255 arg1>='0' && arg1<='9' false arg1>=0x21 && arg1<=0x7E false 0:255 arg1>=0x21 && arg1<=0x7E false arg1>=0x61 && arg1<=0x7A false 0:255 arg1>=0x61 && arg1<=0x7A false arg1>=0x20 && arg1<=0x7E false 0:255 arg1>=0x20 && arg1<=0x7E false arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E false 0:255 arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E false arg1>=0x09 && arg1<=0x0D || arg1==0x20 false 0:255 arg1>=0x09 && arg1<=0x0D || arg1==0x20 false arg1>=0x41 && arg1<=0x5A false 0:255 arg1>=0x41 && arg1<=0x5A false arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 false 0:255 arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 false false arg1 < 'A' || arg1 > 'Z' ? arg1 : arg1 + 32 false arg1 < 'a' || arg1 > 'z' ? arg1 : arg1 - 32 false false false false false false arg1>0?arg1:-arg1 false arg1>0?arg1:-arg1 false false false false false false false false false false false false false false false false 0: false :-1,1: false :-1,1: false false false 4.94066e-324: false 1.4013e-45: false 4.94066e-324: false false false false arg1>arg2?1:0 false arg1 >= arg2?1:0 false false false false false arg1<arg2?1:0 false arg1 <= arg2?1:0 false (arg1<arg2 || arg1>arg2)?1:0 false false false false false false false false false false false 4.94066e-324: false 1.4013e-45: false 4.94066e-324: false false false false 4.94066e-324: false 1.4013e-45: false 4.94066e-324: false false false false false false false false false true false 0: false 0: false 0: false 0: 0: false 0: 0: false 0: false 0: false 0: false 0: false 0: 0: false 0: false 0: false 0: 0: false 0: false 0: false false false false false false false false false false false false false false false false false false false 0: 0: false 0: 0: false 0: 0: false 0: false false 0: false 0: false 0: false false false 0: false 0: 0: false false false false false false false false false maybe false false false false false false false 0: false false false false false 0:255 false false arg1 false false 1: false false 0: strlen(arg1) false false 0: false 0: false false 0: false 0: false 0: false 0: false 0: false 0: false false false false 0: false 0: false false false false false false false false false false 0: false false false 0:255 false false 0: false false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false false 0: false arg1 < 'A' || arg1 > 'Z' ? arg1 : arg1 + 32 false 0:255 arg1 < 'a' || arg1 > 'z' ? arg1 : arg1 - 32 false 0:255 false false false false false false false false false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false 0: 0:255 false false 0: false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false false 0: false 0: false false 0: false false 0: false 0: false false false false false false false arg1<arg2?arg1:arg2 false arg1>arg2?arg1:arg2 false false false false false false 0: false false false false false false 0: false false false false false false false 1: false 1: false 1: false 1: false false false false false false false false false false false 0: false false 0: false false 0: false false false false false 0: false 0: false 0: false 0: false false false false 0: 0: false false false false 0: 0: false false 0: 0: false 0: 0: false 0: 0: false false 0: false 0: false 0: false 0: false 0: false 0: false 2:36 false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false malloc calloc aligned_alloc realloc reallocarray free fopen tmpfile freopen fclose std::insert_iterator std::lock_guard std::unique_lock std::shared_lock std::fstream std::wfstream std::ofstream std::wofstream std::basic_fstream std::basic_ofstream std::pair cppcheck-2.7/cfg/tinyxml2.cfg000066400000000000000000000043631417746362400161740ustar00rootroot00000000000000 false false false cppcheck-2.7/cfg/vcl.cfg000066400000000000000000000001361417746362400151640ustar00rootroot00000000000000 cppcheck-2.7/cfg/windows.cfg000066400000000000000000031510221417746362400160760ustar00rootroot00000000000000 CreatePen CreateBrushIndirect CreateDIBPatternBrush CreateDIBPatternBrushPt CreateHatchBrush CreatePatternBrush CreateSolidBrush CreateFont CreateFontIndirect CreateFontIndirectEx CreateBitmap CreateBitmapIndirect CreateCompatibleBitmap CreateDIBitmap CreateDIBSection CreateDiscardableBitmap CreateEllipticRgn CreateEllipticRgnIndirect CreatePolygonRgn CreatePolyPolygonRgn CreateRectRgn CreateRectRgnIndirect CreateRoundRectRgn CreateHalftonePalette CreatePalette DeleteObject closesocket socket CreateThread CreateFile CreateFileA CreateFileW OpenFile CreateJobObject CreateRemoteThread CreateConsoleScreenBuffer OpenBackupEventLog OpenEventLog CreateFileMapping CreateFileMappingFromApp CreateFileMappingNuma CreateMemoryResourceNotification OpenFileMapping CreateNamedPipe CreateEvent CreateEventA CreateEventW CreateEventEx CreateEventExA CreateEventExW CreateMutex CreateMutexA CreateMutexW CreateMutexEx CreateMutexExA CreateMutexExW CreateSemaphore CreateSemaphoreA CreateSemaphoreW CreateSemaphoreEx CreateSemaphoreExA CreateSemaphoreExW CreateTimerQueue CreateWaitableTimer OpenEvent OpenEventA OpenEventW OpenMutex OpenMutexA OpenMutexW OpenSemaphore OpenSemaphoreA OpenSemaphoreW OpenWaitableTimer OpenJobObject OpenProcess OpenThread CreateMailslot CloseHandle FindFirstFile FindFirstFileW FindFirstFileA FindFirstFileEx FindFirstFileExW FindFirstFileExA FindFirstFileNameW FindFirstFileNameTransactedW FindFirstStreamTransactedW FindFirstFileTransacted FindFirstFileTransactedA FindFirstFileTransactedW FindFirstStreamW FindClose OpenSCManager OpenService CreateService CloseServiceHandle LockServiceDatabase UnlockServiceDatabase HeapCreate HeapDestroy _wfopen _tfopen _wfopen_s _tfopen_s fclose _fcloseall _open _topen _wopen _close _popen _wpopen _tpopen _pclose LoadLibrary LoadLibraryA LoadLibraryW LoadLibraryEx LoadLibraryExA LoadLibraryExW FreeLibrary FreeLibraryAndExitThread UuidToString UuidToStringA UuidToStringW RpcStringFree ExAllocatePool ExAllocatePoolWithQuota ExAllocatePoolWithQuotaTag ExAllocatePoolWithTag ExAllocatePoolWithTagPriority ExFreePool ExFreePoolWithTag _dupenv_s _wdupenv_s _tdupenv_s free HeapAlloc HeapReAlloc HeapFree IoAllocateErrorLogEntry IoWriteErrorLogEntry IoFreeErrorLogEntry IoAllocateIrp IoFreeIrp IofCallDriver IoCallDriver IoAllocateMdl IoFreeMdl MmAllocateContiguousMemory MmFreeContiguousMemory MmAllocateContiguousMemorySpecifyCache MmAllocateContiguousMemorySpecifyCacheNode MmFreeContiguousMemorySpecifyCache IoAllocateWorkItem IoFreeWorkItem RtlAllocateHeap RtlFreeHeap ExAllocateFromPagedLookasideList ExFreeToPagedLookasideList ExAllocateFromNPagedLookasideList ExFreeToNPagedLookasideList AllocateHeap FreeHeap AllocateLsaHeap FreeLsaHeap AllocatePrivateHeap FreePrivateHeap VirtualAlloc VirtualFree VirtualAllocEx VirtualAllocExNuma VirtualFreeEx LocalAlloc LocalFree GlobalAlloc GlobalFree SetClipboardData MapViewOfFile MapViewOfFileEx MapViewOfFileExNuma MapViewOfFileFromApp UnmapViewOfFile RtlCreateHeap RtlDestroyHeap strdup wcsdup _strdup _wcsdup _mbsdup _tcsdup _malloc_dbg _calloc_dbg _strdup_dbg _wcsdup_dbg _tcsdup_dbg free _free_dbg _aligned_malloc _aligned_malloc_dbg _aligned_offset_malloc _aligned_offset_malloc_dbg _aligned_free _aligned_free_dbg CoTaskMemAlloc CoTaskMemFree _malloca _freea AllocateAndInitializeSid FreeSid false 0: false false 0: false 0: false false 0: false 0: true true false false false false false false false 0: false 0: false false 0: 0: false false false false false false false false false false false false false false arg1 false arg1 false false 1: Security Warning: Consider using StringCchCopy instead. Using this function incorrectly can compromise the security of your application. This function uses structured exception handling (SEH) to catch access violations and other errors. When this function catches SEH errors, it returns NULL without null-terminating the string and without notifying the caller of the error. The caller is not safe to assume that insufficient space is the error condition. false 1: Security Warning: Consider using StringCchCopy instead. Using this function incorrectly can compromise the security of your application. This function uses structured exception handling (SEH) to catch access violations and other errors. When this function catches SEH errors, it returns NULL without null-terminating the string and without notifying the caller of the error. The caller is not safe to assume that insufficient space is the error condition. false 1: false false 1: false false false false false false false false false false false false false false false 0: false false false false false false false false false false 0: false false false false 0: false 0: false 0: 0: false 0: 0: false false These POSIX functions are deprecated. Use the ISO C++ conformant _strdup, _wcsdup, _mbsdup instead. false These POSIX functions are deprecated. Use the ISO C++ conformant _strdup, _wcsdup, _mbsdup instead. false false 0: false false 0: false false 0: false false false false false false false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false arg1>0?arg1:-arg1 false false false false false false false false false 0 false false false false false 0: false false false false 0: false 0: false false 0: false false false false false false false 0: 0: false false false 0: false 0: false 0: false 0: false 0: false 0: false false strlen(arg1) false strlen(arg1) false false false false false 0: false 0: false false false 0: false 0: false 0: false 0: false false false false false false false false false false false false 0 false false false false false 0: true true false 1: false false false false false false false false false false false false false false false false false Due to security concerns it is not recommended to use this function, see MSDN for details. false Due to security concerns it is not recommended to use this function, see MSDN for details. false false Due to security concerns it is not recommended to use this function, see MSDN for details. false Due to security concerns it is not recommended to use this function, see MSDN for details. false 1: false 1: 0 false false 1: false false false 0,1 false false false 0: false 0: false arg1 false arg1 false false false false false false false false 0: false 0: false false false false false false false false 1: false false false false 0: false 0: false false false false false false false false false false false false false false false false false false 0: false false false false false false false false arg1 < 'A' || arg1 > 'Z' ? arg1 : arg1 + 32 false 0:255 false 0:255 false arg1 < 'a' || arg1 > 'z' ? arg1 : arg1 - 32 false 0:255 false 0:255 false false 0: false 0: false false false false false false false 0: false 0: false 0: false false 2:36 false 2:36 false 0: false 0: false false false false false false 0: 0: false false false false false false false false false false false false GetVersion may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper functions false GetVersionEx may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper functions false false false false false false false cppcheck-2.7/cfg/wxsqlite3.cfg000066400000000000000000002002671417746362400163520ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-2.7/cfg/wxsvg.cfg000066400000000000000000022115361417746362400155700ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -3.402823e+38:3.402823e+38 false -3.402823e+38:3.402823e+38 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false -3.402823e+38:3.402823e+38 false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false -3.402823e+38:3.402823e+38 false -3.402823e+38:3.402823e+38 -3.402823e+38:3.402823e+38 false -3.402823e+38:3.402823e+38 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false -1.79769e+308:1.79769e+308 false false false false false false false false false -1.79769e+308:1.79769e+308 -1.79769e+308:1.79769e+308 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-2.7/cfg/wxwidgets.cfg000066400000000000000000025711171417746362400164430ustar00rootroot00000000000000 false false false 0: false false false false false false "#arg1" false false false This is the same as 'wxString::IsEmpty' and is kept for wxWidgets 1.xx compatibility. You should not use it in new code. false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false This is the same as 'wxString::Len' and is kept for wxWidgets 1.xx compatibility. You should not use it in new code. false false false false false false false false false false true false false false false false false false false 10,16 false 0: false false false false 0: false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 1: false false 0: false false false false false false false false false false false false false false false false false false false false false false false This function is deprecated and kept mostly for backwards compatibility. Please override 'wxApp::MacOpenFiles' method instead in any new code. false This function is deprecated, use 'wxPlatformInfo::GetBitness()' instead. false This function is deprecated, please use 'ToWChar()' instead. false This function is deprecated, please use 'FromWChar()' instead. false This function has been deprecated in favour of 'wxMenuBar::SetMenuLabel()'. false This function only exists for compatibility, please override FormatTimeMS() in the new code. false This function is deprecated. Please use 'wxMenuItem::GetMenuLabel()' or 'wxMenuItem::GetMenuLabelText()' instead. false This function is deprecated. Please use 'wxMenuItem::GetItemLabel()' or 'wxMenuItem::GetItemLabelText()' instead. false This function is deprecated in favour of 'wxMenuItem::GetItemLabel()'. false This function is deprecated, use 'wxPlatformInfo::GetBitness()' instead. false This function is deprecated, use 'wxPGProperty::AddPrivateChild()' instead. false This function is deprecated. Ids generated by it can conflict with the Ids defined by the user code, use wxID_ANY to assign ids which are guaranteed to not conflict with the user-defined ids for the controls and menu items you create instead of using this function. false This function is deprecated, use 'wxPGProperty::GetValueAsString()' instead. false This function is deprecated, use 'wxPlatformInfo::GetBitnessName()' instead. false This function is deprecated, use 'wxPlatformInfo::SetBitness()' instead. false This function is deprecated in favour of 'wxMenuItem::SetItemLabel()'. false This function is deprecated, please use 'wxFileName::SplitPath()' instead. false This function is deprecated. This is the old way of detecting tool right clicks, although it will still work, you should use the EVT_TOOL_RCLICKED() macro instead. false The setup dialog is deprecated, though retained for backward compatibility. false This function has been deprecated, use 'wxWindow::Close()' instead. false The setup dialog is deprecated, though retained for backward compatibility. false This function is deprecated use 'CreateThread()' instead. false This function is deprecated in favour of 'IsShown()'. false This function is deprecated and kept mostly for backwards compatibility. Please override 'PushBack' method instead in any new code. false This function should be used instead of changing 'wxCAL_NO_YEAR_CHANGE' style bit directly. It allows or disallows the user to change the year interactively. Only in generic 'wxCalendarCtrl'. false This function is deprecated. Construct a wxFileName with wxPATH_UNIX and then use wxFileName::GetFullPath(wxPATH_DOS) instead. false This function is deprecated. false This function is kept mostly for backwards compatibility. Use 'LastReadCount()' or 'LastWriteCount()' instead. 'LastCount()' is still needed for use with less commonly used functions: 'Discard()', 'Peek()', and 'Unread()'. false This function is deprecated. false This function is deprecated, use GetStream() instead false This function is deprecated because its name is misleading: notice that the argument is in milliseconds, not microseconds. Please use either 'wxMilliSleep()' or 'wxMicroSleep()' depending on the resolution you need. false This function is kept mostly for backwards compatibility. false This function is deprecated, use 'wxWindow::SetInitialSize' instead. false This function is deprecated, use 'wxTextInputStream::ReadLine()' or 'wxTextInputStream::ReadWord()' instead. false Use 'wxStyledTextEvent::GetString()' instead. false This function is deprecated. false This function is deprecated, use 'wxDC::SetPalette' instead. false This function is deprecated and kept mostly for backwards compatibility. Please override 'GetMargins()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'Dismiss()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'Popup()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'SetMargins()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'wxDataViewCustomRenderer::ActivateCell()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'wxDataViewCustomRenderer::ActivateCell()' method instead in any new code. false false false This function is deprecated and is replaced by 'wxLog' functionality. false This function is deprecated and is replaced by 'wxLog' functionality. false This function is deprecated. Construct a 'wxFileName' with 'wxPATH_DOS' and then use 'wxFileName::GetFullPath(wxPATH_UNIX)' instead. false This function is deprecated. Please use 'wxFileName::SplitPath()' instead. false false false This function is deprecated. false false This function is deprecated. This is exactly the same as 'FitInside()' in wxWidgets 2.9 and later, please replace calls to it with 'FitInside()'. false This function is deprecated. This function does not free the old sizer which may result in memory leaks, use 'wxSizerItem::AssignSizer' which does free it instead. false This is identical to 'wxSizerItem::SetMinSize()', prefer to use the other function, as its name is more clear false false false false false false false false false false false false false 0: false false This function is deprecated. This function does not free the old sizer which may result in memory leaks, use 'wxSizerItem::AssignSpacer' which does free it instead. false This function is for backward compatibility only, and applications should use 'wxHelpControllerBase::DisplaySection()' instead. false This function is deprecated. false This function is deprecated in favour of 'wxIconizeEvent::IsIconized()'. false This function is deprecated, use 'wxList::GetCount()' instead. Returns the number of elements in the list. false This function is deprecated since version 3.1.2, dimensions and depth can only be set at construction time. false false false false false false This function is deprecated, please use 'wxWindow::FindWindowByLabel()' instead. false This function is deprecated, please use 'wxWindow::FindWindowByName()' instead. false This function is deprecated, please use 'wxFileName::CreateTempFileName()' instead. false This function is deprecated, use 'wxGetUserId()' with no arguments instead. false This function is deprecated, use 'wxGetEmailAddress()' with no arguments instead. false This function is deprecated, use 'wxGetHostName()' with no arguments instead. false This function is deprecated, use 'wxGetUserName()' with no arguments instead. false This function is deprecated, use 'wxGetCwd()' instead. false This function is deprecated. Please use 'wxGrid::SetCellAlignment(row, col, horiz, vert)' instead. false false false false false false false false false false false false 0 false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false strlen(arg1) false false false false false false This is the same as wxString::Trim() except that it doesn't change this string. This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false false This function is deprecated since version 3.1.2, dimensions and depth can only be set at construction time. false 0: false 0,2:36 false false false false false false false false false 0,1 false false false false false false false false false false false false false false false false false false false false false false false 0: 0: false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false true false false false false 0: false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 1: false 1: false false false false false false false false false false false 0: false 0: false false false 0: false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: false 0: false 0: false 1: false false false false false false false false false false false 0: false false false false false false false false false false 1: false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: false false false 0: 0: false 0: 0: false false false false false 0: false false false false false false false false false false false 0:59 false 0:60 false 0:23 false false 1:12 false 1:31 false false false false false false 0: false false false false false 1: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0.0:1.0 false false false false false false false false false false false false false false false false false false false false false false false false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::Mid' in new applications instead. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false arg1<arg2?arg1:arg2 false arg1>arg2?arg1:arg2 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 1: false 0: false false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::Find' in new applications instead. false This is a wxWidgets 1.xx compatibility function. You should not use it in new code. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::Truncate' in new applications instead. false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::MakeUpper' in new applications instead. false This is a wxWidgets 1.xx compatibility function. Use 'wxString::MakeLower' in new applications instead. false false false false false 0:255 0:255 0:255 false false false false false false false false false false false false 0: 1: false false 1: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false 0: false false false false 0: 0: false false false false false 0: false 0: false 0: false 0: false false false false false 0:15 false 0:15 false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false 0: false 0: false false false 0: 1: false 0: false false false false 0: 0: 0: false false false false false false false false false false false false false false false Please note that this method does the same thing as the standard 'reserve()' one and should not be used in new code. false false false false 0: cppcheck-2.7/cfg/zlib.cfg000066400000000000000000001131471417746362400153470ustar00rootroot00000000000000 false false false false false false false false false 0: false false false false false false 0: false false :16 false false false 0: false false false false false false :16 false false false false false false 0: false 0: false 0: false 0: false gzopen gzopen64 gzdopen gzopen_w gzclose gzclose_r gzclose_w false false false 0: false false 0: false 0: 0: false 0: false 0: 0: false false false 0: false 0:255 false false 0:255 false false false false false false false false false false false false false false 0: 0: false 0: 0: false 0: 0: false 0: 0: false 0: 0: false 0: 0: false false false false false false false false cppcheck-2.7/cli/000077500000000000000000000000001417746362400137275ustar00rootroot00000000000000cppcheck-2.7/cli/CMakeLists.txt000066400000000000000000000035061417746362400164730ustar00rootroot00000000000000file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") file(GLOB mainfile "main.cpp") list(REMOVE_ITEM srcs ${mainfile}) add_library(cli_objs OBJECT ${hdrs} ${srcs}) target_include_directories(cli_objs PRIVATE ${PROJECT_SOURCE_DIR}/lib/) if(USE_BUNDLED_TINYXML2) target_include_directories(cli_objs PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2/) endif() target_include_directories(cli_objs PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/) list(APPEND cppcheck_SOURCES ${hdrs} ${mainfile} $ $ $) if(USE_BUNDLED_TINYXML2) list(APPEND cppcheck_SOURCES $) endif() add_executable(cppcheck ${cppcheck_SOURCES}) target_include_directories(cppcheck PRIVATE ${PROJECT_SOURCE_DIR}/lib/) if(USE_BUNDLED_TINYXML2) target_include_directories(cppcheck PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2/) endif() target_include_directories(cppcheck PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/) if (HAVE_RULES) target_link_libraries(cppcheck ${PCRE_LIBRARY}) endif() if (USE_Z3) target_link_libraries(cppcheck ${Z3_LIBRARIES}) endif() if (WIN32 AND NOT BORLAND) if(NOT MINGW) target_link_libraries(cppcheck Shlwapi.lib) else() target_link_libraries(cppcheck shlwapi) endif() endif() if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) target_link_libraries(cppcheck tinyxml2) endif() add_dependencies(cppcheck copy_cfg) add_dependencies(cppcheck copy_addons) install(TARGETS cppcheck RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) install(FILES ${addons} DESTINATION ${FILESDIR}/addons COMPONENT headers) install(FILES ${cfgs} DESTINATION ${FILESDIR}/cfg COMPONENT headers) install(FILES ${platforms} DESTINATION ${FILESDIR}/platforms COMPONENT headers) cppcheck-2.7/cli/cli.vcxproj000066400000000000000000000647661417746362400161360ustar00rootroot00000000000000 Debug-PCRE Win32 Debug-PCRE x64 Debug Win32 Debug x64 Release-PCRE Win32 Release-PCRE x64 Release Win32 Release x64 {35CBDF51-2456-3EC3-99ED-113C30858883} cli 10.0 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 $(SolutionDir)bin\debug\ $(SolutionDir)bin\debug\ $(SolutionDir)bin\debug\ $(SolutionDir)bin\debug\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck cppcheck cppcheck cppcheck true true true true $(SolutionDir)bin\ $(SolutionDir)bin\ $(SolutionDir)bin\ $(SolutionDir)bin\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck cppcheck cppcheck cppcheck true true true true true true true true ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level4 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true true stdcpp14 /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true $(TargetDir)cli.pdb true 8000000 8000000 ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level4 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true true stdcpplatest /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true true 8000000 8000000 ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level4 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true true stdcpp14 /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true 8000000 8000000 ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level4 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true true stdcpplatest /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true 8000000 8000000 ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDLL Level4 AnySuitable true Speed true true 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpp14 shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true true 8000000 8000000 ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) MultiThreadedDLL Level4 AnySuitable true Speed true true 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true true 8000000 8000000 ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL Level4 AnySuitable true Speed true true true 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpp14 shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true true true true 8000000 8000000 ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL Level4 AnySuitable true Speed true true true 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true 8000000 8000000 {c183db5b-ad6c-423d-80ca-1f9549555a1a} cppcheck-2.7/cli/cli.vcxproj.filters000066400000000000000000000035441417746362400175700ustar00rootroot00000000000000 {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx {6d3be647-edb6-43e6-a7eb-3031a2c7b655} Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Resource Files cppcheck-2.7/cli/cmdlineparser.cpp000066400000000000000000001661451417746362400173000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cmdlineparser.h" #include "check.h" #include "cppcheckexecutor.h" #include "errortypes.h" #include "filelister.h" #include "importproject.h" #include "path.h" #include "platform.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include "threadexecutor.h" // Threading model #include "timer.h" #include "utils.h" #include #include #include #include // EXIT_FAILURE #include #include #include #include #ifdef HAVE_RULES // xml is used for rules #include #endif static void addFilesToList(const std::string& fileList, std::vector& pathNames) { // To keep things initially simple, if the file can't be opened, just be silent and move on. std::istream *files; std::ifstream infile; if (fileList == "-") { // read from stdin files = &std::cin; } else { infile.open(fileList); files = &infile; } if (files && *files) { std::string fileName; while (std::getline(*files, fileName)) { // next line if (!fileName.empty()) { pathNames.emplace_back(fileName); } } } } static bool addIncludePathsToList(const std::string& fileList, std::list* pathNames) { std::ifstream files(fileList); if (files) { std::string pathName; while (std::getline(files, pathName)) { // next line if (!pathName.empty()) { pathName = Path::removeQuotationMarks(pathName); pathName = Path::fromNativeSeparators(pathName); // If path doesn't end with / or \, add it if (!endsWith(pathName, '/')) pathName += '/'; pathNames->emplace_back(pathName); } } return true; } return false; } static bool addPathsToSet(const std::string& fileName, std::set* set) { std::list templist; if (!addIncludePathsToList(fileName, &templist)) return false; set->insert(templist.begin(), templist.end()); return true; } CmdLineParser::CmdLineParser(Settings *settings) : mSettings(settings) , mShowHelp(false) , mShowVersion(false) , mShowErrorMessages(false) , mExitAfterPrint(false) {} void CmdLineParser::printMessage(const std::string &message) { std::cout << "cppcheck: " << message << std::endl; } void CmdLineParser::printError(const std::string &message) { printMessage("error: " + message); } bool CmdLineParser::parseFromArgs(int argc, const char* const argv[]) { bool def = false; bool maxconfigs = false; mSettings->exename = argv[0]; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { // User define if (std::strncmp(argv[i], "-D", 2) == 0) { std::string define; // "-D define" if (std::strcmp(argv[i], "-D") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-D' is missing."); return false; } define = argv[i]; } // "-Ddefine" else { define = 2 + argv[i]; } // No "=", append a "=1" if (define.find('=') == std::string::npos) define += "=1"; if (!mSettings->userDefines.empty()) mSettings->userDefines += ";"; mSettings->userDefines += define; def = true; } // -E else if (std::strcmp(argv[i], "-E") == 0) { mSettings->preprocessOnly = true; mSettings->quiet = true; } // Include paths else if (std::strncmp(argv[i], "-I", 2) == 0) { std::string path; // "-I path/" if (std::strcmp(argv[i], "-I") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-I' is missing."); return false; } path = argv[i]; } // "-Ipath/" else { path = 2 + argv[i]; } path = Path::removeQuotationMarks(path); path = Path::fromNativeSeparators(path); // If path doesn't end with / or \, add it if (!endsWith(path,'/')) path += '/'; mSettings->includePaths.emplace_back(path); } // User undef else if (std::strncmp(argv[i], "-U", 2) == 0) { std::string undef; // "-U undef" if (std::strcmp(argv[i], "-U") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-U' is missing."); return false; } undef = argv[i]; } // "-Uundef" else { undef = 2 + argv[i]; } mSettings->userUndefs.insert(undef); } else if (std::strncmp(argv[i], "--addon=", 8) == 0) mSettings->addons.emplace_back(argv[i]+8); else if (std::strncmp(argv[i],"--addon-python=", 15) == 0) mSettings->addonPython.assign(argv[i]+15); else if (std::strcmp(argv[i], "--bug-hunting") == 0) mSettings->bugHunting = true; // TODO: Rename or move this parameter? else if (std::strncmp(argv[i], "--bug-hunting-check-function-max-time=", 38) == 0) mSettings->bugHuntingCheckFunctionMaxTime = std::atoi(argv[i] + 38); // Check configuration else if (std::strcmp(argv[i], "--check-config") == 0) mSettings->checkConfiguration = true; // Check library definitions else if (std::strcmp(argv[i], "--check-library") == 0) { mSettings->checkLibrary = true; // need to add "information" or no messages will be shown at all mSettings->addEnabled("information"); } else if (std::strncmp(argv[i], "--clang", 7) == 0) { mSettings->clang = true; if (std::strncmp(argv[i], "--clang=", 8) == 0) { mSettings->clangExecutable = argv[i] + 8; } } else if (std::strncmp(argv[i], "--config-exclude=",17) ==0) { mSettings->configExcludePaths.insert(Path::fromNativeSeparators(argv[i] + 17)); } else if (std::strncmp(argv[i], "--config-excludes-file=", 23) == 0) { // open this file and read every input file (1 file name per line) const std::string cfgExcludesFile(23 + argv[i]); if (!addPathsToSet(cfgExcludesFile, &mSettings->configExcludePaths)) { printError("unable to open config excludes file at '" + cfgExcludesFile + "'"); return false; } } else if (std::strncmp(argv[i], "--cppcheck-build-dir=", 21) == 0) { mSettings->buildDir = Path::fromNativeSeparators(argv[i] + 21); if (endsWith(mSettings->buildDir, '/')) mSettings->buildDir.pop_back(); } // Show --debug output after the first simplifications else if (std::strcmp(argv[i], "--debug") == 0 || std::strcmp(argv[i], "--debug-normal") == 0) mSettings->debugnormal = true; // show bug hunting debug output else if (std::strcmp(argv[i], "--debug-bug-hunting") == 0) mSettings->debugBugHunting = true; // Flag used for various purposes during debugging else if (std::strcmp(argv[i], "--debug-simplified") == 0) mSettings->debugSimplified = true; // Show template information else if (std::strcmp(argv[i], "--debug-template") == 0) mSettings->debugtemplate = true; // Show debug warnings else if (std::strcmp(argv[i], "--debug-warnings") == 0) mSettings->debugwarnings = true; // documentation.. else if (std::strcmp(argv[i], "--doc") == 0) { std::ostringstream doc; // Get documentation.. for (const Check * it : Check::instances()) { const std::string& name(it->name()); const std::string info(it->classInfo()); if (!name.empty() && !info.empty()) doc << "## " << name << " ##\n" << info << "\n"; } std::cout << doc.str(); mExitAfterPrint = true; return true; } // dump cppcheck data else if (std::strcmp(argv[i], "--dump") == 0) mSettings->dump = true; else if (std::strncmp(argv[i], "--enable=", 9) == 0) { const std::string errmsg = mSettings->addEnabled(argv[i] + 9); if (!errmsg.empty()) { printError(errmsg); return false; } // when "style" is enabled, also enable "warning", "performance" and "portability" if (mSettings->severity.isEnabled(Severity::style)) { mSettings->addEnabled("warning"); mSettings->addEnabled("performance"); mSettings->addEnabled("portability"); } } // print all possible error messages.. else if (std::strcmp(argv[i], "--errorlist") == 0) { mShowErrorMessages = true; mSettings->xml = true; mExitAfterPrint = true; } // --error-exitcode=1 else if (std::strncmp(argv[i], "--error-exitcode=", 17) == 0) { const std::string temp = argv[i]+17; std::istringstream iss(temp); if (!(iss >> mSettings->exitCode)) { mSettings->exitCode = 0; printError("argument must be an integer. Try something like '--error-exitcode=1'."); return false; } } // Exception handling inside cppcheck client else if (std::strcmp(argv[i], "--exception-handling") == 0) mSettings->exceptionHandling = true; else if (std::strncmp(argv[i], "--exception-handling=", 21) == 0) { mSettings->exceptionHandling = true; const std::string exceptionOutfilename = &(argv[i][21]); CppCheckExecutor::setExceptionOutput((exceptionOutfilename=="stderr") ? stderr : stdout); } // Filter errors else if (std::strncmp(argv[i], "--exitcode-suppressions=", 24) == 0) { // exitcode-suppressions=filename.txt std::string filename = 24 + argv[i]; std::ifstream f(filename); if (!f.is_open()) { printError("couldn't open the file: \"" + filename + "\"."); return false; } const std::string errmsg(mSettings->nofail.parseFile(f)); if (!errmsg.empty()) { printError(errmsg); return false; } } // use a file filter else if (std::strncmp(argv[i], "--file-filter=", 14) == 0) mSettings->fileFilters.push_back(argv[i] + 14); // file list specified else if (std::strncmp(argv[i], "--file-list=", 12) == 0) // open this file and read every input file (1 file name per line) addFilesToList(12 + argv[i], mPathNames); // Force checking of files that have "too many" configurations else if (std::strcmp(argv[i], "-f") == 0 || std::strcmp(argv[i], "--force") == 0) mSettings->force = true; // Print help else if (std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0) { mPathNames.clear(); mShowHelp = true; mExitAfterPrint = true; break; } // Ignored paths else if (std::strncmp(argv[i], "-i", 2) == 0) { std::string path; // "-i path/" if (std::strcmp(argv[i], "-i") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-i' is missing."); return false; } path = argv[i]; } // "-ipath/" else { path = 2 + argv[i]; } if (!path.empty()) { path = Path::removeQuotationMarks(path); path = Path::fromNativeSeparators(path); path = Path::simplifyPath(path); if (FileLister::isDirectory(path)) { // If directory name doesn't end with / or \, add it if (!endsWith(path, '/')) path += '/'; } mIgnoredPaths.emplace_back(path); } } else if (std::strncmp(argv[i], "--include=", 10) == 0) { mSettings->userIncludes.emplace_back(Path::fromNativeSeparators(argv[i] + 10)); } else if (std::strncmp(argv[i], "--includes-file=", 16) == 0) { // open this file and read every input file (1 file name per line) const std::string includesFile(16 + argv[i]); if (!addIncludePathsToList(includesFile, &mSettings->includePaths)) { printError("unable to open includes file at '" + includesFile + "'"); return false; } } // Inconclusive checking else if (std::strcmp(argv[i], "--inconclusive") == 0) mSettings->certainty.enable(Certainty::inconclusive); // Enables inline suppressions. else if (std::strcmp(argv[i], "--inline-suppr") == 0) mSettings->inlineSuppressions = true; // Checking threads else if (std::strncmp(argv[i], "-j", 2) == 0) { std::string numberString; // "-j 3" if (std::strcmp(argv[i], "-j") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-j' is missing."); return false; } numberString = argv[i]; } // "-j3" else numberString = argv[i]+2; std::istringstream iss(numberString); if (!(iss >> mSettings->jobs)) { printError("argument to '-j' is not a number."); return false; } if (mSettings->jobs > 10000) { // This limit is here just to catch typos. If someone has // need for more jobs, this value should be increased. printError("argument for '-j' is allowed to be 10000 at max."); return false; } } else if (std::strncmp(argv[i], "-l", 2) == 0) { std::string numberString; // "-l 3" if (std::strcmp(argv[i], "-l") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-l' is missing."); return false; } numberString = argv[i]; } // "-l3" else numberString = argv[i]+2; std::istringstream iss(numberString); if (!(iss >> mSettings->loadAverage)) { printError("argument to '-l' is not a number."); return false; } } // Enforce language (--language=, -x) else if (std::strncmp(argv[i], "--language=", 11) == 0 || std::strcmp(argv[i], "-x") == 0) { std::string str; if (argv[i][2]) { str = argv[i]+11; } else { i++; if (i >= argc || argv[i][0] == '-') { printError("no language given to '-x' option."); return false; } str = argv[i]; } if (str == "c") mSettings->enforcedLang = Settings::C; else if (str == "c++") mSettings->enforcedLang = Settings::CPP; else { printError("unknown language '" + str + "' enforced."); return false; } } // --library else if (std::strncmp(argv[i], "--library=", 10) == 0) { mSettings->libraries.emplace_back(argv[i] + 10); } // Set maximum number of #ifdef configurations to check else if (std::strncmp(argv[i], "--max-configs=", 14) == 0) { mSettings->force = false; std::istringstream iss(14+argv[i]); if (!(iss >> mSettings->maxConfigs)) { printError("argument to '--max-configs=' is not a number."); return false; } if (mSettings->maxConfigs < 1) { printError("argument to '--max-configs=' must be greater than 0."); return false; } maxconfigs = true; } // max ctu depth else if (std::strncmp(argv[i], "--max-ctu-depth=", 16) == 0) mSettings->maxCtuDepth = std::atoi(argv[i] + 16); // Write results in file else if (std::strncmp(argv[i], "--output-file=", 14) == 0) mSettings->outputFile = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 14)); // Specify platform else if (std::strncmp(argv[i], "--platform=", 11) == 0) { const std::string platform(11+argv[i]); if (platform == "win32A") mSettings->platform(Settings::Win32A); else if (platform == "win32W") mSettings->platform(Settings::Win32W); else if (platform == "win64") mSettings->platform(Settings::Win64); else if (platform == "unix32") mSettings->platform(Settings::Unix32); else if (platform == "unix64") mSettings->platform(Settings::Unix64); else if (platform == "native") mSettings->platform(Settings::Native); else if (platform == "unspecified") mSettings->platform(Settings::Unspecified); else if (!mSettings->loadPlatformFile(argv[0], platform)) { std::string message("unrecognized platform: \""); message += platform; message += "\"."; printError(message); return false; } } // Write results in results.plist else if (std::strncmp(argv[i], "--plist-output=", 15) == 0) { mSettings->plistOutput = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 15)); if (mSettings->plistOutput.empty()) mSettings->plistOutput = "./"; else if (!endsWith(mSettings->plistOutput,'/')) mSettings->plistOutput += '/'; const std::string plistOutput = Path::toNativeSeparators(mSettings->plistOutput); if (!FileLister::isDirectory(plistOutput)) { std::string message("plist folder does not exist: \""); message += plistOutput; message += "\"."; printError(message); return false; } } // --project else if (std::strncmp(argv[i], "--project=", 10) == 0) { mSettings->checkAllConfigurations = false; // Can be overridden with --max-configs or --force const std::string projectFile = argv[i]+10; ImportProject::Type projType = mSettings->project.import(projectFile, mSettings); mSettings->project.projectType = projType; if (projType == ImportProject::Type::CPPCHECK_GUI) { mPathNames = mSettings->project.guiProject.pathNames; for (const std::string &lib : mSettings->project.guiProject.libraries) mSettings->libraries.emplace_back(lib); for (const std::string &ignorePath : mSettings->project.guiProject.excludedPaths) mIgnoredPaths.emplace_back(ignorePath); const std::string platform(mSettings->project.guiProject.platform); if (platform == "win32A") mSettings->platform(Settings::Win32A); else if (platform == "win32W") mSettings->platform(Settings::Win32W); else if (platform == "win64") mSettings->platform(Settings::Win64); else if (platform == "unix32") mSettings->platform(Settings::Unix32); else if (platform == "unix64") mSettings->platform(Settings::Unix64); else if (platform == "native") mSettings->platform(Settings::Native); else if (platform == "unspecified" || platform == "Unspecified" || platform == "") ; else if (!mSettings->loadPlatformFile(projectFile.c_str(), platform) && !mSettings->loadPlatformFile(argv[0], platform)) { std::string message("unrecognized platform: \""); message += platform; message += "\"."; printError(message); return false; } if (!mSettings->project.guiProject.projectFile.empty()) projType = mSettings->project.import(mSettings->project.guiProject.projectFile, mSettings); } if (projType == ImportProject::Type::VS_SLN || projType == ImportProject::Type::VS_VCXPROJ) { if (mSettings->project.guiProject.analyzeAllVsConfigs == "false") mSettings->project.selectOneVsConfig(mSettings->platformType); if (!CppCheckExecutor::tryLoadLibrary(mSettings->library, argv[0], "windows.cfg")) { // This shouldn't happen normally. printError("failed to load 'windows.cfg'. Your Cppcheck installation is broken. Please re-install."); return false; } } if (projType == ImportProject::Type::MISSING) { printError("failed to open project '" + projectFile + "'. The file does not exist."); return false; } if (projType == ImportProject::Type::UNKNOWN) { printError("failed to load project '" + projectFile + "'. The format is unknown."); return false; } if (projType == ImportProject::Type::FAILURE) { printError("failed to load project '" + projectFile + "'. An error occurred."); return false; } } // --project-configuration else if (std::strncmp(argv[i], "--project-configuration=", 24) == 0) { mVSConfig = argv[i] + 24; if (!mVSConfig.empty() && (mSettings->project.projectType == ImportProject::Type::VS_SLN || mSettings->project.projectType == ImportProject::Type::VS_VCXPROJ)) mSettings->project.ignoreOtherConfigs(mVSConfig); } // Only print something when there are errors else if (std::strcmp(argv[i], "-q") == 0 || std::strcmp(argv[i], "--quiet") == 0) mSettings->quiet = true; // Output relative paths else if (std::strcmp(argv[i], "-rp") == 0 || std::strcmp(argv[i], "--relative-paths") == 0) mSettings->relativePaths = true; else if (std::strncmp(argv[i], "-rp=", 4) == 0 || std::strncmp(argv[i], "--relative-paths=", 17) == 0) { mSettings->relativePaths = true; if (argv[i][argv[i][3]=='='?4:17] != 0) { std::string paths = argv[i]+(argv[i][3]=='='?4:17); for (;;) { const std::string::size_type pos = paths.find(';'); if (pos == std::string::npos) { mSettings->basePaths.emplace_back(Path::fromNativeSeparators(paths)); break; } mSettings->basePaths.emplace_back(Path::fromNativeSeparators(paths.substr(0, pos))); paths.erase(0, pos + 1); } } else { printError("no paths specified for the '" + std::string(argv[i]) + "' option."); return false; } } // Report progress else if (std::strcmp(argv[i], "--report-progress") == 0) { mSettings->reportProgress = true; } #ifdef HAVE_RULES // Rule given at command line else if (std::strncmp(argv[i], "--rule=", 7) == 0) { Settings::Rule rule; rule.pattern = 7 + argv[i]; mSettings->rules.emplace_back(rule); } // Rule file else if (std::strncmp(argv[i], "--rule-file=", 12) == 0) { tinyxml2::XMLDocument doc; if (doc.LoadFile(12+argv[i]) == tinyxml2::XML_SUCCESS) { tinyxml2::XMLElement *node = doc.FirstChildElement(); for (; node && strcmp(node->Value(), "rule") == 0; node = node->NextSiblingElement()) { Settings::Rule rule; tinyxml2::XMLElement *tokenlist = node->FirstChildElement("tokenlist"); if (tokenlist) rule.tokenlist = tokenlist->GetText(); tinyxml2::XMLElement *pattern = node->FirstChildElement("pattern"); if (pattern) { rule.pattern = pattern->GetText(); } tinyxml2::XMLElement *message = node->FirstChildElement("message"); if (message) { tinyxml2::XMLElement *severity = message->FirstChildElement("severity"); if (severity) rule.severity = Severity::fromString(severity->GetText()); tinyxml2::XMLElement *id = message->FirstChildElement("id"); if (id) rule.id = id->GetText(); tinyxml2::XMLElement *summary = message->FirstChildElement("summary"); if (summary) rule.summary = summary->GetText() ? summary->GetText() : ""; } if (!rule.pattern.empty()) mSettings->rules.emplace_back(rule); } } else { printError("unable to load rule-file: " + std::string(12+argv[i])); return false; } } #endif // show timing information.. else if (std::strncmp(argv[i], "--showtime=", 11) == 0) { const std::string showtimeMode = argv[i] + 11; if (showtimeMode == "file") mSettings->showtime = SHOWTIME_MODES::SHOWTIME_FILE; else if (showtimeMode == "summary") mSettings->showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY; else if (showtimeMode == "top5") mSettings->showtime = SHOWTIME_MODES::SHOWTIME_TOP5; else if (showtimeMode.empty()) mSettings->showtime = SHOWTIME_MODES::SHOWTIME_NONE; else { printError("unrecognized showtime mode: \"" + showtimeMode + "\". Supported modes: file, summary, top5."); return false; } } // --std else if (std::strcmp(argv[i], "--std=c89") == 0) { mSettings->standards.c = Standards::C89; } else if (std::strcmp(argv[i], "--std=c99") == 0) { mSettings->standards.c = Standards::C99; } else if (std::strcmp(argv[i], "--std=c11") == 0) { mSettings->standards.c = Standards::C11; } else if (std::strcmp(argv[i], "--std=c++03") == 0) { mSettings->standards.cpp = Standards::CPP03; } else if (std::strcmp(argv[i], "--std=c++11") == 0) { mSettings->standards.cpp = Standards::CPP11; } else if (std::strcmp(argv[i], "--std=c++14") == 0) { mSettings->standards.cpp = Standards::CPP14; } else if (std::strcmp(argv[i], "--std=c++17") == 0) { mSettings->standards.cpp = Standards::CPP17; } else if (std::strcmp(argv[i], "--std=c++20") == 0) { mSettings->standards.cpp = Standards::CPP20; } else if (std::strncmp(argv[i], "--suppress=", 11) == 0) { const std::string suppression = argv[i]+11; const std::string errmsg(mSettings->nomsg.addSuppressionLine(suppression)); if (!errmsg.empty()) { printError(errmsg); return false; } } // Filter errors else if (std::strncmp(argv[i], "--suppressions-list=", 20) == 0) { std::string filename = argv[i]+20; std::ifstream f(filename); if (!f.is_open()) { std::string message("couldn't open the file: \""); message += filename; message += "\"."; if (std::count(filename.begin(), filename.end(), ',') > 0 || std::count(filename.begin(), filename.end(), '.') > 1) { // If user tried to pass multiple files (we can only guess that) // e.g. like this: --suppressions-list=a.txt,b.txt // print more detailed error message to tell user how he can solve the problem message += "\nIf you want to pass two files, you can do it e.g. like this:"; message += "\n cppcheck --suppressions-list=a.txt --suppressions-list=b.txt file.cpp"; } printError(message); return false; } const std::string errmsg(mSettings->nomsg.parseFile(f)); if (!errmsg.empty()) { printError(errmsg); return false; } } else if (std::strncmp(argv[i], "--suppress-xml=", 15) == 0) { const char * filename = argv[i] + 15; const std::string errmsg(mSettings->nomsg.parseXmlFile(filename)); if (!errmsg.empty()) { printError(errmsg); return false; } } // Output formatter else if (std::strcmp(argv[i], "--template") == 0 || std::strncmp(argv[i], "--template=", 11) == 0) { // "--template format" if (argv[i][10] == '=') mSettings->templateFormat = argv[i] + 11; else if ((i+1) < argc && argv[i+1][0] != '-') { ++i; mSettings->templateFormat = argv[i]; } else { printError("argument to '--template' is missing."); return false; } if (mSettings->templateFormat == "gcc") { mSettings->templateFormat = "{bold}{file}:{line}:{column}: {magenta}warning:{default} {message} [{id}]{reset}\\n{code}"; mSettings->templateLocation = "{bold}{file}:{line}:{column}: {dim}note:{reset} {info}\\n{code}"; } else if (mSettings->templateFormat == "daca2") { mSettings->daca = true; mSettings->templateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]"; mSettings->templateLocation = "{file}:{line}:{column}: note: {info}"; } else if (mSettings->templateFormat == "vs") mSettings->templateFormat = "{file}({line}): {severity}: {message}"; else if (mSettings->templateFormat == "edit") mSettings->templateFormat = "{file} +{line}: {severity}: {message}"; else if (mSettings->templateFormat == "cppcheck1") mSettings->templateFormat = "{callstack}: ({severity}{inconclusive:, inconclusive}) {message}"; else if (mSettings->templateFormat == "selfcheck") { mSettings->templateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}"; mSettings->templateLocation = "{file}:{line}:{column}: note: {info}\\n{code}"; mSettings->daca = true; } } else if (std::strcmp(argv[i], "--template-location") == 0 || std::strncmp(argv[i], "--template-location=", 20) == 0) { // "--template-location format" if (argv[i][19] == '=') mSettings->templateLocation = argv[i] + 20; else if ((i+1) < argc && argv[i+1][0] != '-') { ++i; mSettings->templateLocation = argv[i]; } else { printError("argument to '--template' is missing."); return false; } } else if (std::strcmp(argv[i], "-v") == 0 || std::strcmp(argv[i], "--verbose") == 0) mSettings->verbose = true; else if (std::strcmp(argv[i], "--version") == 0) { mShowVersion = true; mExitAfterPrint = true; return true; } // Write results in results.xml else if (std::strcmp(argv[i], "--xml") == 0) mSettings->xml = true; // Define the XML file version (and enable XML output) else if (std::strncmp(argv[i], "--xml-version=", 14) == 0) { const std::string numberString(argv[i]+14); std::istringstream iss(numberString); if (!(iss >> mSettings->xml_version)) { printError("argument to '--xml-version' is not a number."); return false; } if (mSettings->xml_version != 2) { // We only have xml version 2 printError("'--xml-version' can only be 2."); return false; } // Enable also XML if version is set mSettings->xml = true; } else { std::string message("unrecognized command line option: \""); message += argv[i]; message += "\"."; printError(message); return false; } } else { mPathNames.emplace_back(Path::fromNativeSeparators(Path::removeQuotationMarks(argv[i]))); } } mSettings->loadCppcheckCfg(argv[0]); // Default template format.. if (mSettings->templateFormat.empty()) { mSettings->templateFormat = "{bold}{file}:{line}:{column}: {red}{inconclusive:{magenta}}{severity}:{inconclusive: inconclusive:}{default} {message} [{id}]{reset}\\n{code}"; if (mSettings->templateLocation.empty()) mSettings->templateLocation = "{bold}{file}:{line}:{column}: {dim}note:{reset} {info}\\n{code}"; } mSettings->project.ignorePaths(mIgnoredPaths); if (mSettings->force || maxconfigs) mSettings->checkAllConfigurations = true; if (mSettings->force) mSettings->maxConfigs = INT_MAX; else if ((def || mSettings->preprocessOnly) && !maxconfigs) mSettings->maxConfigs = 1U; if (mSettings->checks.isEnabled(Checks::unusedFunction) && mSettings->jobs > 1) { printMessage("unusedFunction check can't be used with '-j' option. Disabling unusedFunction check."); } if (argc <= 1) { mShowHelp = true; mExitAfterPrint = true; } if (mShowHelp) { printHelp(); return true; } // Print error only if we have "real" command and expect files if (!mExitAfterPrint && mPathNames.empty() && mSettings->project.fileSettings.empty()) { printError("no C or C++ source files found."); return false; } // Use paths _pathnames if no base paths for relative path output are given if (mSettings->basePaths.empty() && mSettings->relativePaths) mSettings->basePaths = mPathNames; return true; } void CmdLineParser::printHelp() { std::cout << "Cppcheck - A tool for static C/C++ code analysis\n" "\n" "Syntax:\n" " cppcheck [OPTIONS] [files or paths]\n" "\n" "If a directory is given instead of a filename, *.cpp, *.cxx, *.cc, *.c++, *.c, *.ipp,\n" "*.ixx, *.tpp, and *.txx files are checked recursively from the given directory.\n\n" "Options:\n" " --addon=\n" " Execute addon. i.e. --addon=cert. If options must be\n" " provided a json configuration is needed.\n" " --addon-python=\n" " You can specify the python interpreter either in the\n" " addon json files or through this command line option.\n" " If not present, Cppcheck will try \"python3\" first and\n" " then \"python\".\n" " --bug-hunting\n" " Enable noisy and soundy analysis. The normal Cppcheck\n" " analysis is turned off.\n" " --cppcheck-build-dir=\n" " Cppcheck work folder. Advantages:\n" " * whole program analysis\n" " * faster analysis; Cppcheck will reuse the results if\n" " the hash for a file is unchanged.\n" " * some useful debug information, i.e. commands used to\n" " execute clang/clang-tidy/addons.\n" " --check-config Check cppcheck configuration. The normal code\n" " analysis is disabled by this flag.\n" " --check-library Show information messages when library files have\n" " incomplete info.\n" " --clang= Experimental: Use Clang parser instead of the builtin Cppcheck\n" " parser. Takes the executable as optional parameter and\n" " defaults to `clang`. Cppcheck will run the given Clang\n" " executable, import the Clang AST and convert it into\n" " Cppcheck data. After that the normal Cppcheck analysis is\n" " used. You must have the executable in PATH if no path is\n" " given.\n" " --config-exclude=\n" " Path (prefix) to be excluded from configuration\n" " checking. Preprocessor configurations defined in\n" " headers (but not sources) matching the prefix will not\n" " be considered for evaluation.\n" " --config-excludes-file=\n" " A file that contains a list of config-excludes\n" " --dump Dump xml data for each translation unit. The dump\n" " files have the extension .dump and contain ast,\n" " tokenlist, symboldatabase, valueflow.\n" " -D Define preprocessor symbol. Unless --max-configs or\n" " --force is used, Cppcheck will only check the given\n" " configuration when -D is used.\n" " Example: '-DDEBUG=1 -D__cplusplus'.\n" " -E Print preprocessor output on stdout and don't do any\n" " further processing.\n" " --enable= Enable additional checks. The available ids are:\n" " * all\n" " Enable all checks. It is recommended to only\n" " use --enable=all when the whole program is\n" " scanned, because this enables unusedFunction.\n" " * warning\n" " Enable warning messages\n" " * style\n" " Enable all coding style checks. All messages\n" " with the severities 'style', 'warning',\n" " 'performance' and 'portability' are enabled.\n" " * performance\n" " Enable performance messages\n" " * portability\n" " Enable portability messages\n" " * information\n" " Enable information messages\n" " * unusedFunction\n" " Check for unused functions. It is recommended\n" " to only enable this when the whole program is\n" " scanned.\n" " * missingInclude\n" " Warn if there are missing includes. For\n" " detailed information, use '--check-config'.\n" " Several ids can be given if you separate them with\n" " commas. See also --std\n" " --error-exitcode= If errors are found, integer [n] is returned instead of\n" " the default '0'. '" << EXIT_FAILURE << "' is returned\n" " if arguments are not valid or if no input files are\n" " provided. Note that your operating system can modify\n" " this value, e.g. '256' can become '0'.\n" " --errorlist Print a list of all the error messages in XML format.\n" " --exitcode-suppressions=\n" " Used when certain messages should be displayed but\n" " should not cause a non-zero exitcode.\n" " --file-filter= Analyze only those files matching the given filter str\n" " Can be used multiple times\n" " Example: --file-filter=*bar.cpp analyzes only files\n" " that end with bar.cpp.\n" " --file-list= Specify the files to check in a text file. Add one\n" " filename per line. When file is '-,' the file list will\n" " be read from standard input.\n" " -f, --force Force checking of all configurations in files. If used\n" " together with '--max-configs=', the last option is the\n" " one that is effective.\n" " -h, --help Print this help.\n" " -I Give path to search for include files. Give several -I\n" " parameters to give several paths. First given path is\n" " searched for contained header files first. If paths are\n" " relative to source files, this is not needed.\n" " --includes-file=\n" " Specify directory paths to search for included header\n" " files in a text file. Add one include path per line.\n" " First given path is searched for contained header\n" " files first. If paths are relative to source files,\n" " this is not needed.\n" " --include=\n" " Force inclusion of a file before the checked file.\n" " -i Give a source file or source file directory to exclude\n" " from the check. This applies only to source files so\n" " header files included by source files are not matched.\n" " Directory name is matched to all parts of the path.\n" " --inconclusive Allow that Cppcheck reports even though the analysis is\n" " inconclusive.\n" " There are false positives with this option. Each result\n" " must be carefully investigated before you know if it is\n" " good or bad.\n" " --inline-suppr Enable inline suppressions. Use them by placing one or\n" " more comments, like: '// cppcheck-suppress warningId'\n" " on the lines before the warning to suppress.\n" " -j Start threads to do the checking simultaneously.\n" #ifdef THREADING_MODEL_FORK " -l Specifies that no new threads should be started if\n" " there are other threads running and the load average is\n" " at least .\n" #endif " --language=, -x \n" " Forces cppcheck to check all files as the given\n" " language. Valid values are: c, c++\n" " --library= Load file that contains information about types\n" " and functions. With such information Cppcheck\n" " understands your code better and therefore you\n" " get better results. The std.cfg file that is\n" " distributed with Cppcheck is loaded automatically.\n" " For more information about library files, read the\n" " manual.\n" " --max-ctu-depth=N Max depth in whole program analysis. The default value\n" " is 2. A larger value will mean more errors can be found\n" " but also means the analysis will be slower.\n" " --output-file= Write results to file, rather than standard error.\n" " --project= Run Cppcheck on project. The can be a Visual\n" " Studio Solution (*.sln), Visual Studio Project\n" " (*.vcxproj), compile database (compile_commands.json),\n" " or Borland C++ Builder 6 (*.bpr). The files to analyse,\n" " include paths, defines, platform and undefines in\n" " the specified file will be used.\n" " --project-configuration=\n" " If used together with a Visual Studio Solution (*.sln)\n" " or Visual Studio Project (*.vcxproj) you can limit\n" " the configuration cppcheck should check.\n" " For example: '--project-configuration=Release|Win32'\n" " --max-configs=\n" " Maximum number of configurations to check in a file\n" " before skipping it. Default is '12'. If used together\n" " with '--force', the last option is the one that is\n" " effective.\n" " --platform=, --platform=\n" " Specifies platform specific types and sizes. The\n" " available builtin platforms are:\n" " * unix32\n" " 32 bit unix variant\n" " * unix64\n" " 64 bit unix variant\n" " * win32A\n" " 32 bit Windows ASCII character encoding\n" " * win32W\n" " 32 bit Windows UNICODE character encoding\n" " * win64\n" " 64 bit Windows\n" " * avr8\n" " 8 bit AVR microcontrollers\n" " * elbrus-e1cp\n" " Elbrus e1c+ architecture\n" " * pic8\n" " 8 bit PIC microcontrollers\n" " Baseline and mid-range architectures\n" " * pic8-enhanced\n" " 8 bit PIC microcontrollers\n" " Enhanced mid-range and high end (PIC18) architectures\n" " * pic16\n" " 16 bit PIC microcontrollers\n" " * mips32\n" " 32 bit MIPS microcontrollers\n" " * native\n" " Type sizes of host system are assumed, but no\n" " further assumptions.\n" " * unspecified\n" " Unknown type sizes\n" " --plist-output=\n" " Generate Clang-plist output files in folder.\n" " -q, --quiet Do not show progress reports.\n" " -rp=, --relative-paths=\n" " Use relative paths in output. When given, are\n" " used as base. You can separate multiple paths by ';'.\n" " Otherwise path where source files are searched is used.\n" " We use string comparison to create relative paths, so\n" " using e.g. ~ for home folder does not work. It is\n" " currently only possible to apply the base paths to\n" " files that are on a lower level in the directory tree.\n" " --report-progress Report progress messages while checking a file.\n" #ifdef HAVE_RULES " --rule= Match regular expression.\n" " --rule-file= Use given rule file. For more information, see:\n" " http://sourceforge.net/projects/cppcheck/files/Articles/\n" #endif " --std= Set standard.\n" " The available options are:\n" " * c89\n" " C code is C89 compatible\n" " * c99\n" " C code is C99 compatible\n" " * c11\n" " C code is C11 compatible (default)\n" " * c++03\n" " C++ code is C++03 compatible\n" " * c++11\n" " C++ code is C++11 compatible\n" " * c++14\n" " C++ code is C++14 compatible\n" " * c++17\n" " C++ code is C++17 compatible\n" " * c++20\n" " C++ code is C++20 compatible (default)\n" " --suppress= Suppress warnings that match . The format of\n" " is:\n" " [error id]:[filename]:[line]\n" " The [filename] and [line] are optional. If [error id]\n" " is a wildcard '*', all error ids match.\n" " --suppressions-list=\n" " Suppress warnings listed in the file. Each suppression\n" " is in the same format as above.\n" " --suppress-xml=\n" " Suppress warnings listed in a xml file. XML file should\n" " follow the manual.pdf format specified in section.\n" " `6.4 XML suppressions` .\n" " --template='' Format the error messages. Available fields:\n" " {file} file name\n" " {line} line number\n" " {column} column number\n" " {callstack} show a callstack. Example:\n" " [file.c:1] -> [file.c:100]\n" " {inconclusive:text} if warning is inconclusive, text\n" " is written\n" " {severity} severity\n" " {message} warning message\n" " {id} warning id\n" " {cwe} CWE id (Common Weakness Enumeration)\n" " {code} show the real code\n" " \\t insert tab\n" " \\n insert newline\n" " \\r insert carriage return\n" " Example formats:\n" " '{file}:{line},{severity},{id},{message}' or\n" " '{file}({line}):({severity}) {message}' or\n" " '{callstack} {message}'\n" " Pre-defined templates: gcc (default), cppcheck1 (old default), vs, edit.\n" // Note: template daca2 also exists, but is for internal use (cppcheck scripts). " --template-location=''\n" " Format error message location. If this is not provided\n" " then no extra location info is shown.\n" " Available fields:\n" " {file} file name\n" " {line} line number\n" " {column} column number\n" " {info} location info\n" " {code} show the real code\n" " \\t insert tab\n" " \\n insert newline\n" " \\r insert carriage return\n" " Example format (gcc-like):\n" " '{file}:{line}:{column}: note: {info}\\n{code}'\n" " -U Undefine preprocessor symbol. Use -U to explicitly\n" " hide certain #ifdef code paths from checking.\n" " Example: '-UDEBUG'\n" " -v, --verbose Output more detailed error information.\n" " --version Print out version number.\n" " --xml Write results in xml format to error stream (stderr).\n" "\n" "Example usage:\n" " # Recursively check the current folder. Print the progress on the screen and\n" " # write errors to a file:\n" " cppcheck . 2> err.txt\n" "\n" " # Recursively check ../myproject/ and don't print progress:\n" " cppcheck --quiet ../myproject/\n" "\n" " # Check test.cpp, enable all checks:\n" " cppcheck --enable=all --inconclusive --library=posix test.cpp\n" "\n" " # Check f.cpp and search include files from inc1/ and inc2/:\n" " cppcheck -I inc1/ -I inc2/ f.cpp\n" "\n" "For more information:\n" " https://cppcheck.sourceforge.io/manual.pdf\n" "\n" "Many thanks to the 3rd party libraries we use:\n" " * tinyxml2 -- loading project/library/ctu files.\n" " * picojson -- loading compile database.\n" " * pcre -- rules.\n" " * qt -- used in GUI\n" " * z3 -- theorem prover from Microsoft Research used in bug hunting.\n"; } cppcheck-2.7/cli/cmdlineparser.h000066400000000000000000000060511417746362400167320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CMDLINE_PARSER_H #define CMDLINE_PARSER_H #include #include class Settings; /// @addtogroup CLI /// @{ /** * @brief The command line parser. * The command line parser parses options and parameters user gives to * cppcheck command line. * * The parser takes a pointer to Settings instance which it will update * based on options user has given. Couple of options are handled as * class internal options. */ class CmdLineParser { public: /** * The constructor. * @param settings Settings instance that will be modified according to * options user has given. */ explicit CmdLineParser(Settings *settings); /** * Parse given command line. * @return true if command line was ok, false if there was an error. */ bool parseFromArgs(int argc, const char* const argv[]); /** * Return if user wanted to see program version. */ bool getShowVersion() const { return mShowVersion; } /** * Return if user wanted to see list of error messages. */ bool getShowErrorMessages() const { return mShowErrorMessages; } /** * Return the path names user gave to command line. */ const std::vector& getPathNames() const { return mPathNames; } /** * Return if help is shown to user. */ bool getShowHelp() const { return mShowHelp; } /** * Return if we should exit after printing version, help etc. */ bool exitAfterPrinting() const { return mExitAfterPrint; } /** * Return a list of paths user wants to ignore. */ const std::vector& getIgnoredPaths() const { return mIgnoredPaths; } protected: /** * Print help text to the console. */ static void printHelp(); /** * Print message (to stdout). */ static void printMessage(const std::string &message); /** * Print error message (to stdout). */ static void printError(const std::string &message); private: std::vector mPathNames; std::vector mIgnoredPaths; Settings *mSettings; bool mShowHelp; bool mShowVersion; bool mShowErrorMessages; bool mExitAfterPrint; std::string mVSConfig; }; /// @} #endif // CMDLINE_PARSER_H cppcheck-2.7/cli/cppcheckexecutor.cpp000066400000000000000000001321131417746362400177730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppcheckexecutor.h" #include "analyzerinfo.h" #include "cmdlineparser.h" #include "color.h" #include "config.h" #include "cppcheck.h" #include "errortypes.h" #include "filelister.h" #include "importproject.h" #include "library.h" #include "path.h" #include "pathmatch.h" #include "preprocessor.h" #include "settings.h" #include "suppressions.h" #include "threadexecutor.h" #include "utils.h" #include "checkunusedfunctions.h" #include #include #include #include #include // EXIT_SUCCESS and EXIT_FAILURE #include #include #include #include #include #include #include #include #if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__) #define USE_UNIX_SIGNAL_HANDLING #include #if defined(__APPLE__) # define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined # include # undef _XOPEN_SOURCE #elif !defined(__OpenBSD__) && !defined(__HAIKU__) # include #endif #ifdef __linux__ #include #endif #endif #if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(USE_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__) #define USE_UNIX_BACKTRACE_SUPPORT #include #include #endif #if defined(_WIN32) #if defined(_MSC_VER) #define USE_WINDOWS_SEH #endif #if defined (__MINGW32__) # include # include # include #else # include # include # include #endif #include #endif /*static*/ FILE* CppCheckExecutor::mExceptionOutput = stdout; CppCheckExecutor::CppCheckExecutor() : mSettings(nullptr), mLatestProgressOutputTime(0), mErrorOutput(nullptr), mBugHuntingReport(nullptr), mShowAllErrors(false) {} CppCheckExecutor::~CppCheckExecutor() { delete mErrorOutput; delete mBugHuntingReport; } bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[]) { Settings& settings = cppcheck->settings(); CmdLineParser parser(&settings); const bool success = parser.parseFromArgs(argc, argv); if (success) { if (parser.getShowVersion() && !parser.getShowErrorMessages()) { const char * const extraVersion = CppCheck::extraVersion(); if (*extraVersion != 0) std::cout << "Cppcheck " << CppCheck::version() << " (" << extraVersion << ')' << std::endl; else std::cout << "Cppcheck " << CppCheck::version() << std::endl; } if (parser.getShowErrorMessages()) { mShowAllErrors = true; std::cout << ErrorMessage::getXMLHeader(); cppcheck->getErrorMessages(); std::cout << ErrorMessage::getXMLFooter() << std::endl; } if (parser.exitAfterPrinting()) { Settings::terminate(); return true; } } else { return false; } // Check that all include paths exist { for (std::list::iterator iter = settings.includePaths.begin(); iter != settings.includePaths.end(); ) { const std::string path(Path::toNativeSeparators(*iter)); if (FileLister::isDirectory(path)) ++iter; else { // If the include path is not found, warn user and remove the non-existing path from the list. if (settings.severity.isEnabled(Severity::information)) std::cout << "(information) Couldn't find path given by -I '" << path << '\'' << std::endl; iter = settings.includePaths.erase(iter); } } } // Output a warning for the user if he tries to exclude headers bool warn = false; const std::vector& ignored = parser.getIgnoredPaths(); for (const std::string &i : ignored) { if (Path::isHeader(i)) { warn = true; break; } } if (warn) { std::cout << "cppcheck: filename exclusion does not apply to header (.h and .hpp) files." << std::endl; std::cout << "cppcheck: Please use --suppress for ignoring results from the header files." << std::endl; } const std::vector& pathnames = parser.getPathNames(); #if defined(_WIN32) // For Windows we want case-insensitive path matching const bool caseSensitive = false; #else const bool caseSensitive = true; #endif if (!mSettings->project.fileSettings.empty() && !mSettings->fileFilters.empty()) { // filter only for the selected filenames from all project files std::list newList; for (const ImportProject::FileSettings &fsetting : settings.project.fileSettings) { if (matchglobs(mSettings->fileFilters, fsetting.filename)) { newList.emplace_back(fsetting); } } if (!newList.empty()) settings.project.fileSettings = newList; else { std::cout << "cppcheck: error: could not find any files matching the filter." << std::endl; return false; } } else if (!pathnames.empty()) { // Execute recursiveAddFiles() to each given file parameter const PathMatch matcher(ignored, caseSensitive); for (const std::string &pathname : pathnames) { std::string err = FileLister::recursiveAddFiles(mFiles, Path::toNativeSeparators(pathname), mSettings->library.markupExtensions(), matcher); if (!err.empty()) { std::cout << "cppcheck: " << err << std::endl; } } } if (mFiles.empty() && settings.project.fileSettings.empty()) { std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl; if (!ignored.empty()) std::cout << "cppcheck: Maybe all paths were ignored?" << std::endl; return false; } else if (!mSettings->fileFilters.empty() && settings.project.fileSettings.empty()) { std::map newMap; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) if (matchglobs(mSettings->fileFilters, i->first)) { newMap[i->first] = i->second; } mFiles = newMap; if (mFiles.empty()) { std::cout << "cppcheck: error: could not find any files matching the filter." << std::endl; return false; } } return true; } int CppCheckExecutor::check(int argc, const char* const argv[]) { Preprocessor::missingIncludeFlag = false; Preprocessor::missingSystemIncludeFlag = false; CheckUnusedFunctions::clear(); CppCheck cppCheck(*this, true, executeCommand); const Settings& settings = cppCheck.settings(); mSettings = &settings; if (!parseFromArgs(&cppCheck, argc, argv)) { mSettings = nullptr; return EXIT_FAILURE; } if (Settings::terminated()) { mSettings = nullptr; return EXIT_SUCCESS; } int ret; if (cppCheck.settings().exceptionHandling) ret = check_wrapper(cppCheck, argc, argv); else ret = check_internal(cppCheck, argc, argv); mSettings = nullptr; return ret; } void CppCheckExecutor::setSettings(const Settings &settings) { mSettings = &settings; } /** * Simple helper function: * \return size of array * */ template std::size_t getArrayLength(const T (&)[size]) { return size; } #if defined(USE_UNIX_SIGNAL_HANDLING) /* * Try to print the callstack. * That is very sensitive to the operating system, hardware, compiler and runtime. * The code is not meant for production environment! * One reason is named first: it's using functions not whitelisted for usage in a signal handler function. */ static void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem) { #if defined(USE_UNIX_BACKTRACE_SUPPORT) // 32 vs. 64bit #define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8) const int fd = fileno(output); void *callstackArray[32]= {nullptr}; // the less resources the better... const int currentdepth = backtrace(callstackArray, (int)getArrayLength(callstackArray)); const int offset=2; // some entries on top are within our own exception handling code or libc if (maxdepth<0) maxdepth=currentdepth-offset; else maxdepth = std::min(maxdepth, currentdepth); if (lowMem) { fputs("Callstack (symbols only):\n", output); backtrace_symbols_fd(callstackArray+offset, maxdepth, fd); } else { char **symbolStringList = backtrace_symbols(callstackArray, currentdepth); if (symbolStringList) { fputs("Callstack:\n", output); char demangle_buffer[2048]= {0}; for (int i = offset; i < maxdepth; ++i) { const char * const symbolString = symbolStringList[i]; char * realnameString = nullptr; const char * const firstBracketName = strchr(symbolString, '('); const char * const firstBracketAddress = strchr(symbolString, '['); const char * const secondBracketAddress = strchr(firstBracketAddress, ']'); const char * const beginAddress = firstBracketAddress+3; const int addressLen = int(secondBracketAddress-beginAddress); const int padLen = int(ADDRESSDISPLAYLENGTH-addressLen); if (demangling && firstBracketName) { const char * const plus = strchr(firstBracketName, '+'); if (plus && (plus>(firstBracketName+1))) { char input_buffer[1024]= {0}; strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1); size_t length = getArrayLength(demangle_buffer); int status=0; // We're violating the specification - passing stack address instead of malloc'ed heap. // Benefit is that no further heap is required, while there is sufficient stack... realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success } } const int ordinal=i-offset; fprintf(output, "#%-2d 0x", ordinal); if (padLen>0) fprintf(output, "%0*d", padLen, 0); if (realnameString) { fprintf(output, "%.*s in %s\n", (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, realnameString); } else { fprintf(output, "%.*s in %.*s\n", (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, (int)(firstBracketAddress-symbolString), symbolString); } } free(symbolStringList); } else { fputs("Callstack could not be obtained\n", output); } } #undef ADDRESSDISPLAYLENGTH #else UNUSED(output); UNUSED(demangling); UNUSED(maxdepth); UNUSED(lowMem); #endif } #ifdef __USE_DYNAMIC_STACK_SIZE static const size_t MYSTACKSIZE = 16*1024+32768; // wild guess about a reasonable buffer #else static const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer #endif static char mytstack[MYSTACKSIZE]= {0}; // alternative stack for signal handler static bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper() /** * \param[in] ptr address to be examined. * \return true if address is supposed to be on stack (contrary to heap). If ptr is 0 false will be returned. * If unknown better return false. */ static bool IsAddressOnStack(const void* ptr) { if (nullptr==ptr) return false; char a; if (bStackBelowHeap) return ptr < &a; else return ptr > &a; } /* (declare this list here, so it may be used in signal handlers in addition to main()) * A list of signals available in ISO C * Check out http://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html * For now we only want to detect abnormal behaviour for a few selected signals: */ #define DECLARE_SIGNAL(x) std::make_pair(x, #x) using Signalmap_t = std::map; static const Signalmap_t listofsignals = { DECLARE_SIGNAL(SIGABRT), DECLARE_SIGNAL(SIGBUS), DECLARE_SIGNAL(SIGFPE), DECLARE_SIGNAL(SIGILL), DECLARE_SIGNAL(SIGINT), DECLARE_SIGNAL(SIGQUIT), DECLARE_SIGNAL(SIGSEGV), DECLARE_SIGNAL(SIGSYS), // don't care: SIGTERM DECLARE_SIGNAL(SIGUSR1), //DECLARE_SIGNAL(SIGUSR2) no usage currently }; #undef DECLARE_SIGNAL /* * Entry pointer for signal handlers * It uses functions which are not safe to be called from a signal handler, * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 has a whitelist) * but when ending up here something went terribly wrong anyway. * And all which is left is just printing some information and terminate. */ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context) { int type = -1; pid_t killid; #if defined(__linux__) && defined(REG_ERR) const ucontext_t* const uc = reinterpret_cast(context); killid = (pid_t) syscall(SYS_gettid); if (uc) { type = (int)uc->uc_mcontext.gregs[REG_ERR] & 2; } #else UNUSED(context); killid = getpid(); #endif const Signalmap_t::const_iterator it=listofsignals.find(signo); const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str(); bool printCallstack=true; // try to print a callstack? bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then. bool unexpectedSignal=true; // unexpected indicates program failure bool terminate=true; // exit process/thread const bool isAddressOnStack = IsAddressOnStack(info->si_addr); FILE* output = CppCheckExecutor::getExceptionOutput(); switch (signo) { case SIGABRT: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); fputs( #ifdef NDEBUG " - out of memory?\n", #else " - out of memory or assertion?\n", #endif output); lowMem=true; // educated guess break; case SIGBUS: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case BUS_ADRALN: // invalid address alignment fputs(" - BUS_ADRALN", output); break; case BUS_ADRERR: // nonexistent physical address fputs(" - BUS_ADRERR", output); break; case BUS_OBJERR: // object-specific hardware error fputs(" - BUS_OBJERR", output); break; #ifdef BUS_MCEERR_AR case BUS_MCEERR_AR: // Hardware memory error consumed on a machine check; fputs(" - BUS_MCEERR_AR", output); break; #endif #ifdef BUS_MCEERR_AO case BUS_MCEERR_AO: // Hardware memory error detected in process but not consumed fputs(" - BUS_MCEERR_AO", output); break; #endif default: break; } fprintf(output, " (at 0x%lx).\n", (unsigned long)info->si_addr); break; case SIGFPE: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case FPE_INTDIV: // integer divide by zero fputs(" - FPE_INTDIV", output); break; case FPE_INTOVF: // integer overflow fputs(" - FPE_INTOVF", output); break; case FPE_FLTDIV: // floating-point divide by zero fputs(" - FPE_FLTDIV", output); break; case FPE_FLTOVF: // floating-point overflow fputs(" - FPE_FLTOVF", output); break; case FPE_FLTUND: // floating-point underflow fputs(" - FPE_FLTUND", output); break; case FPE_FLTRES: // floating-point inexact result fputs(" - FPE_FLTRES", output); break; case FPE_FLTINV: // floating-point invalid operation fputs(" - FPE_FLTINV", output); break; case FPE_FLTSUB: // subscript out of range fputs(" - FPE_FLTSUB", output); break; default: break; } fprintf(output, " (at 0x%lx).\n", (unsigned long)info->si_addr); break; case SIGILL: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case ILL_ILLOPC: // illegal opcode fputs(" - ILL_ILLOPC", output); break; case ILL_ILLOPN: // illegal operand fputs(" - ILL_ILLOPN", output); break; case ILL_ILLADR: // illegal addressing mode fputs(" - ILL_ILLADR", output); break; case ILL_ILLTRP: // illegal trap fputs(" - ILL_ILLTRP", output); break; case ILL_PRVOPC: // privileged opcode fputs(" - ILL_PRVOPC", output); break; case ILL_PRVREG: // privileged register fputs(" - ILL_PRVREG", output); break; case ILL_COPROC: // coprocessor error fputs(" - ILL_COPROC", output); break; case ILL_BADSTK: // internal stack error fputs(" - ILL_BADSTK", output); break; default: break; } fprintf(output, " (at 0x%lx).%s\n", (unsigned long)info->si_addr, (isAddressOnStack)?" Stackoverflow?":""); break; case SIGINT: unexpectedSignal=false; // legal usage: interrupt application via CTRL-C fputs("cppcheck received signal ", output); fputs(signame, output); printCallstack=true; fputs(".\n", output); break; case SIGSEGV: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case SEGV_MAPERR: // address not mapped to object fputs(" - SEGV_MAPERR", output); break; case SEGV_ACCERR: // invalid permissions for mapped object fputs(" - SEGV_ACCERR", output); break; default: break; } fprintf(output, " (%sat 0x%lx).%s\n", // cppcheck-suppress knownConditionTrueFalse ; FP (type==-1)? "" : (type==0) ? "reading " : "writing ", (unsigned long)info->si_addr, (isAddressOnStack)?" Stackoverflow?":"" ); break; case SIGUSR1: fputs("cppcheck received signal ", output); fputs(signame, output); fputs(".\n", output); terminate=false; break; default: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); fputs(".\n", output); break; } if (printCallstack) { print_stacktrace(output, true, -1, lowMem); } if (unexpectedSignal) { fputs("\nPlease report this to the cppcheck developers!\n", output); } fflush(output); if (terminate) { // now let things proceed, shutdown and hopefully dump core for post-mortem analysis struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler=SIG_DFL; sigaction(signo, &act, nullptr); kill(killid, signo); } } #endif #ifdef USE_WINDOWS_SEH namespace { const ULONG maxnamelength = 512; struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 { TCHAR nameExt[maxnamelength]; // actually no need to worry about character encoding here }; typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64); fpStackWalk64 pStackWalk64; typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64); fpSymGetModuleBase64 pSymGetModuleBase64; typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); fpSymGetSymFromAddr64 pSymGetSymFromAddr64; typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64); fpSymGetLineFromAddr64 pSymGetLineFromAddr64; typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD); fpUnDecorateSymbolName pUnDecorateSymbolName; typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64); fpSymFunctionTableAccess64 pSymFunctionTableAccess64; typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL); fpSymInitialize pSymInitialize; HMODULE hLibDbgHelp; // avoid explicit dependency on Dbghelp.dll bool loadDbgHelp() { hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll"); if (!hLibDbgHelp) return false; pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64"); pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64"); pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64"); pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64"); pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64"); pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize"); pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName"); return true; } void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex) { if (!loadDbgHelp()) return; const HANDLE hProcess = GetCurrentProcess(); const HANDLE hThread = GetCurrentThread(); pSymInitialize( hProcess, nullptr, TRUE ); CONTEXT context = *(ex->ContextRecord); STACKFRAME64 stack= {0}; #ifdef _M_IX86 stack.AddrPC.Offset = context.Eip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = context.Esp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = context.Ebp; stack.AddrFrame.Mode = AddrModeFlat; #else stack.AddrPC.Offset = context.Rip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = context.Rsp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = context.Rsp; stack.AddrFrame.Mode = AddrModeFlat; #endif IMAGEHLP_SYMBOL64_EXT symbol; symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); symbol.MaxNameLength = maxnamelength; DWORD64 displacement = 0; int beyond_main=-1; // emergency exit, see below for (ULONG frame = 0; ; frame++) { BOOL result = pStackWalk64 ( #ifdef _M_IX86 IMAGE_FILE_MACHINE_I386, #else IMAGE_FILE_MACHINE_AMD64, #endif hProcess, hThread, &stack, &context, nullptr, pSymFunctionTableAccess64, pSymGetModuleBase64, nullptr ); if (!result) // official end... break; pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol); TCHAR undname[maxnamelength]= {0}; pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE); if (beyond_main>=0) ++beyond_main; if (_tcscmp(undname, _T("main"))==0) beyond_main=0; fprintf(outputFile, "%lu. 0x%08I64X in ", frame, (ULONG64)stack.AddrPC.Offset); fputs((const char *)undname, outputFile); fputc('\n', outputFile); if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end... break; } FreeLibrary(hLibDbgHelp); hLibDbgHelp=nullptr; } void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description) { fputs(description, outputFile); fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress); // Using %p for ULONG_PTR later on, so it must have size identical to size of pointer // This is not the universally portable solution but good enough for Win32/64 C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1])); switch (ex->ExceptionRecord->ExceptionInformation[0]) { case 0: fprintf(outputFile, "reading from 0x%p", reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); break; case 1: fprintf(outputFile, "writing to 0x%p", reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); break; case 8: fprintf(outputFile, "data execution prevention at 0x%p", reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); break; default: break; } } /* * Any evaluation of the exception needs to be done here! */ int filterException(int code, PEXCEPTION_POINTERS ex) { FILE *outputFile = stdout; fputs("Internal error: ", outputFile); switch (ex->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: writeMemoryErrorDetails(outputFile, ex, "Access violation"); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: fputs("Out of array bounds", outputFile); break; case EXCEPTION_BREAKPOINT: fputs("Breakpoint", outputFile); break; case EXCEPTION_DATATYPE_MISALIGNMENT: fputs("Misaligned data", outputFile); break; case EXCEPTION_FLT_DENORMAL_OPERAND: fputs("Denormalized floating-point value", outputFile); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: fputs("Floating-point divide-by-zero", outputFile); break; case EXCEPTION_FLT_INEXACT_RESULT: fputs("Inexact floating-point value", outputFile); break; case EXCEPTION_FLT_INVALID_OPERATION: fputs("Invalid floating-point operation", outputFile); break; case EXCEPTION_FLT_OVERFLOW: fputs("Floating-point overflow", outputFile); break; case EXCEPTION_FLT_STACK_CHECK: fputs("Floating-point stack overflow", outputFile); break; case EXCEPTION_FLT_UNDERFLOW: fputs("Floating-point underflow", outputFile); break; case EXCEPTION_GUARD_PAGE: fputs("Page-guard access", outputFile); break; case EXCEPTION_ILLEGAL_INSTRUCTION: fputs("Illegal instruction", outputFile); break; case EXCEPTION_IN_PAGE_ERROR: writeMemoryErrorDetails(outputFile, ex, "Invalid page access"); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: fputs("Integer divide-by-zero", outputFile); break; case EXCEPTION_INT_OVERFLOW: fputs("Integer overflow", outputFile); break; case EXCEPTION_INVALID_DISPOSITION: fputs("Invalid exception dispatcher", outputFile); break; case EXCEPTION_INVALID_HANDLE: fputs("Invalid handle", outputFile); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: fputs("Non-continuable exception", outputFile); break; case EXCEPTION_PRIV_INSTRUCTION: fputs("Invalid instruction", outputFile); break; case EXCEPTION_SINGLE_STEP: fputs("Single instruction step", outputFile); break; case EXCEPTION_STACK_OVERFLOW: fputs("Stack overflow", outputFile); break; default: fprintf(outputFile, "Unknown exception (%d)\n", code); break; } fputc('\n', outputFile); printCallstack(outputFile, ex); fflush(outputFile); return EXCEPTION_EXECUTE_HANDLER; } } #endif /** * Signal/SEH handling * Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed! * TODO Check for multi-threading issues! * */ int CppCheckExecutor::check_wrapper(CppCheck& cppcheck, int argc, const char* const argv[]) { #ifdef USE_WINDOWS_SEH FILE *outputFile = stdout; __try { return check_internal(cppcheck, argc, argv); } __except (filterException(GetExceptionCode(), GetExceptionInformation())) { // reporting to stdout may not be helpful within a GUI application... fputs("Please report this to the cppcheck developers!\n", outputFile); return -1; } #elif defined(USE_UNIX_SIGNAL_HANDLING) // determine stack vs. heap char stackVariable; char *heapVariable=(char*)malloc(1); bStackBelowHeap = &stackVariable < heapVariable; free(heapVariable); // set up alternative stack for signal handler stack_t segv_stack; segv_stack.ss_sp = mytstack; segv_stack.ss_flags = 0; segv_stack.ss_size = MYSTACKSIZE; sigaltstack(&segv_stack, nullptr); // install signal handler struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_flags=SA_SIGINFO|SA_ONSTACK; act.sa_sigaction=CppcheckSignalHandler; for (std::map::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) { sigaction(sig->first, &act, nullptr); } return check_internal(cppcheck, argc, argv); #else return check_internal(cppcheck, argc, argv); #endif } /* * That is a method which gets called from check_wrapper * */ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const char* const argv[]) { Settings& settings = cppcheck.settings(); mSettings = &settings; const bool std = tryLoadLibrary(settings.library, argv[0], "std.cfg"); for (const std::string &lib : settings.libraries) { if (!tryLoadLibrary(settings.library, argv[0], lib.c_str())) { const std::string msg("Failed to load the library " + lib); const std::list callstack; ErrorMessage errmsg(callstack, emptyString, Severity::information, msg, "failedToLoadCfg", Certainty::normal); reportErr(errmsg); return EXIT_FAILURE; } } bool posix = true; if (settings.posix()) posix = tryLoadLibrary(settings.library, argv[0], "posix.cfg"); bool windows = true; if (settings.isWindowsPlatform()) windows = tryLoadLibrary(settings.library, argv[0], "windows.cfg"); if (!std || !posix || !windows) { const std::list callstack; const std::string msg("Failed to load " + std::string(!std ? "std.cfg" : !posix ? "posix.cfg" : "windows.cfg") + ". Your Cppcheck installation is broken, please re-install."); #ifdef FILESDIR const std::string details("The Cppcheck binary was compiled with FILESDIR set to \"" FILESDIR "\" and will therefore search for " "std.cfg in " FILESDIR "/cfg."); #else const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(argv[0])) + "cfg"); const std::string details("The Cppcheck binary was compiled without FILESDIR set. Either the " "std.cfg should be available in " + cfgfolder + " or the FILESDIR " "should be configured."); #endif ErrorMessage errmsg(callstack, emptyString, Severity::information, msg+" "+details, "failedToLoadCfg", Certainty::normal); reportErr(errmsg); return EXIT_FAILURE; } if (settings.reportProgress) mLatestProgressOutputTime = std::time(nullptr); if (!settings.outputFile.empty()) { mErrorOutput = new std::ofstream(settings.outputFile); } if (settings.xml) { reportErr(ErrorMessage::getXMLHeader()); } if (!settings.buildDir.empty()) { settings.loadSummaries(); std::list fileNames; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) fileNames.emplace_back(i->first); AnalyzerInformation::writeFilesTxt(settings.buildDir, fileNames, settings.userDefines, settings.project.fileSettings); } unsigned int returnValue = 0; if (settings.jobs == 1) { // Single process settings.jointSuppressionReport = true; std::size_t totalfilesize = 0; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { totalfilesize += i->second; } std::size_t processedsize = 0; unsigned int c = 0; if (settings.project.fileSettings.empty()) { for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { if (!mSettings->library.markupFile(i->first) || !mSettings->library.processMarkupAfterCode(i->first)) { returnValue += cppcheck.check(i->first); processedsize += i->second; if (!settings.quiet) reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize); c++; } } } else { // filesettings // check all files of the project for (const ImportProject::FileSettings &fs : settings.project.fileSettings) { returnValue += cppcheck.check(fs); ++c; if (!settings.quiet) reportStatus(c, settings.project.fileSettings.size(), c, settings.project.fileSettings.size()); if (settings.clangTidy) cppcheck.analyseClangTidy(fs); } } // second loop to parse all markup files which may not work until all // c/cpp files have been parsed and checked for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { if (mSettings->library.markupFile(i->first) && mSettings->library.processMarkupAfterCode(i->first)) { returnValue += cppcheck.check(i->first); processedsize += i->second; if (!settings.quiet) reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize); c++; } } if (cppcheck.analyseWholeProgram()) returnValue++; } else if (!ThreadExecutor::isEnabled()) { std::cout << "No thread support yet implemented for this platform." << std::endl; } else { // Multiple processes ThreadExecutor executor(mFiles, settings, *this); returnValue = executor.check(); } cppcheck.analyseWholeProgram(mSettings->buildDir, mFiles); if (settings.severity.isEnabled(Severity::information) || settings.checkConfiguration) { const bool enableUnusedFunctionCheck = cppcheck.isUnusedFunctionCheckEnabled(); if (settings.jointSuppressionReport) { for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { const bool err = reportUnmatchedSuppressions(settings.nomsg.getUnmatchedLocalSuppressions(i->first, enableUnusedFunctionCheck)); if (err && returnValue == 0) returnValue = settings.exitCode; } } const bool err = reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions(enableUnusedFunctionCheck)); if (err && returnValue == 0) returnValue = settings.exitCode; } if (!settings.checkConfiguration) { cppcheck.tooManyConfigsError("",0U); if (settings.checks.isEnabled(Checks::missingInclude) && (Preprocessor::missingIncludeFlag || Preprocessor::missingSystemIncludeFlag)) { const std::list callStack; ErrorMessage msg(callStack, emptyString, Severity::information, "Cppcheck cannot find all the include files (use --check-config for details)\n" "Cppcheck cannot find all the include files. Cppcheck can check the code without the " "include files found. But the results will probably be more accurate if all the include " "files are found. Please check your project's include directories and add all of them " "as include directories for Cppcheck. To see what files Cppcheck cannot find use " "--check-config.", Preprocessor::missingIncludeFlag ? "missingInclude" : "missingIncludeSystem", Certainty::normal); reportInfo(msg); } } if (settings.xml) { reportErr(ErrorMessage::getXMLFooter()); } mSettings = nullptr; if (returnValue) return settings.exitCode; return 0; } #ifdef _WIN32 // fix trac ticket #439 'Cppcheck reports wrong filename for filenames containing 8-bit ASCII' static inline std::string ansiToOEM(const std::string &msg, bool doConvert) { if (doConvert) { const unsigned msglength = msg.length(); // convert ANSI strings to OEM strings in two steps std::vector wcContainer(msglength); std::string result(msglength, '\0'); // ansi code page characters to wide characters MultiByteToWideChar(CP_ACP, 0, msg.data(), msglength, wcContainer.data(), msglength); // wide characters to oem codepage characters WideCharToMultiByte(CP_OEMCP, 0, wcContainer.data(), msglength, const_cast(result.data()), msglength, nullptr, nullptr); return result; // hope for return value optimization } return msg; } #else // no performance regression on non-windows systems #define ansiToOEM(msg, doConvert) (msg) #endif void CppCheckExecutor::reportErr(const std::string &errmsg) { if (mErrorOutput) *mErrorOutput << errmsg << std::endl; else { std::cerr << ansiToOEM(errmsg, (mSettings == nullptr) ? true : !mSettings->xml) << std::endl; } } void CppCheckExecutor::reportOut(const std::string &outmsg, Color c) { std::cout << c << ansiToOEM(outmsg, true) << Color::Reset << std::endl; } void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value) { (void)filename; if (!mLatestProgressOutputTime) return; // Report progress messages every 10 seconds const std::time_t currentTime = std::time(nullptr); if (currentTime >= (mLatestProgressOutputTime + 10)) { mLatestProgressOutputTime = currentTime; // format a progress message std::ostringstream ostr; ostr << "progress: " << stage << ' ' << value << '%'; // Report progress message reportOut(ostr.str()); } } void CppCheckExecutor::reportInfo(const ErrorMessage &msg) { reportErr(msg); } void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal) { if (filecount > 1) { std::ostringstream oss; const long percentDone = (sizetotal > 0) ? static_cast(static_cast(sizedone) / sizetotal * 100) : 0; oss << fileindex << '/' << filecount << " files checked " << percentDone << "% done"; std::cout << Color::FgBlue << oss.str() << Color::Reset << std::endl; } } void CppCheckExecutor::reportErr(const ErrorMessage &msg) { if (mShowAllErrors) { reportOut(msg.toXML()); return; } // Alert only about unique errors if (!mShownErrors.insert(msg.toString(mSettings->verbose)).second) return; if (mSettings->xml) reportErr(msg.toXML()); else reportErr(msg.toString(mSettings->verbose, mSettings->templateFormat, mSettings->templateLocation)); } void CppCheckExecutor::bughuntingReport(const std::string &str) { if (!mSettings || str.empty()) return; if (!mBugHuntingReport) mBugHuntingReport = new std::ofstream(mSettings->bugHuntingReport); (*mBugHuntingReport) << str << std::endl; } void CppCheckExecutor::setExceptionOutput(FILE* exceptionOutput) { mExceptionOutput = exceptionOutput; } FILE* CppCheckExecutor::getExceptionOutput() { return mExceptionOutput; } bool CppCheckExecutor::tryLoadLibrary(Library& destination, const char* basepath, const char* filename) { const Library::Error err = destination.load(basepath, filename); if (err.errorcode == Library::ErrorCode::UNKNOWN_ELEMENT) std::cout << "cppcheck: Found unknown elements in configuration file '" << filename << "': " << err.reason << std::endl; else if (err.errorcode != Library::ErrorCode::OK) { std::cout << "cppcheck: Failed to load library configuration file '" << filename << "'. "; switch (err.errorcode) { case Library::ErrorCode::OK: break; case Library::ErrorCode::FILE_NOT_FOUND: std::cout << "File not found"; break; case Library::ErrorCode::BAD_XML: std::cout << "Bad XML"; break; case Library::ErrorCode::UNKNOWN_ELEMENT: std::cout << "Unexpected element"; break; case Library::ErrorCode::MISSING_ATTRIBUTE: std::cout << "Missing attribute"; break; case Library::ErrorCode::BAD_ATTRIBUTE_VALUE: std::cout << "Bad attribute value"; break; case Library::ErrorCode::UNSUPPORTED_FORMAT: std::cout << "File is of unsupported format version"; break; case Library::ErrorCode::DUPLICATE_PLATFORM_TYPE: std::cout << "Duplicate platform type"; break; case Library::ErrorCode::PLATFORM_TYPE_REDEFINED: std::cout << "Platform type redefined"; break; } if (!err.reason.empty()) std::cout << " '" + err.reason + "'"; std::cout << std::endl; return false; } return true; } /** * Execute a shell command and read the output from it. Returns true if command terminated successfully. */ // cppcheck-suppress passedByValue - "exe" copy needed in _WIN32 code bool CppCheckExecutor::executeCommand(std::string exe, std::vector args, const std::string &redirect, std::string *output_) { output_->clear(); std::string joinedArgs; for (const std::string &arg : args) { if (!joinedArgs.empty()) joinedArgs += " "; if (arg.find(" ") != std::string::npos) joinedArgs += '"' + arg + '"'; else joinedArgs += arg; } #ifdef _WIN32 // Extra quoutes are needed in windows if filename has space if (exe.find(" ") != std::string::npos) exe = "\"" + exe + "\""; const std::string cmd = exe + " " + joinedArgs + " " + redirect; std::unique_ptr pipe(_popen(cmd.c_str(), "r"), _pclose); #else const std::string cmd = exe + " " + joinedArgs + " " + redirect; std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); #endif if (!pipe) return false; char buffer[1024]; while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) *output_ += buffer; return true; } cppcheck-2.7/cli/cppcheckexecutor.h000066400000000000000000000143261417746362400174450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CPPCHECKEXECUTOR_H #define CPPCHECKEXECUTOR_H #include "color.h" #include "config.h" #include "errorlogger.h" #include #include #include #include #include #include #include class CppCheck; class Library; class Settings; /** * This class works as an example of how CppCheck can be used in external * programs without very little knowledge of the internal parts of the * program itself. If you wish to use cppcheck e.g. as a part of IDE, * just rewrite this class for your needs and possibly use other methods * from CppCheck class instead the ones used here. */ class CppCheckExecutor : public ErrorLogger { public: /** * Constructor */ CppCheckExecutor(); CppCheckExecutor(const CppCheckExecutor &) = delete; void operator=(const CppCheckExecutor&) = delete; /** * Destructor */ ~CppCheckExecutor() OVERRIDE; /** * Starts the checking. * * @param argc from main() * @param argv from main() * @return EXIT_FAILURE if arguments are invalid or no input files * were found. * If errors are found and --error-exitcode is used, * given value is returned instead of default 0. * If no errors are found, 0 is returned. */ int check(int argc, const char* const argv[]); /** * Information about progress is directed here. This should be * called by the CppCheck class only. * * @param outmsg Progress message e.g. "Checking main.cpp..." */ void reportOut(const std::string &outmsg, Color c = Color::Reset) OVERRIDE; /** xml output of errors */ void reportErr(const ErrorMessage &msg) OVERRIDE; void reportProgress(const std::string &filename, const char stage[], const std::size_t value) OVERRIDE; /** * Output information messages. */ void reportInfo(const ErrorMessage &msg) OVERRIDE; void bughuntingReport(const std::string &str) OVERRIDE; /** * Information about how many files have been checked * * @param fileindex This many files have been checked. * @param filecount This many files there are in total. * @param sizedone The sum of sizes of the files checked. * @param sizetotal The total sizes of the files. */ static void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal); /** * @param exceptionOutput Output file */ static void setExceptionOutput(FILE* exceptionOutput); /** * @return file name to be used for output from exception handler. Has to be either "stdout" or "stderr". */ static FILE* getExceptionOutput(); /** * Tries to load a library and prints warning/error messages * @return false, if an error occurred (except unknown XML elements) */ static bool tryLoadLibrary(Library& destination, const char* basepath, const char* filename); /** * Execute a shell command and read the output from it. Returns true if command terminated successfully. */ static bool executeCommand(std::string exe, std::vector args, const std::string &redirect, std::string *output_); protected: /** * Helper function to print out errors. Appends a line change. * @param errmsg String printed to error stream */ void reportErr(const std::string &errmsg); /** * @brief Parse command line args and get settings and file lists * from there. * * @param cppcheck cppcheck instance * @param argc argc from main() * @param argv argv from main() * @return false when errors are found in the input */ bool parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[]); /** * Helper function to supply settings. This can be used for testing. * @param settings Reference to an Settings instance */ void setSettings(const Settings &settings); private: /** * Wrapper around check_internal * - installs optional platform dependent signal handling * * @param cppcheck cppcheck instance * @param argc from main() * @param argv from main() **/ int check_wrapper(CppCheck& cppcheck, int argc, const char* const argv[]); /** * Starts the checking. * * @param cppcheck cppcheck instance * @param argc from main() * @param argv from main() * @return EXIT_FAILURE if arguments are invalid or no input files * were found. * If errors are found and --error-exitcode is used, * given value is returned instead of default 0. * If no errors are found, 0 is returned. */ int check_internal(CppCheck& cppcheck, int argc, const char* const argv[]); /** * Pointer to current settings; set while check() is running. */ const Settings* mSettings; /** * Used to filter out duplicate error messages. */ std::set mShownErrors; /** * Filename associated with size of file */ std::map mFiles; /** * Report progress time */ std::time_t mLatestProgressOutputTime; /** * Output file name for exception handler */ static FILE* mExceptionOutput; /** * Error output */ std::ofstream *mErrorOutput; /** * Bug hunting report */ std::ostream *mBugHuntingReport; /** * Has --errorlist been given? */ bool mShowAllErrors; }; #endif // CPPCHECKEXECUTOR_H cppcheck-2.7/cli/filelister.cpp000066400000000000000000000235501417746362400166020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filelister.h" #include "path.h" #include "pathmatch.h" #include "utils.h" #include #include // fix NAME_MAX not found on macOS GCC8.1 #include #ifdef _WIN32 /////////////////////////////////////////////////////////////////////////////// ////// This code is WIN32 systems ///////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #include #ifndef __BORLANDC__ #include #endif // Here is the catch: cppcheck core is Ansi code (using char type). // When compiling Unicode targets WinAPI automatically uses *W Unicode versions // of called functions. Thus, we explicitly call *A versions of the functions. static BOOL myIsDirectory(const std::string& path) { #ifdef __BORLANDC__ return (GetFileAttributes(path.c_str()) & FILE_ATTRIBUTE_DIRECTORY); #else // See http://msdn.microsoft.com/en-us/library/bb773621(VS.85).aspx return PathIsDirectoryA(path.c_str()); #endif } static HANDLE myFindFirstFile(const std::string& path, LPWIN32_FIND_DATAA findData) { HANDLE hFind = FindFirstFileA(path.c_str(), findData); return hFind; } static BOOL myFileExists(const std::string& path) { #ifdef __BORLANDC__ DWORD fa = GetFileAttributes(path.c_str()); BOOL result = FALSE; if (fa != INVALID_FILE_ATTRIBUTES && !(fa & FILE_ATTRIBUTE_DIRECTORY)) result = TRUE; #else const BOOL result = PathFileExistsA(path.c_str()) && !PathIsDirectoryA(path.c_str()); #endif return result; } std::string FileLister::recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra, const PathMatch& ignored) { return addFiles(files, path, extra, true, ignored); } std::string FileLister::addFiles(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored) { const std::string cleanedPath = Path::toNativeSeparators(path); // basedir is the base directory which is used to form pathnames. // It always has a trailing backslash available for concatenation. std::string basedir; // searchPattern is the search string passed into FindFirst and FindNext. std::string searchPattern = cleanedPath; // The user wants to check all files in a dir const bool checkAllFilesInDir = (myIsDirectory(cleanedPath) != FALSE); if (checkAllFilesInDir) { const char c = cleanedPath.back(); switch (c) { case '\\': searchPattern += '*'; basedir = cleanedPath; break; case '*': basedir = cleanedPath.substr(0, cleanedPath.length() - 1); break; default: searchPattern += "\\*"; if (cleanedPath != ".") basedir = cleanedPath + '\\'; } } else { const std::string::size_type pos = cleanedPath.find_last_of('\\'); if (std::string::npos != pos) { basedir = cleanedPath.substr(0, pos + 1); } } WIN32_FIND_DATAA ffd; HANDLE hFind = myFindFirstFile(searchPattern, &ffd); if (INVALID_HANDLE_VALUE == hFind) return ""; do { if (ffd.cFileName[0] == '.' || ffd.cFileName[0] == '\0') continue; const char* ansiFfd = ffd.cFileName; if (std::strchr(ansiFfd,'?')) { ansiFfd = ffd.cAlternateFileName; } const std::string fname(basedir + ansiFfd); if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // File if ((!checkAllFilesInDir || Path::acceptFile(fname, extra)) && !ignored.match(fname)) { const std::string nativename = Path::fromNativeSeparators(fname); // Limitation: file sizes are assumed to fit in a 'size_t' #ifdef _WIN64 files[nativename] = (static_cast(ffd.nFileSizeHigh) << 32) | ffd.nFileSizeLow; #else files[nativename] = ffd.nFileSizeLow; #endif } } else { // Directory if (recursive) { if (!ignored.match(fname)) { std::string err = FileLister::recursiveAddFiles(files, fname, extra, ignored); if (!err.empty()) return err; } } } } while (FindNextFileA(hFind, &ffd) != FALSE); FindClose(hFind); return ""; } bool FileLister::isDirectory(const std::string &path) { return (myIsDirectory(path) != FALSE); } bool FileLister::fileExists(const std::string &path) { return (myFileExists(path) != FALSE); } #else /////////////////////////////////////////////////////////////////////////////// ////// This code is POSIX-style systems /////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #if defined(__CYGWIN__) #undef __STRICT_ANSI__ #endif #include #include #include #ifndef NAME_MAX #ifdef MAXNAMLEN #define NAME_MAX MAXNAMLEN #endif #endif static std::string addFiles2(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored ) { struct stat file_stat; if (stat(path.c_str(), &file_stat) != -1) { if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { DIR * dir = opendir(path.c_str()); if (!dir) return ""; dirent * dir_result; // make sure we reserve enough space for the readdir_r() buffer; // according to POSIX: // The storage pointed to by entry shall be large enough for a // dirent with an array of char d_name members containing at // least {NAME_MAX}+1 elements. // on some platforms, d_name is not a static sized-array but // a pointer to space usually reserved right after the dirent // struct; the union here allows to reserve the space and to // provide a pointer to the right type that can be passed where // needed without casts union { dirent entry; char buf[sizeof(*dir_result) + (sizeof(dir_result->d_name) > 1 ? 0 : NAME_MAX + 1)]; } dir_result_buffer; UNUSED(dir_result_buffer.buf); // do not trigger cppcheck itself on the "unused buf" std::string new_path; new_path.reserve(path.length() + 100);// prealloc some memory to avoid constant new/deletes in loop while ((readdir_r(dir, &dir_result_buffer.entry, &dir_result) == 0) && (dir_result != nullptr)) { if ((std::strcmp(dir_result->d_name, ".") == 0) || (std::strcmp(dir_result->d_name, "..") == 0)) continue; new_path = path + '/' + dir_result->d_name; #if defined(_DIRENT_HAVE_D_TYPE) || defined(_BSD_SOURCE) bool path_is_directory = (dir_result->d_type == DT_DIR || (dir_result->d_type == DT_UNKNOWN && FileLister::isDirectory(new_path))); #else bool path_is_directory = FileLister::isDirectory(new_path); #endif if (path_is_directory) { if (recursive && !ignored.match(new_path)) { std::string err = addFiles2(files, new_path, extra, recursive, ignored); if (!err.empty()) return err; } } else { if (Path::acceptFile(new_path, extra) && !ignored.match(new_path)) { if (stat(new_path.c_str(), &file_stat) != -1) files[new_path] = file_stat.st_size; else return "Can't stat " + new_path + " errno: " + std::to_string(errno); } } } closedir(dir); } else files[path] = file_stat.st_size; } return ""; } std::string FileLister::recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra, const PathMatch& ignored) { return addFiles(files, path, extra, true, ignored); } std::string FileLister::addFiles(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored) { if (!path.empty()) { std::string corrected_path = path; if (endsWith(corrected_path, '/')) corrected_path.erase(corrected_path.end() - 1); return addFiles2(files, corrected_path, extra, recursive, ignored); } return ""; } bool FileLister::isDirectory(const std::string &path) { struct stat file_stat; return (stat(path.c_str(), &file_stat) != -1 && (file_stat.st_mode & S_IFMT) == S_IFDIR); } bool FileLister::fileExists(const std::string &path) { struct stat file_stat; return (stat(path.c_str(), &file_stat) != -1 && (file_stat.st_mode & S_IFMT) == S_IFREG); } #endif cppcheck-2.7/cli/filelister.h000066400000000000000000000070061417746362400162450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef filelisterH #define filelisterH #include #include #include #include class PathMatch; /// @addtogroup CLI /// @{ /** @brief Cross-platform FileLister */ class FileLister { public: /** * @brief Recursively add source files to a map. * Add source files from given directory and all subdirectries to the * given map. Only files with accepted extensions * (*.c;*.cpp;*.cxx;*.c++;*.cc;*.txx) are added. * @param files output map that associates the size of each file with its name * @param path root path * @param ignored ignored paths * @return On success, an empty string is returned. On error, a error message is returned. */ static std::string recursiveAddFiles(std::map &files, const std::string &path, const PathMatch& ignored) { const std::set extra; return recursiveAddFiles(files, path, extra, ignored); } /** * @brief Recursively add source files to a map. * Add source files from given directory and all subdirectries to the * given map. Only files with accepted extensions * (*.c;*.cpp;*.cxx;*.c++;*.cc;*.txx) are added. * @param files output map that associates the size of each file with its name * @param path root path * @param extra Extra file extensions * @param ignored ignored paths * @return On success, an empty string is returned. On error, a error message is returned. */ static std::string recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra, const PathMatch& ignored); /** * @brief (Recursively) add source files to a map. * Add source files from given directory and all subdirectries to the * given map. Only files with accepted extensions * (*.c;*.cpp;*.cxx;*.c++;*.cc;*.txx) are added. * @param files output map that associates the size of each file with its name * @param path root path * @param extra Extra file extensions * @param recursive Enable recursion * @param ignored ignored paths * @return On success, an empty string is returned. On error, a error message is returned. */ static std::string addFiles(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored); /** * @brief Is given path a directory? * @return returns true if the path is a directory */ static bool isDirectory(const std::string &path); /** * @brief Check if the given path is a file and if it exists? * @return true if path points to file and the file exists. */ static bool fileExists(const std::string &path); }; /// @} #endif // #ifndef filelisterH cppcheck-2.7/cli/main.cpp000066400000000000000000000102421417746362400153560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * * @mainpage Cppcheck * @version 2.7 * * @section overview_sec Overview * Cppcheck is a simple tool for static analysis of C/C++ code. * * When you write a checker you have access to: * - %Token list - the tokenized code * - Syntax tree - Syntax tree of each expression * - %SymbolDatabase - Information about all types/variables/functions/etc * in the current translation unit * - Library - Configuration of functions/types * - Value flow analysis - Data flow analysis that determine possible values for each token * * Use --debug-normal on the command line to see debug output for the token list * and the syntax tree. If both --debug-normal and --verbose is used, the symbol * database is also written. * * The checks are written in C++. * * @section detailed_overview_sec Detailed overview * This happens when you execute cppcheck from the command line: * -# CppCheckExecutor::check this function executes the Cppcheck * -# CmdLineParser::parseFromArgs parse command line arguments * - The Settings class is used to maintain settings * - Use FileLister and command line arguments to get files to check * -# ThreadExecutor create more instances of CppCheck if needed * -# CppCheck::check is called for each file. It checks a single file * -# Preprocess the file (through Preprocessor) * - Comments are removed * - Macros are expanded * -# Tokenize the file (see Tokenizer) * -# Run the runChecks of all check classes. * -# Simplify the tokenlist (Tokenizer::simplifyTokenList2) * -# Run the runSimplifiedChecks of all check classes * * When errors are found, they are reported back to the CppCheckExecutor through the ErrorLogger interface. */ #include "cppcheckexecutor.h" #ifdef NDEBUG #include "errortypes.h" #include #include #include #include #endif #ifdef _WIN32 #include static char exename[1024] = {0}; #endif /** * Main function of cppcheck * * @param argc Passed to CppCheck::parseFromArgs() * @param argv Passed to CppCheck::parseFromArgs() * @return What CppCheckExecutor::check() returns. */ int main(int argc, char* argv[]) { // MS Visual C++ memory leak debug tracing #if defined(_MSC_VER) && defined(_DEBUG) _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); #endif CppCheckExecutor exec; #ifdef _WIN32 GetModuleFileNameA(nullptr, exename, sizeof(exename)/sizeof(exename[0])-1); argv[0] = exename; #endif // *INDENT-OFF* #ifdef NDEBUG try { #endif return exec.check(argc, argv); #ifdef NDEBUG } catch (const InternalError& e) { std::cout << e.errorMessage << std::endl; } catch (const std::exception& error) { std::cout << error.what() << std::endl; } catch (...) { std::cout << "Unknown exception" << std::endl; } return EXIT_FAILURE; #endif // *INDENT-ON* } // Warn about deprecated compilers #ifdef __clang__ # if (__clang_major__ < 2 || (__clang_major__ == 2 && __clang_minor__ < 9)) # warning "Using Clang 2.8 or earlier. Support for this version has been removed." # endif #elif defined(__GNUC__) # if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) # warning "Using GCC 4.5 or earlier. Support for this version has been removed." # endif #elif defined(_MSC_VER) # if (_MSC_VER < 1800) # pragma message("WARNING: Using Visual Studio 2012 or earlier. Support for this version has been removed.") # endif #endif cppcheck-2.7/cli/threadexecutor.cpp000066400000000000000000000440621417746362400174670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "threadexecutor.h" #include "color.h" #include "config.h" #include "cppcheck.h" #include "cppcheckexecutor.h" #include "errortypes.h" #include "importproject.h" #include "settings.h" #include "suppressions.h" #include #include #include #include #include #include #include #ifdef __SVR4 // Solaris #include #endif #ifdef THREADING_MODEL_FORK #if defined(__linux__) #include #endif #include #include #include #include #include #endif #ifdef THREADING_MODEL_WIN #include #include #endif // required for FD_ZERO using std::memset; ThreadExecutor::ThreadExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger) : mFiles(files), mSettings(settings), mErrorLogger(errorLogger), mFileCount(0) { #if defined(THREADING_MODEL_FORK) mWpipe = 0; #elif defined(THREADING_MODEL_WIN) mProcessedFiles = 0; mTotalFiles = 0; mProcessedSize = 0; mTotalFileSize = 0; #endif } ThreadExecutor::~ThreadExecutor() {} // cppcheck-suppress unusedFunction - only used in unit tests void ThreadExecutor::addFileContent(const std::string &path, const std::string &content) { mFileContents[path] = content; } void ThreadExecutor::reportErr(const ErrorMessage &msg) { report(msg, MessageType::REPORT_ERROR); } void ThreadExecutor::reportInfo(const ErrorMessage &msg) { report(msg, MessageType::REPORT_INFO); } /////////////////////////////////////////////////////////////////////////////// ////// This code is for platforms that support fork() only //////////////////// /////////////////////////////////////////////////////////////////////////////// #if defined(THREADING_MODEL_FORK) int ThreadExecutor::handleRead(int rpipe, unsigned int &result) { char type = 0; if (read(rpipe, &type, 1) <= 0) { if (errno == EAGAIN) return 0; // need to increment so a missing pipe (i.e. premature exit of forked process) results in an error exitcode ++result; return -1; } if (type != REPORT_OUT && type != REPORT_ERROR && type != REPORT_INFO && type != CHILD_END) { std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(EXIT_FAILURE); } unsigned int len = 0; if (read(rpipe, &len, sizeof(len)) <= 0) { std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(EXIT_FAILURE); } // Don't rely on incoming data being null-terminated. // Allocate +1 element and null-terminate the buffer. char *buf = new char[len + 1]; const ssize_t readIntoBuf = read(rpipe, buf, len); if (readIntoBuf <= 0) { std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(EXIT_FAILURE); } buf[readIntoBuf] = 0; if (type == REPORT_OUT) { mErrorLogger.reportOut(buf); } else if (type == REPORT_ERROR || type == REPORT_INFO) { ErrorMessage msg; try { msg.deserialize(buf); } catch (const InternalError& e) { std::cerr << "#### ThreadExecutor::handleRead error, internal error:" << e.errorMessage << std::endl; std::exit(EXIT_FAILURE); } if (!mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) { // Alert only about unique errors std::string errmsg = msg.toString(mSettings.verbose); if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) { mErrorList.emplace_back(errmsg); if (type == REPORT_ERROR) mErrorLogger.reportErr(msg); else mErrorLogger.reportInfo(msg); } } } else if (type == CHILD_END) { std::istringstream iss(buf); unsigned int fileResult = 0; iss >> fileResult; result += fileResult; delete[] buf; return -1; } delete[] buf; return 1; } bool ThreadExecutor::checkLoadAverage(size_t nchildren) { #if defined(__CYGWIN__) || defined(__QNX__) || defined(__HAIKU__) // getloadavg() is unsupported on Cygwin, Qnx, Haiku. return true; #else if (!nchildren || !mSettings.loadAverage) { return true; } double sample(0); if (getloadavg(&sample, 1) != 1) { // disable load average checking on getloadavg error return true; } else if (sample < mSettings.loadAverage) { return true; } return false; #endif } unsigned int ThreadExecutor::check() { mFileCount = 0; unsigned int result = 0; std::size_t totalfilesize = 0; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { totalfilesize += i->second; } std::list rpipes; std::map childFile; std::map pipeFile; std::size_t processedsize = 0; std::map::const_iterator iFile = mFiles.begin(); std::list::const_iterator iFileSettings = mSettings.project.fileSettings.begin(); for (;;) { // Start a new child size_t nchildren = childFile.size(); if ((iFile != mFiles.end() || iFileSettings != mSettings.project.fileSettings.end()) && nchildren < mSettings.jobs && checkLoadAverage(nchildren)) { int pipes[2]; if (pipe(pipes) == -1) { std::cerr << "#### ThreadExecutor::check, pipe() failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } int flags = 0; if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0) { std::cerr << "#### ThreadExecutor::check, fcntl(F_GETFL) failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) { std::cerr << "#### ThreadExecutor::check, fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid < 0) { // Error std::cerr << "#### ThreadExecutor::check, Failed to create child process: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } else if (pid == 0) { #if defined(__linux__) prctl(PR_SET_PDEATHSIG, SIGHUP); #endif close(pipes[0]); mWpipe = pipes[1]; CppCheck fileChecker(*this, false, CppCheckExecutor::executeCommand); fileChecker.settings() = mSettings; unsigned int resultOfCheck = 0; if (iFileSettings != mSettings.project.fileSettings.end()) { resultOfCheck = fileChecker.check(*iFileSettings); } else if (!mFileContents.empty() && mFileContents.find(iFile->first) != mFileContents.end()) { // File content was given as a string resultOfCheck = fileChecker.check(iFile->first, mFileContents[iFile->first]); } else { // Read file from a file resultOfCheck = fileChecker.check(iFile->first); } std::ostringstream oss; oss << resultOfCheck; writeToPipe(CHILD_END, oss.str()); std::exit(EXIT_SUCCESS); } close(pipes[1]); rpipes.push_back(pipes[0]); if (iFileSettings != mSettings.project.fileSettings.end()) { childFile[pid] = iFileSettings->filename + ' ' + iFileSettings->cfg; pipeFile[pipes[0]] = iFileSettings->filename + ' ' + iFileSettings->cfg; ++iFileSettings; } else { childFile[pid] = iFile->first; pipeFile[pipes[0]] = iFile->first; ++iFile; } } if (!rpipes.empty()) { fd_set rfds; FD_ZERO(&rfds); for (std::list::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp) FD_SET(*rp, &rfds); struct timeval tv; // for every second polling of load average condition tv.tv_sec = 1; tv.tv_usec = 0; int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, nullptr, nullptr, &tv); if (r > 0) { std::list::iterator rp = rpipes.begin(); while (rp != rpipes.end()) { if (FD_ISSET(*rp, &rfds)) { int readRes = handleRead(*rp, result); if (readRes == -1) { std::size_t size = 0; std::map::iterator p = pipeFile.find(*rp); if (p != pipeFile.end()) { std::string name = p->second; pipeFile.erase(p); std::map::const_iterator fs = mFiles.find(name); if (fs != mFiles.end()) { size = fs->second; } } mFileCount++; processedsize += size; if (!mSettings.quiet) CppCheckExecutor::reportStatus(mFileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize); close(*rp); rp = rpipes.erase(rp); } else ++rp; } else ++rp; } } } if (!childFile.empty()) { int stat = 0; pid_t child = waitpid(0, &stat, WNOHANG); if (child > 0) { std::string childname; std::map::iterator c = childFile.find(child); if (c != childFile.end()) { childname = c->second; childFile.erase(c); } if (WIFEXITED(stat)) { const int exitstatus = WEXITSTATUS(stat); if (exitstatus != EXIT_SUCCESS) { std::ostringstream oss; oss << "Child process exited with " << exitstatus; reportInternalChildErr(childname, oss.str()); } } else if (WIFSIGNALED(stat)) { std::ostringstream oss; oss << "Child process crashed with signal " << WTERMSIG(stat); reportInternalChildErr(childname, oss.str()); } } } if (iFile == mFiles.end() && iFileSettings == mSettings.project.fileSettings.end() && rpipes.empty() && childFile.empty()) { // All done break; } } return result; } void ThreadExecutor::writeToPipe(PipeSignal type, const std::string &data) { unsigned int len = static_cast(data.length() + 1); char *out = new char[len + 1 + sizeof(len)]; out[0] = static_cast(type); std::memcpy(&(out[1]), &len, sizeof(len)); std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len); if (write(mWpipe, out, len + 1 + sizeof(len)) <= 0) { delete[] out; out = nullptr; std::cerr << "#### ThreadExecutor::writeToPipe, Failed to write to pipe" << std::endl; std::exit(EXIT_FAILURE); } delete[] out; } void ThreadExecutor::reportOut(const std::string &outmsg, Color c) { writeToPipe(REPORT_OUT, ::toString(c) + outmsg + ::toString(Color::Reset)); } void ThreadExecutor::bughuntingReport(const std::string &str) { writeToPipe(REPORT_VERIFICATION, str); } void ThreadExecutor::report(const ErrorMessage &msg, MessageType msgType) { PipeSignal pipeSignal; switch (msgType) { case MessageType::REPORT_ERROR: pipeSignal = REPORT_ERROR; break; case MessageType::REPORT_INFO: pipeSignal = REPORT_INFO; break; } writeToPipe(pipeSignal, msg.serialize()); } void ThreadExecutor::reportInternalChildErr(const std::string &childname, const std::string &msg) { std::list locations; locations.emplace_back(childname, 0, 0); const ErrorMessage errmsg(locations, emptyString, Severity::error, "Internal error: " + msg, "cppcheckError", Certainty::normal); if (!mSettings.nomsg.isSuppressed(errmsg.toSuppressionsErrorMessage())) mErrorLogger.reportErr(errmsg); } #elif defined(THREADING_MODEL_WIN) unsigned int ThreadExecutor::check() { std::vector> threadFutures; threadFutures.reserve(mSettings.jobs); mItNextFile = mFiles.begin(); mItNextFileSettings = mSettings.project.fileSettings.begin(); mProcessedFiles = 0; mProcessedSize = 0; mTotalFiles = mFiles.size() + mSettings.project.fileSettings.size(); mTotalFileSize = 0; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { mTotalFileSize += i->second; } for (unsigned int i = 0; i < mSettings.jobs; ++i) { try { threadFutures.emplace_back(std::async(std::launch::async, threadProc, this)); } catch (const std::system_error &e) { std::cerr << "#### ThreadExecutor::check exception :" << e.what() << std::endl; exit(EXIT_FAILURE); } } return std::accumulate(threadFutures.begin(), threadFutures.end(), 0U, [](unsigned int v, std::future& f) { return v + f.get(); }); } unsigned int __stdcall ThreadExecutor::threadProc(ThreadExecutor* threadExecutor) { unsigned int result = 0; std::map::const_iterator &itFile = threadExecutor->mItNextFile; std::list::const_iterator &itFileSettings = threadExecutor->mItNextFileSettings; // guard static members of CppCheck against concurrent access threadExecutor->mFileSync.lock(); for (;;) { if (itFile == threadExecutor->mFiles.end() && itFileSettings == threadExecutor->mSettings.project.fileSettings.end()) { threadExecutor->mFileSync.unlock(); break; } CppCheck fileChecker(*threadExecutor, false, CppCheckExecutor::executeCommand); fileChecker.settings() = threadExecutor->mSettings; std::size_t fileSize = 0; if (itFile != threadExecutor->mFiles.end()) { const std::string &file = itFile->first; fileSize = itFile->second; ++itFile; threadExecutor->mFileSync.unlock(); const std::map::const_iterator fileContent = threadExecutor->mFileContents.find(file); if (fileContent != threadExecutor->mFileContents.end()) { // File content was given as a string result += fileChecker.check(file, fileContent->second); } else { // Read file from a file result += fileChecker.check(file); } } else { // file settings.. const ImportProject::FileSettings &fs = *itFileSettings; ++itFileSettings; threadExecutor->mFileSync.unlock(); result += fileChecker.check(fs); if (threadExecutor->mSettings.clangTidy) fileChecker.analyseClangTidy(fs); } threadExecutor->mFileSync.lock(); threadExecutor->mProcessedSize += fileSize; threadExecutor->mProcessedFiles++; if (!threadExecutor->mSettings.quiet) { std::lock_guard lg(threadExecutor->mReportSync); CppCheckExecutor::reportStatus(threadExecutor->mProcessedFiles, threadExecutor->mTotalFiles, threadExecutor->mProcessedSize, threadExecutor->mTotalFileSize); } } return result; } void ThreadExecutor::reportOut(const std::string &outmsg, Color c) { std::lock_guard lg(mReportSync); mErrorLogger.reportOut(outmsg, c); } void ThreadExecutor::bughuntingReport(const std::string & /*str*/) { // TODO } void ThreadExecutor::report(const ErrorMessage &msg, MessageType msgType) { if (mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) return; // Alert only about unique errors bool reportError = false; const std::string errmsg = msg.toString(mSettings.verbose); { std::lock_guard lg(mErrorSync); if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) { mErrorList.emplace_back(errmsg); reportError = true; } } if (reportError) { std::lock_guard lg(mReportSync); switch (msgType) { case MessageType::REPORT_ERROR: mErrorLogger.reportErr(msg); break; case MessageType::REPORT_INFO: mErrorLogger.reportInfo(msg); break; } } } #endif cppcheck-2.7/cli/threadexecutor.h000066400000000000000000000112551417746362400171320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef THREADEXECUTOR_H #define THREADEXECUTOR_H #include "color.h" #include "config.h" #include "errorlogger.h" #include #include #include #include #if ((defined(__GNUC__) || defined(__sun)) && !defined(__MINGW32__) && !defined(__CYGWIN__)) || defined(__CPPCHECK__) #define THREADING_MODEL_FORK #elif defined(_WIN32) #define THREADING_MODEL_WIN #include "importproject.h" #include #else #error "No threading moodel defined" #endif class Settings; /// @addtogroup CLI /// @{ /** * This class will take a list of filenames and settings and check then * all files using threads. */ class ThreadExecutor : public ErrorLogger { public: ThreadExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger); ThreadExecutor(const ThreadExecutor &) = delete; ~ThreadExecutor() OVERRIDE; void operator=(const ThreadExecutor &) = delete; unsigned int check(); void reportOut(const std::string &outmsg, Color c) OVERRIDE; void reportErr(const ErrorMessage &msg) OVERRIDE; void reportInfo(const ErrorMessage &msg) OVERRIDE; void bughuntingReport(const std::string &str) OVERRIDE; /** * @brief Add content to a file, to be used in unit testing. * * @param path File name (used as a key to link with real file). * @param content If the file would be a real file, this should be * the content of the file. */ void addFileContent(const std::string &path, const std::string &content); private: enum class MessageType {REPORT_ERROR, REPORT_INFO}; const std::map &mFiles; Settings &mSettings; ErrorLogger &mErrorLogger; unsigned int mFileCount; void report(const ErrorMessage &msg, MessageType msgType); #if defined(THREADING_MODEL_FORK) /** @brief Key is file name, and value is the content of the file */ std::map mFileContents; private: enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', REPORT_INFO='3', REPORT_VERIFICATION='4', CHILD_END='5'}; /** * Read from the pipe, parse and handle what ever is in there. *@return -1 in case of error * 0 if there is nothing in the pipe to be read * 1 if we did read something */ int handleRead(int rpipe, unsigned int &result); void writeToPipe(PipeSignal type, const std::string &data); /** * Write end of status pipe, different for each child. * Not used in master process. */ std::list mErrorList; int mWpipe; /** * @brief Check load average condition * @param nchildren - count of currently ran children * @return true - if new process can be started */ bool checkLoadAverage(size_t nchildren); /** * @brief Reports internal errors related to child processes * @param msg The error message */ void reportInternalChildErr(const std::string &childname, const std::string &msg); public: /** * @return true if support for threads exist. */ static bool isEnabled() { return true; } #elif defined(THREADING_MODEL_WIN) private: std::map mFileContents; std::map::const_iterator mItNextFile; std::list::const_iterator mItNextFileSettings; std::size_t mProcessedFiles; std::size_t mTotalFiles; std::size_t mProcessedSize; std::size_t mTotalFileSize; std::mutex mFileSync; std::list mErrorList; std::mutex mErrorSync; std::mutex mReportSync; static unsigned __stdcall threadProc(ThreadExecutor *threadExecutor); public: /** * @return true if support for threads exist. */ static bool isEnabled() { return true; } #else public: /** * @return true if support for threads exist. */ static bool isEnabled() { return false; } #endif }; /// @} #endif // THREADEXECUTOR_H cppcheck-2.7/cli/version.rc000066400000000000000000000016441417746362400157470ustar00rootroot00000000000000#include "../lib/version.h" #include "winresrc.h" VS_VERSION_INFO VERSIONINFO FILEVERSION CPPCHECK_VERSION PRODUCTVERSION CPPCHECK_VERSION FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS (0x1L|VS_FF_PRERELEASE) #else FILEFLAGS (0x0L|VS_FF_PRERELEASE) #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "cppcheck Application" VALUE "FileVersion", CPPCHECK_VERSION_STRING VALUE "InternalName", "cppcheck" VALUE "LegalCopyright", LEGALCOPYRIGHT VALUE "OriginalFilename", "cppcheck.exe" VALUE "ProductName", "cppcheck Application" VALUE "ProductVersion", CPPCHECK_VERSION_STRING END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END cppcheck-2.7/cmake/000077500000000000000000000000001417746362400142405ustar00rootroot00000000000000cppcheck-2.7/cmake/buildFiles.cmake000066400000000000000000000003461417746362400173270ustar00rootroot00000000000000CONFIGURE_FILE("${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake") cppcheck-2.7/cmake/clang_tidy.cmake000066400000000000000000000014741417746362400173650ustar00rootroot00000000000000if (NOT NPROC) include(ProcessorCount) ProcessorCount(NPROC) if(NPROC EQUAL 0) message(FATAL_ERROR "could not get processor count") endif() endif() message(STATUS "NPROC=${NPROC}") find_program(RUN_CLANG_TIDY NAMES run-clang-tidy run-clang-tidy-13 run-clang-tidy-12 run-clang-tidy-11 run-clang-tidy-10 run-clang-tidy-9 run-clang-tidy-8) message(STATUS "RUN_CLANG_TIDY=${RUN_CLANG_TIDY}") if (RUN_CLANG_TIDY) # disable all compiler warnings since we are just interested in the tidy ones add_custom_target(run-clang-tidy ${RUN_CLANG_TIDY} -p=${CMAKE_BINARY_DIR} -j ${NPROC} -extra-arg=-w -quiet) if (BUILD_GUI) add_dependencies(run-clang-tidy gui-build-deps) if (BUILD_TESTS) add_dependencies(run-clang-tidy triage-build-ui-deps) endif() endif() endif()cppcheck-2.7/cmake/cmake_uninstall.cmake.in000066400000000000000000000012651417746362400210240ustar00rootroot00000000000000# ----------------------------------------------- # File that provides "make uninstall" target # We use the file 'install_manifest.txt' # ----------------------------------------------- if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") execute_process(COMMAND rm $ENV{DESTDIR}${file}) endforeach(file) cppcheck-2.7/cmake/compilerDefinitions.cmake000066400000000000000000000014651417746362400212560ustar00rootroot00000000000000if (MSVC) # Visual Studio only sets _DEBUG if (CMAKE_BUILD_TYPE MATCHES "Debug") add_definitions(-DDEBUG) endif() add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif() # TODO: this should probably apply to the compiler and not the platform if (CPPCHK_GLIBCXX_DEBUG AND UNIX) # TODO: check if this can be enabled again for Clang - also done in Makefile if (CMAKE_BUILD_TYPE MATCHES "Debug" AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) add_definitions(-D_GLIBCXX_DEBUG) endif() endif() if (HAVE_RULES) add_definitions(-DHAVE_RULES -DTIXML_USE_STL) endif() if (USE_Z3) add_definitions(-DUSE_Z3) endif() if (ENABLE_CHECK_INTERNAL) add_definitions(-DCHECK_INTERNAL) endif() file(TO_CMAKE_PATH ${FILESDIR} _filesdir) add_definitions(-DFILESDIR="${_filesdir}") cppcheck-2.7/cmake/compileroptions.cmake000066400000000000000000000146051417746362400204760ustar00rootroot00000000000000include(CheckCXXCompilerFlag) function(add_compile_options_safe FLAG) string(MAKE_C_IDENTIFIER "HAS_CXX_FLAG${FLAG}" mangled_flag) check_cxx_compiler_flag(${FLAG} ${mangled_flag}) if (${mangled_flag}) add_compile_options(${FLAG}) endif() endfunction() function(target_compile_options_safe TARGET FLAG) string(MAKE_C_IDENTIFIER "HAS_CXX_FLAG${FLAG}" mangled_flag) check_cxx_compiler_flag(${FLAG} ${mangled_flag}) if (${mangled_flag}) target_compile_options(${TARGET} PRIVATE ${FLAG}) endif() endfunction() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Weverything) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_BUILD_TYPE MATCHES "Release") # "Release" uses -O3 by default add_compile_options(-O2) endif() if (WARNINGS_ARE_ERRORS) add_compile_options(-Werror) endif() add_compile_options(-pedantic) add_compile_options(-Wall) add_compile_options(-Wextra) add_compile_options(-Wcast-qual) # Cast for removing type qualifiers add_compile_options(-Wfloat-equal) # Floating values used in equality comparisons add_compile_options(-Wmissing-declarations) # If a global function is defined without a previous declaration add_compile_options(-Wmissing-format-attribute) # add_compile_options(-Wno-long-long) add_compile_options(-Wpacked) # add_compile_options(-Wredundant-decls) # if anything is declared more than once in the same scope add_compile_options(-Wundef) add_compile_options(-Wno-missing-field-initializers) add_compile_options(-Wno-missing-braces) add_compile_options(-Wno-sign-compare) add_compile_options(-Wno-multichar) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) message(FATAL_ERROR "${PROJECT_NAME} c++11 support requires g++ 4.6 or greater, but it is ${CMAKE_CXX_COMPILER_VERSION}") endif () add_compile_options(-Woverloaded-virtual) # when a function declaration hides virtual functions from a base class add_compile_options(-Wno-maybe-uninitialized) # there are some false positives add_compile_options(-Wsuggest-attribute=noreturn) add_compile_options(-Wno-shadow) # whenever a local variable or type declaration shadows another one elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options_safe(-Wno-documentation-unknown-command) # TODO: fix and enable these warnings - or move to suppression list below add_compile_options_safe(-Wno-deprecated-copy-dtor) add_compile_options_safe(-Wno-non-virtual-dtor) add_compile_options_safe(-Wno-inconsistent-missing-destructor-override) # caused by Qt moc code add_compile_options_safe(-Wno-unused-exception-parameter) add_compile_options_safe(-Wno-old-style-cast) add_compile_options_safe(-Wno-global-constructors) add_compile_options_safe(-Wno-exit-time-destructors) add_compile_options_safe(-Wno-sign-conversion) add_compile_options_safe(-Wno-shadow-field-in-constructor) add_compile_options_safe(-Wno-covered-switch-default) add_compile_options_safe(-Wno-shorten-64-to-32) add_compile_options_safe(-Wno-zero-as-null-pointer-constant) add_compile_options_safe(-Wno-format-nonliteral) add_compile_options_safe(-Wno-implicit-int-conversion) add_compile_options_safe(-Wno-double-promotion) add_compile_options_safe(-Wno-shadow-field) add_compile_options_safe(-Wno-shadow-uncaptured-local) add_compile_options_safe(-Wno-unreachable-code) add_compile_options_safe(-Wno-implicit-float-conversion) add_compile_options_safe(-Wno-switch-enum) add_compile_options_safe(-Wno-float-conversion) add_compile_options_safe(-Wno-redundant-parens) # caused by Qt moc code add_compile_options_safe(-Wno-enum-enum-conversion) add_compile_options_safe(-Wno-date-time) add_compile_options_safe(-Wno-suggest-override) add_compile_options_safe(-Wno-suggest-destructor-override) add_compile_options_safe(-Wno-conditional-uninitialized) # warnings we are not interested in add_compile_options(-Wno-four-char-constants) add_compile_options(-Wno-c++98-compat) add_compile_options(-Wno-weak-vtables) add_compile_options(-Wno-padded) add_compile_options(-Wno-c++98-compat-pedantic) add_compile_options(-Wno-disabled-macro-expansion) add_compile_options(-Wno-reserved-id-macro) add_compile_options_safe(-Wno-return-std-move-in-c++11) if(ENABLE_COVERAGE OR ENABLE_COVERAGE_XML) message(FATAL_ERROR "Do not use clang for generate code coverage. Use gcc.") endif() endif() if (MSVC) add_compile_options(/W4) add_compile_options(/wd4018) # warning C4018: '>': signed/unsigned mismatch add_compile_options(/wd4127) # warning C4127: conditional expression is constant add_compile_options(/wd4146) # warning C4146: unary minus operator applied to unsigned type, result still unsigned add_compile_options(/wd4244) # warning C4244: 'initializing': conversion from 'int' to 'char', possible loss of data add_compile_options(/wd4251) # Clang: -Wshorten-64-to-32 -Wimplicit-int-conversion add_compile_options(/wd4267) # warning C4267: 'return': conversion from 'size_t' to 'int', possible loss of data add_compile_options(/wd4389) # warning C4389: '==': signed/unsigned mismatch add_compile_options(/wd4482) add_compile_options(/wd4512) add_compile_options(/wd4701) # warning C4701: potentially uninitialized local variable 'err' used add_compile_options(/wd4706) # warning C4706: assignment within conditional expression add_compile_options(/wd4800) # warning C4800: 'const SymbolDatabase *' : forcing value to bool 'true' or 'false' (performance warning) add_compile_options(/wd4805) # warning C4805: '==' : unsafe mix of type 'bool' and type 'long long' in operation if (WARNINGS_ARE_ERRORS) add_compile_options(/WX) endif() endif() # TODO: check if this can be enabled again - also done in Makefile if (CMAKE_SYSTEM_NAME MATCHES "Linux" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-U_GLIBCXX_DEBUG) endif() if (MSVC) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8000000") endif() if (CYGWIN) # TODO: this is a linker flag - not a compiler flag add_compile_options(-Wl,--stack,8388608) endif() include(cmake/dynamic_analyzer_options.cmake) cppcheck-2.7/cmake/cxx11.cmake000066400000000000000000000007311417746362400162070ustar00rootroot00000000000000macro(use_cxx11) if (CMAKE_VERSION VERSION_LESS "3.1") if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif () else () set (CMAKE_CXX_STANDARD 11) set (CMAKE_CXX_STANDARD_REQUIRED ON) if (POLICY CMP0025) cmake_policy(SET CMP0025 NEW) endif () endif () endmacro(use_cxx11) cppcheck-2.7/cmake/dynamic_analyzer_options.cmake000066400000000000000000000021401417746362400223430ustar00rootroot00000000000000if(ANALYZE_MEMORY) add_compile_options(-fsanitize=memory) add_compile_options(-fsanitize-memory-track-origins=2) add_compile_options(-fno-omit-frame-pointer) add_compile_options(-fno-optimize-sibling-calls) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=memory") elseif(ANALYZE_ADDRESS) add_compile_options(-fsanitize=address) add_compile_options(-fno-omit-frame-pointer) add_compile_options(-fno-optimize-sibling-calls) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") elseif(ANALYZE_THREAD) add_compile_options(-fsanitize=thread) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread") endif() if(ANALYZE_UNDEFINED) add_compile_options(-fsanitize=undefined) add_compile_options(-fno-sanitize-recover=all) add_compile_options(-fno-omit-frame-pointer) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined") endif() if(ANALYZE_DATAFLOW) add_compile_options(-fsanitize=dataflow) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=dataflow") endif() cppcheck-2.7/cmake/findDependencies.cmake000066400000000000000000000036371417746362400205020ustar00rootroot00000000000000if (BUILD_GUI) if (BUILD_TESTS) if (NOT WITH_QCHART) find_package(Qt5 COMPONENTS Core Gui Widgets PrintSupport LinguistTools Help Test REQUIRED) else() find_package(Qt5 COMPONENTS Core Gui Widgets PrintSupport LinguistTools Help Test Charts REQUIRED) endif() else() if (NOT WITH_QCHART) find_package(Qt5 COMPONENTS Core Gui Widgets PrintSupport LinguistTools Help REQUIRED) else() find_package(Qt5 COMPONENTS Core Gui Widgets PrintSupport LinguistTools Help Charts REQUIRED) endif() endif() endif() if (HAVE_RULES) find_path(PCRE_INCLUDE pcre.h) find_library(PCRE_LIBRARY pcre) if (NOT PCRE_LIBRARY OR NOT PCRE_INCLUDE) message(FATAL_ERROR "pcre dependency for RULES has not been found") endif() endif() if (USE_Z3) find_package(Z3 QUIET) if (NOT Z3_FOUND) find_library(Z3_LIBRARIES z3) if (NOT Z3_LIBRARIES) message(FATAL_ERROR "z3 dependency has not been found") endif() find_path(Z3_CXX_INCLUDE_DIRS z3++.h PATH_SUFFIXES "z3") if (NOT Z3_CXX_INCLUDE_DIRS) message(FATAL_ERROR "z3++.h has not been found") endif() endif() endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) if (NOT USE_MATCHCOMPILER_OPT MATCHES "Off") find_package(PythonInterp) if (NOT ${PYTHONINTERP_FOUND}) message(WARNING "No python interpreter found. Therefore, the match compiler is switched off.") set(USE_MATCHCOMPILER_OPT "Off") endif() endif() if (NOT USE_BUNDLED_TINYXML2) find_package(tinyxml2 QUIET) if (NOT tinyxml2_FOUND) find_library(tinyxml2_LIBRARY tinyxml2) if (NOT tinyxml2_LIBRARY) message(FATAL_ERROR "tinyxml2 has not been found") else() message(STATUS "tinyxml2_LIBRARY: ${tinyxml2_LIBRARY}") set(tinyxml2_FOUND 1) endif() endif() endif() cppcheck-2.7/cmake/options.cmake000066400000000000000000000067541417746362400167510ustar00rootroot00000000000000#------------------------------------------------------ # Build type #------------------------------------------------------ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel" CACHE STRING "Configs" FORCE) if(DEFINED CMAKE_BUILD_TYPE) SET_PROPERTY(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CMAKE_CONFIGURATION_TYPES}) endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() # ---------------------------------------------------------------------------- # PROJECT CONFIGURATION # ---------------------------------------------------------------------------- option(ANALYZE_MEMORY "Clang dynamic analyzer: detector of uninitialized reads." OFF) option(ANALYZE_ADDRESS "Clang dynamic analyzer: fast memory error detector. " OFF) option(ANALYZE_THREAD "Clang dynamic analyzer: tool that detects data races. " OFF) option(ANALYZE_UNDEFINED "Clang dynamic analyzer: undefined behavior checker. " OFF) option(ANALYZE_DATAFLOW "Clang dynamic analyzer: general dynamic dataflow analysis." OFF) option(WARNINGS_ARE_ERRORS "Treat warnings as errors" OFF) set(USE_MATCHCOMPILER "Auto" CACHE STRING "Usage of match compiler") set_property(CACHE USE_MATCHCOMPILER PROPERTY STRINGS Auto Off On Verify) if (USE_MATCHCOMPILER MATCHES "Auto") if (NOT CMAKE_BUILD_TYPE MATCHES "Debug") set(USE_MATCHCOMPILER_OPT "On") else() set(USE_MATCHCOMPILER_OPT "Off") endif() else() set(USE_MATCHCOMPILER_OPT ${USE_MATCHCOMPILER}) endif() option(BUILD_TESTS "Build tests" OFF) option(REGISTER_TESTS "Register tests in CTest" ON) option(ENABLE_CHECK_INTERNAL "Enable internal checks" OFF) option(ENABLE_OSS_FUZZ "Enable the OSS-Fuzz related targets" ON) option(BUILD_GUI "Build the qt application" OFF) option(WITH_QCHART "When building GUI(need BUILD_GUI=ON), use Qt5 Charts" OFF) option(HAVE_RULES "Usage of rules (needs PCRE library and headers)" OFF) option(USE_Z3 "Usage of z3 library" OFF) option(USE_BUNDLED_TINYXML2 "Usage of bundled tinyxml2 library" ON) option(CPPCHK_GLIBCXX_DEBUG "Usage of _GLIBCXX_DEBUG in Debug build" ON) if (CMAKE_VERSION VERSION_EQUAL "3.16" OR CMAKE_VERSION VERSION_GREATER "3.16") set(CMAKE_DISABLE_PRECOMPILE_HEADERS Off CACHE BOOL "Disable precompiled headers") # need to disable the prologue or it will be treated like a system header and not emit any warnings # see https://gitlab.kitware.com/cmake/cmake/-/issues/21219 set(CMAKE_PCH_PROLOGUE "") else() set(CMAKE_DISABLE_PRECOMPILE_HEADERS On CACHE BOOL "Disable precompiled headers") endif() set(CMAKE_INCLUDE_DIRS_CONFIGCMAKE ${CMAKE_INSTALL_PREFIX}/include CACHE PATH "Output directory for headers") set(CMAKE_LIB_DIRS_CONFIGCMAKE ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Output directory for libraries") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(FILESDIR ${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME} CACHE STRING "Cppcheck files directory") cppcheck-2.7/cmake/printInfo.cmake000066400000000000000000000060551417746362400172200ustar00rootroot00000000000000message( STATUS "------------------ General configuration for ${PROJECT_NAME} ${VERSION} -----------------") message( STATUS ) message( STATUS "CMake Generator = ${CMAKE_GENERATOR}") message( STATUS "Compiler = ${CMAKE_CXX_COMPILER_ID}") message( STATUS "Compiler Version = ${CMAKE_CXX_COMPILER_VERSION}") message( STATUS "Build type = ${CMAKE_BUILD_TYPE}") message( STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}" ) message( STATUS "CMAKE_DISABLE_PRECOMPILE_HEADERS = ${CMAKE_DISABLE_PRECOMPILE_HEADERS}" ) message( STATUS "C++ flags (General) = ${CMAKE_CXX_FLAGS}") message( STATUS "C++ flags (Release) = ${CMAKE_CXX_FLAGS_RELEASE}") message( STATUS "C++ flags (RelWithDebInfo) = ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") message( STATUS "C++ flags (Debug) = ${CMAKE_CXX_FLAGS_DEBUG}") get_directory_property( DirDefs DIRECTORY ${CMAKE_SOURCE_DIR} COMPILE_DEFINITIONS ) foreach( d ${DirDefs} ) message( STATUS "Found Define: " ${d} ) endforeach() message( STATUS ) message( STATUS "---------------------------------------------------------" ) message( STATUS "ANALYZE_MEMORY = ${ANALYZE_MEMORY}" ) message( STATUS "ANALYZE_ADDRESS = ${ANALYZE_ADDRESS}" ) message( STATUS "ANALYZE_THREAD = ${ANALYZE_THREAD}" ) message( STATUS "ANALYZE_UNDEFINED = ${ANALYZE_UNDEFINED}" ) message( STATUS "ANALYZE_DATAFLOW = ${ANALYZE_DATAFLOW}" ) message( STATUS "WARNINGS_ARE_ERRORS = ${WARNINGS_ARE_ERRORS}" ) message( STATUS ) message( STATUS "USE_MATCHCOMPILER = ${USE_MATCHCOMPILER}" ) message( STATUS "USE_MATCHCOMPILER_OPT = ${USE_MATCHCOMPILER_OPT}" ) message( STATUS ) if(NOT DEFINED BUILD_SHARED_LIBS) message( STATUS "BUILD_SHARED_LIBS = OFF" ) else() message( STATUS "BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}" ) endif(NOT DEFINED BUILD_SHARED_LIBS) message( STATUS "LIBXML2_XMLLINT_EXECUTABLE = ${LIBXML2_XMLLINT_EXECUTABLE}" ) message( STATUS "BUILD_TESTS = ${BUILD_TESTS}" ) if(BUILD_TESTS) message( STATUS "REGISTER_TESTS = ${REGISTER_TESTS}" ) endif() message( STATUS "ENABLE_CHECK_INTERNAL = ${ENABLE_CHECK_INTERNAL}" ) message( STATUS "ENABLE_OSS_FUZZ = ${ENABLE_OSS_FUZZ}" ) message( STATUS ) message( STATUS "BUILD_GUI = ${BUILD_GUI}" ) message( STATUS "WITH_QCHART = ${WITH_QCHART}" ) message( STATUS ) message( STATUS "HAVE_RULES = ${HAVE_RULES}" ) if (HAVE_RULES) message( STATUS "PCRE_LIBRARY = ${PCRE_LIBRARY}" ) endif() message( STATUS ) message( STATUS "USE_Z3 = ${USE_Z3}" ) if (USE_Z3) message( STATUS "Z3_LIBRARIES = ${Z3_LIBRARIES}" ) message( STATUS "Z3_CXX_INCLUDE_DIRS = ${Z3_CXX_INCLUDE_DIRS}" ) endif() message( STATUS "USE_BUNDLED_TINYXML2 = ${USE_BUNDLED_TINYXML2}" ) message( STATUS ) if(${ANALYZE_ADDRESS}) message("##########################################################") message(STATUS "For better visualization change environment variable: ASAN_SYMBOLIZER_PATH=/path/to/llvm-symbolizer") message("##########################################################") endif() cppcheck-2.7/cmake/versions.cmake000066400000000000000000000004511417746362400171120ustar00rootroot00000000000000# Version for libraries CPP SET(VERSION "2.7") STRING(REGEX MATCHALL "[0-9]" VERSION_PARTS "${VERSION}") LIST(GET VERSION_PARTS 0 VERSION_MAJOR) LIST(GET VERSION_PARTS 1 VERSION_MINOR) SET(SOVERSION "${VERSION_MAJOR}.${VERSION_MINOR}") # Postfix of so's: SET(DLLVERSION "") SET(DEBUG_POSTFIX "") cppcheck-2.7/console_common.pri000066400000000000000000000010731417746362400167070ustar00rootroot00000000000000# console_common.pri # These are common definitions for console builds. win32 { CONFIG += embed_manifest_exe console DEFINES += _CRT_SECURE_NO_WARNINGS LIBS += -lshlwapi } # Add more strict compiling flags for GCC contains(QMAKE_CXX, g++) { QMAKE_CXXFLAGS_WARN_ON += -Wextra -pedantic -Wfloat-equal -Wcast-qual -Wlogical-op -Wno-long-long } # Change Visual Studio compiler (CL) warning level to W4 contains(QMAKE_CXX, cl) { QMAKE_CXXFLAGS_WARN_ON -= -W3 QMAKE_CXXFLAGS_WARN_ON += -W4 } CONFIG(release, debug|release) { DEFINES += NDEBUG } cppcheck-2.7/cppcheck-errors.rng000066400000000000000000000061151417746362400167650ustar00rootroot00000000000000 2 [1-9]\.[0-9]+.* error information performance portability style warning 0 1 0 0 cppcheck-2.7/cppcheck.cppcheck000066400000000000000000000011231417746362400164370ustar00rootroot00000000000000 out1 true cppcheck-2.7/cppcheck.sln000066400000000000000000000123501417746362400154570ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29020.237 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli\cli.vcxproj", "{35CBDF51-2456-3EC3-99ED-113C30858883}" ProjectSection(ProjectDependencies) = postProject {C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testrunner", "test\testrunner.vcxproj", "{4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}" ProjectSection(ProjectDependencies) = postProject {C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cppcheck", "lib\cppcheck.vcxproj", "{C183DB5B-AD6C-423D-80CA-1F9549555A1A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Debug-PCRE|Win32 = Debug-PCRE|Win32 Debug-PCRE|x64 = Debug-PCRE|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 Release-PCRE|Win32 = Release-PCRE|Win32 Release-PCRE|x64 = Release-PCRE|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|Win32.ActiveCfg = Debug|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|Win32.Build.0 = Debug|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|x64.ActiveCfg = Debug|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|x64.Build.0 = Debug|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|Win32.ActiveCfg = Debug-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|Win32.Build.0 = Debug-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|x64.ActiveCfg = Debug-PCRE|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|x64.Build.0 = Debug-PCRE|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|Win32.ActiveCfg = Release|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|Win32.Build.0 = Release|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|x64.ActiveCfg = Release|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|x64.Build.0 = Release|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|Win32.ActiveCfg = Release-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|Win32.Build.0 = Release-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|x64.ActiveCfg = Release-PCRE|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|x64.Build.0 = Release-PCRE|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|Win32.ActiveCfg = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|Win32.Build.0 = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|x64.ActiveCfg = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|x64.Build.0 = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|Win32.ActiveCfg = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|Win32.Build.0 = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|x64.ActiveCfg = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|x64.Build.0 = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|Win32.ActiveCfg = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|Win32.Build.0 = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|x64.ActiveCfg = Release|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|x64.Build.0 = Release|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|Win32.ActiveCfg = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|Win32.Build.0 = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|x64.ActiveCfg = Release|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|x64.Build.0 = Release|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|Win32.ActiveCfg = Debug|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|Win32.Build.0 = Debug|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|x64.ActiveCfg = Debug|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|x64.Build.0 = Debug|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|Win32.ActiveCfg = Debug-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|Win32.Build.0 = Debug-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|x64.ActiveCfg = Debug-PCRE|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|x64.Build.0 = Debug-PCRE|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|Win32.ActiveCfg = Release|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|Win32.Build.0 = Release|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|x64.ActiveCfg = Release|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|x64.Build.0 = Release|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|Win32.ActiveCfg = Release-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|Win32.Build.0 = Release-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|x64.ActiveCfg = Release-PCRE|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|x64.Build.0 = Release-PCRE|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal cppcheck-2.7/createrelease000077500000000000000000000104401417746362400157110ustar00rootroot00000000000000#!/bin/bash # # A script for creating release packages. The release packages are create in the home directory. # # Create release candidate # ======================== # # Windows installer: # - ensure latest build was successful # - ensure cfg files etc are included (win_installer/cppcheck.wxs) # # self check, fix critical issues: # make clean && make CXXFLAGS=-O2 MATCHCOMPILER=yes -j4 # ./cppcheck -D__CPPCHECK__ --std=c++11 --library=cppcheck-lib --enable=style --inconclusive --inline-suppr --suppress=bitwiseOnBoolean --suppress=shadowFunction --suppress=useStlAlgorithm --suppress=*:externals/picojson.h --suppress=functionConst --suppress=functionStatic --xml cli gui/*.cpp lib 2> selfcheck.xml # # check --bug-hunting output: # make clean && make -j4 USE_Z3=yes CXXFLAGS=-O2 MATCHCOMPILER=yes && ./cppcheck -D__CPPCHECK__ --bug-hunting --xml lib 2> bughunting.xml # # Update translations # lupdate gui.pro # # Update copyright year # git diff 2.6 -- */*.cpp */*.h | grep '^diff --git a/' | sed 's|.* b/||' | xargs sed -i 's/Copyright (C) 2007-20[12]./Copyright (C) 2007-2022/' # git diff | grep '^diff --git a/' # # Make sure "cppcheck --errorlist" works: # make clean && make -j4 && ./cppcheck --errorlist > errlist.xml && xmllint --noout errlist.xml # # Update AUTHORS using output from: # git log --format='%aN' 1.81..HEAD | sort -u > AUTHORS2 && diff -y AUTHORS AUTHORS2 | less # # Create 2.6.x branch # git checkout -b 2.6.x ; git push -u origin 2.6.x # # Update version numbers in: # sed -i -r "s/version 2[.][0-9]+([.]99)*/2.7/" cli/main.cpp # sed -i -r "s|2[.][0-9]+([.]99)*|2.7|" cmake/versions.cmake # sed -i -r "s/MINOR [0-9]+/MINOR 7/" lib/version.h # sed -i -r "s/2[.][0-9]+([.]99)*/2.7/" win_installer/productInfo.wxi # sed -i -r "s/subtitle: Version 2\.[0-9]+/subtitle: Version 2.7/" man/*.md # Ensure that "-rc1" is added in productInfo.wxi and lib/version.h # Verify: # grep '\.99' */*.[ch]* && grep '[0-9][0-9] dev' */*.[ch]* # egrep "2\.[0-9]+" */*.h */*.cpp man/*.md | grep -v "test/test" | less # git commit -a -m "1.43: Set versions" # # Build and test the windows installer # # Update the Makefile: # make dmake && ./dmake --release # git commit -a -m "1.43: Updated Makefile" # # Tag: # git tag 2.6-rc1 # git push --tags # # Release # ======= # # Write release notes # # Remove "-rc1" from versions. Test: git grep "\-rc[0-9]" # # git tag 2.6 ; git push --tags # ./createrelease 2.6 # # Create a release folder on sourceforge: # https://sourceforge.net/projects/cppcheck/files/cppcheck/ # # copy msi from release-windows # copy manual from build-manual # # Update download link on index.php main page # # write a news # # save "cppcheck --doc" output on wiki # # compile new democlient: # ssh -t danielmarjamaki,cppcheck@shell.sourceforge.net create # ./build-cppcheck.sh 1.43 # # run daca with new release # 1. edit tools/donate-cpu-server.py. Update OLD_VERSION and VERSION # 2. scp tools/donate-cpu-server.py danielmarjamaki@cppcheck1.osuosl.org:/var/daca@home/ # # self check, fix stylistic issues: # ./cppcheck -D__CPPCHECK__ --library=cppcheck-lib --enable=style --inconclusive --inline-suppr --suppress=bitwiseOnBoolean --suppress=shadowFunction --suppress=useStlAlgorithm --suppress=*:externals/picojson.h cli gui/*.cpp lib # # Set debug version (see 803eea912c9512c810a7e78d58bb927d89a6daa1) # Tag to use tag=$1 # Name of release releasename=cppcheck-$tag set -e cd ~/cppcheck git checkout $tag mkdir -p upload make clean # Create archives.. git archive --format=tar --prefix=$releasename/ $tag | gzip > upload/$releasename.tar.gz git archive --format=tar --prefix=$releasename/ $tag | bzip2 > upload/$releasename.tar.bz2 git archive --format=zip -9 --prefix=$releasename/ $tag > upload/$releasename.zip cd upload scp $releasename.* danielmarjamaki,cppcheck@frs.sourceforge.net:/home/frs/project/c/cp/cppcheck/cppcheck/$tag/ rm $releasename.* cd .. # Generate version.txt make -j4 ./cppcheck --version > upload/version.txt cd ~/cppcheck/upload scp * danielmarjamaki,cppcheck@web.sourceforge.net:htdocs/ cd ~/cppcheck rm -rf upload # Local cppcheck binary mkdir -p ~/.cppcheck/$tag cd ~/.cppcheck/$tag cp -R ~/cppcheck/cfg . cp -R ~/cppcheck/addons . cp -R ~/cppcheck/platforms . cd ~/cppcheck make clean ; make -j4 FILESDIR=~/.cppcheck/$tag MATCHCOMPILER=yes USE_Z3=yes mv cppcheck ~/.cppcheck/cppcheck-$tag git checkout main cppcheck-2.7/cve-test-suite/000077500000000000000000000000001417746362400160415ustar00rootroot00000000000000cppcheck-2.7/cve-test-suite/cve-2018-1000618.cpp000066400000000000000000000005661417746362400205360ustar00rootroot00000000000000 // Reduced source code. Inspired by this fix: // https://github.com/EOSIO/eos/pull/4112/commits/ef62761c5e388880e8bb1bb41e8b512a5187f255 #include class C { std::set typedefs; bool is_type(int type) const { if (typedefs.find(type) != typedefs.end()) return is_type(type); // BUG: endless recursion return false; } }; cppcheck-2.7/cve-test-suite/cve-2018-11360.c000066400000000000000000000004601417746362400200220ustar00rootroot00000000000000 // CVE: CVE-2018-6836 // This is a simplified code example based on CVE-2018-11360. void *malloc(unsigned long); void free(void *); void f(int size) { char *ia5_string = malloc(size); // Hint: Off by one for (int i = 0; i <= size; i++) ia5_string[i]=0; // BUG free(ia5_string); } cppcheck-2.7/cve-test-suite/cve-2018-5334.c000066400000000000000000000002441417746362400177460ustar00rootroot00000000000000 // CVE-2018-5334 #define LEN 100 void f(const int *m_ptr, int sig_off, int rec_size) { if (m_ptr[sig_off] == 0xdd && (sig_off + 15 <= (rec_size - LEN))) {} } cppcheck-2.7/cve-test-suite/cve-2018-6836.c000066400000000000000000000012411417746362400177540ustar00rootroot00000000000000// Bug: free uninitialized pointer // Fix: https://code.wireshark.org/review/gitweb?p=wireshark.git;a=commit;h=28960d79cca262ac6b974f339697b299a1e28fef void *malloc(unsigned long); void free(void *); struct comment { int *data; }; struct table { struct comment *com; }; void destroy_table(struct table *comment_table) { free(comment_table->com->data); free(comment_table->com); free(comment_table); } void f() { struct table *comment_table = (struct table *)malloc(sizeof(struct table)); struct comment *comment_rec = (struct comment *)malloc(sizeof(struct comment)); comment_table->com = comment_rec; destroy_table(comment_table); } cppcheck-2.7/cve-test-suite/download.sh000077500000000000000000000023471417746362400202150ustar00rootroot00000000000000#!/bin/bash # Fetch CVE issues that are interesting to look at echo "CVE" > cve.txt for i in $(seq 1 20); do echo "page $i" # CVE 119 issues: # https://www.cvedetails.com/vulnerability-list/cweid-119/vulnerabilities.html # Use curl to get page $i: curl -s "https://www.cvedetails.com/vulnerability-list.php?vendor_id=0&product_id=0&version_id=0&page=$i&hasexp=0&opdos=0&opec=0&opov=0&opcsrf=0&opgpriv=0&opsqli=0&opxss=0&opdirt=0&opmemc=0&ophttprs=0&opbyp=0&opfileinc=0&opginf=0&cvssscoremin=0&cvssscoremax=0&year=0&month=0&cweid=119&order=1&trc=11185&sha=a76f56dbb935840fc028b135d550322223547356" > v.html # for each cve: for cve in $(grep /cve/CVE-2018- v.html | sed 's|.*/cve/CVE-2018-\([0-9]*\).*|CVE-2018-\1|'); do echo "$cve" >> cve.txt curl -s "https://www.cvedetails.com/cve/$cve/" > download-cve # cve type cat download-cve | grep '>Overflow<' >> cve.txt # is there a code reference? cat download-cve | grep 'https*://.*[a-f0-9]\{30,50\}' | sed 's|.*\(https*://[^ ]*[a-f0-9]\{30,\}\).*|\1|' >> cve.txt # is there a pull request reference? cat download-cve | grep 'https*://github.com/[^ ]*/pull/' | sed 's|.*\(https*://github.com/[^ ]*\).*|\1|' >> cve.txt done done rm v.html rm download-cve cppcheck-2.7/cve-test-suite/readme.txt000066400000000000000000000024731417746362400200450ustar00rootroot00000000000000 Background ========== The CVE database contains known vulnerabilities in various source code projects. For instance, to list known "overflow" vulnerabilities, this link can be used: https://www.cvedetails.com/vulnerability-list/cweid-119/vulnerabilities.html Many issues in the CVE database are "out of reach" for static analysis because of required domain knowledge etc. However there are also issues that could be "possible" to detect with static analysis. For each such issue that we see that we think is "possible" to detect with static analysis, we can create a file in this folder. The filename is the CVE id. The contents of the file should contain this info: * Recommended: URL that can be used to download source code, file with bug * Description * Reduced example code. The code should be plain C/C++ without dependencies. Possible usages: ================ The test cases can inspire future Cppcheck development. These files could be used for a quick and easy tool evaluation. For Cppcheck and other tools. Because only plain C/C++ is used, tools should have all info they need, so hopefully no extra configuration is needed. An extended tool evaluation can use the real source code. It's possible to lookup the real source code using the CWE id. However in such tool evaluation, the tools must be configured properly. cppcheck-2.7/democlient/000077500000000000000000000000001417746362400153035ustar00rootroot00000000000000cppcheck-2.7/democlient/build.sh000077500000000000000000000013121417746362400167360ustar00rootroot00000000000000#!/bin/bash # this script downloads and builds the democlient # syntax: # ./build 1.60.1 # cppcheck lib folder cppchecklib=cppcheck-$1/lib echo Downloading... wget http://downloads.sourceforge.net/project/cppcheck/cppcheck/$1/cppcheck-$1.tar.bz2 echo Unpacking... tar xjvf cppcheck-$1.tar.bz2 rm cppcheck-$1.tar.bz2 rm cppcheck-$1/Changelog echo Building... g++ -O2 -o democlient-$1.cgi -I$cppchecklib -Icppcheck-$1/externals/tinyxml2 cppcheck-$1/democlient/democlient.cpp $cppchecklib/*.cpp cppcheck-$1/externals/tinyxml2/tinyxml2.cpp echo Copy cgi to webspace... cp democlient-$1.cgi /home/project-web/cppcheck/cgi-bin/democlient.cgi chmod +rx /home/project-web/cppcheck/cgi-bin/democlient.cgi echo Done! cppcheck-2.7/democlient/democlient.cpp000066400000000000000000000056351417746362400201430ustar00rootroot00000000000000#include #include #include #include #include #include "cppcheck.h" #include "version.h" static void unencode(const char *src, char *dest) { for (; *src; src++, dest++) { if (*src == '+') *dest = ' '; else if (*src == '%') { unsigned int code; if (std::sscanf(src+1, "%2x", &code) != 1) code = '?'; *dest = code; src += 2; } else *dest = *src; } *dest = '\0'; } static FILE *logfile = nullptr; class CppcheckExecutor : public ErrorLogger { private: const std::time_t stoptime; CppCheck cppcheck; public: CppcheckExecutor() : ErrorLogger() , stoptime(std::time(nullptr)+2U) , cppcheck(*this, false, nullptr) { cppcheck.settings().addEnabled("all"); cppcheck.settings().certainty.enable(Certainty::inconclusive); } void run(const char code[]) { cppcheck.check("test.cpp", code); } void bughuntingReport(const std::string&) override {} void reportOut(const std::string &outmsg, Color c) override {} void reportErr(const ErrorMessage &msg) override { const std::string s = msg.toString(true); std::cout << s << std::endl; if (logfile != nullptr) std::fprintf(logfile, "%s\n", s.c_str()); } void reportProgress(const std::string& filename, const char stage[], const std::size_t value) override { if (std::time(nullptr) >= stoptime) { std::cout << "Time to analyse the code exceeded 2 seconds. Terminating.\n\n"; Settings::terminate(); } } }; int main() { std::cout << "Content-type: text/html\r\n\r\n" << "\n"; char data[4096] = {0}; const char *query_string = std::getenv("QUERY_STRING"); if (query_string) std::strncpy(data, query_string, sizeof(data)-2); const char *lenstr = std::getenv("CONTENT_LENGTH"); if (lenstr) { int len = std::min(1 + std::atoi(lenstr), (int)(sizeof(data) - 2)); std::fgets(data, len, stdin); } if (data[4000] != '\0') { std::cout << "For performance reasons the code must be shorter than 1000 chars."; return EXIT_SUCCESS; } const char *pdata = data; if (std::strncmp(pdata, "code=", 5)==0) pdata += 5; char code[4096] = {0}; unencode(pdata, code); logfile = std::fopen("democlient.log", "at"); if (logfile != nullptr) std::fprintf(logfile, "===========================================================\n%s\n", code); std::cout << "Cppcheck " CPPCHECK_VERSION_STRING "
";

    CppcheckExecutor cppcheckExecutor;
    cppcheckExecutor.run(code);

    std::fclose(logfile);

    std::cout << "
Done!"; return EXIT_SUCCESS; } cppcheck-2.7/doxyfile000066400000000000000000002365171417746362400147440ustar00rootroot00000000000000# Doxyfile 1.8.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed # in front of the TAG it is preceding . # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follows. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = Cppcheck # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doxyoutput # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, # Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, # Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields or simple typedef fields will be shown # inline in the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO (the default), structs, classes, and unions are shown on a separate # page (for HTML and Man pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can # be an expensive process and often the same symbol appear multiple times in # the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too # small doxygen will become slower. If the cache is too large, memory is wasted. # The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid # range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 # symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = cli/ \ gui/ \ lib/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be ignored. # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES # If CLANG_ASSISTED_PARSING is set to YES, then doxygen will use the clang parser # for more accurate parsing at the cost of reduced performance. This can be # particularly helpful with template rich C++ code for which doxygen's built-in # parser lacks the necessary type information. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified at INPUT and INCLUDE_PATH. CLANG_OPTIONS = #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefore more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript # pieces of code that will be used on startup of the MathJax code. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search # engine library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4 will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images # or other source files which should be copied to the LaTeX output directory. # Note that the files will be copied as-is; there are no commands or markers # available. LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- # If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files # that can be used to generate PDF. GENERATE_DOCBOOK = NO # The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in # front of it. If left blank docbook will be used as the default path. DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # If the EXTERNAL_PAGES tag is set to YES all external pages will be listed # in the related pages index. If set to NO, only the current project's # pages will be listed. EXTERNAL_PAGES = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES cppcheck-2.7/externals/000077500000000000000000000000001417746362400151655ustar00rootroot00000000000000cppcheck-2.7/externals/.clang-tidy000066400000000000000000000001731417746362400172220ustar00rootroot00000000000000--- Checks: '-*,misc-definitions-in-headers' CheckOptions: - { key: HeaderFileExtensions, value: "x" } cppcheck-2.7/externals/externals.pri000066400000000000000000000005341417746362400177100ustar00rootroot00000000000000INCLUDEPATH += $${PWD} \ $${PWD}/picojson \ $${PWD}/simplecpp \ $${PWD}/tinyxml2 HEADERS += $${PWD}/picojson/picojson.h \ $${PWD}/simplecpp/simplecpp.h \ $${PWD}/tinyxml2/tinyxml2.h SOURCES += $${PWD}/simplecpp/simplecpp.cpp \ $${PWD}/tinyxml2/tinyxml2.cpp cppcheck-2.7/externals/picojson/000077500000000000000000000000001417746362400170115ustar00rootroot00000000000000cppcheck-2.7/externals/picojson/LICENSE000066400000000000000000000024701417746362400200210ustar00rootroot00000000000000Copyright 2009-2010 Cybozu Labs, Inc. Copyright 2011-2014 Kazuho Oku All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cppcheck-2.7/externals/picojson/picojson.h000066400000000000000000000627661417746362400210270ustar00rootroot00000000000000/* * Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2011-2014 Kazuho Oku * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef picojson_h #define picojson_h #include #include #include #include #include #include #include #include #include #include #include // for isnan/isinf #if __cplusplus>=201103L # include #else extern "C" { # ifdef _MSC_VER # include # elif defined(__INTEL_COMPILER) # include # else # include # endif } #endif // experimental support for int64_t (see README.mkdn for detail) #ifdef PICOJSON_USE_INT64 # define __STDC_FORMAT_MACROS # include # include #endif // to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 #ifndef PICOJSON_USE_LOCALE # define PICOJSON_USE_LOCALE 1 #endif #if PICOJSON_USE_LOCALE extern "C" { # include } #endif #ifndef PICOJSON_ASSERT # define PICOJSON_ASSERT(e) do { if (! (e)) throw std::runtime_error(#e); } while (0) #endif #ifdef _MSC_VER #define SNPRINTF _snprintf_s #pragma warning(push) #pragma warning(disable : 4244) // conversion from int to char #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4702) // unreachable code #else #define SNPRINTF snprintf #endif namespace picojson { enum { null_type, boolean_type, number_type, string_type, array_type, object_type #ifdef PICOJSON_USE_INT64 , int64_type #endif }; enum { INDENT_WIDTH = 2 }; struct null {}; class value { public: typedef std::vector array; typedef std::map object; union _storage { bool boolean_; double number_; #ifdef PICOJSON_USE_INT64 int64_t int64_; #endif std::string* string_; array* array_; object* object_; }; protected: int type_; _storage u_; public: value(); value(int type, bool); explicit value(bool b); #ifdef PICOJSON_USE_INT64 explicit value(int64_t i); #endif explicit value(double n); explicit value(const std::string& s); explicit value(const array& a); explicit value(const object& o); explicit value(const char* s); value(const char* s, size_t len); ~value(); value(const value& x); value& operator=(const value& x); void swap(value& x); template bool is() const; template const T& get() const; template T& get(); bool evaluate_as_boolean() const; const value& get(size_t idx) const; const value& get(const std::string& key) const; value& get(size_t idx); value& get(const std::string& key); bool contains(size_t idx) const; bool contains(const std::string& key) const; std::string to_str() const; template void serialize(Iter os, bool prettify = false) const; std::string serialize(bool prettify = false) const; private: template value(const T*); // intentionally defined to block implicit conversion of pointer to bool template static void _indent(Iter os, int indent); template void _serialize(Iter os, int indent) const; std::string _serialize(int indent) const; }; typedef value::array array; typedef value::object object; inline value::value() : type_(null_type) {} inline value::value(int type, bool) : type_(type) { switch (type) { #define INIT(p, v) case p##type: u_.p = v; break INIT(boolean_, false); INIT(number_, 0.0); #ifdef PICOJSON_USE_INT64 INIT(int64_, 0); #endif INIT(string_, new std::string()); INIT(array_, new array()); INIT(object_, new object()); #undef INIT default: break; } } inline value::value(bool b) : type_(boolean_type) { u_.boolean_ = b; } #ifdef PICOJSON_USE_INT64 inline value::value(int64_t i) : type_(int64_type) { u_.int64_ = i; } #endif inline value::value(double n) : type_(number_type) { if ( #ifdef _MSC_VER ! _finite(n) #elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf)) std::isnan(n) || std::isinf(n) #else isnan(n) || isinf(n) #endif ) { throw std::overflow_error(""); } u_.number_ = n; } inline value::value(const std::string& s) : type_(string_type) { u_.string_ = new std::string(s); } inline value::value(const array& a) : type_(array_type) { u_.array_ = new array(a); } inline value::value(const object& o) : type_(object_type) { u_.object_ = new object(o); } inline value::value(const char* s) : type_(string_type) { u_.string_ = new std::string(s); } inline value::value(const char* s, size_t len) : type_(string_type) { u_.string_ = new std::string(s, len); } inline value::~value() { switch (type_) { #define DEINIT(p) case p##type: delete u_.p; break DEINIT(string_); DEINIT(array_); DEINIT(object_); #undef DEINIT default: break; } } inline value::value(const value& x) : type_(x.type_) { switch (type_) { #define INIT(p, v) case p##type: u_.p = v; break INIT(string_, new std::string(*x.u_.string_)); INIT(array_, new array(*x.u_.array_)); INIT(object_, new object(*x.u_.object_)); #undef INIT default: u_ = x.u_; break; } } inline value& value::operator=(const value& x) { if (this != &x) { value t(x); swap(t); } return *this; } inline void value::swap(value& x) { std::swap(type_, x.type_); std::swap(u_, x.u_); } #define IS(ctype, jtype) \ template <> inline bool value::is() const { \ return type_ == jtype##_type; \ } IS(null, null) IS(bool, boolean) #ifdef PICOJSON_USE_INT64 IS(int64_t, int64) #endif IS(std::string, string) IS(array, array) IS(object, object) #undef IS template <> inline bool value::is() const { return type_ == number_type #ifdef PICOJSON_USE_INT64 || type_ == int64_type #endif ; } #define GET(ctype, var) \ template <> inline const ctype& value::get() const { \ PICOJSON_ASSERT("type mismatch! call is() before get()" \ && is()); \ return var; \ } \ template <> inline ctype& value::get() { \ PICOJSON_ASSERT("type mismatch! call is() before get()" \ && is()); \ return var; \ } GET(bool, u_.boolean_) GET(std::string, *u_.string_) GET(array, *u_.array_) GET(object, *u_.object_) #ifdef PICOJSON_USE_INT64 GET(double, (type_ == int64_type && (const_cast(this)->type_ = number_type, const_cast(this)->u_.number_ = u_.int64_), u_.number_)) GET(int64_t, u_.int64_) #else GET(double, u_.number_) #endif #undef GET inline bool value::evaluate_as_boolean() const { switch (type_) { case null_type: return false; case boolean_type: return u_.boolean_; case number_type: return u_.number_ != 0; case string_type: return ! u_.string_->empty(); default: return true; } } inline const value& value::get(size_t idx) const { static value s_null; PICOJSON_ASSERT(is()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } inline value& value::get(size_t idx) { static value s_null; PICOJSON_ASSERT(is()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } inline const value& value::get(const std::string& key) const { static value s_null; PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } inline value& value::get(const std::string& key) { static value s_null; PICOJSON_ASSERT(is()); object::iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } inline bool value::contains(size_t idx) const { PICOJSON_ASSERT(is()); return idx < u_.array_->size(); } inline bool value::contains(const std::string& key) const { PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end(); } inline std::string value::to_str() const { switch (type_) { case null_type: return "null"; case boolean_type: return u_.boolean_ ? "true" : "false"; #ifdef PICOJSON_USE_INT64 case int64_type: { char buf[sizeof("-9223372036854775808")]; SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); return buf; } #endif case number_type: { char buf[256]; double tmp; SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); #if PICOJSON_USE_LOCALE char *decimal_point = localeconv()->decimal_point; if (strcmp(decimal_point, ".") != 0) { size_t decimal_point_len = strlen(decimal_point); for (char *p = buf; *p != '\0'; ++p) { if (strncmp(p, decimal_point, decimal_point_len) == 0) { return std::string(buf, p) + "." + (p + decimal_point_len); } } } #endif return buf; } case string_type: return *u_.string_; case array_type: return "array"; case object_type: return "object"; default: PICOJSON_ASSERT(0); #ifdef _MSC_VER __assume(0); #endif } return std::string(); } template void copy(const std::string& s, Iter oi) { std::copy(s.begin(), s.end(), oi); } template void serialize_str(const std::string& s, Iter oi) { *oi++ = '"'; for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { switch (*i) { #define MAP(val, sym) case val: copy(sym, oi); break MAP('"', "\\\""); MAP('\\', "\\\\"); MAP('/', "\\/"); MAP('\b', "\\b"); MAP('\f', "\\f"); MAP('\n', "\\n"); MAP('\r', "\\r"); MAP('\t', "\\t"); #undef MAP default: if (static_cast(*i) < 0x20 || *i == 0x7f) { char buf[7]; SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff); copy(buf, buf + 6, oi); } else { *oi++ = *i; } break; } } *oi++ = '"'; } template void value::serialize(Iter oi, bool prettify) const { return _serialize(oi, prettify ? 0 : -1); } inline std::string value::serialize(bool prettify) const { return _serialize(prettify ? 0 : -1); } template void value::_indent(Iter oi, int indent) { *oi++ = '\n'; for (int i = 0; i < indent * INDENT_WIDTH; ++i) { *oi++ = ' '; } } template void value::_serialize(Iter oi, int indent) const { switch (type_) { case string_type: serialize_str(*u_.string_, oi); break; case array_type: { *oi++ = '['; if (indent != -1) { ++indent; } for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { if (i != u_.array_->begin()) { *oi++ = ','; } if (indent != -1) { _indent(oi, indent); } i->_serialize(oi, indent); } if (indent != -1) { --indent; if (! u_.array_->empty()) { _indent(oi, indent); } } *oi++ = ']'; break; } case object_type: { *oi++ = '{'; if (indent != -1) { ++indent; } for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { if (i != u_.object_->begin()) { *oi++ = ','; } if (indent != -1) { _indent(oi, indent); } serialize_str(i->first, oi); *oi++ = ':'; if (indent != -1) { *oi++ = ' '; } i->second._serialize(oi, indent); } if (indent != -1) { --indent; if (! u_.object_->empty()) { _indent(oi, indent); } } *oi++ = '}'; break; } default: copy(to_str(), oi); break; } if (indent == 0) { *oi++ = '\n'; } } inline std::string value::_serialize(int indent) const { std::string s; _serialize(std::back_inserter(s), indent); return s; } template class input { protected: Iter cur_, end_; int last_ch_; bool ungot_; int line_; public: input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {} int getc() { if (ungot_) { ungot_ = false; return last_ch_; } if (cur_ == end_) { last_ch_ = -1; return -1; } if (last_ch_ == '\n') { line_++; } last_ch_ = *cur_ & 0xff; ++cur_; return last_ch_; } void ungetc() { if (last_ch_ != -1) { PICOJSON_ASSERT(! ungot_); ungot_ = true; } } Iter cur() const { return cur_; } int line() const { return line_; } void skip_ws() { while (1) { int ch = getc(); if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { ungetc(); break; } } } bool expect(int expect) { skip_ws(); if (getc() != expect) { ungetc(); return false; } return true; } bool match(const std::string& pattern) { for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { if (getc() != *pi) { ungetc(); return false; } } return true; } }; template inline int _parse_quadhex(input &in) { int uni_ch = 0, hex; for (int i = 0; i < 4; i++) { if ((hex = in.getc()) == -1) { return -1; } if ('0' <= hex && hex <= '9') { hex -= '0'; } else if ('A' <= hex && hex <= 'F') { hex -= 'A' - 0xa; } else if ('a' <= hex && hex <= 'f') { hex -= 'a' - 0xa; } else { in.ungetc(); return -1; } uni_ch = uni_ch * 16 + hex; } return uni_ch; } template inline bool _parse_codepoint(String& out, input& in) { int uni_ch; if ((uni_ch = _parse_quadhex(in)) == -1) { return false; } if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { if (0xdc00 <= uni_ch) { // a second 16-bit of a surrogate pair appeared return false; } // first 16-bit of surrogate pair, get the next one if (in.getc() != '\\' || in.getc() != 'u') { in.ungetc(); return false; } int second = _parse_quadhex(in); if (! (0xdc00 <= second && second <= 0xdfff)) { return false; } uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); uni_ch += 0x10000; } if (uni_ch < 0x80) { out.push_back(uni_ch); } else { if (uni_ch < 0x800) { out.push_back(0xc0 | (uni_ch >> 6)); } else { if (uni_ch < 0x10000) { out.push_back(0xe0 | (uni_ch >> 12)); } else { out.push_back(0xf0 | (uni_ch >> 18)); out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); } out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); } out.push_back(0x80 | (uni_ch & 0x3f)); } return true; } template inline bool _parse_string(String& out, input& in) { while (1) { int ch = in.getc(); if (ch < ' ') { in.ungetc(); return false; } else if (ch == '"') { return true; } else if (ch == '\\') { if ((ch = in.getc()) == -1) { return false; } switch (ch) { #define MAP(sym, val) case sym: out.push_back(val); break MAP('"', '\"'); MAP('\\', '\\'); MAP('/', '/'); MAP('b', '\b'); MAP('f', '\f'); MAP('n', '\n'); MAP('r', '\r'); MAP('t', '\t'); #undef MAP case 'u': if (! _parse_codepoint(out, in)) { return false; } break; default: return false; } } else { out.push_back(ch); } } return false; } template inline bool _parse_array(Context& ctx, input& in) { if (! ctx.parse_array_start()) { return false; } size_t idx = 0; if (in.expect(']')) { return ctx.parse_array_stop(idx); } do { if (! ctx.parse_array_item(in, idx)) { return false; } idx++; } while (in.expect(',')); return in.expect(']') && ctx.parse_array_stop(idx); } template inline bool _parse_object(Context& ctx, input& in) { if (! ctx.parse_object_start()) { return false; } if (in.expect('}')) { return true; } do { std::string key; if (! in.expect('"') || ! _parse_string(key, in) || ! in.expect(':')) { return false; } if (! ctx.parse_object_item(in, key)) { return false; } } while (in.expect(',')); return in.expect('}'); } template inline std::string _parse_number(input& in) { std::string num_str; while (1) { int ch = in.getc(); if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') { num_str.push_back(ch); } else if (ch == '.') { #if PICOJSON_USE_LOCALE num_str += localeconv()->decimal_point; #else num_str.push_back('.'); #endif } else { in.ungetc(); break; } } return num_str; } template inline bool _parse(Context& ctx, input& in) { in.skip_ws(); int ch = in.getc(); switch (ch) { #define IS(ch, text, op) case ch: \ if (in.match(text) && op) { \ return true; \ } else { \ return false; \ } IS('n', "ull", ctx.set_null()); IS('f', "alse", ctx.set_bool(false)); IS('t', "rue", ctx.set_bool(true)); #undef IS case '"': return ctx.parse_string(in); case '[': return _parse_array(ctx, in); case '{': return _parse_object(ctx, in); default: if (('0' <= ch && ch <= '9') || ch == '-') { double f; char *endp; in.ungetc(); std::string num_str = _parse_number(in); if (num_str.empty()) { return false; } #ifdef PICOJSON_USE_INT64 { errno = 0; intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); if (errno == 0 && std::numeric_limits::min() <= ival && ival <= std::numeric_limits::max() && endp == num_str.c_str() + num_str.size()) { ctx.set_int64(ival); return true; } } #endif f = strtod(num_str.c_str(), &endp); if (endp == num_str.c_str() + num_str.size()) { ctx.set_number(f); return true; } return false; } break; } in.ungetc(); return false; } class deny_parse_context { public: bool set_null() { return false; } bool set_bool(bool) { return false; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t) { return false; } #endif bool set_number(double) { return false; } template bool parse_string(input&) { return false; } bool parse_array_start() { return false; } template bool parse_array_item(input&, size_t) { return false; } bool parse_array_stop(size_t) { return false; } bool parse_object_start() { return false; } template bool parse_object_item(input&, const std::string&) { return false; } }; class default_parse_context { protected: value* out_; public: default_parse_context(value* out) : out_(out) {} bool set_null() { *out_ = value(); return true; } bool set_bool(bool b) { *out_ = value(b); return true; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t i) { *out_ = value(i); return true; } #endif bool set_number(double f) { *out_ = value(f); return true; } template bool parse_string(input& in) { *out_ = value(string_type, false); return _parse_string(out_->get(), in); } bool parse_array_start() { *out_ = value(array_type, false); return true; } template bool parse_array_item(input& in, size_t) { array& a = out_->get(); a.push_back(value()); default_parse_context ctx(&a.back()); return _parse(ctx, in); } bool parse_array_stop(size_t) { return true; } bool parse_object_start() { *out_ = value(object_type, false); return true; } template bool parse_object_item(input& in, const std::string& key) { object& o = out_->get(); default_parse_context ctx(&o[key]); return _parse(ctx, in); } private: default_parse_context(const default_parse_context&); default_parse_context& operator=(const default_parse_context&); }; class null_parse_context { public: struct dummy_str { void push_back(int) {} }; public: null_parse_context() {} bool set_null() { return true; } bool set_bool(bool) { return true; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t) { return true; } #endif bool set_number(double) { return true; } template bool parse_string(input& in) { dummy_str s; return _parse_string(s, in); } bool parse_array_start() { return true; } template bool parse_array_item(input& in, size_t) { return _parse(*this, in); } bool parse_array_stop(size_t) { return true; } bool parse_object_start() { return true; } template bool parse_object_item(input& in, const std::string&) { return _parse(*this, in); } private: null_parse_context(const null_parse_context&); null_parse_context& operator=(const null_parse_context&); }; // obsolete, use the version below template inline std::string parse(value& out, Iter& pos, const Iter& last) { std::string err; pos = parse(out, pos, last, &err); return err; } template inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { input in(first, last); if (! _parse(ctx, in) && err != NULL) { char buf[64]; SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); *err = buf; while (1) { int ch = in.getc(); if (ch == -1 || ch == '\n') { break; } else if (ch >= ' ') { err->push_back(ch); } } } return in.cur(); } template inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { default_parse_context ctx(&out); return _parse(ctx, first, last, err); } inline std::string parse(value& out, const std::string& s) { std::string err; parse(out, s.begin(), s.end(), &err); return err; } inline std::string parse(value& out, std::istream& is) { std::string err; parse(out, std::istreambuf_iterator(is.rdbuf()), std::istreambuf_iterator(), &err); return err; } template struct last_error_t { static std::string s; }; template std::string last_error_t::s; inline void set_last_error(const std::string& s) { last_error_t::s = s; } inline const std::string& get_last_error() { return last_error_t::s; } inline bool operator==(const value& x, const value& y) { if (x.is()) return y.is(); #define PICOJSON_CMP(type) \ if (x.is()) \ return y.is() && x.get() == y.get() PICOJSON_CMP(bool); PICOJSON_CMP(double); PICOJSON_CMP(std::string); PICOJSON_CMP(array); PICOJSON_CMP(object); #undef PICOJSON_CMP PICOJSON_ASSERT(0); #ifdef _MSC_VER __assume(0); #endif return false; } inline bool operator!=(const value& x, const value& y) { return ! (x == y); } } namespace std { template<> inline void swap(picojson::value& x, picojson::value& y) { x.swap(y); } } inline std::istream& operator>>(std::istream& is, picojson::value& x) { picojson::set_last_error(std::string()); std::string err = picojson::parse(x, is); if (! err.empty()) { picojson::set_last_error(err); is.setstate(std::ios::failbit); } return is; } inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) { x.serialize(std::ostream_iterator(os)); return os; } #ifdef _MSC_VER #pragma warning(pop) #endif #endif cppcheck-2.7/externals/simplecpp/000077500000000000000000000000001417746362400171615ustar00rootroot00000000000000cppcheck-2.7/externals/simplecpp/CMakeLists.txt000066400000000000000000000001421417746362400217160ustar00rootroot00000000000000file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") add_library(simplecpp_objs OBJECT ${srcs} ${hdrs}) cppcheck-2.7/externals/simplecpp/LICENSE000066400000000000000000000167441417746362400202020ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. cppcheck-2.7/externals/simplecpp/simplecpp.cpp000066400000000000000000003641351417746362400216750ustar00rootroot00000000000000/* * simplecpp - A simple and high-fidelity C/C++ preprocessor library * Copyright (C) 2016 Daniel Marjamäki. * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) #define SIMPLECPP_WINDOWS #define NOMINMAX #endif #include "simplecpp.h" #include #include #include #include #include #include // IWYU pragma: keep #include #include #include // IWYU pragma: keep #include #include #include #include #ifdef SIMPLECPP_WINDOWS #include #undef ERROR #undef TRUE #endif static bool isHex(const std::string &s) { return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0); } static bool isOct(const std::string &s) { return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8'); } static const simplecpp::TokenString DEFINE("define"); static const simplecpp::TokenString UNDEF("undef"); static const simplecpp::TokenString INCLUDE("include"); static const simplecpp::TokenString ERROR("error"); static const simplecpp::TokenString WARNING("warning"); static const simplecpp::TokenString IF("if"); static const simplecpp::TokenString IFDEF("ifdef"); static const simplecpp::TokenString IFNDEF("ifndef"); static const simplecpp::TokenString DEFINED("defined"); static const simplecpp::TokenString ELSE("else"); static const simplecpp::TokenString ELIF("elif"); static const simplecpp::TokenString ENDIF("endif"); static const simplecpp::TokenString PRAGMA("pragma"); static const simplecpp::TokenString ONCE("once"); static const simplecpp::TokenString HAS_INCLUDE("__has_include"); template static std::string toString(T t) { std::ostringstream ostr; ostr << t; return ostr.str(); } static long long stringToLL(const std::string &s) { long long ret; const bool hex = isHex(s); const bool oct = isOct(s); std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); if (hex) istr >> std::hex; else if (oct) istr >> std::oct; istr >> ret; return ret; } static unsigned long long stringToULL(const std::string &s) { unsigned long long ret; const bool hex = isHex(s); const bool oct = isOct(s); std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); if (hex) istr >> std::hex; else if (oct) istr >> std::oct; istr >> ret; return ret; } static bool startsWith(const std::string &str, const std::string &s) { return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0); } static bool endsWith(const std::string &s, const std::string &e) { return (s.size() >= e.size() && s.compare(s.size() - e.size(), e.size(), e) == 0); } static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) { return tok1 && tok2 && tok1->location.sameline(tok2->location); } static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt) { return (tok->name && tok->str() == alt && tok->previous && tok->next && (tok->previous->number || tok->previous->name || tok->previous->op == ')') && (tok->next->number || tok->next->name || tok->next->op == '(')); } static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt) { return ((tok->name && tok->str() == alt) && (!tok->previous || tok->previous->op == '(') && (tok->next && (tok->next->name || tok->next->number))); } static std::string replaceAll(std::string s, const std::string& from, const std::string& to) { for (size_t pos = s.find(from); pos != std::string::npos; pos = s.find(from, pos + to.size())) s.replace(pos, from.size(), to); return s; } const std::string simplecpp::Location::emptyFileName; void simplecpp::Location::adjust(const std::string &str) { if (str.find_first_of("\r\n") == std::string::npos) { col += str.size(); return; } for (std::size_t i = 0U; i < str.size(); ++i) { col++; if (str[i] == '\n' || str[i] == '\r') { col = 1; line++; if (str[i] == '\r' && (i+1)previous) tok = tok->previous; for (; tok; tok = tok->next) { if (tok->previous) { std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); } std::cout << tok->str(); } std::cout << std::endl; } void simplecpp::Token::printOut() const { for (const Token *tok = this; tok; tok = tok->next) { if (tok != this) { std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); } std::cout << tok->str(); } std::cout << std::endl; } simplecpp::TokenList::TokenList(std::vector &filenames) : frontToken(NULL), backToken(NULL), files(filenames) {} simplecpp::TokenList::TokenList(std::istream &istr, std::vector &filenames, const std::string &filename, OutputList *outputList) : frontToken(NULL), backToken(NULL), files(filenames) { readfile(istr,filename,outputList); } simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(NULL), backToken(NULL), files(other.files) { *this = other; } #if __cplusplus >= 201103L simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(NULL), backToken(NULL), files(other.files) { *this = std::move(other); } #endif simplecpp::TokenList::~TokenList() { clear(); } simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other) { if (this != &other) { clear(); for (const Token *tok = other.cfront(); tok; tok = tok->next) push_back(new Token(*tok)); sizeOfType = other.sizeOfType; } return *this; } #if __cplusplus >= 201103L simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other) { if (this != &other) { clear(); backToken = other.backToken; other.backToken = NULL; frontToken = other.frontToken; other.frontToken = NULL; sizeOfType = std::move(other.sizeOfType); } return *this; } #endif void simplecpp::TokenList::clear() { backToken = NULL; while (frontToken) { Token *next = frontToken->next; delete frontToken; frontToken = next; } sizeOfType.clear(); } void simplecpp::TokenList::push_back(Token *tok) { if (!frontToken) frontToken = tok; else backToken->next = tok; tok->previous = backToken; backToken = tok; } void simplecpp::TokenList::dump() const { std::cout << stringify() << std::endl; } std::string simplecpp::TokenList::stringify() const { std::ostringstream ret; Location loc(files); for (const Token *tok = cfront(); tok; tok = tok->next) { if (tok->location.line < loc.line || tok->location.fileIndex != loc.fileIndex) { ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; loc = tok->location; } while (tok->location.line > loc.line) { ret << '\n'; loc.line++; } if (sameline(tok->previous, tok)) ret << ' '; ret << tok->str(); loc.adjust(tok->str()); } return ret.str(); } static unsigned char readChar(std::istream &istr, unsigned int bom) { unsigned char ch = (unsigned char)istr.get(); // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the // character is non-ASCII character then replace it with 0xff if (bom == 0xfeff || bom == 0xfffe) { const unsigned char ch2 = (unsigned char)istr.get(); const int ch16 = (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch); ch = (unsigned char)((ch16 >= 0x80) ? 0xff : ch16); } // Handling of newlines.. if (ch == '\r') { ch = '\n'; if (bom == 0 && (char)istr.peek() == '\n') (void)istr.get(); else if (bom == 0xfeff || bom == 0xfffe) { int c1 = istr.get(); int c2 = istr.get(); int ch16 = (bom == 0xfeff) ? (c1<<8 | c2) : (c2<<8 | c1); if (ch16 != '\n') { istr.unget(); istr.unget(); } } } return ch; } static unsigned char peekChar(std::istream &istr, unsigned int bom) { unsigned char ch = (unsigned char)istr.peek(); // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the // character is non-ASCII character then replace it with 0xff if (bom == 0xfeff || bom == 0xfffe) { (void)istr.get(); const unsigned char ch2 = (unsigned char)istr.peek(); istr.unget(); const int ch16 = (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch); ch = (unsigned char)((ch16 >= 0x80) ? 0xff : ch16); } // Handling of newlines.. if (ch == '\r') ch = '\n'; return ch; } static void ungetChar(std::istream &istr, unsigned int bom) { istr.unget(); if (bom == 0xfeff || bom == 0xfffe) istr.unget(); } static unsigned short getAndSkipBOM(std::istream &istr) { const int ch1 = istr.peek(); // The UTF-16 BOM is 0xfffe or 0xfeff. if (ch1 >= 0xfe) { unsigned short bom = ((unsigned char)istr.get() << 8); if (istr.peek() >= 0xfe) return bom | (unsigned char)istr.get(); istr.unget(); return 0; } // Skip UTF-8 BOM 0xefbbbf if (ch1 == 0xef) { (void)istr.get(); if (istr.get() == 0xbb && istr.peek() == 0xbf) { (void)istr.get(); } else { istr.unget(); istr.unget(); } } return 0; } static bool isNameChar(unsigned char ch) { return std::isalnum(ch) || ch == '_' || ch == '$'; } static std::string escapeString(const std::string &str) { std::ostringstream ostr; ostr << '\"'; for (std::size_t i = 1U; i < str.size() - 1; ++i) { char c = str[i]; if (c == '\\' || c == '\"' || c == '\'') ostr << '\\'; ostr << c; } ostr << '\"'; return ostr.str(); } static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector &files, const simplecpp::Location &location) { if (!outputList) return; simplecpp::Output err(files); err.type = simplecpp::Output::PORTABILITY_BACKSLASH; err.location = location; err.msg = "Combination 'backslash space newline' is not portable."; outputList->push_back(err); } static bool isStringLiteralPrefix(const std::string &str) { return str == "u" || str == "U" || str == "L" || str == "u8" || str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R"; } void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location *location) { if (fileIndex != location->fileIndex || line >= location->line) { location->fileIndex = fileIndex; location->line = line; return; } if (line + 2 >= location->line) { location->line = line; while (cback()->op != '#') deleteToken(back()); deleteToken(back()); return; } } void simplecpp::TokenList::readfile(std::istream &istr, const std::string &filename, OutputList *outputList) { std::stack loc; unsigned int multiline = 0U; const Token *oldLastToken = NULL; const unsigned short bom = getAndSkipBOM(istr); Location location(files); location.fileIndex = fileIndex(filename); location.line = 1U; location.col = 1U; while (istr.good()) { unsigned char ch = readChar(istr,bom); if (!istr.good()) break; if (ch < ' ' && ch != '\t' && ch != '\n' && ch != '\r') ch = ' '; if (ch >= 0x80) { if (outputList) { simplecpp::Output err(files); err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR; err.location = location; std::ostringstream s; s << (int)ch; err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported."; outputList->push_back(err); } clear(); return; } if (ch == '\n') { if (cback() && cback()->op == '\\') { if (location.col > cback()->location.col + 1U) portabilityBackslash(outputList, files, cback()->location); ++multiline; deleteToken(back()); } else { location.line += multiline + 1; multiline = 0U; } if (!multiline) location.col = 1; if (oldLastToken != cback()) { oldLastToken = cback(); const std::string lastline(lastLine()); if (lastline == "# file %str%") { const Token *strtok = cback(); while (strtok->comment) strtok = strtok->previous; loc.push(location); location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); location.line = 1U; } else if (lastline == "# line %num%") { const Token *numtok = cback(); while (numtok->comment) numtok = numtok->previous; lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), &location); } else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") { const Token *strtok = cback(); while (strtok->comment) strtok = strtok->previous; const Token *numtok = strtok->previous; while (numtok->comment) numtok = numtok->previous; lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")), std::atol(numtok->str().c_str()), &location); } // #endfile else if (lastline == "# endfile" && !loc.empty()) { location = loc.top(); loc.pop(); } } continue; } if (std::isspace(ch)) { location.col++; continue; } TokenString currentToken; if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#' && (lastLine() == "# error" || lastLine() == "# warning")) { char prev = ' '; while (istr.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) { currentToken += ch; prev = ch; ch = readChar(istr, bom); } ungetChar(istr, bom); push_back(new Token(currentToken, location)); location.adjust(currentToken); continue; } // number or name if (isNameChar(ch)) { const bool num = std::isdigit(ch); while (istr.good() && isNameChar(ch)) { currentToken += ch; ch = readChar(istr,bom); if (num && ch=='\'' && isNameChar(peekChar(istr,bom))) ch = readChar(istr,bom); } ungetChar(istr,bom); } // comment else if (ch == '/' && peekChar(istr,bom) == '/') { while (istr.good() && ch != '\r' && ch != '\n') { currentToken += ch; ch = readChar(istr, bom); } const std::string::size_type pos = currentToken.find_last_not_of(" \t"); if (pos < currentToken.size() - 1U && currentToken[pos] == '\\') portabilityBackslash(outputList, files, location); if (currentToken[currentToken.size() - 1U] == '\\') { ++multiline; currentToken.erase(currentToken.size() - 1U); } else { ungetChar(istr, bom); } } // comment else if (ch == '/' && peekChar(istr,bom) == '*') { currentToken = "/*"; (void)readChar(istr,bom); ch = readChar(istr,bom); while (istr.good()) { currentToken += ch; if (currentToken.size() >= 4U && endsWith(currentToken, "*/")) break; ch = readChar(istr,bom); } // multiline.. std::string::size_type pos = 0; while ((pos = currentToken.find("\\\n",pos)) != std::string::npos) { currentToken.erase(pos,2); ++multiline; } if (multiline || startsWith(lastLine(10),"# ")) { pos = 0; while ((pos = currentToken.find('\n',pos)) != std::string::npos) { currentToken.erase(pos,1); ++multiline; } } } // string / char literal else if (ch == '\"' || ch == '\'') { std::string prefix; if (cback() && cback()->name && isStringLiteralPrefix(cback()->str()) && ((cback()->location.col + cback()->str().size()) == location.col) && (cback()->location.line == location.line)) { prefix = cback()->str(); } // C++11 raw string literal if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') { std::string delim; currentToken = ch; prefix.resize(prefix.size() - 1); ch = readChar(istr,bom); while (istr.good() && ch != '(' && ch != '\n') { delim += ch; ch = readChar(istr,bom); } if (!istr.good() || ch == '\n') { if (outputList) { Output err(files); err.type = Output::SYNTAX_ERROR; err.location = location; err.msg = "Invalid newline in raw string delimiter."; outputList->push_back(err); } return; } const std::string endOfRawString(')' + delim + currentToken); while (istr.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1)) currentToken += readChar(istr,bom); if (!endsWith(currentToken, endOfRawString)) { if (outputList) { Output err(files); err.type = Output::SYNTAX_ERROR; err.location = location; err.msg = "Raw string missing terminating delimiter."; outputList->push_back(err); } return; } currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U); currentToken = escapeString(currentToken); currentToken.insert(0, prefix); back()->setstr(currentToken); location.adjust(currentToken); if (currentToken.find_first_of("\r\n") == std::string::npos) location.col += 2 + 2 * delim.size(); else location.col += 1 + delim.size(); continue; } currentToken = readUntil(istr,location,ch,ch,outputList,bom); if (currentToken.size() < 2U) // Error is reported by readUntil() return; std::string s = currentToken; std::string::size_type pos; int newlines = 0; while ((pos = s.find_first_of("\r\n")) != std::string::npos) { s.erase(pos,1); newlines++; } if (prefix.empty()) push_back(new Token(s, location)); // push string without newlines else back()->setstr(prefix + s); if (newlines > 0 && lastLine().compare(0,9,"# define ") == 0) { multiline += newlines; location.adjust(s); } else { location.adjust(currentToken); } continue; } else { currentToken += ch; } if (currentToken == "<" && lastLine() == "# include") { currentToken = readUntil(istr, location, '<', '>', outputList, bom); if (currentToken.size() < 2U) return; } push_back(new Token(currentToken, location)); if (multiline) location.col += currentToken.size(); else location.adjust(currentToken); } combineOperators(); } void simplecpp::TokenList::constFold() { while (cfront()) { // goto last '(' Token *tok = back(); while (tok && tok->op != '(') tok = tok->previous; // no '(', goto first token if (!tok) tok = front(); // Constant fold expression constFoldUnaryNotPosNeg(tok); constFoldMulDivRem(tok); constFoldAddSub(tok); constFoldShift(tok); constFoldComparison(tok); constFoldBitwise(tok); constFoldLogicalOp(tok); constFoldQuestionOp(&tok); // If there is no '(' we are done with the constant folding if (tok->op != '(') break; if (!tok->next || !tok->next->next || tok->next->next->op != ')') break; tok = tok->next; deleteToken(tok->previous); deleteToken(tok->next); } } static bool isFloatSuffix(const simplecpp::Token *tok) { if (!tok || tok->str().size() != 1U) return false; const char c = std::tolower(tok->str()[0]); return c == 'f' || c == 'l'; } void simplecpp::TokenList::combineOperators() { std::stack executableScope; executableScope.push(false); for (Token *tok = front(); tok; tok = tok->next) { if (tok->op == '{') { if (executableScope.top()) { executableScope.push(true); continue; } const Token *prev = tok->previous; while (prev && prev->isOneOf(";{}()")) prev = prev->previous; executableScope.push(prev && prev->op == ')'); continue; } if (tok->op == '}') { if (executableScope.size() > 1) executableScope.pop(); continue; } if (tok->op == '.') { // ellipsis ... if (tok->next && tok->next->op == '.' && tok->next->location.col == (tok->location.col + 1) && tok->next->next && tok->next->next->op == '.' && tok->next->next->location.col == (tok->location.col + 2)) { tok->setstr("..."); deleteToken(tok->next); deleteToken(tok->next); continue; } // float literals.. if (tok->previous && tok->previous->number) { tok->setstr(tok->previous->str() + '.'); deleteToken(tok->previous); if (isFloatSuffix(tok->next) || (tok->next && tok->next->startsWithOneOf("AaBbCcDdEeFfPp"))) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } if (tok->next && tok->next->number) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } // match: [0-9.]+E [+-] [0-9]+ const char lastChar = tok->str()[tok->str().size() - 1]; if (tok->number && !isOct(tok->str()) && ((!isHex(tok->str()) && (lastChar == 'E' || lastChar == 'e')) || (isHex(tok->str()) && (lastChar == 'P' || lastChar == 'p'))) && tok->next && tok->next->isOneOf("+-") && tok->next->next && tok->next->next->number) { tok->setstr(tok->str() + tok->next->op + tok->next->next->str()); deleteToken(tok->next); deleteToken(tok->next); } if (tok->op == '\0' || !tok->next || tok->next->op == '\0') continue; if (!sameline(tok,tok->next)) continue; if (tok->location.col + 1U != tok->next->location.col) continue; if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) { if (tok->op == '&' && !executableScope.top()) { // don't combine &= if it is a anonymous reference parameter with default value: // void f(x&=2) int indentlevel = 0; const Token *start = tok; while (indentlevel >= 0 && start) { if (start->op == ')') ++indentlevel; else if (start->op == '(') --indentlevel; else if (start->isOneOf(";{}")) break; start = start->previous; } if (indentlevel == -1 && start) { const Token *ftok = start; bool isFuncDecl = ftok->name; while (isFuncDecl) { if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&') isFuncDecl = false; if (!start->previous) break; if (start->previous->isOneOf(";{}:")) break; start = start->previous; } isFuncDecl &= start != ftok && start->name; if (isFuncDecl) { // TODO: we could loop through the parameters here and check if they are correct. continue; } } } tok->setstr(tok->str() + "="); deleteToken(tok->next); } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } else if (tok->op == ':' && tok->next->op == ':') { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } else if (tok->op == '-' && tok->next->op == '>') { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } else if ((tok->op == '<' || tok->op == '>') && tok->op == tok->next->op) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) { if (tok->location.col + 1U != tok->next->location.col) continue; if (tok->previous && tok->previous->number) continue; if (tok->next->next && tok->next->next->number) continue; tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } } static const std::string COMPL("compl"); static const std::string NOT("not"); void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { // "not" might be ! if (isAlternativeUnaryOp(tok, NOT)) tok->op = '!'; // "compl" might be ~ else if (isAlternativeUnaryOp(tok, COMPL)) tok->op = '~'; if (tok->op == '!' && tok->next && tok->next->number) { tok->setstr(tok->next->str() == "0" ? "1" : "0"); deleteToken(tok->next); } else if (tok->op == '~' && tok->next && tok->next->number) { tok->setstr(toString(~stringToLL(tok->next->str()))); deleteToken(tok->next); } else { if (tok->previous && (tok->previous->number || tok->previous->name)) continue; if (!tok->next || !tok->next->number) continue; switch (tok->op) { case '+': tok->setstr(tok->next->str()); deleteToken(tok->next); break; case '-': tok->setstr(tok->op + tok->next->str()); deleteToken(tok->next); break; } } } } void simplecpp::TokenList::constFoldMulDivRem(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (tok->op == '*') result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str())); else if (tok->op == '/' || tok->op == '%') { long long rhs = stringToLL(tok->next->str()); if (rhs == 0) throw std::overflow_error("division/modulo by zero"); long long lhs = stringToLL(tok->previous->str()); if (rhs == -1 && lhs == std::numeric_limits::min()) throw std::overflow_error("division overflow"); if (tok->op == '/') result = (lhs / rhs); else result = (lhs % rhs); } else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } void simplecpp::TokenList::constFoldAddSub(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (tok->op == '+') result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str()); else if (tok->op == '-') result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str()); else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } void simplecpp::TokenList::constFoldShift(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (tok->str() == "<<") result = stringToLL(tok->previous->str()) << stringToLL(tok->next->str()); else if (tok->str() == ">>") result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str()); else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } static const std::string NOTEQ("not_eq"); void simplecpp::TokenList::constFoldComparison(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (isAlternativeBinaryOp(tok,NOTEQ)) tok->setstr("!="); if (!tok->startsWithOneOf("<>=!")) continue; if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; int result; if (tok->str() == "==") result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str())); else if (tok->str() == "!=") result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str())); else if (tok->str() == ">") result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str())); else if (tok->str() == ">=") result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str())); else if (tok->str() == "<") result = (stringToLL(tok->previous->str()) < stringToLL(tok->next->str())); else if (tok->str() == "<=") result = (stringToLL(tok->previous->str()) <= stringToLL(tok->next->str())); else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } static const std::string BITAND("bitand"); static const std::string BITOR("bitor"); static const std::string XOR("xor"); void simplecpp::TokenList::constFoldBitwise(Token *tok) { Token * const tok1 = tok; for (const char *op = "&^|"; *op; op++) { const std::string* alternativeOp; if (*op == '&') alternativeOp = &BITAND; else if (*op == '|') alternativeOp = &BITOR; else alternativeOp = &XOR; for (tok = tok1; tok && tok->op != ')'; tok = tok->next) { if (tok->op != *op && !isAlternativeBinaryOp(tok, *alternativeOp)) continue; if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (*op == '&') result = (stringToLL(tok->previous->str()) & stringToLL(tok->next->str())); else if (*op == '^') result = (stringToLL(tok->previous->str()) ^ stringToLL(tok->next->str())); else /*if (*op == '|')*/ result = (stringToLL(tok->previous->str()) | stringToLL(tok->next->str())); tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } } static const std::string AND("and"); static const std::string OR("or"); void simplecpp::TokenList::constFoldLogicalOp(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (tok->name) { if (isAlternativeBinaryOp(tok,AND)) tok->setstr("&&"); else if (isAlternativeBinaryOp(tok,OR)) tok->setstr("||"); } if (tok->str() != "&&" && tok->str() != "||") continue; if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; int result; if (tok->str() == "||") result = (stringToLL(tok->previous->str()) || stringToLL(tok->next->str())); else /*if (tok->str() == "&&")*/ result = (stringToLL(tok->previous->str()) && stringToLL(tok->next->str())); tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } void simplecpp::TokenList::constFoldQuestionOp(Token **tok1) { bool gotoTok1 = false; for (Token *tok = *tok1; tok && tok->op != ')'; tok = gotoTok1 ? *tok1 : tok->next) { gotoTok1 = false; if (tok->str() != "?") continue; if (!tok->previous || !tok->next || !tok->next->next) throw std::runtime_error("invalid expression"); if (!tok->previous->number) continue; if (tok->next->next->op != ':') continue; Token * const condTok = tok->previous; Token * const trueTok = tok->next; Token * const falseTok = trueTok->next->next; if (!falseTok) throw std::runtime_error("invalid expression"); if (condTok == *tok1) *tok1 = (condTok->str() != "0" ? trueTok : falseTok); deleteToken(condTok->next); // ? deleteToken(trueTok->next); // : deleteToken(condTok->str() == "0" ? trueTok : falseTok); deleteToken(condTok); gotoTok1 = true; } } void simplecpp::TokenList::removeComments() { Token *tok = frontToken; while (tok) { Token *tok1 = tok; tok = tok->next; if (tok1->comment) deleteToken(tok1); } } std::string simplecpp::TokenList::readUntil(std::istream &istr, const Location &location, const char start, const char end, OutputList *outputList, unsigned int bom) { std::string ret; ret += start; bool backslash = false; char ch = 0; while (ch != end && ch != '\r' && ch != '\n' && istr.good()) { ch = readChar(istr, bom); if (backslash && ch == '\n') { ch = 0; backslash = false; continue; } backslash = false; ret += ch; if (ch == '\\') { bool update_ch = false; char next = 0; do { next = readChar(istr, bom); if (next == '\r' || next == '\n') { ret.erase(ret.size()-1U); backslash = (next == '\r'); update_ch = false; } else if (next == '\\') update_ch = !update_ch; ret += next; } while (next == '\\'); if (update_ch) ch = next; } } if (!istr.good() || ch != end) { clear(); if (outputList) { Output err(files); err.type = Output::SYNTAX_ERROR; err.location = location; err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported."; outputList->push_back(err); } return ""; } return ret; } std::string simplecpp::TokenList::lastLine(int maxsize) const { std::string ret; int count = 0; for (const Token *tok = cback(); sameline(tok,cback()); tok = tok->previous) { if (tok->comment) continue; if (!ret.empty()) ret.insert(0, 1, ' '); ret.insert(0, tok->str()[0] == '\"' ? std::string("%str%") : tok->number ? std::string("%num%") : tok->str()); if (++count > maxsize) return ""; } return ret; } unsigned int simplecpp::TokenList::fileIndex(const std::string &filename) { for (unsigned int i = 0; i < files.size(); ++i) { if (files[i] == filename) return i; } files.push_back(filename); return files.size() - 1U; } namespace simplecpp { class Macro { public: explicit Macro(std::vector &f) : nameTokDef(NULL), variadic(false), valueToken(NULL), endToken(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) {} Macro(const Token *tok, std::vector &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(true) { if (sameline(tok->previous, tok)) throw std::runtime_error("bad macro syntax"); if (tok->op != '#') throw std::runtime_error("bad macro syntax"); const Token * const hashtok = tok; tok = tok->next; if (!tok || tok->str() != DEFINE) throw std::runtime_error("bad macro syntax"); tok = tok->next; if (!tok || !tok->name || !sameline(hashtok,tok)) throw std::runtime_error("bad macro syntax"); if (!parseDefine(tok)) throw std::runtime_error("bad macro syntax"); } Macro(const std::string &name, const std::string &value, std::vector &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) { const std::string def(name + ' ' + value); std::istringstream istr(def); tokenListDefine.readfile(istr); if (!parseDefine(tokenListDefine.cfront())) throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value); } Macro(const Macro ¯o) : nameTokDef(NULL), files(macro.files), tokenListDefine(macro.files), valueDefinedInCode_(macro.valueDefinedInCode_) { *this = macro; } void operator=(const Macro ¯o) { if (this != ¯o) { valueDefinedInCode_ = macro.valueDefinedInCode_; if (macro.tokenListDefine.empty()) parseDefine(macro.nameTokDef); else { tokenListDefine = macro.tokenListDefine; parseDefine(tokenListDefine.cfront()); } } } bool valueDefinedInCode() const { return valueDefinedInCode_; } /** * Expand macro. This will recursively expand inner macros. * @param output destination tokenlist * @param rawtok macro token * @param macros list of macros * @param inputFiles the input files * @return token after macro * @throw Can throw wrongNumberOfParameters or invalidHashHash */ const Token * expand(TokenList * const output, const Token * rawtok, const std::map ¯os, std::vector &inputFiles) const { std::set expandedmacros; TokenList output2(inputFiles); if (functionLike() && rawtok->next && rawtok->next->op == '(') { // Copy macro call to a new tokenlist with no linebreaks const Token * const rawtok1 = rawtok; TokenList rawtokens2(inputFiles); rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); rawtok = rawtok->next; rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); rawtok = rawtok->next; int par = 1; while (rawtok && par > 0) { if (rawtok->op == '(') ++par; else if (rawtok->op == ')') --par; else if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) throw Error(rawtok->location, "it is invalid to use a preprocessor directive as macro parameter"); rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); rawtok = rawtok->next; } bool first = true; if (valueToken && valueToken->str() == rawtok1->str()) first = false; if (expand(&output2, rawtok1->location, rawtokens2.cfront(), macros, expandedmacros, first)) rawtok = rawtok1->next; } else { rawtok = expand(&output2, rawtok->location, rawtok, macros, expandedmacros); } while (output2.cback() && rawtok) { unsigned int par = 0; Token* macro2tok = output2.back(); while (macro2tok) { if (macro2tok->op == '(') { if (par==0) break; --par; } else if (macro2tok->op == ')') ++par; macro2tok = macro2tok->previous; } if (macro2tok) { // macro2tok->op == '(' macro2tok = macro2tok->previous; expandedmacros.insert(name()); } else if (rawtok->op == '(') macro2tok = output2.back(); if (!macro2tok || !macro2tok->name) break; if (output2.cfront() != output2.cback() && macro2tok->str() == this->name()) break; const std::map::const_iterator macro = macros.find(macro2tok->str()); if (macro == macros.end() || !macro->second.functionLike()) break; TokenList rawtokens2(inputFiles); const Location loc(macro2tok->location); while (macro2tok) { Token *next = macro2tok->next; rawtokens2.push_back(new Token(macro2tok->str(), loc)); output2.deleteToken(macro2tok); macro2tok = next; } par = (rawtokens2.cfront() != rawtokens2.cback()) ? 1U : 0U; const Token *rawtok2 = rawtok; for (; rawtok2; rawtok2 = rawtok2->next) { rawtokens2.push_back(new Token(rawtok2->str(), loc)); if (rawtok2->op == '(') ++par; else if (rawtok2->op == ')') { if (par <= 1U) break; --par; } } if (!rawtok2 || par != 1U) break; if (macro->second.expand(&output2, rawtok->location, rawtokens2.cfront(), macros, expandedmacros) != NULL) break; rawtok = rawtok2->next; } output->takeTokens(output2); return rawtok; } /** macro name */ const TokenString &name() const { return nameTokDef->str(); } /** location for macro definition */ const Location &defineLocation() const { return nameTokDef->location; } /** how has this macro been used so far */ const std::list &usage() const { return usageList; } /** is this a function like macro */ bool functionLike() const { return nameTokDef->next && nameTokDef->next->op == '(' && sameline(nameTokDef, nameTokDef->next) && nameTokDef->next->location.col == nameTokDef->location.col + nameTokDef->str().size(); } /** base class for errors */ struct Error { Error(const Location &loc, const std::string &s) : location(loc), what(s) {} Location location; std::string what; }; /** Struct that is thrown when macro is expanded with wrong number of parameters */ struct wrongNumberOfParameters : public Error { wrongNumberOfParameters(const Location &loc, const std::string ¯oName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {} }; /** Struct that is thrown when there is invalid ## usage */ struct invalidHashHash : public Error { invalidHashHash(const Location &loc, const std::string ¯oName) : Error(loc, "Invalid ## usage when expanding \'" + macroName + "\'.") {} }; private: /** Create new token where Token::macro is set for replaced tokens */ Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced, const Token *expandedFromToken=NULL) const { Token *tok = new Token(str,loc); if (replaced) tok->macro = nameTokDef->str(); if (expandedFromToken) tok->setExpandedFrom(expandedFromToken, this); return tok; } bool parseDefine(const Token *nametoken) { nameTokDef = nametoken; variadic = false; if (!nameTokDef) { valueToken = endToken = NULL; args.clear(); return false; } // function like macro.. if (functionLike()) { args.clear(); const Token *argtok = nameTokDef->next->next; while (sameline(nametoken, argtok) && argtok->op != ')') { if (argtok->str() == "..." && argtok->next && argtok->next->op == ')') { variadic = true; if (!argtok->previous->name) args.push_back("__VA_ARGS__"); argtok = argtok->next; // goto ')' break; } if (argtok->op != ',') args.push_back(argtok->str()); argtok = argtok->next; } if (!sameline(nametoken, argtok)) { endToken = argtok ? argtok->previous : argtok; valueToken = NULL; return false; } valueToken = argtok ? argtok->next : NULL; } else { args.clear(); valueToken = nameTokDef->next; } if (!sameline(valueToken, nameTokDef)) valueToken = NULL; endToken = valueToken; while (sameline(endToken, nameTokDef)) endToken = endToken->next; return true; } unsigned int getArgNum(const TokenString &str) const { unsigned int par = 0; while (par < args.size()) { if (str == args[par]) return par; par++; } return ~0U; } std::vector getMacroParameters(const Token *nameTokInst, bool calledInDefine) const { if (!nameTokInst->next || nameTokInst->next->op != '(' || !functionLike()) return std::vector(); std::vector parametertokens; parametertokens.push_back(nameTokInst->next); unsigned int par = 0U; for (const Token *tok = nameTokInst->next->next; calledInDefine ? sameline(tok, nameTokInst) : (tok != NULL); tok = tok->next) { if (tok->op == '(') ++par; else if (tok->op == ')') { if (par == 0U) { parametertokens.push_back(tok); break; } --par; } else if (par == 0U && tok->op == ',' && (!variadic || parametertokens.size() < args.size())) parametertokens.push_back(tok); } return parametertokens; } const Token *appendTokens(TokenList *tokens, const Location &rawloc, const Token * const lpar, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { if (!lpar || lpar->op != '(') return NULL; unsigned int par = 0; const Token *tok = lpar; while (sameline(lpar, tok)) { if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) { // A##B => AB tok = expandHashHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens); } else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') { tok = expandHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens); } else { if (!expandArg(tokens, tok, rawloc, macros, expandedmacros, parametertokens)) { bool expanded = false; const std::map::const_iterator it = macros.find(tok->str()); if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { const Macro &m = it->second; if (!m.functionLike()) { m.expand(tokens, rawloc, tok, macros, expandedmacros); expanded = true; } } if (!expanded) { tokens->push_back(new Token(*tok)); if (tok->macro.empty() && (par > 0 || tok->str() != "(")) tokens->back()->macro = name(); } } if (tok->op == '(') ++par; else if (tok->op == ')') { --par; if (par == 0U) break; } tok = tok->next; } } for (Token *tok2 = tokens->front(); tok2; tok2 = tok2->next) tok2->location = lpar->location; return sameline(lpar,tok) ? tok : NULL; } const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const std::map ¯os, std::set expandedmacros, bool first=false) const { if (!first) expandedmacros.insert(nameTokInst->str()); usageList.push_back(loc); if (nameTokInst->str() == "__FILE__") { output->push_back(new Token('\"'+loc.file()+'\"', loc)); return nameTokInst->next; } if (nameTokInst->str() == "__LINE__") { output->push_back(new Token(toString(loc.line), loc)); return nameTokInst->next; } if (nameTokInst->str() == "__COUNTER__") { output->push_back(new Token(toString(usageList.size()-1U), loc)); return nameTokInst->next; } const bool calledInDefine = (loc.fileIndex != nameTokInst->location.fileIndex || loc.line < nameTokInst->location.line); std::vector parametertokens1(getMacroParameters(nameTokInst, calledInDefine)); if (functionLike()) { // No arguments => not macro expansion if (nameTokInst->next && nameTokInst->next->op != '(') { output->push_back(new Token(nameTokInst->str(), loc)); return nameTokInst->next; } // Parse macro-call if (variadic) { if (parametertokens1.size() < args.size()) { throw wrongNumberOfParameters(nameTokInst->location, name()); } } else { if (parametertokens1.size() != args.size() + (args.empty() ? 2U : 1U)) throw wrongNumberOfParameters(nameTokInst->location, name()); } } // If macro call uses __COUNTER__ then expand that first TokenList tokensparams(files); std::vector parametertokens2; if (!parametertokens1.empty()) { bool counter = false; for (const Token *tok = parametertokens1[0]; tok != parametertokens1.back(); tok = tok->next) { if (tok->str() == "__COUNTER__") { counter = true; break; } } const std::map::const_iterator m = macros.find("__COUNTER__"); if (!counter || m == macros.end()) parametertokens2.swap(parametertokens1); else { const Macro &counterMacro = m->second; unsigned int par = 0; for (const Token *tok = parametertokens1[0]; tok && par < parametertokens1.size(); tok = tok->next) { if (tok->str() == "__COUNTER__") { tokensparams.push_back(new Token(toString(counterMacro.usageList.size()), tok->location)); counterMacro.usageList.push_back(tok->location); } else { tokensparams.push_back(new Token(*tok)); if (tok == parametertokens1[par]) { parametertokens2.push_back(tokensparams.cback()); par++; } } } } } Token * const output_end_1 = output->back(); // expand for (const Token *tok = valueToken; tok != endToken;) { if (tok->op != '#') { // A##B => AB if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') { if (!sameline(tok, tok->next->next->next)) throw invalidHashHash(tok->location, name()); TokenList new_output(files); if (!expandArg(&new_output, tok, parametertokens2)) output->push_back(newMacroToken(tok->str(), loc, isReplaced(expandedmacros), tok)); else if (new_output.empty()) // placemarker token output->push_back(newMacroToken("", loc, isReplaced(expandedmacros))); else for (const Token *tok2 = new_output.cfront(); tok2; tok2 = tok2->next) output->push_back(newMacroToken(tok2->str(), loc, isReplaced(expandedmacros), tok2)); tok = tok->next; } else { tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens2); } continue; } int numberOfHash = 1; const Token *hashToken = tok->next; while (sameline(tok,hashToken) && hashToken->op == '#') { hashToken = hashToken->next; ++numberOfHash; } if (numberOfHash == 4 && tok->next->location.col + 1 == tok->next->next->location.col) { // # ## # => ## output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros))); tok = hashToken; continue; } if (numberOfHash >= 2 && tok->location.col + 1 < tok->next->location.col) { output->push_back(new Token(*tok)); tok = tok->next; continue; } tok = tok->next; if (tok == endToken) { output->push_back(new Token(*tok->previous)); break; } if (tok->op == '#') { // A##B => AB tok = expandHashHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); } else { // #123 => "123" tok = expandHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); } } if (!functionLike()) { for (Token *tok = output_end_1 ? output_end_1->next : output->front(); tok; tok = tok->next) { tok->macro = nameTokInst->str(); } } if (!parametertokens1.empty()) parametertokens1.swap(parametertokens2); return functionLike() ? parametertokens2.back()->next : nameTokInst->next; } const Token *recursiveExpandToken(TokenList *output, TokenList &temp, const Location &loc, const Token *tok, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) { output->takeTokens(temp); return tok->next; } if (!sameline(tok, tok->next)) { output->takeTokens(temp); return tok->next; } const std::map::const_iterator it = macros.find(temp.cback()->str()); if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) { output->takeTokens(temp); return tok->next; } const Macro &calledMacro = it->second; if (!calledMacro.functionLike()) { output->takeTokens(temp); return tok->next; } TokenList temp2(files); temp2.push_back(new Token(temp.cback()->str(), tok->location)); const Token *tok2 = appendTokens(&temp2, loc, tok->next, macros, expandedmacros, parametertokens); if (!tok2) return tok->next; output->takeTokens(temp); output->deleteToken(output->back()); calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros); return tok2->next; } const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { // Not name.. if (!tok->name) { output->push_back(newMacroToken(tok->str(), loc, true, tok)); return tok->next; } // Macro parameter.. { TokenList temp(files); if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros, parametertokens); } // Macro.. const std::map::const_iterator it = macros.find(tok->str()); if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { std::set expandedmacros2(expandedmacros); expandedmacros2.insert(tok->str()); const Macro &calledMacro = it->second; if (!calledMacro.functionLike()) { TokenList temp(files); calledMacro.expand(&temp, loc, tok, macros, expandedmacros); return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros2, parametertokens); } if (!sameline(tok, tok->next) || tok->next->op != '(') { output->push_back(newMacroToken(tok->str(), loc, true, tok)); return tok->next; } TokenList tokens(files); tokens.push_back(new Token(*tok)); const Token *tok2 = appendTokens(&tokens, loc, tok->next, macros, expandedmacros, parametertokens); if (!tok2) { output->push_back(newMacroToken(tok->str(), loc, true, tok)); return tok->next; } TokenList temp(files); calledMacro.expand(&temp, loc, tokens.cfront(), macros, expandedmacros); return recursiveExpandToken(output, temp, loc, tok2, macros, expandedmacros2, parametertokens); } else if (tok->str() == DEFINED) { const Token *tok2 = tok->next; const Token *tok3 = tok2 ? tok2->next : NULL; const Token *tok4 = tok3 ? tok3->next : NULL; const Token *defToken = NULL; const Token *lastToken = NULL; if (sameline(tok, tok4) && tok2->op == '(' && tok3->name && tok4->op == ')') { defToken = tok3; lastToken = tok4; } else if (sameline(tok,tok2) && tok2->name) { defToken = lastToken = tok2; } if (defToken) { std::string macroName = defToken->str(); if (defToken->next && defToken->next->op == '#' && defToken->next->next && defToken->next->next->op == '#' && defToken->next->next->next && defToken->next->next->next->name && sameline(defToken,defToken->next->next->next)) { TokenList temp(files); if (expandArg(&temp, defToken, parametertokens)) macroName = temp.cback()->str(); if (expandArg(&temp, defToken->next->next->next, parametertokens)) macroName += temp.cback()->str(); else macroName += defToken->next->next->next->str(); lastToken = defToken->next->next->next; } const bool def = (macros.find(macroName) != macros.end()); output->push_back(newMacroToken(def ? "1" : "0", loc, true)); return lastToken->next; } } output->push_back(newMacroToken(tok->str(), loc, true, tok)); return tok->next; } bool expandArg(TokenList *output, const Token *tok, const std::vector ¶metertokens) const { if (!tok->name) return false; const unsigned int argnr = getArgNum(tok->str()); if (argnr >= args.size()) return false; // empty variadic parameter if (variadic && argnr + 1U >= parametertokens.size()) return true; for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next) output->push_back(new Token(*partok)); return true; } bool expandArg(TokenList *output, const Token *tok, const Location &loc, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { if (!tok->name) return false; const unsigned int argnr = getArgNum(tok->str()); if (argnr >= args.size()) return false; if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter return true; for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) { const std::map::const_iterator it = macros.find(partok->str()); if (it != macros.end() && !partok->isExpandedFrom(&it->second) && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end())) partok = it->second.expand(output, loc, partok, macros, expandedmacros); else { output->push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros), partok)); output->back()->macro = partok->macro; partok = partok->next; } } return true; } /** * Expand #X => "X" * @param output destination tokenlist * @param loc location for expanded token * @param tok The # token * @param macros all macros * @param expandedmacros set with expanded macros, with this macro * @param parametertokens parameters given when expanding this macro * @return token after the X */ const Token *expandHash(TokenList *output, const Location &loc, const Token *tok, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { TokenList tokenListHash(files); tok = expandToken(&tokenListHash, loc, tok->next, macros, expandedmacros, parametertokens); std::ostringstream ostr; ostr << '\"'; for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next) ostr << hashtok->str(); ostr << '\"'; output->push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros))); return tok; } /** * Expand A##B => AB * The A should already be expanded. Call this when you reach the first # token * @param output destination tokenlist * @param loc location for expanded token * @param tok first # token * @param macros all macros * @param expandedmacros set with expanded macros, with this macro * @param parametertokens parameters given when expanding this macro * @return token after B */ const Token *expandHashHash(TokenList *output, const Location &loc, const Token *tok, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { Token *A = output->back(); if (!A) throw invalidHashHash(tok->location, name()); if (!sameline(tok, tok->next) || !sameline(tok, tok->next->next)) throw invalidHashHash(tok->location, name()); bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>"; if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual) throw invalidHashHash(tok->location, name()); Token *B = tok->next->next; if (!B->name && !B->number && B->op && !B->isOneOf("#=")) throw invalidHashHash(tok->location, name()); if ((canBeConcatenatedWithEqual && B->op != '=') || (!canBeConcatenatedWithEqual && B->op == '=')) throw invalidHashHash(tok->location, name()); std::string strAB; const bool varargs = variadic && args.size() >= 1U && B->str() == args[args.size()-1U]; TokenList tokensB(files); if (expandArg(&tokensB, B, parametertokens)) { if (tokensB.empty()) strAB = A->str(); else if (varargs && A->op == ',') { strAB = ","; } else { strAB = A->str() + tokensB.cfront()->str(); tokensB.deleteToken(tokensB.front()); } } else { strAB = A->str() + B->str(); } const Token *nextTok = B->next; if (varargs && tokensB.empty() && tok->previous->str() == ",") output->deleteToken(A); else if (strAB != "," && macros.find(strAB) == macros.end()) { A->setstr(strAB); for (Token *b = tokensB.front(); b; b = b->next) b->location = loc; output->takeTokens(tokensB); } else if (nextTok->op == '#' && nextTok->next->op == '#') { TokenList output2(files); output2.push_back(new Token(strAB, tok->location)); nextTok = expandHashHash(&output2, loc, nextTok, macros, expandedmacros, parametertokens); output->deleteToken(A); output->takeTokens(output2); } else { output->deleteToken(A); TokenList tokens(files); tokens.push_back(new Token(strAB, tok->location)); // for function like macros, push the (...) if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') { const std::map::const_iterator it = macros.find(strAB); if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) { const Token *tok2 = appendTokens(&tokens, loc, B->next, macros, expandedmacros, parametertokens); if (tok2) nextTok = tok2->next; } } expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens); for (Token *b = tokensB.front(); b; b = b->next) b->location = loc; output->takeTokens(tokensB); } return nextTok; } static bool isReplaced(const std::set &expandedmacros) { // return true if size > 1 std::set::const_iterator it = expandedmacros.begin(); if (it == expandedmacros.end()) return false; ++it; return (it != expandedmacros.end()); } /** name token in definition */ const Token *nameTokDef; /** arguments for macro */ std::vector args; /** is macro variadic? */ bool variadic; /** first token in replacement string */ const Token *valueToken; /** token after replacement string */ const Token *endToken; /** files */ std::vector &files; /** this is used for -D where the definition is not seen anywhere in code */ TokenList tokenListDefine; /** usage of this macro */ mutable std::list usageList; /** was the value of this macro actually defined in the code? */ bool valueDefinedInCode_; }; } namespace simplecpp { std::string convertCygwinToWindowsPath(const std::string &cygwinPath) { std::string windowsPath; std::string::size_type pos = 0; if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) { unsigned char driveLetter = cygwinPath[10]; if (std::isalpha(driveLetter)) { if (cygwinPath.size() == 11) { windowsPath = toupper(driveLetter); windowsPath += ":\\"; // volume root directory pos = 11; } else if (cygwinPath[11] == '/') { windowsPath = toupper(driveLetter); windowsPath += ":"; pos = 11; } } } for (; pos < cygwinPath.size(); ++pos) { unsigned char c = cygwinPath[pos]; if (c == '/') c = '\\'; windowsPath += c; } return windowsPath; } } #ifdef SIMPLECPP_WINDOWS class ScopedLock { public: explicit ScopedLock(CRITICAL_SECTION& criticalSection) : m_criticalSection(criticalSection) { EnterCriticalSection(&m_criticalSection); } ~ScopedLock() { LeaveCriticalSection(&m_criticalSection); } private: ScopedLock& operator=(const ScopedLock&); ScopedLock(const ScopedLock&); CRITICAL_SECTION& m_criticalSection; }; class RealFileNameMap { public: RealFileNameMap() { InitializeCriticalSection(&m_criticalSection); } ~RealFileNameMap() { DeleteCriticalSection(&m_criticalSection); } bool getCacheEntry(const std::string& path, std::string* returnPath) { ScopedLock lock(m_criticalSection); std::map::iterator it = m_fileMap.find(path); if (it != m_fileMap.end()) { *returnPath = it->second; return true; } return false; } void addToCache(const std::string& path, const std::string& actualPath) { ScopedLock lock(m_criticalSection); m_fileMap[path] = actualPath; } private: std::map m_fileMap; CRITICAL_SECTION m_criticalSection; }; static RealFileNameMap realFileNameMap; static bool realFileName(const std::string &f, std::string *result) { // are there alpha characters in last subpath? bool alpha = false; for (std::string::size_type pos = 1; pos <= f.size(); ++pos) { unsigned char c = f[f.size() - pos]; if (c == '/' || c == '\\') break; if (std::isalpha(c)) { alpha = true; break; } } // do not convert this path if there are no alpha characters (either pointless or cause wrong results for . and ..) if (!alpha) return false; // Lookup filename or foldername on file system if (!realFileNameMap.getCacheEntry(f, result)) { WIN32_FIND_DATAA FindFileData; #ifdef __CYGWIN__ std::string fConverted = simplecpp::convertCygwinToWindowsPath(f); HANDLE hFind = FindFirstFileExA(fConverted.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); #else HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); #endif if (INVALID_HANDLE_VALUE == hFind) return false; *result = FindFileData.cFileName; realFileNameMap.addToCache(f, *result); FindClose(hFind); } return true; } static RealFileNameMap realFilePathMap; /** Change case in given path to match filesystem */ static std::string realFilename(const std::string &f) { std::string ret; ret.reserve(f.size()); // this will be the final size if (realFilePathMap.getCacheEntry(f, &ret)) return ret; // Current subpath std::string subpath; for (std::string::size_type pos = 0; pos < f.size(); ++pos) { unsigned char c = f[pos]; // Separator.. add subpath and separator if (c == '/' || c == '\\') { // if subpath is empty just add separator if (subpath.empty()) { ret += c; continue; } bool isDriveSpecification = (pos == 2 && subpath.size() == 2 && std::isalpha(subpath[0]) && subpath[1] == ':'); // Append real filename (proper case) std::string f2; if (!isDriveSpecification && realFileName(f.substr(0, pos), &f2)) ret += f2; else ret += subpath; subpath.clear(); // Append separator ret += c; } else { subpath += c; } } if (!subpath.empty()) { std::string f2; if (realFileName(f,&f2)) ret += f2; else ret += subpath; } realFilePathMap.addToCache(f, ret); return ret; } static bool isAbsolutePath(const std::string &path) { if (path.length() >= 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) return true; return path.length() > 1U && (path[0] == '/' || path[0] == '\\'); } #else #define realFilename(f) f static bool isAbsolutePath(const std::string &path) { return path.length() > 1U && path[0] == '/'; } #endif namespace simplecpp { /** * perform path simplifications for . and .. */ std::string simplifyPath(std::string path) { if (path.empty()) return path; std::string::size_type pos; // replace backslash separators std::replace(path.begin(), path.end(), '\\', '/'); const bool unc(path.compare(0,2,"//") == 0); // replace "//" with "/" pos = 0; while ((pos = path.find("//",pos)) != std::string::npos) { path.erase(pos,1); } // remove "./" pos = 0; while ((pos = path.find("./",pos)) != std::string::npos) { if (pos == 0 || path[pos - 1U] == '/') path.erase(pos,2); else pos += 2; } // remove trailing dot if path ends with "/." if (endsWith(path,"/.")) path.erase(path.size()-1); // simplify ".." pos = 1; // don't simplify ".." if path starts with that while ((pos = path.find("/..", pos)) != std::string::npos) { // not end of path, then string must be "/../" if (pos + 3 < path.size() && path[pos + 3] != '/') { ++pos; continue; } // get previous subpath const std::string::size_type pos1 = path.rfind('/', pos - 1U) + 1U; const std::string previousSubPath = path.substr(pos1, pos-pos1); if (previousSubPath == "..") { // don't simplify ++pos; } else { // remove previous subpath and ".." path.erase(pos1,pos-pos1+4); if (path.empty()) path = "."; // update pos pos = (pos1 == 0) ? 1 : (pos1 - 1); } } // Remove trailing '/'? //if (path.size() > 1 && endsWith(path, "/")) // path.erase(path.size()-1); if (unc) path = '/' + path; return path.find_first_of("*?") == std::string::npos ? realFilename(path) : path; } } /** Evaluate sizeof(type) */ static void simplifySizeof(simplecpp::TokenList &expr, const std::map &sizeOfType) { for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->str() != "sizeof") continue; simplecpp::Token *tok1 = tok->next; if (!tok1) { throw std::runtime_error("missing sizeof argument"); } simplecpp::Token *tok2 = tok1->next; if (!tok2) { throw std::runtime_error("missing sizeof argument"); } if (tok1->op == '(') { tok1 = tok1->next; while (tok2->op != ')') { tok2 = tok2->next; if (!tok2) { throw std::runtime_error("invalid sizeof expression"); } } } std::string type; for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) { if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name) continue; if (typeToken->str() == "*" && type.find('*') != std::string::npos) continue; if (!type.empty()) type += ' '; type += typeToken->str(); } const std::map::const_iterator it = sizeOfType.find(type); if (it != sizeOfType.end()) tok->setstr(toString(it->second)); else continue; tok2 = tok2->next; while (tok->next != tok2) expr.deleteToken(tok->next); } } static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"}; static const std::set altop(&altopData[0], &altopData[8]); static void simplifyName(simplecpp::TokenList &expr) { for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->name) { if (altop.find(tok->str()) != altop.end()) { bool alt; if (tok->str() == "not" || tok->str() == "compl") { alt = isAlternativeUnaryOp(tok,tok->str()); } else { alt = isAlternativeBinaryOp(tok,tok->str()); } if (alt) continue; } tok->setstr("0"); } } } /* * Reads at least minlen and at most maxlen digits (inc. prefix) in base base * from s starting at position pos and converts them to a * unsigned long long value, updating pos to point to the first * unused element of s. * Returns ULLONG_MAX if the result is not representable and * throws if the above requirements were not possible to satisfy. */ static unsigned long long stringToULLbounded( const std::string& s, std::size_t& pos, int base = 0, std::ptrdiff_t minlen = 1, std::size_t maxlen = std::string::npos ) { std::string sub = s.substr(pos, maxlen); const char* start = sub.c_str(); char* end; unsigned long long value = std::strtoull(start, &end, base); pos += end - start; if (end - start < minlen) throw std::runtime_error("expected digit"); return value; } /* Converts character literal (including prefix, but not ud-suffix) * to long long value. * * Assumes ASCII-compatible single-byte encoded str for narrow literals * and UTF-8 otherwise. * * For target assumes * - execution character set encoding matching str * - UTF-32 execution wide-character set encoding * - requirements for __STDC_UTF_16__, __STDC_UTF_32__ and __STDC_ISO_10646__ satisfied * - char16_t is 16bit wide * - char32_t is 32bit wide * - wchar_t is 32bit wide and unsigned * - matching char signedness to host * - matching sizeof(int) to host * * For host assumes * - ASCII-compatible execution character set * * For host and target assumes * - CHAR_BIT == 8 * - two's complement * * Implements multi-character narrow literals according to GCC's behavior, * except multi code unit universal character names are not supported. * Multi-character wide literals are not supported. * Limited support of universal character names for non-UTF-8 execution character set encodings. */ long long simplecpp::characterLiteralToLL(const std::string& str) { // default is wide/utf32 bool narrow = false; bool utf8 = false; bool utf16 = false; std::size_t pos; if (str.size() >= 1 && str[0] == '\'') { narrow = true; pos = 1; } else if (str.size() >= 2 && str[0] == 'u' && str[1] == '\'') { utf16 = true; pos = 2; } else if (str.size() >= 3 && str[0] == 'u' && str[1] == '8' && str[2] == '\'') { utf8 = true; pos = 3; } else if (str.size() >= 2 && (str[0] == 'L' || str[0] == 'U') && str[1] == '\'') { pos = 2; } else throw std::runtime_error("expected a character literal"); unsigned long long multivalue = 0; std::size_t nbytes = 0; while (pos + 1 < str.size()) { if (str[pos] == '\'' || str[pos] == '\n') throw std::runtime_error("raw single quotes and newlines not allowed in character literals"); if (nbytes >= 1 && !narrow) throw std::runtime_error("multiple characters only supported in narrow character literals"); unsigned long long value; if (str[pos] == '\\') { pos++; char escape = str[pos++]; if (pos >= str.size()) throw std::runtime_error("unexpected end of character literal"); switch (escape) { // obscure GCC extensions case '%': case '(': case '[': case '{': // standard escape sequences case '\'': case '"': case '?': case '\\': value = static_cast(escape); break; case 'a': value = static_cast('\a'); break; case 'b': value = static_cast('\b'); break; case 'f': value = static_cast('\f'); break; case 'n': value = static_cast('\n'); break; case 'r': value = static_cast('\r'); break; case 't': value = static_cast('\t'); break; case 'v': value = static_cast('\v'); break; // GCC extension for ESC character case 'e': case 'E': value = static_cast('\x1b'); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': // octal escape sequences consist of 1 to 3 digits value = stringToULLbounded(str, --pos, 8, 1, 3); break; case 'x': // hexadecimal escape sequences consist of at least 1 digit value = stringToULLbounded(str, pos, 16); break; case 'u': case 'U': { // universal character names have exactly 4 or 8 digits std::size_t ndigits = (escape == 'u' ? 4 : 8); value = stringToULLbounded(str, pos, 16, ndigits, ndigits); // UTF-8 encodes code points above 0x7f in multiple code units // code points above 0x10ffff are not allowed if (((narrow || utf8) && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) throw std::runtime_error("code point too large"); if (value >= 0xd800 && value <= 0xdfff) throw std::runtime_error("surrogate code points not allowed in universal character names"); break; } default: throw std::runtime_error("invalid escape sequence"); } } else { value = static_cast(str[pos++]); if (!narrow && value >= 0x80) { // Assuming this is a UTF-8 encoded code point. // This decoder may not completely validate the input. // Noncharacters are neither rejected nor replaced. int additional_bytes; if (value >= 0xf5) // higher values would result in code points above 0x10ffff throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); else if (value >= 0xf0) additional_bytes = 3; else if (value >= 0xe0) additional_bytes = 2; else if (value >= 0xc2) // 0xc0 and 0xc1 are always overlong 2-bytes encodings additional_bytes = 1; else throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); value &= (1 << (6 - additional_bytes)) - 1; while (additional_bytes--) { if (pos + 1 >= str.size()) throw std::runtime_error("assumed UTF-8 encoded source, but character literal ends unexpectedly"); unsigned char c = str[pos++]; if (((c >> 6) != 2) // ensure c has form 0xb10xxxxxx || (!value && additional_bytes == 1 && c < 0xa0) // overlong 3-bytes encoding || (!value && additional_bytes == 2 && c < 0x90)) // overlong 4-bytes encoding throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); value = (value << 6) | (c & ((1 << 7) - 1)); } if (value >= 0xd800 && value <= 0xdfff) throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); if ((utf8 && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) throw std::runtime_error("code point too large"); } } if (((narrow || utf8) && value > std::numeric_limits::max()) || (utf16 && value >> 16) || value >> 32) throw std::runtime_error("numeric escape sequence too large"); multivalue <<= CHAR_BIT; multivalue |= value; nbytes++; } if (pos + 1 != str.size() || str[pos] != '\'') throw std::runtime_error("missing closing quote in character literal"); if (!nbytes) throw std::runtime_error("empty character literal"); // ordinary narrow character literal's value is determined by (possibly signed) char if (narrow && nbytes == 1) return static_cast(multivalue); // while multi-character literal's value is determined by (signed) int if (narrow) return static_cast(multivalue); // All other cases are unsigned. Since long long is at least 64bit wide, // while the literals at most 32bit wide, the conversion preserves all values. return multivalue; } static void simplifyNumbers(simplecpp::TokenList &expr) { for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->str().size() == 1U) continue; if (tok->str().compare(0,2,"0x") == 0) tok->setstr(toString(stringToULL(tok->str()))); else if (!tok->number && tok->str().find('\'') != tok->str().npos) tok->setstr(toString(simplecpp::characterLiteralToLL(tok->str()))); } } static long long evaluate(simplecpp::TokenList &expr, const std::map &sizeOfType) { simplifySizeof(expr, sizeOfType); simplifyName(expr); simplifyNumbers(expr); expr.constFold(); // TODO: handle invalid expressions return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL; } static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok) { const unsigned int line = tok->location.line; const unsigned int file = tok->location.fileIndex; while (tok && tok->location.line == line && tok->location.fileIndex == file) tok = tok->next; return tok; } #ifdef SIMPLECPP_WINDOWS class NonExistingFilesCache { public: NonExistingFilesCache() { InitializeCriticalSection(&m_criticalSection); } ~NonExistingFilesCache() { DeleteCriticalSection(&m_criticalSection); } bool contains(const std::string& path) { ScopedLock lock(m_criticalSection); return (m_pathSet.find(path) != m_pathSet.end()); } void add(const std::string& path) { ScopedLock lock(m_criticalSection); m_pathSet.insert(path); } private: std::set m_pathSet; CRITICAL_SECTION m_criticalSection; }; static NonExistingFilesCache nonExistingFilesCache; #endif static std::string _openHeader(std::ifstream &f, const std::string &path) { #ifdef SIMPLECPP_WINDOWS std::string simplePath = simplecpp::simplifyPath(path); if (nonExistingFilesCache.contains(simplePath)) return ""; // file is known not to exist, skip expensive file open call f.open(simplePath.c_str()); if (f.is_open()) return simplePath; else { nonExistingFilesCache.add(simplePath); return ""; } #else f.open(path.c_str()); return f.is_open() ? simplecpp::simplifyPath(path) : ""; #endif } static std::string getRelativeFileName(const std::string &sourcefile, const std::string &header) { if (sourcefile.find_first_of("\\/") != std::string::npos) return simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header); return simplecpp::simplifyPath(header); } static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) { return _openHeader(f, getRelativeFileName(sourcefile, header)); } static std::string getIncludePathFileName(const std::string &includePath, const std::string &header) { std::string path = includePath; if (!path.empty() && path[path.size()-1U]!='/' && path[path.size()-1U]!='\\') path += '/'; return path + header; } static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header) { for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { std::string simplePath = _openHeader(f, getIncludePathFileName(*it, header)); if (!simplePath.empty()) return simplePath; } return ""; } static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader) { if (isAbsolutePath(header)) return _openHeader(f, header); std::string ret; if (systemheader) { ret = openHeaderIncludePath(f, dui, header); return ret.empty() ? openHeaderRelative(f, sourcefile, header) : ret; } ret = openHeaderRelative(f, sourcefile, header); return ret.empty() ? openHeaderIncludePath(f, dui, header) : ret; } static std::string getFileName(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) { if (filedata.empty()) { return ""; } if (isAbsolutePath(header)) { return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : ""; } const std::string relativeFilename = getRelativeFileName(sourcefile, header); if (!systemheader && filedata.find(relativeFilename) != filedata.end()) return relativeFilename; for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { std::string s = simplecpp::simplifyPath(getIncludePathFileName(*it, header)); if (filedata.find(s) != filedata.end()) return s; } if (filedata.find(relativeFilename) != filedata.end()) return relativeFilename; return ""; } static bool hasFile(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) { return !getFileName(filedata, sourcefile, header, dui, systemheader).empty(); } std::map simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector &fileNumbers, const simplecpp::DUI &dui, simplecpp::OutputList *outputList) { std::map ret; std::list filelist; // -include files for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { const std::string &filename = realFilename(*it); if (ret.find(filename) != ret.end()) continue; std::ifstream fin(filename.c_str()); if (!fin.is_open()) { if (outputList) { simplecpp::Output err(fileNumbers); err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; err.location = Location(fileNumbers); err.msg = "Can not open include file '" + filename + "' that is explicitly included."; outputList->push_back(err); } continue; } TokenList *tokenlist = new TokenList(fin, fileNumbers, filename, outputList); if (!tokenlist->front()) { delete tokenlist; continue; } ret[filename] = tokenlist; filelist.push_back(tokenlist->front()); } for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : NULL) { if (rawtok == NULL) { rawtok = filelist.back(); filelist.pop_back(); } if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok)) continue; rawtok = rawtok->nextSkipComments(); if (!rawtok || rawtok->str() != INCLUDE) continue; const std::string &sourcefile = rawtok->location.file(); const Token *htok = rawtok->nextSkipComments(); if (!sameline(rawtok, htok)) continue; bool systemheader = (htok->str()[0] == '<'); const std::string header(realFilename(htok->str().substr(1U, htok->str().size() - 2U))); if (hasFile(ret, sourcefile, header, dui, systemheader)) continue; std::ifstream f; const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); if (!f.is_open()) continue; TokenList *tokens = new TokenList(f, fileNumbers, header2, outputList); ret[header2] = tokens; if (tokens->front()) filelist.push_back(tokens->front()); } return ret; } static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token **tok1, std::map ¯os, std::vector &files, simplecpp::OutputList *outputList) { const simplecpp::Token *tok = *tok1; const std::map::const_iterator it = macros.find(tok->str()); if (it != macros.end()) { simplecpp::TokenList value(files); try { *tok1 = it->second.expand(&value, tok, macros, files); } catch (simplecpp::Macro::Error &err) { if (outputList) { simplecpp::Output out(files); out.type = simplecpp::Output::SYNTAX_ERROR; out.location = err.location; out.msg = "failed to expand \'" + tok->str() + "\', " + err.what; outputList->push_back(out); } return false; } output.takeTokens(value); } else { if (!tok->comment) output.push_back(new simplecpp::Token(*tok)); *tok1 = tok->next; } return true; } void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector &files, std::map &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage, std::list *ifCond) { std::map sizeOfType(rawtokens.sizeOfType); sizeOfType.insert(std::make_pair("char", sizeof(char))); sizeOfType.insert(std::make_pair("short", sizeof(short))); sizeOfType.insert(std::make_pair("short int", sizeOfType["short"])); sizeOfType.insert(std::make_pair("int", sizeof(int))); sizeOfType.insert(std::make_pair("long", sizeof(long))); sizeOfType.insert(std::make_pair("long int", sizeOfType["long"])); sizeOfType.insert(std::make_pair("long long", sizeof(long long))); sizeOfType.insert(std::make_pair("float", sizeof(float))); sizeOfType.insert(std::make_pair("double", sizeof(double))); sizeOfType.insert(std::make_pair("long double", sizeof(long double))); sizeOfType.insert(std::make_pair("char *", sizeof(char *))); sizeOfType.insert(std::make_pair("short *", sizeof(short *))); sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"])); sizeOfType.insert(std::make_pair("int *", sizeof(int *))); sizeOfType.insert(std::make_pair("long *", sizeof(long *))); sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"])); sizeOfType.insert(std::make_pair("long long *", sizeof(long long *))); sizeOfType.insert(std::make_pair("float *", sizeof(float *))); sizeOfType.insert(std::make_pair("double *", sizeof(double *))); sizeOfType.insert(std::make_pair("long double *", sizeof(long double *))); const bool hasInclude = (dui.std.size() == 5 && dui.std.compare(0,3,"c++") == 0 && dui.std >= "c++17"); std::map macros; for (std::list::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { const std::string ¯ostr = *it; const std::string::size_type eq = macrostr.find('='); const std::string::size_type par = macrostr.find('('); const std::string macroname = macrostr.substr(0, std::min(eq,par)); if (dui.undefined.find(macroname) != dui.undefined.end()) continue; const std::string lhs(macrostr.substr(0,eq)); const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1)); const Macro macro(lhs, rhs, files); macros.insert(std::pair(macro.name(), macro)); } macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", files))); macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", files))); macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", files))); if (dui.std == "c++11") macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", "201103L", files))); else if (dui.std == "c++14") macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", "201402L", files))); else if (dui.std == "c++17") macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", "201703L", files))); else if (dui.std == "c++20") macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", "202002L", files))); // TRUE => code in current #if block should be kept // ELSE_IS_TRUE => code in current #if block should be dropped. the code in the #else should be kept. // ALWAYS_FALSE => drop all code in #if and #else enum IfState { TRUE, ELSE_IS_TRUE, ALWAYS_FALSE }; std::stack ifstates; ifstates.push(TRUE); std::stack includetokenstack; std::set pragmaOnce; includetokenstack.push(rawtokens.cfront()); for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { const std::map::const_iterator f = filedata.find(*it); if (f != filedata.end()) includetokenstack.push(f->second->cfront()); } for (const Token *rawtok = NULL; rawtok || !includetokenstack.empty();) { if (rawtok == NULL) { rawtok = includetokenstack.top(); includetokenstack.pop(); continue; } if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) { if (!sameline(rawtok, rawtok->next)) { rawtok = rawtok->next; continue; } rawtok = rawtok->next; if (!rawtok->name) { rawtok = gotoNextLine(rawtok); continue; } if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) { if (outputList) { simplecpp::Output err(files); err.type = Output::SYNTAX_ERROR; err.location = rawtok->location; err.msg = "#" + rawtok->str() + " without #if"; outputList->push_back(err); } output.clear(); return; } if (ifstates.top() == TRUE && (rawtok->str() == ERROR || rawtok->str() == WARNING)) { if (outputList) { simplecpp::Output err(rawtok->location.files); err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING; err.location = rawtok->location; for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) { if (!err.msg.empty() && isNameChar(tok->str()[0])) err.msg += ' '; err.msg += tok->str(); } err.msg = '#' + rawtok->str() + ' ' + err.msg; outputList->push_back(err); } if (rawtok->str() == ERROR) { output.clear(); return; } } if (rawtok->str() == DEFINE) { if (ifstates.top() != TRUE) continue; try { const Macro ¯o = Macro(rawtok->previous, files); if (dui.undefined.find(macro.name()) == dui.undefined.end()) { std::map::iterator it = macros.find(macro.name()); if (it == macros.end()) macros.insert(std::pair(macro.name(), macro)); else it->second = macro; } } catch (const std::runtime_error &) { if (outputList) { simplecpp::Output err(files); err.type = Output::SYNTAX_ERROR; err.location = rawtok->location; err.msg = "Failed to parse #define"; outputList->push_back(err); } output.clear(); return; } } else if (ifstates.top() == TRUE && rawtok->str() == INCLUDE) { TokenList inc1(files); for (const Token *inctok = rawtok->next; sameline(rawtok,inctok); inctok = inctok->next) { if (!inctok->comment) inc1.push_back(new Token(*inctok)); } TokenList inc2(files); if (!inc1.empty() && inc1.cfront()->name) { const Token *inctok = inc1.cfront(); if (!preprocessToken(inc2, &inctok, macros, files, outputList)) { output.clear(); return; } } else { inc2.takeTokens(inc1); } if (!inc2.empty() && inc2.cfront()->op == '<' && inc2.cback()->op == '>') { TokenString hdr; // TODO: Sometimes spaces must be added in the string // Somehow preprocessToken etc must be told that the location should be source location not destination location for (const Token *tok = inc2.cfront(); tok; tok = tok->next) { hdr += tok->str(); } inc2.clear(); inc2.push_back(new Token(hdr, inc1.cfront()->location)); inc2.front()->op = '<'; } if (inc2.empty() || inc2.cfront()->str().size() <= 2U) { if (outputList) { simplecpp::Output err(files); err.type = Output::SYNTAX_ERROR; err.location = rawtok->location; err.msg = "No header in #include"; outputList->push_back(err); } output.clear(); return; } const Token *inctok = inc2.cfront(); const bool systemheader = (inctok->op == '<'); const std::string header(realFilename(inctok->str().substr(1U, inctok->str().size() - 2U))); std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader); if (header2.empty()) { // try to load file.. std::ifstream f; header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader); if (f.is_open()) { TokenList *tokens = new TokenList(f, files, header2, outputList); filedata[header2] = tokens; } } if (header2.empty()) { if (outputList) { simplecpp::Output out(files); out.type = Output::MISSING_HEADER; out.location = rawtok->location; out.msg = "Header not found: " + inctok->str(); outputList->push_back(out); } } else if (includetokenstack.size() >= 400) { if (outputList) { simplecpp::Output out(files); out.type = Output::INCLUDE_NESTED_TOO_DEEPLY; out.location = rawtok->location; out.msg = "#include nested too deeply"; outputList->push_back(out); } } else if (pragmaOnce.find(header2) == pragmaOnce.end()) { includetokenstack.push(gotoNextLine(rawtok)); const TokenList *includetokens = filedata.find(header2)->second; rawtok = includetokens ? includetokens->cfront() : NULL; continue; } } else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) { if (!sameline(rawtok,rawtok->next)) { if (outputList) { simplecpp::Output out(files); out.type = Output::SYNTAX_ERROR; out.location = rawtok->location; out.msg = "Syntax error in #" + rawtok->str(); outputList->push_back(out); } output.clear(); return; } bool conditionIsTrue; if (ifstates.top() == ALWAYS_FALSE || (ifstates.top() == ELSE_IS_TRUE && rawtok->str() != ELIF)) conditionIsTrue = false; else if (rawtok->str() == IFDEF) conditionIsTrue = (macros.find(rawtok->next->str()) != macros.end() || (hasInclude && rawtok->next->str() == HAS_INCLUDE)); else if (rawtok->str() == IFNDEF) conditionIsTrue = (macros.find(rawtok->next->str()) == macros.end() && !(hasInclude && rawtok->next->str() == HAS_INCLUDE)); else { /*if (rawtok->str() == IF || rawtok->str() == ELIF)*/ TokenList expr(files); for (const Token *tok = rawtok->next; tok && tok->location.sameline(rawtok->location); tok = tok->next) { if (!tok->name) { expr.push_back(new Token(*tok)); continue; } if (tok->str() == DEFINED) { tok = tok->next; const bool par = (tok && tok->op == '('); if (par) tok = tok->next; if (tok) { if (macros.find(tok->str()) != macros.end()) expr.push_back(new Token("1", tok->location)); else if (hasInclude && tok->str() == HAS_INCLUDE) expr.push_back(new Token("1", tok->location)); else expr.push_back(new Token("0", tok->location)); } if (par) tok = tok ? tok->next : NULL; if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) { if (outputList) { Output out(rawtok->location.files); out.type = Output::SYNTAX_ERROR; out.location = rawtok->location; out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; outputList->push_back(out); } output.clear(); return; } continue; } if (hasInclude && tok->str() == HAS_INCLUDE) { tok = tok->next; const bool par = (tok && tok->op == '('); if (par) tok = tok->next; if (tok) { const std::string &sourcefile = rawtok->location.file(); const bool systemheader = (tok->str()[0] == '<'); const std::string header(realFilename(tok->str().substr(1U, tok->str().size() - 2U))); std::ifstream f; const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); expr.push_back(new Token(header2.empty() ? "0" : "1", tok->location)); } if (par) tok = tok ? tok->next : NULL; if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) { if (outputList) { Output out(rawtok->location.files); out.type = Output::SYNTAX_ERROR; out.location = rawtok->location; out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; outputList->push_back(out); } output.clear(); return; } continue; } const Token *tmp = tok; if (!preprocessToken(expr, &tmp, macros, files, outputList)) { output.clear(); return; } if (!tmp) break; tok = tmp->previous; } try { if (ifCond) { std::string E; for (const simplecpp::Token *tok = expr.cfront(); tok; tok = tok->next) E += (E.empty() ? "" : " ") + tok->str(); const long long result = evaluate(expr, sizeOfType); conditionIsTrue = (result != 0); ifCond->push_back(IfCond(rawtok->location, E, result)); } else { const long long result = evaluate(expr, sizeOfType); conditionIsTrue = (result != 0); } } catch (const std::exception &e) { if (outputList) { Output out(rawtok->location.files); out.type = Output::SYNTAX_ERROR; out.location = rawtok->location; out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; if (e.what() && *e.what()) out.msg += std::string(", ") + e.what(); outputList->push_back(out); } output.clear(); return; } } if (rawtok->str() != ELIF) { // push a new ifstate.. if (ifstates.top() != TRUE) ifstates.push(ALWAYS_FALSE); else ifstates.push(conditionIsTrue ? TRUE : ELSE_IS_TRUE); } else if (ifstates.top() == TRUE) { ifstates.top() = ALWAYS_FALSE; } else if (ifstates.top() == ELSE_IS_TRUE && conditionIsTrue) { ifstates.top() = TRUE; } } else if (rawtok->str() == ELSE) { ifstates.top() = (ifstates.top() == ELSE_IS_TRUE) ? TRUE : ALWAYS_FALSE; } else if (rawtok->str() == ENDIF) { ifstates.pop(); } else if (rawtok->str() == UNDEF) { if (ifstates.top() == TRUE) { const Token *tok = rawtok->next; while (sameline(rawtok,tok) && tok->comment) tok = tok->next; if (sameline(rawtok, tok)) macros.erase(tok->str()); } } else if (ifstates.top() == TRUE && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) { pragmaOnce.insert(rawtok->location.file()); } rawtok = gotoNextLine(rawtok); continue; } if (ifstates.top() != TRUE) { // drop code rawtok = gotoNextLine(rawtok); continue; } bool hash=false, hashhash=false; if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) { if (rawtok->next->op != '#') { hash = true; rawtok = rawtok->next; // skip '#' } else if (sameline(rawtok,rawtok->next->next)) { hashhash = true; rawtok = rawtok->next->next; // skip '#' '#' } } const Location loc(rawtok->location); TokenList tokens(files); if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) { output.clear(); return; } if (hash || hashhash) { std::string s; for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next) s += hashtok->str(); if (hash) output.push_back(new Token('\"' + s + '\"', loc)); else if (output.back()) output.back()->setstr(output.cback()->str() + s); else output.push_back(new Token(s, loc)); } else { output.takeTokens(tokens); } } if (macroUsage) { for (std::map::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) { const Macro ¯o = macroIt->second; const std::list &usage = macro.usage(); for (std::list::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) { MacroUsage mu(usageIt->files, macro.valueDefinedInCode()); mu.macroName = macro.name(); mu.macroLocation = macro.defineLocation(); mu.useLocation = *usageIt; macroUsage->push_back(mu); } } } } void simplecpp::cleanup(std::map &filedata) { for (std::map::iterator it = filedata.begin(); it != filedata.end(); ++it) delete it->second; filedata.clear(); } cppcheck-2.7/externals/simplecpp/simplecpp.h000066400000000000000000000260361417746362400213350ustar00rootroot00000000000000/* * simplecpp - A simple and high-fidelity C/C++ preprocessor library * Copyright (C) 2016 Daniel Marjamäki. * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef simplecppH #define simplecppH #include #include #include #include #include #include #include #include #ifdef _WIN32 # ifdef SIMPLECPP_EXPORT # define SIMPLECPP_LIB __declspec(dllexport) # elif defined(SIMPLECPP_IMPORT) # define SIMPLECPP_LIB __declspec(dllimport) # else # define SIMPLECPP_LIB # endif #else # define SIMPLECPP_LIB #endif namespace simplecpp { typedef std::string TokenString; /** * Location in source code */ class SIMPLECPP_LIB Location { public: explicit Location(const std::vector &f) : files(f), fileIndex(0), line(1U), col(0U) {} Location(const Location &loc) : files(loc.files), fileIndex(loc.fileIndex), line(loc.line), col(loc.col) {} Location &operator=(const Location &other) { if (this != &other) { fileIndex = other.fileIndex; line = other.line; col = other.col; } return *this; } /** increment this location by string */ void adjust(const std::string &str); bool operator<(const Location &rhs) const { if (fileIndex != rhs.fileIndex) return fileIndex < rhs.fileIndex; if (line != rhs.line) return line < rhs.line; return col < rhs.col; } bool sameline(const Location &other) const { return fileIndex == other.fileIndex && line == other.line; } const std::string& file() const { return fileIndex < files.size() ? files[fileIndex] : emptyFileName; } const std::vector &files; unsigned int fileIndex; unsigned int line; unsigned int col; private: static const std::string emptyFileName; }; /** * token class. * @todo don't use std::string representation - for both memory and performance reasons */ class SIMPLECPP_LIB Token { public: Token(const TokenString &s, const Location &loc) : location(loc), previous(NULL), next(NULL), string(s) { flags(); } Token(const Token &tok) : macro(tok.macro), location(tok.location), previous(NULL), next(NULL), string(tok.string) { flags(); } void flags() { name = (std::isalpha((unsigned char)string[0]) || string[0] == '_' || string[0] == '$') && (string.find('\'') == string.npos); comment = string.size() > 1U && string[0] == '/' && (string[1] == '/' || string[1] == '*'); number = std::isdigit((unsigned char)string[0]) || (string.size() > 1U && string[0] == '-' && std::isdigit((unsigned char)string[1])); op = (string.size() == 1U) ? string[0] : '\0'; } const TokenString& str() const { return string; } void setstr(const std::string &s) { string = s; flags(); } bool isOneOf(const char ops[]) const; bool startsWithOneOf(const char c[]) const; bool endsWithOneOf(const char c[]) const; TokenString macro; char op; bool comment; bool name; bool number; Location location; Token *previous; Token *next; const Token *previousSkipComments() const { const Token *tok = this->previous; while (tok && tok->comment) tok = tok->previous; return tok; } const Token *nextSkipComments() const { const Token *tok = this->next; while (tok && tok->comment) tok = tok->next; return tok; } void setExpandedFrom(const Token *tok, const void* m) { mExpandedFrom = tok->mExpandedFrom; mExpandedFrom.insert(m); } bool isExpandedFrom(const void* m) const { return mExpandedFrom.find(m) != mExpandedFrom.end(); } void printAll() const; void printOut() const; private: TokenString string; std::set mExpandedFrom; // Not implemented - prevent assignment Token &operator=(const Token &tok); }; /** Output from preprocessor */ struct SIMPLECPP_LIB Output { explicit Output(const std::vector &files) : type(ERROR), location(files) {} enum Type { ERROR, /* #error */ WARNING, /* #warning */ MISSING_HEADER, INCLUDE_NESTED_TOO_DEEPLY, SYNTAX_ERROR, PORTABILITY_BACKSLASH, UNHANDLED_CHAR_ERROR, EXPLICIT_INCLUDE_NOT_FOUND } type; Location location; std::string msg; }; typedef std::list OutputList; /** List of tokens. */ class SIMPLECPP_LIB TokenList { public: explicit TokenList(std::vector &filenames); TokenList(std::istream &istr, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = NULL); TokenList(const TokenList &other); #if __cplusplus >= 201103L TokenList(TokenList &&other); #endif ~TokenList(); TokenList &operator=(const TokenList &other); #if __cplusplus >= 201103L TokenList &operator=(TokenList &&other); #endif void clear(); bool empty() const { return !frontToken; } void push_back(Token *tok); void dump() const; std::string stringify() const; void readfile(std::istream &istr, const std::string &filename=std::string(), OutputList *outputList = NULL); void constFold(); void removeComments(); Token *front() { return frontToken; } const Token *cfront() const { return frontToken; } Token *back() { return backToken; } const Token *cback() const { return backToken; } void deleteToken(Token *tok) { if (!tok) return; Token *prev = tok->previous; Token *next = tok->next; if (prev) prev->next = next; if (next) next->previous = prev; if (frontToken == tok) frontToken = next; if (backToken == tok) backToken = prev; delete tok; } void takeTokens(TokenList &other) { if (!other.frontToken) return; if (!frontToken) { frontToken = other.frontToken; } else { backToken->next = other.frontToken; other.frontToken->previous = backToken; } backToken = other.backToken; other.frontToken = other.backToken = NULL; } /** sizeof(T) */ std::map sizeOfType; private: void combineOperators(); void constFoldUnaryNotPosNeg(Token *tok); void constFoldMulDivRem(Token *tok); void constFoldAddSub(Token *tok); void constFoldShift(Token *tok); void constFoldComparison(Token *tok); void constFoldBitwise(Token *tok); void constFoldLogicalOp(Token *tok); void constFoldQuestionOp(Token **tok1); std::string readUntil(std::istream &istr, const Location &location, char start, char end, OutputList *outputList, unsigned int bom); void lineDirective(unsigned int fileIndex, unsigned int line, Location *location); std::string lastLine(int maxsize=100000) const; unsigned int fileIndex(const std::string &filename); Token *frontToken; Token *backToken; std::vector &files; }; /** Tracking how macros are used */ struct SIMPLECPP_LIB MacroUsage { explicit MacroUsage(const std::vector &f, bool macroValueKnown_) : macroLocation(f), useLocation(f), macroValueKnown(macroValueKnown_) {} std::string macroName; Location macroLocation; Location useLocation; bool macroValueKnown; }; /** Tracking #if/#elif expressions */ struct SIMPLECPP_LIB IfCond { explicit IfCond(const Location& location, const std::string &E, long long result) : location(location), E(E), result(result) {} Location location; // location of #if/#elif std::string E; // preprocessed condition long long result; // condition result }; /** * Command line preprocessor settings. * On the command line these are configured by -D, -U, -I, --include, -std */ struct SIMPLECPP_LIB DUI { DUI() {} std::list defines; std::set undefined; std::list includePaths; std::list includes; std::string std; }; SIMPLECPP_LIB long long characterLiteralToLL(const std::string& str); SIMPLECPP_LIB std::map load(const TokenList &rawtokens, std::vector &filenames, const DUI &dui, OutputList *outputList = NULL); /** * Preprocess * @todo simplify interface * @param output TokenList that receives the preprocessing output * @param rawtokens Raw tokenlist for top sourcefile * @param files internal data of simplecpp * @param filedata output from simplecpp::load() * @param dui defines, undefs, and include paths * @param outputList output: list that will receive output messages * @param macroUsage output: macro usage * @param ifCond output: #if/#elif expressions */ SIMPLECPP_LIB void preprocess(TokenList &output, const TokenList &rawtokens, std::vector &files, std::map &filedata, const DUI &dui, OutputList *outputList = NULL, std::list *macroUsage = NULL, std::list *ifCond = NULL); /** * Deallocate data */ SIMPLECPP_LIB void cleanup(std::map &filedata); /** Simplify path */ SIMPLECPP_LIB std::string simplifyPath(std::string path); /** Convert Cygwin path to Windows path */ SIMPLECPP_LIB std::string convertCygwinToWindowsPath(const std::string &cygwinPath); } #endif cppcheck-2.7/externals/tinyxml2/000077500000000000000000000000001417746362400167535ustar00rootroot00000000000000cppcheck-2.7/externals/tinyxml2/CMakeLists.txt000066400000000000000000000007071417746362400215170ustar00rootroot00000000000000file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") add_library(tinyxml2_objs OBJECT ${srcs} ${hdrs}) # TODO: needs to be fixed upstream if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") target_compile_options(tinyxml2_objs PRIVATE -Wno-suggest-attribute=format) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options_safe(tinyxml2_objs -Wno-extra-semi-stmt) target_compile_options_safe(tinyxml2_objs -Wno-implicit-fallthrough) endif() cppcheck-2.7/externals/tinyxml2/LICENSE000066400000000000000000000014501417746362400177600ustar00rootroot00000000000000This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. cppcheck-2.7/externals/tinyxml2/tinyxml2.cpp000066400000000000000000002164231417746362400212550ustar00rootroot00000000000000/* Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "tinyxml2.h" #include // yes, this one new style header, is in the Android SDK. #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) # include # include #else # include # include #endif #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) // Microsoft Visual Studio, version 2005 and higher. Not WinCE. /*int _snprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format [, argument] ... );*/ static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) { va_list va; va_start( va, format ); int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); va_end( va ); return result; } static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) { int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); return result; } #define TIXML_VSCPRINTF _vscprintf #define TIXML_SSCANF sscanf_s #elif defined _MSC_VER // Microsoft Visual Studio 2003 and earlier or WinCE #define TIXML_SNPRINTF _snprintf #define TIXML_VSNPRINTF _vsnprintf #define TIXML_SSCANF sscanf #if (_MSC_VER < 1400 ) && (!defined WINCE) // Microsoft Visual Studio 2003 and not WinCE. #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. #else // Microsoft Visual Studio 2003 and earlier or WinCE. static inline int TIXML_VSCPRINTF( const char* format, va_list va ) { int len = 512; for (;;) { len = len*2; char* str = new char[len](); const int required = _vsnprintf(str, len, format, va); delete[] str; if ( required != -1 ) { TIXMLASSERT( required >= 0 ); len = required; break; } } TIXMLASSERT( len >= 0 ); return len; } #endif #else // GCC version 3 and higher //#warning( "Using sn* functions." ) #define TIXML_SNPRINTF snprintf #define TIXML_VSNPRINTF vsnprintf static inline int TIXML_VSCPRINTF( const char* format, va_list va ) { int len = vsnprintf( 0, 0, format, va ); TIXMLASSERT( len >= 0 ); return len; } #define TIXML_SSCANF sscanf #endif static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF static const char LF = LINE_FEED; static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out static const char CR = CARRIAGE_RETURN; static const char SINGLE_QUOTE = '\''; static const char DOUBLE_QUOTE = '\"'; // Bunch of unicode info at: // http://www.unicode.org/faq/utf_bom.html // ef bb bf (Microsoft "lead bytes") - designates UTF-8 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; namespace tinyxml2 { struct Entity { const char* pattern; int length; char value; }; static const int NUM_ENTITIES = 5; static const Entity entities[NUM_ENTITIES] = { { "quot", 4, DOUBLE_QUOTE }, { "amp", 3, '&' }, { "apos", 4, SINGLE_QUOTE }, { "lt", 2, '<' }, { "gt", 2, '>' } }; StrPair::~StrPair() { Reset(); } void StrPair::TransferTo( StrPair* other ) { if ( this == other ) { return; } // This in effect implements the assignment operator by "moving" // ownership (as in auto_ptr). TIXMLASSERT( other != 0 ); TIXMLASSERT( other->_flags == 0 ); TIXMLASSERT( other->_start == 0 ); TIXMLASSERT( other->_end == 0 ); other->Reset(); other->_flags = _flags; other->_start = _start; other->_end = _end; _flags = 0; _start = 0; _end = 0; } void StrPair::Reset() { if ( _flags & NEEDS_DELETE ) { delete [] _start; } _flags = 0; _start = 0; _end = 0; } void StrPair::SetStr( const char* str, int flags ) { TIXMLASSERT( str ); Reset(); size_t len = strlen( str ); TIXMLASSERT( _start == 0 ); _start = new char[ len+1 ]; memcpy( _start, str, len+1 ); _end = _start + len; _flags = flags | NEEDS_DELETE; } char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) { TIXMLASSERT( p ); TIXMLASSERT( endTag && *endTag ); TIXMLASSERT(curLineNumPtr); char* start = p; char endChar = *endTag; size_t length = strlen( endTag ); // Inner loop of text parsing. while ( *p ) { if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { Set( start, p, strFlags ); return p + length; } else if (*p == '\n') { ++(*curLineNumPtr); } ++p; TIXMLASSERT( p ); } return 0; } char* StrPair::ParseName( char* p ) { if ( !p || !(*p) ) { return 0; } if ( !XMLUtil::IsNameStartChar( *p ) ) { return 0; } char* const start = p; ++p; while ( *p && XMLUtil::IsNameChar( *p ) ) { ++p; } Set( start, p, 0 ); return p; } void StrPair::CollapseWhitespace() { // Adjusting _start would cause undefined behavior on delete[] TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); // Trim leading space. _start = XMLUtil::SkipWhiteSpace( _start, 0 ); if ( *_start ) { const char* p = _start; // the read pointer char* q = _start; // the write pointer while( *p ) { if ( XMLUtil::IsWhiteSpace( *p )) { p = XMLUtil::SkipWhiteSpace( p, 0 ); if ( *p == 0 ) { break; // don't write to q; this trims the trailing space. } *q = ' '; ++q; } *q = *p; ++q; ++p; } *q = 0; } } const char* StrPair::GetStr() { TIXMLASSERT( _start ); TIXMLASSERT( _end ); if ( _flags & NEEDS_FLUSH ) { *_end = 0; _flags ^= NEEDS_FLUSH; if ( _flags ) { const char* p = _start; // the read pointer char* q = _start; // the write pointer while( p < _end ) { if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { // CR-LF pair becomes LF // CR alone becomes LF // LF-CR becomes LF if ( *(p+1) == LF ) { p += 2; } else { ++p; } *q = LF; ++q; } else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { if ( *(p+1) == CR ) { p += 2; } else { ++p; } *q = LF; ++q; } else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { // Entities handled by tinyXML2: // - special entities in the entity table [in/out] // - numeric character reference [in] // 中 or 中 if ( *(p+1) == '#' ) { const int buflen = 10; char buf[buflen] = { 0 }; int len = 0; char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); if ( adjusted == 0 ) { *q = *p; ++p; ++q; } else { TIXMLASSERT( 0 <= len && len <= buflen ); TIXMLASSERT( q + len <= adjusted ); p = adjusted; memcpy( q, buf, len ); q += len; } } else { bool entityFound = false; for( int i = 0; i < NUM_ENTITIES; ++i ) { const Entity& entity = entities[i]; if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 && *( p + entity.length + 1 ) == ';' ) { // Found an entity - convert. *q = entity.value; ++q; p += entity.length + 2; entityFound = true; break; } } if ( !entityFound ) { // fixme: treat as error? ++p; ++q; } } } else { *q = *p; ++p; ++q; } } *q = 0; } // The loop below has plenty going on, and this // is a less useful mode. Break it out. if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { CollapseWhitespace(); } _flags = (_flags & NEEDS_DELETE); } TIXMLASSERT( _start ); return _start; } // --------- XMLUtil ----------- // const char* XMLUtil::writeBoolTrue = "true"; const char* XMLUtil::writeBoolFalse = "false"; void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse) { static const char* defTrue = "true"; static const char* defFalse = "false"; writeBoolTrue = (writeTrue) ? writeTrue : defTrue; writeBoolFalse = (writeFalse) ? writeFalse : defFalse; } const char* XMLUtil::ReadBOM( const char* p, bool* bom ) { TIXMLASSERT( p ); TIXMLASSERT( bom ); *bom = false; const unsigned char* pu = reinterpret_cast(p); // Check for BOM: if ( *(pu+0) == TIXML_UTF_LEAD_0 && *(pu+1) == TIXML_UTF_LEAD_1 && *(pu+2) == TIXML_UTF_LEAD_2 ) { *bom = true; p += 3; } TIXMLASSERT( p ); return p; } void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) { const unsigned long BYTE_MASK = 0xBF; const unsigned long BYTE_MARK = 0x80; const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; if (input < 0x80) { *length = 1; } else if ( input < 0x800 ) { *length = 2; } else if ( input < 0x10000 ) { *length = 3; } else if ( input < 0x200000 ) { *length = 4; } else { *length = 0; // This code won't convert this correctly anyway. return; } output += *length; // Scary scary fall throughs are annotated with carefully designed comments // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc switch (*length) { case 4: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 3: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 2: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); break; default: TIXMLASSERT( false ); } } const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) { // Presume an entity, and pull it out. *length = 0; if ( *(p+1) == '#' && *(p+2) ) { unsigned long ucs = 0; TIXMLASSERT( sizeof( ucs ) >= 4 ); ptrdiff_t delta = 0; unsigned mult = 1; static const char SEMICOLON = ';'; if ( *(p+2) == 'x' ) { // Hexadecimal. const char* q = p+3; if ( !(*q) ) { return 0; } q = strchr( q, SEMICOLON ); if ( !q ) { return 0; } TIXMLASSERT( *q == SEMICOLON ); delta = q-p; --q; while ( *q != 'x' ) { unsigned int digit = 0; if ( *q >= '0' && *q <= '9' ) { digit = *q - '0'; } else if ( *q >= 'a' && *q <= 'f' ) { digit = *q - 'a' + 10; } else if ( *q >= 'A' && *q <= 'F' ) { digit = *q - 'A' + 10; } else { return 0; } TIXMLASSERT( digit < 16 ); TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); const unsigned int digitScaled = mult * digit; TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); ucs += digitScaled; TIXMLASSERT( mult <= UINT_MAX / 16 ); mult *= 16; --q; } } else { // Decimal. const char* q = p+2; if ( !(*q) ) { return 0; } q = strchr( q, SEMICOLON ); if ( !q ) { return 0; } TIXMLASSERT( *q == SEMICOLON ); delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) { const unsigned int digit = *q - '0'; TIXMLASSERT( digit < 10 ); TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); const unsigned int digitScaled = mult * digit; TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); ucs += digitScaled; } else { return 0; } TIXMLASSERT( mult <= UINT_MAX / 10 ); mult *= 10; --q; } } // convert the UCS to UTF-8 ConvertUTF32ToUTF8( ucs, value, length ); return p + delta + 1; } return p+1; } void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); } void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); } void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse); } /* ToStr() of a number is a very tricky topic. https://github.com/leethomason/tinyxml2/issues/106 */ void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); } void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); } void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) { // horrible syntax trick to make the compiler happy about %lld TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); } bool XMLUtil::ToInt( const char* str, int* value ) { if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) { if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToBool( const char* str, bool* value ) { int ival = 0; if ( ToInt( str, &ival )) { *value = (ival==0) ? false : true; return true; } if ( StringEqual( str, "true" ) ) { *value = true; return true; } else if ( StringEqual( str, "false" ) ) { *value = false; return true; } return false; } bool XMLUtil::ToFloat( const char* str, float* value ) { if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToDouble( const char* str, double* value ) { if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToInt64(const char* str, int64_t* value) { long long v = 0; // horrible syntax trick to make the compiler happy about %lld if (TIXML_SSCANF(str, "%lld", &v) == 1) { *value = (int64_t)v; return true; } return false; } char* XMLDocument::Identify( char* p, XMLNode** node ) { TIXMLASSERT( node ); TIXMLASSERT( p ); char* const start = p; int const startLine = _parseCurLineNum; p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); if( !*p ) { *node = 0; TIXMLASSERT( p ); return p; } // These strings define the matching patterns: static const char* xmlHeader = { "( _commentPool ); returnNode->_parseLineNum = _parseCurLineNum; p += xmlHeaderLen; } else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { returnNode = CreateUnlinkedNode( _commentPool ); returnNode->_parseLineNum = _parseCurLineNum; p += commentHeaderLen; } else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { XMLText* text = CreateUnlinkedNode( _textPool ); returnNode = text; returnNode->_parseLineNum = _parseCurLineNum; p += cdataHeaderLen; text->SetCData( true ); } else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { returnNode = CreateUnlinkedNode( _commentPool ); returnNode->_parseLineNum = _parseCurLineNum; p += dtdHeaderLen; } else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { returnNode = CreateUnlinkedNode( _elementPool ); returnNode->_parseLineNum = _parseCurLineNum; p += elementHeaderLen; } else { returnNode = CreateUnlinkedNode( _textPool ); returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character p = start; // Back it up, all the text counts. _parseCurLineNum = startLine; } TIXMLASSERT( returnNode ); TIXMLASSERT( p ); *node = returnNode; return p; } bool XMLDocument::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); if ( visitor->VisitEnter( *this ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { break; } } } return visitor->VisitExit( *this ); } // --------- XMLNode ----------- // XMLNode::XMLNode( XMLDocument* doc ) : _document( doc ), _parent( 0 ), _value(), _parseLineNum( 0 ), _firstChild( 0 ), _lastChild( 0 ), _prev( 0 ), _next( 0 ), _userData( 0 ), _memPool( 0 ) { } XMLNode::~XMLNode() { DeleteChildren(); if ( _parent ) { _parent->Unlink( this ); } } const char* XMLNode::Value() const { // Edge case: XMLDocuments don't have a Value. Return null. if ( this->ToDocument() ) return 0; return _value.GetStr(); } void XMLNode::SetValue( const char* str, bool staticMem ) { if ( staticMem ) { _value.SetInternedStr( str ); } else { _value.SetStr( str ); } } XMLNode* XMLNode::DeepClone(XMLDocument* target) const { XMLNode* clone = this->ShallowClone(target); if (!clone) return 0; for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { XMLNode* childClone = child->DeepClone(target); TIXMLASSERT(childClone); clone->InsertEndChild(childClone); } return clone; } void XMLNode::DeleteChildren() { while( _firstChild ) { TIXMLASSERT( _lastChild ); DeleteChild( _firstChild ); } _firstChild = _lastChild = 0; } void XMLNode::Unlink( XMLNode* child ) { TIXMLASSERT( child ); TIXMLASSERT( child->_document == _document ); TIXMLASSERT( child->_parent == this ); if ( child == _firstChild ) { _firstChild = _firstChild->_next; } if ( child == _lastChild ) { _lastChild = _lastChild->_prev; } if ( child->_prev ) { child->_prev->_next = child->_next; } if ( child->_next ) { child->_next->_prev = child->_prev; } child->_next = 0; child->_prev = 0; child->_parent = 0; } void XMLNode::DeleteChild( XMLNode* node ) { TIXMLASSERT( node ); TIXMLASSERT( node->_document == _document ); TIXMLASSERT( node->_parent == this ); Unlink( node ); TIXMLASSERT(node->_prev == 0); TIXMLASSERT(node->_next == 0); TIXMLASSERT(node->_parent == 0); DeleteNode( node ); } XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) { TIXMLASSERT( addThis ); if ( addThis->_document != _document ) { TIXMLASSERT( false ); return 0; } InsertChildPreamble( addThis ); if ( _lastChild ) { TIXMLASSERT( _firstChild ); TIXMLASSERT( _lastChild->_next == 0 ); _lastChild->_next = addThis; addThis->_prev = _lastChild; _lastChild = addThis; addThis->_next = 0; } else { TIXMLASSERT( _firstChild == 0 ); _firstChild = _lastChild = addThis; addThis->_prev = 0; addThis->_next = 0; } addThis->_parent = this; return addThis; } XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) { TIXMLASSERT( addThis ); if ( addThis->_document != _document ) { TIXMLASSERT( false ); return 0; } InsertChildPreamble( addThis ); if ( _firstChild ) { TIXMLASSERT( _lastChild ); TIXMLASSERT( _firstChild->_prev == 0 ); _firstChild->_prev = addThis; addThis->_next = _firstChild; _firstChild = addThis; addThis->_prev = 0; } else { TIXMLASSERT( _lastChild == 0 ); _firstChild = _lastChild = addThis; addThis->_prev = 0; addThis->_next = 0; } addThis->_parent = this; return addThis; } XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) { TIXMLASSERT( addThis ); if ( addThis->_document != _document ) { TIXMLASSERT( false ); return 0; } TIXMLASSERT( afterThis ); if ( afterThis->_parent != this ) { TIXMLASSERT( false ); return 0; } if ( afterThis == addThis ) { // Current state: BeforeThis -> AddThis -> OneAfterAddThis // Now AddThis must disappear from it's location and then // reappear between BeforeThis and OneAfterAddThis. // So just leave it where it is. return addThis; } if ( afterThis->_next == 0 ) { // The last node or the only node. return InsertEndChild( addThis ); } InsertChildPreamble( addThis ); addThis->_prev = afterThis; addThis->_next = afterThis->_next; afterThis->_next->_prev = addThis; afterThis->_next = addThis; addThis->_parent = this; return addThis; } const XMLElement* XMLNode::FirstChildElement( const char* name ) const { for( const XMLNode* node = _firstChild; node; node = node->_next ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } const XMLElement* XMLNode::LastChildElement( const char* name ) const { for( const XMLNode* node = _lastChild; node; node = node->_prev ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } const XMLElement* XMLNode::NextSiblingElement( const char* name ) const { for( const XMLNode* node = _next; node; node = node->_next ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const { for( const XMLNode* node = _prev; node; node = node->_prev ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) { // This is a recursive method, but thinking about it "at the current level" // it is a pretty simple flat list: // // // // With a special case: // // // // // Where the closing element (/foo) *must* be the next thing after the opening // element, and the names must match. BUT the tricky bit is that the closing // element will be read by the child. // // 'endTag' is the end tag for this node, it is returned by a call to a child. // 'parentEnd' is the end tag for the parent, which is filled in and returned. while( p && *p ) { XMLNode* node = 0; p = _document->Identify( p, &node ); TIXMLASSERT( p ); if ( node == 0 ) { break; } int initialLineNum = node->_parseLineNum; StrPair endTag; p = node->ParseDeep( p, &endTag, curLineNumPtr ); if ( !p ) { DeleteNode( node ); if ( !_document->Error() ) { _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); } break; } XMLDeclaration* decl = node->ToDeclaration(); if ( decl ) { // Declarations are only allowed at document level bool wellLocated = ( ToDocument() != 0 ); if ( wellLocated ) { // Multiple declarations are allowed but all declarations // must occur before anything else for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) { if ( !existingNode->ToDeclaration() ) { wellLocated = false; break; } } } if ( !wellLocated ) { _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value()); DeleteNode( node ); break; } } XMLElement* ele = node->ToElement(); if ( ele ) { // We read the end tag. Return it to the parent. if ( ele->ClosingType() == XMLElement::CLOSING ) { if ( parentEndTag ) { ele->_value.TransferTo( parentEndTag ); } node->_memPool->SetTracked(); // created and then immediately deleted. DeleteNode( node ); return p; } // Handle an end tag returned to this level. // And handle a bunch of annoying errors. bool mismatch = false; if ( endTag.Empty() ) { if ( ele->ClosingType() == XMLElement::OPEN ) { mismatch = true; } } else { if ( ele->ClosingType() != XMLElement::OPEN ) { mismatch = true; } else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { mismatch = true; } } if ( mismatch ) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name()); DeleteNode( node ); break; } } InsertEndChild( node ); } return 0; } /*static*/ void XMLNode::DeleteNode( XMLNode* node ) { if ( node == 0 ) { return; } TIXMLASSERT(node->_document); if (!node->ToDocument()) { node->_document->MarkInUse(node); } MemPool* pool = node->_memPool; node->~XMLNode(); pool->Free( node ); } void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const { TIXMLASSERT( insertThis ); TIXMLASSERT( insertThis->_document == _document ); if (insertThis->_parent) { insertThis->_parent->Unlink( insertThis ); } else { insertThis->_document->MarkInUse(insertThis); insertThis->_memPool->SetTracked(); } } const XMLElement* XMLNode::ToElementWithName( const char* name ) const { const XMLElement* element = this->ToElement(); if ( element == 0 ) { return 0; } if ( name == 0 ) { return element; } if ( XMLUtil::StringEqual( element->Name(), name ) ) { return element; } return 0; } // --------- XMLText ---------- // char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { if ( this->CData() ) { p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 ); } return p; } else { int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; } p = _value.ParseText( p, "<", flags, curLineNumPtr ); if ( p && *p ) { return p-1; } if ( !p ) { _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 ); } } return 0; } XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? text->SetCData( this->CData() ); return text; } bool XMLText::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLText* text = compare->ToText(); return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); } bool XMLText::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLComment ---------- // XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) { } XMLComment::~XMLComment() { } char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { // Comment parses as text. p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 ); } return p; } XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? return comment; } bool XMLComment::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLComment* comment = compare->ToComment(); return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); } bool XMLComment::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLDeclaration ---------- // XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) { } XMLDeclaration::~XMLDeclaration() { //printf( "~XMLDeclaration\n" ); } char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { // Declaration parses as text. p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 ); } return p; } XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? return dec; } bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLDeclaration* declaration = compare->ToDeclaration(); return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); } bool XMLDeclaration::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLUnknown ---------- // XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) { } XMLUnknown::~XMLUnknown() { } char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { // Unknown parses as text. p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 ); } return p; } XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? return text; } bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLUnknown* unknown = compare->ToUnknown(); return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); } bool XMLUnknown::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLAttribute ---------- // const char* XMLAttribute::Name() const { return _name.GetStr(); } const char* XMLAttribute::Value() const { return _value.GetStr(); } char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) { // Parse using the name rules: bug fix, was using ParseText before p = _name.ParseName( p ); if ( !p || !*p ) { return 0; } // Skip white space before = p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); if ( *p != '=' ) { return 0; } ++p; // move up to opening quote p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); if ( *p != '\"' && *p != '\'' ) { return 0; } char endTag[2] = { *p, 0 }; ++p; // move past opening quote p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); return p; } void XMLAttribute::SetName( const char* n ) { _name.SetStr( n ); } XMLError XMLAttribute::QueryIntValue( int* value ) const { if ( XMLUtil::ToInt( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const { if ( XMLUtil::ToUnsigned( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryInt64Value(int64_t* value) const { if (XMLUtil::ToInt64(Value(), value)) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryBoolValue( bool* value ) const { if ( XMLUtil::ToBool( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryFloatValue( float* value ) const { if ( XMLUtil::ToFloat( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryDoubleValue( double* value ) const { if ( XMLUtil::ToDouble( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } void XMLAttribute::SetAttribute( const char* v ) { _value.SetStr( v ); } void XMLAttribute::SetAttribute( int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute(int64_t v) { char buf[BUF_SIZE]; XMLUtil::ToStr(v, buf, BUF_SIZE); _value.SetStr(buf); } void XMLAttribute::SetAttribute( bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( float v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } // --------- XMLElement ---------- // XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), _closingType( OPEN ), _rootAttribute( 0 ) { } XMLElement::~XMLElement() { while( _rootAttribute ) { XMLAttribute* next = _rootAttribute->_next; DeleteAttribute( _rootAttribute ); _rootAttribute = next; } } const XMLAttribute* XMLElement::FindAttribute( const char* name ) const { for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { if ( XMLUtil::StringEqual( a->Name(), name ) ) { return a; } } return 0; } const char* XMLElement::Attribute( const char* name, const char* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return 0; } if ( !value || XMLUtil::StringEqual( a->Value(), value )) { return a->Value(); } return 0; } int XMLElement::IntAttribute(const char* name, int defaultValue) const { int i = defaultValue; QueryIntAttribute(name, &i); return i; } unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const { unsigned i = defaultValue; QueryUnsignedAttribute(name, &i); return i; } int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const { int64_t i = defaultValue; QueryInt64Attribute(name, &i); return i; } bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const { bool b = defaultValue; QueryBoolAttribute(name, &b); return b; } double XMLElement::DoubleAttribute(const char* name, double defaultValue) const { double d = defaultValue; QueryDoubleAttribute(name, &d); return d; } float XMLElement::FloatAttribute(const char* name, float defaultValue) const { float f = defaultValue; QueryFloatAttribute(name, &f); return f; } const char* XMLElement::GetText() const { if ( FirstChild() && FirstChild()->ToText() ) { return FirstChild()->Value(); } return 0; } void XMLElement::SetText( const char* inText ) { if ( FirstChild() && FirstChild()->ToText() ) FirstChild()->SetValue( inText ); else { XMLText* theText = GetDocument()->NewText( inText ); InsertFirstChild( theText ); } } void XMLElement::SetText( int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText( unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText(int64_t v) { char buf[BUF_SIZE]; XMLUtil::ToStr(v, buf, BUF_SIZE); SetText(buf); } void XMLElement::SetText( bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText( float v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText( double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } XMLError XMLElement::QueryIntText( int* ival ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToInt( t, ival ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToUnsigned( t, uval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryInt64Text(int64_t* ival) const { if (FirstChild() && FirstChild()->ToText()) { const char* t = FirstChild()->Value(); if (XMLUtil::ToInt64(t, ival)) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryBoolText( bool* bval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToBool( t, bval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryDoubleText( double* dval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToDouble( t, dval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryFloatText( float* fval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToFloat( t, fval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } int XMLElement::IntText(int defaultValue) const { int i = defaultValue; QueryIntText(&i); return i; } unsigned XMLElement::UnsignedText(unsigned defaultValue) const { unsigned i = defaultValue; QueryUnsignedText(&i); return i; } int64_t XMLElement::Int64Text(int64_t defaultValue) const { int64_t i = defaultValue; QueryInt64Text(&i); return i; } bool XMLElement::BoolText(bool defaultValue) const { bool b = defaultValue; QueryBoolText(&b); return b; } double XMLElement::DoubleText(double defaultValue) const { double d = defaultValue; QueryDoubleText(&d); return d; } float XMLElement::FloatText(float defaultValue) const { float f = defaultValue; QueryFloatText(&f); return f; } XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) { XMLAttribute* last = 0; XMLAttribute* attrib = 0; for( attrib = _rootAttribute; attrib; last = attrib, attrib = attrib->_next ) { if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { break; } } if ( !attrib ) { attrib = CreateAttribute(); TIXMLASSERT( attrib ); if ( last ) { TIXMLASSERT( last->_next == 0 ); last->_next = attrib; } else { TIXMLASSERT( _rootAttribute == 0 ); _rootAttribute = attrib; } attrib->SetName( name ); } return attrib; } void XMLElement::DeleteAttribute( const char* name ) { XMLAttribute* prev = 0; for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { if ( XMLUtil::StringEqual( name, a->Name() ) ) { if ( prev ) { prev->_next = a->_next; } else { _rootAttribute = a->_next; } DeleteAttribute( a ); break; } prev = a; } } char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) { XMLAttribute* prevAttribute = 0; // Read the attributes. while( p ) { p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); if ( !(*p) ) { _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() ); return 0; } // attribute. if (XMLUtil::IsNameStartChar( *p ) ) { XMLAttribute* attrib = CreateAttribute(); TIXMLASSERT( attrib ); attrib->_parseLineNum = _document->_parseCurLineNum; int attrLineNum = attrib->_parseLineNum; p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); if ( !p || Attribute( attrib->Name() ) ) { DeleteAttribute( attrib ); _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() ); return 0; } // There is a minor bug here: if the attribute in the source xml // document is duplicated, it will not be detected and the // attribute will be doubly added. However, tracking the 'prevAttribute' // avoids re-scanning the attribute list. Preferring performance for // now, may reconsider in the future. if ( prevAttribute ) { TIXMLASSERT( prevAttribute->_next == 0 ); prevAttribute->_next = attrib; } else { TIXMLASSERT( _rootAttribute == 0 ); _rootAttribute = attrib; } prevAttribute = attrib; } // end of the tag else if ( *p == '>' ) { ++p; break; } // end of the tag else if ( *p == '/' && *(p+1) == '>' ) { _closingType = CLOSED; return p+2; // done; sealed element. } else { _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 ); return 0; } } return p; } void XMLElement::DeleteAttribute( XMLAttribute* attribute ) { if ( attribute == 0 ) { return; } MemPool* pool = attribute->_memPool; attribute->~XMLAttribute(); pool->Free( attribute ); } XMLAttribute* XMLElement::CreateAttribute() { TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); TIXMLASSERT( attrib ); attrib->_memPool = &_document->_attributePool; attrib->_memPool->SetTracked(); return attrib; } // // // foobar // char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) { // Read the element name. p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); // The closing element is the form. It is // parsed just like a regular element then deleted from // the DOM. if ( *p == '/' ) { _closingType = CLOSING; ++p; } p = _value.ParseName( p ); if ( _value.Empty() ) { return 0; } p = ParseAttributes( p, curLineNumPtr ); if ( !p || !*p || _closingType != OPEN ) { return p; } p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); return p; } XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? } return element; } bool XMLElement::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLElement* other = compare->ToElement(); if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { const XMLAttribute* a=FirstAttribute(); const XMLAttribute* b=other->FirstAttribute(); while ( a && b ) { if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { return false; } a = a->Next(); b = b->Next(); } if ( a || b ) { // different count return false; } return true; } return false; } bool XMLElement::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); if ( visitor->VisitEnter( *this, _rootAttribute ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { break; } } } return visitor->VisitExit( *this ); } // --------- XMLDocument ----------- // // Warning: List must match 'enum XMLError' const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { "XML_SUCCESS", "XML_NO_ATTRIBUTE", "XML_WRONG_ATTRIBUTE_TYPE", "XML_ERROR_FILE_NOT_FOUND", "XML_ERROR_FILE_COULD_NOT_BE_OPENED", "XML_ERROR_FILE_READ_ERROR", "UNUSED_XML_ERROR_ELEMENT_MISMATCH", "XML_ERROR_PARSING_ELEMENT", "XML_ERROR_PARSING_ATTRIBUTE", "UNUSED_XML_ERROR_IDENTIFYING_TAG", "XML_ERROR_PARSING_TEXT", "XML_ERROR_PARSING_CDATA", "XML_ERROR_PARSING_COMMENT", "XML_ERROR_PARSING_DECLARATION", "XML_ERROR_PARSING_UNKNOWN", "XML_ERROR_EMPTY_DOCUMENT", "XML_ERROR_MISMATCHED_ELEMENT", "XML_ERROR_PARSING", "XML_CAN_NOT_CONVERT_TEXT", "XML_NO_TEXT_NODE" }; XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : XMLNode( 0 ), _writeBOM( false ), _processEntities( processEntities ), _errorID(XML_SUCCESS), _whitespaceMode( whitespaceMode ), _errorStr(), _errorLineNum( 0 ), _charBuffer( 0 ), _parseCurLineNum( 0 ), _unlinked(), _elementPool(), _attributePool(), _textPool(), _commentPool() { // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) _document = this; } XMLDocument::~XMLDocument() { Clear(); } void XMLDocument::MarkInUse(XMLNode* node) { TIXMLASSERT(node); TIXMLASSERT(node->_parent == 0); for (int i = 0; i < _unlinked.Size(); ++i) { if (node == _unlinked[i]) { _unlinked.SwapRemove(i); break; } } } void XMLDocument::Clear() { DeleteChildren(); while( _unlinked.Size()) { DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. } #ifdef TINYXML2_DEBUG const bool hadError = Error(); #endif ClearError(); delete [] _charBuffer; _charBuffer = 0; #if 0 _textPool.Trace( "text" ); _elementPool.Trace( "element" ); _commentPool.Trace( "comment" ); _attributePool.Trace( "attribute" ); #endif #ifdef TINYXML2_DEBUG if ( !hadError ) { TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); } #endif } void XMLDocument::DeepCopy(XMLDocument* target) const { TIXMLASSERT(target); if (target == this) { return; // technically success - a no-op. } target->Clear(); for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { target->InsertEndChild(node->DeepClone(target)); } } XMLElement* XMLDocument::NewElement( const char* name ) { XMLElement* ele = CreateUnlinkedNode( _elementPool ); ele->SetName( name ); return ele; } XMLComment* XMLDocument::NewComment( const char* str ) { XMLComment* comment = CreateUnlinkedNode( _commentPool ); comment->SetValue( str ); return comment; } XMLText* XMLDocument::NewText( const char* str ) { XMLText* text = CreateUnlinkedNode( _textPool ); text->SetValue( str ); return text; } XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) { XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); return dec; } XMLUnknown* XMLDocument::NewUnknown( const char* str ) { XMLUnknown* unk = CreateUnlinkedNode( _commentPool ); unk->SetValue( str ); return unk; } static FILE* callfopen( const char* filepath, const char* mode ) { TIXMLASSERT( filepath ); TIXMLASSERT( mode ); #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) FILE* fp = 0; errno_t err = fopen_s( &fp, filepath, mode ); if ( err ) { return 0; } #else FILE* fp = fopen( filepath, mode ); #endif return fp; } void XMLDocument::DeleteNode( XMLNode* node ) { TIXMLASSERT( node ); TIXMLASSERT(node->_document == this ); if (node->_parent) { node->_parent->DeleteChild( node ); } else { // Isn't in the tree. // Use the parent delete. // Also, we need to mark it tracked: we 'know' // it was never used. node->_memPool->SetTracked(); // Call the static XMLNode version: XMLNode::DeleteNode(node); } } XMLError XMLDocument::LoadFile( const char* filename ) { if ( !filename ) { TIXMLASSERT( false ); SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); return _errorID; } Clear(); FILE* fp = callfopen( filename, "rb" ); if ( !fp ) { SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ); return _errorID; } LoadFile( fp ); fclose( fp ); return _errorID; } // This is likely overengineered template art to have a check that unsigned long value incremented // by one still fits into size_t. If size_t type is larger than unsigned long type // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit // -Wtype-limits warning. This piece makes the compiler select code with a check when a check // is useful and code with no check when a check is redundant depending on how size_t and unsigned long // types sizes relate to each other. template = sizeof(size_t))> struct LongFitsIntoSizeTMinusOne { static bool Fits( unsigned long value ) { return value < (size_t)-1; } }; template <> struct LongFitsIntoSizeTMinusOne { static bool Fits( unsigned long ) { return true; } }; XMLError XMLDocument::LoadFile( FILE* fp ) { Clear(); fseek( fp, 0, SEEK_SET ); if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } fseek( fp, 0, SEEK_END ); const long filelength = ftell( fp ); fseek( fp, 0, SEEK_SET ); if ( filelength == -1L ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } TIXMLASSERT( filelength >= 0 ); if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { // Cannot handle files which won't fit in buffer together with null terminator SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } if ( filelength == 0 ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } const size_t size = filelength; TIXMLASSERT( _charBuffer == 0 ); _charBuffer = new char[size+1]; size_t read = fread( _charBuffer, 1, size, fp ); if ( read != size ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } _charBuffer[size] = 0; Parse(); return _errorID; } XMLError XMLDocument::SaveFile( const char* filename, bool compact ) { if ( !filename ) { TIXMLASSERT( false ); SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); return _errorID; } FILE* fp = callfopen( filename, "w" ); if ( !fp ) { SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ); return _errorID; } SaveFile(fp, compact); fclose( fp ); return _errorID; } XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) { // Clear any error from the last save, otherwise it will get reported // for *this* call. ClearError(); XMLPrinter stream( fp, compact ); Print( &stream ); return _errorID; } XMLError XMLDocument::Parse( const char* p, size_t len ) { Clear(); if ( len == 0 || !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } if ( len == (size_t)(-1) ) { len = strlen( p ); } TIXMLASSERT( _charBuffer == 0 ); _charBuffer = new char[ len+1 ]; memcpy( _charBuffer, p, len ); _charBuffer[len] = 0; Parse(); if ( Error() ) { // clean up now essentially dangling memory. // and the parse fail can put objects in the // pools that are dead and inaccessible. DeleteChildren(); _elementPool.Clear(); _attributePool.Clear(); _textPool.Clear(); _commentPool.Clear(); } return _errorID; } void XMLDocument::Print( XMLPrinter* streamer ) const { if ( streamer ) { Accept( streamer ); } else { XMLPrinter stdoutStreamer( stdout ); Accept( &stdoutStreamer ); } } void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... ) { TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); _errorID = error; _errorLineNum = lineNum; _errorStr.Reset(); size_t BUFFER_SIZE = 1000; char* buffer = new char[BUFFER_SIZE]; TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum); if (format) { size_t len = strlen(buffer); TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": "); len = strlen(buffer); va_list va; va_start(va, format); TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va); va_end(va); } _errorStr.SetStr(buffer); delete[] buffer; } /*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) { TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); const char* errorName = _errorNames[errorID]; TIXMLASSERT( errorName && errorName[0] ); return errorName; } const char* XMLDocument::ErrorStr() const { return _errorStr.Empty() ? "" : _errorStr.GetStr(); } void XMLDocument::PrintError() const { printf("%s\n", ErrorStr()); } const char* XMLDocument::ErrorName() const { return ErrorIDToName(_errorID); } void XMLDocument::Parse() { TIXMLASSERT( NoChildren() ); // Clear() must have been called previously TIXMLASSERT( _charBuffer ); _parseCurLineNum = 1; _parseLineNum = 1; char* p = _charBuffer; p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); if ( !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return; } ParseDeep(p, 0, &_parseCurLineNum ); } XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : _elementJustOpened( false ), _stack(), _firstElement( true ), _fp( file ), _depth( depth ), _textDepth( -1 ), _processEntities( true ), _compactMode( compact ), _buffer() { for( int i=0; i'] = true; // not required, but consistency is nice _buffer.Push( 0 ); } void XMLPrinter::Print( const char* format, ... ) { va_list va; va_start( va, format ); if ( _fp ) { vfprintf( _fp, format, va ); } else { const int len = TIXML_VSCPRINTF( format, va ); // Close out and re-start the va-args va_end( va ); TIXMLASSERT( len >= 0 ); va_start( va, format ); TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. TIXML_VSNPRINTF( p, len+1, format, va ); } va_end( va ); } void XMLPrinter::Write( const char* data, size_t size ) { if ( _fp ) { fwrite ( data , sizeof(char), size, _fp); } else { char* p = _buffer.PushArr( static_cast(size) ) - 1; // back up over the null terminator. memcpy( p, data, size ); p[size] = 0; } } void XMLPrinter::Putc( char ch ) { if ( _fp ) { fputc ( ch, _fp); } else { char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator. p[0] = ch; p[1] = 0; } } void XMLPrinter::PrintSpace( int depth ) { for( int i=0; i 0 && *q < ENTITY_RANGE ) { // Check for entities. If one is found, flush // the stream up until the entity, write the // entity, and keep looking. if ( flag[(unsigned char)(*q)] ) { while ( p < q ) { const size_t delta = q - p; const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; Write( p, toPrint ); p += toPrint; } bool entityPatternPrinted = false; for( int i=0; i( bom ) ); } if ( writeDec ) { PushDeclaration( "xml version=\"1.0\"" ); } } void XMLPrinter::OpenElement( const char* name, bool compactMode ) { SealElementIfJustOpened(); _stack.Push( name ); if ( _textDepth < 0 && !_firstElement && !compactMode ) { Putc( '\n' ); } if ( !compactMode ) { PrintSpace( _depth ); } Write ( "<" ); Write ( name ); _elementJustOpened = true; _firstElement = false; ++_depth; } void XMLPrinter::PushAttribute( const char* name, const char* value ) { TIXMLASSERT( _elementJustOpened ); Putc ( ' ' ); Write( name ); Write( "=\"" ); PrintString( value, false ); Putc ( '\"' ); } void XMLPrinter::PushAttribute( const char* name, int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute( const char* name, unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute(const char* name, int64_t v) { char buf[BUF_SIZE]; XMLUtil::ToStr(v, buf, BUF_SIZE); PushAttribute(name, buf); } void XMLPrinter::PushAttribute( const char* name, bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute( const char* name, double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::CloseElement( bool compactMode ) { --_depth; const char* name = _stack.Pop(); if ( _elementJustOpened ) { Write( "/>" ); } else { if ( _textDepth < 0 && !compactMode) { Putc( '\n' ); PrintSpace( _depth ); } Write ( "" ); } if ( _textDepth == _depth ) { _textDepth = -1; } if ( _depth == 0 && !compactMode) { Putc( '\n' ); } _elementJustOpened = false; } void XMLPrinter::SealElementIfJustOpened() { if ( !_elementJustOpened ) { return; } _elementJustOpened = false; Putc( '>' ); } void XMLPrinter::PushText( const char* text, bool cdata ) { _textDepth = _depth-1; SealElementIfJustOpened(); if ( cdata ) { Write( "" ); } else { PrintString( text, true ); } } void XMLPrinter::PushText( int64_t value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( int value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( unsigned value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( bool value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( float value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( double value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushComment( const char* comment ) { SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Putc( '\n' ); PrintSpace( _depth ); } _firstElement = false; Write( "" ); } void XMLPrinter::PushDeclaration( const char* value ) { SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Putc( '\n' ); PrintSpace( _depth ); } _firstElement = false; Write( "" ); } void XMLPrinter::PushUnknown( const char* value ) { SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Putc( '\n' ); PrintSpace( _depth ); } _firstElement = false; Write( "' ); } bool XMLPrinter::VisitEnter( const XMLDocument& doc ) { _processEntities = doc.ProcessEntities(); if ( doc.HasBOM() ) { PushHeader( true, false ); } return true; } bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) { const XMLElement* parentElem = 0; if ( element.Parent() ) { parentElem = element.Parent()->ToElement(); } const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; OpenElement( element.Name(), compactMode ); while ( attribute ) { PushAttribute( attribute->Name(), attribute->Value() ); attribute = attribute->Next(); } return true; } bool XMLPrinter::VisitExit( const XMLElement& element ) { CloseElement( CompactMode(element) ); return true; } bool XMLPrinter::Visit( const XMLText& text ) { PushText( text.Value(), text.CData() ); return true; } bool XMLPrinter::Visit( const XMLComment& comment ) { PushComment( comment.Value() ); return true; } bool XMLPrinter::Visit( const XMLDeclaration& declaration ) { PushDeclaration( declaration.Value() ); return true; } bool XMLPrinter::Visit( const XMLUnknown& unknown ) { PushUnknown( unknown.Value() ); return true; } } // namespace tinyxml2 cppcheck-2.7/externals/tinyxml2/tinyxml2.h000066400000000000000000002107541417746362400207230ustar00rootroot00000000000000/* Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYXML2_INCLUDED #define TINYXML2_INCLUDED #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) # include # include # include # include # include # if defined(__PS3__) # include # endif #else # include # include # include # include # include #endif #include /* TODO: intern strings instead of allocation. */ /* gcc: g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe Formatting, Artistic Style: AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h */ #if defined( _DEBUG ) || defined (__DEBUG__) # ifndef TINYXML2_DEBUG # define TINYXML2_DEBUG # endif #endif #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4251) #endif #ifdef _WIN32 # ifdef TINYXML2_EXPORT # define TINYXML2_LIB __declspec(dllexport) # elif defined(TINYXML2_IMPORT) # define TINYXML2_LIB __declspec(dllimport) # else # define TINYXML2_LIB # endif #elif __GNUC__ >= 4 # define TINYXML2_LIB __attribute__((visibility("default"))) #else # define TINYXML2_LIB #endif #if defined(TINYXML2_DEBUG) # if defined(_MSC_VER) # // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like # define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } # elif defined (ANDROID_NDK) # include # define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } # else # include # define TIXMLASSERT assert # endif #else # define TIXMLASSERT( x ) {} #endif /* Versioning, past 1.0.14: http://semver.org/ */ static const int TIXML2_MAJOR_VERSION = 6; static const int TIXML2_MINOR_VERSION = 1; static const int TIXML2_PATCH_VERSION = 0; #define TINYXML2_MAJOR_VERSION 6 #define TINYXML2_MINOR_VERSION 1 #define TINYXML2_PATCH_VERSION 0 namespace tinyxml2 { class XMLDocument; class XMLElement; class XMLAttribute; class XMLComment; class XMLText; class XMLDeclaration; class XMLUnknown; class XMLPrinter; /* A class that wraps strings. Normally stores the start and end pointers into the XML file itself, and will apply normalization and entity translation if actually read. Can also store (and memory manage) a traditional char[] */ class StrPair { public: enum { NEEDS_ENTITY_PROCESSING = 0x01, NEEDS_NEWLINE_NORMALIZATION = 0x02, NEEDS_WHITESPACE_COLLAPSING = 0x04, TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, ATTRIBUTE_NAME = 0, ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, COMMENT = NEEDS_NEWLINE_NORMALIZATION }; StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} ~StrPair(); void Set( char* start, char* end, int flags ) { TIXMLASSERT( start ); TIXMLASSERT( end ); Reset(); _start = start; _end = end; _flags = flags | NEEDS_FLUSH; } const char* GetStr(); bool Empty() const { return _start == _end; } void SetInternedStr( const char* str ) { Reset(); _start = const_cast(str); } void SetStr( const char* str, int flags=0 ); char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); char* ParseName( char* in ); void TransferTo( StrPair* other ); void Reset(); private: void CollapseWhitespace(); enum { NEEDS_FLUSH = 0x100, NEEDS_DELETE = 0x200 }; int _flags; char* _start; char* _end; StrPair( const StrPair& other ); // not supported void operator=( StrPair& other ); // not supported, use TransferTo() }; /* A dynamic array of Plain Old Data. Doesn't support constructors, etc. Has a small initial memory pool, so that low or no usage will not cause a call to new/delete */ template class DynArray { public: DynArray() : _mem( _pool ), _allocated( INITIAL_SIZE ), _size( 0 ) { } ~DynArray() { if ( _mem != _pool ) { delete [] _mem; } } void Clear() { _size = 0; } void Push( T t ) { TIXMLASSERT( _size < INT_MAX ); EnsureCapacity( _size+1 ); _mem[_size] = t; ++_size; } T* PushArr( int count ) { TIXMLASSERT( count >= 0 ); TIXMLASSERT( _size <= INT_MAX - count ); EnsureCapacity( _size+count ); T* ret = &_mem[_size]; _size += count; return ret; } T Pop() { TIXMLASSERT( _size > 0 ); --_size; return _mem[_size]; } void PopArr( int count ) { TIXMLASSERT( _size >= count ); _size -= count; } bool Empty() const { return _size == 0; } T& operator[](int i) { TIXMLASSERT( i>= 0 && i < _size ); return _mem[i]; } const T& operator[](int i) const { TIXMLASSERT( i>= 0 && i < _size ); return _mem[i]; } const T& PeekTop() const { TIXMLASSERT( _size > 0 ); return _mem[ _size - 1]; } int Size() const { TIXMLASSERT( _size >= 0 ); return _size; } int Capacity() const { TIXMLASSERT( _allocated >= INITIAL_SIZE ); return _allocated; } void SwapRemove(int i) { TIXMLASSERT(i >= 0 && i < _size); TIXMLASSERT(_size > 0); _mem[i] = _mem[_size - 1]; --_size; } const T* Mem() const { TIXMLASSERT( _mem ); return _mem; } T* Mem() { TIXMLASSERT( _mem ); return _mem; } private: DynArray( const DynArray& ); // not supported void operator=( const DynArray& ); // not supported void EnsureCapacity( int cap ) { TIXMLASSERT( cap > 0 ); if ( cap > _allocated ) { TIXMLASSERT( cap <= INT_MAX / 2 ); int newAllocated = cap * 2; T* newMem = new T[newAllocated]; TIXMLASSERT( newAllocated >= _size ); memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs if ( _mem != _pool ) { delete [] _mem; } _mem = newMem; _allocated = newAllocated; } } T* _mem; T _pool[INITIAL_SIZE]; int _allocated; // objects allocated int _size; // number objects in use }; /* Parent virtual class of a pool for fast allocation and deallocation of objects. */ class MemPool { public: MemPool() {} virtual ~MemPool() {} virtual int ItemSize() const = 0; virtual void* Alloc() = 0; virtual void Free( void* ) = 0; virtual void SetTracked() = 0; virtual void Clear() = 0; }; /* Template child class to create pools of the correct type. */ template< int ITEM_SIZE > class MemPoolT : public MemPool { public: MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} ~MemPoolT() { Clear(); } void Clear() { // Delete the blocks. while( !_blockPtrs.Empty()) { Block* lastBlock = _blockPtrs.Pop(); delete lastBlock; } _root = 0; _currentAllocs = 0; _nAllocs = 0; _maxAllocs = 0; _nUntracked = 0; } virtual int ItemSize() const { return ITEM_SIZE; } int CurrentAllocs() const { return _currentAllocs; } virtual void* Alloc() { if ( !_root ) { // Need a new block. Block* block = new Block(); _blockPtrs.Push( block ); Item* blockItems = block->items; for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { blockItems[i].next = &(blockItems[i + 1]); } blockItems[ITEMS_PER_BLOCK - 1].next = 0; _root = blockItems; } Item* const result = _root; TIXMLASSERT( result != 0 ); _root = _root->next; ++_currentAllocs; if ( _currentAllocs > _maxAllocs ) { _maxAllocs = _currentAllocs; } ++_nAllocs; ++_nUntracked; return result; } virtual void Free( void* mem ) { if ( !mem ) { return; } --_currentAllocs; Item* item = static_cast( mem ); #ifdef TINYXML2_DEBUG memset( item, 0xfe, sizeof( *item ) ); #endif item->next = _root; _root = item; } void Trace( const char* name ) { printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); } void SetTracked() { --_nUntracked; } int Untracked() const { return _nUntracked; } // This number is perf sensitive. 4k seems like a good tradeoff on my machine. // The test file is large, 170k. // Release: VS2010 gcc(no opt) // 1k: 4000 // 2k: 4000 // 4k: 3900 21000 // 16k: 5200 // 32k: 4300 // 64k: 4000 21000 // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK // in private part if ITEMS_PER_BLOCK is private enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; private: MemPoolT( const MemPoolT& ); // not supported void operator=( const MemPoolT& ); // not supported union Item { Item* next; char itemData[ITEM_SIZE]; }; struct Block { Item items[ITEMS_PER_BLOCK]; }; DynArray< Block*, 10 > _blockPtrs; Item* _root; int _currentAllocs; int _nAllocs; int _maxAllocs; int _nUntracked; }; /** Implements the interface to the "Visitor pattern" (see the Accept() method.) If you call the Accept() method, it requires being passed a XMLVisitor class to handle callbacks. For nodes that contain other nodes (Document, Element) you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs are simply called with Visit(). If you return 'true' from a Visit method, recursive parsing will continue. If you return false, no children of this node or its siblings will be visited. All flavors of Visit methods have a default implementation that returns 'true' (continue visiting). You need to only override methods that are interesting to you. Generally Accept() is called on the XMLDocument, although all nodes support visiting. You should never change the document from a callback. @sa XMLNode::Accept() */ class TINYXML2_LIB XMLVisitor { public: virtual ~XMLVisitor() {} /// Visit a document. virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { return true; } /// Visit a document. virtual bool VisitExit( const XMLDocument& /*doc*/ ) { return true; } /// Visit an element. virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { return true; } /// Visit an element. virtual bool VisitExit( const XMLElement& /*element*/ ) { return true; } /// Visit a declaration. virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { return true; } /// Visit a text node. virtual bool Visit( const XMLText& /*text*/ ) { return true; } /// Visit a comment node. virtual bool Visit( const XMLComment& /*comment*/ ) { return true; } /// Visit an unknown node. virtual bool Visit( const XMLUnknown& /*unknown*/ ) { return true; } }; // WARNING: must match XMLDocument::_errorNames[] enum XMLError { XML_SUCCESS = 0, XML_NO_ATTRIBUTE, XML_WRONG_ATTRIBUTE_TYPE, XML_ERROR_FILE_NOT_FOUND, XML_ERROR_FILE_COULD_NOT_BE_OPENED, XML_ERROR_FILE_READ_ERROR, UNUSED_XML_ERROR_ELEMENT_MISMATCH, // remove at next major version XML_ERROR_PARSING_ELEMENT, XML_ERROR_PARSING_ATTRIBUTE, UNUSED_XML_ERROR_IDENTIFYING_TAG, // remove at next major version XML_ERROR_PARSING_TEXT, XML_ERROR_PARSING_CDATA, XML_ERROR_PARSING_COMMENT, XML_ERROR_PARSING_DECLARATION, XML_ERROR_PARSING_UNKNOWN, XML_ERROR_EMPTY_DOCUMENT, XML_ERROR_MISMATCHED_ELEMENT, XML_ERROR_PARSING, XML_CAN_NOT_CONVERT_TEXT, XML_NO_TEXT_NODE, XML_ERROR_COUNT }; /* Utility functionality. */ class TINYXML2_LIB XMLUtil { public: static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { TIXMLASSERT( p ); while( IsWhiteSpace(*p) ) { if (curLineNumPtr && *p == '\n') { ++(*curLineNumPtr); } ++p; } TIXMLASSERT( p ); return p; } static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) { return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); } // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't // correct, but simple, and usually works. static bool IsWhiteSpace( char p ) { return !IsUTF8Continuation(p) && isspace( static_cast(p) ); } inline static bool IsNameStartChar( unsigned char ch ) { if ( ch >= 128 ) { // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() return true; } if ( isalpha( ch ) ) { return true; } return ch == ':' || ch == '_'; } inline static bool IsNameChar( unsigned char ch ) { return IsNameStartChar( ch ) || isdigit( ch ) || ch == '.' || ch == '-'; } inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { if ( p == q ) { return true; } TIXMLASSERT( p ); TIXMLASSERT( q ); TIXMLASSERT( nChar >= 0 ); return strncmp( p, q, nChar ) == 0; } inline static bool IsUTF8Continuation( char p ) { return ( p & 0x80 ) != 0; } static const char* ReadBOM( const char* p, bool* hasBOM ); // p is the starting location, // the UTF-8 value of the entity will be placed in value, and length filled in. static const char* GetCharacterRef( const char* p, char* value, int* length ); static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); // converts primitive types to strings static void ToStr( int v, char* buffer, int bufferSize ); static void ToStr( unsigned v, char* buffer, int bufferSize ); static void ToStr( bool v, char* buffer, int bufferSize ); static void ToStr( float v, char* buffer, int bufferSize ); static void ToStr( double v, char* buffer, int bufferSize ); static void ToStr(int64_t v, char* buffer, int bufferSize); // converts strings to primitive types static bool ToInt( const char* str, int* value ); static bool ToUnsigned( const char* str, unsigned* value ); static bool ToBool( const char* str, bool* value ); static bool ToFloat( const char* str, float* value ); static bool ToDouble( const char* str, double* value ); static bool ToInt64(const char* str, int64_t* value); // Changes what is serialized for a boolean value. // Default to "true" and "false". Shouldn't be changed // unless you have a special testing or compatibility need. // Be careful: static, global, & not thread safe. // Be sure to set static const memory as parameters. static void SetBoolSerialization(const char* writeTrue, const char* writeFalse); private: static const char* writeBoolTrue; static const char* writeBoolFalse; }; /** XMLNode is a base class for every object that is in the XML Document Object Model (DOM), except XMLAttributes. Nodes have siblings, a parent, and children which can be navigated. A node is always in a XMLDocument. The type of a XMLNode can be queried, and it can be cast to its more defined type. A XMLDocument allocates memory for all its Nodes. When the XMLDocument gets deleted, all its Nodes will also be deleted. @verbatim A Document can contain: Element (container or leaf) Comment (leaf) Unknown (leaf) Declaration( leaf ) An Element can contain: Element (container or leaf) Text (leaf) Attributes (not on tree) Comment (leaf) Unknown (leaf) @endverbatim */ class TINYXML2_LIB XMLNode { friend class XMLDocument; friend class XMLElement; public: /// Get the XMLDocument that owns this XMLNode. const XMLDocument* GetDocument() const { TIXMLASSERT( _document ); return _document; } /// Get the XMLDocument that owns this XMLNode. XMLDocument* GetDocument() { TIXMLASSERT( _document ); return _document; } /// Safely cast to an Element, or null. virtual XMLElement* ToElement() { return 0; } /// Safely cast to Text, or null. virtual XMLText* ToText() { return 0; } /// Safely cast to a Comment, or null. virtual XMLComment* ToComment() { return 0; } /// Safely cast to a Document, or null. virtual XMLDocument* ToDocument() { return 0; } /// Safely cast to a Declaration, or null. virtual XMLDeclaration* ToDeclaration() { return 0; } /// Safely cast to an Unknown, or null. virtual XMLUnknown* ToUnknown() { return 0; } virtual const XMLElement* ToElement() const { return 0; } virtual const XMLText* ToText() const { return 0; } virtual const XMLComment* ToComment() const { return 0; } virtual const XMLDocument* ToDocument() const { return 0; } virtual const XMLDeclaration* ToDeclaration() const { return 0; } virtual const XMLUnknown* ToUnknown() const { return 0; } /** The meaning of 'value' changes for the specific type. @verbatim Document: empty (NULL is returned, not an empty string) Element: name of the element Comment: the comment text Unknown: the tag contents Text: the text string @endverbatim */ const char* Value() const; /** Set the Value of an XML node. @sa Value() */ void SetValue( const char* val, bool staticMem=false ); /// Gets the line number the node is in, if the document was parsed from a file. int GetLineNum() const { return _parseLineNum; } /// Get the parent of this node on the DOM. const XMLNode* Parent() const { return _parent; } XMLNode* Parent() { return _parent; } /// Returns true if this node has no children. bool NoChildren() const { return !_firstChild; } /// Get the first child node, or null if none exists. const XMLNode* FirstChild() const { return _firstChild; } XMLNode* FirstChild() { return _firstChild; } /** Get the first child element, or optionally the first child element with the specified name. */ const XMLElement* FirstChildElement( const char* name = 0 ) const; XMLElement* FirstChildElement( const char* name = 0 ) { return const_cast(const_cast(this)->FirstChildElement( name )); } /// Get the last child node, or null if none exists. const XMLNode* LastChild() const { return _lastChild; } XMLNode* LastChild() { return _lastChild; } /** Get the last child element or optionally the last child element with the specified name. */ const XMLElement* LastChildElement( const char* name = 0 ) const; XMLElement* LastChildElement( const char* name = 0 ) { return const_cast(const_cast(this)->LastChildElement(name) ); } /// Get the previous (left) sibling node of this node. const XMLNode* PreviousSibling() const { return _prev; } XMLNode* PreviousSibling() { return _prev; } /// Get the previous (left) sibling element of this node, with an optionally supplied name. const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; XMLElement* PreviousSiblingElement( const char* name = 0 ) { return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); } /// Get the next (right) sibling node of this node. const XMLNode* NextSibling() const { return _next; } XMLNode* NextSibling() { return _next; } /// Get the next (right) sibling element of this node, with an optionally supplied name. const XMLElement* NextSiblingElement( const char* name = 0 ) const; XMLElement* NextSiblingElement( const char* name = 0 ) { return const_cast(const_cast(this)->NextSiblingElement( name ) ); } /** Add a child node as the last (right) child. If the child node is already part of the document, it is moved from its old location to the new location. Returns the addThis argument or 0 if the node does not belong to the same document. */ XMLNode* InsertEndChild( XMLNode* addThis ); XMLNode* LinkEndChild( XMLNode* addThis ) { return InsertEndChild( addThis ); } /** Add a child node as the first (left) child. If the child node is already part of the document, it is moved from its old location to the new location. Returns the addThis argument or 0 if the node does not belong to the same document. */ XMLNode* InsertFirstChild( XMLNode* addThis ); /** Add a node after the specified child node. If the child node is already part of the document, it is moved from its old location to the new location. Returns the addThis argument or 0 if the afterThis node is not a child of this node, or if the node does not belong to the same document. */ XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); /** Delete all the children of this node. */ void DeleteChildren(); /** Delete a child of this node. */ void DeleteChild( XMLNode* node ); /** Make a copy of this node, but not its children. You may pass in a Document pointer that will be the owner of the new Node. If the 'document' is null, then the node returned will be allocated from the current Document. (this->GetDocument()) Note: if called on a XMLDocument, this will return null. */ virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; /** Make a copy of this node and all its children. If the 'target' is null, then the nodes will be allocated in the current document. If 'target' is specified, the memory will be allocated is the specified XMLDocument. NOTE: This is probably not the correct tool to copy a document, since XMLDocuments can have multiple top level XMLNodes. You probably want to use XMLDocument::DeepCopy() */ XMLNode* DeepClone( XMLDocument* target ) const; /** Test if 2 nodes are the same, but don't test children. The 2 nodes do not need to be in the same Document. Note: if called on a XMLDocument, this will return false. */ virtual bool ShallowEqual( const XMLNode* compare ) const = 0; /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the XML tree will be conditionally visited and the host will be called back via the XMLVisitor interface. This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this interface versus any other.) The interface has been based on ideas from: - http://www.saxproject.org/ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern Which are both good references for "visiting". An example of using Accept(): @verbatim XMLPrinter printer; tinyxmlDoc.Accept( &printer ); const char* xmlcstr = printer.CStr(); @endverbatim */ virtual bool Accept( XMLVisitor* visitor ) const = 0; /** Set user data into the XMLNode. TinyXML-2 in no way processes or interprets user data. It is initially 0. */ void SetUserData(void* userData) { _userData = userData; } /** Get user data set into the XMLNode. TinyXML-2 in no way processes or interprets user data. It is initially 0. */ void* GetUserData() const { return _userData; } protected: XMLNode( XMLDocument* ); virtual ~XMLNode(); virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); XMLDocument* _document; XMLNode* _parent; mutable StrPair _value; int _parseLineNum; XMLNode* _firstChild; XMLNode* _lastChild; XMLNode* _prev; XMLNode* _next; void* _userData; private: MemPool* _memPool; void Unlink( XMLNode* child ); static void DeleteNode( XMLNode* node ); void InsertChildPreamble( XMLNode* insertThis ) const; const XMLElement* ToElementWithName( const char* name ) const; XMLNode( const XMLNode& ); // not supported XMLNode& operator=( const XMLNode& ); // not supported }; /** XML text. Note that a text node can have child element nodes, for example: @verbatim This is bold @endverbatim A text node can have 2 ways to output the next. "normal" output and CDATA. It will default to the mode it was parsed from the XML file and you generally want to leave it alone, but you can change the output mode with SetCData() and query it with CData(). */ class TINYXML2_LIB XMLText : public XMLNode { friend class XMLDocument; public: virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLText* ToText() { return this; } virtual const XMLText* ToText() const { return this; } /// Declare whether this should be CDATA or standard text. void SetCData( bool isCData ) { _isCData = isCData; } /// Returns true if this is a CDATA text element. bool CData() const { return _isCData; } virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} virtual ~XMLText() {} char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: bool _isCData; XMLText( const XMLText& ); // not supported XMLText& operator=( const XMLText& ); // not supported }; /** An XML Comment. */ class TINYXML2_LIB XMLComment : public XMLNode { friend class XMLDocument; public: virtual XMLComment* ToComment() { return this; } virtual const XMLComment* ToComment() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLComment( XMLDocument* doc ); virtual ~XMLComment(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); private: XMLComment( const XMLComment& ); // not supported XMLComment& operator=( const XMLComment& ); // not supported }; /** In correct XML the declaration is the first entry in the file. @verbatim @endverbatim TinyXML-2 will happily read or write files without a declaration, however. The text of the declaration isn't interpreted. It is parsed and written as a string. */ class TINYXML2_LIB XMLDeclaration : public XMLNode { friend class XMLDocument; public: virtual XMLDeclaration* ToDeclaration() { return this; } virtual const XMLDeclaration* ToDeclaration() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLDeclaration( XMLDocument* doc ); virtual ~XMLDeclaration(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: XMLDeclaration( const XMLDeclaration& ); // not supported XMLDeclaration& operator=( const XMLDeclaration& ); // not supported }; /** Any tag that TinyXML-2 doesn't recognize is saved as an unknown. It is a tag of text, but should not be modified. It will be written back to the XML, unchanged, when the file is saved. DTD tags get thrown into XMLUnknowns. */ class TINYXML2_LIB XMLUnknown : public XMLNode { friend class XMLDocument; public: virtual XMLUnknown* ToUnknown() { return this; } virtual const XMLUnknown* ToUnknown() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLUnknown( XMLDocument* doc ); virtual ~XMLUnknown(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: XMLUnknown( const XMLUnknown& ); // not supported XMLUnknown& operator=( const XMLUnknown& ); // not supported }; /** An attribute is a name-value pair. Elements have an arbitrary number of attributes, each with a unique name. @note The attributes are not XMLNodes. You may only query the Next() attribute in a list. */ class TINYXML2_LIB XMLAttribute { friend class XMLElement; public: /// The name of the attribute. const char* Name() const; /// The value of the attribute. const char* Value() const; /// Gets the line number the attribute is in, if the document was parsed from a file. int GetLineNum() const { return _parseLineNum; } /// The next attribute in the list. const XMLAttribute* Next() const { return _next; } /** IntValue interprets the attribute as an integer, and returns the value. If the value isn't an integer, 0 will be returned. There is no error checking; use QueryIntValue() if you need error checking. */ int IntValue() const { int i = 0; QueryIntValue(&i); return i; } int64_t Int64Value() const { int64_t i = 0; QueryInt64Value(&i); return i; } /// Query as an unsigned integer. See IntValue() unsigned UnsignedValue() const { unsigned i=0; QueryUnsignedValue( &i ); return i; } /// Query as a boolean. See IntValue() bool BoolValue() const { bool b=false; QueryBoolValue( &b ); return b; } /// Query as a double. See IntValue() double DoubleValue() const { double d=0; QueryDoubleValue( &d ); return d; } /// Query as a float. See IntValue() float FloatValue() const { float f=0; QueryFloatValue( &f ); return f; } /** QueryIntValue interprets the attribute as an integer, and returns the value in the provided parameter. The function will return XML_SUCCESS on success, and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. */ XMLError QueryIntValue( int* value ) const; /// See QueryIntValue XMLError QueryUnsignedValue( unsigned int* value ) const; /// See QueryIntValue XMLError QueryInt64Value(int64_t* value) const; /// See QueryIntValue XMLError QueryBoolValue( bool* value ) const; /// See QueryIntValue XMLError QueryDoubleValue( double* value ) const; /// See QueryIntValue XMLError QueryFloatValue( float* value ) const; /// Set the attribute to a string value. void SetAttribute( const char* value ); /// Set the attribute to value. void SetAttribute( int value ); /// Set the attribute to value. void SetAttribute( unsigned value ); /// Set the attribute to value. void SetAttribute(int64_t value); /// Set the attribute to value. void SetAttribute( bool value ); /// Set the attribute to value. void SetAttribute( double value ); /// Set the attribute to value. void SetAttribute( float value ); private: enum { BUF_SIZE = 200 }; XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} virtual ~XMLAttribute() {} XMLAttribute( const XMLAttribute& ); // not supported void operator=( const XMLAttribute& ); // not supported void SetName( const char* name ); char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); mutable StrPair _name; mutable StrPair _value; int _parseLineNum; XMLAttribute* _next; MemPool* _memPool; }; /** The element is a container class. It has a value, the element name, and can contain other elements, text, comments, and unknowns. Elements also contain an arbitrary number of attributes. */ class TINYXML2_LIB XMLElement : public XMLNode { friend class XMLDocument; public: /// Get the name of an element (which is the Value() of the node.) const char* Name() const { return Value(); } /// Set the name of the element. void SetName( const char* str, bool staticMem=false ) { SetValue( str, staticMem ); } virtual XMLElement* ToElement() { return this; } virtual const XMLElement* ToElement() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. For example: @verbatim const char* value = ele->Attribute( "foo" ); @endverbatim The 'value' parameter is normally null. However, if specified, the attribute will only be returned if the 'name' and 'value' match. This allow you to write code: @verbatim if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); @endverbatim rather than: @verbatim if ( ele->Attribute( "foo" ) ) { if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); } @endverbatim */ const char* Attribute( const char* name, const char* value=0 ) const; /** Given an attribute name, IntAttribute() returns the value of the attribute interpreted as an integer. The default value will be returned if the attribute isn't present, or if there is an error. (For a method with error checking, see QueryIntAttribute()). */ int IntAttribute(const char* name, int defaultValue = 0) const; /// See IntAttribute() unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; /// See IntAttribute() int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; /// See IntAttribute() bool BoolAttribute(const char* name, bool defaultValue = false) const; /// See IntAttribute() double DoubleAttribute(const char* name, double defaultValue = 0) const; /// See IntAttribute() float FloatAttribute(const char* name, float defaultValue = 0) const; /** Given an attribute name, QueryIntAttribute() returns XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion can't be performed, or XML_NO_ATTRIBUTE if the attribute doesn't exist. If successful, the result of the conversion will be written to 'value'. If not successful, nothing will be written to 'value'. This allows you to provide default value: @verbatim int value = 10; QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 @endverbatim */ XMLError QueryIntAttribute( const char* name, int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryIntValue( value ); } /// See QueryIntAttribute() XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryUnsignedValue( value ); } /// See QueryIntAttribute() XMLError QueryInt64Attribute(const char* name, int64_t* value) const { const XMLAttribute* a = FindAttribute(name); if (!a) { return XML_NO_ATTRIBUTE; } return a->QueryInt64Value(value); } /// See QueryIntAttribute() XMLError QueryBoolAttribute( const char* name, bool* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryBoolValue( value ); } /// See QueryIntAttribute() XMLError QueryDoubleAttribute( const char* name, double* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryDoubleValue( value ); } /// See QueryIntAttribute() XMLError QueryFloatAttribute( const char* name, float* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryFloatValue( value ); } /// See QueryIntAttribute() XMLError QueryStringAttribute(const char* name, const char** value) const { const XMLAttribute* a = FindAttribute(name); if (!a) { return XML_NO_ATTRIBUTE; } *value = a->Value(); return XML_SUCCESS; } /** Given an attribute name, QueryAttribute() returns XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion can't be performed, or XML_NO_ATTRIBUTE if the attribute doesn't exist. It is overloaded for the primitive types, and is a generally more convenient replacement of QueryIntAttribute() and related functions. If successful, the result of the conversion will be written to 'value'. If not successful, nothing will be written to 'value'. This allows you to provide default value: @verbatim int value = 10; QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 @endverbatim */ int QueryAttribute( const char* name, int* value ) const { return QueryIntAttribute( name, value ); } int QueryAttribute( const char* name, unsigned int* value ) const { return QueryUnsignedAttribute( name, value ); } int QueryAttribute(const char* name, int64_t* value) const { return QueryInt64Attribute(name, value); } int QueryAttribute( const char* name, bool* value ) const { return QueryBoolAttribute( name, value ); } int QueryAttribute( const char* name, double* value ) const { return QueryDoubleAttribute( name, value ); } int QueryAttribute( const char* name, float* value ) const { return QueryFloatAttribute( name, value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, const char* value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, int value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, unsigned value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute(const char* name, int64_t value) { XMLAttribute* a = FindOrCreateAttribute(name); a->SetAttribute(value); } /// Sets the named attribute to value. void SetAttribute( const char* name, bool value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, double value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, float value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /** Delete an attribute. */ void DeleteAttribute( const char* name ); /// Return the first attribute in the list. const XMLAttribute* FirstAttribute() const { return _rootAttribute; } /// Query a specific attribute in the list. const XMLAttribute* FindAttribute( const char* name ) const; /** Convenience function for easy access to the text inside an element. Although easy and concise, GetText() is limited compared to getting the XMLText child and accessing it directly. If the first child of 'this' is a XMLText, the GetText() returns the character string of the Text node, else null is returned. This is a convenient method for getting the text of simple contained text: @verbatim This is text const char* str = fooElement->GetText(); @endverbatim 'str' will be a pointer to "This is text". Note that this function can be misleading. If the element foo was created from this XML: @verbatim This is text @endverbatim then the value of str would be null. The first child node isn't a text node, it is another element. From this XML: @verbatim This is text @endverbatim GetText() will return "This is ". */ const char* GetText() const; /** Convenience function for easy access to the text inside an element. Although easy and concise, SetText() is limited compared to creating an XMLText child and mutating it directly. If the first child of 'this' is a XMLText, SetText() sets its value to the given string, otherwise it will create a first child that is an XMLText. This is a convenient method for setting the text of simple contained text: @verbatim This is text fooElement->SetText( "Hullaballoo!" ); Hullaballoo! @endverbatim Note that this function can be misleading. If the element foo was created from this XML: @verbatim This is text @endverbatim then it will not change "This is text", but rather prefix it with a text element: @verbatim Hullaballoo!This is text @endverbatim For this XML: @verbatim @endverbatim SetText() will generate @verbatim Hullaballoo! @endverbatim */ void SetText( const char* inText ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( int value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( unsigned value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText(int64_t value); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( bool value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( double value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( float value ); /** Convenience method to query the value of a child text node. This is probably best shown by example. Given you have a document is this form: @verbatim 1 1.4 @endverbatim The QueryIntText() and similar functions provide a safe and easier way to get to the "value" of x and y. @verbatim int x = 0; float y = 0; // types of x and y are contrived for example const XMLElement* xElement = pointElement->FirstChildElement( "x" ); const XMLElement* yElement = pointElement->FirstChildElement( "y" ); xElement->QueryIntText( &x ); yElement->QueryFloatText( &y ); @endverbatim @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. */ XMLError QueryIntText( int* ival ) const; /// See QueryIntText() XMLError QueryUnsignedText( unsigned* uval ) const; /// See QueryIntText() XMLError QueryInt64Text(int64_t* uval) const; /// See QueryIntText() XMLError QueryBoolText( bool* bval ) const; /// See QueryIntText() XMLError QueryDoubleText( double* dval ) const; /// See QueryIntText() XMLError QueryFloatText( float* fval ) const; int IntText(int defaultValue = 0) const; /// See QueryIntText() unsigned UnsignedText(unsigned defaultValue = 0) const; /// See QueryIntText() int64_t Int64Text(int64_t defaultValue = 0) const; /// See QueryIntText() bool BoolText(bool defaultValue = false) const; /// See QueryIntText() double DoubleText(double defaultValue = 0) const; /// See QueryIntText() float FloatText(float defaultValue = 0) const; // internal: enum ElementClosingType { OPEN, // CLOSED, // CLOSING // }; ElementClosingType ClosingType() const { return _closingType; } virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: XMLElement( XMLDocument* doc ); virtual ~XMLElement(); XMLElement( const XMLElement& ); // not supported void operator=( const XMLElement& ); // not supported XMLAttribute* FindAttribute( const char* name ) { return const_cast(const_cast(this)->FindAttribute( name )); } XMLAttribute* FindOrCreateAttribute( const char* name ); //void LinkAttribute( XMLAttribute* attrib ); char* ParseAttributes( char* p, int* curLineNumPtr ); static void DeleteAttribute( XMLAttribute* attribute ); XMLAttribute* CreateAttribute(); enum { BUF_SIZE = 200 }; ElementClosingType _closingType; // The attribute list is ordered; there is no 'lastAttribute' // because the list needs to be scanned for dupes before adding // a new attribute. XMLAttribute* _rootAttribute; }; enum Whitespace { PRESERVE_WHITESPACE, COLLAPSE_WHITESPACE }; /** A Document binds together all the functionality. It can be saved, loaded, and printed to the screen. All Nodes are connected and allocated to a Document. If the Document is deleted, all its Nodes are also deleted. */ class TINYXML2_LIB XMLDocument : public XMLNode { friend class XMLElement; // Gives access to SetError, but over-access for everything else. // Wishing C++ had "internal" scope. friend class XMLNode; friend class XMLText; friend class XMLComment; friend class XMLDeclaration; friend class XMLUnknown; public: /// constructor XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); ~XMLDocument(); virtual XMLDocument* ToDocument() { TIXMLASSERT( this == _document ); return this; } virtual const XMLDocument* ToDocument() const { TIXMLASSERT( this == _document ); return this; } /** Parse an XML file from a character string. Returns XML_SUCCESS (0) on success, or an errorID. You may optionally pass in the 'nBytes', which is the number of bytes which will be parsed. If not specified, TinyXML-2 will assume 'xml' points to a null terminated string. */ XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); /** Load an XML file from disk. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError LoadFile( const char* filename ); /** Load an XML file from disk. You are responsible for providing and closing the FILE*. NOTE: The file should be opened as binary ("rb") not text in order for TinyXML-2 to correctly do newline normalization. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError LoadFile( FILE* ); /** Save the XML file to disk. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError SaveFile( const char* filename, bool compact = false ); /** Save the XML file to disk. You are responsible for providing and closing the FILE*. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError SaveFile( FILE* fp, bool compact = false ); bool ProcessEntities() const { return _processEntities; } Whitespace WhitespaceMode() const { return _whitespaceMode; } /** Returns true if this document has a leading Byte Order Mark of UTF8. */ bool HasBOM() const { return _writeBOM; } /** Sets whether to write the BOM when writing the file. */ void SetBOM( bool useBOM ) { _writeBOM = useBOM; } /** Return the root element of DOM. Equivalent to FirstChildElement(). To get the first node, use FirstChild(). */ XMLElement* RootElement() { return FirstChildElement(); } const XMLElement* RootElement() const { return FirstChildElement(); } /** Print the Document. If the Printer is not provided, it will print to stdout. If you provide Printer, this can print to a file: @verbatim XMLPrinter printer( fp ); doc.Print( &printer ); @endverbatim Or you can use a printer to print to memory: @verbatim XMLPrinter printer; doc.Print( &printer ); // printer.CStr() has a const char* to the XML @endverbatim */ void Print( XMLPrinter* streamer=0 ) const; virtual bool Accept( XMLVisitor* visitor ) const; /** Create a new Element associated with this Document. The memory for the Element is managed by the Document. */ XMLElement* NewElement( const char* name ); /** Create a new Comment associated with this Document. The memory for the Comment is managed by the Document. */ XMLComment* NewComment( const char* comment ); /** Create a new Text associated with this Document. The memory for the Text is managed by the Document. */ XMLText* NewText( const char* text ); /** Create a new Declaration associated with this Document. The memory for the object is managed by the Document. If the 'text' param is null, the standard declaration is used.: @verbatim @endverbatim */ XMLDeclaration* NewDeclaration( const char* text=0 ); /** Create a new Unknown associated with this Document. The memory for the object is managed by the Document. */ XMLUnknown* NewUnknown( const char* text ); /** Delete a node associated with this document. It will be unlinked from the DOM. */ void DeleteNode( XMLNode* node ); void ClearError() { SetError(XML_SUCCESS, 0, 0); } /// Return true if there was an error parsing the document. bool Error() const { return _errorID != XML_SUCCESS; } /// Return the errorID. XMLError ErrorID() const { return _errorID; } const char* ErrorName() const; static const char* ErrorIDToName(XMLError errorID); /** Returns a "long form" error description. A hopefully helpful diagnostic with location, line number, and/or additional info. */ const char* ErrorStr() const; /// A (trivial) utility function that prints the ErrorStr() to stdout. void PrintError() const; /// Return the line where the error occurred, or zero if unknown. int ErrorLineNum() const { return _errorLineNum; } /// Clear the document, resetting it to the initial state. void Clear(); /** Copies this document to a target document. The target will be completely cleared before the copy. If you want to copy a sub-tree, see XMLNode::DeepClone(). NOTE: that the 'target' must be non-null. */ void DeepCopy(XMLDocument* target) const; // internal char* Identify( char* p, XMLNode** node ); // internal void MarkInUse(XMLNode*); virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { return 0; } virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { return false; } private: XMLDocument( const XMLDocument& ); // not supported void operator=( const XMLDocument& ); // not supported bool _writeBOM; bool _processEntities; XMLError _errorID; Whitespace _whitespaceMode; mutable StrPair _errorStr; int _errorLineNum; char* _charBuffer; int _parseCurLineNum; // Memory tracking does add some overhead. // However, the code assumes that you don't // have a bunch of unlinked nodes around. // Therefore it takes less memory to track // in the document vs. a linked list in the XMLNode, // and the performance is the same. DynArray _unlinked; MemPoolT< sizeof(XMLElement) > _elementPool; MemPoolT< sizeof(XMLAttribute) > _attributePool; MemPoolT< sizeof(XMLText) > _textPool; MemPoolT< sizeof(XMLComment) > _commentPool; static const char* _errorNames[XML_ERROR_COUNT]; void Parse(); void SetError( XMLError error, int lineNum, const char* format, ... ); template NodeType* CreateUnlinkedNode( MemPoolT& pool ); }; template inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) { TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); NodeType* returnNode = new (pool.Alloc()) NodeType( this ); TIXMLASSERT( returnNode ); returnNode->_memPool = &pool; _unlinked.Push(returnNode); return returnNode; } /** A XMLHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 DOM structure. It is a separate utility class. Take an example: @verbatim @endverbatim Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very easy to write a *lot* of code that looks like: @verbatim XMLElement* root = document.FirstChildElement( "Document" ); if ( root ) { XMLElement* element = root->FirstChildElement( "Element" ); if ( element ) { XMLElement* child = element->FirstChildElement( "Child" ); if ( child ) { XMLElement* child2 = child->NextSiblingElement( "Child" ); if ( child2 ) { // Finally do something useful. @endverbatim And that doesn't even cover "else" cases. XMLHandle addresses the verbosity of such code. A XMLHandle checks for null pointers so it is perfectly safe and correct to use: @verbatim XMLHandle docHandle( &document ); XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); if ( child2 ) { // do something useful @endverbatim Which is MUCH more concise and useful. It is also safe to copy handles - internally they are nothing more than node pointers. @verbatim XMLHandle handleCopy = handle; @endverbatim See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. */ class TINYXML2_LIB XMLHandle { public: /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. XMLHandle( XMLNode* node ) : _node( node ) { } /// Create a handle from a node. XMLHandle( XMLNode& node ) : _node( &node ) { } /// Copy constructor XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { } /// Assignment XMLHandle& operator=( const XMLHandle& ref ) { _node = ref._node; return *this; } /// Get the first child of this handle. XMLHandle FirstChild() { return XMLHandle( _node ? _node->FirstChild() : 0 ); } /// Get the first child element of this handle. XMLHandle FirstChildElement( const char* name = 0 ) { return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); } /// Get the last child of this handle. XMLHandle LastChild() { return XMLHandle( _node ? _node->LastChild() : 0 ); } /// Get the last child element of this handle. XMLHandle LastChildElement( const char* name = 0 ) { return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); } /// Get the previous sibling of this handle. XMLHandle PreviousSibling() { return XMLHandle( _node ? _node->PreviousSibling() : 0 ); } /// Get the previous sibling element of this handle. XMLHandle PreviousSiblingElement( const char* name = 0 ) { return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); } /// Get the next sibling of this handle. XMLHandle NextSibling() { return XMLHandle( _node ? _node->NextSibling() : 0 ); } /// Get the next sibling element of this handle. XMLHandle NextSiblingElement( const char* name = 0 ) { return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); } /// Safe cast to XMLNode. This can return null. XMLNode* ToNode() { return _node; } /// Safe cast to XMLElement. This can return null. XMLElement* ToElement() { return ( _node ? _node->ToElement() : 0 ); } /// Safe cast to XMLText. This can return null. XMLText* ToText() { return ( _node ? _node->ToText() : 0 ); } /// Safe cast to XMLUnknown. This can return null. XMLUnknown* ToUnknown() { return ( _node ? _node->ToUnknown() : 0 ); } /// Safe cast to XMLDeclaration. This can return null. XMLDeclaration* ToDeclaration() { return ( _node ? _node->ToDeclaration() : 0 ); } private: XMLNode* _node; }; /** A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the same in all regards, except for the 'const' qualifiers. See XMLHandle for API. */ class TINYXML2_LIB XMLConstHandle { public: XMLConstHandle( const XMLNode* node ) : _node( node ) { } XMLConstHandle( const XMLNode& node ) : _node( &node ) { } XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { } XMLConstHandle& operator=( const XMLConstHandle& ref ) { _node = ref._node; return *this; } const XMLConstHandle FirstChild() const { return XMLConstHandle( _node ? _node->FirstChild() : 0 ); } const XMLConstHandle FirstChildElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); } const XMLConstHandle LastChild() const { return XMLConstHandle( _node ? _node->LastChild() : 0 ); } const XMLConstHandle LastChildElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); } const XMLConstHandle PreviousSibling() const { return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); } const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); } const XMLConstHandle NextSibling() const { return XMLConstHandle( _node ? _node->NextSibling() : 0 ); } const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); } const XMLNode* ToNode() const { return _node; } const XMLElement* ToElement() const { return ( _node ? _node->ToElement() : 0 ); } const XMLText* ToText() const { return ( _node ? _node->ToText() : 0 ); } const XMLUnknown* ToUnknown() const { return ( _node ? _node->ToUnknown() : 0 ); } const XMLDeclaration* ToDeclaration() const { return ( _node ? _node->ToDeclaration() : 0 ); } private: const XMLNode* _node; }; /** Printing functionality. The XMLPrinter gives you more options than the XMLDocument::Print() method. It can: -# Print to memory. -# Print to a file you provide. -# Print XML without a XMLDocument. Print to Memory @verbatim XMLPrinter printer; doc.Print( &printer ); SomeFunction( printer.CStr() ); @endverbatim Print to a File You provide the file pointer. @verbatim XMLPrinter printer( fp ); doc.Print( &printer ); @endverbatim Print without a XMLDocument When loading, an XML parser is very useful. However, sometimes when saving, it just gets in the way. The code is often set up for streaming, and constructing the DOM is just overhead. The Printer supports the streaming case. The following code prints out a trivially simple XML file without ever creating an XML document. @verbatim XMLPrinter printer( fp ); printer.OpenElement( "foo" ); printer.PushAttribute( "foo", "bar" ); printer.CloseElement(); @endverbatim */ class TINYXML2_LIB XMLPrinter : public XMLVisitor { public: /** Construct the printer. If the FILE* is specified, this will print to the FILE. Else it will print to memory, and the result is available in CStr(). If 'compact' is set to true, then output is created with only required whitespace and newlines. */ XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); virtual ~XMLPrinter() {} /** If streaming, write the BOM and declaration. */ void PushHeader( bool writeBOM, bool writeDeclaration ); /** If streaming, start writing an element. The element must be closed with CloseElement() */ void OpenElement( const char* name, bool compactMode=false ); /// If streaming, add an attribute to an open element. void PushAttribute( const char* name, const char* value ); void PushAttribute( const char* name, int value ); void PushAttribute( const char* name, unsigned value ); void PushAttribute(const char* name, int64_t value); void PushAttribute( const char* name, bool value ); void PushAttribute( const char* name, double value ); /// If streaming, close the Element. virtual void CloseElement( bool compactMode=false ); /// Add a text node. void PushText( const char* text, bool cdata=false ); /// Add a text node from an integer. void PushText( int value ); /// Add a text node from an unsigned. void PushText( unsigned value ); /// Add a text node from an unsigned. void PushText(int64_t value); /// Add a text node from a bool. void PushText( bool value ); /// Add a text node from a float. void PushText( float value ); /// Add a text node from a double. void PushText( double value ); /// Add a comment void PushComment( const char* comment ); void PushDeclaration( const char* value ); void PushUnknown( const char* value ); virtual bool VisitEnter( const XMLDocument& /*doc*/ ); virtual bool VisitExit( const XMLDocument& /*doc*/ ) { return true; } virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); virtual bool VisitExit( const XMLElement& element ); virtual bool Visit( const XMLText& text ); virtual bool Visit( const XMLComment& comment ); virtual bool Visit( const XMLDeclaration& declaration ); virtual bool Visit( const XMLUnknown& unknown ); /** If in print to memory mode, return a pointer to the XML file in memory. */ const char* CStr() const { return _buffer.Mem(); } /** If in print to memory mode, return the size of the XML file in memory. (Note the size returned includes the terminating null.) */ int CStrSize() const { return _buffer.Size(); } /** If in print to memory mode, reset the buffer to the beginning. */ void ClearBuffer() { _buffer.Clear(); _buffer.Push(0); _firstElement = true; } protected: virtual bool CompactMode( const XMLElement& ) { return _compactMode; } /** Prints out the space before an element. You may override to change the space and tabs used. A PrintSpace() override should call Print(). */ virtual void PrintSpace( int depth ); void Print( const char* format, ... ); void Write( const char* data, size_t size ); inline void Write( const char* data ) { Write( data, strlen( data ) ); } void Putc( char ch ); void SealElementIfJustOpened(); bool _elementJustOpened; DynArray< const char*, 10 > _stack; private: void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. bool _firstElement; FILE* _fp; int _depth; int _textDepth; bool _processEntities; bool _compactMode; enum { ENTITY_RANGE = 64, BUF_SIZE = 200 }; bool _entityFlag[ENTITY_RANGE]; bool _restrictedEntityFlag[ENTITY_RANGE]; DynArray< char, 20 > _buffer; // Prohibit cloning, intentionally not implemented XMLPrinter( const XMLPrinter& ); XMLPrinter& operator=( const XMLPrinter& ); }; } // tinyxml2 #if defined(_MSC_VER) # pragma warning(pop) #endif #endif // TINYXML2_INCLUDED cppcheck-2.7/externals/z3-LICENSE000066400000000000000000000021101417746362400165160ustar00rootroot00000000000000Z3 Copyright (c) Microsoft Corporation All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.cppcheck-2.7/externals/z3_version_old.h000066400000000000000000000002621417746362400202750ustar00rootroot00000000000000#ifndef Z3_MAJOR_VERSION #define Z3_MAJOR_VERSION 0 #endif #ifndef Z3_MINOR_VERSION #define Z3_MINOR_VERSION 0 #endif #ifndef Z3_BUILD_NUMBER #define Z3_BUILD_NUMBER 0 #endif cppcheck-2.7/generate_coverage_report000077500000000000000000000010231417746362400201420ustar00rootroot00000000000000#!/bin/bash set -e make clean rm -rf coverage_report make test CXXFLAGS="-g -fprofile-arcs -ftest-coverage" test/cfg/runtests.sh gcov lib/*.cpp -o lib/ lcov --directory ./ --capture --output-file lcov_tmp.info -b ./ lcov --extract lcov_tmp.info "$(pwd)/*" --output-file lcov.info genhtml lcov.info -o coverage_report --frame --legend --demangle-cpp rm cli/*.gcda rm cli/*.gcno rm lib/*.gcda rm lib/*.gcno rm test/*.gcda rm test/*.gcno rm externals/tinyxml2/*.gcda rm externals/tinyxml2/*.gcno rm lcov.info lcov_tmp.info make clean cppcheck-2.7/gui/000077500000000000000000000000001417746362400137445ustar00rootroot00000000000000cppcheck-2.7/gui/CMakeLists.txt000066400000000000000000000050401417746362400165030ustar00rootroot00000000000000if (BUILD_GUI) if (CMAKE_BUILD_TYPE MATCHES "Release") add_definitions(-DQT_NO_DEBUG) add_definitions(-DQT_NO_DEBUG_OUTPUT) add_definitions(-DQT_NO_WARNING_OUTPUT) else() add_definitions(-DQT_DEBUG) endif() file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") file(GLOB uis "*.ui") file(GLOB tss "*.ts") QT5_WRAP_UI(uis_hdrs ${uis}) QT5_ADD_RESOURCES(resources "gui.qrc") QT5_ADD_TRANSLATION(qms ${tss}) list(APPEND cppcheck-gui-deps ${hdrs} ${uis_hdrs} ${resources} ${qms} ) add_custom_target(gui-build-deps SOURCES ${cppcheck-gui-deps}) list(APPEND cppcheck-gui_SOURCES ${srcs} $ $) if(USE_BUNDLED_TINYXML2) list(APPEND cppcheck-gui_SOURCES $) endif() add_executable(cppcheck-gui ${cppcheck-gui-deps} ${cppcheck-gui_SOURCES}) set_target_properties(cppcheck-gui PROPERTIES AUTOMOC ON) target_include_directories(cppcheck-gui PRIVATE ${PROJECT_SOURCE_DIR}/lib/) if(USE_BUNDLED_TINYXML2) target_include_directories(cppcheck-gui PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2/) endif() if (HAVE_RULES) target_link_libraries(cppcheck-gui ${PCRE_LIBRARY}) endif() if (USE_Z3) target_link_libraries(cppcheck-gui ${Z3_LIBRARIES}) endif() if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) target_link_libraries(cppcheck-gui tinyxml2) endif() target_link_libraries(cppcheck-gui Qt5::Core Qt5::Gui Qt5::Widgets Qt5::PrintSupport Qt5::Help) if(WITH_QCHART) target_compile_definitions (cppcheck-gui PRIVATE HAVE_QCHART) target_link_libraries(cppcheck-gui Qt5::Charts) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8.0.0") # Q_UNUSED() in generated code target_compile_options(cppcheck-gui PRIVATE -Wno-extra-semi-stmt) endif() endif() install(TARGETS cppcheck-gui RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) install(FILES ${qms} DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) install(FILES cppcheck-gui.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) # icons install(FILES cppcheck-gui.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) install(FILES cppcheck-gui.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps) if (BUILD_TESTS) add_subdirectory(test) endif() endif() cppcheck-2.7/gui/about.ui000066400000000000000000000111011417746362400154070ustar00rootroot00000000000000 About 0 0 478 375 About Cppcheck :/cppcheck-gui.png Qt::Vertical 20 40 Qt::Horizontal 40 20 Version %1 Cppcheck - A tool for static C/C++ code analysis. true Copyright © 2007-%1 Cppcheck team. true This program is licensed under the terms of the GNU General Public License version 3 true Visit Cppcheck homepage at %1 true true <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> Qt::Horizontal QDialogButtonBox::Ok mButtons accepted() About accept() 248 254 157 274 mButtons rejected() About reject() 316 260 286 274 cppcheck-2.7/gui/aboutdialog.cpp000066400000000000000000000027411417746362400167460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "aboutdialog.h" #include #include AboutDialog::AboutDialog(const QString &version, const QString &extraVersion, QWidget *parent) : QDialog(parent) { mUI.setupUi(this); QString fmtVersion(version); if (!extraVersion.isEmpty()) { fmtVersion += " (" + extraVersion + ")"; } mUI.mVersion->setText(mUI.mVersion->text().arg(fmtVersion)); QString date = __DATE__; mUI.mCopyright->setText(mUI.mCopyright->text().arg(date.right(4))); QString url = "https://cppcheck.sourceforge.io/"; mUI.mHomepage->setText(mUI.mHomepage->text().arg(url)); connect(mUI.mButtons, &QDialogButtonBox::accepted, this, &AboutDialog::accept); } cppcheck-2.7/gui/aboutdialog.h000066400000000000000000000022451417746362400164120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ABOUT_DIALOG_H #define ABOUT_DIALOG_H #include "ui_about.h" #include #include class QWidget; /// @addtogroup GUI /// @{ /** * @brief About dialog * */ class AboutDialog : public QDialog { Q_OBJECT public: AboutDialog(const QString &version, const QString &extraVersion, QWidget *parent = nullptr); private: Ui::About mUI; }; /// @} #endif // ABOUT_DIALOG_H cppcheck-2.7/gui/application.cpp000066400000000000000000000016741417746362400167630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "application.h" Application::Application(const QString &name, const QString &path, const QString ¶ms) : mName(name) , mPath(path) , mParameters(params) {} cppcheck-2.7/gui/application.h000066400000000000000000000057471417746362400164350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APPLICATION_H #define APPLICATION_H #include /** * @brief A class containing information of the application to execute. * * Each application has a name and a path. Name is displayed to the user * and has no other meaning. It isn't used to start the application. * Path contains the full path to the application containing the executable name. * Parameters contains the command line arguments for the executable. * * User can also specify certain predefined strings to parameters. These strings * will be replaced with appropriate values concerning the error. Strings are: * (file) - Filename containing the error * (line) - Line number containing the error * (message) - Error message * (severity) - Error severity * * Example opening a file with Kate and make Kate scroll to the correct line. * Executable: kate * Parameters: -l(line) (file) */ class Application { public: Application() {} Application(const QString &name, const QString &path, const QString ¶ms); /** * @brief Get application name. * @return Application name. */ QString getName() const { return mName; } /** * @brief Get application path. * @return Application path. */ QString getPath() const { return mPath; } /** * @brief Get application command line parameters. * @return Application command line parameters. */ QString getParameters() const { return mParameters; } /** * @brief Set application name. * @param name Application name. */ void setName(const QString &name) { mName = name; } /** * @brief Set application path. * @param path Application path. */ void setPath(const QString &path) { mPath = path; } /** * @brief Set application command line parameters. * @param parameters Application command line parameters. */ void setParameters(const QString ¶meters) { mParameters = parameters; } private: /** * @brief Application's name */ QString mName; /** * @brief Application's path */ QString mPath; /** * @brief Application's parameters */ QString mParameters; }; #endif // APPLICATION_H cppcheck-2.7/gui/application.ui000066400000000000000000000125211417746362400166070ustar00rootroot00000000000000 ApplicationDialog Qt::WindowModal 0 0 569 471 0 0 Add an application 0 0 0 0 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Qt::AutoText false Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true &Name: mName &Executable: mPath &Parameters: mParameters Qt::Horizontal 40 20 Browse Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok mName mPath mParameters mButtonBrowse mButtons mButtons accepted() ApplicationDialog accept() 248 254 157 274 mButtons rejected() ApplicationDialog reject() 316 260 286 274 cppcheck-2.7/gui/applicationdialog.cpp000066400000000000000000000061211417746362400201330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "applicationdialog.h" #include "application.h" #include "common.h" #include #include #include #include ApplicationDialog::ApplicationDialog(const QString &title, Application &app, QWidget *parent) : QDialog(parent), mApplication(app) { mUI.setupUi(this); connect(mUI.mButtonBrowse, &QPushButton::clicked, this, &ApplicationDialog::browse); connect(mUI.mButtons, &QDialogButtonBox::accepted, this, &ApplicationDialog::ok); connect(mUI.mButtons, &QDialogButtonBox::rejected, this, &ApplicationDialog::reject); mUI.mPath->setText(app.getPath()); mUI.mName->setText(app.getName()); mUI.mParameters->setText(app.getParameters()); setWindowTitle(title); adjustSize(); } ApplicationDialog::~ApplicationDialog() { //dtor } void ApplicationDialog::browse() { QString filter; #ifdef Q_OS_WIN // In Windows (almost) all executables have .exe extension // so it does not make sense to show everything. filter += tr("Executable files (*.exe);;All files(*.*)"); #endif // Q_OS_WIN QString selectedFile = QFileDialog::getOpenFileName(this, tr("Select viewer application"), getPath(SETTINGS_LAST_APP_PATH), filter); if (!selectedFile.isEmpty()) { setPath(SETTINGS_LAST_APP_PATH, selectedFile); QString path(QDir::toNativeSeparators(selectedFile)); mUI.mPath->setText(path); } } void ApplicationDialog::ok() { if (mUI.mName->text().isEmpty() || mUI.mPath->text().isEmpty()) { QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), tr("You must specify a name, a path and optionally parameters for the application!"), QMessageBox::Ok, this); msg.exec(); reject(); } else { // Convert possible native (Windows) path to internal presentation format mApplication.setName(mUI.mName->text()); mApplication.setPath(QDir::fromNativeSeparators(mUI.mPath->text())); mApplication.setParameters(mUI.mParameters->text()); accept(); } } cppcheck-2.7/gui/applicationdialog.h000066400000000000000000000035411417746362400176030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APPLICATIONDIALOG_H #define APPLICATIONDIALOG_H #include "ui_application.h" #include #include class QWidget; class Application; /// @addtogroup GUI /// @{ /** * @brief Dialog to edit a startable application. * User can open errors with user specified applications. This is a dialog * to modify/add an application to open errors with. * */ class ApplicationDialog : public QDialog { Q_OBJECT public: /** * @brief Constructor. * @param title Title for the dialog. * @param app Application definition. * @param parent Parent widget. */ ApplicationDialog(const QString &title, Application &app, QWidget *parent = nullptr); virtual ~ApplicationDialog(); protected slots: void ok(); /** * @brief Slot to browse for an application * */ void browse(); protected: /** * @brief UI from the Qt designer * */ Ui::ApplicationDialog mUI; private: /** * @brief Underlying Application */ Application& mApplication; }; /// @} #endif // APPLICATIONDIALOG_H cppcheck-2.7/gui/applicationlist.cpp000066400000000000000000000220421417746362400176470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "applicationlist.h" #include "application.h" #include "common.h" #include #include #include ApplicationList::ApplicationList(QObject *parent) : QObject(parent), mDefaultApplicationIndex(-1) { //ctor } ApplicationList::~ApplicationList() { clear(); } bool ApplicationList::loadSettings() { QSettings settings; QStringList names = settings.value(SETTINGS_APPLICATION_NAMES, QStringList()).toStringList(); QStringList paths = settings.value(SETTINGS_APPLICATION_PATHS, QStringList()).toStringList(); QStringList params = settings.value(SETTINGS_APPLICATION_PARAMS, QStringList()).toStringList(); int defapp = settings.value(SETTINGS_APPLICATION_DEFAULT, -1).toInt(); // Params will be empty first time starting with the new setting. // Return false and inform user about problem with application settings. bool succeeded = true; if (!names.empty() && !paths.empty() && params.empty()) { for (int i = 0; i < paths.length(); i++) params << QString(); succeeded = false; } if (names.empty() && paths.empty() && params.empty()) { #ifndef _WIN32 // use as default for gnome environments if (QFileInfo("/usr/bin/gedit").isExecutable()) { Application app; app.setName("gedit"); app.setPath("/usr/bin/gedit"); app.setParameters("+(line) (file)"); addApplication(app); defapp = 0; } checkAndAddApplication("/usr/bin/geany","geany","+(line) (file)"); checkAndAddApplication("/usr/bin/qtcreator","Qt Creator","-client (file):(line)"); // use as default for kde environments if (QFileInfo("/usr/bin/kate").isExecutable()) { Application app; app.setName("kate"); app.setPath("/usr/bin/kate"); app.setParameters("-l(line) (file)"); addApplication(app); defapp = 0; } #else if (findDefaultWindowsEditor()) { defapp = 0; } #endif } else if (names.size() == paths.size()) { for (int i = 0; i < names.size(); i++) { const Application app(names[i], paths[i], params[i]); addApplication(app); } } if (defapp == -1) mDefaultApplicationIndex = 0; else if (defapp < names.size()) mDefaultApplicationIndex = defapp; else mDefaultApplicationIndex = 0; return succeeded; } void ApplicationList::saveSettings() const { QSettings settings; QStringList names; QStringList paths; QStringList params; for (int i = 0; i < getApplicationCount(); i++) { const Application& app = getApplication(i); names << app.getName(); paths << app.getPath(); params << app.getParameters(); } settings.setValue(SETTINGS_APPLICATION_NAMES, names); settings.setValue(SETTINGS_APPLICATION_PATHS, paths); settings.setValue(SETTINGS_APPLICATION_PARAMS, params); settings.setValue(SETTINGS_APPLICATION_DEFAULT, mDefaultApplicationIndex); } int ApplicationList::getApplicationCount() const { return mApplications.size(); } Application& ApplicationList::getApplication(const int index) { if (index >= 0 && index < mApplications.size()) { return mApplications[index]; } static Application dummy; // TODO: Throw exception instead? return dummy; } const Application& ApplicationList::getApplication(const int index) const { if (index >= 0 && index < mApplications.size()) { return mApplications[index]; } static const Application dummy; // TODO: Throw exception instead? return dummy; } void ApplicationList::addApplication(const Application &app) { if (app.getName().isEmpty() || app.getPath().isEmpty()) { return; } mApplications << app; } void ApplicationList::removeApplication(const int index) { mApplications.removeAt(index); } void ApplicationList::setDefault(const int index) { if (index < mApplications.size() && index >= 0) { mDefaultApplicationIndex = index; } } void ApplicationList::copy(const ApplicationList *list) { if (!list) { return; } clear(); for (int i = 0; i < list->getApplicationCount(); i++) { const Application& app = list->getApplication(i); addApplication(app); } mDefaultApplicationIndex = list->getDefaultApplication(); } void ApplicationList::clear() { mApplications.clear(); mDefaultApplicationIndex = -1; } bool ApplicationList::checkAndAddApplication(const QString& appPath, const QString& name, const QString& parameters) { if (QFileInfo(appPath).exists() && QFileInfo(appPath).isExecutable()) { Application app; app.setName(name); app.setPath("\"" + appPath + "\""); app.setParameters(parameters); addApplication(app); return true; } return false; } #ifdef _WIN32 bool ApplicationList::findDefaultWindowsEditor() { bool foundOne = false; #ifdef WIN64 // As long as we do support 32-bit XP, we cannot be sure that the environment variable "ProgramFiles(x86)" exists const QString appPathx86(getenv("ProgramFiles(x86)")); #else const QString appPathx86(getenv("ProgramFiles")); #endif const QString appPathx64(getenv("ProgramW6432")); const QString windowsPath(getenv("windir")); if (checkAndAddApplication(appPathx86 + "\\Notepad++\\notepad++.exe", "Notepad++", "-n(line) (file)")) foundOne = true; else if (checkAndAddApplication(appPathx64 + "\\Notepad++\\notepad++.exe", "Notepad++", "-n(line) (file)")) foundOne = true; if (checkAndAddApplication(appPathx86 + "\\Notepad2\\Notepad2.exe", "Notepad2", "/g (line) (file)")) foundOne = true; else if (checkAndAddApplication(appPathx64 + "\\Notepad2\\Notepad2.exe", "Notepad2", "/g (line) (file)")) foundOne = true; if (checkAndAddApplication(windowsPath + "\\system32\\notepad.exe", "Notepad", "(file)")) foundOne = true; QString regPath = "HKEY_CLASSES_ROOT\\Applications\\QtProject.QtCreator.pro\\shell\\Open\\command"; QSettings registry(regPath, QSettings::NativeFormat); QString qtCreatorRegistry = registry.value("Default", QString()).toString(); QString qtCreatorPath = qtCreatorRegistry.left(qtCreatorRegistry.indexOf(".exe") + 4); if (!qtCreatorRegistry.isEmpty() && checkAndAddApplication(qtCreatorPath, "Qt Creator", "-client (file):(line)")) { foundOne = true; } const QString regPathUEdit32 = "HKEY_CLASSES_ROOT\\Applications\\Uedit32.exe\\shell\\open\\Command"; const QSettings registryUEdit32(regPathUEdit32, QSettings::NativeFormat); const QString uedit32Registry = registryUEdit32.value("Default", QString()).toString(); if (!uedit32Registry.isEmpty()) { // Extract path to executable and make sure there is no single quotation mark at the beginning const QString uedit32Path = uedit32Registry.left(uedit32Registry.indexOf(".exe") + 4).replace("\"", ""); if (checkAndAddApplication(uedit32Path, "UltraEdit 32", "(file)/(line)")) { foundOne = true; } } const QString regPathUEdit64 = "HKEY_CLASSES_ROOT\\Applications\\uedit64.exe\\shell\\open\\Command"; const QSettings registryUEdit64(regPathUEdit64, QSettings::NativeFormat); const QString uedit64Registry = registryUEdit64.value("Default", QString()).toString(); if (!uedit64Registry.isEmpty()) { // Extract path to executable and make sure there is no single quotation mark at the beginning const QString uedit64Path = uedit64Registry.left(uedit64Registry.indexOf(".exe") + 4).replace("\"", ""); if (checkAndAddApplication(uedit64Path, "UltraEdit 64", "(file)/(line)")) { foundOne = true; } } const QString regPathMSVSCode = "HKEY_CLASSES_ROOT\\Applications\\Code.exe\\shell\\open\\command"; const QSettings registryMSVSCode(regPathMSVSCode, QSettings::NativeFormat); const QString msvscodeRegistry = registryMSVSCode.value("Default", QString()).toString(); if (!msvscodeRegistry.isEmpty()) { const QString msvscodePath = msvscodeRegistry.left(msvscodeRegistry.indexOf(".exe") + 4).replace("\"", ""); if (checkAndAddApplication(msvscodePath, "Microsoft VS Code", "-g (file):(line)")) { foundOne = true; } } return foundOne; } #endif cppcheck-2.7/gui/applicationlist.h000066400000000000000000000065641417746362400173270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APPLICATIONLIST_H #define APPLICATIONLIST_H #include "application.h" #include /// @addtogroup GUI /// @{ /** * @brief List of applications user has specified to open errors with. */ class ApplicationList : public QObject { Q_OBJECT public: explicit ApplicationList(QObject *parent = nullptr); virtual ~ApplicationList(); /** * @brief Load all applications * * @return true if loading succeeded, false if there is problem with * application list. Most probably because of older version settings need * to be upgraded. */ bool loadSettings(); /** * @brief Save all applications */ void saveSettings() const; /** * @brief Get the amount of applications in the list * @return The count of applications */ int getApplicationCount() const; /** * @brief Get specific application's name * * @param index Index of the application whose name to get * @return Name of the application */ const Application& getApplication(const int index) const; Application& getApplication(const int index); /** * @brief Return the default application. * @return Index of the default application. */ int getDefaultApplication() const { return mDefaultApplicationIndex; } /** * @brief Add a new application * * @param app Application to add. */ void addApplication(const Application &app); /** * @brief Remove an application from the list * * @param index Index of the application to remove. */ void removeApplication(const int index); /** * @brief Set application as default application. * @param index Index of the application to make the default one */ void setDefault(const int index); /** * @brief Remove all applications from this list and copy all applications from * list given as a parameter. * @param list Copying source */ void copy(const ApplicationList *list); protected: /** * @brief Clear the list * */ void clear(); #ifdef _WIN32 /** * @brief Find editor used by default in Windows. * Check if Notepad++ is installed and use it. If not, use Notepad. */ bool findDefaultWindowsEditor(); #endif private: bool checkAndAddApplication(const QString& appPath, const QString& name, const QString& parameters); /** * @brief List of applications * */ QList mApplications; /** * @brief Index of the default application. * */ int mDefaultApplicationIndex; }; /// @} #endif // APPLICATIONLIST_H cppcheck-2.7/gui/checkstatistics.cpp000066400000000000000000000061671417746362400176520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkstatistics.h" #include CheckStatistics::CheckStatistics(QObject *parent) : QObject(parent) { clear(); } static void addItem(QMap &m, const QString &key) { if (m.contains(key)) m[key]++; else m[key] = 0; } void CheckStatistics::addItem(const QString &tool, ShowTypes::ShowType type) { const QString lower = tool.toLower(); switch (type) { case ShowTypes::ShowStyle: ::addItem(mStyle, tool); break; case ShowTypes::ShowWarnings: ::addItem(mWarning, tool); break; case ShowTypes::ShowPerformance: ::addItem(mPerformance, tool); break; case ShowTypes::ShowPortability: ::addItem(mPortability, tool); break; case ShowTypes::ShowErrors: ::addItem(mError, tool); break; case ShowTypes::ShowInformation: ::addItem(mInformation, tool); break; case ShowTypes::ShowNone: default: qDebug() << "Unknown error type - not added to statistics."; break; } } void CheckStatistics::clear() { mStyle.clear(); mWarning.clear(); mPerformance.clear(); mPortability.clear(); mInformation.clear(); mError.clear(); } unsigned CheckStatistics::getCount(const QString &tool, ShowTypes::ShowType type) const { const QString lower = tool.toLower(); switch (type) { case ShowTypes::ShowStyle: return mStyle.value(lower,0); case ShowTypes::ShowWarnings: return mWarning.value(lower,0); case ShowTypes::ShowPerformance: return mPerformance.value(lower,0); case ShowTypes::ShowPortability: return mPortability.value(lower,0); case ShowTypes::ShowErrors: return mError.value(lower,0); case ShowTypes::ShowInformation: return mInformation.value(lower,0); case ShowTypes::ShowNone: default: qDebug() << "Unknown error type - returning zero statistics."; return 0; } } QStringList CheckStatistics::getTools() const { QSet ret; for (const QString& tool: mStyle.keys()) ret.insert(tool); for (const QString& tool: mWarning.keys()) ret.insert(tool); for (const QString& tool: mPerformance.keys()) ret.insert(tool); for (const QString& tool: mPortability.keys()) ret.insert(tool); for (const QString& tool: mError.keys()) ret.insert(tool); return QStringList(ret.values()); } cppcheck-2.7/gui/checkstatistics.h000066400000000000000000000036751417746362400173200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CHECKSTATISTICS_H #define CHECKSTATISTICS_H #include "showtypes.h" #include #include #include /// @addtogroup GUI /// @{ /** * A class for check statistics. */ class CheckStatistics : public QObject { public: explicit CheckStatistics(QObject *parent = nullptr); /** * @brief Add new checked item to statistics. * * @param tool Tool. * @param type Type of the item to add. */ void addItem(const QString &tool, ShowTypes::ShowType type); /** * @brief Clear the statistics. * */ void clear(); /** * @brief Return statistics for given type. * * @param tool Tool. * @param type Type for which the statistics are returned. * @return Number of items of given type. */ unsigned getCount(const QString &tool, ShowTypes::ShowType type) const; /** Get tools with results */ QStringList getTools() const; private: QMap mStyle; QMap mWarning; QMap mPerformance; QMap mPortability; QMap mInformation; QMap mError; }; /// @} #endif // CHECKSTATISTICS_H cppcheck-2.7/gui/checkthread.cpp000066400000000000000000000366321417746362400167270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkthread.h" #include "common.h" #include "cppcheck.h" #include "erroritem.h" #include "threadresult.h" #include #include #include #include #include static bool executeCommand(std::string exe, std::vector args, std::string redirect, std::string *output) { output->clear(); QStringList args2; for (std::string arg: args) args2 << QString::fromStdString(arg); QProcess process; process.start(QString::fromStdString(exe), args2); process.waitForFinished(); if (redirect == "2>&1") { QString s1 = process.readAllStandardOutput(); QString s2 = process.readAllStandardError(); *output = (s1 + "\n" + s2).toStdString(); } else *output = process.readAllStandardOutput().toStdString(); if (redirect.compare(0,3,"2> ") == 0) { std::ofstream fout(redirect.substr(3)); fout << process.readAllStandardError().toStdString(); } return process.exitCode() == 0; } CheckThread::CheckThread(ThreadResult &result) : mState(Ready), mResult(result), mCppcheck(result, true, executeCommand), mAnalyseWholeProgram(false) { //ctor } CheckThread::~CheckThread() { //dtor } void CheckThread::check(const Settings &settings) { mFiles.clear(); mCppcheck.settings() = settings; start(); } void CheckThread::analyseWholeProgram(const QStringList &files) { mFiles = files; mAnalyseWholeProgram = true; start(); } // cppcheck-suppress unusedFunction - TODO: false positive void CheckThread::run() { mState = Running; if (!mFiles.isEmpty() || mAnalyseWholeProgram) { mAnalyseWholeProgram = false; qDebug() << "Whole program analysis"; const std::string &buildDir = mCppcheck.settings().buildDir; if (!buildDir.empty()) { std::map files2; for (const QString& file : mFiles) files2[file.toStdString()] = 0; mCppcheck.analyseWholeProgram(buildDir, files2); } mFiles.clear(); emit done(); return; } QString file = mResult.getNextFile(); while (!file.isEmpty() && mState == Running) { qDebug() << "Checking file" << file; mCppcheck.check(file.toStdString()); runAddonsAndTools(nullptr, file); emit fileChecked(file); if (mState == Running) file = mResult.getNextFile(); } ImportProject::FileSettings fileSettings = mResult.getNextFileSettings(); while (!fileSettings.filename.empty() && mState == Running) { file = QString::fromStdString(fileSettings.filename); qDebug() << "Checking file" << file; mCppcheck.check(fileSettings); runAddonsAndTools(&fileSettings, QString::fromStdString(fileSettings.filename)); emit fileChecked(file); if (mState == Running) fileSettings = mResult.getNextFileSettings(); } if (mState == Running) mState = Ready; else mState = Stopped; emit done(); } void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSettings, const QString &fileName) { foreach (const QString addon, mAddonsAndTools) { if (addon == CLANG_ANALYZER || addon == CLANG_TIDY) { if (!fileSettings) continue; if (!fileSettings->cfg.empty() && fileSettings->cfg.compare(0,5,"Debug") != 0) continue; QStringList args; for (std::list::const_iterator incIt = fileSettings->includePaths.begin(); incIt != fileSettings->includePaths.end(); ++incIt) args << ("-I" + QString::fromStdString(*incIt)); for (std::list::const_iterator i = fileSettings->systemIncludePaths.begin(); i != fileSettings->systemIncludePaths.end(); ++i) args << "-isystem" << QString::fromStdString(*i); foreach (QString def, QString::fromStdString(fileSettings->defines).split(";")) { args << ("-D" + def); } foreach (const std::string& U, fileSettings->undefs) { args << QString::fromStdString("-U" + U); } const QString clangPath = CheckThread::clangTidyCmd(); if (!clangPath.isEmpty()) { QDir dir(clangPath + "/../lib/clang"); foreach (QString ver, dir.entryList()) { QString includePath = dir.absolutePath() + '/' + ver + "/include"; if (ver[0] != '.' && QDir(includePath).exists()) { args << "-isystem" << includePath; break; } } } #ifdef Q_OS_WIN // To create compile_commands.json in windows see: // https://bitsmaker.gitlab.io/post/clang-tidy-from-vs2015/ foreach (QString includePath, mClangIncludePaths) { if (!includePath.isEmpty()) { includePath.replace("\\", "/"); args << "-isystem" << includePath.trimmed(); } } args << "-U__STDC__" << "-fno-ms-compatibility"; #endif if (!fileSettings->standard.empty()) args << ("-std=" + QString::fromStdString(fileSettings->standard)); else { switch (mCppcheck.settings().standards.cpp) { case Standards::CPP03: args << "-std=c++03"; break; case Standards::CPP11: args << "-std=c++11"; break; case Standards::CPP14: args << "-std=c++14"; break; case Standards::CPP17: args << "-std=c++17"; break; case Standards::CPP20: args << "-std=c++20"; break; } } QString analyzerInfoFile; const std::string &buildDir = mCppcheck.settings().buildDir; if (!buildDir.empty()) { analyzerInfoFile = QString::fromStdString(AnalyzerInformation::getAnalyzerInfoFile(buildDir, fileSettings->filename, fileSettings->cfg)); QStringList args2(args); args2.insert(0,"-E"); args2 << fileName; QProcess process; process.start(clangCmd(),args2); process.waitForFinished(); const QByteArray &ba = process.readAllStandardOutput(); const quint16 chksum = qChecksum(ba.data(), ba.length()); QFile f1(analyzerInfoFile + '.' + addon + "-E"); if (f1.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in1(&f1); const quint16 oldchksum = in1.readAll().toInt(); if (oldchksum == chksum) { QFile f2(analyzerInfoFile + '.' + addon + "-results"); if (f2.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in2(&f2); parseClangErrors(addon, fileName, in2.readAll()); continue; } } f1.close(); } f1.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out1(&f1); out1 << chksum; QFile::remove(analyzerInfoFile + '.' + addon + "-results"); } if (addon == CLANG_ANALYZER) { /* // Using clang args.insert(0,"--analyze"); args.insert(1, "-Xanalyzer"); args.insert(2, "-analyzer-output=text"); args << fileName; */ // Using clang-tidy args.insert(0,"-checks=-*,clang-analyzer-*"); args.insert(1, fileName); args.insert(2, "--"); } else { args.insert(0,"-checks=*,-clang-analyzer-*,-llvm*"); args.insert(1, fileName); args.insert(2, "--"); } { const QString cmd(clangTidyCmd()); QString debug(cmd.contains(" ") ? ('\"' + cmd + '\"') : cmd); foreach (QString arg, args) { if (arg.contains(" ")) debug += " \"" + arg + '\"'; else debug += ' ' + arg; } qDebug() << debug; if (!analyzerInfoFile.isEmpty()) { QFile f(analyzerInfoFile + '.' + addon + "-cmd"); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); out << debug; } } } QProcess process; process.start(clangTidyCmd(), args); process.waitForFinished(600*1000); const QString errout(process.readAllStandardOutput() + "\n\n\n" + process.readAllStandardError()); if (!analyzerInfoFile.isEmpty()) { QFile f(analyzerInfoFile + '.' + addon + "-results"); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); out << errout; } } parseClangErrors(addon, fileName, errout); } } } void CheckThread::stop() { mState = Stopping; Settings::terminate(); } void CheckThread::parseClangErrors(const QString &tool, const QString &file0, QString err) { QList errorItems; ErrorItem errorItem; QRegExp r1("(.+):([0-9]+):([0-9]+): (note|warning|error|fatal error): (.*)"); QRegExp r2("(.*)\\[([a-zA-Z0-9\\-_\\.]+)\\]"); QTextStream in(&err, QIODevice::ReadOnly); while (!in.atEnd()) { QString line = in.readLine(); if (line.startsWith("Assertion failed:")) { ErrorItem e; e.errorPath.append(QErrorPathItem()); e.errorPath.last().file = file0; e.errorPath.last().line = 1; e.errorPath.last().column = 1; e.errorId = tool + "-internal-error"; e.file0 = file0; e.message = line; e.severity = Severity::information; errorItems.append(e); continue; } if (!r1.exactMatch(line)) continue; if (r1.cap(4) != "note") { errorItems.append(errorItem); errorItem = ErrorItem(); errorItem.file0 = r1.cap(1); } errorItem.errorPath.append(QErrorPathItem()); errorItem.errorPath.last().file = r1.cap(1); errorItem.errorPath.last().line = r1.cap(2).toInt(); errorItem.errorPath.last().column = r1.cap(3).toInt(); if (r1.cap(4) == "warning") errorItem.severity = Severity::SeverityType::warning; else if (r1.cap(4) == "error" || r1.cap(4) == "fatal error") errorItem.severity = Severity::SeverityType::error; QString message,id; if (r2.exactMatch(r1.cap(5))) { message = r2.cap(1); const QString id1(r2.cap(2)); if (id1.startsWith("clang")) id = id1; else id = tool + '-' + r2.cap(2); if (tool == CLANG_TIDY) { if (id1.startsWith("performance")) errorItem.severity = Severity::SeverityType::performance; else if (id1.startsWith("portability")) errorItem.severity = Severity::SeverityType::portability; else if (id1.startsWith("cert") || (id1.startsWith("misc") && !id1.contains("unused"))) errorItem.severity = Severity::SeverityType::warning; else errorItem.severity = Severity::SeverityType::style; } } else { message = r1.cap(5); id = CLANG_ANALYZER; } if (errorItem.errorPath.size() == 1) { errorItem.message = message; errorItem.errorId = id; } errorItem.errorPath.last().info = message; } errorItems.append(errorItem); foreach (const ErrorItem &e, errorItems) { if (e.errorPath.isEmpty()) continue; Suppressions::ErrorMessage errorMessage; errorMessage.setFileName(e.errorPath.back().file.toStdString()); errorMessage.lineNumber = e.errorPath.back().line; errorMessage.errorId = e.errorId.toStdString(); errorMessage.symbolNames = e.symbolNames.toStdString(); if (isSuppressed(errorMessage)) continue; std::list callstack; foreach (const QErrorPathItem &path, e.errorPath) { callstack.push_back(ErrorMessage::FileLocation(path.file.toStdString(), path.info.toStdString(), path.line, path.column)); } const std::string f0 = file0.toStdString(); const std::string msg = e.message.toStdString(); const std::string id = e.errorId.toStdString(); ErrorMessage errmsg(callstack, f0, e.severity, msg, id, Certainty::normal); mResult.reportErr(errmsg); } } bool CheckThread::isSuppressed(const Suppressions::ErrorMessage &errorMessage) const { foreach (const Suppressions::Suppression &suppression, mSuppressions) { if (suppression.isSuppressed(errorMessage)) return true; } return false; } QString CheckThread::clangCmd() { QString path = QSettings().value(SETTINGS_CLANG_PATH,QString()).toString(); if (!path.isEmpty()) path += '/'; path += "clang"; #ifdef Q_OS_WIN path += ".exe"; #endif QProcess process; process.start(path, QStringList() << "--version"); process.waitForFinished(); if (process.exitCode() == 0) return path; #ifdef Q_OS_WIN // Try to autodetect clang if (QFileInfo("C:/Program Files/LLVM/bin/clang.exe").exists()) return "C:/Program Files/LLVM/bin/clang.exe"; #endif return QString(); } QString CheckThread::clangTidyCmd() { QString path = QSettings().value(SETTINGS_CLANG_PATH,QString()).toString(); if (!path.isEmpty()) path += '/'; path += "clang-tidy"; #ifdef Q_OS_WIN path += ".exe"; #endif QProcess process; process.start(path, QStringList() << "--version"); process.waitForFinished(); if (process.exitCode() == 0) return path; #ifdef Q_OS_WIN // Try to autodetect clang-tidy if (QFileInfo("C:/Program Files/LLVM/bin/clang-tidy.exe").exists()) return "C:/Program Files/LLVM/bin/clang-tidy.exe"; #endif return QString(); } cppcheck-2.7/gui/checkthread.h000066400000000000000000000070651417746362400163720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CHECKTHREAD_H #define CHECKTHREAD_H #include "cppcheck.h" #include "suppressions.h" #include class Settings; class ThreadResult; /// @addtogroup GUI /// @{ /** * @brief Thread to run cppcheck * */ class CheckThread : public QThread { Q_OBJECT public: explicit CheckThread(ThreadResult &result); virtual ~CheckThread(); /** * @brief Set settings for cppcheck * * @param settings settings for cppcheck */ void check(const Settings &settings); /** * @brief Run whole program analysis * @param files All files */ void analyseWholeProgram(const QStringList &files); void setAddonsAndTools(const QStringList &addonsAndTools) { mAddonsAndTools = addonsAndTools; } void setDataDir(const QString &dataDir) { mDataDir = dataDir; } void setClangIncludePaths(const QStringList &s) { mClangIncludePaths = s; } void setSuppressions(const QList &s) { mSuppressions = s; } /** * @brief method that is run in a thread * */ void run() override; void stop(); /** * Determine command to run clang * \return Command to run clang, empty if it is not found */ static QString clangCmd(); /** * Determine command to run clang-tidy * \return Command to run clang-tidy, empty if it is not found */ static QString clangTidyCmd(); signals: /** * @brief cpp checking is done * */ void done(); void fileChecked(const QString &file); protected: /** * @brief States for the check thread. * Whole purpose of these states is to allow stopping of the checking. When * stopping we say for the thread (Stopping) that stop when current check * has been completed. Thread must be stopped cleanly, just terminating thread * likely causes unpredictable side-effects. */ enum State { Running, /**< The thread is checking. */ Stopping, /**< The thread will stop after current work. */ Stopped, /**< The thread has been stopped. */ Ready, /**< The thread is ready. */ }; /** * @brief Thread's current execution state. */ State mState; ThreadResult &mResult; /** * @brief Cppcheck itself */ CppCheck mCppcheck; private: void runAddonsAndTools(const ImportProject::FileSettings *fileSettings, const QString &fileName); void parseClangErrors(const QString &tool, const QString &file0, QString err); bool isSuppressed(const Suppressions::ErrorMessage &errorMessage) const; QStringList mFiles; bool mAnalyseWholeProgram; QStringList mAddonsAndTools; QString mDataDir; QStringList mClangIncludePaths; QList mSuppressions; }; /// @} #endif // CHECKTHREAD_H cppcheck-2.7/gui/codeeditor.cpp000066400000000000000000000335721417746362400166030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "codeeditor.h" #include "codeeditorstyle.h" #include #include Highlighter::Highlighter(QTextDocument *parent, CodeEditorStyle *widgetStyle) : QSyntaxHighlighter(parent), mWidgetStyle(widgetStyle) { HighlightingRule rule; mKeywordFormat.setForeground(mWidgetStyle->keywordColor); mKeywordFormat.setFontWeight(mWidgetStyle->keywordWeight); QStringList keywordPatterns; keywordPatterns << "alignas" << "alignof" << "asm" << "auto" << "bool" << "break" << "case" << "catch" << "char" << "char8_­t" << "char16_­t" << "char32_­t" << "class" << "concept" << "const" << "consteval" << "constexpr" << "constinit" << "const_­cast" << "continue" << "co_­await" << "co_­return" << "co_­yield" << "decltype" << "default" << "delete" << "do" << "double" << "dynamic_­cast" << "else" << "enum" << "explicit" << "export" << "extern" << "false" << "float" << "for" << "friend" << "goto" << "if" << "inline" << "int" << "long" << "mutable" << "namespace" << "new" << "noexcept" << "nullptr" << "operator" << "private" << "protected" << "public" << "reinterpret_­cast" << "requires" << "return" << "short" << "signed" << "static" << "static_­assert" << "static_­cast" << "struct" << "switch" << "template" << "this" << "thread_­local" << "throw" << "true" << "try" << "typedef" << "typeid" << "typename" << "union" << "unsigned" << "virtual" << "void" << "volatile" << "wchar_­t" << "while"; foreach (const QString &pattern, keywordPatterns) { rule.pattern = QRegularExpression("\\b" + pattern + "\\b"); rule.format = mKeywordFormat; rule.ruleRole = RuleRole::Keyword; mHighlightingRules.append(rule); } mClassFormat.setForeground(mWidgetStyle->classColor); mClassFormat.setFontWeight(mWidgetStyle->classWeight); rule.pattern = QRegularExpression("\\bQ[A-Za-z]+\\b"); rule.format = mClassFormat; rule.ruleRole = RuleRole::Class; mHighlightingRules.append(rule); mQuotationFormat.setForeground(mWidgetStyle->quoteColor); mQuotationFormat.setFontWeight(mWidgetStyle->quoteWeight); rule.pattern = QRegularExpression("\".*\""); rule.format = mQuotationFormat; rule.ruleRole = RuleRole::Quote; mHighlightingRules.append(rule); mSingleLineCommentFormat.setForeground(mWidgetStyle->commentColor); mSingleLineCommentFormat.setFontWeight(mWidgetStyle->commentWeight); rule.pattern = QRegularExpression("//[^\n]*"); rule.format = mSingleLineCommentFormat; rule.ruleRole = RuleRole::Comment; mHighlightingRules.append(rule); mHighlightingRulesWithSymbols = mHighlightingRules; mMultiLineCommentFormat.setForeground(mWidgetStyle->commentColor); mMultiLineCommentFormat.setFontWeight(mWidgetStyle->commentWeight); mSymbolFormat.setForeground(mWidgetStyle->symbolFGColor); mSymbolFormat.setBackground(mWidgetStyle->symbolBGColor); mSymbolFormat.setFontWeight(mWidgetStyle->symbolWeight); mCommentStartExpression = QRegularExpression("/\\*"); mCommentEndExpression = QRegularExpression("\\*/"); } void Highlighter::setSymbols(const QStringList &symbols) { mHighlightingRulesWithSymbols = mHighlightingRules; foreach (const QString &sym, symbols) { HighlightingRule rule; rule.pattern = QRegularExpression("\\b" + sym + "\\b"); rule.format = mSymbolFormat; rule.ruleRole = RuleRole::Symbol; mHighlightingRulesWithSymbols.append(rule); } } void Highlighter::setStyle(const CodeEditorStyle &newStyle) { mKeywordFormat.setForeground(newStyle.keywordColor); mKeywordFormat.setFontWeight(newStyle.keywordWeight); mClassFormat.setForeground(newStyle.classColor); mClassFormat.setFontWeight(newStyle.classWeight); mSingleLineCommentFormat.setForeground(newStyle.commentColor); mSingleLineCommentFormat.setFontWeight(newStyle.commentWeight); mMultiLineCommentFormat.setForeground(newStyle.commentColor); mMultiLineCommentFormat.setFontWeight(newStyle.commentWeight); mQuotationFormat.setForeground(newStyle.quoteColor); mQuotationFormat.setFontWeight(newStyle.quoteWeight); mSymbolFormat.setForeground(newStyle.symbolFGColor); mSymbolFormat.setBackground(newStyle.symbolBGColor); mSymbolFormat.setFontWeight(newStyle.symbolWeight); for (HighlightingRule& rule : mHighlightingRules) { applyFormat(rule); } for (HighlightingRule& rule : mHighlightingRulesWithSymbols) { applyFormat(rule); } } void Highlighter::highlightBlock(const QString &text) { foreach (const HighlightingRule &rule, mHighlightingRulesWithSymbols) { QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); while (matchIterator.hasNext()) { QRegularExpressionMatch match = matchIterator.next(); setFormat(match.capturedStart(), match.capturedLength(), rule.format); } } setCurrentBlockState(0); int startIndex = 0; if (previousBlockState() != 1) startIndex = text.indexOf(mCommentStartExpression); while (startIndex >= 0) { QRegularExpressionMatch match = mCommentEndExpression.match(text, startIndex); int endIndex = match.capturedStart(); int commentLength = 0; if (endIndex == -1) { setCurrentBlockState(1); commentLength = text.length() - startIndex; } else { commentLength = endIndex - startIndex + match.capturedLength(); } setFormat(startIndex, commentLength, mMultiLineCommentFormat); startIndex = text.indexOf(mCommentStartExpression, startIndex + commentLength); } } void Highlighter::applyFormat(HighlightingRule &rule) { switch (rule.ruleRole) { case RuleRole::Keyword: rule.format = mKeywordFormat; break; case RuleRole::Class: rule.format = mClassFormat; break; case RuleRole::Comment: rule.format = mSingleLineCommentFormat; break; case RuleRole::Quote: rule.format = mQuotationFormat; break; case RuleRole::Symbol: rule.format = mSymbolFormat; break; } } CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent), mWidgetStyle(new CodeEditorStyle(defaultStyleLight)) { mLineNumberArea = new LineNumberArea(this); mHighlighter = new Highlighter(document(), mWidgetStyle); mErrorPosition = -1; QFont font("Monospace"); font.setStyleHint(QFont::TypeWriter); setFont(font); mLineNumberArea->setFont(font); // set widget coloring by overriding widget style sheet setObjectName("CodeEditor"); setStyleSheet(generateStyleString()); QShortcut *copyText = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_C),this); QShortcut *allText = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_A),this); connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); connect(copyText, SIGNAL(activated()), this, SLOT(copy())); connect(allText, SIGNAL(activated()), this, SLOT(selectAll())); updateLineNumberAreaWidth(0); } CodeEditor::~CodeEditor() { // NOTE: not a Qt Object - delete manually delete mWidgetStyle; } static int getPos(const QString &fileData, int lineNumber) { if (lineNumber <= 1) return 0; for (int pos = 0, line = 1; pos < fileData.size(); ++pos) { if (fileData[pos] != '\n') continue; ++line; if (line >= lineNumber) return pos + 1; } return fileData.size(); } void CodeEditor::setStyle(const CodeEditorStyle& newStyle) { *mWidgetStyle = newStyle; // apply new styling setStyleSheet(generateStyleString()); mHighlighter->setStyle(newStyle); mHighlighter->rehighlight(); highlightErrorLine(); } void CodeEditor::setError(const QString &code, int errorLine, const QStringList &symbols) { mHighlighter->setSymbols(symbols); setPlainText(code); mErrorPosition = getPos(code, errorLine); QTextCursor tc = textCursor(); tc.setPosition(mErrorPosition); setTextCursor(tc); centerCursor(); highlightErrorLine(); } void CodeEditor::setError(int errorLine, const QStringList &symbols) { mHighlighter->setSymbols(symbols); mErrorPosition = getPos(toPlainText(), errorLine); QTextCursor tc = textCursor(); tc.setPosition(mErrorPosition); setTextCursor(tc); centerCursor(); highlightErrorLine(); } int CodeEditor::lineNumberAreaWidth() { int digits = 1; int max = qMax(1, blockCount()); while (max >= 10) { max /= 10; ++digits; } #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; #else int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; #endif return space; } void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */) { setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); } void CodeEditor::updateLineNumberArea(const QRect &rect, int dy) { if (dy) mLineNumberArea->scroll(0, dy); else mLineNumberArea->update(0, rect.y(), mLineNumberArea->width(), rect.height()); if (rect.contains(viewport()->rect())) updateLineNumberAreaWidth(0); } void CodeEditor::resizeEvent(QResizeEvent *event) { QPlainTextEdit::resizeEvent(event); QRect cr = contentsRect(); mLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); } void CodeEditor::highlightErrorLine() { QList extraSelections; QTextEdit::ExtraSelection selection; selection.format.setBackground(mWidgetStyle->highlightBGColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = QTextCursor(document()); if (mErrorPosition >= 0) { selection.cursor.setPosition(mErrorPosition); } else { selection.cursor.setPosition(0); } selection.cursor.clearSelection(); extraSelections.append(selection); setExtraSelections(extraSelections); } void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) { QPainter painter(mLineNumberArea); painter.fillRect(event->rect(), mWidgetStyle->lineNumBGColor); QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); int bottom = top + (int) blockBoundingRect(block).height(); while (block.isValid() && top <= event->rect().bottom()) { if (block.isVisible() && bottom >= event->rect().top()) { QString number = QString::number(blockNumber + 1); painter.setPen(mWidgetStyle->lineNumFGColor); painter.drawText(0, top, mLineNumberArea->width(), fontMetrics().height(), Qt::AlignRight, number); } block = block.next(); top = bottom; bottom = top + (int) blockBoundingRect(block).height(); ++blockNumber; } } QString CodeEditor::generateStyleString() { QString bgcolor = QString("background:rgb(%1,%2,%3);") .arg(mWidgetStyle->widgetBGColor.red()) .arg(mWidgetStyle->widgetBGColor.green()) .arg(mWidgetStyle->widgetBGColor.blue()); QString fgcolor = QString("color:rgb(%1,%2,%3);") .arg(mWidgetStyle->widgetFGColor.red()) .arg(mWidgetStyle->widgetFGColor.green()) .arg(mWidgetStyle->widgetFGColor.blue()); QString style = QString("%1 %2") .arg(bgcolor) .arg(fgcolor); return style; } cppcheck-2.7/gui/codeeditor.h000066400000000000000000000100231417746362400162320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CODEEDITOR_H #define CODEEDITOR_H #include #include #include class CodeEditorStyle; class QPaintEvent; class QResizeEvent; class Highlighter : public QSyntaxHighlighter { Q_OBJECT public: explicit Highlighter(QTextDocument *parent, CodeEditorStyle *widgetStyle); void setSymbols(const QStringList &symbols); void setStyle(const CodeEditorStyle &newStyle); protected: void highlightBlock(const QString &text) override; private: enum RuleRole { Keyword = 1, Class = 2, Comment = 3, Quote = 4, Symbol = 5 }; struct HighlightingRule { QRegularExpression pattern; QTextCharFormat format; RuleRole ruleRole; }; void applyFormat(HighlightingRule &rule); QVector mHighlightingRules; QVector mHighlightingRulesWithSymbols; QRegularExpression mCommentStartExpression; QRegularExpression mCommentEndExpression; QTextCharFormat mKeywordFormat; QTextCharFormat mClassFormat; QTextCharFormat mSingleLineCommentFormat; QTextCharFormat mMultiLineCommentFormat; QTextCharFormat mQuotationFormat; QTextCharFormat mSymbolFormat; CodeEditorStyle *mWidgetStyle; }; class CodeEditor : public QPlainTextEdit { Q_OBJECT public: explicit CodeEditor(QWidget *parent); CodeEditor(const CodeEditor &) = delete; CodeEditor &operator=(const CodeEditor &) = delete; ~CodeEditor(); void lineNumberAreaPaintEvent(QPaintEvent *event); int lineNumberAreaWidth(); void setStyle(const CodeEditorStyle& newStyle); /** * Set source code to show, goto error line and highlight that line. * \param code The source code. * \param errorLine line number * \param symbols the related symbols, these are marked */ void setError(const QString &code, int errorLine, const QStringList &symbols); /** * Goto another error in existing source file * \param errorLine line number * \param symbols the related symbols, these are marked */ void setError(int errorLine, const QStringList &symbols); void setFileName(const QString &fileName) { mFileName = fileName; } QString getFileName() const { return mFileName; } void clear() { mFileName.clear(); setPlainText(QString()); } protected: void resizeEvent(QResizeEvent *event) override; private slots: void updateLineNumberAreaWidth(int newBlockCount); void highlightErrorLine(); void updateLineNumberArea(const QRect &, int); private: QString generateStyleString(); private: QWidget *mLineNumberArea; Highlighter *mHighlighter; CodeEditorStyle *mWidgetStyle; int mErrorPosition; QString mFileName; }; class LineNumberArea : public QWidget { public: explicit LineNumberArea(CodeEditor *editor) : QWidget(editor) { mCodeEditor = editor; } QSize sizeHint() const override { return QSize(mCodeEditor->lineNumberAreaWidth(), 0); } protected: void paintEvent(QPaintEvent *event) override { mCodeEditor->lineNumberAreaPaintEvent(event); } private: CodeEditor *mCodeEditor; }; #endif // CODEEDITOR_H cppcheck-2.7/gui/codeeditorstyle.cpp000066400000000000000000000227121417746362400176560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "codeeditorstyle.h" #include CodeEditorStyle::CodeEditorStyle( const QColor& CtrlFGColor, const QColor& CtrlBGColor, const QColor& HiLiBGColor, const QColor& LnNumFGColor, const QColor& LnNumBGColor, const QColor& KeyWdFGColor, const QFont::Weight& KeyWdWeight, const QColor& ClsFGColor, const QFont::Weight& ClsWeight, const QColor& QteFGColor, const QFont::Weight& QteWeight, const QColor& CmtFGColor, const QFont::Weight& CmtWeight, const QColor& SymbFGColor, const QColor& SymbBGColor, const QFont::Weight& SymbWeight) : mSystemTheme(false), widgetFGColor(CtrlFGColor), widgetBGColor(CtrlBGColor), highlightBGColor(HiLiBGColor), lineNumFGColor(LnNumFGColor), lineNumBGColor(LnNumBGColor), keywordColor(KeyWdFGColor), keywordWeight(KeyWdWeight), classColor(ClsFGColor), classWeight(ClsWeight), quoteColor(QteFGColor), quoteWeight(QteWeight), commentColor(CmtFGColor), commentWeight(CmtWeight), symbolFGColor(SymbFGColor), symbolBGColor(SymbBGColor), symbolWeight(SymbWeight) {} bool CodeEditorStyle::operator==(const CodeEditorStyle& rhs) const { if (mSystemTheme != rhs.mSystemTheme) return false; if (widgetFGColor != rhs.widgetFGColor) return false; if (widgetBGColor != rhs.widgetBGColor) return false; if (highlightBGColor != rhs.highlightBGColor) return false; if (lineNumFGColor != rhs.lineNumFGColor) return false; if (lineNumBGColor != rhs.lineNumBGColor) return false; if (keywordColor != rhs.keywordColor) return false; if (keywordWeight != rhs.keywordWeight) return false; if (classColor != rhs.classColor) return false; if (classWeight != rhs.classWeight) return false; if (quoteColor != rhs.quoteColor) return false; if (quoteWeight != rhs.quoteWeight) return false; if (commentColor != rhs.commentColor) return false; if (commentWeight != rhs.commentWeight) return false; if (symbolFGColor != rhs.symbolFGColor) return false; if (symbolBGColor != rhs.symbolBGColor) return false; if (symbolWeight != rhs.symbolWeight) return false; return true; } bool CodeEditorStyle::operator!=(const CodeEditorStyle& rhs) const { return !(*this == rhs); } CodeEditorStyle CodeEditorStyle::getSystemTheme() { CodeEditorStyle theStyle(defaultStyleLight); theStyle.mSystemTheme = true; return theStyle; } CodeEditorStyle CodeEditorStyle::loadSettings(QSettings *settings) { CodeEditorStyle theStyle(CodeEditorStyle::getSystemTheme()); if (!settings) return theStyle; if (!settings->childGroups().contains(SETTINGS_STYLE_GROUP)) return theStyle; // style section exists - load values settings->beginGroup(SETTINGS_STYLE_GROUP); QString type = settings->value( SETTINGS_STYLE_TYPE, QVariant(SETTINGS_STYLE_TYPE_LIGHT) ).toString(); if (type == SETTINGS_STYLE_TYPE_LIGHT) { settings->endGroup(); return theStyle; } if (type == SETTINGS_STYLE_TYPE_DARK) { theStyle = defaultStyleDark; settings->endGroup(); return theStyle; } if (type == SETTINGS_STYLE_TYPE_CUSTOM) { theStyle.widgetFGColor = settings->value( SETTINGS_STYLE_WIDGETFG, QVariant(defaultStyleLight.widgetFGColor)).value(); theStyle.widgetBGColor = settings->value( SETTINGS_STYLE_WIDGETBG, QVariant(defaultStyleLight.widgetBGColor)).value(); theStyle.highlightBGColor = settings->value( SETTINGS_STYLE_HILIFG, QVariant(defaultStyleLight.highlightBGColor)).value(); theStyle.lineNumFGColor = settings->value( SETTINGS_STYLE_LINENUMFG, QVariant(defaultStyleLight.lineNumFGColor)).value(); theStyle.lineNumBGColor = settings->value( SETTINGS_STYLE_LINENUMBG, QVariant(defaultStyleLight.lineNumBGColor)).value(); theStyle.keywordColor = settings->value( SETTINGS_STYLE_KEYWORDFG, QVariant(defaultStyleLight.keywordColor)).value(); QVariant defKeyWWt(static_cast(defaultStyleLight.keywordWeight)); theStyle.keywordWeight = static_cast( settings->value(SETTINGS_STYLE_KEYWORDWT, defKeyWWt).toInt()); theStyle.classColor = settings->value( SETTINGS_STYLE_CLASSFG, QVariant(defaultStyleLight.classColor)).value(); QVariant defClsWt(static_cast(defaultStyleLight.classWeight)); theStyle.classWeight = static_cast( settings->value(SETTINGS_STYLE_CLASSWT, defClsWt).toInt()); theStyle.quoteColor = settings->value( SETTINGS_STYLE_QUOTEFG, QVariant(defaultStyleLight.quoteColor)).value(); QVariant defQteWt(static_cast(defaultStyleLight.quoteWeight)); theStyle.quoteWeight = static_cast( settings->value(SETTINGS_STYLE_QUOTEWT, defQteWt).toInt()); theStyle.commentColor = settings->value( SETTINGS_STYLE_COMMENTFG, QVariant(defaultStyleLight.commentColor)).value(); QVariant defCmtWt(static_cast(defaultStyleLight.commentWeight)); theStyle.commentWeight = static_cast( settings->value(SETTINGS_STYLE_COMMENTWT, defCmtWt).toInt()); theStyle.symbolFGColor = settings->value( SETTINGS_STYLE_SYMBOLFG, QVariant(defaultStyleLight.symbolFGColor)).value(); theStyle.symbolBGColor = settings->value( SETTINGS_STYLE_SYMBOLBG, QVariant(defaultStyleLight.symbolBGColor)).value(); QVariant defSymWt(static_cast(defaultStyleLight.symbolWeight)); theStyle.symbolWeight = static_cast( settings->value(SETTINGS_STYLE_SYMBOLWT, defSymWt).toInt()); } settings->endGroup(); return theStyle; } void CodeEditorStyle::saveSettings(QSettings *settings, const CodeEditorStyle& theStyle) { if (!settings) return; if (settings->childGroups().contains(SETTINGS_STYLE_GROUP)) { settings->remove(SETTINGS_STYLE_GROUP); if (theStyle.isSystemTheme()) return; } settings->beginGroup(SETTINGS_STYLE_GROUP); bool isDefaultLight = (defaultStyleLight == theStyle); bool isDefaultDark = (defaultStyleDark == theStyle); if (isDefaultLight && !isDefaultDark) { settings->setValue(SETTINGS_STYLE_TYPE, SETTINGS_STYLE_TYPE_LIGHT); } else if (!isDefaultLight && isDefaultDark) { settings->setValue(SETTINGS_STYLE_TYPE, SETTINGS_STYLE_TYPE_DARK); } else { settings->setValue(SETTINGS_STYLE_TYPE, SETTINGS_STYLE_TYPE_CUSTOM); settings->setValue(SETTINGS_STYLE_WIDGETFG, QVariant(theStyle.widgetFGColor)); settings->setValue(SETTINGS_STYLE_WIDGETBG, QVariant(theStyle.widgetBGColor)); settings->setValue(SETTINGS_STYLE_HILIFG, QVariant(theStyle.highlightBGColor)); settings->setValue(SETTINGS_STYLE_LINENUMFG, QVariant(theStyle.lineNumFGColor)); settings->setValue(SETTINGS_STYLE_LINENUMBG, QVariant(theStyle.lineNumBGColor)); settings->setValue(SETTINGS_STYLE_KEYWORDFG, QVariant(theStyle.keywordColor)); settings->setValue(SETTINGS_STYLE_KEYWORDWT, QVariant(static_cast(theStyle.keywordWeight))); settings->setValue(SETTINGS_STYLE_CLASSFG, QVariant(theStyle.classColor)); settings->setValue(SETTINGS_STYLE_CLASSWT, QVariant(static_cast(theStyle.classWeight))); settings->setValue(SETTINGS_STYLE_QUOTEFG, QVariant(theStyle.quoteColor)); settings->setValue(SETTINGS_STYLE_QUOTEWT, QVariant(static_cast(theStyle.quoteWeight))); settings->setValue(SETTINGS_STYLE_COMMENTFG, QVariant(theStyle.commentColor)); settings->setValue(SETTINGS_STYLE_COMMENTWT, QVariant(static_cast(theStyle.commentWeight))); settings->setValue(SETTINGS_STYLE_SYMBOLFG, QVariant(theStyle.symbolFGColor)); settings->setValue(SETTINGS_STYLE_SYMBOLBG, QVariant(theStyle.symbolBGColor)); settings->setValue(SETTINGS_STYLE_SYMBOLWT, QVariant(static_cast(theStyle.symbolWeight))); } settings->endGroup(); } cppcheck-2.7/gui/codeeditorstyle.h000066400000000000000000000112341417746362400173200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CODEEDITORSTYLE_H #define CODEEDITORSTYLE_H #include #include #include const QString SETTINGS_STYLE_GROUP("EditorStyle"); const QString SETTINGS_STYLE_TYPE("StyleType"); const QString SETTINGS_STYLE_TYPE_LIGHT("DefaultLight"); const QString SETTINGS_STYLE_TYPE_DARK("DefaultDark"); const QString SETTINGS_STYLE_TYPE_CUSTOM("Custom"); const QString SETTINGS_STYLE_WIDGETFG("StyleWidgetFG"); const QString SETTINGS_STYLE_WIDGETBG("StyleWidgetBG"); const QString SETTINGS_STYLE_HILIFG("StyleHighlightFG"); const QString SETTINGS_STYLE_LINENUMFG("StyleLineNumFG"); const QString SETTINGS_STYLE_LINENUMBG("StyleLineNumBG"); const QString SETTINGS_STYLE_KEYWORDFG("StyleKeywordFG"); const QString SETTINGS_STYLE_KEYWORDWT("StyleKeywordWeight"); const QString SETTINGS_STYLE_CLASSFG("StyleClassFG"); const QString SETTINGS_STYLE_CLASSWT("StyleClassWeight"); const QString SETTINGS_STYLE_QUOTEFG("StyleQuoteFG"); const QString SETTINGS_STYLE_QUOTEWT("StyleQuoteWeight"); const QString SETTINGS_STYLE_COMMENTFG("StyleCommentFG"); const QString SETTINGS_STYLE_COMMENTWT("StyleCommentWeight"); const QString SETTINGS_STYLE_SYMBOLFG("StyleSymbolFG"); const QString SETTINGS_STYLE_SYMBOLBG("StyleSymbolBG"); const QString SETTINGS_STYLE_SYMBOLWT("StyleSymbolWeight"); class QSettings; class CodeEditorStyle { public: explicit CodeEditorStyle( const QColor& CtrlFGColor, const QColor& CtrlBGColor, const QColor& HiLiBGColor, const QColor& LnNumFGColor, const QColor& LnNumBGColor, const QColor& KeyWdFGColor, const QFont::Weight& KeyWdWeight, const QColor& ClsFGColor, const QFont::Weight& ClsWeight, const QColor& QteFGColor, const QFont::Weight& QteWeight, const QColor& CmtFGColor, const QFont::Weight& CmtWeight, const QColor& SymbFGColor, const QColor& SymbBGColor, const QFont::Weight& SymbWeight); ~CodeEditorStyle() {} bool operator==(const CodeEditorStyle& rhs) const; bool operator!=(const CodeEditorStyle& rhs) const; bool isSystemTheme() const { return mSystemTheme; } static CodeEditorStyle getSystemTheme(); static CodeEditorStyle loadSettings(QSettings *settings); static void saveSettings(QSettings *settings, const CodeEditorStyle& theStyle); public: bool mSystemTheme; QColor widgetFGColor; QColor widgetBGColor; QColor highlightBGColor; QColor lineNumFGColor; QColor lineNumBGColor; QColor keywordColor; QFont::Weight keywordWeight; QColor classColor; QFont::Weight classWeight; QColor quoteColor; QFont::Weight quoteWeight; QColor commentColor; QFont::Weight commentWeight; QColor symbolFGColor; QColor symbolBGColor; QFont::Weight symbolWeight; }; static const CodeEditorStyle defaultStyleLight( /* editor FG/BG */ Qt::black, QColor(240, 240, 240), /* highlight BG */ QColor(255, 220, 220), /* line number FG/BG */ Qt::black, QColor(240, 240, 240), /* keyword FG/Weight */ Qt::darkBlue, QFont::Bold, /* class FG/Weight */ Qt::darkMagenta, QFont::Bold, /* quote FG/Weight */ Qt::darkGreen, QFont::Normal, /* comment FG/Weight */ Qt::gray, QFont::Normal, /* Symbol FG/BG/Weight */ Qt::red, QColor(220, 220, 255), QFont::Normal ); // Styling derived from Eclipse Color Theme - 'RecognEyes' // http://www.eclipsecolorthemes.org/?view=theme&id=30 static const CodeEditorStyle defaultStyleDark( /* editor FG/BG */ QColor(218, 218, 218), QColor(16, 16, 32), /* highlight BG */ QColor(64, 64, 64), /* line number FG/BG */ QColor(43, 145, 175), QColor(16, 16, 32), /* keyword FG/Weight */ QColor(0, 204, 204), QFont::Bold, /* class FG/Weight */ QColor(218, 0, 218), QFont::Bold, /* quote FG/Weight */ QColor(0, 204, 0), QFont::Normal, /* comment FG/Weight */ QColor(180, 180, 180), QFont::Normal, /* Symbol FG/BG/Weight */ QColor(218, 32, 32), QColor(32, 32, 108), QFont::Normal ); #endif /* CODEEDITORSTYLE_H */ cppcheck-2.7/gui/codeeditstylecontrols.cpp000066400000000000000000000070401417746362400210760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "codeeditstylecontrols.h" #include SelectColorButton::SelectColorButton(QWidget* parent) : QPushButton(parent), mColor(QColor(255, 255, 255)) { updateColor(); connect(this, SIGNAL(clicked()), this, SLOT(changeColor())); } void SelectColorButton::updateColor() { QString btnColorStyle = QString( "background-color:rgb(%1,%2,%3);" "border-style:outset;" "border-width: 1px;") .arg(mColor.red()) .arg(mColor.green()) .arg(mColor.blue()); setObjectName("SelectColorButton"); setStyleSheet(btnColorStyle); } void SelectColorButton::changeColor() { QColorDialog pDlg(mColor); pDlg.setModal(true); int nResult = pDlg.exec(); if (nResult == QDialog::Accepted) { setColor(pDlg.selectedColor()); emit colorChanged(mColor); } } void SelectColorButton::setColor(const QColor& color) { mColor = color; updateColor(); } // cppcheck-suppress unusedFunction const QColor& SelectColorButton::getColor() { return mColor; } SelectFontWeightCombo::SelectFontWeightCombo(QWidget* parent) : QComboBox(parent), mWeight(QFont::Normal) { addItem(QObject::tr("Thin"), QVariant(static_cast(QFont::Thin))); addItem(QObject::tr("ExtraLight"), QVariant(static_cast(QFont::ExtraLight))); addItem(QObject::tr("Light"), QVariant(static_cast(QFont::Light))); addItem(QObject::tr("Normal"), QVariant(static_cast(QFont::Normal))); addItem(QObject::tr("Medium"), QVariant(static_cast(QFont::Medium))); addItem(QObject::tr("DemiBold"), QVariant(static_cast(QFont::DemiBold))); addItem(QObject::tr("Bold"), QVariant(static_cast(QFont::Bold))); addItem(QObject::tr("ExtraBold"), QVariant(static_cast(QFont::ExtraBold))); addItem(QObject::tr("Black"), QVariant(static_cast(QFont::Black))); updateWeight(); connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(changeWeight(int))); } void SelectFontWeightCombo::updateWeight() { int nResult = findData(QVariant(static_cast(mWeight))); if (nResult != -1) { setCurrentIndex(nResult); } else { setCurrentIndex(findData(static_cast(QFont::Normal))); } } void SelectFontWeightCombo::changeWeight(int index) { if (index != -1) { setWeight(static_cast(itemData(index).toInt())); emit weightChanged(mWeight); } } void SelectFontWeightCombo::setWeight(const QFont::Weight& weight) { mWeight = weight; updateWeight(); } // cppcheck-suppress unusedFunction const QFont::Weight& SelectFontWeightCombo::getWeight() { return mWeight; } cppcheck-2.7/gui/codeeditstylecontrols.h000066400000000000000000000035371417746362400205520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // widget subclass methodology derived from here: // https://stackoverflow.com/questions/18257281/qt-color-picker-widget/43871405#43871405 #ifndef CODEEDITORSTYLECONTROLS_H #define CODEEDITORSTYLECONTROLS_H #include #include #include #include class SelectColorButton : public QPushButton { Q_OBJECT public: explicit SelectColorButton(QWidget* parent); virtual ~SelectColorButton() {} void setColor(const QColor& color); const QColor& getColor(); signals: void colorChanged(const QColor& newColor); public slots: void updateColor(); void changeColor(); private: QColor mColor; }; class SelectFontWeightCombo : public QComboBox { Q_OBJECT public: explicit SelectFontWeightCombo(QWidget* parent); virtual ~SelectFontWeightCombo() {} void setWeight(const QFont::Weight& weight); const QFont::Weight& getWeight(); signals: void weightChanged(const QFont::Weight& newWeight); public slots: void updateWeight(); void changeWeight(int index); private: QFont::Weight mWeight; }; #endif //CODEEDITORSTYLECONTROLS_H cppcheck-2.7/gui/codeeditstyledialog.cpp000066400000000000000000000307011417746362400204720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "codeeditstyledialog.h" #include "codeeditor.h" #include "codeeditstylecontrols.h" #include #include const QString StyleEditDialog::mSampleDocument( "/*****\n" "* Multiline Comment\n" "*****/\n" "#include \n" "#include \n" "\n" "class fwdClass;\n" "\n" "int main(int argc, char *argv[])\n" "{\n" " QApplication a(argc, argv);\n" " int nLife = 42;\n" " w.show();\n" " // single line comment\n" " // line below is highlighted\n" " fwdClass( nLife );\n" " return a.exec();\n" "}\n" "\n" "void class fwdClass( double dValue ) {\n" " std::cout << \"Ipsum Lorem: \"\n" " << nValue\n" " << std::endl;\n" "}\n"); const QStringList StyleEditDialog::mErrSymbolsList = ( QStringList(QStringList() << "nLife" << "dValue" << "nValue")); const int StyleEditDialog::mErrLineNum = 16; StyleEditDialog::StyleEditDialog(const CodeEditorStyle& newStyle, QWidget *parent /*= nullptr*/) : QDialog(parent), mStyleIncoming(newStyle), mStyleOutgoing(newStyle) { QVBoxLayout *vboxMain = new QVBoxLayout(this); QHBoxLayout *hboxEdit = new QHBoxLayout(); // Color/Weight controls QFormLayout *flEditControls = new QFormLayout(); mBtnWidgetColorFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Editor Foreground Color"), mBtnWidgetColorFG); mBtnWidgetColorBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Editor Background Color"), mBtnWidgetColorBG); mBtnHighlightBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Highlight Background Color"), mBtnHighlightBG); mBtnLineNumFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Line Number Foreground Color"), mBtnLineNumFG); mBtnLineNumBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Line Number Background Color"), mBtnLineNumBG); mBtnKeywordFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Keyword Foreground Color"), mBtnKeywordFG); mCBKeywordWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Keyword Font Weight"), mCBKeywordWeight); mBtnClassFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Class Foreground Color"), mBtnClassFG); mCBClassWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Class Font Weight"), mCBClassWeight); mBtnQuoteFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Quote Foreground Color"), mBtnQuoteFG); mCBQuoteWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Quote Font Weight"), mCBQuoteWeight); mBtnCommentFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Comment Foreground Color"), mBtnCommentFG); mCBCommentWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Comment Font Weight"), mCBCommentWeight); mBtnSymbolFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Symbol Foreground Color"), mBtnSymbolFG); mBtnSymbolBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Symbol Background Color"), mBtnSymbolBG); mCBSymbolWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Symbol Font Weight"), mCBSymbolWeight); hboxEdit->addLayout(flEditControls); // CodeEditor to display Style mSampleEditor = new CodeEditor(this); QFont sampleFont("Monospace"); QFontMetrics fm(sampleFont); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) mSampleEditor->setMinimumWidth(fm.horizontalAdvance(QString(40, 'W'))); #else mSampleEditor->setMinimumWidth(fm.width(QString(40, 'W'))); #endif // designate highlight, errors, and symbols mSampleEditor->setError(mSampleDocument, mErrLineNum, mErrSymbolsList); // End Controls hboxEdit->addWidget(mSampleEditor); vboxMain->addLayout(hboxEdit); // Default Controls QHBoxLayout *hboxDefaultControls = new QHBoxLayout(); mBtnDefaultLight = new QPushButton(QObject::tr("Set to Default Light"), this); mBtnDefaultDark = new QPushButton(QObject::tr("Set to Default Dark"), this); hboxDefaultControls->addStretch(1); hboxDefaultControls->addWidget(mBtnDefaultLight); hboxDefaultControls->addWidget(mBtnDefaultDark); hboxDefaultControls->addStretch(1); vboxMain->addLayout(hboxDefaultControls); vboxMain->addStretch(2); // dialog controls QDialogButtonBox *dBtnBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Ok | QDialogButtonBox::Reset); vboxMain->addStretch(1); vboxMain->addWidget(dBtnBox); // setup values for style controls updateControls(); updateStyle(); connect(dBtnBox, SIGNAL(accepted()), this, SLOT(accept())); connect(dBtnBox, SIGNAL(rejected()), this, SLOT(reject())); connect(dBtnBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(resetStyle())); connect(mBtnDefaultLight, SIGNAL(clicked()), this, SLOT(setStyleDefaultLight())); connect(mBtnDefaultDark, SIGNAL(clicked()), this, SLOT(setStyleDefaultDark())); connect(mBtnWidgetColorFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedWidgetFG(const QColor&))); connect(mBtnWidgetColorBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedWidgetBG(const QColor&))); connect(mBtnHighlightBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedHighlightBG(const QColor&))); connect(mBtnLineNumFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedLineNumFG(const QColor&))); connect(mBtnLineNumBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedLineNumBG(const QColor&))); connect(mBtnKeywordFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedKeywordFG(const QColor&))); connect(mCBKeywordWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedKeyword(const QFont::Weight&))); connect(mBtnClassFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedClassFG(const QColor&))); connect(mCBClassWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedClass(const QFont::Weight&))); connect(mBtnQuoteFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedQuoteFG(const QColor&))); connect(mCBQuoteWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedQuote(const QFont::Weight&))); connect(mBtnCommentFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedCommentFG(const QColor&))); connect(mCBCommentWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedComment(const QFont::Weight&))); connect(mBtnSymbolFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedSymbolFG(const QColor&))); connect(mBtnSymbolBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedSymbolBG(const QColor&))); connect(mCBSymbolWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedSymbol(const QFont::Weight&))); } void StyleEditDialog::updateControls() { mBtnWidgetColorFG->setColor(mStyleOutgoing.widgetFGColor); mBtnWidgetColorBG->setColor(mStyleOutgoing.widgetBGColor); mBtnHighlightBG->setColor(mStyleOutgoing.highlightBGColor); mBtnLineNumFG->setColor(mStyleOutgoing.lineNumFGColor); mBtnLineNumBG->setColor(mStyleOutgoing.lineNumBGColor); mBtnKeywordFG->setColor(mStyleOutgoing.keywordColor); mCBKeywordWeight->setWeight(mStyleOutgoing.keywordWeight); mBtnClassFG->setColor(mStyleOutgoing.classColor); mCBClassWeight->setWeight(mStyleOutgoing.classWeight); mBtnQuoteFG->setColor(mStyleOutgoing.quoteColor); mCBQuoteWeight->setWeight(mStyleOutgoing.quoteWeight); mBtnCommentFG->setColor(mStyleOutgoing.commentColor); mCBCommentWeight->setWeight(mStyleOutgoing.commentWeight); mBtnSymbolFG->setColor(mStyleOutgoing.symbolFGColor); mBtnSymbolBG->setColor(mStyleOutgoing.symbolBGColor); mCBSymbolWeight->setWeight(mStyleOutgoing.symbolWeight); } void StyleEditDialog::updateStyle() { mBtnDefaultLight->setEnabled(mStyleOutgoing != defaultStyleLight); mBtnDefaultDark->setEnabled(mStyleOutgoing != defaultStyleDark); // set Editor Styling mSampleEditor->setStyle(mStyleOutgoing); } CodeEditorStyle StyleEditDialog::getStyle() { return mStyleOutgoing; } void StyleEditDialog::resetStyle() { mStyleOutgoing = mStyleIncoming; updateControls(); updateStyle(); } void StyleEditDialog::setStyleDefaultLight() { mStyleOutgoing = defaultStyleLight; updateControls(); updateStyle(); } void StyleEditDialog::setStyleDefaultDark() { mStyleOutgoing = defaultStyleDark; updateControls(); updateStyle(); } void StyleEditDialog::colorChangedWidgetFG(const QColor& newColor) { mStyleOutgoing.widgetFGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedWidgetBG(const QColor& newColor) { mStyleOutgoing.widgetBGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedHighlightBG(const QColor& newColor) { mStyleOutgoing.highlightBGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedLineNumFG(const QColor& newColor) { mStyleOutgoing.lineNumFGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedLineNumBG(const QColor& newColor) { mStyleOutgoing.lineNumBGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedKeywordFG(const QColor& newColor) { mStyleOutgoing.keywordColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedKeyword(const QFont::Weight& newWeight) { mStyleOutgoing.keywordWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedClassFG(const QColor& newColor) { mStyleOutgoing.classColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedClass(const QFont::Weight& newWeight) { mStyleOutgoing.classWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedQuoteFG(const QColor& newColor) { mStyleOutgoing.quoteColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedQuote(const QFont::Weight& newWeight) { mStyleOutgoing.quoteWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedCommentFG(const QColor& newColor) { mStyleOutgoing.commentColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedComment(const QFont::Weight& newWeight) { mStyleOutgoing.commentWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedSymbolFG(const QColor& newColor) { mStyleOutgoing.symbolFGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedSymbolBG(const QColor& newColor) { mStyleOutgoing.symbolBGColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedSymbol(const QFont::Weight& newWeight) { mStyleOutgoing.symbolWeight = newWeight; updateStyle(); } cppcheck-2.7/gui/codeeditstyledialog.h000066400000000000000000000064721417746362400201470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CODEEDITSTYLEDIALOG_H #define CODEEDITSTYLEDIALOG_H #include "codeeditorstyle.h" #include class CodeEditor; class SelectColorButton; class SelectFontWeightCombo; class QPushButton; class StyleEditDialog : public QDialog { Q_OBJECT public: explicit StyleEditDialog(const CodeEditorStyle& newStyle, QWidget *parent = nullptr); virtual ~StyleEditDialog() {} CodeEditorStyle getStyle(); private: void updateControls(); void updateStyle(); public slots: void resetStyle(); void setStyleDefaultLight(); void setStyleDefaultDark(); void colorChangedWidgetFG(const QColor& newColor); void colorChangedWidgetBG(const QColor& newColor); void colorChangedHighlightBG(const QColor& newColor); void colorChangedLineNumFG(const QColor& newColor); void colorChangedLineNumBG(const QColor& newColor); void colorChangedKeywordFG(const QColor& newColor); void weightChangedKeyword(const QFont::Weight& newWeight); void colorChangedClassFG(const QColor& newColor); void weightChangedClass(const QFont::Weight& newWeight); void colorChangedQuoteFG(const QColor& newColor); void weightChangedQuote(const QFont::Weight& newWeight); void colorChangedCommentFG(const QColor& newColor); void weightChangedComment(const QFont::Weight& newWeight); void colorChangedSymbolFG(const QColor& newColor); void colorChangedSymbolBG(const QColor& newColor); void weightChangedSymbol(const QFont::Weight& newWeight); private: CodeEditorStyle mStyleIncoming; CodeEditorStyle mStyleOutgoing; CodeEditor *mSampleEditor; SelectColorButton *mBtnWidgetColorFG; SelectColorButton *mBtnWidgetColorBG; SelectColorButton *mBtnHighlightBG; SelectColorButton *mBtnLineNumFG; SelectColorButton *mBtnLineNumBG; SelectColorButton *mBtnKeywordFG; SelectFontWeightCombo *mCBKeywordWeight; SelectColorButton *mBtnClassFG; SelectFontWeightCombo *mCBClassWeight; SelectColorButton *mBtnQuoteFG; SelectFontWeightCombo *mCBQuoteWeight; SelectColorButton *mBtnCommentFG; SelectFontWeightCombo *mCBCommentWeight; SelectColorButton *mBtnSymbolFG; SelectColorButton *mBtnSymbolBG; SelectFontWeightCombo *mCBSymbolWeight; QPushButton *mBtnDefaultLight; QPushButton *mBtnDefaultDark; static const QString mSampleDocument; static const QStringList mErrSymbolsList; static const int mErrLineNum; }; #endif //CODEEDITSTYLEDIALOG_H cppcheck-2.7/gui/common.cpp000066400000000000000000000053501417746362400157430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "common.h" #include #include #include #include QString getPath(const QString &type) { QSettings settings; QString path = settings.value(type, QString()).toString(); if (path.isEmpty()) { // if not set, fallback to last check path hoping that it will be close enough path = settings.value(SETTINGS_LAST_CHECK_PATH, QString()).toString(); if (path.isEmpty()) // if not set, return user's home directory as the best we can do for now return QDir::homePath(); } return path; } void setPath(const QString &type, const QString &value) { QSettings settings; settings.setValue(type, value); } QString toFilterString(const QMap& filters, bool addAllSupported, bool addAll) { QStringList entries; if (addAllSupported) { entries << QCoreApplication::translate("toFilterString", "All supported files (%1)") .arg(QStringList(filters.values()).join(" ")); } if (addAll) { entries << QCoreApplication::translate("toFilterString", "All files (%1)").arg("*.*"); } // We're using the description of the filters as the map keys, the file // name patterns are our values. The generated filter string list will // thus be sorted alphabetically over the descriptions. for (const auto& k: filters.keys()) { entries << QString("%1 (%2)").arg(k).arg(filters.value(k)); } return entries.join(";;"); } QString getDataDir() { QSettings settings; const QString dataDir = settings.value("DATADIR", QString()).toString(); if (!dataDir.isEmpty()) return dataDir; const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); if (QFileInfo(appPath + "/std.cfg").exists()) return appPath; if (appPath.indexOf("/cppcheck/", 0, Qt::CaseInsensitive) > 0) return appPath.left(appPath.indexOf("/cppcheck/", 0, Qt::CaseInsensitive) + 9); return appPath; } cppcheck-2.7/gui/common.h000066400000000000000000000141111417746362400154030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COMMON_H #define COMMON_H #include #include /// @addtogroup GUI /// @{ #define CLANG_ANALYZER "clang-analyzer" #define CLANG_TIDY "clang-tidy" /** * QSetting value names */ // Window/dialog sizes #define SETTINGS_WINDOW_MAXIMIZED "Window maximized" #define SETTINGS_WINDOW_WIDTH "Window width" #define SETTINGS_WINDOW_HEIGHT "Window height" #define SETTINGS_LOG_VIEW_WIDTH "Log/View width" #define SETTINGS_LOG_VIEW_HEIGHT "Log/View height" #define SETTINGS_MAINWND_SPLITTER_STATE "Mainwindow/Vertical splitter state" #define SETTINGS_CHECK_DIALOG_WIDTH "Check dialog width" #define SETTINGS_CHECK_DIALOG_HEIGHT "Check dialog height" #define SETTINGS_PROJECT_DIALOG_WIDTH "Project dialog width" #define SETTINGS_PROJECT_DIALOG_HEIGHT "Project dialog height" // Main window settings #define SETTINGS_RESULT_COLUMN_WIDTH "Result column %1 width" #define SETTINGS_TOOLBARS_MAIN_SHOW "Toolbars/ShowStandard" #define SETTINGS_TOOLBARS_VIEW_SHOW "Toolbars/ShowView" #define SETTINGS_TOOLBARS_FILTER_SHOW "Toolbars/ShowFilter" // Show * states #define SETTINGS_SHOW_STYLE "Show style" #define SETTINGS_SHOW_ERRORS "Show errors" #define SETTINGS_SHOW_WARNINGS "Show warnings" #define SETTINGS_SHOW_PERFORMANCE "Show performance" #define SETTINGS_SHOW_INFORMATION "Show information" #define SETTINGS_SHOW_PORTABILITY "Show portability" // Standards support #define SETTINGS_STD_CPP "Standard CPP" #define SETTINGS_STD_C "Standard C" // Language enforcement #define SETTINGS_ENFORCED_LANGUAGE "Enforced language" // Other settings #define SETTINGS_CHECK_FORCE "Check force" #define SETTINGS_CHECK_THREADS "Check threads" #define SETTINGS_SHOW_FULL_PATH "Show full path" #define SETTINGS_SHOW_NO_ERRORS "Show no errors message" #define SETTINGS_SHOW_DEBUG_WARNINGS "Show debug warnings" #define SETTINGS_SAVE_ALL_ERRORS "Save all errors" #define SETTINGS_SAVE_FULL_PATH "Save full path" #define SETTINGS_APPLICATION_NAMES "Application names" #define SETTINGS_APPLICATION_PATHS "Application paths" #define SETTINGS_APPLICATION_PARAMS "Application parameters" #define SETTINGS_APPLICATION_DEFAULT "Default Application" #define SETTINGS_LANGUAGE "Application language" #define SETTINGS_GLOBAL_INCLUDE_PATHS "Global include paths" #define SETTINGS_PYTHON_PATH "Python path" #define SETTINGS_MISRA_FILE "MISRA C 2012 file" #define SETTINGS_CLANG_PATH "Clang path" #define SETTINGS_VS_INCLUDE_PATHS "VS include paths" #define SETTINGS_INLINE_SUPPRESSIONS "Inline suppressions" #define SETTINGS_INCONCLUSIVE_ERRORS "Inconclusive errors" #define SETTINGS_MRU_PROJECTS "MRU Projects" #define SETTINGS_SHOW_ERROR_ID "Show error Id" #define SETTINGS_SHOW_STATISTICS "Show statistics" #define SETTINGS_OPEN_PROJECT "Open Project" // The maximum value for the progress bar #define PROGRESS_MAX 1024.0 #define SETTINGS_CHECKED_PLATFORM "Checked platform" #define SETTINGS_LAST_CHECK_PATH "Last check path" #define SETTINGS_LAST_PROJECT_PATH "Last project path" #define SETTINGS_LAST_RESULT_PATH "Last result path" #define SETTINGS_LAST_SOURCE_PATH "Last source path" #define SETTINGS_LAST_INCLUDE_PATH "Last include path" #define SETTINGS_LAST_APP_PATH "Last application path" #define SETTINGS_LAST_ANALYZE_FILES_FILTER "Last analyze files filter" /** * @brief Obtains the path of specified type * Returns the path of specified type if not empty. Otherwise returns last check * path if valid or user's home directory. * @param type Type of path to obtain * @return Best path for provided type */ QString getPath(const QString &type); /** * @brief Stores last used path of specified type * Stores provided path as last used path for specified type. * @param type Type of the path to store * @param value Path to store */ void setPath(const QString &type, const QString &value); /** * @brief Creates a string suitable for passing as the filter argument to * methods like QFileDialog::getOpenFileName. * @param filters A map of filter descriptions to the associated file name * patterns. * @param addAllSupported If set to true (the default), the function will * include a filter entry containing all the file name patterns found in * \p filters. This entry will be the first in the resulting filter string. * @param addAll If set to true (the default), the function will * include a filter entry displaying all files. This entry will be placed * after the entry for \p addAllSupported files. * * Example usage: * * @code * QMap filters; * filters[tr("Supported images")] = "*.bmp *.jpg *.png"; * filters[tr("Plain text")] = "*.txt"; * * const QString filterString = toFilterString(filters); * * // filterString contains "All supported files (*.txt *.bmp *.jpg *.png);;All files (*.*);;Plain text (*.txt);;Supported images (*.bmp *.jpg *.png)" * @endcode */ QString toFilterString(const QMap& filters, bool addAllSupported=true, bool addAll=true); /** * Get configured data dir. If not configured then it will try to determine that from exe path. */ QString getDataDir(); /// @} #endif cppcheck-2.7/gui/cppcheck-gui.desktop000066400000000000000000000002611417746362400177000ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=Cppcheck Comment=A tool for static C/C++ code analysis Exec=cppcheck-gui Icon=cppcheck-gui Categories=Development;Debugger;Qt; cppcheck-2.7/gui/cppcheck-gui.png000066400000000000000000000047571417746362400170310ustar00rootroot00000000000000PNG  IHDRAAE IDATxt$񎝬m۶mڶm\۶m۾kS{*'әAMwuUX^V x/1ovb :n*m⃟ `.A 11V>2`vIJzFfH/S6o>#S=oW2iҫj-n[Ixx.1B<$((X/g";w^]c۷_3ޗWH2Mh3(m 'QSAX Ԭ[. ΧҲ)QYo=Z Iɒ s煲l>X?wA4C@T0;]uGj.npaUTl3W!_Leow8Wo2G"N` ZP]C'!8Fg;ZLTFQN@zZi\EsE\ͻzBȍ.]ޣ:[ȓntƍ{L(P/\n? @6j`e9!|Ѩ1m;~Y!,Xdyvb3d o,jdxVP ,tU!Gg!n^ǔH5}u مЬYԯ?4]ХPE!t 5Hmo͚`=q˒{=Hdd<3nCHJzZ7< ~n #f6ȰނzO. +QN+=^A#$6}8BFߣ<ҥ qwim:iwG4Uk*a읖~ĒY'>Gh QqUjƺ'd;\4F+wmJH,W`X~+#&ԁ/P=0NrSijrȨt_iےUnK$"*5xᶪJJ'%4'5)mVc G`ǎR׍řuXn d},ݒ]- K=ƫ EᲚ$27.ѣ\ siQ""f}wH|.K&'9,ך/#&GU_Ԫ՗0vCanݖLr~FEhOC]u)HѲrzAKj65Q.+q|YG^mUNO|mS,q1kUgsS7YRd_:DqIlԚ?RZy5DX: { ] pK:Uu' `(_~6TuBHD%gݖf݈_71K0jH|L{&3I=荀8)Uɒv2;?\ ?k!CQO]d !~D T(KPiN< /OAC O0>g,-I>z#~6d=vbFߡ;V}-tzoNC, M=_P~pjQc|x t:!jf`:^]_<>jVQsLmï{!Ӽ?X=eJN͂=U[a{%)Am˜ƬU/[V0 AY\xwTٶoP  PťjIƚzD ˪4@OЏL5^-0_BRWr[-XA1  jta3_X Pc==W,cTP7UC==YyGԓ?#u`4vaj}ʧ(lQA!Ju4\.e r{h,9VѰpc_پ \@ۍOU脀WϥIENDB`cppcheck-2.7/gui/cppcheck-gui.rc000066400000000000000000000002661417746362400166400ustar00rootroot00000000000000// Include command line program's resource file containing version info #include "../cli/version.rc" // GUI Icon IDI_ICON1 ICON DISCARDABLE "cppcheck.ico" cppcheck-2.7/gui/cppcheck-gui.svg000066400000000000000000000077731417746362400170450ustar00rootroot00000000000000 image/svg+xml c ++ cppcheck-2.7/gui/cppcheck.ico000066400000000000000000000611761417746362400162330ustar00rootroot00000000000000 00h ( 00 h^"00 %'  nM h^(0`p 32333 0#3333p`#33333#p#33333330p333333332 p333333333333333332333332333330#333330p33332333333 p3303333332p#333332p3333330p333333 3333330sp333333"wpp#333333 pp|3333333333333 |pppp#33332  3330p|p32 3 |wppwGpppp pp p p p @ p 0  @p p p pp w wpwpww@ p  @ p p| p| pw|p|Lpwtpp???~???>A3B5D6F8I:K<L=QATCWEaMeQjUlVnXpYs\v^ya|c}dAAAFFFIIINNNPPPTTTYYY^^^nAApBBuEExGGzHH}JJ```eeehhhoooqqquuuzzz}}}LLNNPPSSTTUUXX[[]]^^``bbccddiikkmmooppssuuwwyy{{||~~fhjmpstwxz|~ځ݂ބⅅ戈銊싋/ P6pLbx1Qq,/KPip1Qq/-P?pRcv1Qqϑܱ/Pp!&,>X1qQq@pp6C|)b8%|%``CJ Zj7*l7[lr6dh5*A`Wr@YdAl>kaX=|*ca55a Im]d4$nKe8~)8g9mr __KAbj< C|Xf .r I+7[ lB ib @Jd;!r~sz~r\a?K*/3ML3 C~~!X9#||@"Rt?#8^JH,uTRV}HiZ?B1y,%Kl.Nxmaf9nCEy{A3T*q[k;Ir-"~+Q0z%=k>I*u?t@#6dW?s,GsCy(znnI @znn+,oDz?/|+0~}l?xE|+1~|3?G|+3~JQ*?G|+3AVwA?G|+3~!wuC*Gq)3rVH##G ##.~"#3 %##ytHP{,P?uvB'{0HTy?&y{0yrL~/00S00{-00P00)+zznGBzz|zzm3Czzz!(U??G|+3~nF2'??G|+3~QIlN??G|+3~IM%??G|+3~!UO.%R??U0|+V-~"UOmnP@C}B!OM'LP.COF.qI,OTuywtRL-@onC+ !.JsGwGwGwGwGw?GwGwGwGwGwGwGwGwGw?GwGw?GwGwGwGwGwGw~Gw??Gw?Gw2 :1 ;2 ?4%!!)"".##)&&-$$*)),++0 4""2$$9##8$$1**5((4--:**=++9,,=,, 1-$30)42+74-64*84100:11<222;96=;F**F--I--L--P//A33D11I22M22M44@88A>>E<<P00T22R77U77\66]99`99a<<f<<h==B5B6D7H;J<M=M>RBUDXG[I^K_L8B@?FEaMhSkUmWoYpYs\ya|c~eAAAGCCEEEICCIEELFFEHGFIHIIIMHHILKIOMNNNRNNTOOKQOPQQVRRVUUUXW[]\]]]kAAnAApBBvEEyHHKK`]]^a_bbbfeennnpoorqqvuuyyy}}}MMOOPPTTVVXXZZ]]``bbcceehhkkmmqqrrttvvxxyy||~~hnpvxy}ہ݃鉉싋/ P6pLbx1Qq,/KPip1Qq/-P?pRcv1Qqϑܱ/Pp!&,>X1qQq:~aX9 ^4UVr3g {gua] hdwRfvbi0fd{rY[ 5_zTijZ9ic@=PL/o3\S9/A`ZW"CBo7HT2<lGWk8#+.KN#7yP *;Q)mF+%+>(,Ikn8ttO/I{|//O/I{|8EEO}'|8D6+BC}7J8p.$&7xx?xx|3c3qssxs( 1,,E++F..H//N00Q::W::TF XH ]LTHZL*SK+TL3RK:UO6WP:VQgSiUmWr[s\zaZEE\FFSOOTPPoBBjKKqJJwKKzII{MM|NNbPPfRRcTTnSSnUUj^^o^^qQQ{QQJa\Tb_Uca[ge_libbbgccmcciiiqbbreeujjvllktrrppuqqvttyww|wwqyw~~~VVZZ^^mmyyqy~}܂戈 =1[Qyq/"P0p=LYgx1Qq&/@PZpt1Qq/&PAp[tϩ1Qq/P"p0>M[iy1Qqұ/Pp  >1\Qzq/Pp!+6@IZ1pQq/ P6pLbx1Qq,/KPip1Qq/-P?pRcv1Qqϑܱ/Pp!&,>X1qQq/0NJ P KONOO:5ONOOI  LONJOOH*BC!-HOOLUF<)D@K1?TW+#$=A.+WW6#WV9A"WV9CSEGS(WW*<3;3>(W,,7&RS%A%R43''! '<?G]](0` [6R*!Z )cOxj/%%WF{WEM k E7g{b+" C*"~ewI: 9<( lVu^%{!WEso?30d D6tlVf S<0}dt`MB5nm8--diTaM&%fz`M V8 L=w{mW#I= nXh-$!.%h~s\1'X) TC~yTC OWeQ|c7, bC5pYum  i,#jzG9 ;lt"Uw_fQjAkVo4*!8_{oH+K<|bN^7'4C''@&&3;B5ug1' [55[[zzځ߄ᅅ݃{{kkj?? tg.%lxQA GA pBBفuuNNa::Z55MMddᅅ܂h== Ix`xF8LG.mm}JJ!J,,ww{HHq9bOoX1' [8!!zzKK )!_4tt닋`88. I;|c3) b%ppvv .Y55yy,~:.p}d<0mMPPoBB+x qqm@@' _)!lVA3u*ބ;##2KKss m-oH!4d3LLqq f7 cc^^ @) /{,B, Vsscc 5  !@zHH8!!BMM/3݃XX2333+ @߄NN=$$BTT3X44쌌LL@߄NN=$$BTT3i>>vFF@߄NN=$$BTT3xGGoBB55hNN=$$553 55jTT3855LLj??yNN=$$TT3|IIm@@ ]]bbbb}}oobbbbT11XXbbbbmmbbbbU33nAAtEELL-戈MM`99{II銊}JJ+ރ~KKA&&ㆆSSU``[),,[[_88,, ',,__V33,+ g zz__%9  rNN=$$`TT3=__kkOY SSc;;$@߄NN=$$BTT3<##||0ㆆOO$@߄NN=$$BTT3hjjW33!J,,OO$@߄NN=$$BTT3D((ہ``giccOO$@߄NN=$$BTT3?d;;牉O..c U[66싋OO$@b::-Bi>>%ud;;ᅅځO.. xV33OO$<?uM..qqㆆ]]D((  @&&UUㆆW33$<EE)=EE&LO//ii~~艉ꊊㆆۂ}}mmLL;## #w M..b99qBB~JJxGGnAA\66B''%w<%G{}K/GwGwGwGw?GwGwGwGwGw?Gw?GwGwGwGwGw?Gw`<GwGwGwGwpGw0Gw8GwGwGwGwGw7Gw?Gw?Gw?GwGwGwGwGwGwGwGw?Gw?Gw?Gw?Gw?Gw?Gw?GwGw?GwGw( @ a 84!bNJ; K8#_Lh) &f C6npF8F[ <0pqZ&"1!s\zn6+0`M~y^K3)x`kV~>@3opY1' }hS}d1'. oYxmW1'U:QAzXGc G ( i) >1z|c"!M*"fyUD _7VhfQ* w{bjT!I%E((f<1 kT@&&zzj?? 7 ``qq )/%hvM=1'ttqq A=$$߃^77v 7B@3V=*_@%%WW ӝ]]wFF&  dJA TTnAAq+)P//ei>> γjjZ55\ +,T22eoBB $vvM--Q9,T22o&oBB *||G**N=,T22oBB 'yyJ++OYL--qqyyssMM ݷkkss݃vv``!!ssP//TY`99cc艉{{* ``b::c? D((h== KK,xFFyHHF))0u5W,T225,.FoBB 75A닋cc  eeZ55c+,T22eoBB VV}}137.Z55c+,T22eoBB דWWbb e1/` ШccZ55c* )c6 Vؚ[[[[B'' =$$ZZ싋E))ct2 VY#$MMeessxxttmm``OOF)) 0K "(&  ^4??00p x!0!!!??(  9#lV@3J VDr[ r[x`'gSy2(8mP@+"HHL=cO ~0.)2(}q%("*PP[[zHHf<<v!rK<_W33yy L*]]Z zadP$ `99uEE7 f|X443V^>%%2a>%%zHH%n@&&R@&&avFF&-ZZ戈mm戈oBBK,,8!!  K,, u K,, t܂pBBoBB.@&&2a@&&1戈<##;""ᅅ( %H  {Q00vEEqCCP// 6A?AAAAAAAAAAAAAAAcppcheck-2.7/gui/cppcheck_de.ts000066400000000000000000003400301417746362400165440ustar00rootroot00000000000000 About About Cppcheck Über Cppcheck Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Ein Werkzeug zur statischen C/C++-Code-Analyse. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright © 2007-%1 Cppcheck-Team. This program is licensed under the terms of the GNU General Public License version 3 Dieses Programm ist unter den Bedingungen der GNU General Public License Version 3 lizenziert Visit Cppcheck homepage at %1 Besuchen Sie die Cppcheck-Homepage unter %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> <html><head/><body> <p>Vielen Dank für die von uns genutzten Bibliotheken:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Anwendung hinzufügen Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Hier können Sie Anwendungen hinzufügen, die Codedateien öffnen können. Geben Sie den Namen der Anwendung, deren ausführbare Datei und Kommandozeilenparameter für die Ausführung an. Die folgenden Texte in Parametern werden durch die passenden Werte ersetzt, wenn die Anwendung ausgeführt wird: (file) - Name der Datei, die den Fehler enthält (line) - Zeile, die den Fehler enthält (message) - Fehlermeldung (severity) - Schweregrad des Fehlers Beispiel: Öffnen einer Datei mit Kate, automatisch zur korrekten Zeile scrollen: Ausführbare Datei: kate Parameter: -l(line) (file) &Name: &Name: &Executable: &Ausführbare Datei: &Parameters: &Parameter: Browse Suchen Executable files (*.exe);;All files(*.*) Ausführbare Dateien (*.exe);;Alle Dateien(*.*) Select viewer application Anzeigeanwendung auswählen Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Sie müssen einen Namen, einen Pfad und ggf. Parameter für die Anwendung angeben! FileViewDialog Could not find the file: %1 Konnte die Datei nicht finden: %1 Cppcheck Cppcheck Could not read the file: %1 Konnte die Datei nicht lesen: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Funktion hinzufügen Function name(s) Funktionsname(n) Number of arguments Anzahl Argumente LibraryDialog Library Editor Bibliothekseditor Open Öffnen Save Speichern Save as Speichern unter Functions Funktionen Sort Sortiere Add Hinzufügen Filter: Filter: Comments Kommentare noreturn Zurückkehrend False Ja True Nein Unknown Unbekannt return value must be used Rückgabewert muss genutzt werden ignore function in leaks checking Ignoriere Funktion in Speicherleck-Prüfung Arguments Argumente Edit Bearbeiten Library files (*.cfg) Bibliotheksdateien (*.cfg) Open library file Bibliothek öffnen Cppcheck Cppcheck Cannot open file %1. Datei %1 kann nicht geöffnet werden. Failed to load %1. %2. %1 kann nicht geladen werden. %2. Cannot save file %1. Datei %1 kann nicht gespeichert werden. Save the library as Speichere Bibliothek unter LibraryEditArgDialog Edit argument Argument bearbeiten <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> <html><head/><body> <p>Ist ein boolscher Wert, beispielsweise das Ergebnis eines Vergleichsoperators, oder von '!' zulässig?</p> <p>Diese Option wird typischerweise gesetzt, wenn das Argument ein Zeiger, eine Größe, etc. ist.</p> <p>Beispiel:</p> <pre> memcmp(x, y, i == 123); // Das letzte Argument sollte kein boolscher Wert sein.</pre> </body></html> Not bool Nicht boolsch <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> <html><head/><body> <p>Ist die Übergabe von Null zulässig?</p> <p>Dies wird typischerweise für Funktionen mit Zeigern als Parameter genutzt, die nicht Null sein dürfen.</p> <p>Beispiel:</p> <pre> strcpy(x,y); // Weder x noch y dürfen ein Nullzeiger sein.</pre> </body></html> Not null Nicht Null Not uninit Nicht uninitialisiert String String Format string Formatstring Min size of buffer Minimale Puffergröße Type Typ None Keine argvalue Argumentwert mul Multiplikation strlen strlen Arg Argument 1 Arg2 Argument 2 and und Valid values Zulässige Werte MainWindow Cppcheck Cppcheck Standard Standard &File &Datei &View &Ansicht &Toolbars &Symbolleisten A&nalyze A&nalysieren C++ standard C++-Standard &C standard &C-Standard &Edit &Bearbeiten &License... &Lizenz... A&uthors... &Autoren... &About... Ü&ber... &Files... &Dateien... Analyze files Analysiere Dateien Ctrl+F Strg+F &Directory... &Verzeichnis... Analyze directory Analysiere Verzeichnis Ctrl+D Strg+D Ctrl+R Strg+R &Stop &Stoppen Stop analysis Analyse abbrechen Esc Esc &Save results to file... &Ergebnisse in Datei speichern... Ctrl+S Strg+S &Quit &Beenden &Clear results Ergebnisse &löschen &Preferences &Einstellungen Show errors Zeige Fehler Show warnings Zeige Warnungen Show performance warnings Zeige Performance-Warnungen Show &hidden Zeige &versteckte Information Information Show information messages Zeige Informationsmeldungen Show portability warnings Zeige Portabilitätswarnungen Show Cppcheck results Zeige Cppcheck-Ergebnisse Clang Clang Show Clang results Zeige Clang-Ergebnisse &Filter &Filter Filter results Gefilterte Ergebnisse Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit &Print... Drucken... Print the Current Report Aktuellen Bericht ausdrucken Print Pre&view... Druckvorschau Open a Print Preview Dialog for the Current Results Druckvorschaudialog für aktuelle Ergebnisse öffnen Open library editor Bibliothekseditor öffnen &Check all Alle &auswählen Filter Filter &Reanalyze modified files Veränderte Dateien neu analysieren Reanal&yze all files Alle Dateien erneut anal&ysieren Ctrl+Q Style war&nings Stilwar&nungen E&rrors F&ehler &Uncheck all Alle a&bwählen Collapse &all Alle &reduzieren &Expand all Alle &erweitern &Standard &Standard Standard items Standardeinträge Toolbar Symbolleiste &Categories &Kategorien Error categories Fehler-Kategorien &Open XML... Öffne &XML... Open P&roject File... Pr&ojektdatei öffnen... Ctrl+Shift+O Sh&ow Scratchpad... &Zeige Schmierzettel... &New Project File... &Neue Projektdatei... Ctrl+Shift+N &Log View &Loganzeige Log View Loganzeige C&lose Project File Projektdatei &schließen &Edit Project File... Projektdatei &bearbeiten... &Statistics &Statistik &Warnings &Warnungen Per&formance warnings Per&formance-Warnungen &Information &Information &Portability &Portabilität P&latforms P&lattformen C++&11 C++&11 C&99 C&99 &Posix Posix C&11 C&11 &C89 &C89 &C++03 &C++03 &Library Editor... &Bibliothekseditor &Auto-detect language Sprache &automatisch erkennen &Enforce C++ C++ &erzwingen E&nforce C C e&rzwingen C++14 C++14 Reanalyze and check library Neu analysieren und Bibliothek prüfen Check configuration (defines, includes) Prüfe Konfiguration (Definitionen, Includes) C++17 C++17 C++20 C++20 &Contents &Inhalte Categories Kategorien Show style warnings Zeige Stilwarnungen Open the help contents Öffnet die Hilfe-Inhalte F1 F1 &Help &Hilfe Quick Filter: Schnellfilter: Select configuration Konfiguration wählen Found project file: %1 Do you want to load this project file instead? Gefundene Projektdatei: %1 Möchten Sie stattdessen diese öffnen? File not found Datei nicht gefunden Bad XML Fehlerhaftes XML Missing attribute Fehlendes Attribut Bad attribute value Falscher Attributwert Duplicate platform type Plattformtyp doppelt Platform type redefined Plattformtyp neu definiert Failed to load the selected library '%1'. %2 Laden der ausgewählten Bibliothek '%1' schlug fehl. %2 License Lizenz Authors Autoren Save the report file Speichert die Berichtdatei XML files (*.xml) XML-Dateien (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Beim Laden der Editor-Anwendungseinstellungen trat ein Problem auf. Dies wurde vermutlich durch einen Wechsel der Cppcheck-Version hervorgerufen. Bitte prüfen (und korrigieren) Sie die Einstellungen, andernfalls könnte die Editor-Anwendung nicht korrekt starten. You must close the project file before selecting new files or directories! Sie müssen die Projektdatei schließen, bevor Sie neue Dateien oder Verzeichnisse auswählen! The library '%1' contains unknown elements: %2 Die Bibliothek '%1' enthält unbekannte Elemente: %2 Unsupported format Nicht unterstütztes Format Unknown element Unbekanntes Element Unknown issue Unbekannter Fehler Error Fehler Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Laden von %1 fehlgeschlagen. Ihre Cppcheck-Installation ist defekt. Sie können --data-dir=<Verzeichnis> als Kommandozeilenparameter verwenden, um anzugeben, wo die Datei sich befindet. Bitte beachten Sie, dass --data-dir in Installationsroutinen genutzt werden soll, und die GUI bei dessen Nutzung nicht startet, sondern die Einstellungen konfiguriert. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Aktuelle Ergebnisse werden gelöscht. Das Einlesen einer XML-Datei löscht die aktuellen Ergebnisse. Fortfahren? Open the report file Berichtdatei öffnen Text files (*.txt) Textdateien (*.txt) CSV files (*.csv) CSV-Dateien (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Projektdateien (*.cppcheck);;Alle Dateien(*.*) Select Project File Projektdatei auswählen Project: Projekt: No suitable files found to analyze! Keine passenden Dateien für Analyse gefunden! C/C++ Source C/C++-Quellcode Compile database Compilerdatenbank Visual Studio Visual Studio Borland C++ Builder 6 Borland C++-Builder 6 Select files to analyze Dateien für Analyse auswählen Select directory to analyze Verzeichnis für Analyse auswählen Select the configuration that will be analyzed Zu analysierende Konfiguration auswählen Found project files from the directory. Do you want to proceed analysis without using any of these project files? Projektdateien im Verzeichnis gefunden. Wollen sie fortfahren, ohne diese Projektdateien zu nutzen? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Aktuelle Ergebnisse werden gelöscht. Eine neue XML-Datei zu öffnen wird die aktuellen Ergebnisse löschen Möchten sie fortfahren? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? Analyse läuft. Wollen sie die Analyse abbrechen und Cppcheck beenden? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML-Dateien (*.xml);;Textdateien (*.txt);;CSV-Dateien (*.csv) Build dir '%1' does not exist, create it? Erstellungsverzeichnis '%1' existiert nicht. Erstellen? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Import von '%1' fehlgeschlagen; Analyse wurde abgebrochen. Project files (*.cppcheck) Projektdateien (*.cppcheck) Select Project Filename Projektnamen auswählen No project file loaded Keine Projektdatei geladen The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Die Projektdatei %1 konnte nicht gefunden werden! Möchten Sie die Datei von der Liste der zuletzt benutzten Projekte entfernen? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONEN] [Dateien oder Pfade] Options: -h, --help Gibt diese Hilfeinformationen aus -p <file> Öffnet das angegebene Projekt und beginnt die Prüfung -l <file> Öffnet die angegebene XML-Ergebnisdatei -d <directory> Gibt das Verzeichnis an, das geprüft wurde, um das unter -l angegebene XML-Ergebnis zu erzeugen -v, --version Zeigt Programmversion an --data-dir=<directory> Gibt das Verzeichnis an, unter dem sich die Konfigurationsdateien für die GUI (Übersetzungen, Cfg) befinden. Die GUI startet bei Nutzung dieser Option nicht. Cppcheck GUI - Command line parameters Cppcheck GUI - Kommandozeilenparameter NewSuppressionDialog New suppression Neue Fehlerunterdrückung Error ID Fehler-ID File name Dateiname Line number Zeilennummer Symbol name Symbolname Edit suppression Fehlerunterdrückung bearbeiten Platforms Native Nativ Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Windows 64-bit Windows 64-bit ProjectFile Project File Projektdatei Paths and Defines Pfade und Definitionen Import Project (Visual studio / compile database/ Borland C++ Builder 6) Importiere Projekt (Visual Studio / Compile-Datenbank / Borland C++-Builder 6) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Definitionen müssen mit einem Semikolon getrennt werden. Beispiel: DEF1;DEF2=5;DEF3=int Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Hinweis: Legen Sie eigene .cfg-Dateien in den Ordner der Projektdatei. Dann sollten sie oben sichtbar werden. Addons and tools Addons und Werkzeuge MISRA C 2012 MISRA C 2012 MISRA rule texts MISRA-Regeltexte <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Text aus Anhang A &quot;Summary of guidelines&quot; aus der MISRA-C-2012-PDF in eine Textdatei einfügen.</p></body></html> ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>Sie haben die Auswahl:</p><p> * Alle Debug- und Release-Konfigurationen analysieren</p><p> * Nur die erste passende Debug-Konfiguration analysieren</p><p><br/></p></body></html> Browse... Durchsuchen... Analyze all Visual Studio configurations Alle Visual-Studio-Konfigurationen analysieren Selected VS Configurations Paths: Pfade: Add... Hinzufügen... Edit Bearbeiten Remove Entfernen Undefines: Un-Definitionen: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Un-Definitionen müssen Semikolon-getrennt sein. Beispiel: UNDEF1;UNDEF2;UNDEF3 Include Paths: Includepfade: Up Auf Down Ab Checking Prüfung Platform Plattform Analysis Analyse This is a workfolder that Cppcheck will use for various purposes. Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (slower analysis, more results) Prüfe Code in Headern (langsamere Analyse, mehr Ergebnisse) Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Prüfe Code in ungenutzten Templates (langsamere und weniger genaue Analyse) Max CTU depth Maximale CTU-Tiefe Max recursion in template instantiation Warning options Warnoptionen Root path: Wurzelverzeichnis: Filepaths in warnings will be relative to this path Warning tags (separated by semicolon) Warnungs-Tags (Semikolon-getrennt) Exclude source files in paths Quelldateien in Pfaden ausschließen Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. Hinweis: Addons setzen voraus, dass <a href="https://www.python.org/">Python</a> installiert ist. External tools Externe Werkzeuge Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck-Arbeitsverzeichnis (Vollständige Programmanalyse, inkrementelle Analyse, Statistiken, etc.) Types and Functions Libraries Bibliotheken Parser Cppcheck (built in) Clang Clang Check that each class has a safe public interface Limit analysis Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... Suppressions Fehlerunterdrückungen Add Hinzufügen Addons Add-Ons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Y2038 Thread safety Threadsicherheit Coding standards Programmierstandards CERT CERT Clang analyzer Clang-Analyzer Clang-tidy Clang-Tidy Defines: Definitionen: ProjectFileDialog Project file: %1 Projektdatei: %1 Select Cppcheck build dir Wähle Cppcheck-Erstellungsverzeichnis Select include directory Wähle Include-Verzeichnisse Select a directory to check Wähle zu prüfendes Verzeichnis (no rule texts file) (keine Regeltexte) Clang-tidy (not found) Clang-tidy (nicht gefunden) Visual Studio Visual Studio Compile database Compilerdatenbank Borland C++ Builder 6 Borland C++-Builder 6 Import Project Projekt importieren Select directory to ignore Wähle zu ignorierendes Verzeichnis Source files All files Exclude file Select MISRA rule texts file Wähle MISRA-Regeltext-Datei MISRA rule texts file (%1) MISRA-Regeltext-Datei QDialogButtonBox OK OK Cancel Abbrechen Close Schließen Save Speichern QObject Unknown language specified! Unbekannte Sprache angegeben! Language file %1 not found! Sprachdatei %1 nicht gefunden! Failed to load translation for language %1 from file %2 Die Übersetzungen der Sprache %1 konnten nicht aus der Datei %2 geladen werden line %1: Unhandled element %2 Zeile %1: Nicht behandeltes Element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) (nicht gefunden) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Klassen-Vordergrundfarbe Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK OK Cancel Abbrechen Close Schließen Save Speichern ResultsTree File Datei Severity Schweregrad Line Zeile Summary Zusammenfassung Undefined file Undefinierte Datei Copy Kopieren Could not find file: Kann Datei nicht finden: Please select the folder '%1' Bitte wählen Sie den Ordner '%1' Select Directory '%1' Wähle Verzeichnis '%1' Please select the directory where file is located. Bitte wählen Sie das Verzeichnis, wo sich die Datei befindet debug Debug note Anmerkung Recheck Erneut prüfen Hide Verstecken Hide all with id Verstecke alle mit gleicher ID Suppress selected id(s) Ausgewählte ID(s) unterdrücken Open containing folder Übergeordneten Ordner öffnen Edit contract.. Suppress Tag Tag No tag Kein Tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Keine Editor-Anwendung eingestellt. Konfigurieren Sie diese unter Einstellungen/Anwendungen. No default editor application selected. Please select the default editor application in preferences/Applications. Keine Standard-Editor-Anwendung eingestellt. Bitte wählen Sie eine Standardanwendung unter Einstellungen/Anwendungen. Could not find the file! Datei konnte nicht gefunden werden! Could not start %1 Please check the application path and parameters are correct. %1 konnte nicht gestartet werden. Bitte überprüfen Sie ob der Pfad und die Parameter der Anwendung richtig eingestellt sind. Select Directory Wähle Verzeichnis Id Id Inconclusive Unklar Since date Seit Datum style Stil error Fehler warning Warnung performance Performance portability Portabilität information Information ResultsView Print Report Bericht drucken No errors found, nothing to print. Keine Funde, nichts zu drucken. %p% (%1 of %2 files checked) %p% (%1 von %2 Dateien geprüft) Cppcheck Cppcheck No errors found. Keine Fehler gefunden. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Es wurden Fehler gefunden, aber sie sind so konfiguriert, ausgeblendet zu werden. Legen Sie unter dem Menü Ansicht fest, welche Arten von Fehlern angezeigt werden sollen. Failed to read the report. Lesen des Berichts fehlgeschlagen. XML format version 1 is no longer supported. XML-Format-Version 1 wird nicht länger unterstützt. First included by Zuerst inkludiert von Id Id Bug hunting analysis is incomplete Clear Log Protokoll leeren Copy this Log entry Diesen Protokolleintrag kopieren Copy complete Log Gesamtes Protokoll kopieren No errors found, nothing to save. Keine Fehler gefunden, nichts zu speichern. Failed to save the report. Der Bericht konnte nicht speichern werden. Results Berichte Analysis Log Analyseprotokoll Warning Details Warnungs-Details Functions Funktionen Variables Variablen Only show variable names that contain text: Configured contracts: Missing contracts: ScratchPad Scratchpad Schmierzettel Copy or write some C/C++ code here: Kopieren oder schreiben Sie C/C++-Code hierher: Optionally enter a filename (mainly for automatic language detection) and click on "Check": Optional einen Dateinamen (hauptsächlich für automatische Spracherkennung) eingeben und auf "Prüfe" klicken: filename Dateiname Check Prüfe Settings Preferences Einstellungen General Allgemein Add... Hinzufügen... Number of threads: Anzahl der Threads: Ideal count: Ideale Anzahl: Force checking all #ifdef configurations Erzwinge Prüfung aller #ifdef-Konfigurationen Show full path of files Vollständigen Dateipfad anzeigen Show "No errors found" message when no errors found "Keine Fehler gefunden"-Meldung anzeigen, wenn keine Fehler gefunden werden Display error Id in column "Id" Zeige Meldungs-Id in Spalte "Id" Enable inline suppressions Inline-Fehlerunterdrückung aktivieren Check for inconclusive errors also Auch nach unklaren Fehlern suchen Show statistics on check completion Zeige Statistiken nach Prüfungsabschluss Show internal warnings in log Interne Warnungen im Log anzeigen Addons Add-Ons Python binary (leave this empty to use python in the PATH) Python-Binärdatei (Python aus PATH wird genutzt, wenn leer) ... ... MISRA addon MISRA-Addon MISRA rule texts file MISRA-Regeltext-Datei <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Text aus Anhang A &quot;Summary of guidelines&quot; aus der MISRA-C-2012-PDF in eine Textdatei einfügen.</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clang-Verzeichnis (PATH wird genutzt, wenn leer) Visual Studio headers Visual-Studio-Header <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Pfade zu Visual-Studio-Headern, Semikolon-getrennt.</p><p>Sie können eine Visual-Studio-Kommandozeile öffnen, &quot;SET INCLUDE&quot; eingeben und dann die Pfade hier reinkopieren.</p></body></html> Code Editor Code-Editor Code Editor Style Code-Editor-Stil System Style Systemstil Default Light Style Heller Standardstil Default Dark Style Dunkler Standardstil Custom Benutzerdefiniert Remove Entfernen Applications Anwendungen Edit... Bearbeiten... Set as default Als Standard festlegen Reports Berichte Save all errors when creating report Alle Fehler beim Erstellen von Berichten speichern Save full path to files in reports Vollständigen Dateipfad in Berichten speichern Language Sprache SettingsDialog N/A kA The executable file "%1" is not available Add a new application Neue Anwendung hinzufügen Modify an application Anwendung ändern [Default] [Standard] [Default] [Standard] Select python binary Python-Binärdatei auswählen Select MISRA File Wähle MISRA-Datei Select clang path Clang-Verzeichnis auswählen StatsDialog Statistics Statistik Project Projekt Project: Projekt: Paths: Pfade: Include paths: Include-Pfade: Defines: Definitionen: Undefines: Un-Definitionen: Previous Scan Vorherige Prüfung Path Selected: Ausgewählte Pfade: Number of Files Scanned: Anzahl der geprüften Dateien: Scan Duration: Prüfungsdauer: Errors: Fehler: Warnings: Warnungen: Stylistic warnings: Stilwarnungen: Portability warnings: Portabilitätswarnungen: Performance issues: Performance-Probleme: Information messages: Informationsmeldungen: History Verlauf File: Datei: Copy to Clipboard In die Zwischenablage kopieren Pdf Export PDF-Export 1 day 1 Tag %1 days %1 Tage 1 hour 1 Stunde %1 hours %1 Stunden 1 minute 1 Minute %1 minutes %1 Minuten 1 second 1 Sekunde %1 seconds %1 Sekunden 0.%1 seconds 0,%1 Sekunden and und Export PDF Exportiere PDF Project Settings Projekteinstellungen Paths Pfade Include paths Include-Pfade Defines Definitionen Undefines Un-Definitionen Path selected Gewählte Pfade Number of files scanned Anzahl geprüfter Dateien Scan duration Prüfungsdauer Errors Fehler File: Datei: No cppcheck build dir Kein Cppcheck-Analyseverzeichnis Warnings Warnungen Style warnings Stilwarnungen Portability warnings Portabilitätswarnungen Performance warnings Performance-Warnungen Information messages Informationsmeldungen ThreadResult %1 of %2 files checked %1 von %2 Dateien geprüft TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Wechsel der Sprache der Benutzeroberfläche fehlgeschlagen: %1 Die Sprache wurde auf Englisch zurückgesetzt. Öffnen Sie den Einstellungen-Dialog um eine verfügbare Sprache auszuwählen. Cppcheck Cppcheck TxtReport inconclusive unklar VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) Alle unterstützten Dateien (%1) All files (%1) Alle Dateien (%1) cppcheck-2.7/gui/cppcheck_es.ts000066400000000000000000003507331417746362400165760ustar00rootroot00000000000000 About About Cppcheck Acerca de Cppcheck Version %1 Versión %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Una utilidad para el análisis estático de código C/C++. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright © 2007-2021 el equipo de cppcheck. This program is licensed under the terms of the GNU General Public License version 3 Este programa está licenciado bajo los términos de GNU General Public License versión 3 Visit Cppcheck homepage at %1 Visita el sitio de Cppcheck en %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Añade una aplicación Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) &Name: &Nombre: &Executable: &Ejecutable: &Parameters: &Parámetros: Browse Buscar Executable files (*.exe);;All files(*.*) Archivos ejecutables (*.exe);;Todos los archivos(*.*) Select viewer application Selecciona la aplicación para visualizar Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! ¡Debes especificar el nombre, la ruta y opcionalmente los parámetros para la aplicación! FileViewDialog Could not find the file: %1 No se ha encontrado el fichero: %1 Cppcheck Cppcheck Could not read the file: %1 No se ha podido leer el fichero: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Añadir función Function name(s) Nombre(s) de la función Number of arguments Número de argumentos LibraryDialog Library Editor Editor de bibliotecas Open Abrir Save Guardar Save as Functions Funciones Sort Add Añadir Filter: Comments noreturn False Falso True Verdadero Unknown Desconocido return value must be used ignore function in leaks checking Arguments Argumentos Edit Editar Library files (*.cfg) Archivos de biblioteca (*.cfg) Open library file Abrir archivo de biblioteca Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument Editar argumento <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool No bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null No null Not uninit No uninit String String Format string Min size of buffer Type Tipo None Ninguno argvalue mul strlen Arg Arg2 and Valid values Valores válidos LogView Checking Log Comprobando el log Clear Limpiar Save Log Guardar el log Text files (*.txt *.log);;All files (*.*) Archivos de texto (*.txt *.log);;Todos los archivos(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" No se pudo abrir el fichero para escritura: "%1" MainWindow Cppcheck Cppcheck &File &Archivo &View &Ver &Toolbars &Herramientas &Help &Ayuda &Check &Comprobar C++ standard C++ estándar &C standard C standard C estándar &Edit &Editar Standard Estándar Categories Categorías &License... &Licencia... A&uthors... A&utores... &About... &Acerca de... &Files... &Ficheros... Analyze files Check files Comprobar archivos Ctrl+F Ctrl+F &Directory... &Carpeta... Analyze directory Check directory Comprobar carpeta Ctrl+D Ctrl+D &Recheck files &Volver a revisar ficheros Ctrl+R Ctrl+R &Stop &Detener Stop analysis Stop checking Detener comprobación Esc Esc &Save results to file... &Guardar los resultados en el fichero... Ctrl+S Ctrl+S &Quit &Salir &Clear results &Limpiar resultados &Preferences &Preferencias Style warnings Advertencias de estilo Show style warnings Mostrar advertencias de estilo Errors Errores Show errors Mostrar errores Show S&cratchpad... Mostrar S&cratchpad... Information Información Show information messages Mostrar mensajes de información Portability Portabilidad Show portability warnings Mostrar advertencias de portabilidad Show Cppcheck results Clang Show Clang results &Filter &Filtro Filter results Resultados del filtro Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Plataformas C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Im&primir... Print the Current Report Imprimir el informe actual Print Pre&view... Pre&visualización de impresión... Open a Print Preview Dialog for the Current Results Abre el diálogo de previsualización de impresión para el informe actual Library Editor... Editor de bibliotecas... Open library editor Abrir el editor de bibliotecas &Check all &Seleccionar todo A&nalyze Filter Filtro &Reanalyze modified files &Recheck modified files Reanal&yze all files Ctrl+Q Style war&nings E&rrors &Uncheck all &Deseleccionar todo Collapse &all Contraer &todo &Expand all &Expandir todo &Standard &Estándar Standard items Elementos estándar &Contents &Contenidos Open the help contents Abrir la ayuda de contenidos F1 F1 Toolbar Barra de herramientas &Categories &Categorías Error categories Categorías de error &Open XML... &Abrir XML... Open P&roject File... Abrir P&royecto... Ctrl+Shift+O Sh&ow Scratchpad... &New Project File... &Nuevo Proyecto... Ctrl+Shift+N &Log View &Visor del log Log View Visor del log C&lose Project File C&errar Proyecto &Edit Project File... &Editar Proyecto... &Statistics &Estadísticas &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 Warnings Advertencias Show warnings Mostrar advertencias Performance warnings Advertencias de rendimiento Show performance warnings Mostrar advertencias de rendimiento Show &hidden Mostrar &ocultos There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. No suitable files found to check! ¡No se han encontrado ficheros para comprobar! You must close the project file before selecting new files or directories! ¡Tienes que cerrar el proyecto antes de seleccionar nuevos ficheros o carpetas! Select directory to check Selecciona una carpeta para comprobar Select configuration File not found Archivo no encontrado Bad XML XML malformado Missing attribute Falta el atributo Bad attribute value Unsupported format Formato no soportado Failed to load the selected library '%1'. %2 XML files (*.xml) Archivos XML (*.xml) Open the report file Abrir informe Checking is running. Do you want to stop the checking and exit Cppcheck? El proceso de comprobación está en curso. ¿Quieres parar la comprobación y salir del Cppcheck? License Licencia Authors Autores XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) Archivos XML versión 2 (*.xml);;Archivos XML versión 1 (*.xml);;Archivos de texto (*.txt);;Archivos CSV (*.csv) Save the report file Guardar informe Quick Filter: Filtro rápido: Select files to check Selecciona los archivos a comprobar Found project file: %1 Do you want to load this project file instead? Se encontró el fichero de proyecto: %1 ¿Quiere cargar este fichero de proyecto en su lugar? Found project files from the directory. Do you want to proceed checking without using any of these project files? Se encontraron ficheros de proyecto en el directorio. ¿Quiere proceder a comprobar sin utilizar ninguno de estos ficheros de proyecto? The library '%1' contains unknown elements: %2 La biblioteca '%1' contiene elementos deconocidos: %2 Duplicate platform type Platform type redefined Unknown element Unknown issue Error Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Los resultados actuales serán eliminados. Abrir un nuevo fichero XML eliminará los resultados actuales. ¿Desea continuar? XML files version 1 (*.xml) Archivos XML versión 1 (*.xml) XML files version 2 (*.xml) Archivos XML versión 2 (*.xml) Text files (*.txt) Ficheros de texto (*.txt) CSV files (*.csv) Ficheros CVS (*.cvs) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Ficheros de proyecto (*.cppcheck;;Todos los ficheros (*.*) Select Project File Selecciona el archivo de proyecto Project: Proyecto: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename Selecciona el nombre del proyecto No project file loaded No hay ningún proyecto cargado The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? ¡El fichero de proyecto %1 no puede ser encontrado! ¿Quiere eliminar el fichero de la lista de proyectos recientes? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Built-in Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. No se ha podido leer el fichero. Could not write the project file. No se ha podido escribir el fichero de proyecto. ProjectFile Project File Archivo de proyecto Project Proyecto Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' &Root: Root: Raíz: Libraries: Bibliotecas: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Nota: Ponga sus propios archivos .cfg en la misma carpeta que el proyecto. Debería verlos arriba. If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Selected VS Configurations Paths: Rutas: Add... Añadir... Edit Editar Remove Eliminar Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Types and Functions Analysis This is a workfolder that Cppcheck will use for various purposes. Parser Cppcheck (built in) Check that each class has a safe public interface Limit analysis Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth External tools Includes Incluir Include directories: Incluir los directorios: Up Subir Down Bajar Platform Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) Max recursion in template instantiation Warning options Root path: Filepaths in warnings will be relative to this path Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Excluir Suppressions Supresiones Suppression list: Lista de supresiones: Add Añadir Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards CERT Clang analyzer Clang-tidy Defines: Definiciones: ProjectFileDialog Project file: %1 Archivo de proyecto: %1 Select Cppcheck build dir Select include directory Selecciona una carpeta para incluir Select a directory to check Selecciona la carpeta a comprobar (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Selecciona la carpeta a ignorar Source files All files Exclude file Add Suppression Añadir supresión Select MISRA rule texts file MISRA rule texts file (%1) QDialogButtonBox OK Aceptar Cancel Cancelar Close Cerrar Save Guardar QObject Unknown language specified! ¡Idioma especificado desconocido! Language file %1 not found! ¡Fichero de idioma %1 no encontrado! Failed to load translation for language %1 from file %2 Fallo al cargar la traducción para el idioma %1 desde el fichero %2 line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Aceptar Cancel Cancelar Close Cerrar Save Guardar ResultsTree File Archivo Severity Severidad Line Línea Summary Resumen Undefined file Fichero no definido Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [No concluyente] portability portabilidad note information información debug depuración Recheck Copy filename Copiar nombre del archivo Copy full path Copiar ruta completa Copy message Copiar mensaje Copy message id Copiar id del mensaje Hide Ocultar Hide all with id Ocultar todos con el mismo id Suppress selected id(s) Open containing folder Abrir carpeta contenedora Edit contract.. Suppress Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. No se ha configurado una aplicación para editar. Configura el programa para editar en Preferencias/Aplicaciones. No default editor application selected. Please select the default editor application in preferences/Applications. No se ha definido una aplicación para editar prefeterminada. Configura el programa para editar por defecto en Preferencias/Aplicaciones. Could not find the file! ¡No se ha encontrado el fichero! Could not start %1 Please check the application path and parameters are correct. No se ha podido ejecutar %1 Por favor comprueba que la ruta a la aplicación y los parámetros son correctos. Could not find file: %1 Please select the directory where file is located. No se ha encontrado el fichero: %1 Por favor selecciona la carpeta donde se encuentra. Select Directory Selecciona carpeta Id Id Inconclusive Since date style estilo error error warning advertencia performance ajuste ResultsView Results Resultados Analysis Log Warning Details Functions Funciones Variables Only show variable names that contain text: Configured contracts: Missing contracts: No errors found, nothing to save. No se han encontrado errores, nada que guardar. Failed to save the report. Error al guardar el informe. Print Report Imprimir informe No errors found, nothing to print. No se encontraron errores, nada que imprimir. %p% (%1 of %2 files checked) %p% (%1 of %2 archivos comprobados) Cppcheck Cppcheck No errors found. No se han encontrado errores. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Se han encontrado errores, pero están configurados para que no se muestren. Para cambiar el tipo de comportamiento, abra el menú Ver. Failed to read the report. Error al leer el informe. XML format version 1 is no longer supported. Summary Resumen Message Mensaje First included by Id Id Bug hunting analysis is incomplete Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename nombre de archivo Check Comprobar Settings Preferences Preferencias General General Ideal count: Cantidad ideal: Force checking all #ifdef configurations Forzar comprobación de todas las configuraciones #ifdef Display error Id in column "Id" Mostrar el Id del error en la columna "Id" Enable inline suppressions Habilitar supresiones inline Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Rutas Include paths: Rutas incluidas: Add... Añadir... Edit... Editar... Set as default Definir por defecto Language Idioma Advanced Avanzado &Show inconclusive errors &Mostrar errores no concluyentes S&how internal warnings in log M&ostrar advertencias internas en el log Number of threads: Número de hilos: Show full path of files Mostrar la ruta completa de los ficheros Show "No errors found" message when no errors found Mostrar el mensaje "No se han encontrado errores" Edit Editar Remove Eliminar Applications Aplicaciones Reports Informes Save all errors when creating report Guardar todos los errores cuando se cree el informe Save full path to files in reports Guardar la ruta completa en los ficheros de informes SettingsDialog N/A N/A The executable file "%1" is not available Add a new application Añadir una nueva aplicación Modify an application Modificar una aplicación [Default] [Default] [Predeterminada] Select python binary Select MISRA File Select clang path Select include directory Seleccionar carpeta a incluir StatsDialog Statistics Estadísticas Project Proyecto Project: Proyecto: Paths: Rutas: Include paths: Incluye las rutas: Defines: Definiciones: Undefines: Previous Scan Análisis anterior Path Selected: Ruta seleccionada: Number of Files Scanned: Número de archivos analizados: Scan Duration: Duración del análisis: Errors: Errores: Warnings: Advertencias: Stylistic warnings: Advertencias de estilo: Portability warnings: Advertencias de portabilidad: Performance issues: Problemas de rendimiento: Information messages: Mensajes de información: History File: Copy to Clipboard Copiar al portapapeles Pdf Export 1 day 1 día %1 days %1 días 1 hour 1 hora %1 hours %1 horas 1 minute 1 minuto %1 minutes %1 minutos 1 second 1 segundo %1 seconds %1 segundos 0.%1 seconds 0.%1 segundos and y Export PDF Project Settings Preferencias del proyecto Paths Rutas Include paths Incluye las rutas Defines Definiciones Undefines Path selected Ruta seleccionada Number of files scanned Número de archivos analizados Scan duration Duración del análisis Errors Errores File: No cppcheck build dir Warnings Advertencias Style warnings Advertencias de estilo Portability warnings Advertencias de portabilidad Performance warnings Advertencias de rendimiento Information messages Mensajes de información ThreadResult %1 of %2 files checked %1 de %2 archivos comprobados TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Ocurrió un error al cambiar el idioma de la interfaz gráfica: %1 El idioma de la interfaz gráfica ha sido cambiado a Inglés. Abra la ventana de Preferencias para seleccionar alguno de los idiomas disponibles. Cppcheck Cppcheck TxtReport inconclusive no concluyente VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_fi.ts000066400000000000000000003302701417746362400165570ustar00rootroot00000000000000 About About Cppcheck Tietoa ohjelmasta Cppcheck Version %1 Versio %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Työkalu C/C++ koodin staattiseen analysointiin. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright (C) 2007-2021 Daniel Marjamäki ja cppcheck tiimi. This program is licensed under the terms of the GNU General Public License version 3 Tämä ohjelma on lisensoitu GNU General Public lisenssin version 3 alaisuuteen Visit Cppcheck homepage at %1 Cppcheckin kotisivu löytyy osoitteesta %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Lisää uusi ohjelma Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) &Name: &Executable: &Parameters: Browse Selaa Executable files (*.exe);;All files(*.*) Suoritettavat tiedostot (*.exe);;Kaikki tiedostot(*.*) Select viewer application Valitse ohjelma jolla avata virhetiedosto Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! FileViewDialog Could not find the file: %1 Could not find the file: Tiedostoa %1 ei löytynyt Cppcheck Cppcheck Could not read the file: %1 Tiedoston %1 lukeminen epäonnistui FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Cppcheck Cppcheck MainWindow Cppcheck Cppcheck A&nalyze Standard Vakio &File &Tiedosto &View &Näytä &Toolbars &Check &Tarkista C++ standard &C standard C standard &Edit &Muokkaa &License... &Lisenssi... A&uthors... &Tekijät... &About... &Tietoa ohjelmasta Cppcheck... &Files... &Tiedostot... Analyze files Check files Ctrl+F Ctrl+F &Directory... &Hakemisto... Analyze directory Check directory Ctrl+D Ctrl+D &Recheck files Tarkista tiedostot &uudelleen Ctrl+R Ctrl+R &Stop &Pysäytä Stop analysis Stop checking Esc Esc &Save results to file... &Tallenna tulokset tiedostoon... Ctrl+S Ctrl+S &Quit &Lopeta &Clear results &Tyhjennä tulokset &Preferences &Asetukset Show errors Show warnings Show performance warnings Show &hidden Information Show information messages Show portability warnings Show Cppcheck results Clang Show Clang results &Filter Filter results Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor &Check all &Valitse kaikki Filter &Reanalyze modified files &Recheck modified files Reanal&yze all files Ctrl+Q Style war&nings E&rrors &Uncheck all &Poista kaikista valinta Collapse &all &Pienennä kaikki &Expand all &Laajenna kaikki &Standard Standard items Toolbar &Categories Error categories &Open XML... Open P&roject File... Ctrl+Shift+O Sh&ow Scratchpad... &New Project File... Ctrl+Shift+N &Log View Log View C&lose Project File &Edit Project File... &Statistics &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++20 &Contents Categories Show style warnings Open the help contents F1 &Help &Ohje Select directory to check Valitse tarkistettava hakemisto No suitable files found to check! Tarkistettavaksi sopivia tiedostoja ei löytynyt! Quick Filter: Select configuration Found project file: %1 Do you want to load this project file instead? File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 License Lisenssi Authors Tekijät XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML-tiedostot (*.xml);;Tekstitiedostot (*.txt);;CSV-tiedostot (*.csv) Save the report file Tallenna raportti XML files (*.xml) XML-tiedostot (*xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. You must close the project file before selecting new files or directories! Select files to check Valitse tarkistettavat tiedostot The library '%1' contains unknown elements: %2 Unsupported format Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Open the report file Text files (*.txt) Tekstitiedostot (*.txt) CSV files (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Select Project File Project: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename No project file loaded The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Native Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Project Cppcheck Cppcheck ProjectFile Project File Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Selected VS Configurations Paths: Add... Edit Remove Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Up Down Platform Analysis This is a workfolder that Cppcheck will use for various purposes. Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth Max recursion in template instantiation Warning options Root path: Filepaths in warnings will be relative to this path Warning tags (separated by semicolon) External tools Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Types and Functions Libraries Parser Cppcheck (built in) Check that each class has a safe public interface Limit analysis Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... Suppressions Add Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards CERT Clang analyzer Clang-tidy Defines: ProjectFileDialog Project file: %1 Select Cppcheck build dir Select include directory Select a directory to check (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Source files All files Exclude file Select MISRA rule texts file MISRA rule texts file (%1) QObject Unknown language specified! Language file %1 not found! Language file %1.qm not found! Käännöstiedostoa %1 ei löytynyt! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Käänöksen lataaminen kielelle %1 tiedostosta %2 epäonnistui line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Close Save ResultsTree File Tiedosto Severity Tyyppi Line Rivi Summary Undefined file Määrittelemätön tiedosto Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. debug note Recheck Copy filename Kopioi tiedostonimi Copy full path Kopioi tiedoston koko polku Hide Hide all with id Suppress selected id(s) Open containing folder Edit contract.. Suppress Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Voit asetuksista määritellä muita ohjelmia joilla avata tämän virheen sisältävän tiedoston. No default editor application selected. Please select the default editor application in preferences/Applications. Could not find the file! Could not start %1 Please check the application path and parameters are correct. Ohjelman %1 käynnistäminen epäonnistui Tarkista että ohjelman polku ja parametrit ovat oikeat. Select Directory Id Inconclusive Since date style Tyyli error Yleinen warning performance portability information ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) Cppcheck Cppcheck No errors found. Virheitä ei löytynyt. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Virheitä löytyi, mutta asetuksissa kyseiset virheet on määritelty piilotettavaksi. Määrittääksesi minkä tyyppisiä virheitä näytetään, avaa näkymä valikko. Failed to read the report. XML format version 1 is no longer supported. First included by Id Bug hunting analysis is incomplete Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Virheitä ei löytynyt, ei mitään tallennettavaa. Failed to save the report. Raportin tallentaminen epäonnistui. Results Tulokset Analysis Log Warning Details Functions Variables Only show variable names that contain text: Configured contracts: Missing contracts: ScratchPad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Check Settings Preferences Asetukset General Yleiset Add... Number of threads: Säikeiden lukumäärä: Ideal count: Force checking all #ifdef configurations Check all #ifdef configurations Tarkista kaikki #ifdef kombinaatiot Show full path of files Näytä tiedostojen täysi polku Show "No errors found" message when no errors found Näytä "virheitä ei löytynyt"-viesti jos virheitä ei löydy Display error Id in column "Id" Enable inline suppressions Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Remove Applications Ohjelmat Edit... Set as default Reports Raportit Save all errors when creating report Tallenna kaikki virheet raporttia luodessa Save full path to files in reports Tallenna tiedostojen koko polku raportteihin Language SettingsDialog N/A The executable file "%1" is not available Add a new application Lisää uusi ohjelma Modify an application Muokkaa ohjelmaa [Default] [Default] Select python binary Select MISRA File Select clang path StatsDialog Statistics Project Project: Paths: Include paths: Defines: Undefines: Previous Scan Path Selected: Number of Files Scanned: Scan Duration: Errors: Warnings: Stylistic warnings: Portability warnings: Performance issues: Information messages: History File: Copy to Clipboard Pdf Export 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Export PDF Project Settings Paths Include paths Defines Undefines Path selected Number of files scanned Scan duration Errors File: No cppcheck build dir Warnings Style warnings Portability warnings Performance warnings Information messages ThreadResult %1 of %2 files checked TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Cppcheck Cppcheck TxtReport inconclusive VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_fr.ts000066400000000000000000003404401417746362400165700ustar00rootroot00000000000000 About About Cppcheck A propos Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Un outil d'analyse statique de code C/C++. This program is licensed under the terms of the GNU General Public License version 3 Ce programme est sous licence GNU General Public License version 3 Visit Cppcheck homepage at %1 Visitez le site Cppcheck : %1 Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Ajouter une application Browse Parcourir Executable files (*.exe);;All files(*.*) Fichier exécutable (*.exe);;Tous les fichiers(*.*) Select viewer application Sélection de l'application Cppcheck &Executable: &Exécutable : &Parameters: &Paramètres : Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Vous pouvez ajouter une application capable d'ouvrir les fichiers d'erreurs. Spécifiez le nom de l'application, la commande à exécuter, ainsi que les paramètres de la commande Les textes en paramètres sont remplacés par les valeurs appropriées lorsque l'application est exécutée : (fichier) - Fichier contenant l'erreur (ligne) - Numéro de ligne contenant l'erreur (message) - Message d'erreur (sévérité) - Sévérité de l'erreur Exemple pour ouvrir un fichier avec Kate et se déplacer vers une ligne spécifique : Exécutable : kate Paramètres : -l(ligne) (fichier) &Name: &Nom : You must specify a name, a path and optionally parameters for the application! Vous devez spécifier un nom, un chemin, et eventuellement des paramètres en option à l'application ! FileViewDialog Could not find the file: %1 Ne trouve pas le fichier : %1 Cppcheck Could not read the file: %1 Ne peut pas lire le fichier : %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck LibraryAddFunctionDialog Add function Ajouter une fonction Function name(s) Nom(s) de la fonction Number of arguments Nombre d'arguments LibraryDialog Library Editor Open Save Sauvegarder Functions Add Ajouter noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Editer Library files (*.cfg) Open library file Sort Filter: Comments Save as Cppcheck Save the library as Failed to load %1. %2. Cannot open file %1. Cannot save file %1. LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values MainWindow Cppcheck &File &Fichier &View &Affichage &Help &Aide &Check &Vérifier &Edit &Édition Standard Standard &License... &Licence... A&uthors... A&uteurs... &About... À &Propos... &Files... &Fichiers... Ctrl+F &Directory... &Répertoires... Ctrl+D &Recheck files &Revérifier les fichiers Ctrl+R &Stop &Arrêter Esc &Save results to file... &Sauvegarder les résultats dans un fichier... Ctrl+S &Quit &Quitter &Clear results &Effacer les résultats &Preferences &Préférences &Check all &Tout cocher &Uncheck all &Tout décocher Collapse &all &Tout réduire &Expand all &Tout dérouler &Contents &Contenus Open the help contents Ouvir l'aide F1 No suitable files found to check! Pas de fichiers trouvés à vérifier ! Select directory to check Sélectionner le répertoire à vérifier License Licence Authors Auteurs Save the report file Sauvegarder le rapport XML files (*.xml) Fichiers XML (*.xml) Text files (*.txt) Fichiers Texte (*.txt) CSV files (*.csv) Fichiers CSV (*.csv) &Toolbars &Boite à outils Categories Catégories Check files Vérifier les fichiers Check directory Vérifier un répertoire Stop checking Arrêter la vérification Style warnings Avertissement de style Show style warnings Afficher les avertissements de style Errors Erreurs Show errors Afficher les erreurs &Standard Standard items Toolbar &Categories Error categories &Open XML... &Ouvrir un fichier XML... Open P&roject File... Ouvrir un P&rojet... &New Project File... &Nouveau Projet... &Log View &Journal Log View Journal C&lose Project File F&ermer le projet &Edit Project File... &Editer le projet &Statistics Statistiques Warnings Avertissements Show warnings Afficher les avertissements Performance warnings Avertissements de performance Show performance warnings Afficher les avertissements de performance Show &hidden Information Information Show information messages Afficher les messages d'information Portability Portabilité Show portability warnings Afficher les problèmes de portabilité You must close the project file before selecting new files or directories! Vous devez d'abord fermer le projet avant de choisir des fichiers/répertoires Open the report file Ouvrir le rapport Checking is running. Do you want to stop the checking and exit Cppcheck? Vérification en cours. Voulez-vous arrêter la vérification et quitter CppCheck ? Project files (*.cppcheck);;All files(*.*) Select Project File To check the project using addons, you need a build directory. Select Project Filename No project file loaded There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. &Filter &Filtre Filter results Quick Filter: Filtre rapide : Found project file: %1 Do you want to load this project file instead? Project: Projet : The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Filter Filtre Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Les résultats courant seront effacés. L'ouverture d'un nouveau fichier XML effacera les resultats. Voulez-vous confirmar l'opération ? Select files to check Sélectionner les fichiers à vérifier Cppcheck GUI - Command line parameters C++ standard Error Erreur File not found Fichier introuvable Bad XML Mauvais fichier XML Missing attribute Attribut manquant Bad attribute value Mauvaise valeur d'attribut Failed to load the selected library '%1'. %2 Echec lors du chargement de la bibliothèque '%1'. %2 Unsupported format Format non supporté The library '%1' contains unknown elements: %2 La bibliothèque '%1' contient des éléments inconnus: %2 Duplicate platform type Platform type redefined &Print... &Imprimer... Print the Current Report Imprimer le rapport Print Pre&view... Apercu d'impression... Open a Print Preview Dialog for the Current Results Open library editor Auto-detect language Auto-detection du langage &Recheck modified files &Revérifier les fichiers modifiés &Recheck all files &Revérifier tous les fichiers Unknown element Unknown issue Select configuration Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Build dir '%1' does not exist, create it? Analyze files Analyze directory &Reanalyze modified files Stop analysis XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) No suitable files found to analyze! Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? A&nalyze &C standard Reanal&yze all files Ctrl+Q Style war&nings E&rrors Ctrl+Shift+O Sh&ow Scratchpad... Ctrl+Shift+N &Warnings Per&formance warnings &Information &Portability Show Cppcheck results Clang Show Clang results P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 Failed to import '%1', analysis is stopped Project files (*.cppcheck) Reanalyze and check library Check configuration (defines, includes) C++17 C++20 C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Native Project Could not read the project file. Impossible de lire le fichier projet. Could not write the project file. Impossible d'écrire dans le fichier projet. ProjectFile Project File Fichier Projet Paths: Chemins : Defines: Project Projet Add... Ajouter... Edit Editer Remove Supprimer Includes Inclusions Include directories: Inclure les répertoires Root: Répertoire racine Up Monter Down Descendre Exclude Exclure Libraries: Bibliothèques Suppressions Suppressions Suppression list: Liste de suppressions Add Ajouter Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. ... Include Paths: Paths and Defines <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Analyze all Visual Studio configurations Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Selected VS Configurations Types and Functions Libraries Parser Cppcheck (built in) Check that each class has a safe public interface Limit analysis Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards CERT Clang analyzer Clang-tidy Browse... Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Platform This is a workfolder that Cppcheck will use for various purposes. Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) Max recursion in template instantiation Warning options Filepaths in warnings will be relative to this path If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> External tools Import Project (Visual studio / compile database/ Borland C++ Builder 6) Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Analysis Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth ProjectFileDialog Project file: %1 Fichier projet : %1 Select include directory Selectionner un répertoire à inclure Select directory to ignore Selectionner un répertoire à ignorer Select a directory to check Selectionner un répertoire à vérifier Select Cppcheck build dir Import Project Clang-tidy (not found) (no rule texts file) Source files All files Exclude file Select MISRA rule texts file MISRA rule texts file (%1) Visual Studio Compile database Borland C++ Builder 6 QDialogButtonBox OK OK Cancel Annuler Close Fermer Save Sauvegarder QObject Language file %1 not found! Fichier de langue %1 non trouvé ! Failed to load translation for language %1 from file %2 Erreur lors du chargement de la langue %1 depuis le fichier %2 Unknown language specified! line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark Class Foreground Color QPlatformTheme OK OK Cancel Annuler Close Fermer Save Sauvegarder ResultsTree File Fichier Severity Sévérité Line Ligne Undefined file Fichier indéterminé Copy filename Copier le nom du fichier Copy full path Copier le chemin complet Copy message Copier le message Cppcheck Could not start %1 Please check the application path and parameters are correct. Ne peut pas démarrer %1 Merci de vérifier que le chemin de l'application et que les paramètres sont corrects. style erreur de style error erreur Summary Résumé Hide Cacher Could not find the file! Fichier introuvable ! Could not find file: %1 Please select the directory where file is located. Fichier introuvable: %1 Veuillez sélectionner le répertoire où est situé le fichier. Select Directory Selectionner dossier warning avertissement performance performance portability portabilité information information debug débogage Edit contract.. Suppress No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. No default editor application selected. Please select the default editor application in preferences/Applications. Id Id Copy message id Copier l'identifiant du message Hide all with id Open containing folder Ouvrir l'emplacement du fichier Inconclusive Recheck Revérifier note Suppress selected id(s) Tag No tag Since date Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. Copy ResultsView Results Résultats Cppcheck No errors found. Pas d'erreurs trouvées. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Des erreurs ont été trouvées mais sont configurées pour rester cachées. Pour configurer les erreurs affichées, ouvrez le menu d'affichage. Bug hunting analysis is incomplete No errors found, nothing to save. Pas d'erreurs trouvées, rien à sauvegarder. Failed to save the report. Erreur lors de la sauvegarde du rapport. Failed to read the report. Erreur lors de la lecture du rapport Summary Résumé Message Message %p% (%1 of %2 files checked) %p% (%1 fichiers sur %2 vérifiés) Id Id Print Report Imprimer le rapport No errors found, nothing to print. Aucune erreur trouvée. Il n'y a rien à imprimer First included by XML format version 1 is no longer supported. Analysis Log Warning Details Functions Variables Only show variable names that contain text: Configured contracts: Missing contracts: Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad filename Check Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": Settings Preferences Préférences General Général Number of threads: Nombre de fils : Show full path of files Montrer le chemin complet des fichiers Show "No errors found" message when no errors found Afficher un message "Pas d'erreur trouvée" lorsque aucune erreur est trouvée Applications Applications Reports Rapports Save all errors when creating report Sauvegarder toutes les erreurs lorsqu'un rapport est créé Save full path to files in reports Sauvegarder le chemin complet des fichiers dans les rapports Include paths: Inclure les chemins Add... Ajouter... Ideal count: Force checking all #ifdef configurations Enable inline suppressions Language Langue Paths Chemins Edit Editer Remove Supprimer Edit... Editer... Set as default Display error Id in column "Id" Afficher l'identifiant d'erreur Id dans la colonne "Id" Check for inconclusive errors also Show internal warnings in log Montrer les avertissements internes dans le journal Show statistics on check completion Addons Python binary (leave this empty to use python in the PATH) ... Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Code Editor Code Editor Style Default Light Style Default Dark Style Custom System Style SettingsDialog Add a new application Ajouter une nouvelle application Modify an application Modifier une application N/A Select include directory Selectionner un répertoire à inclure [Default] [Default] The executable file "%1" is not available Select python binary Select clang path Select MISRA File StatsDialog Statistics Statistiques Project Projet Project: Projet : Paths: Chemins : Include paths: Inclure les chemins : Defines: Previous Scan Analyse précédente Path Selected: Chemin sélectionné : Number of Files Scanned: Nombre de fichiers analysés : Scan Duration: Durée de l'analyse : Errors: Erreurs : Warnings: Avertissements Stylistic warnings: Avertissements de style Portability warnings: Avertissements de portabilité Performance issues: Problème de performance Information messages: Messages d'information : Copy to Clipboard Copier vers le presse-papier 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Project Settings Paths Chemins Include paths Defines Path selected Number of files scanned Scan duration Errors Erreurs Warnings Avertissements Style warnings Avertissement de style Portability warnings Performance warnings Avertissements de performance Information messages Pdf Export Export PDF History File: File: No cppcheck build dir Undefines: Undefines ThreadResult %1 of %2 files checked TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Cppcheck TxtReport inconclusive VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_it.ts000066400000000000000000003513301417746362400165750ustar00rootroot00000000000000 About About Cppcheck Informazioni su Cppcheck Version %1 Versione %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Uno strumento per l'analisi statica di codice C/C++ Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright © 2007-2021 il team Cppcheck. This program is licensed under the terms of the GNU General Public License version 3 Questo programma è rilasciato sotto i termini della GNU General Public License versione 3 Visit Cppcheck homepage at %1 Visita la pagina iniziale di Cppcheck all'indirizzo: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Aggiungi un'applicazione Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Qui puoi aggiungere un'applicazione che può aprire i file contenente l'errore. Specifica un nome per l'applicazione, l'eseguibile dell'applicazione e i parametri di comando per l'applicazione. I seguenti testi nei parametri sono sostituiti con appropriati valori quando l'applicazione è eseguita: (file) - Il nome del file contenente l'errore (line) - La linea contenente l'errore (message) - Messaggio d'errore (severity) - Severità dell'errore Un'esempio di apertura di un file con Kate e lo scorrimento alla giusta linea: Eseguibile: kate Parametri: -l(line) (file) &Name: &Nome: &Executable: &Eseguibile: &Parameters: &Parametri: Browse Esplora Executable files (*.exe);;All files(*.*) File di esecuzione (*.exe);;Tutti i file(*.*) Select viewer application Seleziona l'applicazione di lettura Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Devi specificare un nome, un percorso ed, opzionalmente, i parametri per l'applicazione! FileViewDialog Could not find the file: %1 File non trovato: %1 Cppcheck Cppcheck Could not read the file: %1 Non è stato possibile leggere il file: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Modifica Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log Log sulla scansione Clear Cancella Save Log Salva il rapporto Text files (*.txt *.log);;All files (*.*) File di testo (*.txt *.log);;Tutti i files(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Non è stato possibile aprire il file per la scrittura: "%1" MainWindow Cppcheck Cppcheck A&nalyze Standard Standard &File &File &View &Visualizza &Toolbars &Barre degli strumenti &Check &Scansiona C++ standard &C standard C standard &Edit &Modifica &License... &Licenza... A&uthors... A&utori... &About... I&nformazioni su... &Files... &File... Analyze files Check files Scansiona i file Ctrl+F Ctrl+F &Directory... &Cartella... Analyze directory Check directory Scansiona la cartella Ctrl+D Ctrl+D &Recheck files &Riscansiona i file Ctrl+R Ctrl+R &Stop &Ferma Stop analysis Stop checking Ferma la scansione Esc Esc &Save results to file... &Salva i risultati nel file... Ctrl+S Ctrl+S &Quit &Esci &Clear results &Cancella i risultati &Preferences &Preferenze Errors Errori Show errors Mostra gli errori Show S&cratchpad... Mostra il blocchetto per appunti... Warnings Avvisi Show warnings Mostra gli avvisi Performance warnings Avvisi sulle prestazioni Show performance warnings Mostra gli avvisi sulle prestazioni Show &hidden Mostra &i nascosti Information Informazione Show information messages Mostra messaggi di informazione Portability Portabilità Show portability warnings Mostra gli avvisi sulla portabilità Show Cppcheck results Clang Show Clang results &Filter &Filtro Filter results Filtra i risultati Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Piattaforme C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor &Check all &Seleziona tutto Filter Filtro &Reanalyze modified files &Recheck modified files Reanal&yze all files Ctrl+Q Style war&nings E&rrors &Uncheck all &Deseleziona tutto Collapse &all Riduci &tutto &Expand all &Espandi tutto &Standard &Standard Standard items Oggetti standard Toolbar Barra degli strumenti &Categories &Categorie Error categories Categorie di errore &Open XML... &Apri XML... Open P&roject File... Apri file di p&rogetto... Ctrl+Shift+O Sh&ow Scratchpad... &New Project File... &Nuovo file di progetto... Ctrl+Shift+N &Log View &Visualizza il rapporto Log View Visualizza il rapporto C&lose Project File C&hiudi il file di progetto &Edit Project File... &Modifica il file di progetto... &Statistics &Statistiche &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 &Contents &Contenuti Categories Categorie Style warnings Avvisi sullo stile Show style warnings Mostra gli avvisi sullo stile Open the help contents Apri i contenuti di aiuto F1 F1 &Help &Aiuto Select directory to check Seleziona una cartella da scansionare No suitable files found to check! Nessun file trovato idoneo alla scansione! Quick Filter: Rapido filtro: Select configuration Found project file: %1 Do you want to load this project file instead? Trovato il file di progetto: %1 Vuoi piuttosto caricare questo file di progetto? Found project files from the directory. Do you want to proceed checking without using any of these project files? Trovati file di progetto dalla directory. Vuoi procedere alla scansione senza usare qualcuno di questi file di progetto? File not found Bad XML Missing attribute Bad attribute value Unsupported format Failed to load the selected library '%1'. %2 License Licenza Authors Autori XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) File XML Versione 2 (*.xml);;File XML Versione 1 (*.xml);;File di testo (*.txt);;File CSV (*.csv) Save the report file Salva il file di rapporto XML files (*.xml) File XML (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. C'è stato un problema con il caricamento delle impostazioni delle applicazioni editor. Probabilmente ciò è avvenuto perché le impostazioni sono state modificate tra le versioni di Cppcheck. Per favore controlla (e sistema) le impostazioni delle applicazioni editor, altrimenti il programma editor può non partire correttamente. You must close the project file before selecting new files or directories! Devi chiudere il file di progetto prima di selezionare nuovi file o cartelle! Select files to check Seleziona i file da scansionare The library '%1' contains unknown elements: %2 Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? I risultati correnti verranno ripuliti. L'apertura di un nuovo file XML ripulirà i risultati correnti. Vuoi procedere? Open the report file Apri il file di rapporto Checking is running. Do you want to stop the checking and exit Cppcheck? La scansione è in esecuzione. Vuoi fermare la scansione ed uscire da Cppcheck? XML files version 1 (*.xml) Files XML versione 1 (*.xml) XML files version 2 (*.xml) Files XML versione 2 (*.xml) Text files (*.txt) File di testo (*.txt) CSV files (*.csv) Files CSV (*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Fallito il tentativo di cambio della lingua dell'interfaccia utente: %1 L'interfaccia utente è stata risettata in Inglese. Apri la finestra di dialogo Preferenze per selezionare una qualunque lingua a disposizione. Project files (*.cppcheck);;All files(*.*) Files di progetto (*.cppcheck);;Tutti i files(*.*) Select Project File Seleziona il file di progetto Project: Progetto: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename Seleziona il nome del file di progetto No project file loaded Nessun file di progetto caricato The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Il file di progetto %1 non è stato trovato! Vuoi rimuovere il file dalla lista dei progetti recentemente usati? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Built-in Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Non è stato possibile leggere il file di progetto. Could not write the project file. Non è stato possibile scrivere il file di progetto. ProjectFile Project File File di progetto Project Progetto Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' &Root: Root: Root: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Selected VS Configurations Paths: Percorsi: Add... Aggiungi... Edit Modifica Remove Rimuovi Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Types and Functions Analysis This is a workfolder that Cppcheck will use for various purposes. Parser Cppcheck (built in) Check that each class has a safe public interface Limit analysis Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth External tools Includes Inclusioni Include directories: Cartelle di inclusione: Up Su Down Giù Platform Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) Max recursion in template instantiation Warning options Root path: Filepaths in warnings will be relative to this path Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Escludi Suppressions Add Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards CERT Clang analyzer Clang-tidy Defines: Definizioni: ProjectFileDialog Project file: %1 File di progetto: %1 Select Cppcheck build dir Select include directory Seleziona la cartella da includere Select a directory to check Seleziona una cartella da scansionare (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Seleziona la cartella da ignorare Source files All files Exclude file Select MISRA rule texts file MISRA rule texts file (%1) QDialogButtonBox Close Chiudi QObject Unknown language specified! Lingua specificata sconosciuta! Language file %1 not found! Il file di lingua %1 non trovato! Failed to load translation for language %1 from file %2 Fallito il tentativo di aprire la traduzione per la lingua %1 dal file %2 line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Close Chiudi Save ResultsTree File File Severity Severità Line Linea Summary Riassunto Undefined file File indefinito Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [Inconcludente] debug debug note Recheck Copy filename Copia nome file Copy full path Copia tutto il percorso Copy message Copia messaggio Copy message id Copia id del messaggio Hide Nascondi Hide all with id Suppress selected id(s) Open containing folder Edit contract.. Suppress Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Nessun'applicazione di scrittura è stato configurato. Configura l'applicazione di scrittura per Cppcheck in Preferenze/Applicazioni. No default editor application selected. Please select the default editor application in preferences/Applications. Nessun'applicazione di scrittura predefinito Keine è stato selezionato. Per favore seleziona l'applicazione di scrittura predefinito in Preferenze/Applicazioni. Could not find the file! Non è stato possibile trovare il file! Could not start %1 Please check the application path and parameters are correct. Non è stato possibile avviare %1 Per favore verifica che il percorso dell'applicazione e i parametri siano corretti. Could not find file: %1 Please select the directory where file is located. Non è stato possibile trovare il file: %1 Per favore selezioa la cartella dove il file è posizionato. Select Directory Seleziona Cartella Id Id Inconclusive Since date style stile error errore warning avviso performance performance portability portabilità information Informazione ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) %p% (%1 su %2 file scansionati) Cppcheck Cppcheck No errors found. Nessun errore trovato. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Sono stati trovati errori, ma sono stati configurati per essere nascosti. Per vedere il tipo di errori che sono mostrati, apri il menu Visualizza. Failed to read the report. Apertura del report fallito. XML format version 1 is no longer supported. Summary Riassunto Message Messaggio First included by Id Id Bug hunting analysis is incomplete Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Nessun errore trovato, nulla da salvare. Failed to save the report. Salvataggio del report fallito. Results Risultati Analysis Log Warning Details Functions Variables Only show variable names that contain text: Configured contracts: Missing contracts: ScratchPad Scratchpad Blocchetto per appunti Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Nome file Check Scansiona Settings Preferences Preferenze General Generale Include paths: Percorsi d'inclusione: Add... Aggiungi... Number of threads: Numero di threads: Ideal count: Numero ideale: Force checking all #ifdef configurations Forza la scansione di tutte le configurazioni #ifdef Show full path of files Mostra tutto il percorso dei files Show "No errors found" message when no errors found Mostra il messaggio "Nessun errore trovato" quando nessun errore è stato trovato Display error Id in column "Id" Mostra l'id dell'errore nella colonna "Id" Enable inline suppressions Abilita le soppressioni Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Percorsi Edit Modifica Remove Rimuovi Applications Applicazioni Edit... Modifica... Set as default Imposta come predefinito Reports Rapporti Save all errors when creating report Salva tutti gli errori quando viene creato il rapporto Save full path to files in reports Salva tutto il percorso ai files nei rapporti Language Lingua Advanced Avanzate &Show inconclusive errors &Mostra errori inconcludenti S&how internal warnings in log &Mostra avvisi interni nel log SettingsDialog N/A N/A The executable file "%1" is not available Add a new application Aggiungi una nuova applicazione Modify an application Modifica un'applicazione [Default] [Default] [Predefinito] Select python binary Select MISRA File Select clang path Select include directory Seleziona la cartella da includere StatsDialog Statistics Statistiche Project Progetto Project: Progetto: Paths: Percorsi: Include paths: Percorsi di inclusione: Defines: Definizioni: Undefines: Previous Scan Precedente Scansione Path Selected: Selezionato percorso: Number of Files Scanned: Numero di Files Scansionati: Scan Duration: Durata della scansione: Errors: Errori: Warnings: Avvisi: Stylistic warnings: Avvisi sullo stile: Portability warnings: Avvisi sulla portabilità: Performance issues: Casi sulla performance: Information messages: Messaggi di informazione: History File: Copy to Clipboard Copia negli Appunti Pdf Export 1 day 1 giorno %1 days %1 giorni 1 hour 1 ora %1 hours %1 ore 1 minute 1 minuto %1 minutes %1 minuti 1 second 1 secondo %1 seconds %1 secondi 0.%1 seconds 0,%1 secondi and e Export PDF Project Settings Impostazioni progetto Paths Percorsi Include paths Percorsi di inclusione Defines Definizioni Undefines Path selected Selezionato percorso Number of files scanned Numero di file scansionati Scan duration Durata della scansione Errors Errori File: No cppcheck build dir Warnings Avvisi Style warnings Stilwarnungen Portability warnings Avvisi sulla portabilità Performance warnings Avvisi sulle performance Information messages Messaggi di informazione ThreadResult %1 of %2 files checked %1 su %2 file scansionati TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Fallito il tentativo di cambio della lingua dell'interfaccia utente: %1 L'interfaccia utente è stata risettata in Inglese. Apri la finestra di dialogo Preferenze per selezionare una qualunque lingua a disposizione. Cppcheck Cppcheck TxtReport inconclusive inconcludente VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_ja.ts000066400000000000000000004030401417746362400165470ustar00rootroot00000000000000 About About Cppcheck cppcheckについて Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. CppcheckはC/C++ 静的コード解析ツールです. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright © 2007-%1 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 本ソフトウェアはGNU General Public License Version3 ライセンスの元で配布されます Visit Cppcheck homepage at %1 Cppcheckのホームページはこちら %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> <html><head/><body> <p>私達は以下のライブラリを使用しています。ここで感謝の意を表明します。</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> ApplicationDialog Add an application アプリケーションの追加 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) fix Japanese word 名前 to 表示名 ここにエラー指摘のあるファイルを開くアプリケーションを追加できます。そのアプリケーションの表示名、実行ファイル名、コマンドラインパラメータを指定してください。 パラメータ中の以下の文字列を使用してパラメータ(Parameters:)に設定します。これらの文字列はアプリケーションが実行されたときに、適切な値に変換されます。: (file) - エラー指摘のあるファイル (line) - エラー指摘のある行 (message) - エラー指摘メッセージ (severity) - エラー指摘重大度 Kate テキストエディタでファイルを開き、該当する行に移動する例: Executable: kate Parameters: -l(line) (file) &Name: 表示名(&N): &Executable: 実行ファイルのパス(&E): &Parameters: パラメータ(&P): Browse 参照 Executable files (*.exe);;All files(*.*) 実行ファイル (*.exe);;すべてのファイル(*.*) Select viewer application 表示アプリケーションの選択 Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! アプリケーションの表示名と実行ファイルのパスと(オプションの)引数を指定してください! FileViewDialog Could not find the file: %1 ファイル:%1 が見つかりません Cppcheck Cppcheck Could not read the file: %1 ファイル:%1 が読み込めません FunctionContractDialog Function contract 関数の構成 Name 名前 Requirements for parameters パラメータの要求事項 HelpDialog Cppcheck GUI help Cppcheck GUI ヘルプ Contents 目次 Index インデックス Helpfile '%1' was not found ヘルプファイル '%1' が見つかりません Cppcheck Cppcheck LibraryAddFunctionDialog Add function 関数の追加 Function name(s) 関数の名称 Number of arguments 引数の数 LibraryDialog Library Editor ライブラリエディタ Open 開く Save 保存する Save as 別名で保存する Functions 関数 Sort ソート Add 追加 Filter: フィルタ: Comments コメント noreturn noreturn(返り値なし) False False(偽) True True(真) Unknown Unknown(不明) return value must be used 返り値は使用されなければならない ignore function in leaks checking リークの解析中に無視する関数 Arguments Arguments(引数) Edit 編集 Library files (*.cfg) ライブラリファイル(*.cfg) Open library file ライブラリファイルを開く Cppcheck Cppcheck Cannot open file %1. Can not open file %1. ファイルが見つかりません %1。 Failed to load %1. %2. 読み込みに失敗しました(%1.%2)。 Cannot save file %1. Can not save file %1. ファイルが保存できません %1。 Save the library as このライブラリに名前をつけて保存する LibraryEditArgDialog Edit argument 引数の編集 <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> <html><head/><body> <p>ブール値は許可されていますか? 例えば、比較の結果または '!' 演算子</p> <p>典型的に引数がポインタやサイズを表す場合、これを設定します。</p> <p>例:</p> <pre> memcmp(x, y, i == 123); // 最後の引数は、ブール型であってはならない </body></html> Not bool 非ブール型 非bool値 <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> <html><head/><body> <p>null値が許可されていますか?</p> <p>典型的には、nullを渡してはいけないポインタのパラメータに使用してください。</p> <p>例:</p> <pre> strcpy(x,y); // x も y も null であってはならない。</pre> </body></html> Not null 非NULL Not uninit 未初期化 String 文字列 Format string フォーマット文字列 Min size of buffer バッファの最小サイズ Type Type(型) None None(無) argvalue argvalue(引数の値) constant constant(定数) mul mul(積) strlen strlen(文字数) Arg Arg(引数) Arg2 Arg2(第二引数) and and(和) Valid values 妥当な値 LogView Checking Log Cppcheck ログ Clear 消去 Save Log ログ保存 Text files (*.txt *.log);;All files (*.*) テキストファイル (*.txt *.log);;すべてのファイル(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" ファイルを書き込みできない MainWindow Cppcheck Cppcheck &File ファイル(&F) &View 表示(&V) &Toolbars ツールバー(&T) &Help ヘルプ(&H) &Check 解析(&A) C++ standard C++標準 &C standard C standard &C標準 &Edit 編集(&E) Standard 言語規格 Categories カテゴリ &License... ライセンス(&L)... A&uthors... 作者(&u)... &About... Cppcheckについて(&A)... &Files... ファイル選択(&F)... Analyze files Check files ファイルをチェックする Ctrl+F Ctrl+F &Directory... ディレクトリ選択(&D)... Analyze directory Check directory ディレクトリをチェックする Ctrl+D Ctrl+D &Recheck files 再チェック(&R) Ctrl+R Ctrl+R &Reanalyze all files &Recheck all files 全ファイル再チェック &Stop 停止(&S) Stop analysis Stop checking チェックを停止する Esc Esc &Save results to file... 結果をファイルに保存(&S)... Ctrl+S Ctrl+S &Quit 終了(&Q) &Clear results 結果をクリア(&C) &Preferences 設定(&P) Style warnings スタイル警告 Show style warnings スタイル警告を表示 Errors エラー Show errors エラーを表示 Show S&cratchpad... スクラッチパッドを表示 Information 情報 Show information messages 情報メッセージを表示 Portability 移植可能性 Show portability warnings 移植可能性の問題を表示 Show Cppcheck results Cppcheck結果を表示する Clang Clang Show Clang results Clangの結果を表示 &Filter フィルター(&F) Filter results フィルタ結果 Windows 32-bit ANSI Windows 32-bit ANSIエンコード Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms プラットフォーム C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... 印刷(&P)... Print the Current Report 現在のレポートを印刷 Print Pre&view... 印刷プレビュー(&v)... Open a Print Preview Dialog for the Current Results 現在のレポートをプレビュー表示 Library Editor... ライブラリの編集 Open library editor ライブラリエディタを開く Auto-detect language 言語を自動検出 C&lose Project File プロジェクトを閉じる(&l) &Edit Project File... プロジェクトの編集(&E)... &Statistics 統計情報(&S) Warnings 警告 Show warnings 警告を表示 Performance warnings パフォーマンス警告 Show performance warnings パフォーマンス警告を表示 Show &hidden 非表示を表示(&h) &Check all すべてのエラーを表示(&C) A&nalyze チェック(&n) Filter フィルター &Reanalyze modified files &Recheck modified files 変更ありファイルを再解析(&R) Reanal&yze all files 全ファイル再解析(&y) Ctrl+Q Ctrl+Q Style war&nings スタイル警告(&n) E&rrors エラー(&r) &Uncheck all すべてのエラーを非表示(&U) Collapse &all ツリーを折り畳む(&a) &Expand all ツリーを展開(&E) &Standard 言語規格(&S) Standard items 標準項目 &Contents コンテンツ(&C) Open the help contents ヘルプファイルを開く F1 F1 Toolbar ツールバー &Categories カテゴリ(&C) Error categories エラーカテゴリ &Open XML... XMLを開く(&O)... Open P&roject File... プロジェクトを開く(&R)... Ctrl+Shift+O Ctrl+Shift+O Sh&ow Scratchpad... スクラッチパッドを表示(&o)... &New Project File... 新規プロジェクト(&N)... Ctrl+Shift+N Ctrl+Shift+N &Log View ログを表示(&L) Log View ログ表示 &Warnings 警告(&W) Per&formance warnings パフォーマンス警告(&f) &Information 情報(&I) &Portability 移植可能性(&P) P&latforms プラットフォーム(&l) C++&11 C++11(&1) C&99 C99(&9) &Posix Posix(&P) C&11 C11(&1) &C89 C89(&C) &C++03 C++03(&C) &Library Editor... ライブラリエディタ(&L)... &Auto-detect language 自動言語検出(&A) &Enforce C++ C++ 強制(&E) E&nforce C C 強制(&n) C++14 C++14 Reanalyze and check library ライブラリを再チェックする Check configuration (defines, includes) チェックの設定(define、インクルード) C++17 C++17 C++20 C++20 There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. エディタアプリの設定の読み込みで問題が発生しました。 Cppcheckの古いバージョンの設定には互換性がありません。エディタアプリケーションの設定を確認して修正してください、そうしないと正しく起動できないかもしれません。 No suitable files found to check! 解析可能なファイルではありません You must close the project file before selecting new files or directories! 新しいファイル/ディレクトリをチェックするには現在のプロジェクトを閉じてください! C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) C/C++ソースコード、プロジェクトファイル、Visual Studioソリューション(%1 %2 *.sln *.vcxproj) Select directory to check チェック対象のディレクトリを選択 Quick Filter: クイックフィルタ: Select files to check チェック対象のファイルを選択 C/C++ Source (%1) C/C++ ソース (%1) Select configuration コンフィグレーションの選択 Found project file: %1 Do you want to load this project file instead? プロジェクトファイルを検出しました: %1 現在のプロジェクトの代わりにこのプロジェクトファイルを読み込んでもかまいませんか? Found project files from the directory. Do you want to proceed checking without using any of these project files? ディレクトリからプロジェクトファイルが検出されました。 これらのプロジェクトファイルを使用せずに解析を進めてもかまいませんか? The library '%1' contains unknown elements: %2 このライブラリ '%1' には次の不明な要素が含まれています。 %2 File not found ファイルがありません Bad XML 不正なXML Missing attribute 属性がありません Bad attribute value 不正な属性があります Unsupported format サポートされていないフォーマット Duplicate platform type プラットフォームの種類が重複しています Platform type redefined プラットフォームの種類が再定義されました Unknown element 不明な要素 Unknown issue 不明な課題 Failed to load the selected library '%1'. %2 選択したライブラリの読み込みに失敗しました '%1' %2 Error エラー Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. %1の読み込みに失敗しました。CppCheckのインストールに失敗しています。コマンドライン引数に --data-dir=<directory> を指定して、このファイルの場所を指定してください。 Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. %1のロードに失敗しました。あなたの Cppcheck は正しくインストールされていません。あなたは --data-dir=<directory> コマンドラインオプションでロードするファイルの場所を指定できます。ただし、この --data-dir はインストールスクリプトによってサポートされており、GUI版ではサポートされていません。全ての設定は調整済みでなければなりません。 Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 現在の結果を作成します。 新しくXMLファイルを開くと現在の結果が削除されます。実行しますか? XML files (*.xml) XML ファイル (*.xml) Open the report file レポートを開く Checking is running. Do you want to stop the checking and exit Cppcheck? 解析中です. 解析を停止してCppcheckを終了しますか?. License ライセンス Authors 作者 XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML ファイル (*.xml);;テキストファイル (*.txt);;CSV形式ファイル (*.csv) Save the report file レポートを保存 XML files version 1 (*.xml) XMLファイルのバージョン1 XML files version 2 (*.xml) XMLファイルのバージョン2 Text files (*.txt) テキストファイル (*.txt) CSV files (*.csv) CSV形式ファイル (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) プロジェクトファイル (*.cppcheck);;すべてのファイル(*.*) Select Project File プロジェクトファイルを選択 Project: プロジェクト: No suitable files found to analyze! チェック対象のファイルがみつかりません! C/C++ Source C/C++のソースコード Compile database コンパイルデータベース Visual Studio Visual Studio Borland C++ Builder 6 Borland C++ Builder 6 Select files to analyze チェック対象のファイルを選択 Select directory to analyze チェックするディレクトリを選択してください Select the configuration that will be analyzed チェックの設定を選択 Found project files from the directory. Do you want to proceed analysis without using any of these project files? ディレクトリ内にプロジェクトファイルがありました。 みつかったプロジェクトファイルを使用せずにチェックしますか? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? 現在の結果を作成します。 新しくXMLファイルを開くと現在の結果が削除されます。実行しますか? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? チェック中です。 チェックを中断して、Cppcheckを終了しますか? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML ファイル (*.xml);;テキストファイル (*.txt);;CSVファイル (*.csv) Build dir '%1' does not exist, create it? ビルドディレクトリ'%1'がありません。作成しますか? To check the project using addons, you need a build directory. アドオンを使用してプロジェクトをチェックするためには、ビルドディレクトリが必要です。 Failed to import '%1', analysis is stopped '%1'のインポートに失敗しました。(チェック中断) Project files (*.cppcheck) プロジェクトファイル (*.cppcheck) Select Project Filename プロジェクトファイル名を選択 No project file loaded プロジェクトファイルが読み込まれていません The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? このプロジェクトファイル %1 が見つかりません。 最近使用したプロジェクトのリストからこのファイルを取り除きますか? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI. シンタックス: cppcheck-gui [OPTIONS] [files または paths] オプション: -h, --help このヘルプを表示する。 -p <file> 指定のプロジェクトファイルを開き、チェックを開始する。 -l <file> 指定の、結果XMLファイルを開く -d <directory> フォルダを指定してチェックする。これは -l オプションで 指定した、結果XMLファイルを生成する。 -v, --version バージョンを表示する。 --data-dir=<directory> GUI のデータファイル(翻訳やcfg)のあるディレクトリを指定する。このオプションを指定した場合、GUIで起動しません。 Cppcheck GUI - Command line parameters Cppcheck GUI - コマンドラインパラメータ NewSuppressionDialog New suppression 新しい指摘の抑制 Error ID エラーID File name ファイル名 Line number 行数 Symbol name シンボル名 Edit suppression 抑制の編集 Platforms Built-in ビルトイン Native ネイティブ Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSIエンコード Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. プロジェクトファイルが読み込めませんでした Could not write the project file. プロジェクトファイルが保存できませんでした ProjectFile Project File プロジェクトファイル Project プロジェクト Paths and Defines パスと定義 Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) プロジェクトのインポート(Visual studio / compile database Borland C++ Builder 6)) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' 定義(Define)はセミコロン';'で区切る必要があります。 例: DEF1;DEF2=5;DEF3=int &Root: Root: ルート: Libraries: ライブラリ Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. カスタマイズした cfgファイルを同じフォルダにプロジェクトファイルとして保存してください。ここに表示できるようになります。 Exclude paths 除外するパス MISRA C 2012 MISRA C 2012 MISRA rule texts MISRA ルールテキスト <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>MISRA C 2012 pdfのAppendix A &quot;Summary of guidelines&quot; からテキストをコピーペーストしてください。</p></body></html> ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>選択済み:</p><p> * 全Debug と Release設定をチェックする</p><p> * 最初にマッチした Debug 設定のみチェックする</p><p><br/></p></body></html> Browse... 参照... Analyze all Visual Studio configurations Visual Studioの全ての設定をチェックする Selected VS Configurations 選択したVS設定 Paths: パス: Add... 追加... Edit 編集 Remove 取り除く Undefines: 定義取り消し(Undefines): Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 定義の取り消しはセミコロンで区切ります。例: UNDEF1;UNDEF2;UNDEF3 Include Paths: インクルードパス: Types and Functions 型と関数 Analysis チェック This is a workfolder that Cppcheck will use for various purposes. Cppcheckがさまざまな目的で使用するワークディレクトリ。 Parser パーサー Cppcheck (built in) Cppcheckビルトイン Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) ヘッダファイルのコードもチェック (通常はONにしてください、制限するときのみOFF) If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. タグが追加された場合、警告上で右クリックしてそれらのタグの中の一つを設定できます。警告を分類できます。 Exclude source files 除外するソースファイル Exclude folder... フォルダで除外... Exclude file... ファイルで除外... Clang Clang Check that code is safe コードの安全性確認 Bug hunting -- Detect all bugs. Generates mostly noise. バグハント -- 全てのバグを検出。ノイズになりがち。 Check that each class has a safe public interface クラスが安全で公開されたインターフェースをもっているか確認 Limit analysis 解析の制限 Check code in headers (slower analysis, more results) ヘッダファイルのコードもチェック(解析に時間がかかりますが結果は増えます) Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) 未使用テンプレートのコードもチェック (解析に時間がかかり、また正確性は低い) Max CTU depth CTUの最大深さ Exclude source files in paths 除外するソースファイルのPATH Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. 注意: アドオンには<a href="https://www.python.org/">Python</a> が必要です。 External tools 外部ツール Includes インクルード Include directories: インクルードディレクトリ: Up Down Checking チェック Platform プラットフォーム Clang (experimental) Clang (実験的) Normal analysis -- Avoid false positives. 通常解析--偽陽性を避ける。 Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. バグハンティング-- 不必要な指摘を含む。これはノイズが多くても全てのバグを検出する目的で使用します。 If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. 可能な限りクラスが柔軟であり堅牢であることを望む場合、公開されたインターフェースが非常に堅牢です。Cppcheckは引数があらゆる値をとりうると仮定します。 Max recursion in template instantiation テンプレートインスタンス化の最大再帰回数 Warning options 警告オプション Root path: ルートパス: Filepaths in warnings will be relative to this path 警告中のファイルパスはこのパスからの相対パスになります Warning tags (separated by semicolon) 警告タグ(セミコロン区切り) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck ビルドディレクトリ (全プログラムチェック, 差分チェック, 統計等) Libraries ライブラリ Exclude 除外する Suppressions 指摘の抑制 Suppression list: 抑制リスト Add 追加 Addons and tools アドオンとツール Addons アドオン Note: Addons require <a href="https://www.python.org/">Python</a> being installed. 注意: アドオンには<a href="https://www.python.org/">Python</a>が必要です。 Y2038 Y2038 Thread safety スレッドセーフ Coding standards コーディング標準 CERT CERT Extra Tools エクストラツール It is common best practice to use several tools. 複数ツールの併用はよい結果を生みます。 Clang analyzer Clang Analyzer Clang-tidy Clang-tidy Defines: 定義(Defines): ProjectFileDialog Project file: %1 プロジェクトファイル:%1 Select Cppcheck build dir Cppcheckビルドディレクトリ Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Visual Studio (*.sln *.vcxproj);;コンパイルデータベース (compile_commands.json) Select include directory includeディレクトリを選択 Select a directory to check チェックするディレクトリを選択してください (no rule texts file) (ルールテキストファイルがない) Clang-tidy (not found) Clang-tidy (みつかりません) Visual Studio Visual Studio Compile database コンパイルデータベース Borland C++ Builder 6 Borland C++ Builder 6 Import Project プロジェクトのインポート Select directory to ignore 除外するディレクトリを選択してください Source files ソースファイル All files 全ファイル Exclude file 除外ファイル Add Suppression 抑制する指摘を追加 Select error id suppress: 抑制するエラーID(error id)を選択してください Select MISRA rule texts file MISRAルールテキストファイルを選択 MISRA rule texts file (%1) MISRAルールテキストファイル (%1) QDialogButtonBox OK OK Cancel キャンセル Close 閉じる Save 保存する QObject Unknown language specified! 未知の言語が指定されました! Language file %1 not found! 言語ファイル %1 が見つかりません! Failed to load translation for language %1 from file %2 言語 %2 から %1 への翻訳ファイルの読み込みに失敗 line %1: Unhandled element %2 行 %1: 扱われていない要素(Unhandled element) %2 line %1: Mandatory attribute '%2' missing in '%3' 行 %1: 必須の属性 '%2' が '%3'にない (Not found) (見つかりません) Thin シン(細) ExtraLight エクストラライト Light ライト Normal ノーマル Medium メディウム DemiBold デミボールト Bold ボールド ExtraBold エクストラボールド Black Editor Foreground Color エディタの前景色 Editor Background Color エディタの背景色 Highlight Background Color ハイライトの背景色 Line Number Foreground Color 行番号の前景色 Line Number Background Color 行番号の背景色 Keyword Foreground Color キーワードの前景色 Keyword Font Weight キーワードのフォントのウェイト Class Foreground Color Class ForegroundColor クラスの前景色 Class Font Weight クラスフォントのウェイト Quote Foreground Color クォートの前景色 Quote Font Weight クォートのフォントウェイト Comment Foreground Color コメントの前景色 Comment Font Weight コメントフォントのウェイト Symbol Foreground Color シンボルの前景色 Symbol Background Color シンボルの背景色 Symbol Font Weight シンボルのフォントウェイト Set to Default Light デフォルトをライトに設定 Set to Default Dark デフォルトをダークに設定 QPlatformTheme OK OK Cancel キャンセル Close 閉じる Save 保存する ResultsTree File ファイル Severity 警告の種別 Line Summary 要約 Undefined file 未定義ファイル Copy コピー Could not find file: ファイルが見つかりません: Please select the folder '%1' フォルダ '%1' を選択してください Select Directory '%1' ディレクトリ '%1' 選択 Please select the directory where file is located. ファイルのあるディレクトリを選択してください。 [Inconclusive] [結論の出ない] debug デバッグ note 注意 Recheck 再チェック Copy filename ファイル名をコピー Copy full path フルパスをコピー Copy message メッセージをコピー Copy message id メッセージidをコピー Hide 非表示 Hide all with id IDで非表示を指定 Suppress selected id(s) 選択したidを抑制 Open containing folder 含まれるフォルダを開く Edit contract.. 関数の構成を編集。 Suppress 抑制 Tag タグ No tag タグなし Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. エディタアプリが設定されていません。 Cppcheckの「設定」からテキストファイルを編集するアプリケーションを設定してください。 No default editor application selected. Please select the default editor application in preferences/Applications. デフォルトのエディタアプリケーションが指定されていません。 設定からデフォルトのエディタアプリケーションを設定してください。 Could not find the file! ファイルが見つかりません! Could not start %1 Please check the application path and parameters are correct. %1 が実行できません。 実行ファイルパスや引数の設定を確認してください。 Could not find file: %1 Please select the directory where file is located. ファイルが見つかりません: %1 ディレクトリにファイルが存在するか確認してください。 Select Directory ディレクトリを選択 Id Id Inconclusive 結論のでない Since date 日付 style スタイル error エラー warning 警告 performance パフォーマンス portability 移植可能性 information 情報 ResultsView Results 結果 Analysis Log チェックログ Warning Details 警告の詳細 Functions 関数 Variables 変数 Only show variable names that contain text: 指定テキストを含む変数名のみ表示: Contracts 構成 Configured contracts: 設定した構成: Missing contracts: 構成なし: No errors found, nothing to save. 警告/エラーが見つからなかったため、保存しません。 Failed to save the report. レポートの保存に失敗しました。 Print Report レポートの印刷 No errors found, nothing to print. 指摘がないため、印刷するものがありません。 %p% (%1 of %2 files checked) %p% (%1 / %2 :ファイル数) Cppcheck Cppcheck No errors found. 警告/エラーは見つかりませんでした。 Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. 警告/エラーが見つかりましたが、非表示設定になっています。 Failed to read the report. レポートの読み込みに失敗. XML format version 1 is no longer supported. XML フォーマットバージョン 1 はもうサポートされていません。 Summary 内容 Message メッセージ First included by は次のものが最初にインクルードしました Id ID Bug hunting analysis is incomplete バグハントの解析は不完全です Clear Log ログの消去 Copy this Log entry このログ項目をコピー Copy complete Log ログ全体をコピー ScratchPad Scratchpad スクラッチパッド Copy or write some C/C++ code here: ここに C/C++のコードをコピーペーストまたは記入してください: Optionally enter a filename (mainly for automatic language detection) and click on "Check": オプション: ファイル名を入力(言語は自動判定)して"チェック"をクリックしてください: filename ファイル名 Check チェック Settings Preferences 設定 General 全般 Include paths: Include ディレクトリ: Add... 追加... Number of threads: 解析用のスレッド数: Ideal count: 理想的な数: Force checking all #ifdef configurations Check all #ifdef configurations すべての #ifdef をチェックする Show full path of files ファイルのフルパスを表示 Show "No errors found" message when no errors found エラーが無いときは"エラーなし"を表示 Display error Id in column "Id" エラーIDを "Id" に表示する Enable inline suppressions inline抑制を有効にする Check for inconclusive errors also 結論のでない指摘もチェックする Show statistics on check completion チェック完了時に統計情報を表示する Show internal warnings in log ログの内部警告も表示する Addons アドオン Python binary (leave this empty to use python in the PATH) Pythonインタプリタの場所(空白の場合システムのPATHから検索) ... ... MISRA addon MISRAアドオン MISRA rule texts file MISRA ルールテキストファイル <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdfのテキストをテキストファイルにコピー</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clangの場所(空白の場合システムのPATHから検索) Visual Studio headers Visual Studioのヘッダ <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Visual Studioのヘッダーファイル(セミコロン区切り';')。</p><p>Visual Studio コマンドプロンプトを開き、 &quot;SET INCLUDE&quot;. と入力後、そのパスをコピーペーストしてください。</p></body></html> Code Editor コードエディタ Code Editor Style コードエディタスタイル System Style システムのデフォルトのスタイル Default Light Style ライトスタイルをデフォルトに Default Dark Style ダークスタイルをデフォルトに Custom カスタム Paths パス Edit 編集 Remove 削除 Applications アプリケーション Edit... 編集... Set as default デフォルトとして設定 Reports レポート Save all errors when creating report レポート作成時にすべての警告/エラーを保存 Save full path to files in reports レポートにファイルのフルパスを保存 Language 言語 Advanced 高度 &Show inconclusive errors 結論の出ないのエラーを表示 SettingsDialog N/A N/A The executable file "%1" is not available 実行ファイル "%1" が利用できません Add a new application 新しいアプリケーションの追加 Modify an application アプリケーションの変更 [Default] [デフォルト] [Default] [デフォルト] Select python binary pythonの場所の選択 Select MISRA File MISRAファイルの選択 Select clang path clangのパスの選択 Select include directory include ディレクトリを選択 StatsDialog Statistics 統計情報 Project プロジェクト Project: プロジェクト: Paths: パス: Include paths: インクルードパス: Defines: 定義(define): Undefines: 定義取り消し(undef): Previous Scan 前回の解析 Path Selected: ディレクトリ選択: Number of Files Scanned: 解析済みファイル数: Scan Duration: 解析にかかった時間: Errors: エラー: Warnings: 警告: Stylistic warnings: スタイル警告: Portability warnings: 移植可能性の警告: Performance issues: パフォーマンス警告: Information messages: 情報メッセージ: History ヒストリー File: ファイル: Copy to Clipboard クリップボードにコピー Pdf Export PDF エクスポート 1 day 一日 %1 days %1日 1 hour 一時間 %1 hours %1時間 1 minute 一分 %1 minutes %1分 1 second 一秒 %1 seconds %1秒 0.%1 seconds 0.%1秒 and Export PDF PDF エクスポート Project Settings プロジェクトの設定 Paths パス Include paths インクルードパス Defines 定義(define) Undefines 定義取り消し(Undef) Path selected 選択されたパス Number of files scanned スキャンしたファイルの数 Scan duration スキャン期間 Errors エラー File: ファイル: No cppcheck build dir cppcheckビルドディレクトリがありません Warnings 警告 Style warnings スタイル警告 Portability warnings 移植可能性警告 Performance warnings パフォーマンス警告 Information messages 情報メッセージ ThreadResult %1 of %2 files checked チェック: %1 / %2 (ファイル数) TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. ユーザーインターフェースの言語 %1 への変更に失敗しました。 そのため言語を 英語にリセットします。設定ダイアログから利用可能な言語を選択してください。 Cppcheck Cppcheck TxtReport inconclusive 結論の出ない VariableContractsDialog Dialog ダイアログ You can specify min and max value for the variable here ここで変数の最小値と最大値を指定します。 Min 最小値 Max 最大値 toFilterString All supported files (%1) 全サポートファイル (%1) All files (%1) 全ファイル(%1) cppcheck-2.7/gui/cppcheck_ko.ts000066400000000000000000003450251417746362400165760ustar00rootroot00000000000000 About About Cppcheck Cppcheck 정보 Version %1 버전 %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - 정적 C/C++ 코드 분석 도구. This program is licensed under the terms of the GNU General Public License version 3 이 프로그램은 GNU General Public License version 3을 준수합니다 Visit Cppcheck homepage at %1 Cppcheck 홈페이지(%1)를 방문해보세요 Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application 응용 프로그램 추가 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) 에러 파일을 열 응용 프로그램을 추가할 수 있습니다. 응용 프로그램의 이름, 실행 파일, 명령행 인자를 입력하세요. 인자 중에서 아래와 같은 텍스트는 응용 프로그램 실행 시 해당 값으로 대치됩니다: (file) - 에러를 포함한 파일이름 (line) - 에러를 포함한 행번호 (message) - 에러 메시지 (severity) - 에러 종류 Kate로 파일을 열고, 해당 행으로 이동하는 예제: 실행파일: kate 인자: -l(line) (file) &Name: 이름(&N): &Executable: 실행 파일(&E): &Parameters: 명령행 인자(&P): Browse 찾기 Executable files (*.exe);;All files(*.*) 실행 파일(*.exe);;모든 파일(*.*) Select viewer application 뷰어 프로그램 선택 Cppcheck Cppcheck You must specify a name, a path and parameters for the application! 응용 프로그램의 이름, 경로 및 인자를 명시해야 합니다! You must specify a name, a path and optionally parameters for the application! FileViewDialog Could not find the file: %1 파일 찾기 실패: %1 Cppcheck Cppcheck Could not read the file: %1 파일 읽기 실패: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Functions Add noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit 편집 Library files (*.cfg) Open library file Sort Filter: Comments Save as Cppcheck Cppcheck Save the library as Failed to load %1. %2. Cannot open file %1. Cannot save file %1. LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log 로그 확인 Clear 지우기 Save Log 로그 저장 Text files (*.txt *.log);;All files (*.*) 텍스트 파일 (*.txt *.log);;모든 파일 (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" 기록할 파일 열기 실패: "%1" MainWindow Cppcheck Cppcheck &File 파일(&F) &View 보기(&V) &Toolbars 도구바(&T) &Help 도움말(&H) &Check 검사(&C) &Edit 편집(&E) Standard 표준 도구 Categories 분류 도구 Filter 필터 도구 &License... 저작권(&L)... A&uthors... 제작자(&u)... &About... 정보(&A)... &Files... 파일(&F)... Check files 파일 검사 Ctrl+F Ctrl+F &Directory... 디렉토리(&D)... Check directory 디렉토리 검사 Ctrl+D Ctrl+D &Recheck files 파일 재검사(&R) Ctrl+R Ctrl+R &Stop 중지(&S) Stop checking 검사 중지 Esc Esc &Save results to file... 결과를 파일에 저장(&S)... Ctrl+S Ctrl+S &Quit 종료(&Q) &Clear results 결과 지우기(&C) &Preferences 설정(&P) Style warnings 스타일 경고 Show style warnings 스타일 경고 표시 Errors 에러 Show errors 애러 표시 &Check all 전체 선택(&C) &Uncheck all 전체 해제(&U) Collapse &all 전체 접기(&A) &Expand all 전체 펼치기(&E) &Standard 표준 도구(&S) Standard items 표준 아이템 &Contents 내용(&C) Open the help contents 도움말을 엽니다 F1 F1 Toolbar 도구바 &Categories 분류 도구(&C) Error categories 에러 종류 &Open XML... XML 열기(&O)... Open P&roject File... 프로젝트 파일 열기(&R)... &New Project File... 새 프로젝트 파일(&N)... &Log View 로그 보기(&L) Log View 로그 보기 C&lose Project File 프로젝트 파일 닫기(&L) &Edit Project File... 프로젝트 파일 편집(&E)... &Statistics 통계 보기(&S) Warnings 경고 Show warnings 경고 표시 Performance warnings 성능 경고 Show performance warnings 성능 경고 표시 Show &hidden 숨기기 보기(&H) Information 정보 Show information messages 정보 표시 Portability 이식성 경고 Show portability warnings 이식성 경고 표시 &Filter 필터 도구(&F) Filter results 필터링 결과 Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms 플랫폼 C++11 C++11 C99 C99 Posix Posix Quick Filter: 빠른 필터: There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. 편집기 설정을 불러오는데 문제가 있습니다. Cppcheck 버전간 설정 방법 차이때문인 것으로 보입니다. 편집기 설정을 검사(및 수정)해주세요, 그렇지 않으면 편집기가 제대로 시작하지 않습니다. No suitable files found to check! 검사할 수 있는 파일이 없습니다! You must close the project file before selecting new files or directories! 새로운 파일이나 디렉토리를 선택하기 전에 프로젝트 파일을 닫으세요! Select directory to check 검사할 디렉토리 선택 Found project file: %1 Do you want to load this project file instead? 프로젝트 파일 존재: %1 이 프로젝트 파일을 불러오겠습니까? Found project files from the directory. Do you want to proceed checking without using any of these project files? 디렉토리에 프로젝트 파일 존재. 이 프로젝트 파일을 사용하지 않고 검사를 계속하시겠습니까? XML files (*.xml) XML 파일 (*.xml) Open the report file 보고서 파일 열기 Checking is running. Do you want to stop the checking and exit Cppcheck? 검사 중. 검사를 중지하고 Cppcheck을 종료하시겠습니까? License 저작권 Authors 제작자 XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML 파일 버전 2 (*.xml);;XML 파일 버전 1 (*.xml);;텍스트 파일 (*.txt);;CSV 파일 (*.csv) Save the report file 보고서 파일 저장 XML files version 1 (*.xml) XML 파일 버전 1 (*.xml) XML files version 2 (*.xml) XML 파일 버전 2 (*.xml) Text files (*.txt) 텍스트 파일 (*.txt) CSV files (*.csv) CSV 파일 (*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 언어 변경 실패: %1 언어가 영어로 초기화 됐습니다. 설정창을 열어서 설정 가능한 언어를 선택하세요. Project files (*.cppcheck);;All files(*.*) 프로젝트 파일 (*.cppcheck);;모든 파일(*.*) Select Project File 프로젝트 파일 선택 Project: 프로젝트: To check the project using addons, you need a build directory. Select Project Filename 프로젝트 파일이름 선택 No project file loaded 프로젝트 파일 불러오기 실패 The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? 프로젝트 파일 %1 이 존재하지 않습니다! 최근 프로젝트 목록에서 파일을 제거하시겠습니까? Select files to check 검사할 파일 선택 Cppcheck GUI - Command line parameters C++ standard C11 C11 C89 C89 C++03 C++03 Error File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 Unsupported format The library '%1' contains unknown elements: %2 Duplicate platform type Platform type redefined &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor Unknown element Unknown issue Select configuration Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Build dir '%1' does not exist, create it? Analyze files Analyze directory &Reanalyze modified files Stop analysis XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) No suitable files found to analyze! Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? A&nalyze &C standard Reanal&yze all files Ctrl+Q Style war&nings E&rrors Ctrl+Shift+O Sh&ow Scratchpad... Ctrl+Shift+N &Warnings Per&formance warnings &Information &Portability Show Cppcheck results Clang Show Clang results P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Failed to import '%1', analysis is stopped Project files (*.cppcheck) Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Built-in 내장 방식 Native Project Cppcheck Cppcheck Could not read the project file. 프로젝트 파일을 읽을 수 없습니다. Could not write the project file. 프로젝트 파일에 쓸 수 없습니다. ProjectFile Project File 프로젝트 파일 Project 프로젝트 Defines: Defines: Root: Root: Paths: 경로: Add... 추가... Edit 편집 Remove 제거 Includes Includes Include directories: Include 디렉토리: Up 위로 Down 아래로 Exclude Exclude Suppressions Add Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. ... Include Paths: Paths and Defines <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Analyze all Visual Studio configurations Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Selected VS Configurations Types and Functions Libraries Parser Cppcheck (built in) Check that each class has a safe public interface Limit analysis Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards CERT Clang analyzer Clang-tidy Browse... Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Platform This is a workfolder that Cppcheck will use for various purposes. Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) Max recursion in template instantiation Warning options Filepaths in warnings will be relative to this path If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> External tools Import Project (Visual studio / compile database/ Borland C++ Builder 6) Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Analysis Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth ProjectFileDialog Project file: %1 프로젝트 파일: %1 Select include directory Include 디렉토리 선택 Select a directory to check 검사할 디렉토리 선택 Select directory to ignore 무시할 디렉토리 선택 Select Cppcheck build dir Import Project Clang-tidy (not found) (no rule texts file) Source files All files Exclude file Select MISRA rule texts file MISRA rule texts file (%1) Visual Studio Compile database Borland C++ Builder 6 QDialogButtonBox Close 닫기 QObject Unknown language specified! 알 수 없는 언어입니다! Language file %1 not found! 언어 파일(%1)이 없습니다! Failed to load translation for language %1 from file %2 파일(%2)로부터 언어(%1) 불러오기 실패 line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark Class Foreground Color QPlatformTheme OK Cancel Close 닫기 Save ResultsTree File 파일 Severity 분류 Line Summary 요약 Undefined file 미정의된 파일 [Inconclusive] [불확실] style 스타일 error 에러 warning 경고 performance 성능 portability 이식성 information 정보 debug 디버그 Copy filename 파일이름 복사 Copy full path 전체 경로 복사 Copy message 메시지 복사 Hide 숨기기 Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. 편집기 미설정. [설정 - 응용 프로그램]에서 편집기를 설정하세요. No default editor application selected. Please select the default editor application in preferences/Applications. 기본 편집기 미선택. [설정 - 응용 프로그램]에서 기본 편집기를 선택하세요. Could not find the file! 파일을 찾을 수 없습니다! Could not start %1 Please check the application path and parameters are correct. %1을 시잘할 수 없습니다 경로와 인자가 정확한지 확인하세요. Could not find file: %1 Please select the directory where file is located. 파일 찾기 실패: %1 파일이 위치한 디렉토리를 선택하세요. Select Directory 디렉토리 선택 Id Hide all with id Open containing folder Inconclusive Recheck note Edit contract.. Suppress selected id(s) Suppress Tag No tag Since date Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. Copy ResultsView Results 결과 No errors found, nothing to save. 에러가 발견되지 않았고, 저장할 내용이 없습니다. Failed to save the report. 결과 저장 실패. %p% (%1 of %2 files checked) %p% (%2 중 %1 파일 검사됨) Cppcheck Cppcheck No errors found. 에러가 발견되지 않았습니다. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. 에러가 발견되었지만, 감추도록 설정되어 있습니다. 에러 종류를 표시하도록 설정하려면, 보기 메뉴를 선택하세요. Failed to read the report. 결과 불러오기 실패. Bug hunting analysis is incomplete Summary 요약 Message 내용 Id Print Report No errors found, nothing to print. First included by XML format version 1 is no longer supported. Analysis Log Warning Details Functions Variables Only show variable names that contain text: Configured contracts: Missing contracts: Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad filename Check 검사 Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": Settings Preferences 설정 General 일반 Number of threads: 쓰레드 수: Ideal count: 최적 값: Force checking all #ifdef configurations 모든 #ifdef 설정을 강제로 검사 Show full path of files 파일의 전체 경로 표시 Show "No errors found" message when no errors found 에러가 발견되지 않는 경우 "에러가 없습니다." 메시지 표시 Enable inline suppressions Inline suppression 사용 Paths 경로 Include paths: Include 경로: Add... 추가... Edit 편집 Remove 제거 Applications 응용 프로그램 Edit... 편집... Set as default 기본으로 지정 Reports 보고서 Save all errors when creating report 보고서 생성 시 모든 에러 저장 Save full path to files in reports 보고서에 파일의 전체 경로 저장 Language 언어 Advanced 고급 &Show inconclusive errors 불확실한 에러 표시(&S) S&how internal warnings in log 로그에 내부 경고 표시(&H) Display error Id in column "Id" Check for inconclusive errors also Show internal warnings in log Show statistics on check completion Addons Python binary (leave this empty to use python in the PATH) ... Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Code Editor Code Editor Style Default Light Style Default Dark Style Custom System Style SettingsDialog N/A N/A The executable file "%1" is not available Add a new application 새 응용 프로그램 추가 Modify an application 응용 프로그램 편집 [Default] [기본] Select include directory Include 디렉토리 선택 [Default] Select python binary Select clang path Select MISRA File StatsDialog Statistics 통계 Project 프로젝트 Project: 프로젝트: Paths: 경로: Include paths: Include 경로: Defines: Defines: Previous Scan 직전 검사 Path Selected: 선택된 경로: Number of Files Scanned: 검사된 파일 수: Scan Duration: 검사 시간: Errors: 에러: Warnings: 경고: Stylistic warnings: 스타일 경고: Portability warnings: 이식성 경고: Performance issues: 성능 경고: Information messages: 정보 메시지: Copy to Clipboard 클립보드에 복사 1 day 1일 %1 days %1일 1 hour 1시간 %1 hours %1시간 1 minute 1분 %1 minutes %1분 1 second 1초 %1 seconds %1초 0.%1 seconds 0.%1초 and Project Settings 프로젝트 설정 Paths 경로 Include paths Include 경로 Defines Defines Path selected 선택된 경로 Number of files scanned 검사된 파일 수 Scan duration 검사 시간 Errors 에러 Warnings 경고 Style warnings 스타일 경고 Portability warnings 이식성 경고 Performance warnings 성능 경고 Information messages 정보 메시지 Pdf Export Export PDF History File: File: No cppcheck build dir Undefines: Undefines ThreadResult %1 of %2 files checked %2 중 %1 파일 검사됨 TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 언어 변경 실패: %1 언어가 영어로 초기화 됐습니다. 설정창을 열어서 설정 가능한 언어를 선택하세요. Cppcheck Cppcheck TxtReport inconclusive 불확실 VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_nl.ts000066400000000000000000003474661417746362400166110ustar00rootroot00000000000000 About About Cppcheck Over Cppcheck Version %1 Versie %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Een tool voor statische C/C++ code analyse. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright © 2007-2021 het cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 Dit programma is beschikbaar onder te termen van de GNU General Public License versie 3 Visit Cppcheck homepage at %1 Bezoek de Cppcheck homepage op %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Voeg een nieuwe applicatie toe Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Hier kunt u toepassingen toevoegen die de foute bestanden kan openen. Geef naam op van de toepassing, het uitvoerbare bestand en command lijn parameters voor de toepassing De volgende tekst in de parameters word vervangen door de juiste waarden wanneer de toepassing wordt uitgevoerd: (bestand) - Bestandsnaam waarin de fout zit (lijn) - Lijnnummer waar de fout zit (bericht) Foutmelding (ernst) Ernst van foutmelding Voorbeeld een bestand openen met KonOs2 en laat KonOs2 scrollen naar de juiste lijn: Uitvoerbaar: KonOs2 Parameters: -l(lijn) (bestand) &Name: &Naam: &Executable: &Uitvoerbaar: &Parameters: Browse Bladeren Executable files (*.exe);;All files(*.*) Uitvoerbare bestanden (*.exe);;Alle bestanden(*.*) Select viewer application Selecteer applicatie Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Geef een naam op, een pad en eventueel parameters voor de toepassing! FileViewDialog Could not find the file: %1 Could not find the file: Kon het bestand niet vinden: %1 Cppcheck Cppcheck Could not read the file: %1 Kon het bestand niet lezen: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Opslaan Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Bewerk Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log Controleer log Clear Wis Save Log Opslaan log Text files (*.txt *.log);;All files (*.*) Tekst bestanden (*.txt *.log);;Alle bestanden(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Kan bestand: "%1" niet openen om te schrijven MainWindow Cppcheck Cppcheck A&nalyze Standard Standaard &File &Bestand &View &Weergave &Toolbars &Werkbalken &Check &Controleer C++ standard C++standaard &C standard C standard C standaard &Edit Be&werken &License... &Licentie... A&uthors... A&uteurs... &About... &Over... &Files... &Bestanden... Analyze files Check files Controleer bestanden Ctrl+F Ctrl+F &Directory... &Mappen... Analyze directory Check directory Controleer Map Ctrl+D Ctrl+D &Recheck files &Opnieuw controleren Ctrl+R Ctrl+R &Stop &Stop Stop analysis Stop checking Stop controle Esc Esc &Save results to file... &Resultaten opslaan... Ctrl+S Ctrl+S &Quit &Afsluiten &Clear results &Resultaten wissen &Preferences &Voorkeuren Errors Fouten Show errors Toon fouten Show S&cratchpad... Toon S&cratchpad... Warnings Waarschuwingen Show warnings Toon waarschuwingen Performance warnings Presentatie waarschuwingen Show performance warnings Toon presentatie waarschuwingen Show &hidden Toon &verborgen Information Informatie Show information messages Toon informatie bericht Portability Portabiliteit Show portability warnings Toon portabiliteit waarschuwingen Show Cppcheck results Clang Show Clang results &Filter &Filter Filter results Filter resultaten Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor &Check all &Controleer alles Filter &Reanalyze modified files &Recheck modified files Reanal&yze all files Ctrl+Q Style war&nings E&rrors &Uncheck all Selecteer &niets Collapse &all Alles Inkl&appen &Expand all Alles &Uitklappen &Standard &Standaard Standard items Standaard items Toolbar Werkbalk &Categories &Categorieën Error categories Foute Categorieën &Open XML... Open P&roject File... Open P&oject bestand... Ctrl+Shift+O Sh&ow Scratchpad... &New Project File... &Nieuw Project Bestand... Ctrl+Shift+N &Log View &Log weergave Log View Log weergave C&lose Project File &Sluit Project Bestand &Edit Project File... &Bewerk Project Bestand... &Statistics &Statistieken &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++20 &Contents &Inhoud Categories Categorieën Style warnings Stijl waarschuwingen Show style warnings Toon stijl waarschuwingen Open the help contents Open de help inhoud F1 &Help &Help Select directory to check Selecteer een map om te controleren No suitable files found to check! Geen geschikte bestanden gevonden om te controleren! Quick Filter: Snel Filter: Select configuration Found project file: %1 Do you want to load this project file instead? Project bestand gevonden: %1 Wilt u dit project laden in plaats van? Found project files from the directory. Do you want to proceed checking without using any of these project files? Project bestanden gevonden in de map. Wil je verder wilt gaan zonder controle van deze project bestanden? File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 License Licentie Authors Auteurs XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML bestanden (*.xml);;Tekst bestanden (*.txt);;CSV bestanden (*.csv) Save the report file Rapport opslaan XML files (*.xml) XML bestanden (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Er was een probleem met het laden van de bewerker instellingen. Dit is waarschijnlijk omdat de instellingen zijn gewijzigd tussen de versies van cppcheck. Controleer (en maak) de bewerker instellingen, anders zal de bewerker niet correct starten. You must close the project file before selecting new files or directories! Je moet project bestanden sluiten voordat je nieuwe bestanden of mappen selekteerd! Select files to check Selecteer bestanden om te controleren The library '%1' contains unknown elements: %2 Unsupported format Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Huidige resultaten zullen worden gewist Een nieuw XML-bestand openen zal de huidige resultaten wissen Wilt u verder gaan? Open the report file Open het rapport bestand Checking is running. Do you want to stop the checking and exit Cppcheck? Het controleren loopt. Wil je het controleren stoppen en Cppcheck sluiten? XML files version 1 (*.xml) XML files version 1 (*.xml) XML files version 2 (*.xml) XML files version 2 (*.xml) Text files (*.txt) Tekst bestanden (*.txt) CSV files (*.csv) CSV bestanden (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Project bestanden (*.cppcheck);;Alle bestanden(*.*) Select Project File Selecteer project bestand Project: Project: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename Selecteer project bestandsnaam No project file loaded Geen project bestand geladen The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Het project bestand %1 Kan niet worden gevonden! Wilt u het bestand van de onlangs gebruikte project verwijderen -lijst? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck GUI. Syntax: ....cppcheck-gui [Opies] [bestanden of paden] Opties: .....-h, --help Print deze help .....-p <bestand>......Open project bestand en start de controle .....-l <bestand>......Open gegeven resultaten xml bestand .....-d <map> Geef de map aan wat gecontroleerd werd om de xml resultaten te genereren met gespecificeerde -l .....-v,.--versie Toon versie van programma Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters Cppcheck GUI - Command lijn parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Gemaakt in Native Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Kon project bestand niet lezen. Could not write the project file. Kon niet naar project bestand schrijven. ProjectFile Project File Project Bestand Project Project Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' &Root: Root: Hoofdmap: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Selected VS Configurations Paths: Paden: Add... Toevoegen... Edit Bewerk Remove Verwijder Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Types and Functions Analysis This is a workfolder that Cppcheck will use for various purposes. Parser Cppcheck (built in) Check that each class has a safe public interface Limit analysis Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth External tools Includes Inclusief Include directories: Include mappen: Up Omhoog Down Omlaag Platform Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) Max recursion in template instantiation Warning options Root path: Filepaths in warnings will be relative to this path Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Exclusief Suppressions Add Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards CERT Clang analyzer Clang-tidy Defines: Omschrijft: ProjectFileDialog Project file: %1 Project Bestand %1 Select Cppcheck build dir Select include directory Selecteer include map Select a directory to check Selecteer een map om te controleren (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Selecteer een map om te negeren Source files All files Exclude file Select MISRA rule texts file MISRA rule texts file (%1) QDialogButtonBox Cancel Annuleer Close Sluit Save Opslaan QObject Unknown language specified! Onbekende taal gekozen! Language file %1 not found! Language file %1.qm not found! Kon het taalbestand niet vinden: %1! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Kon de vertaling voor taal %1 in bestand %2 niet laden line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Annuleer Close Sluit Save Opslaan ResultsTree File Bestand Severity Ernst Line Regel Summary Overzicht Undefined file Niet gedefinieerd bestand Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [Onduidelijk] debug note Recheck Copy filename Kopier bestandsnaam Copy full path Kopieer volledig pad Copy message Kopieer bericht Copy message id Kopieer bericht id Hide Verberg Hide all with id Verberg alles met id Suppress selected id(s) Open containing folder Edit contract.. Suppress Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Er is geen bewerker toepassing geconfigureerd. Configureer de bewerker toepassing voor cppcheck in voorkeuren/Applicaties. No default editor application selected. Please select the default editor application in preferences/Applications. Geen standaard bewerker geselecteerd. Selecteer de standaard bewerker in voorkeuren/Applicaties. Could not find the file! Kon het bestand niet vinden! Could not start %1 Please check the application path and parameters are correct. Kon applicatie %1 niet starten Gelieve te controleren of de het pad en de parameters correct zijn. Could not find file: %1 Please select the directory where file is located. %1 Selecteer de map waarin het bestand zich bevindt. Select Directory Selecteer map Id Id Inconclusive Since date style Stijlfouten error Fouten warning Waarschuwing performance Presentatie portability Portabiliteit information Informatie ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) %p% (%1 van %2 bestanden gecontroleerd) Cppcheck Cppcheck No errors found. Geen fouten gevonden. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Fouten werden gevonden, maar volgens de configuratie zijn deze verborgen. Gebruik het uitzicht menu om te selecteren welke fouten getoond worden. Failed to read the report. Kon rapport niet lezen. XML format version 1 is no longer supported. Summary Overzicht Message Bericht First included by Id Id Bug hunting analysis is incomplete Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Geen fouten gevonden; geen data om op te slaan. Failed to save the report. Kon het rapport niet opslaan. Results Resultaten Analysis Log Warning Details Functions Variables Only show variable names that contain text: Configured contracts: Missing contracts: ScratchPad Scratchpad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename bestandsnaam Check Controleer Settings Preferences Instellingen General Algemeen Include paths: Include paden: Add... Toevoegen... Number of threads: Aantal threads: Ideal count: Ideale telling: Force checking all #ifdef configurations Check all #ifdef configurations Controleer alle #ifdef combinaties Show full path of files Toon het volledige pad van bestanden Show "No errors found" message when no errors found Toon "Geen fouten gevonden" indien geen fouten gevonden werden Display error Id in column "Id" Toon fout ld in kolom "Id" Enable inline suppressions Schakel inline suppressies in Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Paden Edit Bewerk Remove Verwijder Applications Applicaties Edit... Bewerk... Set as default Instellen als standaard Reports Rapporten Save all errors when creating report Alle fouten opslaan Save full path to files in reports Volledig pad opslaan Language Taal Advanced Geavanceerd &Show inconclusive errors &Toon onduidelijke fouten S&how internal warnings in log T&oon interne waarschuwingen in log SettingsDialog N/A The executable file "%1" is not available Add a new application Nieuwe applicatie toevoegen Modify an application Applicatie wijzigen [Default] [Default] [Standaard] Select python binary Select MISRA File Select clang path Select include directory Selecteer include map StatsDialog Statistics Statistieken Project Project Project: Project: Paths: Paden: Include paths: Bevat paden: Defines: Omschrijft: Undefines: Previous Scan Vorige scan Path Selected: Pad Geselekteerd: Number of Files Scanned: Aantal bestanden gescanned: Scan Duration: Scan tijd: Errors: Fouten: Warnings: Waarschuwingen: Stylistic warnings: Stilistisch waarschuwingen: Portability warnings: Portabiliteit waarschuwingen: Performance issues: Presentatie problemen: Information messages: Informatie bericht: History File: Copy to Clipboard Kopieer naar Clipbord Pdf Export 1 day 1 dag %1 days %1 dagen 1 hour 1 uur %1 hours %1 uren 1 minute 1 minuut %1 minutes %1 minuten 1 second 1 seconde %1 seconds %1 secondes 0.%1 seconds 0.%1 secondes and en Export PDF Project Settings Project instellingen Paths Paden Include paths Bevat paden Defines Omschrijft Undefines Path selected Pad Geselekteerd Number of files scanned Aantal bestanden gescanned Scan duration Scan tijd Errors Fouten File: No cppcheck build dir Warnings Waarschuwingen Style warnings Stijl waarschuwingen Portability warnings Portabiliteit waarschuwingen Performance warnings Presentatie waarschuwingen Information messages Informatie bericht ThreadResult %1 of %2 files checked %1 van %2 bestanden gecontroleerd TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Mislukt om de gebruikers taal te wijzigen: %1 De gebruikerstaal is gereset naar Engels. Open het dialoogvenster om een van de beschikbare talen te selecteren. Cppcheck Cppcheck TxtReport inconclusive Onduidelijk VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_ru.ts000066400000000000000000004106631417746362400166140ustar00rootroot00000000000000 About About Cppcheck О Cppcheck Version %1 Версия %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - программа для статического анализа кода на языках С/С++. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright © 2007-2021 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 Эта программа распространяется на условиях лицензии GNU General Public License, версии 3 Visit Cppcheck homepage at %1 Посетите домашнюю страницу: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> <html><head/><body> <p>Создано при использовании библиотек:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Добавление приложения Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Вы можете добавить приложение, которое можно будет использовать для открытия файла с ошибками. Задайте название приложения, путь до него и параметры командной строки. Следующие текстовые параметры будут заменены реальными значениями при запуске приложения: (file) - файл, содержащий ошибку (line) - номер строки с ошибкой (message) - текст ошибки (severity) - тип ошибки Пример открытия файла с помощью Kate (скролл переместится на нужную строчку): Программа: kate Параметры: -l(line) (file) &Name: &Название: &Executable: &Программа: &Parameters: &Параметры: Browse Просмотреть Executable files (*.exe);;All files(*.*) Выполняемые файлы (*.exe);;Все файлы(*.*) Select viewer application Выберите приложение Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Вы должны задать название и путь к приложению! FileViewDialog Could not find the file: %1 Could not find the file: Невозможно найти файл: %1 Cppcheck Cppcheck Could not read the file: %1 Невозможно прочитать файл: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Добавить функцию Function name(s) Имя(имена) функции Number of arguments Количество аргументов LibraryDialog Library Editor Редактор библиотек Open Открыть Save Сохранить Save as Сохранить как Functions Функции Sort Сортировать Add Добавить Filter: Фильтр: Comments Комментарии noreturn False True Unknown return value must be used должно быть использовано возвращаемое значение ignore function in leaks checking пропускать функцию при проверке на утечки Arguments Аргументы Edit Изменить Library files (*.cfg) Файлы библиотек (*.cfg) Open library file Открыть файл библиотеки Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Невозможно открыть файл %1. Failed to load %1. %2. Ошибка загрузки %1. %2. Cannot save file %1. Can not save file %1. Невозможно сохранить файл %1. Save the library as Сохранить библиотеку как LibraryEditArgDialog Edit argument Редактировать аргумент <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log Лог проверки Clear Очистить Save Log Сохранить Text files (*.txt *.log);;All files (*.*) Текстовые файлы (*.txt *.log);;Все файлы (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Не удалось записать в файл: "%1" MainWindow Cppcheck Cppcheck A&nalyze Анализ Standard Стандартные &File &Файл &View &Вид &Toolbars &Панель инструментов &Check &Проверить C++ standard Стандарт C++ &C standard C standard &Стандарт C &Edit &Правка &License... &Лицензия... A&uthors... &Авторы... &About... &О программе... &Files... &Файлы... Analyze files Check files Проверить файлы Ctrl+F Ctrl+F &Directory... &Каталог... Analyze directory Check directory Проверка каталога Ctrl+D Ctrl+D &Recheck files &Перепроверить файлы Ctrl+R Ctrl+R &Reanalyze all files &Recheck all files Заново проверить все файлы &Stop Остановить Stop analysis Stop checking Остановить проверку Esc Esc &Save results to file... Сохранить отчёт в файл... Ctrl+S Ctrl+S &Quit Выход &Clear results Очистить отчёт &Preferences Параметры Errors Ошибки Show errors Показать ошибки Show S&cratchpad... Показать блокнот Warnings Предупреждения Show warnings Показать предупреждения Performance warnings Предупреждения производительности Show performance warnings Показать предупреждения производительности Show &hidden Показать скрытые Information Информационные сообщения Show information messages Показать информационные сообщения Portability Переносимость Show portability warnings Показать предупреждения переносимости Show Cppcheck results Просмотр результатов Cppcheck Clang Clang Show Clang results Просмотр результатов Clang &Filter Фильтр Filter results Результаты фильтрации Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Платформы C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Печать... Print the Current Report Напечатать текущий отчет Print Pre&view... Предварительный просмотр... Open a Print Preview Dialog for the Current Results Открыть диалог печати для текущих результатов Library Editor... Редактор библиотек... Open library editor Открыть редактор библиотек Auto-detect language Автоопределение языка Enforce C++ Принудительно C++ Enforce C Принудительно C &Check all Отметить все Filter Фильтр &Reanalyze modified files &Recheck modified files Заново проверить измененные файлы Reanal&yze all files Заново проверить все файлы Ctrl+Q Style war&nings Стилистические предупреждения E&rrors Ошибки &Uncheck all Сбросить все Collapse &all Свернуть все &Expand all Развернуть все &Standard Стандартные Standard items Стандартные элементы Toolbar Панель инструментов &Categories Категории Error categories Категории ошибок &Open XML... &Открыть XML... Open P&roject File... Открыть файл &проекта... Ctrl+Shift+O Sh&ow Scratchpad... Показать Блокнот &New Project File... &Новый файл проекта... Ctrl+Shift+N &Log View Посмотреть &лог Log View Посмотреть лог C&lose Project File &Закрыть файл проекта &Edit Project File... &Изменить файл проекта... &Statistics &Статистика &Warnings Предупреждения Per&formance warnings Предупреждения производительности &Information Информационные предупреждения &Portability Предупреждения переносимости P&latforms Платформы C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... Редактор библиотеки &Auto-detect language Автоматическое определение языка &Enforce C++ Принудительно C++ E&nforce C Принудительно C C++14 C++14 Reanalyze and check library Повторный анализ библиотеки Check configuration (defines, includes) Проверить конфигурацию (defines, includes) C++17 C++17 C++20 C++20 &Contents Помощь Categories Категории Style warnings Стилистические предупреждения Show style warnings Показать стилистические предупреждения Open the help contents Открыть помощь F1 F1 &Help Помощь Select directory to check Выберите каталог для проверки No suitable files found to check! Не найдено подходящих файлов для проверки! Quick Filter: Быстрый фильтр: Select configuration Выбор конфигурации Found project file: %1 Do you want to load this project file instead? Найден файл проекта: %1 Вы хотите загрузить этот проект? Found project files from the directory. Do you want to proceed checking without using any of these project files? Найдены файлы проекта из каталога. Вы хотите продолжить проверку, не используя ни одного из этих файлов проекта? File not found Файл не найден Bad XML Некорректный XML Missing attribute Пропущен атрибут Bad attribute value Некорректное значение атрибута Unsupported format Неподдерживаемый формат Failed to load the selected library '%1'. %2 Не удалось загрузить выбранную библиотеку '%1'. %2 Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Не удалось загрузить %1. Установленный Cppcheck поврежден. Вы можете использовать ключ --data-dir=<directory> в командной строке, чтобы указать, где расположен этот файл. License Лицензия Authors Авторы XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML файлы версии 2 (*.xml);;XML файлы версии 1 (*.xml);;Текстовые файлы (*.txt);;CSV файлы (*.csv) Save the report file Сохранить файл с отчетом XML files (*.xml) XML-файлы (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Возникла проблема при загрузке настроек программы. Возможно, это связано с изменениями в версии программы. Пожалуйста, проверьте (и исправьте) настройки приложения. You must close the project file before selecting new files or directories! Вы должны закрыть проект перед выбором новых файлов или каталогов! Select files to check Выберите файлы для проверки The library '%1' contains unknown elements: %2 Библиотека '%1' содержит неизвестные элементы: %2 Duplicate platform type Дубликат типа платформы Platform type redefined Переобъявление типа платформы Unknown element Неизвестный элемент Unknown issue Неизвестная проблема Error Ошибка Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Невозможно загрузить %1. Cppcheck установлен некорректно. Вы можете использовать --data-dir=<directory> в командной строке для указания расположения файлов конфигурации. Обратите внимание, что --data-dir предназначен для использования сценариями установки. При включении данной опции, графический интерфейс пользователя не запускается. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Текущие результаты будут очищены. Открытые нового XML файла приведет к очистке текущих результатов. Продолжить? Open the report file Открыть файл с отчетом Checking is running. Do you want to stop the checking and exit Cppcheck? Идет проверка. Вы хотите завершить проверку и выйти? XML files version 1 (*.xml) XML файлы версии 1 (*.xml) XML files version 2 (*.xml) XML файлы версии 2 (*.xml) Text files (*.txt) Текстовые файлы (*.txt) CSV files (*.csv) CSV файлы(*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Project files (*.cppcheck);;All files(*.*) Файлы проекта (*.cppcheck);;Все файлы(*.*) Select Project File Выберите файл проекта Project: Проект: No suitable files found to analyze! Не найдено подходящих файлов для анализа C/C++ Source Исходный код C/C++ Compile database Visual Studio Visual Studio Borland C++ Builder 6 Borland C++ Builder 6 Select files to analyze Выбор файлов для анализа Select directory to analyze Выбор каталога для анализа Select the configuration that will be analyzed Выбор используемой конфигурации Found project files from the directory. Do you want to proceed analysis without using any of these project files? Обнаружены файлы проекты из каталога. Вы хотите продолжить анализ без использования этих файлов проекта? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Текущие результаты будут очищены. Открытие нового XML-файла приведет к очистке текущих результатов. Вы хотите продолжить? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? Анализатор запущен. Вы хотите остановить анализ и выйти из Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML файлы (*.xml);;Текстовые файлы (*.txt);;CSV файлы (*.csv) Build dir '%1' does not exist, create it? Директория для сборки '%1' не существует, создать? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Невозможно импортировать '%1', анализ остановлен Project files (*.cppcheck) Файлы проекта (*.cppcheck) Select Project Filename Выберите имя файла для проекта No project file loaded Файл с проектом не загружен The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Файл с проектом %1 не найден! Хотите удалить его из списка проектов? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck Графический Интерфейс Пользователя . Синтаксис: cppcheck-gui [ОПЦИИ] [файлы или пути] Опции: -h, --help Показать эту справку -p <file> Откройте данный файл проекта и начните проверять его -l <file> Откройте данные результаты xml файл -d <directory> Укажите каталог, который был проверен, чтобы генерировать результаты xml определенный с -l -v, --version Показать версию программы Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI. Синтаксис: cppcheck-gui [ОПЦИИ] [файлы или пути] Опции: -h, --help Выдать подсказку на стандартный вывод и успешно завершиться. -p <file> Открыть указанный файл проекта и начать проверку -l <file> Открыть xml-файл с полученными результатами -d <directory> Указать каталог, который был проверен для создания результатов xml, указанных с помощью -l -v, --version Выдать информацию о версии на стандартный вывод и успешно завершиться. --data-dir=<directory> Этот параметр предназначен для сценариев установки, чтобы они могли настроить каталог, в котором расположены файлы данных (конфигурация, переводы). Графический интерфейс пользователя не будет запущен, если указана эта опция. Cppcheck GUI - Command line parameters Cppcheck GUI - параметры Командной строки NewSuppressionDialog New suppression Новое подавление Error ID ID File name Имя файла Line number Номер строки Symbol name Имя символа Edit suppression Редактировать подавление Platforms Built-in Встроенная Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Не удалось прочитать файл проекта. Could not write the project file. Не удалось записать файл проекта. ProjectFile Project File Файл проекта Project Проект Paths and Defines Каталоги и определения Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Импорт проекта (Visual studio / compile database/ Borland C++ Builder 6) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Defines должны быть разделены точкой с запятой ';' &Root: Root: Корневой каталог: Libraries: Библиотеки: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Положите свои .cfg-файлы в один каталог с файлом проекта. Вы увидите их сверху. Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) Max recursion in template instantiation Filepaths in warnings will be relative to this path If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... MISRA C 2012 MISRA C 2012 MISRA rule texts Файл с текстами правил MISRA <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Скопируйте текст из Appendix A &quot;Summary of guidelines&quot; из фала правил MISRA C 2012 pdf в текстовый файл.</p></body></html> ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>Выберите:</p><p> * Анализ всех конфигураций Debug и Release</p><p> * Анализ только первой подходящей конфигурации Debug</p><p><br/></p></body></html> Browse... Обзор... Analyze all Visual Studio configurations Анализировать все конфигурации Visual Studio Selected VS Configurations Paths: Пути: Add... Добавить... Edit Изменить Remove Удалить Undefines: Удаленные макроопределения: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Удаленные макроопределения должны быть разделены точкой с запятой, например: UNDEF1;UNDEF2;UNDEF3 Include Paths: Пути заголовочных файлов: Types and Functions Analysis Анализ This is a workfolder that Cppcheck will use for various purposes. Parser Cppcheck (built in) Clang Clang Check that each class has a safe public interface Limit analysis Check code in headers (slower analysis, more results) Проверить код в заголовочных файлах Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Проверить код в неиспользуемых шаблонах Max CTU depth Максимальная глубина CTU Exclude source files in paths Исключить исходные файлы в путях External tools Внешние инструменты Includes Пути для заголовочных файлов Include directories: Пути для поиска заголовочных файлов: Up Вверх Down Вниз Checking Проверка Platform Платформа Warning options Опции предупреждений Root path: Корневой каталог: Warning tags (separated by semicolon) Теги предупреждений (через ';') Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Каталог сборки Cppcheck Libraries Библиотеки Exclude Исключенные пути Suppressions Подавления Add Добавить Addons and tools Дополнения Addons Дополнения Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards Стандарты кодирования CERT CERT Clang analyzer Clang-tidy Defines: Объявленные макроопределения: ProjectFileDialog Project file: %1 Файл проекта: %1 Select Cppcheck build dir Выбрать директорию сборки Cppcheck Select include directory Выберите директорию для поиска заголовочных файлов Select a directory to check Выберите директорию для проверки (no rule texts file) (файл с текстами правил недоступен) Clang-tidy (not found) Clang-tidy (не найден) Visual Studio Visual Studio Compile database Borland C++ Builder 6 Borland C++ Builder 6 Import Project Импорт проекта Select directory to ignore Выберите директорию, которую надо проигнорировать Source files All files Exclude file Select MISRA rule texts file Выбрать файл текстов правил MISRA MISRA rule texts file (%1) Файл текстов правил MISRA (%1) QDialogButtonBox OK OK Cancel Отмена Close Закрыть Save Сохранить QObject Unknown language specified! Неизвестный язык! Language file %1 not found! Language file %1.qm not found! Языковой файл %1 не найден! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Ошибка загрузки переводов для языка %1 из файла %2 line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) (Недоступно) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK OK Cancel Отмена Close Закрыть Save Сохранить ResultsTree File Файл Severity Важность Line Строка Summary Кратко Undefined file Неопределенный файл Copy Копировать Could not find file: Невозможно найти файл: Please select the folder '%1' Выберите каталог '%1' Select Directory '%1' Выбрать каталог '%1' Please select the directory where file is located. Укажите каталог с расположением файла. [Inconclusive] [Неубедительный] debug отлаживать note заметка Recheck Проверить заново Copy filename Скопировать имя файла Copy full path Скопировать полный путь Copy message Скопировать сообщение Copy message id Скопировать номер сообщения Hide Скрыть Hide all with id Скрыть все с id Suppress selected id(s) Подавить выбранные id Open containing folder Открыть содержащую папку Edit contract.. Suppress Tag Тег No tag Тег отсутствует Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Никакое приложение редактора не сконфигурировано. Сконфигурируйте приложение редактора для Cppcheck в предпочтениях/Приложениях. No default editor application selected. Please select the default editor application in preferences/Applications. Никакое приложение редактора по умолчанию не выбрано. Выберите приложение редактора по умолчанию в предпочтениях/Приложениях. Could not find the file! Не удается найти файл! Could not start %1 Please check the application path and parameters are correct. Не удалось запустить %1 Пожалуйста, проверьте путь приложения, и верны ли параметры. Could not find file: %1 Please select the directory where file is located. Не удается найти файл: %1 Пожалуйста, выберите каталог, в котором находится файл. Select Directory Выберите директорию Id Id Inconclusive Спорное Since date Начиная с даты style стиль error ошибка warning предупреждение performance производительность portability переносимость information информация ResultsView Print Report Распечатать отчет No errors found, nothing to print. Ошибок не найдено, нечего распечатывать. %p% (%1 of %2 files checked) %p% (%1 из %2 файлов проверено) Cppcheck Cppcheck No errors found. Ошибок не найдено. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Были обнаружены ошибки, но они настроены быть скрыты. Для переключения какие ошибки отображаются, откройте меню представления. Failed to read the report. Не удалось прочитать отчет. XML format version 1 is no longer supported. XML формат версии 1 больше не поддерживается. Summary Кратко Message Сообщение First included by Только первый включенный Id Id Bug hunting analysis is incomplete Clear Log Очистить лог Copy this Log entry Скопировать данную запись Copy complete Log Скопировать полный лог No errors found, nothing to save. Ошибки не найдены, нечего сохранять. Failed to save the report. Не удалось сохранить отчет. Results Результаты Analysis Log Лог анализа Warning Details Детали предупреждения Functions Функции Variables Only show variable names that contain text: Configured contracts: Missing contracts: ScratchPad Scratchpad Блокнот Copy or write some C/C++ code here: Исходный код C/C++: Optionally enter a filename (mainly for automatic language detection) and click on "Check": При необходимости введите имя файла и нажмите "Проверить": filename имя файла Check Проверить Settings Preferences Параметры General Общие Include paths: Пути для поиска заголовочных файлов: Add... Добавить... Number of threads: Количество потоков исполнения: Ideal count: Рекомендуемое значение: Force checking all #ifdef configurations Check all #ifdef configurations Проверять все варианты #ifdef конфигураций Show full path of files Показывать полные пути к файлам Show "No errors found" message when no errors found Показывать сообщение, если ошибок не найдено Display error Id in column "Id" Отображать номер ошибки в колонке "id" Enable inline suppressions Включить inline-подавление ошибок Check for inconclusive errors also Показывать также спорные ошибки Show statistics on check completion Показывать статистику после завершения проверки Show internal warnings in log Показывать внутренние предупреждения в логе Addons Дополнения Python binary (leave this empty to use python in the PATH) Python (оставьте пустым для использования python из PATH) ... ... MISRA addon Дополнение MISRA MISRA rule texts file Файл с текстами правил MISRA: <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Скопируйте текст из Appendix A &quot;Summary of guidelines&quot; из фала правил MISRA C 2012 pdf в текстовый файл.</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clang (оставьте пустым для использования clang из PATH) Visual Studio headers Заголовочные файлы Visual Studio <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Путь до заголовочных файлов Visual Studio headers, разделенных символом ';'.</p><p>Вы можете открыть командную строку Visual Studio, ввести &quot;SET INCLUDE&quot; и скопировать пути.</p></body></html> Code Editor Редактор Code Editor Style Оформление System Style Default Light Style Default Dark Style Custom Paths Пути Edit Изменить Remove Удалить Applications Приложения Edit... Изменить... Set as default Установить по умолчанию Reports Отчёты Save all errors when creating report Сохранять все ошибки при создании отчёта Save full path to files in reports Сохранять полные пути к файлам в отчётах Language Язык Advanced Прочие &Show inconclusive errors &Показывать незначительные ошибки S&how internal warnings in log &Записывать внутренние предупреждения в лог SettingsDialog N/A Нет данных The executable file "%1" is not available Add a new application Добавить новое приложение Modify an application Изменить приложение [Default] [По умолчанию] [Default] [По умолчанию] Select python binary Выберите исполняемый файл python Select MISRA File Выберите файл текстов правил MISRA Select clang path Выберите исполняемый файл clang Select include directory Выберите директорию StatsDialog Statistics Статистика Project Проект Project: Проект: Paths: Пути: Include paths: Включенные пути: Defines: Объявленные макроопределения: Undefines: Удаленные макроопределения: Previous Scan Последнее сканирование Path Selected: Выбранный путь: Number of Files Scanned: Количество просканированных файлов: Scan Duration: Продолжительность сканирования: Errors: Ошибки: Warnings: Предупреждения: Stylistic warnings: Стилистические предупреждения: Portability warnings: Предупреждения переносимости: Performance issues: Проблемы с производительностью: Information messages: Информационные сообщения: History История File: Файл: Copy to Clipboard Скопировать в буфер обмена Pdf Export Экспорт PDF 1 day 1 день %1 days %1 дней 1 hour 1 час %1 hours %1 часов 1 minute 1 минута %1 minutes %1 минут 1 second 1 секунда %1 seconds %1 секунд 0.%1 seconds 0.1%1 секунд and и Export PDF Экспорт PDF Project Settings Настройки проекта Paths Пути Include paths Включенные пути Defines Объявленные макроопределения: Undefines Удаленные макроопределения: Path selected Выбранные пути Number of files scanned Количество просканированных файлов Scan duration Продолжительность сканирования Errors Ошибки File: Файл: No cppcheck build dir Не задана директория сборки Warnings Предупреждения Style warnings Стилистические предупреждения Portability warnings Предупреждения переносимости Performance warnings Предупреждения производительности Information messages Информационные сообщения ThreadResult %1 of %2 files checked %1 из %2 файлов проверены TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Не удалось изменить язык пользовательского интерфейса: %1 Язык пользовательского интерфейса был сброшен на английский. Откройте Настройки-диалог для выбора любого из доступных языков. Cppcheck Cppcheck TxtReport inconclusive незначительная VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) Все поддерживаемые файлы (%1) All files (%1) Все файлы (%1) cppcheck-2.7/gui/cppcheck_sr.ts000066400000000000000000003321551417746362400166110ustar00rootroot00000000000000 About About Cppcheck About Cppcheck Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - A tool for static C/C++ code analysis. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 This program is licensed under the terms of the GNU General Public License version 3 Visit Cppcheck homepage at %1 Visit Cppcheck homepage at %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Add a new application Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) &Name: &Executable: &Parameters: Browse Browse Executable files (*.exe);;All files(*.*) Executable files (*.exe);;All files(*.*) Select viewer application Select viewer application Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! FileViewDialog Could not find the file: %1 Could not find the file: %1 Cppcheck Cppcheck Could not read the file: %1 Could not read the file: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Cppcheck Cppcheck MainWindow Cppcheck Cppcheck A&nalyze Standard Standard &File &File &View &View &Toolbars &Check &Check C++ standard &C standard C standard &Edit &Edit &License... &License... A&uthors... A&uthors... &About... &About... &Files... &Files... Analyze files Check files Ctrl+F Ctrl+F &Directory... &Directory... Analyze directory Check directory Ctrl+D Ctrl+D &Recheck files &Recheck files Ctrl+R Ctrl+R &Stop &Stop Stop analysis Stop checking Esc Esc &Save results to file... &Save results to file... Ctrl+S Ctrl+S &Quit &Quit &Clear results &Clear results &Preferences &Preferences Show errors Show warnings Show performance warnings Show &hidden Information Show information messages Show portability warnings Show Cppcheck results Clang Show Clang results &Filter Filter results Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit C++11 C++11 C99 C99 &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor Gtk Gtk Posix Posix C11 C11 C89 C89 C++03 C++03 &Check all &Check all Filter &Reanalyze modified files &Recheck modified files Reanal&yze all files Ctrl+Q Style war&nings E&rrors &Uncheck all &Uncheck all Collapse &all Collapse &all &Expand all &Expand all &Standard Standard items Toolbar &Categories Error categories &Open XML... Open P&roject File... Ctrl+Shift+O Sh&ow Scratchpad... &New Project File... Ctrl+Shift+N &Log View Log View C&lose Project File &Edit Project File... &Statistics &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 &Contents Categories Show style warnings Open the help contents F1 F1 &Help &Help Select directory to check Select directory to check No suitable files found to check! No suitable files found to check! Quick Filter: Select configuration Found project file: %1 Do you want to load this project file instead? File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 License License Authors Authors XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Save the report file Save the report file XML files (*.xml) XML files (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. You must close the project file before selecting new files or directories! Select files to check Select files to check The library '%1' contains unknown elements: %2 Unsupported format Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Open the report file Text files (*.txt) Text files (*.txt) CSV files (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Select Project File Project: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename No project file loaded The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck ProjectFile Project File Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Selected VS Configurations Paths: Add... Edit Remove Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Up Down Platform Analysis This is a workfolder that Cppcheck will use for various purposes. Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth Max recursion in template instantiation Warning options Root path: Filepaths in warnings will be relative to this path Warning tags (separated by semicolon) External tools Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Types and Functions Libraries Parser Cppcheck (built in) Check that each class has a safe public interface Limit analysis Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... Suppressions Add Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Thread safety Coding standards CERT Clang analyzer Clang-tidy Defines: ProjectFileDialog Project file: %1 Select Cppcheck build dir Select include directory Select a directory to check (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Source files All files Exclude file Select MISRA rule texts file MISRA rule texts file (%1) QObject Unknown language specified! Language file %1 not found! Could not find the file: %1! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2 line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Close Save ResultsTree File File Severity Severity Line Line Summary Undefined file Undefined file Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. debug note Recheck Copy filename Copy filename Copy full path Copy full path Hide Hide all with id Suppress selected id(s) Open containing folder Edit contract.. Suppress Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. You can open this error by specifying applications in program's settings. No default editor application selected. Please select the default editor application in preferences/Applications. Could not find the file! Could not start %1 Please check the application path and parameters are correct. Could not start %1 Please check the application path and parameters are correct. Select Directory Id Inconclusive Since date style Style error Error warning performance portability information ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) Cppcheck Cppcheck No errors found. No errors found. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Failed to read the report. XML format version 1 is no longer supported. First included by Id Bug hunting analysis is incomplete Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. No errors found, nothing to save. Failed to save the report. Failed to save the report. Results Results Analysis Log Warning Details Functions Variables Only show variable names that contain text: Configured contracts: Missing contracts: ScratchPad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Check Settings Preferences Preferences General General Add... Number of threads: Number of threads: Ideal count: Force checking all #ifdef configurations Check all #ifdef configurations Show full path of files Show full path of files Show "No errors found" message when no errors found Show "No errors found" message when no errors found Display error Id in column "Id" Enable inline suppressions Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Remove Applications Applications Edit... Set as default Reports Reports Save all errors when creating report Save all errors when creating report Save full path to files in reports Save full path to files in reports Language SettingsDialog N/A The executable file "%1" is not available Add a new application Add a new application Modify an application Modify an application [Default] [Default] Select python binary Select MISRA File Select clang path StatsDialog Statistics Project Project: Paths: Include paths: Defines: Undefines: Previous Scan Path Selected: Number of Files Scanned: Scan Duration: Errors: Warnings: Stylistic warnings: Portability warnings: Performance issues: Information messages: History File: Copy to Clipboard Pdf Export 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Export PDF Project Settings Paths Include paths Defines Undefines Path selected Number of files scanned Scan duration Errors File: No cppcheck build dir Warnings Style warnings Portability warnings Performance warnings Information messages ThreadResult %1 of %2 files checked TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Cppcheck Cppcheck TxtReport inconclusive VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_sv.ts000066400000000000000000003675231417746362400166240ustar00rootroot00000000000000 About About Cppcheck Om Cppcheck Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Ett verktyg för statisk analys av C/C++ kod. Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. Copyright © 2007-2021 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 This program is licensed under the terms of the GNU General Public License version 3 Visit Cppcheck homepage at %1 Hemsida: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Lägg till program Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Här kan du ange en applikation som kan användas för att visa fel. Ange applikationens namn, körbara fil samt kommandorads parametrar. Följande texter i parametrarna ersätts med motsvarande värden när applikationen körs: (file) - filnamn för källkodsfil (line) - radnummer (message) - felmeddelande (severity) - typ / svårighetsgrad Exempel för att öppna en fil med Kate och ange att Kate skall skrolla till rätt rad: Körbar fil: kate Parametrar: -l(line) (file) &Name: Namn: &Executable: Körbar fil: &Parameters: Parametrar: Browse Bläddra Executable files (*.exe);;All files(*.*) Exekverbara filer (*.exe);;Alla filer(*.*) Select viewer application Välj program Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Du måste ange namn, sökväg samt eventuellt parametrar för applikationen! You must specify a name, a path and parameters for the application! Du måste ange ett namn, en sökväg samt parametrar för programmet! FileViewDialog Could not find the file: %1 Could not find the file: Kunde inte hitta filen: %1 Cppcheck Cppcheck Could not read the file: %1 Kunde inte läsa filen: %1 FunctionContractDialog Function contract Name Requirements for parameters HelpDialog Cppcheck GUI help Contents Index Helpfile '%1' was not found Cppcheck Cppcheck LibraryAddFunctionDialog Add function Lägg till funktion Function name(s) Funktion namn Number of arguments Antal argument LibraryDialog Library Editor Library Editor Open Öppna Save Spara Save as Spara som Functions Funktioner Sort Sortera Add Lägg till Filter: Filter: Comments Kommentar noreturn noreturn False False True True Unknown Vet ej return value must be used retur värde måste användas ignore function in leaks checking Ignorera funktionen när cppcheck letar efter läckor Arguments Argument Edit Redigera Library files (*.cfg) Library fil (*.cfg) Open library file Öppna Library fil Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Kunde ej öppna filen %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Kunde ej spara filen %1. Save the library as Spara library som LibraryEditArgDialog Edit argument Konfigurera argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Är bool värde tillåtet? Exempelvis resultatet från jämförelse eller från ! operatorn. Normalt bör inte bool värde användas om argumentet är en pekare eller en storlek etc. Exempel: memcmp(x, y, i == 123); // sista argumentet bör inte vara ett bool värde Not bool Ej bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Är null värde tillåtet? Klicka i denna om argumentet är en pointer parameter som ej tillåter null. Exempel: strcpy(x,y); // varken x eller y får vara null. Not null Ej null Not uninit Ej uninit String Sträng Format string Format sträng Min size of buffer Minsta storlek för buffer Type Typ None Ingen argvalue argvalue constant constant mul mul strlen strlen Arg Arg Arg2 Arg2 and och Valid values Tillåtna värden LogView Checking Log Analys logg &Save &Spara Clear Töm Close Stäng Save Log Spara logg Text files (*.txt *.log);;All files (*.*) Text filer (*.txt *.log);;Alla filer (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Kunde ej öppna fil för skrivning: "%1" MainWindow Cppcheck Cppcheck A&nalyze Analysera Standard Standard &File &Arkiv &View &Visa &Toolbars Verktygsfält &Check &Check C++ standard C++ standard &C standard C standard C standard &Edit &Redigera &License... &Licens... A&uthors... &Utvecklat av... &About... &Om... &Files... &Filer... Analyze files Check files Analysera filer Ctrl+F Ctrl+F &Directory... &Katalog... Analyze directory Check directory Analysera mapp Ctrl+D Ctrl+D &Recheck files Starta &om check Ctrl+R Ctrl+R &Reanalyze all files &Recheck all files Analysera om alla filer &Stop &Stoppa Stop analysis Stop checking Stoppa analys Esc Esc &Save results to file... &Spara resultat till fil... Ctrl+S Ctrl+S &Quit &Avsluta &Clear results &Töm resultat &Preferences &Inställningar Errors Fel Show errors Visa fel Show S&cratchpad... Visa s&cratchpad... Warnings Varningar Show warnings Visa varningar Performance warnings Prestanda varningar Show performance warnings Visa prestanda varningar Show &hidden Visa dolda Information Information Show information messages Visa informations meddelanden Portability Portabilitet Show portability warnings Visa portabilitets varningar Show Cppcheck results Visa Cppcheck resultat Clang Clang Show Clang results Visa Clang resultat &Filter &Filter Filter results Filtrera resultat Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Plattformar C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Skriv ut... Print the Current Report Skriv ut aktuell rapport Print Pre&view... Förhandsgranska utskrift... Open a Print Preview Dialog for the Current Results Öppnar förhandsgranskning för nuvarande resultat Library Editor... Library Editor... Open library editor Öppna library editor Auto-detect language Välj språk automatiskt Enforce C++ C++ Enforce C C &Check all &Kryssa alla Filter Filter &Reanalyze modified files &Recheck modified files Analysera om ändrade filer Reanal&yze all files Analysera om alla filer Ctrl+Q Style war&nings Style varningar E&rrors Fel &Uncheck all Kryssa &ur alla Collapse &all Ingen bra översättning! &Fäll ihop alla &Expand all &Expandera alla &Standard &Standard Standard items Standard poster Toolbar Verktygsfält &Categories &Kategorier Error categories Fel kategorier &Open XML... &Öppna XML... Open P&roject File... Öppna Projektfil... Ctrl+Shift+O Sh&ow Scratchpad... Visa Scratchpad... &New Project File... Ny projektfil... Ctrl+Shift+N &Log View Log View Logg vy C&lose Project File Stäng projektfil &Edit Project File... Redigera projektfil... &Statistics Statistik &Warnings Varningar Per&formance warnings Optimerings varningar &Information Information &Portability Portabilitet P&latforms Plattformar C++&11 C++11 C&99 C99 &Posix Posix C&11 C11 &C89 C89 &C++03 C++03 &Library Editor... Library Editor... &Auto-detect language Detektera språk automatiskt &Enforce C++ Tvinga C++ E&nforce C Tvinga C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 &Contents &Innehåll Categories Kategorier Style warnings Stil varningar Show style warnings Visa stil varningar Open the help contents Öppna hjälp F1 F1 &Help &Hjälp Select directory to check Välj katalog som skall kontrolleras No suitable files found to check! Inga lämpliga filer hittades! Quick Filter: Snabbfilter: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) C/C++ källkod, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) Select configuration Välj konfiguration Select the configuration that will be checked Välj konfiguration som kommer analyseras Found project file: %1 Do you want to load this project file instead? Hittade projektfil: %1 Vill du ladda denna projektfil istället? Found project files from the directory. Do you want to proceed checking without using any of these project files? Hittade projektfil(er) i mappen. Vill du fortsätta analysen utan att använda någon av dessa projektfiler? File not found Filen hittades ej Bad XML Ogiltig XML Missing attribute Attribut finns ej Bad attribute value Ogiltigt attribut värde Unsupported format Format stöds ej Failed to load the selected library '%1'. %2 Misslyckades att ladda valda library '%1'. %2 License Licens Authors Utvecklare XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML filer version 2 (*.xml);;XML filer version 1 (*.xml);;Text filer (*.txt);;CSV filer (*.csv) Save the report file Spara rapport XML files (*.xml) XML filer (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Det uppstod ett problem när programinställningarna skulle laddas. En trolig orsak är att inställningarna ändrats för olika Cppcheck versioner. Kontrollera programinställningarna. You must close the project file before selecting new files or directories! Du måste stänga projektfilen innan nya filer eller sökvägar kan väljas! Select files to check Välj filer att kontrollera The library '%1' contains unknown elements: %2 Library filen '%1' har element som ej hanteras: %2 Duplicate platform type Dubbel plattformstyp Platform type redefined Plattformstyp definieras igen Unknown element Element hanteras ej Unknown issue Något problem Error Fel Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Misslyckades att ladda %1. Din Cppcheck installation är ej komplett. Du kan använda --data-dir<directory> på kommandoraden för att specificera var denna fil finns. Det är meningen att --data-dir kommandot skall köras under installationen,så GUIt kommer ej visas när --data-dir används allt som händer är att en inställning görs. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Nuvarande resultat kommer rensas bort. När en ny XML fil öppnas så tas alla nuvarande resultat bort. Vill du fortsätta? Open the report file Öppna rapportfilen Checking is running. Do you want to stop the checking and exit Cppcheck? Cppcheck kör. Vill du stoppa analysen och avsluta Cppcheck? XML files version 1 (*.xml) XML filer version 1 (*.xml) Deprecated XML format Gammalt XML format XML format 1 is deprecated and will be removed in cppcheck 1.81. XML format 1 är gammalt och stödet kommer tas bort i Cppcheck 1.81 XML files version 2 (*.xml) XML filer version 2 (*.xml) Text files (*.txt) Text filer (*.txt) CSV files (*.csv) CSV filer (*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Misslyckades att ändra språk: %1 Språket har nollställts till Engelska. Öppna Preferences och välj något av de tillgängliga språken. Project files (*.cppcheck);;All files(*.*) Projektfiler (*.cppcheck);;Alla filer(*.*) Select Project File Välj projektfil Project: Projekt: No suitable files found to analyze! Inga filer hittades att analysera! C/C++ Source Compile database Visual Studio Visual Studio Borland C++ Builder 6 Select files to analyze Välj filer att analysera Select directory to analyze Välj mapp att analysera Select the configuration that will be analyzed Välj konfiguration som kommer analyseras Found project files from the directory. Do you want to proceed analysis without using any of these project files? Hittade projekt filer i mappen. Vill du fortsätta analysen utan att använda någon av dessa projekt filer? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? Analys körs. Vill du stoppa analysen och avsluta Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML filer (*.xml);;Text filer (*.txt);;CSV filer (*.csv) Build dir '%1' does not exist, create it? Build dir '%1' existerar ej, skapa den? To check the project using addons, you need a build directory. Failed to import '%1', analysis is stopped Misslyckades att importera '%1', analysen stoppas Project files (*.cppcheck) Projekt filer (*.cppcheck) Select Project Filename Välj Projektfil No project file loaded Inget projekt laddat The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Projektfilen %1 kunde inte hittas! Vill du ta bort filen från 'senast använda projekt'-listan? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI - Command line parameters Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Generell Native Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Kunde ej läsa projektfilen. Could not write the project file. Kunde ej skriva projektfilen ProjectFile Project File Projektfil Project Projekt <html><head/><body><p>In the build dir, cppcheck stores data about each translation unit.</p><p>With a build dir you get whole program analysis.</p><p>Unchanged files will be analyzed much faster; Cppcheck skip the analysis of these files and reuse their old data.</p></body></html> I build dir sparar Cppcheck information för varje translation unit. Med build dir får du whole program analys. Omodifierade filer analyseras mycket fortare, Cppcheck hoppar över analysen och återanvänder den gamla informationen Cppcheck build dir (whole program analysis, faster analysis for unchanged files) Cppcheck build dir (whole program analys, snabbare analys för omodifierade filer) Paths and Defines Sökvägar och defines <html><head/><body><p>Cppcheck can import Visual studio solutions (*.sln), Visual studio projects (*.vcxproj) or compile databases.</p><p>Files to check, defines, include paths are imported.</p></body></html> Cppcheck kan importera Visual studio solutions (*.sln), Visual studio projekt (*.vcxproj) eller compile databases. Sökvägar och defines importeras. Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Importera Projekt (Visual Studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Defines separeras med semicolon ';' &Root: Root: Rot: Libraries: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Obs: Lägg dina egna .cfg filer i samma folder som projekt filen. De skall isåfall visas ovan. Visual Studio Visual Studio ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>Du har ett val:</p><p> * Analysera alla Debug och Release konfigurationer</p><p> * Analysera bara den första matchande Debug konfigurationen</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Analysera alla Visual Studio konfigurationer Selected VS Configurations Paths: Sökvägar: Add... Lägg till... Edit Redigera Remove Ta bort Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Include sökvägar: This is a workfolder that Cppcheck will use for various purposes. Clang (experimental) Normal analysis -- Avoid false positives. Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) Max recursion in template instantiation Filepaths in warnings will be relative to this path If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> External tools Includes Include Include directories: Include sökvägar Up Upp Down Ned Platform Analysis Parser Cppcheck (built in) Clang Clang Check that each class has a safe public interface Limit analysis Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) Max CTU depth Warning options Root path: Bas sökväg: Warning tags (separated by semicolon) Varnings taggar (separerade med semikolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck build dir (whole program analys, incremental analys, statistik, etc) Types and Functions Libraries Libraries Exclude Exkludera Suppressions Suppressions Suppression list: Suppression-list: Add Lägg till Addons Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. Y2038 Y2038 Thread safety Tråd säkerhet Coding standards Kodstandarder CERT CERT Extra Tools Extra verktyg It is common best practice to use several tools. Best practice är att använda flera verktyg Clang analyzer Clang analyzer Clang-tidy Clang-tidy Defines: Defines: ProjectFileDialog Project file: %1 Projektfil: %1 (no rule texts file) Clang-tidy (not found) Select Cppcheck build dir Välj Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Välj include sökväg Source files All files Exclude file Select MISRA rule texts file MISRA rule texts file (%1) Select a directory to check Välj mapp att analysera Visual Studio Visual Studio Compile database Borland C++ Builder 6 Import Project Importera Projekt Visual Studio (*.sln *.vcxproj);;Compile database (compile_database.json) Visual Studio (*.sln *.vcxproj);;Compile database (compile_database.json) Select directory to ignore Välj sökväg att ignorera Add Suppression Lägg till Suppression Select error id suppress: Välj error Id suppress: QDialogButtonBox OK OK Cancel Avbryt Close Stäng Save Spara QObject Unknown language specified! Okänt språk valt! Language file %1 not found! Language file %1.qm not found! Språk filen %1 hittades ej! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Misslyckades med att ladda översättningen för %1 från filen %2 line %1: Unhandled element %2 line %1: Mandatory attribute '%2' missing in '%3' (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK OK Cancel Avbryt Close Stäng Save Spara ResultsTree File Fil Severity Typ Line Rad Summary Sammanfattning Undefined file Odefinierad fil Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [Inconclusive] debug debug note note Recheck Analysera om Copy filename Kopiera filnamn Copy full path Kopiera full sökväg Copy message Kopiera meddelande Copy message id Kopiera meddelande id Hide Dölj Hide all with id Dölj alla med id Suppress selected id(s) Stäng av valda id Open containing folder Öppna mapp Edit contract.. Suppress Tag Tag No tag Ingen tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Ingen editor konfigurerad. Konfigurera program i inställningar/program. No default editor application selected. Please select the default editor application in preferences/Applications. Ingen standard editor vald. Vänligen välj standard editor i inställningar/Program. Could not find the file! Kunde inte hitta filen! Could not start %1 Please check the application path and parameters are correct. Kunde inte starta %1 Kontrollera att sökvägen och parametrarna är korrekta. Could not find file: %1 Please select the directory where file is located. Kunde inte hitta filen: %1 Välj mappen där filen finns. Select Directory Välj mapp Id Id Inconclusive Inconclusive Since date Sedan datum style stil error fel warning varning performance prestanda portability portabilitet information information ResultsView Print Report Skriv ut rapport No errors found, nothing to print. Inga fel hittades, inget att skriva ut. %p% (%1 of %2 files checked) %p% (%1 av %2 filer analyserade) Cppcheck Cppcheck No errors found. Inga fel hittades. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Fel hittades, men de visas ej. För att ställa in vilka fel som skall visas använd visa menyn. Failed to read the report. Misslyckades att läsa rapporten. XML format version 1 is no longer supported. XML format version 1 stöds ej längre. Summary Sammanfattning Message Meddelande First included by Först inkluderad av Id Id Bug hunting analysis is incomplete Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Inga fel hittades, ingenting att spara. Failed to save the report. Misslyckades med att spara rapporten. Results Resultat Analysis Log Analys Log Warning Details Varningsdetaljer Functions Funktioner Variables Only show variable names that contain text: Configured contracts: Missing contracts: ScratchPad Scratchpad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Filnamn Check Analysera Settings Preferences Inställningar General Allmänt Include paths: Include sökvägar: Add... Lägg till... Number of threads: Antal trådar: Ideal count: Optimalt värde: Force checking all #ifdef configurations Check all #ifdef configurations Kontrollera alla #ifdef konfigurationer Show full path of files Visa den fulla sökvägen för filer Show "No errors found" message when no errors found Visa "Inga fel hittades" meddelande när inga fel hittas Display error Id in column "Id" Visa meddelande id i kolumn "Id" Enable inline suppressions Använd inline suppressions Check for inconclusive errors also Kör inconclusive analys Show statistics on check completion Visa statistik när analys är klar Show internal warnings in log Visa interna fel i loggen Addons Addons Python binary (leave this empty to use python in the PATH) Python binär fil (lämna tom för att använda python i PATH) ... ... MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clang sökväg (lämna tom för att använda PATH) Visual Studio headers Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Sökvägar till Visual Studio headers, separerade med semikolon ';'.</p><p>Du kan öppna en Visual Studio command prompt, och skriva &quot;SET INCLUDE&quot;. Sedan kopiera och klistra in sökvägarna.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Sökvägar Edit Redigera Remove Ta bort Applications Program Edit... Redigera... Set as default Sätt förvald Reports Rapporter Save all errors when creating report Spara alla fel Save full path to files in reports Spara fulla sökvägar Language Språk Advanced Avancerade &Show inconclusive errors Visa inconclusive meddelanden S&how internal warnings in log Visa interna fel i loggen SettingsDialog N/A Ej tillgängligt The executable file "%1" is not available Add a new application Lägg till program Modify an application Ändra program [Default] [Vald] [Default] [Förvald] Select python binary Välj python binär Select MISRA File Select clang path Välj Clang sökväg Select include directory Välj include mapp StatsDialog Statistics Statistik Project Projekt Project: Projekt: Paths: Sökvägar: Include paths: Include sökvägar: Defines: Defines: Undefines: Previous Scan Föregående analys Path Selected: Vald sökväg: Number of Files Scanned: Antal analyserade filer: Scan Duration: Analys tid: Errors: Fel: Warnings: Varningar: Stylistic warnings: Stil varningar: Portability warnings: Portabilitets varningar: Performance issues: Prestanda varningar: Information messages: Informations meddelanden: History Historik File: Fil: Copy to Clipboard Kopiera Pdf Export Pdf Export 1 day 1 dag %1 days %1 dagar 1 hour 1 timme %1 hours %1 timmar 1 minute 1 minut %1 minutes %1 minuter 1 second 1 sekund %1 seconds %1 sekunder 0.%1 seconds 0.%1 sekunder and och Export PDF Exportera PDF Project Settings Projekt inställningar Paths Sökvägar Include paths Include sökvägar Defines Definitioner Undefines Path selected Vald sökväg Number of files scanned Antal analyserade filer Scan duration Tid Errors Fel File: Fil: No cppcheck build dir Ingen Cppcheck build dir Warnings Varningar Style warnings Stil varningar Portability warnings Portabilitetsvarningar Performance warnings Prestanda varningar Information messages Informationsmeddelanden ThreadResult %1 of %2 files checked %1 av %2 filer analyserade TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Misslyckades att ändra språk: %1 Språket har nollställts till Engelska. Öppna Preferences och välj något av de tillgängliga språken. Cppcheck Cppcheck TxtReport inconclusive inconclusive VariableContractsDialog Dialog You can specify min and max value for the variable here Min Max toFilterString All supported files (%1) All files (%1) cppcheck-2.7/gui/cppcheck_zh_CN.ts000066400000000000000000003543561417746362400171750ustar00rootroot00000000000000 About About Cppcheck 关于 Cppcheck Version %1 版本 %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - C/C++ 静态代码分析工具。 Copyright © 2007-%1 Cppcheck team. Copyright © 2007-2021 Cppcheck team. 版权所有 © 2007-%1 Cppcheck 团队。 This program is licensed under the terms of the GNU General Public License version 3 该程序在 GNU 通用公共授权版本 3 的条款下发布 Visit Cppcheck homepage at %1 访问 Cppcheck 主页: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> <html><head/><body> <p>非常感谢我们使用的这些库:</p><ul> <li>pcre</li> <li>picojson</li> <li>qt</li> <li>tinyxml2</li> <li>z3</li></ul></body></html> ApplicationDialog Add an application 添加应用程序 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) 在这里,你可以添加一个应用程序,用于打开错误文件。请为该应用程序指定一个名称、可执行文件和命令行参数。 当应用程序执行时,在参数中的以下文本将会替换为适当的值: (file) - 包含错误的文件名称 (line) - 包含错误的行号 (message) - 错误消息 (severity) - 错误严重性 示例:使用 Kate 打开一个文件,并使之滚动到相应的行: 可执行文件: kate 参数: -l(line) (file) &Name: 名称(&N): &Executable: 可执行文件(&E): &Parameters: 参数(&P): Browse 浏览 Executable files (*.exe);;All files(*.*) 可执行文件(*.exe);;所有文件(*.*) Select viewer application 选择查看应用程序 Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! 你必须为应用程序指定名称、路径以及可选参数! FileViewDialog Could not find the file: %1 无法找到文件: %1 Cppcheck Cppcheck Could not read the file: %1 无法读取文件: %1 FunctionContractDialog Function contract 函数约定 Name 名称 Requirements for parameters 必须的参数 HelpDialog Cppcheck GUI help Cppcheck GUI 帮助 Contents 内容 Index 索引 Helpfile '%1' was not found 帮助文件 '%1' 未找到 Cppcheck Cppcheck LibraryAddFunctionDialog Add function 添加函数 Function name(s) 函数名 Number of arguments 参数个数 LibraryDialog Library Editor 库编辑器 Open 打开 Save 保存 Save as 另存为 Functions 函数 Sort 排序 Add 添加 Filter: 过滤: Comments 注释 noreturn 无返回 False True Unknown 未知 return value must be used 返回值必须被使用 ignore function in leaks checking 在泄漏检查中忽略函数 Arguments 参数 Edit 编辑 Library files (*.cfg) 库文件 (*.cfg) Open library file 打开库文件 Cppcheck Cppcheck Cannot open file %1. Can not open file %1. 无法打开文件 %1。 Failed to load %1. %2. 加载文件 %1 失败。%2。 Cannot save file %1. Can not save file %1. 无法保存文件 %1。 Save the library as 库另存为 LibraryEditArgDialog Edit argument 编辑参数 <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> <html><head/><body> <p>是否允许布尔值? 例如,来自比较结果或来自 '!' 操作符。</p> <p>通常,如果参数是指针、大小等,则设置此参数。</p> <p>例子:</p> <pre> memcmp(x, y, i == 123); // 最后一个参数不应该有bool值</pre> </body></html> Not bool 非布尔值 <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> <html><head/><body> <p>是否允许空参数值?</p> <p>通常这应该用于任何不允许空指针的参数。</p> <p>例子:</p> <pre> strcpy(x,y); // 无论 x 或 y 都不允许为空。</pre> </body></html> Not null 非空 Not uninit 非未初始化 String 字符串 Format string 格式化字符串 Min size of buffer 最小缓冲区大小 Type 类型 None argvalue argvalue mul mul strlen strlen Arg 参数1 Arg2 参数2 and 并且 Valid values 有效值 LogView Checking Log 正在检查记录 Clear 清空 Save Log 保存记录 Text files (*.txt *.log);;All files (*.*) 文本文件(*.txt *.log);;所有文件(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" 无法打开并写入文件: “%1” MainWindow Cppcheck Cppcheck &File 文件(&F) &View 查看(&V) &Toolbars 工具栏(&T) &Help 帮助(&H) &Check 检查(&C) C++ standard C++ 标准 &C standard C standard &C 标准 &Edit 编辑(&E) Standard 标准 Categories 分类 &License... 许可证(&L)... A&uthors... 作者(&U)... &About... 关于(&A)... &Files... 文件(&F)... Analyze files Check files 分析文件 Ctrl+F Ctrl+F &Directory... 目录(&D)... Analyze directory Check directory 分析目录 Ctrl+D Ctrl+D &Recheck files 重新检查文件(&R) Ctrl+R Ctrl+R &Stop 停止(&S) Stop analysis Stop checking 停止分析 Esc Esc &Save results to file... 保存结果到文件(&S)... Ctrl+S Ctrl+S &Quit 退出(&Q) &Clear results 清空结果(&C) &Preferences 首选项(&P) Style warnings 风格警告 Show style warnings 显示风格警告 Errors 错误 Show errors 显示错误 Show S&cratchpad... 显示便条(&C)... Information 信息 Show information messages 显示信息消息 Portability 移植可能性 Show portability warnings 显示可移植性警告 Show Cppcheck results 显示 Cppcheck 结果 Clang Clang Show Clang results 显示 Clang 结果 &Filter 滤器(&F) Filter results 过滤结果 Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit Platforms 平台 C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... 打印(&P)... Print the Current Report 打印当前报告 Print Pre&view... 打印预览(&v)... Open a Print Preview Dialog for the Current Results 打开当前结果的打印预览窗口 Open library editor 打开库编辑器 C&lose Project File 关闭项目文件(&L) &Edit Project File... 编辑项目文件(&E)... &Statistics 统计(&S) Warnings 警告 Show warnings 显示警告 Performance warnings 性能警告 Show performance warnings 显示性能警告 Show &hidden 显示隐藏项(&H) &Check all 全部选中(&C) A&nalyze 分析(&A) Filter 滤器 &Reanalyze modified files &Recheck modified files 重新分析已修改的文件(&R) Reanal&yze all files 重新分析全部文件(&y) Ctrl+Q Ctrl+Q Style war&nings 风格警告(&n) E&rrors 编辑(&r) &Uncheck all 全部取消选中(&U) Collapse &all 全部折叠(&A) &Expand all 全部展开(&E) &Standard 标准(&S) Standard items 标准项 &Contents 内容(&C) Open the help contents 打开帮助内容 F1 F1 Toolbar 工具栏 &Categories 分类(&C) Error categories 错误分类 &Open XML... 打开 XML (&O)... Open P&roject File... 打开项目文件(&R)... Ctrl+Shift+O Ctrl+Shift+O Sh&ow Scratchpad... 显示便条(&o)... &New Project File... 新建项目文件(&N)... Ctrl+Shift+N Ctrl+Shift+N &Log View 日志视图(&L) Log View 日志视图 &Warnings 警告(&W) Per&formance warnings 性能警告(&f) &Information 信息(&I) &Portability 可移植性(&P) P&latforms 平台(&l) C++&11 C++&11 C&99 C&99 &Posix &Posix C&11 C&11 &C89 &C89 &C++03 &C++03 &Library Editor... 库编辑器(&L)... &Auto-detect language 自动检测语言(&A) &Enforce C++ &Enforce C++ E&nforce C E&nforce C C++14 C++14 Reanalyze and check library 重新分析并检查库 Check configuration (defines, includes) 检查配置(defines, includes) C++17 C++17 C++20 C++20 There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. 加载编辑器应用程序设置出错。 这可能是因为 Cppcheck 不同版本间的设置有所不同。请检查(并修复)编辑器应用程序设置,否则编辑器程序可能不会正确启动。 No suitable files found to check! 未发现适合检查的文件! You must close the project file before selecting new files or directories! 在选择新的文件或目录之前,你必须先关闭此项目文件! Select directory to check 选择目录来检查 Quick Filter: 快速滤器: Select files to check 选择要检查的文件 Select configuration 选择配置 Found project file: %1 Do you want to load this project file instead? 找到项目文件: %1 你是否想加载该项目文件? Found project files from the directory. Do you want to proceed checking without using any of these project files? 在目录中找到项目文件。 你是否想在不使用这些项目文件的情况下,执行检查? The library '%1' contains unknown elements: %2 库 '%1' 包含未知元素: %2 File not found 文件未找到 Bad XML 无效的 XML Missing attribute 缺失属性 Bad attribute value 无效的属性值 Unsupported format 不支持的格式 Duplicate platform type 重复的平台类型 Platform type redefined 平台类型重定义 Unknown element 位置元素 Unknown issue 未知问题 Failed to load the selected library '%1'. %2 选择的库 '%1' 加载失败。 %2 Error 错误 Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. 加载 %1 失败。您的 Cppcheck 安装已损坏。您可以在命令行添加 --data-dir=<目录> 参数来指定文件位置。请注意,'--data-dir' 参数应当由安装脚本使用,因此,当使用此参数时,GUI不会启动,所发生的一切只是配置了设置。 Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 当前结果将被清空。 打开一个新的 XML 文件将会清空当前结果。你要继续吗? XML files (*.xml) XML 文件(*.xml) Open the report file 打开报告文件 Checking is running. Do you want to stop the checking and exit Cppcheck? 检查正在执行。 你是否需要停止检查并退出 Cppcheck? License 许可证 Authors 作者 XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML 文件版本 2 (*.xml);;XML 文件版本 1 (*.xml);; 文本文件(*.txt);; CSV 文件(*.csv) Save the report file 保存报告文件 XML files version 1 (*.xml) XML 文件版本 1 (*.xml) XML files version 2 (*.xml) XML 文件版本 2 (*.xml) Text files (*.txt) 文本文件(*.txt) CSV files (*.csv) CSV 文件(*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 更改用户界面语言失败: %1 用户界面语言已被重置为英语。打开“首选项”对话框,选择任何可用的语言。 Project files (*.cppcheck);;All files(*.*) 项目文件(*.cppcheck);;所有文件(*.*) Select Project File 选择项目文件 Project: 项目: No suitable files found to analyze! 没有找到合适的文件来分析! C/C++ Source C/C++ 源码 Compile database Compile database Visual Studio Visual Studio Borland C++ Builder 6 Borland C++ Builder 6 Select files to analyze 选择要分析的文件 Select directory to analyze 选择要分析的目录 Select the configuration that will be analyzed 选择要分析的配置 Found project files from the directory. Do you want to proceed analysis without using any of these project files? 在目录中发现项目文件。 您想在不使用这些项目文件的情况下进行分析吗? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? 当前结果将被清除。 打开一个新的XML文件将清除当前的结果。 你想继续吗? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? 分析正在运行。 您想停止分析并退出 Cppcheck 吗? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML 文件 (*.xml);;文本文件 (*.txt);;CSV 文件 (*.csv) Build dir '%1' does not exist, create it? 构建文件夹 '%1' 不能存在,创建它吗? To check the project using addons, you need a build directory. 要使用插件检查项目,您需要一个构建目录。 Failed to import '%1', analysis is stopped 导入 '%1' 失败,分析已停止 Project files (*.cppcheck) 项目文件 (*.cppcheck) Select Project Filename 选择项目文件名 No project file loaded 项目文件未加载 The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? 项目文件 %1 未找到! 你要从最近使用的项目列表中删除此文件吗? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI. 语法: cppcheck-gui [选项] [文件或路径] 选项: -h, --help 打印此帮助 -p <file> 打开指定的项目文件并开始检查它 -l <file> 打开指定的 xml 结果文件 -d <directory> 指定检查的目录,用以生成用 -l 指定的 xml 结果 -v, --version 显示程序版本 --data-dir=<directory> 这个选项用于安装脚本,这样他们可以配置数据文件所在的目录(translations, cfg)。 当使用这个选项时,GUI不会启动。 Cppcheck GUI - Command line parameters Cppcheck GUI - 命令行参数 NewSuppressionDialog New suppression 新建抑制 Error ID 错误 ID File name 文件名 Line number 行号 Symbol name 符号名 Edit suppression 编辑抑制 Platforms Built-in 内置 Native 本地 Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. 无法读取项目文件。 Could not write the project file. 无法写入项目文件。 ProjectFile Project File 项目文件 Project 项目 Paths and Defines 路径和定义 Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) 导入项目 (Visual studio / compile database/ Borland C++ Builder 6) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' 定义必须用分号分隔。例如:DEF1;DEF2=5;DEF3=int &Root: Root: 根目录: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. 注意:把你自己的 .cfg 文件放在和项目文件相同的文件夹中。你应该在上面看到它们。 If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. 如果添加了标记,您将能够右键单击警告并设置其中一个标记。您可以手动对警告进行分类。 Exclude source files 排除源文件 Exclude folder... 排除文件夹... Exclude file... 排除文件... MISRA C 2012 MISRA C 2012 MISRA rule texts MISRA 规则文本 <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>从 MISRA C 2012 PDF 的附录 A &quot;指南摘要&quot; 复制/粘贴文本到一个文本文件。</p></body></html> ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>您有一个选择:</p><p> * 分析所有的 Debug 和 Release 配置</p><p> * 只分析第一个匹配的 Debug 配置</p><p><br/></p></body></html> Browse... 浏览... Analyze all Visual Studio configurations 分析全部 Visual Studio 配置 Selected VS Configurations 已选择的 VS 配置 Paths: 路径: Add... 添加... Edit 编辑 Remove 移除 Undefines: 未定义: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 未定义必须用分号分隔。例如:UNDEF1;UNDEF2;UNDEF3 Include Paths: 包含目录: Types and Functions 类型和函数 Analysis 分析 This is a workfolder that Cppcheck will use for various purposes. 这是一个 Cppcheck 将用于各种目的的工作文件夹。 Parser 解析器 Cppcheck (built in) Cppcheck (内建) Check that each class has a safe public interface 检查每个类是否有一个安全的公共接口 Limit analysis 极限分析 Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) Check code in unused templates (slower and less accurate analysis) 检查未使用模板中的代码(正常情况下应该是打开,但理论上可以忽略未使用模板中的警告) Max CTU depth 最大 CTU 深度 External tools 外部工具 Includes 包含 Include directories: Include 目录: Up 向上 Down 向下 Platform 平台 Clang (experimental) Clang (实验性的) Normal analysis -- Avoid false positives. 常规分析 -- 避免误报。 Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. 错误搜寻 -- 生成几乎所有提示。其目的是为了检测出大多数错误并使代码更加 "牢固"。 If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. 如果你想要设计你的类尽可能的灵活和健壮,那么公共接口必须非常健壮。Cppcheck 将假设参数可以取 *任何* 值。 Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) 检查头文件中的代码(通常应该是打开的。如果您想要一个有限的快速分析,那么关掉它)) Max recursion in template instantiation 模板实例化中的最大递归 Warning options 警告选项 Root path: 根路径: Filepaths in warnings will be relative to this path 警告中的文件路径将相对于此路径 Warning tags (separated by semicolon) 警告标志(用分号隔开) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck 构建目录 (整个程序分析、增量分析、统计数据等) Libraries Exclude 排除 Suppressions 抑制 Add 添加 Addons 插件 Note: Addons require <a href="https://www.python.org/">Python</a> being installed. 注意:插件需要安装 <a href="https://www.python.org/">Python</a>。 Y2038 Y2038 Thread safety 线程安全 Coding standards 编码标准 CERT CERT Clang analyzer Clang analyzer Clang-tidy Clang-tidy Defines: 定义: ProjectFileDialog Project file: %1 项目文件: %1 Select Cppcheck build dir 选择 Cppcheck 构建目录 Select include directory 选择 Include 目录 Select a directory to check 选择一个检查目录 (no rule texts file) (无规则文本文件) Clang-tidy (not found) Clang-tidy (未找到) Visual Studio Visual Studio Compile database Compile database Borland C++ Builder 6 Borland C++ Builder 6 Import Project 导入项目 Select directory to ignore 选择忽略的目录 Source files 源文件 All files 全部文件 Exclude file 排除文件 Select MISRA rule texts file 选择 MISRA 规则文本文件 MISRA rule texts file (%1) MISRA 规则文本文件 (%1) QDialogButtonBox Close 关闭 QObject Unknown language specified! 指定了未知语言! Language file %1 not found! 语言文件 %1 不存在! Failed to load translation for language %1 from file %2 无法从文件 %2 中为语言 %1 加载翻译文件 line %1: Unhandled element %2 第%1行:未处理元素 %2 line %1: Mandatory attribute '%2' missing in '%3' 第%1行:在 "%3" 中缺失的必选属性 "%2" (Not found) (未找到) Thin 极细 ExtraLight 更细 Light Normal 常规 Medium 中等 DemiBold 较粗 Bold ExtraBold 更粗 Black 极粗 Editor Foreground Color 编辑器前景色 Editor Background Color 编辑器背景色 Highlight Background Color 高亮背景色 Line Number Foreground Color 行号前景色 Line Number Background Color 行号背景色 Keyword Foreground Color 关键字前景色 Keyword Font Weight 关键字字体大小 Class Foreground Color Class ForegroundColor 类前景色 Class Font Weight 类字体大小 Quote Foreground Color 引用前景色 Quote Font Weight 引用字体大小 Comment Foreground Color 注释前景色 Comment Font Weight 注释字体大小 Symbol Foreground Color 符号前景色 Symbol Background Color 符号背景色 Symbol Font Weight 符号字体大小 Set to Default Light 设置为默认亮色 Set to Default Dark 设置为默认暗色 QPlatformTheme OK 确定 Cancel 取消 Close 关闭 Save 保存 ResultsTree File 文件 Severity 严重性 Line Summary 概要 Undefined file 未定义文件 Copy 复制 Could not find file: 找不到文件: Please select the folder '%1' 请选择文件夹 '%1' Select Directory '%1' 选择目录 '%1' Please select the directory where file is located. 请选择文件所在的目录。 [Inconclusive] [不确定的] debug 调试 note 注意 Recheck 重新检查 Copy filename 复制文件名 Copy full path 复制完整路径 Copy message 复制消息 Copy message id 复制消息 ID Hide 隐藏 Hide all with id 隐藏全部 ID Suppress selected id(s) 抑制选择的 ID Open containing folder 打开包含的文件夹 Edit contract.. 编辑约定.. Suppress 抑制 Tag 标记 No tag 取消标记 Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. 编辑应用程序未配置。 在“首先项 / 应用程序”中为 Cppcheck 配置编辑应用程序。 No default editor application selected. Please select the default editor application in preferences/Applications. 未选中默认编辑应用程序。 请在“首先项 / 应用程序”中选择默认应用程序。 Could not find the file! 找不到文件! Could not start %1 Please check the application path and parameters are correct. 无法启动 %1 请检查此应用程序的路径与参数是否正确。 Could not find file: %1 Please select the directory where file is located. 无法找到文件: %1 请选择文件所在目录。 Select Directory 选择目录 Id Id Inconclusive 不确定的 Since date 日期 style 风格 error 错误 warning 警告 performance 性能 portability 移植可能性 information 信息 ResultsView Results 结果 Analysis Log 分析日志 Warning Details 警告详情 Functions 函数 Variables 变量 Only show variable names that contain text: 只显示包含文本的变量名: Configured contracts: 已配置的约定: Missing contracts: 缺失的约定: No errors found, nothing to save. 未发现错误,没有结果可保存。 Failed to save the report. 保存报告失败。 Print Report 打印报告 No errors found, nothing to print. 没有错误发现,没有可打印内容。 %p% (%1 of %2 files checked) %p% (%2 个文件已检查 %1 个) Cppcheck Cppcheck No errors found. 未发现错误。 Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. 发现错误,但它们被设为隐藏。 打开“查看”菜单,切换需要显示的错误。 Failed to read the report. 读取报告失败。 XML format version 1 is no longer supported. 不再支持 XML 格式版本 1。 Summary 概要 Message 消息 First included by 首次包含于 Id Id Bug hunting analysis is incomplete 错误搜寻分析未完成 Clear Log 清空日志 Copy this Log entry 复制此日志条目 Copy complete Log 复制完整日志 ScratchPad Scratchpad 便条 Copy or write some C/C++ code here: 在这里复制或输入一些 C/C++ 代码: Optionally enter a filename (mainly for automatic language detection) and click on "Check": 可选择输入文件名 (主要用来自动语言检测) 然后点击 "检查": filename 文件名 Check 检查 Settings Preferences 首选项 General 常规 Include paths: Include 路径: Add... 添加... Number of threads: 线程个数: Ideal count: 理想个数: Force checking all #ifdef configurations Check all #ifdef configurations 强制检查所有 #ifdef 配置 Show full path of files 显示文件的完整路径 Show "No errors found" message when no errors found 当未找到错误,显示“未发现错误”消息 Display error Id in column "Id" 在列“Id”中显示错误 Id Enable inline suppressions 启用内联方案 Check for inconclusive errors also 检查不确定的错误 Show statistics on check completion 检查完成后显示统计数据 Show internal warnings in log 在日志中显示内建警告 Addons 插件 Python binary (leave this empty to use python in the PATH) Python 二进制 (留空将使用 PATH 路径中的 python) ... ... MISRA addon MISRA 插件 MISRA rule texts file MISRA 规则文本文件 <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>从 MISRA C 2012 PDF 的附录 A &quot;指南摘要&quot; 复制/粘贴文本到一个文本文件。</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clang 路径 (留空将使用系统 PATH 路径) Visual Studio headers Visual Studio 头文件 <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Visual Studio 头文件路径,用分号 ';' 分割。</p><p>你可以打开一个 Visual Studio 命令提示符,输入 &quot;SET INCLUDE&quot;。然后复制/粘贴路径。</p></body></html> Code Editor 代码编辑器 Code Editor Style 代码编辑器风格 System Style 系统风格 Default Light Style 默认浅色风格 Default Dark Style 默认深色风格 Custom 自定义 Paths 路径 Edit 编辑 Remove 移除 Applications 应用程序 Edit... 编辑... Set as default 设为默认 Reports 报告 Save all errors when creating report 创建报告时,保存所有错误 Save full path to files in reports 在报告中保存文件的完整路径 Language 语言 Advanced 高级 &Show inconclusive errors 显示不确定的错误(&S) S&how internal warnings in log 在日记中显示内部警告(&H) SettingsDialog N/A N/A The executable file "%1" is not available 可执行文件 "%1" 不可用 Add a new application 添加一个新的应用程序 Modify an application 修改一个应用程序 [Default] [默认] [Default] [默认] Select python binary 选择 python 二进制 Select MISRA File 选择 MISRA 文件 Select clang path 选择 clang 路径 Select include directory 选择包含目录 StatsDialog Statistics 统计 Project 项目 Project: 项目: Paths: 路径: Include paths: 包含路径: Defines: 定义: Undefines: 未定义: Previous Scan 上一次扫描 Path Selected: 选中的路径: Number of Files Scanned: 扫描的文件数: Scan Duration: 扫描时间: Errors: 错误: Warnings: 警告: Stylistic warnings: Stylistic 警告: Portability warnings: 可移植性警告: Performance issues: 性能警告: Information messages: 信息: History 历史 File: 文件: Copy to Clipboard 复制到剪贴板 Pdf Export 导出 PDF 1 day 1 天 %1 days %1 天 1 hour 1 小时 %1 hours %1 小时 1 minute 1 分钟 %1 minutes %1 分钟 1 second 1 秒 %1 seconds %1 秒 0.%1 seconds 0.%1 秒 and Export PDF 导出 PDF Project Settings 项目设置 Paths 路径 Include paths 包含路径 Defines 定义 Undefines 未定义 Path selected 选中的路径 Number of files scanned 扫描的文件数 Scan duration 扫描时间 Errors 错误 File: 文件: No cppcheck build dir 没有 cppcheck 构建目录 Warnings 警告 Style warnings 风格警告 Portability warnings 移植可能性警告 Performance warnings 性能警告 Information messages 信息 ThreadResult %1 of %2 files checked %2 个文件已检查 %1 个 TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 更改用户界面语言失败: %1 用户界面语言已被重置为英语。打开“首选项”对话框,选择任何可用的语言。 Cppcheck Cppcheck TxtReport inconclusive 不确定的 VariableContractsDialog Dialog 对话框 You can specify min and max value for the variable here 你可以在这里指定变量的最小值和最大值 Min 最小 Max 最大 toFilterString All supported files (%1) 全部支持的文件 (%1) All files (%1) 全部文件 (%1) cppcheck-2.7/gui/cppchecklibrarydata.cpp000066400000000000000000001067011417746362400204540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppchecklibrarydata.h" #include #include #include const unsigned int CppcheckLibraryData::Function::Arg::ANY = ~0U; const unsigned int CppcheckLibraryData::Function::Arg::VARIADIC = ~1U; CppcheckLibraryData::CppcheckLibraryData() {} static std::string unhandledElement(const QXmlStreamReader &xmlReader) { throw std::runtime_error(QObject::tr("line %1: Unhandled element %2").arg(xmlReader.lineNumber()).arg(xmlReader.name().toString()).toStdString()); } static std::string mandatoryAttibuteMissing(const QXmlStreamReader &xmlReader, QString attributeName) { throw std::runtime_error(QObject::tr("line %1: Mandatory attribute '%2' missing in '%3'") .arg(xmlReader.lineNumber()) .arg(attributeName) .arg(xmlReader.name().toString()).toStdString()); } static CppcheckLibraryData::Container loadContainer(QXmlStreamReader &xmlReader) { CppcheckLibraryData::Container container; container.id = xmlReader.attributes().value("id").toString(); container.inherits = xmlReader.attributes().value("inherits").toString(); container.startPattern = xmlReader.attributes().value("startPattern").toString(); container.endPattern = xmlReader.attributes().value("endPattern").toString(); container.opLessAllowed = xmlReader.attributes().value("opLessAllowed").toString(); container.itEndPattern = xmlReader.attributes().value("itEndPattern").toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "container") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "type") { container.type.templateParameter = xmlReader.attributes().value("templateParameter").toString(); container.type.string = xmlReader.attributes().value("string").toString(); } else if (elementName == "size" || elementName == "access" || elementName == "other") { const QString indexOperator = xmlReader.attributes().value("indexOperator").toString(); if (elementName == "access" && indexOperator == "array-like") container.access_arrayLike = true; const QString templateParameter = xmlReader.attributes().value("templateParameter").toString(); if (elementName == "size" && !templateParameter.isEmpty()) container.size_templateParameter = templateParameter.toInt(); for (;;) { type = xmlReader.readNext(); if (xmlReader.name().toString() == elementName) break; if (type != QXmlStreamReader::StartElement) continue; struct CppcheckLibraryData::Container::Function function; function.name = xmlReader.attributes().value("name").toString(); function.action = xmlReader.attributes().value("action").toString(); function.yields = xmlReader.attributes().value("yields").toString(); if (elementName == "size") container.sizeFunctions.append(function); else if (elementName == "access") container.accessFunctions.append(function); else container.otherFunctions.append(function); } } else { unhandledElement(xmlReader); } } return container; } static CppcheckLibraryData::Define loadDefine(const QXmlStreamReader &xmlReader) { CppcheckLibraryData::Define define; define.name = xmlReader.attributes().value("name").toString(); define.value = xmlReader.attributes().value("value").toString(); return define; } static QString loadUndefine(const QXmlStreamReader &xmlReader) { return xmlReader.attributes().value("name").toString(); } static QString loadSmartPointer(const QXmlStreamReader &xmlReader) { return xmlReader.attributes().value("class-name").toString(); } static CppcheckLibraryData::TypeChecks loadTypeChecks(QXmlStreamReader &xmlReader) { CppcheckLibraryData::TypeChecks typeChecks; QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "type-checks") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "suppress" || elementName == "check") { QPair entry(elementName, xmlReader.readElementText()); typeChecks.append(entry); } } return typeChecks; } static CppcheckLibraryData::Function::Arg loadFunctionArg(QXmlStreamReader &xmlReader) { CppcheckLibraryData::Function::Arg arg; QString argnr = xmlReader.attributes().value("nr").toString(); if (argnr == "any") arg.nr = CppcheckLibraryData::Function::Arg::ANY; else if (argnr == "variadic") arg.nr = CppcheckLibraryData::Function::Arg::VARIADIC; else arg.nr = argnr.toUInt(); arg.defaultValue = xmlReader.attributes().value("default").toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "arg") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "not-bool") arg.notbool = true; else if (elementName == "not-null") arg.notnull = true; else if (elementName == "not-uninit") arg.notuninit = true; else if (elementName == "strz") arg.strz = true; else if (elementName == "formatstr") arg.formatstr = true; else if (elementName == "valid") arg.valid = xmlReader.readElementText(); else if (elementName == "minsize") { CppcheckLibraryData::Function::Arg::MinSize minsize; minsize.type = xmlReader.attributes().value("type").toString(); minsize.arg = xmlReader.attributes().value("arg").toString(); minsize.arg2 = xmlReader.attributes().value("arg2").toString(); arg.minsizes.append(minsize); } else if (elementName == "iterator") { arg.iterator.container = xmlReader.attributes().value("container").toInt(); arg.iterator.type = xmlReader.attributes().value("type").toString(); } else { unhandledElement(xmlReader); } } return arg; } static CppcheckLibraryData::Function loadFunction(QXmlStreamReader &xmlReader, const QString &comments) { CppcheckLibraryData::Function function; function.comments = comments; function.name = xmlReader.attributes().value("name").toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "function") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "noreturn") function.noreturn = (xmlReader.readElementText() == "true") ? CppcheckLibraryData::Function::True : CppcheckLibraryData::Function::False; else if (elementName == "pure") function.gccPure = true; else if (elementName == "const") function.gccConst = true; else if (elementName == "leak-ignore") function.leakignore = true; else if (elementName == "use-retval") function.useretval = true; else if (elementName == "returnValue") { const QString container = xmlReader.attributes().value("container").toString(); function.returnValue.container = container.isNull() ? -1 : container.toInt(); function.returnValue.type = xmlReader.attributes().value("type").toString(); function.returnValue.value = xmlReader.readElementText(); } else if (elementName == "formatstr") { function.formatstr.scan = xmlReader.attributes().value("scan").toString(); function.formatstr.secure = xmlReader.attributes().value("secure").toString(); } else if (elementName == "arg") function.args.append(loadFunctionArg(xmlReader)); else if (elementName == "warn") { function.warn.severity = xmlReader.attributes().value("severity").toString(); function.warn.cstd = xmlReader.attributes().value("cstd").toString(); function.warn.reason = xmlReader.attributes().value("reason").toString(); function.warn.alternatives = xmlReader.attributes().value("alternatives").toString(); function.warn.msg = xmlReader.readElementText(); } else { unhandledElement(xmlReader); } } return function; } static CppcheckLibraryData::MemoryResource loadMemoryResource(QXmlStreamReader &xmlReader) { CppcheckLibraryData::MemoryResource memoryresource; memoryresource.type = xmlReader.name().toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != memoryresource.type) { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "alloc" || elementName == "realloc") { CppcheckLibraryData::MemoryResource::Alloc alloc; alloc.isRealloc = (elementName == "realloc"); alloc.init = (xmlReader.attributes().value("init").toString() == "true"); if (xmlReader.attributes().hasAttribute("arg")) { alloc.arg = xmlReader.attributes().value("arg").toInt(); } if (alloc.isRealloc && xmlReader.attributes().hasAttribute("realloc-arg")) { alloc.reallocArg = xmlReader.attributes().value("realloc-arg").toInt(); } if (memoryresource.type == "memory") { alloc.bufferSize = xmlReader.attributes().value("buffer-size").toString(); } alloc.name = xmlReader.readElementText(); memoryresource.alloc.append(alloc); } else if (elementName == "dealloc") { CppcheckLibraryData::MemoryResource::Dealloc dealloc; if (xmlReader.attributes().hasAttribute("arg")) { dealloc.arg = xmlReader.attributes().value("arg").toInt(); } dealloc.name = xmlReader.readElementText(); memoryresource.dealloc.append(dealloc); } else if (elementName == "use") memoryresource.use.append(xmlReader.readElementText()); else unhandledElement(xmlReader); } return memoryresource; } static CppcheckLibraryData::PodType loadPodType(const QXmlStreamReader &xmlReader) { CppcheckLibraryData::PodType podtype; podtype.name = xmlReader.attributes().value("name").toString(); if (podtype.name.isEmpty()) { mandatoryAttibuteMissing(xmlReader, "name"); } podtype.stdtype = xmlReader.attributes().value("stdtype").toString(); podtype.size = xmlReader.attributes().value("size").toString(); podtype.sign = xmlReader.attributes().value("sign").toString(); return podtype; } static CppcheckLibraryData::PlatformType loadPlatformType(QXmlStreamReader &xmlReader) { CppcheckLibraryData::PlatformType platformType; platformType.name = xmlReader.attributes().value("name").toString(); platformType.value = xmlReader.attributes().value("value").toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "platformtype") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (QStringList({"unsigned", "long", "pointer", "const_ptr", "ptr_ptr"}).contains(elementName)) { platformType.types.append(elementName); } else if (elementName == "platform") { platformType.platforms.append(xmlReader.attributes().value("type").toString()); } else { unhandledElement(xmlReader); } } return platformType; } static CppcheckLibraryData::Reflection loadReflection(QXmlStreamReader &xmlReader) { CppcheckLibraryData::Reflection reflection; QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "reflection") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "call") { CppcheckLibraryData::Reflection::Call call; if (xmlReader.attributes().hasAttribute("arg")) { call.arg = xmlReader.attributes().value("arg").toInt(); } else { mandatoryAttibuteMissing(xmlReader, "arg"); } call.name = xmlReader.readElementText(); reflection.calls.append(call); } else { unhandledElement(xmlReader); } } return reflection; } static CppcheckLibraryData::Markup loadMarkup(QXmlStreamReader &xmlReader) { CppcheckLibraryData::Markup markup; QXmlStreamReader::TokenType type; if (xmlReader.attributes().hasAttribute("ext")) { markup.ext = xmlReader.attributes().value("ext").toString(); } else { mandatoryAttibuteMissing(xmlReader, "ext"); } if (xmlReader.attributes().hasAttribute("aftercode")) { markup.afterCode = (xmlReader.attributes().value("aftercode") == "true") ? true : false; } else { mandatoryAttibuteMissing(xmlReader, "aftercode"); } if (xmlReader.attributes().hasAttribute("reporterrors")) { markup.reportErrors = (xmlReader.attributes().value("reporterrors") == "true") ? true : false; } else { mandatoryAttibuteMissing(xmlReader, "reporterrors"); } while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "markup") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "keywords") { while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "keywords") { if (type != QXmlStreamReader::StartElement) continue; if (xmlReader.name().toString() == "keyword") { markup.keywords.append(xmlReader.attributes().value("name").toString()); } else { unhandledElement(xmlReader); } } } else if (elementName == "codeblocks") { CppcheckLibraryData::Markup::CodeBlocks codeBlock; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "codeblocks") { if (type != QXmlStreamReader::StartElement) continue; if (xmlReader.name().toString() == "block") { codeBlock.blocks.append(xmlReader.attributes().value("name").toString()); } else if (xmlReader.name().toString() == "structure") { codeBlock.offset = xmlReader.attributes().value("offset").toInt(); codeBlock.start = xmlReader.attributes().value("start").toString(); codeBlock.end = xmlReader.attributes().value("end").toString(); } else { unhandledElement(xmlReader); } } markup.codeBlocks.append(codeBlock); } else if (elementName == "exported") { CppcheckLibraryData::Markup::Exporter exporter; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "exported") { if (type != QXmlStreamReader::StartElement) continue; if (xmlReader.name().toString() == "exporter") { exporter.prefix = xmlReader.attributes().value("prefix").toString(); } else if (xmlReader.name().toString() == "prefix") { exporter.prefixList.append(xmlReader.readElementText()); } else if (xmlReader.name().toString() == "suffix") { exporter.suffixList.append(xmlReader.readElementText()); } else { unhandledElement(xmlReader); } } markup.exporter.append(exporter); } else if (elementName == "imported") { while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "imported") { if (type != QXmlStreamReader::StartElement) continue; if (xmlReader.name().toString() == "importer") { markup.importer.append(xmlReader.readElementText()); } else { unhandledElement(xmlReader); } } } else { unhandledElement(xmlReader); } } return markup; } QString CppcheckLibraryData::open(QIODevice &file) { clear(); QString comments; QXmlStreamReader xmlReader(&file); while (!xmlReader.atEnd()) { const QXmlStreamReader::TokenType t = xmlReader.readNext(); switch (t) { case QXmlStreamReader::Comment: if (!comments.isEmpty()) comments += "\n"; comments += xmlReader.text().toString(); break; case QXmlStreamReader::StartElement: try { const QString elementName(xmlReader.name().toString()); if (elementName == "def") ; else if (elementName == "container") containers.append(loadContainer(xmlReader)); else if (elementName == "define") defines.append(loadDefine(xmlReader)); else if (elementName == "undefine") undefines.append(loadUndefine(xmlReader)); else if (elementName == "function") functions.append(loadFunction(xmlReader, comments)); else if (elementName == "memory" || elementName == "resource") memoryresource.append(loadMemoryResource(xmlReader)); else if (elementName == "podtype") podtypes.append(loadPodType(xmlReader)); else if (elementName == "smart-pointer") smartPointers.append(loadSmartPointer(xmlReader)); else if (elementName == "type-checks") typeChecks.append(loadTypeChecks(xmlReader)); else if (elementName == "platformtype") platformTypes.append(loadPlatformType(xmlReader)); else if (elementName == "reflection") reflections.append(loadReflection(xmlReader)); else if (elementName == "markup") markups.append(loadMarkup(xmlReader)); else unhandledElement(xmlReader); } catch (std::runtime_error &e) { return e.what(); } comments.clear(); break; default: break; } } if (xmlReader.hasError()) { return xmlReader.errorString(); } else { return QString(); } } static void writeContainerFunctions(QXmlStreamWriter &xmlWriter, const QString &name, int extra, const QList &functions) { if (functions.isEmpty() && extra < 0) return; xmlWriter.writeStartElement(name); if (extra >= 0) { if (name == "access") xmlWriter.writeAttribute("indexOperator", "array-like"); else if (name == "size") xmlWriter.writeAttribute("templateParameter", QString::number(extra)); } foreach (const CppcheckLibraryData::Container::Function &function, functions) { xmlWriter.writeStartElement("function"); xmlWriter.writeAttribute("name", function.name); if (!function.action.isEmpty()) xmlWriter.writeAttribute("action", function.action); if (!function.yields.isEmpty()) xmlWriter.writeAttribute("yields", function.yields); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } static void writeContainer(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Container &container) { xmlWriter.writeStartElement("container"); xmlWriter.writeAttribute("id", container.id); if (!container.startPattern.isEmpty()) xmlWriter.writeAttribute("startPattern", container.startPattern); if (!container.endPattern.isNull()) xmlWriter.writeAttribute("endPattern", container.endPattern); if (!container.inherits.isEmpty()) xmlWriter.writeAttribute("inherits", container.inherits); if (!container.opLessAllowed.isEmpty()) xmlWriter.writeAttribute("opLessAllowed", container.opLessAllowed); if (!container.itEndPattern.isEmpty()) xmlWriter.writeAttribute("itEndPattern", container.itEndPattern); if (!container.type.templateParameter.isEmpty() || !container.type.string.isEmpty()) { xmlWriter.writeStartElement("type"); if (!container.type.templateParameter.isEmpty()) xmlWriter.writeAttribute("templateParameter", container.type.templateParameter); if (!container.type.string.isEmpty()) xmlWriter.writeAttribute("string", container.type.string); xmlWriter.writeEndElement(); } writeContainerFunctions(xmlWriter, "size", container.size_templateParameter, container.sizeFunctions); writeContainerFunctions(xmlWriter, "access", container.access_arrayLike?1:-1, container.accessFunctions); writeContainerFunctions(xmlWriter, "other", -1, container.otherFunctions); xmlWriter.writeEndElement(); } static void writeFunction(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Function &function) { QString comments = function.comments; while (comments.startsWith("\n")) comments = comments.mid(1); while (comments.endsWith("\n")) comments.chop(1); foreach (const QString &comment, comments.split('\n')) { if (comment.length() >= 1) xmlWriter.writeComment(comment); } xmlWriter.writeStartElement("function"); xmlWriter.writeAttribute("name", function.name); if (function.useretval) xmlWriter.writeEmptyElement("use-retval"); if (function.gccConst) xmlWriter.writeEmptyElement("const"); if (function.gccPure) xmlWriter.writeEmptyElement("pure"); if (!function.returnValue.empty()) { xmlWriter.writeStartElement("returnValue"); if (!function.returnValue.type.isNull()) xmlWriter.writeAttribute("type", function.returnValue.type); if (function.returnValue.container >= 0) xmlWriter.writeAttribute("container", QString::number(function.returnValue.container)); if (!function.returnValue.value.isNull()) xmlWriter.writeCharacters(function.returnValue.value); xmlWriter.writeEndElement(); } if (function.noreturn != CppcheckLibraryData::Function::Unknown) xmlWriter.writeTextElement("noreturn", (function.noreturn == CppcheckLibraryData::Function::True) ? "true" : "false"); if (function.leakignore) xmlWriter.writeEmptyElement("leak-ignore"); // Argument info.. foreach (const CppcheckLibraryData::Function::Arg &arg, function.args) { if (arg.formatstr) { xmlWriter.writeStartElement("formatstr"); if (!function.formatstr.scan.isNull()) xmlWriter.writeAttribute("scan", function.formatstr.scan); if (!function.formatstr.secure.isNull()) xmlWriter.writeAttribute("secure", function.formatstr.secure); xmlWriter.writeEndElement(); } xmlWriter.writeStartElement("arg"); if (arg.nr == CppcheckLibraryData::Function::Arg::ANY) xmlWriter.writeAttribute("nr", "any"); else if (arg.nr == CppcheckLibraryData::Function::Arg::VARIADIC) xmlWriter.writeAttribute("nr", "variadic"); else xmlWriter.writeAttribute("nr", QString::number(arg.nr)); if (!arg.defaultValue.isNull()) xmlWriter.writeAttribute("default", arg.defaultValue); if (arg.formatstr) xmlWriter.writeEmptyElement("formatstr"); if (arg.notnull) xmlWriter.writeEmptyElement("not-null"); if (arg.notuninit) xmlWriter.writeEmptyElement("not-uninit"); if (arg.notbool) xmlWriter.writeEmptyElement("not-bool"); if (arg.strz) xmlWriter.writeEmptyElement("strz"); if (!arg.valid.isEmpty()) xmlWriter.writeTextElement("valid",arg.valid); foreach (const CppcheckLibraryData::Function::Arg::MinSize &minsize, arg.minsizes) { xmlWriter.writeStartElement("minsize"); xmlWriter.writeAttribute("type", minsize.type); xmlWriter.writeAttribute("arg", minsize.arg); if (!minsize.arg2.isEmpty()) xmlWriter.writeAttribute("arg2", minsize.arg2); xmlWriter.writeEndElement(); } if (arg.iterator.container >= 0 || !arg.iterator.type.isNull()) { xmlWriter.writeStartElement("iterator"); if (arg.iterator.container >= 0) xmlWriter.writeAttribute("container", QString::number(arg.iterator.container)); if (!arg.iterator.type.isNull()) xmlWriter.writeAttribute("type", arg.iterator.type); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!function.warn.isEmpty()) { xmlWriter.writeStartElement("warn"); if (!function.warn.severity.isEmpty()) xmlWriter.writeAttribute("severity", function.warn.severity); if (!function.warn.cstd.isEmpty()) xmlWriter.writeAttribute("cstd", function.warn.cstd); if (!function.warn.alternatives.isEmpty()) xmlWriter.writeAttribute("alternatives", function.warn.alternatives); if (!function.warn.reason.isEmpty()) xmlWriter.writeAttribute("reason", function.warn.reason); if (!function.warn.msg.isEmpty()) xmlWriter.writeCharacters(function.warn.msg); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } static void writeMemoryResource(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::MemoryResource &mr) { xmlWriter.writeStartElement(mr.type); foreach (const CppcheckLibraryData::MemoryResource::Alloc &alloc, mr.alloc) { if (alloc.isRealloc) { xmlWriter.writeStartElement("realloc"); } else { xmlWriter.writeStartElement("alloc"); } xmlWriter.writeAttribute("init", alloc.init ? "true" : "false"); if (alloc.arg != -1) { xmlWriter.writeAttribute("arg", QString("%1").arg(alloc.arg)); } if (alloc.isRealloc && alloc.reallocArg != -1) { xmlWriter.writeAttribute("realloc-arg", QString("%1").arg(alloc.reallocArg)); } if (mr.type == "memory" && !alloc.bufferSize.isEmpty()) { xmlWriter.writeAttribute("buffer-size", alloc.bufferSize); } xmlWriter.writeCharacters(alloc.name); xmlWriter.writeEndElement(); } foreach (const CppcheckLibraryData::MemoryResource::Dealloc &dealloc, mr.dealloc) { xmlWriter.writeStartElement("dealloc"); if (dealloc.arg != -1) { xmlWriter.writeAttribute("arg", QString("%1").arg(dealloc.arg)); } xmlWriter.writeCharacters(dealloc.name); xmlWriter.writeEndElement(); } foreach (const QString &use, mr.use) { xmlWriter.writeTextElement("use", use); } xmlWriter.writeEndElement(); } static void writeTypeChecks(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::TypeChecks &typeChecks) { QPair check; xmlWriter.writeStartElement("type-checks"); if (!typeChecks.isEmpty()) { xmlWriter.writeStartElement("unusedvar"); } foreach (check, typeChecks) { xmlWriter.writeStartElement(check.first); xmlWriter.writeCharacters(check.second); xmlWriter.writeEndElement(); } if (!typeChecks.isEmpty()) { xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } static void writePlatformType(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::PlatformType &pt) { xmlWriter.writeStartElement("platformtype"); xmlWriter.writeAttribute("name", pt.name); xmlWriter.writeAttribute("value", pt.value); foreach (const QString type, pt.types) { xmlWriter.writeStartElement(type); xmlWriter.writeEndElement(); } foreach (const QString platform, pt.platforms) { xmlWriter.writeStartElement("platform"); if (!platform.isEmpty()) { xmlWriter.writeAttribute("type", platform); } xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } static void writeReflection(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Reflection &refl) { xmlWriter.writeStartElement("reflection"); foreach (const CppcheckLibraryData::Reflection::Call &call, refl.calls) { xmlWriter.writeStartElement("call"); xmlWriter.writeAttribute("arg", QString("%1").arg(call.arg)); xmlWriter.writeCharacters(call.name); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } static void writeMarkup(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Markup &mup) { xmlWriter.writeStartElement("markup"); xmlWriter.writeAttribute("ext", mup.ext); xmlWriter.writeAttribute("aftercode", QVariant(mup.afterCode).toString()); xmlWriter.writeAttribute("reporterrors", QVariant(mup.reportErrors).toString()); if (!mup.keywords.isEmpty()) { xmlWriter.writeStartElement("keywords"); foreach (const QString &keyword, mup.keywords) { xmlWriter.writeStartElement("keyword"); xmlWriter.writeAttribute("name", keyword); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mup.importer.isEmpty()) { xmlWriter.writeStartElement("imported"); foreach (const QString &import, mup.importer) { xmlWriter.writeStartElement("importer"); xmlWriter.writeCharacters(import); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mup.exporter.isEmpty()) { xmlWriter.writeStartElement("exported"); foreach (const CppcheckLibraryData::Markup::Exporter exporter, mup.exporter) { xmlWriter.writeStartElement("exporter"); xmlWriter.writeAttribute("prefix", exporter.prefix); foreach (const QString &prefix, exporter.prefixList) { xmlWriter.writeStartElement("prefix"); xmlWriter.writeCharacters(prefix); xmlWriter.writeEndElement(); } foreach (const QString &suffix, exporter.suffixList) { xmlWriter.writeStartElement("suffix"); xmlWriter.writeCharacters(suffix); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mup.codeBlocks.isEmpty()) { foreach (const CppcheckLibraryData::Markup::CodeBlocks codeblock, mup.codeBlocks) { xmlWriter.writeStartElement("codeblocks"); foreach (const QString &block, codeblock.blocks) { xmlWriter.writeStartElement("block"); xmlWriter.writeAttribute("name", block); xmlWriter.writeEndElement(); } xmlWriter.writeStartElement("structure"); xmlWriter.writeAttribute("offset", QString("%1").arg(codeblock.offset)); xmlWriter.writeAttribute("start", codeblock.start); xmlWriter.writeAttribute("end", codeblock.end); xmlWriter.writeEndElement(); xmlWriter.writeEndElement(); } } xmlWriter.writeEndElement(); } QString CppcheckLibraryData::toString() const { QString outputString; QXmlStreamWriter xmlWriter(&outputString); xmlWriter.setAutoFormatting(true); xmlWriter.setAutoFormattingIndent(2); xmlWriter.writeStartDocument("1.0"); xmlWriter.writeStartElement("def"); xmlWriter.writeAttribute("format","2"); foreach (const Define &define, defines) { xmlWriter.writeStartElement("define"); xmlWriter.writeAttribute("name", define.name); xmlWriter.writeAttribute("value", define.value); xmlWriter.writeEndElement(); } foreach (const QString &undef, undefines) { xmlWriter.writeStartElement("undefine"); xmlWriter.writeAttribute("name", undef); xmlWriter.writeEndElement(); } foreach (const Function &function, functions) { writeFunction(xmlWriter, function); } foreach (const MemoryResource &mr, memoryresource) { writeMemoryResource(xmlWriter, mr); } foreach (const Container &container, containers) { writeContainer(xmlWriter, container); } foreach (const PodType &podtype, podtypes) { xmlWriter.writeStartElement("podtype"); xmlWriter.writeAttribute("name", podtype.name); if (!podtype.stdtype.isEmpty()) xmlWriter.writeAttribute("stdtype", podtype.stdtype); if (!podtype.sign.isEmpty()) xmlWriter.writeAttribute("sign", podtype.sign); if (!podtype.size.isEmpty()) xmlWriter.writeAttribute("size", podtype.size); xmlWriter.writeEndElement(); } foreach (const TypeChecks check, typeChecks) { writeTypeChecks(xmlWriter, check); } foreach (const QString &smartPtr, smartPointers) { xmlWriter.writeStartElement("smart-pointer"); xmlWriter.writeAttribute("class-name", smartPtr); xmlWriter.writeEndElement(); } foreach (const PlatformType &pt, platformTypes) { writePlatformType(xmlWriter, pt); } foreach (const Reflection &refl, reflections) { writeReflection(xmlWriter, refl); } foreach (const Markup &mup, markups) { writeMarkup(xmlWriter, mup); } xmlWriter.writeEndElement(); return outputString; } cppcheck-2.7/gui/cppchecklibrarydata.h000066400000000000000000000161331417746362400201200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CPPCHECKLIBRARYDATA_H #define CPPCHECKLIBRARYDATA_H #include #include #include class QIODevice; class CppcheckLibraryData { public: CppcheckLibraryData(); struct Container { Container() : access_arrayLike(false), size_templateParameter(-1) {} QString id; QString inherits; QString startPattern; QString endPattern; QString opLessAllowed; QString itEndPattern; bool access_arrayLike; int size_templateParameter; struct { QString templateParameter; QString string; } type; struct Function { QString name; QString yields; QString action; }; QList accessFunctions; QList otherFunctions; QList sizeFunctions; }; struct Define { QString name; QString value; }; struct Function { Function() : noreturn(Unknown), gccPure(false), gccConst(false), leakignore(false), useretval(false) {} QString comments; QString name; enum TrueFalseUnknown { False, True, Unknown } noreturn; bool gccPure; bool gccConst; bool leakignore; bool useretval; struct ReturnValue { ReturnValue() : container(-1) {} QString type; QString value; int container; bool empty() const { return type.isNull() && value.isNull() && container < 0; } } returnValue; struct { QString scan; QString secure; } formatstr; struct Arg { Arg() : nr(0), notbool(false), notnull(false), notuninit(false), formatstr(false), strz(false) {} QString name; unsigned int nr; static const unsigned int ANY; static const unsigned int VARIADIC; QString defaultValue; bool notbool; bool notnull; bool notuninit; bool formatstr; bool strz; QString valid; struct MinSize { QString type; QString arg; QString arg2; }; QList minsizes; struct Iterator { Iterator() : container(-1) {} int container; QString type; } iterator; }; QList args; struct { QString severity; QString cstd; QString reason; QString alternatives; QString msg; bool isEmpty() const { return cstd.isEmpty() && severity.isEmpty() && reason.isEmpty() && alternatives.isEmpty() && msg.isEmpty(); } } warn; }; struct MemoryResource { QString type; // "memory" or "resource" struct Alloc { Alloc() : isRealloc(false), init(false), arg(-1), // -1: Has no optional "arg" attribute reallocArg(-1) // -1: Has no optional "realloc-arg" attribute {} bool isRealloc; bool init; int arg; int reallocArg; QString bufferSize; QString name; }; struct Dealloc { Dealloc() : arg(-1) // -1: Has no optional "arg" attribute {} int arg; QString name; }; QList alloc; QList dealloc; QStringList use; }; struct PodType { QString name; QString stdtype; QString size; QString sign; }; struct PlatformType { QString name; QString value; QStringList types; // Keeps element names w/o attribute (e.g. unsigned) QStringList platforms; // Keeps "type" attribute of each "platform" element }; using TypeChecks = QList>; struct Reflection { struct Call { Call() : arg {-1} // -1: Mandatory "arg" attribute not available {} int arg; QString name; }; QList calls; }; struct Markup { struct CodeBlocks { CodeBlocks() : offset {-1} {} QStringList blocks; int offset; QString start; QString end; }; struct Exporter { QString prefix; QStringList prefixList; QStringList suffixList; }; QString ext; bool afterCode; bool reportErrors; QStringList keywords; QStringList importer; QList codeBlocks; QList exporter; }; void clear() { containers.clear(); defines.clear(); undefines.clear(); functions.clear(); memoryresource.clear(); podtypes.clear(); smartPointers.clear(); typeChecks.clear(); platformTypes.clear(); reflections.clear(); markups.clear(); } void swap(CppcheckLibraryData &other) { containers.swap(other.containers); defines.swap(other.defines); undefines.swap(other.undefines); functions.swap(other.functions); memoryresource.swap(other.memoryresource); podtypes.swap(other.podtypes); smartPointers.swap(other.smartPointers); typeChecks.swap(other.typeChecks); platformTypes.swap(other.platformTypes); reflections.swap(other.reflections); markups.swap(other.markups); } QString open(QIODevice &file); QString toString() const; QList containers; QList defines; QList functions; QList memoryresource; QList podtypes; QList typeChecks; QList platformTypes; QStringList undefines; QStringList smartPointers; QList reflections; QList markups; }; #endif // CPPCHECKLIBRARYDATA_H cppcheck-2.7/gui/csvreport.cpp000066400000000000000000000037061417746362400165050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "csvreport.h" #include "erroritem.h" #include "report.h" #include CsvReport::CsvReport(const QString &filename) : Report(filename) {} CsvReport::~CsvReport() {} bool CsvReport::create() { if (Report::create()) { mTxtWriter.setDevice(Report::getFile()); return true; } return false; } void CsvReport::writeHeader() { // Added 5 columns to the header. #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) mTxtWriter << "File, Line, Severity, Id, Summary" << Qt::endl; #else mTxtWriter << "File, Line, Severity, Id, Summary" << endl; #endif } void CsvReport::writeFooter() { // No footer for CSV report } void CsvReport::writeError(const ErrorItem &error) { /* Error as CSV line gui/test.cpp,23,error,Mismatching allocation and deallocation: k */ const QString file = QDir::toNativeSeparators(error.errorPath.back().file); QString line = QString("%1,%2,").arg(file).arg(error.errorPath.back().line); line += QString("%1,%2,%3").arg(GuiSeverity::toString(error.severity)).arg(error.errorId).arg(error.summary); #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) mTxtWriter << line << Qt::endl; #else mTxtWriter << line << endl; #endif } cppcheck-2.7/gui/csvreport.h000066400000000000000000000035251417746362400161510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CSV_REPORT_H #define CSV_REPORT_H #include "report.h" #include #include /// @addtogroup GUI /// @{ /** * @brief CSV text file report. * This report exports results as CSV (comma separated values). CSV files are * easy to import to many other programs. * @todo This class should be inherited from TxtReport? */ class CsvReport : public Report { public: explicit CsvReport(const QString &filename); virtual ~CsvReport(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create() override; /** * @brief Write report header. */ virtual void writeHeader() override; /** * @brief Write report footer. */ virtual void writeFooter() override; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) override; private: /** * @brief Text stream writer for writing the report in text format. */ QTextStream mTxtWriter; }; /// @} #endif // CSV_REPORT_H cppcheck-2.7/gui/erroritem.cpp000066400000000000000000000063561417746362400164720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "erroritem.h" #include "common.h" QErrorPathItem::QErrorPathItem(const ErrorMessage::FileLocation &loc) : file(QString::fromStdString(loc.getfile(false))) , line(loc.line) , column(loc.column) , info(QString::fromStdString(loc.getinfo())) {} bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2) { return i1.file == i2.file && i1.column == i2.column && i1.line == i2.line && i1.info == i2.info; } ErrorItem::ErrorItem() : severity(Severity::none) , incomplete(false) , inconclusive(false) , cwe(-1) , hash(0) {} ErrorItem::ErrorItem(const ErrorMessage &errmsg) : file0(QString::fromStdString(errmsg.file0)) , function(QString::fromStdString(errmsg.function)) , errorId(QString::fromStdString(errmsg.id)) , severity(errmsg.severity) , incomplete(errmsg.incomplete) , inconclusive(errmsg.certainty == Certainty::inconclusive) , summary(QString::fromStdString(errmsg.shortMessage())) , message(QString::fromStdString(errmsg.verboseMessage())) , cwe(errmsg.cwe.id) , hash(errmsg.hash) , symbolNames(QString::fromStdString(errmsg.symbolNames())) { for (std::list::const_iterator loc = errmsg.callStack.begin(); loc != errmsg.callStack.end(); ++loc) { errorPath << QErrorPathItem(*loc); } } QString ErrorItem::tool() const { if (errorId == CLANG_ANALYZER) return CLANG_ANALYZER; if (errorId.startsWith(CLANG_TIDY)) return CLANG_TIDY; if (errorId.startsWith("clang-")) return "clang"; return "cppcheck"; } QString ErrorItem::toString() const { QString str = errorPath.back().file + " - " + errorId + " - "; if (inconclusive) str += "inconclusive "; str += GuiSeverity::toString(severity) +"\n"; str += summary + "\n"; str += message + "\n"; for (const QErrorPathItem& i : errorPath) { str += " " + i.file + ": " + QString::number(i.line) + "\n"; } return str; } bool ErrorItem::sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2) { if (errorItem1.hash || errorItem2.hash) return errorItem1.hash == errorItem2.hash; // fallback return errorItem1.errorId == errorItem2.errorId && errorItem1.errorPath == errorItem2.errorPath && errorItem1.file0 == errorItem2.file0 && errorItem1.message == errorItem2.message && errorItem1.inconclusive == errorItem2.inconclusive && errorItem1.severity == errorItem2.severity; } cppcheck-2.7/gui/erroritem.h000066400000000000000000000063121417746362400161270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ERRORITEM_H #define ERRORITEM_H #include "errorlogger.h" #include #include #include /// @addtogroup GUI /// @{ /** * @brief GUI versions of severity conversions. * GUI needs wrappers for conversion functions since GUI uses Qt's QString * instead of the std::string used by lib/cli. */ class GuiSeverity { public: static QString toString(Severity::SeverityType severity) { return QString::fromStdString(Severity::toString(severity)); } static Severity::SeverityType fromString(const QString &severity) { return Severity::fromString(severity.toStdString()); } }; /** * @brief A class containing data for one error path item */ class QErrorPathItem { public: QErrorPathItem() : line(0), column(-1) {} explicit QErrorPathItem(const ErrorMessage::FileLocation &loc); QString file; int line; int column; QString info; }; bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2); /** * @brief A class containing error data for one error. * * The paths are stored with internal ("/") separators. Only when we show the * path or copy if for user (to clipboard) we convert to native separators. * Full path is stored instead of relative path for flexibility. It is easy * to get the relative path from full path when needed. */ class ErrorItem { public: ErrorItem(); explicit ErrorItem(const ErrorMessage &errmsg); /** * @brief Convert error item to string. * @return Error item as string. */ QString toString() const; QString tool() const; QString file0; QString function; QString errorId; Severity::SeverityType severity; bool incomplete; bool inconclusive; QString summary; QString message; int cwe; unsigned long long hash; QList errorPath; QString symbolNames; // Special GUI properties QString sinceDate; QString tags; /** * Compare "CID" */ static bool sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2); }; Q_DECLARE_METATYPE(ErrorItem) /** * @brief A class containing error data for one shown error line. */ class ErrorLine { public: QString file; int line; QString file0; QString errorId; bool incomplete; int cwe; unsigned long long hash; bool inconclusive; Severity::SeverityType severity; QString summary; QString message; QString sinceDate; QString tags; }; /// @} #endif // ERRORITEM_H cppcheck-2.7/gui/file.ui000066400000000000000000000030171417746362400152230ustar00rootroot00000000000000 Fileview 0 0 400 300 Fileview true Qt::Horizontal QDialogButtonBox::Close mButtons accepted() Fileview accept() 248 254 157 274 mButtons rejected() Fileview reject() 316 260 286 274 cppcheck-2.7/gui/filelist.cpp000066400000000000000000000072511417746362400162700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filelist.h" #include "pathmatch.h" #include QStringList FileList::getDefaultFilters() { QStringList extensions; extensions << "*.cpp" << "*.cxx" << "*.cc" << "*.c" << "*.c++" << "*.txx" << "*.tpp" << "*.ipp" << "*.ixx"; return extensions; } bool FileList::filterMatches(const QFileInfo &inf) { if (inf.isFile()) { const QStringList filters = FileList::getDefaultFilters(); QString ext("*."); ext += inf.suffix(); if (filters.contains(ext, Qt::CaseInsensitive)) return true; } return false; } void FileList::addFile(const QString &filepath) { QFileInfo inf(filepath); if (filterMatches(inf)) mFileList << inf; } void FileList::addDirectory(const QString &directory, bool recursive) { QDir dir(directory); dir.setSorting(QDir::Name); const QStringList filters = FileList::getDefaultFilters(); const QStringList origNameFilters = dir.nameFilters(); dir.setNameFilters(filters); if (!recursive) { dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); QFileInfoList items = dir.entryInfoList(); mFileList += items; } else { dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); QFileInfoList items = dir.entryInfoList(); mFileList += items; dir.setNameFilters(origNameFilters); dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList list = dir.entryInfoList(); QFileInfo item; foreach (item, list) { const QString path = item.canonicalFilePath(); addDirectory(path, recursive); } } } void FileList::addPathList(const QStringList &paths) { QString path; foreach (path, paths) { QFileInfo inf(path); if (inf.isFile()) addFile(path); else addDirectory(path, true); } } QStringList FileList::getFileList() const { if (mExcludedPaths.empty()) { QStringList names; foreach (QFileInfo item, mFileList) { QString name = QDir::fromNativeSeparators(item.filePath()); names << name; } return names; } else { return applyExcludeList(); } } void FileList::addExcludeList(const QStringList &paths) { mExcludedPaths = paths; } static std::vector toStdStringList(const QStringList &stringList) { std::vector ret; foreach (const QString &s, stringList) { ret.push_back(s.toStdString()); } return ret; } QStringList FileList::applyExcludeList() const { #ifdef _WIN32 const PathMatch pathMatch(toStdStringList(mExcludedPaths), true); #else const PathMatch pathMatch(toStdStringList(mExcludedPaths), false); #endif QStringList paths; foreach (QFileInfo item, mFileList) { QString name = QDir::fromNativeSeparators(item.canonicalFilePath()); if (!pathMatch.match(name.toStdString())) paths << name; } return paths; } cppcheck-2.7/gui/filelist.h000066400000000000000000000061551417746362400157370ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILELIST_H #define FILELIST_H #include #include /** * @brief A class for listing files and directories to check. * This class creates a list of files to check. If directory name is given then * all files in the directory matching the filter will be added. The directory * can be also added recursively when all files in subdirectories are added too. * The filenames are matched against the filter and only those files whose * filename extension is included in the filter list are added. * * This class also handles filtering of paths against ignore filters given. If * there is ignore filters then only paths not matching those filters are * returned. */ class FileList { public: /** * @brief Add filename to the list. * @param filepath Full path to the file. */ void addFile(const QString &filepath); /** * @brief Add files in the directory to the list. * @param directory Full pathname to directory to add. * @param recursive If true also files in subdirectories are added. */ void addDirectory(const QString &directory, bool recursive = false); /** * @brief Add list of filenames and directories to the list. * @param paths List of paths to add. */ void addPathList(const QStringList &paths); /** * @brief Return list of filenames (to check). * @return list of filenames to check. */ QStringList getFileList() const; /** * @brief Add list of paths to exclusion list. * @param paths Paths to exclude. */ void addExcludeList(const QStringList &paths); /** * @brief Return list of default filename extensions included. * @return list of default filename extensions included. */ static QStringList getDefaultFilters(); protected: /** * @brief Test if filename matches the filename extensions filtering. * @return true if filename matches filtering. */ static bool filterMatches(const QFileInfo &inf); /** * @brief Get filtered list of paths. * This method takes the list of paths and applies the exclude lists to * it. And then returns the list of paths that did not match the * exclude filters. * @return Filtered list of paths. */ QStringList applyExcludeList() const; private: QFileInfoList mFileList; QStringList mExcludedPaths; }; #endif // FILELIST_H cppcheck-2.7/gui/fileviewdialog.cpp000066400000000000000000000042141417746362400174430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "fileviewdialog.h" #include #include #include #include FileViewDialog::FileViewDialog(const QString &file, const QString &title, QWidget *parent) : QDialog(parent) { mUI.setupUi(this); setWindowTitle(title); connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(accept())); loadTextFile(file, mUI.mText); } void FileViewDialog::loadTextFile(const QString &filename, QTextEdit *edit) { QFile file(filename); if (!file.exists()) { QString msg(tr("Could not find the file: %1")); msg = msg.arg(filename); QMessageBox msgbox(QMessageBox::Critical, tr("Cppcheck"), msg, QMessageBox::Ok, this); msgbox.exec(); return; } file.open(QIODevice::ReadOnly | QIODevice::Text); if (!file.isReadable()) { QString msg(tr("Could not read the file: %1")); msg = msg.arg(filename); QMessageBox msgbox(QMessageBox::Critical, tr("Cppcheck"), msg, QMessageBox::Ok, this); msgbox.exec(); return; } QByteArray filedata = file.readAll(); file.close(); edit->setPlainText(filedata); } cppcheck-2.7/gui/fileviewdialog.h000066400000000000000000000030411417746362400171050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILEVIEW_DIALOG_H #define FILEVIEW_DIALOG_H #include "ui_file.h" #include #include class QWidget; class QTextEdit; /// @addtogroup GUI /// @{ /** * @brief File view -dialog. * This dialog shows text files. It is used for showing the license file and * the authors list. * */ class FileViewDialog : public QDialog { Q_OBJECT public: FileViewDialog(const QString &file, const QString &title, QWidget *parent = nullptr); protected: /** * @brief Load text file contents to edit control. * * @param filename File to load. * @param edit Control where to load the file contents. */ void loadTextFile(const QString &filename, QTextEdit *edit); Ui::Fileview mUI; }; /// @} #endif // FILEVIEW_DIALOG_H cppcheck-2.7/gui/functioncontractdialog.cpp000066400000000000000000000026711417746362400212210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "functioncontractdialog.h" #include "ui_functioncontractdialog.h" static QString formatFunctionName(QString f) { if (f.endsWith("()")) return f; f.replace("(", "(\n "); f.replace(",", ",\n "); return f; } FunctionContractDialog::FunctionContractDialog(QWidget *parent, const QString &name, const QString &expects) : QDialog(parent), mUi(new Ui::FunctionContractDialog) { mUi->setupUi(this); mUi->functionName->setText(formatFunctionName(name)); mUi->expects->setPlainText(expects); } FunctionContractDialog::~FunctionContractDialog() { delete mUi; } QString FunctionContractDialog::getExpects() const { return mUi->expects->toPlainText(); } cppcheck-2.7/gui/functioncontractdialog.h000066400000000000000000000022671417746362400206670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FUNCTIONCONTRACTDIALOG_H #define FUNCTIONCONTRACTDIALOG_H #include namespace Ui { class FunctionContractDialog; } class FunctionContractDialog : public QDialog { Q_OBJECT public: explicit FunctionContractDialog(QWidget *parent, const QString &name, const QString &expects); ~FunctionContractDialog(); QString getExpects() const; private: Ui::FunctionContractDialog *mUi; }; #endif // FUNCTIONCONTRACTDIALOG_H cppcheck-2.7/gui/functioncontractdialog.ui000066400000000000000000000036571417746362400210610ustar00rootroot00000000000000 FunctionContractDialog 0 0 640 401 Function contract Name Requirements for parameters Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() FunctionContractDialog accept() 248 254 157 274 buttonBox rejected() FunctionContractDialog reject() 316 260 286 274 cppcheck-2.7/gui/gui.cppcheck000066400000000000000000000004441417746362400162340ustar00rootroot00000000000000 cppcheck-2.7/gui/gui.pro000066400000000000000000000146261417746362400152630ustar00rootroot00000000000000lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) TEMPLATE = app TARGET = cppcheck-gui CONFIG += warn_on debug DEPENDPATH += . \ ../lib INCLUDEPATH += . \ ../lib \ ../externals/z3/include QT += widgets QT += printsupport QT += help # Build online help onlinehelp.target = online-help.qhc equals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 12) { # qcollectiongenerator is used in case of QT version < 5.12 onlinehelp.commands = qcollectiongenerator $$PWD/help/online-help.qhcp -o $$PWD/help/online-help.qhc } else { onlinehelp.commands = qhelpgenerator $$PWD/help/online-help.qhcp -o $$PWD/help/online-help.qhc } QMAKE_EXTRA_TARGETS += onlinehelp PRE_TARGETDEPS += online-help.qhc contains(LINKCORE, [yY][eE][sS]) { LIBS += -l../bin/cppcheck-core DEFINES += CPPCHECKLIB_IMPORT } LIBS += -L$$PWD/../externals -L$$PWD/../externals/z3/bin # z3 win32 { LIBS += -llibz3 } else { LIBS += -lz3 } QMAKE_CXXFLAGS += -DUSE_Z3 DESTDIR = . RCC_DIR = temp MOC_DIR = temp OBJECTS_DIR = temp UI_DIR = temp isEmpty(QMAKE_CXX) { isEmpty(CXX)) { QMAKE_CXX = gcc } else { QMAKE_CXX = $$(CXX) } } win32 { CONFIG += windows contains(LINKCORE, [yY][eE][sS]) { DESTDIR = ../bin RCC_DIR = temp/generated MOC_DIR = temp/generated OBJECTS_DIR = temp/generated UI_DIR = temp/generated } else { DESTDIR = ../Build/gui RCC_DIR = ../BuildTmp/gui MOC_DIR = ../BuildTmp/gui OBJECTS_DIR = ../BuildTmp/gui UI_DIR = ../BuildTmp/gui } } RESOURCES = gui.qrc FORMS = about.ui \ application.ui \ file.ui \ functioncontractdialog.ui \ helpdialog.ui \ mainwindow.ui \ projectfiledialog.ui \ resultsview.ui \ scratchpad.ui \ settings.ui \ stats.ui \ librarydialog.ui \ libraryaddfunctiondialog.ui \ libraryeditargdialog.ui \ newsuppressiondialog.ui \ variablecontractsdialog.ui TRANSLATIONS = cppcheck_de.ts \ cppcheck_es.ts \ cppcheck_fi.ts \ cppcheck_fr.ts \ cppcheck_it.ts \ cppcheck_ja.ts \ cppcheck_ko.ts \ cppcheck_nl.ts \ cppcheck_ru.ts \ cppcheck_sr.ts \ cppcheck_sv.ts \ cppcheck_zh_CN.ts # Windows-specific options CONFIG += embed_manifest_exe contains(LINKCORE, [yY][eE][sS]) { } else { BASEPATH = ../lib/ include($$PWD/../lib/lib.pri) } win32-msvc* { MSVC_VER = $$(VisualStudioVersion) message($$MSVC_VER) MSVC_VER_SPLIT = $$split(MSVC_VER, .) MSVC_VER_MAJOR = $$first(MSVC_VER_SPLIT) # doesn't compile with older VS versions - assume VS2019 (16.x) is the first working for now !lessThan(MSVC_VER_MAJOR, 16) { message("using precompiled header") CONFIG += precompile_header PRECOMPILED_HEADER = precompiled_qmake.h } } HEADERS += aboutdialog.h \ application.h \ applicationdialog.h \ applicationlist.h \ checkstatistics.h \ checkthread.h \ codeeditstylecontrols.h \ codeeditorstyle.h \ codeeditstyledialog.h \ codeeditor.h \ common.h \ csvreport.h \ erroritem.h \ filelist.h \ fileviewdialog.h \ functioncontractdialog.h \ helpdialog.h \ mainwindow.h \ platforms.h \ printablereport.h \ projectfile.h \ projectfiledialog.h \ report.h \ resultstree.h \ resultsview.h \ scratchpad.h \ settingsdialog.h \ showtypes.h \ statsdialog.h \ threadhandler.h \ threadresult.h \ translationhandler.h \ txtreport.h \ variablecontractsdialog.h \ xmlreport.h \ xmlreportv2.h \ librarydialog.h \ cppchecklibrarydata.h \ libraryaddfunctiondialog.h \ libraryeditargdialog.h \ newsuppressiondialog.h SOURCES += aboutdialog.cpp \ application.cpp \ applicationdialog.cpp \ applicationlist.cpp \ checkstatistics.cpp \ checkthread.cpp \ codeeditorstyle.cpp \ codeeditstylecontrols.cpp \ codeeditstyledialog.cpp \ codeeditor.cpp \ common.cpp \ csvreport.cpp \ erroritem.cpp \ filelist.cpp \ fileviewdialog.cpp \ functioncontractdialog.cpp \ helpdialog.cpp \ main.cpp \ mainwindow.cpp\ platforms.cpp \ printablereport.cpp \ projectfile.cpp \ projectfiledialog.cpp \ report.cpp \ resultstree.cpp \ resultsview.cpp \ scratchpad.cpp \ settingsdialog.cpp \ showtypes.cpp \ statsdialog.cpp \ threadhandler.cpp \ threadresult.cpp \ translationhandler.cpp \ txtreport.cpp \ variablecontractsdialog.cpp \ xmlreport.cpp \ xmlreportv2.cpp \ librarydialog.cpp \ cppchecklibrarydata.cpp \ libraryaddfunctiondialog.cpp \ libraryeditargdialog.cpp \ newsuppressiondialog.cpp win32 { RC_FILE = cppcheck-gui.rc HEADERS += ../lib/version.h contains(LINKCORE, [yY][eE][sS]) { } else { LIBS += -lshlwapi } } contains(QMAKE_CC, gcc) { QMAKE_CXXFLAGS += -std=c++0x -pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar } contains(QMAKE_CXX, clang++) { QMAKE_CXXFLAGS += -std=c++0x -pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar } contains(HAVE_QCHART, [yY][eE][sS]) { QT += charts DEFINES += HAVE_QCHART } else { message("Charts disabled - to enable it pass HAVE_QCHART=yes to qmake.") } cppcheck-2.7/gui/gui.qrc000066400000000000000000000025521417746362400152430ustar00rootroot00000000000000 cppcheck-gui.png images/dialog-error.png images/dialog-information.png images/dialog-warning.png images/edit-clear.png images/go-down.png images/help-browser.png images/media-floppy.png images/preferences-system.png images/process-stop.png images/text-x-generic.png images/view-recheck.png images/view-refresh.png images/showerrors.png images/showstylewarnings.png images/openproject.png images/scratchpad.png images/showwarnings.png images/showperformance.png images/utilities-system-monitor.png ../COPYING ../AUTHORS images/go-home.png images/go-next.png images/go-previous.png images/applications-development.png images/applications-system.png images/llvm-dragon.png images/verify.svg cppcheck-2.7/gui/help/000077500000000000000000000000001417746362400146745ustar00rootroot00000000000000cppcheck-2.7/gui/help/images/000077500000000000000000000000001417746362400161415ustar00rootroot00000000000000cppcheck-2.7/gui/help/images/index-mainwindow.png000066400000000000000000006104611417746362400221400ustar00rootroot00000000000000PNG  IHDRMQ>sBIT|dtEXtSoftwaregnome-screenshot> IDATxw|lKHBzPEPPAXPE+bAV\) z !M̼ T~>#)9\S&OYON 4:E!B!B!bZ/}/-Z3yzyyھ-nq-ͥ%a4/B!B!(qqudZjF& ˫_R⑕9]B!B!qu6WlQG*// !B!B!%L~Av:O2_cvtr%B!B!ntl%~o)~~a;(!B!B!ܲsScn3^`B!B!rStzAQ5]PB!B!S`P5MB!B!BjNB!B!B)hB!B!BT2HT!B!B!!Blrrs/wB!B MB7n׸m$RV^F ثG]:UUei^v;+V߶v Owɤ qvvngi:> M+sl{#(/Dsˍ otfs<:NIq*...(Ҥ.?5|9I!Zstv_MT! bxqwgCCQULn-VohLH'&O)(,kM3{NF;-p$7罄Ksr2ڢ9@Sk+!H"^^M*s\×BWW;jgן_X6ڵG'{m:8 2H!B- ;@ߘL.\]_,Ve;0w@4 ;ФVRV^ά^xr [Uӱ` B!i"Zp8g_z矞(̞;ߓShrAPB!7wOuԊ^g~):MNv#-==Ʉqcܿo_k`XXlE~a![[neX(rzc܅ɉkG_WPXTgwV:w)WhýyYNgf2{ v@P@C7˓ܼ|-Փ ݿ>b0 pąV9>ԿIN9#u~)k7lggѡmƽgv691l@'p2-4nmj&_bEH/<$EVuAa!a6F#[pӝb2⃅Mz>+ |v;O2V>}] kX@!MR@㷍m=hH}.̃ I}UPSR n֌Q'B!%u2Vёu^夝Jsgd;s0p{s!WD]qlNn.6*>k$CUU{y>[[th׆M[~>r[Wٕ>+32ګL哛OmSakGpP "HL:V9=;ٽo?~>o]glaW+~qizVf7+WIm8xO0ٔnApݘQ\3Jr8xc'uNu] kX@!M~2rڢ9'NJ zm>&}7hT!;r}Hp3̝(ݰ>kѶMT=ҡm^ՆTN6[^B_.Uk9@Aa!ǓSrP6vGYy9'NYlٶbiM7;Vo=rxֵG8zx~%-GC#~Zq@|[? ]Éide0@^DCCTRO(B9L,xyAA$$eמ}t؁O@Q_~\RΫn۸y ?xx:VCIu^ kX@!Uu}/lvL*2`tot B{gaM=d29}zཏ?T:rݻ)& ^]RRJjZkšuT;^eǟ0l+a-=Z'O[Nh՚ÉG)++cמ} <i|jD`qO{ kEQ4q|T4B!D2| l6 ~DV& )T$vﭘ_3E:7`] ~fC`"$aEQz( }'Fk~Й @a,|3?B!նoߵ-qj\j6G<$*\/ҡ]c:e2w7ǏH֡E`t^[S;Ù֟>Tϵw? B:Mj]i''[ ,h6 o-GtdcSc.YW! 4Ĥc==-pww#9%>z0֊]9<6YZGd[.z=׎=η=8(fA$&^mTk'S%!:ݻtf}<5EIکtܸsڹ^e7eǼ 8U#)-4//x1W fC0}zւ @xhs{v(sv4nFߙ9A%v 2O j;UۻwDxh '&UG?؉;cQ)]-B(ݻtbkɩ>TMTZ]ÅEEU?B!_նͿԺTjEAS۴}.CmBNVN:4w0Mn]_WeEYdEYj_ݘlnaZd)@#[QooM!<;3}``άӓ̬,n؄nq,gTu?ث̲?r"- Ɉ *{գhL\Z})'?Obӯ9'":-Bse+:|.:v*S<:>\uˠ~}PU0i,5w}Qt:f?9z;C ڙy/<{5彛0Zbzt#%56wP5\ k'","˅XRk].UNԺ\<} Xg<}I$9!Bq S""<5TvPn0i.w85,m B!B!ąu}*ѭ[ B's !l]Ii)8Lj|ׁ\B!3\B!?3͂yީ!Bqa!B!B!8C4B!B!B*dNS!B!B!BwB!B!B?I !B!B!D/B!B!0IT!B!B!*|!B!B!vB!B!Bi(yyy2>_!B!B!ΐB!B!BQ$MB!B! I !B!B!D4B!B!B*$i*B!B!U4(iw^|||j\6l@~~>&L 664|||8pE Oo}i3gsl/((ĸm6|||عs9JKKi׮+W1?  ^ub\/qm̙̜9e 2B&B!B!И>s@4TUEӴ \c̙3޽{W[Uώbƌ\M[nDGGyfzQmݻ)((3X,|,__戄B!B!04֭5nPSi朤i})¼y.pD0LL>>lڴ}.\ѣ8q"}vBQ {9 ɓٰaCso&P1dL:/_ Fbbb) JKKYx1F18U pI'|XWPP;vh9^I&1x`غuk .W^a9q;zfffs7ӫW/O~i8p42zh,YC=T ݥׯf"55.\ [oqUWq7?{ !B!B4*iw^KMstnŜ9s{y/hjǓ@aa!_=vW^yǟ> <<9se ڷoO޽BEMNLLLnL<իW;߳gf$۾}sM>B>SF+&L;'G9s0awy'7olnFΝ77x#YYY5٠Aظqcݑ#G:t(?0[la˖-]MF@@EJJJιȨۙ6mW_}5o6~~~vm_|ƿoFŽˊ+.HB!B!5<ʔ)~ 11v 8x @}]&NؘPϞgϞ\???{9z=P17[oUcYmڴŅ͛Ӿ} N㦛n>`0~znVj|8 , 33@bcc>}: .ɓe&MSa֭XVǼCw%99~ {o߾iժcƌAQ&.B!B!j֨}5c%88ƄQ%KpWָ 8Б0T#<dѢE|Wݿvڵ+޽A?cvEpp0y |rѣGZՒg "77C駟ؽ{7)))@EB&C dtܙE/WgΝ̞=+V8憽\۶mY?sL벲2VZŪUW[OSzGnݺSO8?!B!Bq5*iXxzziӦjAT.\g`ԨQRXX]v=u3'O&66:t@XXƍcӦMDEEa6ҥKc?uK.8g>o[ng{aĉ,XѣGIN[?~;6l@QJKKիc{aa!O>$?8wT<Yf<#\}նyyyջ$d !B!B.iҴuGn.ek׎K{(_믿Njj*SL5Vv:t(&M1|_~̝;Pnfشi!!!رWWWu1a۷ӥKl6[\s5L6bjpBO^}AE+dϞ=}NfggcX6l3\!B!B4uqq*MǎyxꩧСCw^džJLLϯ:OOO;v,:O>$7pEEEdӇ~mNddsL:V{(TMn;wLJ^zQ^vh֬?}BPPr ={]vu_WL&&MDdd$m۶`00aBBB.ÇyfFy6OOOn6,Yu5|pϟχ~ܹs ovlѣ=fb߾}s9rdexӧcsb_sѦM6mD^^<*:p}'ضmsҥ nnn̜95kӷoKB!B!?] ƨQ/w8… ٹs'~E!B!4Bڽ{7 8r"B!B!=FǧyyyM .o|'9bmu~6 %O!B!B/B!B!U|!B!B!BB!B!BQ!==r B!B!b9MB!B! ;!B!B!Ӑ9MB!B! I !B!B!DM)MB!B!4B!B!B* ;!ű~ Ͽx{{Kn[Du !B!,ٿ?%%%]媪:tn"}!:trrr.HyB!BW燏m۶=gi*PAA[FQl6EKαc4Mꖺ\wS%$$ХK&%B!u; $N?_s[ur> +jا^]_R*))u+JM UOs! ?w)/+Ӱ{9nvk_-[0dhٲejs45wgͼk/|t:EOv crcVvtz%8S,;yh,,hT{){]WWg/7#LQq̊~w,6t>&s?IfV.%EyxKTޱR}VUkײjhXzt:pvoO :on| vAB!B:___6lpqqq^k3 zz V#G;V\3Tݨg1x 4UU RܜxT/¶m;\T4ڶmرc]ǞU :GSٹm1>;:FX[l_/aܸqDEE՚\bXz>vITV!NFҫCsۃ@&Uݻz]8yx0/ 3GL IDAT˱Y09;3uh)?S*ʅv}t3Z5:Ə1˰ZXYHbjGYY+~|짙pսs Nǟ6ѵGo\9wwWLFK- X}1ҤX 5k6W0|6n?т&t~/|LBBIŃҪU+P!772F'B!>5}O1iڿ_lٲ$TU6PCñ&S6ȡo4]Q) Um"tIYdb;rs+?w Kk ?1yE<ί&%Mׯ_Ovv6ƍCן3SUU, ;wkiT=~I7P'`:v@ҙDiI߆zxwxə4 o믗0sL4Mz),ŬSz5n닶iQ1p )Il1}ɳwe6~Fn6hyB# {zڦ >C8y2z yYJK8y2;w&jeĉLh|ߠhtzF.ͦi6,=e ?t&&{ 'EWubH*[U~?M3P,4W&o5]+~=(:E:SUB`˗ěo olyCE_v0jzg&RՆ*iە @3sicX6NCn9vo}ѣسgs~=cĉ8p{n͔)StB!BOV㜦Ugj%??VZa6SI;UU9raaa_ *vJȋ Ti6=E͹fP;ݜ,+%f+|?<,ࡅeZ[ntڕ, 쪉ltYbW_}uR͆No8}O H %P5M#++@ܸyWӈp죪9=I kv|8[ cy xZ@i'nȾQl~-RhtNĝc{zk"~Y(\6 $sRN̟Opi{f33Hv>a?NK5xFD|}Q[GTNu;Ώb0PUOEAQ@Ӭ8J)՛j= #9~ .Wz>^7w]9q2VU[(~z6 ?o7H)b+E#$ _GjNVwQq!7:ڦfZ7c934!ul~ϿHLIHܜjC&G/I^{7oތs?cPu\uUW @ݹ|֯_Rsʫi]}ϻ)l6;z`O B!4gߪm߿?6tv4MjcWTTė_~pn!!! 6 Fh+NQ;cC$g~[b4˅ Ow -{OpkpU?{EXv-yyyt֍,ǜ:Α8Lfر#dժU5AE̡+3=NT^l[ !ج$ը󩍦iG߾}8p 0嬡5 Lܹ^tC-V;.|Ml?Gbc0ut:2vg}q>N-£Y˟nht@?gK}u˖1kq-(w"qf4ݼ9-ط`N#M&CFuNt^^ԉ?NqZ`-I˕\64thj)u .n>v;|8 iT/.ǘ^QѲHN/$7ۓN3"ʃ\|.Nf>mӷX-$%3 EoDQ zڷґj^qVwF7u dk8ŵ1u蔊 \L:dϐٱs?Cip}?>r$u Oǎyg2ѣGHNNfΝL&9@JJ ;vWH4h=ݮb:|}5rF|zB!BqW> bR^^^-aZ |}}qqq9ӳKq$N>Mqqq㓦gdXHKKױ=4UE7dffXؤ;Fjj*/ToXE[)-hulڕL\E:G6# NzN1;E[#fac_Ib:ht;4emҥ-ĉM;yU5TME++Öt ='~"2CB㴾66 nn4•c2z_;XЊ7E↪iVPhMUhZ zQ6@Xwjҟs@44*RXlh* ft:W4b4.Y;68a 8 I:ƒAoГpfWѩ6[xr:mcKb>&tzPy }EuOvU#Lߨ0mк?GFW?TZ;, /ӧ0235kNDGOOdf&/;M0cQU P﹓Ow>J%oI눆'exB!B4]VkGQl6Vsr͑ȬHTvqieӹt%( x9brBU+=L&Sdh}iͮy]?ť`2]<ʇgoEb{|uA="DZ]gv4lxUϧgϞ;^^^`X^ϏӧO;mڴȑ# opf֕_=nbzr)WzYF:L~|A^.wss̴iA$0?{>1_vf5'ش+'+F1: D:G_'ӱ3ň7Vpqn0~F+fN̟k8Ԣ)R̿M1>~^[ iŒ r/f7dյ+^xcbtvWy]x ZyMfAwC( ݂Z1L2&w%;qy}.np:ۇWP_XtUXeV2=5]'շe 茢3Hu]tg*szME3Pm=>J]uLf M(NNAw'fقs>؊'YuSMu[,Uzjt:ڵkjdr!00/= ֝5խi;veʵ٪%"e(~^()-Š7`2l6'5- D\Vc=ui!!!]vq)F# 81~z>̽[蒪^{53fL>}<9r+Wr7̒%K(,,{*ZVVgG!Bpl߾E VH+6PF:ԉ Ndtv,Ν8mXJl]q|w%7lvڦTQfr hIpH<\ [z}HNqIӆ4ݷoݻw'==ݑliӆݻc0~EaРAt uZRRBNNQQQڵк3p[SwOaS&I~?^/1x`cIGqOoo<}(cSO{Q\\Hn`lG E[pr2dD%|A1W gb#DȦ]ɍn|F2-ex8hhgj*y`90v4Ԓ hMEl[N%ln}p0z??Lc(9;'MĻx6닇[èRblSجmŨBN&63vg/#-)ˋmT %7gU/[w|MFzwh$Aز?mOC1Fк ɉ5tF'H9]ϿbsS I^aN(hQU EgQ4;\Mz<]I-^PP5k#-#nJJe͚yԩu'C49 (6|4Ŏm={Cl?Zo'''HNO>aΝ bڬY3Ə_mLUo||5~ׯ_k( jzv զO՟*Ղ+X-7`8ƙnsĉz^Gm 2e ↡x8"{qUmE<.Fs̨QsV&Y_u:v{ 0ƍQRR_9;-ߍ'}0-"BߑH꣩;ʁөxC l Oh\OMݹػw/XX߻wobE3s,]ww1zNr؛xC;sQlv"ij2 zFF-Or:\]Llv6;wp&~ 1Q_~KT4#gW]C|YZكWŒvP~SsU))d;Ƙ<}ѵkWYbcһgO~\ɓqPmuZVZQtfgCSZK@)Gl%nI)3e{pXz[t'hˤ(.PZjFVa[1ƾ9s|QZ fYB7[0a@çu8#m7EΩhq%[lWS:cV 6L.Ad4;l`i8yc2ݟFYY]x L&;Uߢik昶`ȑL&6ҳgOƏTc_Cnݘ8qb=kb6ӊNv$&'4$-(*.!2(W<]ILJAcd@QhtDm 7@ǎ̲eXz5Zǧ^>A#ӓΝ;׸m۶m+B!1iژ'*\FI2;ZTVBnW4j|A!ŤSxO\m>>}0h ͛7W*a|v+)X hN¾̓ϋ&hD \b`u]W׊䞫k xyOHH`װX̚ dff[e]Ѣy3TUnނGZl$OvklFjZ&>xzzb)+7WNSj.Et_6mD˖-%L G歷"..QFqq ǰ_usO?q!f̘(+>>xrrr+ ::Z}> ]w^:7|ϙ6..k8---e;v HΝB gήM|z{aaTB'O@do(LB%%}7u̫xU{ :ݚjwFsB63BCQ ;;5VḹQl˯HZ,NcX,pa> ?JFFy鉿?u{>ҷo_HMM=g7|CAAwyyEEE8pں jm۶dffk.&Ov>35j%%%lڴ UUeKKKٸq#]vh4Nvv6JT!Bo]89;+u gjZX*^NFJ m;2T4U[q9V$%+cXSl45Yj>Ťg}} '̽ss<`X[<,&?تT:zꟖo4۶Rçx F:^( Wtz., gQQqо|ƞmEE*aOu?ex %,,{~M&BFh|EKƜs:ud4<<ܟ}Zvmv9sv:OJ 68{SN7!B!ĵҦ竪JÅA8hZQ\GQãpy4tNQ9 ?x*y~g }_}5G7(jhRmN3F (( 9\LA퓨b};nӃɠl}=?3ඡ|c_ uah߾=۷/t[Zlle-ìӿ@ۺSp}4O\PP\.;̆Cls5jORXRG/*LoL8s,>XGڄϙ;p=N'dڴiy^3VҹVkCSzv~@t8ǣQZ{!%_LE%'{AF="N&&GCzӑMPrr ph& Μdl (Bhhwz( AP{4kтC~ݻ}FՄX= CQT R?Jpr*(:4[ ?аKgN-Y&פ(htkŬ`zgqt>i=` $Ĝ_sLW4 */Quz^=q==ҁWE 9WUG0j89Fɀ1BaayU/b`n6L:Gиqz~yEQѷW#%?۶ΌfN'O nwdη8==!&s]w}EQЮY9ܷN: (?p|JI] UQ8p0 F}cMڤav AUUrKdgddh"n6lΦ<}4N5j\pBSjժ=^z;*ԗ:u ϗLsHT!Bk\AӊL-roNu/=7QˠS8k`MMJGXX^`?8/=գixa6ni4ZIMצ>+o\z;fc`oʽ`}z֬YCNNǎ8c…bh |%ؚ@a~&:c,@L"T}ul' u7b5sc$7Je™4_vKLזJhx$ocGٵy/=$8}YSUQHJ`SԯO;*St:7>Jňl`0`q=ڍnNJ+? 3T|z5-S7*ھ})ŁAǿőq$wywMQn]#G~YqE_FEQ_u$^x Bfb '?#ެK,.g^ipNE5dm[f+W~uy7{΃ct.UQ8| =ZzvԺ ɨ裪V}Y^ӳz_V_(^e/Nr <VoXKZ&l޴-:_ Lh^y9-[OnٻXvԪK/MfIhVMR e~ؼeyyZ>~Evg\;t`_ѻG'֬{_9鈎V+{ 4'Nмy d ^:Ofݺu4h0(ȑ#8!B!ĵ̚|z|1:ӅSs*"7.F U t4u+F[oyGp8ync|+RC*!(Ć8}؈ҫP9~,U3ذ8NdӤNtS5M#''"˸8N:ž}8qCrJ:f?FcX9j̩-TQW\NF0v1 P>8T4T]~]AR[Pu-=3[K-GiݮaØ6mO>$gϤ 6,iX/?؋-?7k TUa٦4[Λg1iX+fM +|+4!C[T]ły]rkl6Ӯ];$??NjEӱuV^zm-{JDŽvOӬYێ]o;""g|?&"<ȨrrY7f}nGs;0 t@m۶>cÆ 4oȐ!CXp!CѪwL)(..6mڰpB>snr;P%~&$$iyyy\wuf~ !B!~it:||1z˃MU̵9gno\6AAAep8p8vߊZdP=nwb's,Y*3G+vs+ߦ&uZ­yג8=ݘ:"C6mڰuVׯ$'',vAaaa ƶm(**?gZV{-U"O_-$)Ux"& Ȫx<@LR7m]ϱ'<LٖnO˯{ҵW_vڇdbСiɌ[o5R3a>edT ޠclDAލ5"7.FkhU/%i> $LYH -hM/[I1Qkb/[.UE)QXeD$'Ä_UUTRXxq> 74jq㨵n]ϓ!='3?tb4d3ӻ-QE`jMJ=UʿT:&9Qxlg (DQ@SIL=,Aǘ O2L&o`@UDܕp]F8Px/D1p&|Gap*;vn7>Y'_Ltx #Yft-[GyH6!%%?gM"2Y~+5( ; TM=EgTVvcFg.]Y&egffFll,V˗c(,,dÆ *B}[wn4 x}5Ap )Gz=_~e`+U+"#;4ѣ\  &oKtR"##Yz=p@֬Y 66B>ڵ#==XRRR.V+j (,,$q: La~&ƴ%N&u?~CA7|.-g _Zc>Z=]Υ C^^MӰt8?_BZѧ}2_ɯ. ؼ܅yk1 zz)lV*aLx ؜1W"QMp8 NO~HHbU%84p)&iN=[͆^_Oz~2օcpOFbbNNcԩFaPoCQEFQM(E1(:o ChT_KMH a`xGz7Ж1H+;܀@6;‚@MjPz;Dq\ՐegO8den;jԨ~e4-p<7x!kצڞlB:LNjRn7Vģ(JkK!!*ckR+9E(^⠒awhƌ[nQiW&/ՌF+#vM~_`2,]EZ׆ܲe?{'/nV+ ݻh4ϧ;V]? 冾d.V|Bc0;7qtv4@V"NYG#oOnmm,EQhժ:gϞ%..޽{_ īz0c\ƌ;y)qLJ*Vją0)ێ2Ews3nBޣ%7͚D֪Uw¸u"f3h 4Fd*|B(f3DDS1JքUUE4<ūM&d8Qgx2])[pCQ(Ug`IEBANz'O[̼и5Mc|<BY55 7]mqؽuiSOuU~8xn=D`nnJDDuz͍+`"1qD_^0=t՞v9yr ZUIj_&#6-bӦu`0~'4ޞUm< }FxJ gOêO# Pp<㎍#:z kXI0vNL&#Z](r%\B!Ϯ\5M+"66nM0E p9]&,K@yM7YcK.:حV+DGGSZ!e G4f3f IDAT7_p"m^s}}pk̺Pc6l@xx8͛7gj Xl1 i!!!ƹa޼y̚5m'MDRRƍ>T@ԩS.k\EhxB!B/^̒%KhF>}*t g֨Qy|DDOTBq{wxwp:޽?ON9s&ټ+PfB!e97`_ЫWYLSM`A&MXt[߾R+Kq*yl<̟?={޽?}=z{-Zp]w~+!l6l6%={6;w,Z͛X,m=iii|G ##ϸqhѢ[laΜ9t ӧ3c |M, <[nbuRvܙWi`Ĉ̜9︸8/^]wE&M4iǏ/h֬'Oȑ#Woزe ?M4aٳ ={6>,/"iiiՋO>T`tҬY3}Q222p8<_R=_bXݛ9s0a5k=͛Y~=cƌ!55Ǘ:ٳyg1c:t`|g:tSҦMڵkK婧[nhт)Spԩܹsڵ+'Ndь=Ԙ[),,,ϩB!װ enݺRυS^ f?s =z`ĉ 0ӧO/__9B?D]DD'NdرdeeѥK֭(n7cƌjժL>}Y\.r ͛7~ %%ݻwn:>C\.etMtؑD6mСC9qyyyL<`jԨAZZ} 65k+Bٕ܌\>#Έ#xw3f -BO?q}1}tFҥKZ_E\jL[nSN,`ذaDFFұcGMΝ;57oތdc1s̀ce,طzz_ҦM3f fܹocF4M_SLaر!CPfMqw?@Yn=z&LfcѢE7 ( s1tP~bccq8Ջz{X,B!+wrn9BZZZmƏ3K.>&\.㉎Fד}ذaKo`02rHKv,i}|<4oPڴiÇILL_fҤI)5blذ἟I}:ٳƖ:VLL ѱcGƏ޽{]6-bҤIhv}}ZXVFX;v, 7f̘1#,ӊ^~aaafcٲe,[QF?77Ԙ<| oFm 4<﫲gqqq={Mؽ{7_=]{!55Bf՗ހ󂂂2 haa9^Obbi+WwRhFxx8z wŋӧWSN{W!*UrfԹ˚5u1VnvLRy_ƔB\KjԨwߍhd̘1<#?vSOLLd^(.刉aĈ|(Baa!-ZVH*U8q"{.ڕ)nv%yyy,|p8(**ʺʺo?ϟ?9}ytޝ'xgyŋ3dȐJ !B+/??5k֔`pUᚦk̃sy>]d_qP&عxdvrYn0p@~m\.wuhv}/TW+R*˧O>5\Fdpm^lKe)Bٲe {y4-^k׮^]_+b#UYkFٹs'ǎjժY&6m}̷Svv6իW//@=%B᫯w޴mmqN:ՋssV!*B|||x(cƌAQ6mĝwIׯy>@.]={6#G$//9s# SNL&^S3l0F_J!B\=|_ T]hMhڴ)Ǐ㩧"-- 2gMFAA[/R"Ws<,_e˖EӦM={6ڵoO{iӦ 6j宻bŊ4jԨb^*k>}pA&L@-xWt GBBu)}BdG妛n*/_ܾU{^zZw}ѣGAZZ6TRVZdZnݘxW5kǏ'55|rҲqkƨQL2:u0h 6o\qϕ xB!>JVV%;\n-[=#WUUٽ{7uR)\.={dԨQ 8]k1u ]GwG!BӦMHJJ 8tΝs^VMS!9x5ڼy37nm۶Z;pmI\]~k|/XÇc4B!eS/B%K0|pbcc!~/^LΝ!B?rgR(U!j-(g6m4bk<\cW+^III9suB!Di:+!jt:\._>y-kqL{]r !BQZ?B\N:GwOZ<ZB!W+zrM t%!B!B!bŊ+z|YJ!B!B!J(wf8.N%&:% L!B!BW urmpȠij+3!B!B!_ɟ&hCJJ}2O+w@mذoO+B!B!*-8c {1+| ol&""Th4p/YEQxtRB!B!B\]*-4333f\VƩPd/`0PXXH^^999䐛K^^vN6:t(/B>czAy*#Gزe 6?gx4h@޽3}tZjE׮]Ypa |fΜIvի ,(Urt{ѷo_6lȤIHOO?o,'Oe˖~[dggsiL&7oSN̝;crq.xn/6^z%&Ly0`+Vbh"V+> <䓜8q_|B!B!B\TwG x_jVV\qTUUiTfUV4lؐDVZE.]شiSzgfܸq<o߾zŃ>@vYr%]taڵ|7=zl 2۷ /pQ9ѣ?hرcZ Uo~9~8{oqƥ۵kr-]g,ӧOoI&wѫW/q^~eV+Ѵk׎1cƐxB!B!JFEEq=Th_MVV69sƟUZ24((_JQ]taϞ=tMꫯ8q"%K[o]fǓ ޽{thݺ5lذ\|9wQFL&ZhADD4oޜSk,ƍ#''jժӺukڴiSjGf̘1t:yO/g B!B!DeLJ * ^O||SҴiSy4oƿPO޽Yp!_}z+|I@%f̘Qx뭷[ʚf.\HJJ Ǐ&''Q2e1b-B||<'NԩS(¨Q/0aؾ[n~o$"##ٿ?'OfĈn,X`l6JRRR2`ϟɓIJJbΜ9Zge֬Yw};Z!B!B@)YYY,n:w~E;iF 0WB MXӉcԫWWAll,k׮UVenrJ:vHNNaaasB!B!Z8]4EAF+-j{!+$r$Ar5F{%X` (R\K1~gev̙3gμ{x^߿_ӡx"͛778=_Rn/ 4IJJ"99 Z-XZZR|yʖ-KJRSSLB!B!/&hjeeS g =-[$***B!B!BD aaaqqttDQ a!. IDATSbcch48q`:۶mVZdff:߂ ׯ+x!o@#}{!B!B!74}A)Q{fSEyZiթSٳ'ffMN=W\)p!B!B!77oAAATXi(d4@/ PLLMMj FCxxx(رӧy *{#B!B!ijTj=M7o^z*:iiBRR $$$HRR阚B1{hт:a,X///v`ժUt&Olݻwi޼9gFׅ 0a >\1ҧOVZE׮]$,,Ǐ(֭[ݻ7 6dƌzy1M͚5pwwGo͓-==e˖ѵkW?>III ,`ӪU|inXʗ/Ν;2dL2?So}ѦMuƷ~+cB!B!!JuLӒNMLM`_!Ƿs>]s5={6dddd_pj׮ d&SK,O>Y:y$ք1h  Z-F޽{c_^]~̙lٲq=VVVzy~~~~4mڔӧcbbRh.\@֭qwwgѢEԩS-[rq5-[pELœ9sXv^ܯ>pBRRR ܧjҭ[E5̧( ӦMcϞ=L4iӦ2zh5 h"lmm ͳOS>LHH ,,}pBn߾M^HMMU>}:|tޝ!Cu'B!B!^>@g\\+V/^zִHMLLԏ(f4((-Zၳ3}9sFo<> h۶^z]vwˋ#GpAڷoٻw/j/55 2h Ο?Ohh(>ܹ3iii@VO &`ggGhh(>ckC,X͛fΜIr倬c{GJJ ӧO/cǎnݺ,--quu^z\dŊ\v*U󻺺O˖-?}1088XMW_Օ~]лwo}ʇ~HϞ=K=h/B!B!/#FkYVK\\Kll,<͘}UX6m/5i҄)B!B!^}tpp(ˠLLLX ʖ--VVVO5h `mmM2eqC?N 7cbb4e-vvv\v۷o3gXz~NJ\\[lQ/VKrr޸Re̙'OiiijϜʕ+Cy͘}~] ˓B!B!x1ZS=/Mx IIIꋠ=z~RRRJx=>Z~E\\\UVVVx{{Quխ[Q牉)0^zrJƍǏ?h0_tЁÇM?s ݻwW(L5V`|5թS'Or uڝ;w8yz ǘ}?!!cǎjܹst [[[$B!B!JmLӒLQZ5,--45.pZjUt/r̘1iӦaiiر#ZK/HV7X[[oOÆ 4icǎeΜ9K?*UC֭fĉL4 N<;Cΰaøz* ˅kʔ)xyyQBZhO8vQ|3n8?~3{-tGGGټy3Zի dk޼9}eȑL:sss-ZĉiذQyiӦ >YfIHHz|WjՊ֭[}N@@-Z(N|ڵU2`;uСCz*۶mߟs2g Gqm-[Xt:6m7|ßI޽3f w߱}v"""puuwޡ{'Ny7ܹs~^yLBӦMW!B!B<]ZBO+NSΞb(񘦮|wzWW"ebjJ >=MQ<Òv̞=2p@222/\ k^^^ԩS%KЧO5 pI cРAߟj5j#,,Lcu3gd˖-70GiӦL>] F~TPoзo_|||_> .QFt҅7oY_z|ruFΝ9wDnHKKO>gϞ_mt̚5֭[3fاrJ|}}=nK,믿we?EQ011̙3tԉEѮ];z-~<\p}^`hтKR~}uQB!B!⯓3~3pj4X"AAA̛78 bŊEJK6zY 511Q?PaӠ g:tpL?3 m۶zuڕw}///9i߾=Ǐg޽DGGT.\ȠA8Ho%͔23_ԕK76h#<<___VXC7;o>ׯ]*Uk׮ 2iӦQbEfϞ'ѣG'piv9B!B!FF}*VX)dɄ'ODRR SlÆ #((Am Ǝ˱c8u9׮]+vȡC4ippp`̙3ÇsKBBB2e k֬!**+W㌆s-[W_}g}愄PvmFEbb"0dx  !B!B+h`nnUV套^F8;;쌕Y4#;Κ5G2w|`vvvу`f͚'˗/W޻w:inݺavڅ/7o.ңc̘1,]___٣e---ٰaL8iӦ'O011a̙x{{Gllϩlٲرwww3f {ȭr8p333&L͛M~Zkkk^u鹏/6mbݛ˗Y&wĉs fΜʕ+Dze˸y&>Z777B!B!ACѱ_~N:=Ռ8MJcʔ0j'Oұ)KFO-OߧroԫW舘G9s& ߳ΊB!B!x=zVZR%;eĄ#Gмys?7^v#A,ii$''Drr2hZLMM|-[ʕ*SJ㙊Ç,Y7>!B!B&hjeeS g =-[$**J OXYYMB!B!BkL猅e˖}Gݻ7X[[?!B!B( !B!B!D4B!B!B$h*B!B!9#pt7$?8CX] P&(J&< !B!B!#/|I]ROS LM05+Nq77Q'~W!p)LrhڴSʑB!/:ROS'e,@F{Iz;{$ 2:B!JLLnݺEZիO)7B!B^1M8.X+S4&6(%Sh*xS֡'k֬@QOEp l۶ZjY| ,_~F孨QcYRDSN,YT-=zDZZڵ FCzzSѣG֛ci߾=;wfŅWR|ymfzEaӦMtЁݻsm1+3֭cdddnܸAHHԯ____KJ}])9OS\B?VVVzU@@ܹsK y!o(Q^ o`ܹ9XiW~fY+%% дiSFAttS]VѣGzmmaܹ?G_0bׯO׮] m[:ÇSNQre/_T4hJ;{>B=~8O[ٳtԉ[n%y0YcGDD3rRmKo(@L.v*'_|3CQϳt=z{n<(c¶:N]f: ti ()[*IH;S(R:u={bfBwBu|w:[III̝;6mNcҤI8p@Nxx8&MʷNOOgʔ)$$$~'ԩSqvv.6W-[дiSׯ .u֜8q/n9R|pIۇV-v:Fѣ=z/7Ӟ(y"􅇇s1>3S]_LL \rEo.kM_Сǎ]vӣGbbbxS͏nܿ?\hhZWg{zEA%ؼy3G?ZjiӆC=lP찴|xa۷{{ܹF͛GPP+V,i7IZ;&hh4h%&6'i7IO>Uy7 g-[F!<`|7UIIIy~:~ ȪfϞMpp0Zʷ>/ί@V]v򊢰{nLF2e#O>Z]IXX^O0f͚ef+==e˖ѵkW?xlCh4YOYj4m7tʗ/Ϸ~>ܹzK{h4W|aw}ݻw~FלkUAm=c΍y3f s+MAʇ~cf~hѢzZP}hL'$$///ZjӉf׮]]k_tm8|wH')7HMq RO+IAjbOt 4)dǕ8׮]#00ٳgsAwR\pj׮ dz///ԩÒ%Kӧ^ϐ'ObmmMXX ́VeԨQܻw0u׫Ϝ9-[0n8°kA8I~~~4mڔӧ? *0|x 틏g…4jԈ.]pM D|שW˗/[ntܙsΩ*jIIIt֍4>zI@@@INc֬Ynݚ1c7ߨ4V\omɒ%|׼̟?( &&&9sN:h"ڵk[og۷o_Պ%?E,]ӭ[7/=7ߤ|,]1cPre,,,ꫯpqq!44(ԩwɓjGlƜk/n8[jj**UһJNNfĉSF ;zh&OL.]"**G-Z-ys(ίgÆ L<^z1|y׉Uٲe /^dʔ)̙3kת=222 e]QMƞ={4iӦMoez ¶7""_:fz-tiii8qB%OٽLk׮{,eʔ1-  ݏdۼy3AAAlܸzk׮|ՠÇ ?OZ,^p֭[΢ES-[zY|}}~-JSоͭ2˜8qBlp{Vb„ t҅/&M`jj5j ** |7X%$$n:͛=zT .y)33(6nH^Zbb"gȐ!,\{{{uf0_ts#22///5jŋ_>gV6{h"߼T>7T6󓚚JHHCe̟?_ΘPtΫ@;w.1NNN+Wf͚u@dd$QQQn Çݻڰaϼyl\Nf4ƙה+G_R~AsyrSk)쨠\rP9/ʍ3*o*4ƍ+WV4h(ܿ_ǏߺuK}Ν7N]}M{wӧ+(?ࠤoݺUi۶(r9P󤝝}) R|}}du-ӵkWRJʦMEQ>L1b2Lq\jҹsgEժlڴIS>|(t1><(W\QEQ:(?VEX)Jddݻw;E?P_U^ye޼yNw.^X;55UVo^2%,S9)|޴+SNUڵvZEQeΝ )b\{(V);wVZnl߾]+ҫW/u?P?~Z(}Q?2h oVNv(/_V] %2l2[QEIJJRߧ(7V>sEQWW%$$^ZquuUt:?\GDD(z˗@9vQۻn:Y^T>|Cj`~W駟@ٻwÆ Sϟܿ_TO?)(K,Qt:k׮3]o+zy Tz(Jes;%%%=r;m8g/'_9s^\\\7*bc^z3gT ncUaN$&&yξX[[+K.Ur=P 6tmO4)1V;w*bS2|z;j->ʭ򑛡OS~ӎ;uQ廤UFFbmmY&{+7σ#G(:NT233 %==]IOOWҔǏ$%11QINNVvm0C4#F(2ZtLLHO; ) I{|1\ O2((`aV8bh߾=W\}(®]Gt:۷ogҥsժU{U>=ze4h@ LC888p<VzuWqqqQɪUXbrSN-vٿeI&$$$pmʗ/nnntܙPn]vرc+1%%W_}5wIIITR;w}v<͛79w\qڶmSL)RuKKK^,uϏz ???:t@ O>Ӈ3fˎ;KيsSXXXzj.\Ȍ3Ϗ Yn~ύ ͭ[pssSYYYѥKB٪P/_FboodV,̕+WhҤ UTQկ_WWW~wZlr9ˋjժѾ}{Oǎquu5z{EQnnnDDDp%ձLF;$kza=r"""h۶m/,Sk..^>nmۄRG$m:C'O*T@LL h4o>|޲?ʕ+djΖ}r>{ڶmٳIHH0z*4BC\]]է"n0~dƍ߿ ҡC|qtt>`xxx¦Mcݹ* 0@o9ooow.k%9 njwٷ)￧[nEJ09 \r_~XiXhƤ{۶mTZ#G0gtɘzܹsl߾SNq 71 _[XX<ƊOi+jر?zq_ka}Nƴ]rjҤ ;|yeffFhh(cǎɓtܙv렩C^aVkCk2ңIKmf"I95hLq¦! d@ ~]?N J!&&HoYbggGDDyILL4vSƖ-[;vl\9ޞCG͓Q{v66h#<<___VXQ +}Q~}TB||<]veȐ!L6+=m|'>}G </‚/K.O?1i$*VȺuԋSn-[ёǏsm&L\'\2!!!Yuرc!## &p< =zYU9g}nL}]@I¢r3tڕ;wҷoIKKӻqV\B!͙^58tN_gذaI*)'''_΍7q_:)N;$'c}aQl|[jVbܹ9wenEU6]I/S UV%((Ht]o';fffOer*jUXy1Fiԅd?:k,50n,c܏Ț5k߿e3bڴir2ΏjINN3p2e([l?Z631v8%id`R4X;c7lgggXz5K.?4jgΜK.,^ݻcggT cGGGΜ9gzv63Sam NO RkKaz=ίy]ZՄ ݻ7?+Wd|i_$mS-$=֒CX5sK',^ܢ:eS9v})Љo s뒿6gAѣ/*/^E ooo"""u뒐@||<5jP?ٿz9;;sB&ի+Wdܸqy/ r1lmmS㘭A޽[ב/+|4mTVNիWŅ7oFs%"##:t(7)i;v,C ;133,šhpssc„ lݺ;w/011 Y1cưuV֮]K>} Lik/{Ǜo)TZ Y&&&+;LNNNNX[[]O2339t52:k֬)E%uɓjUɓEwjՊYfzjBBBW mȢhbl,l)M/2w\۷ӫW/ڵkkˢ)eIЭ[7Μ9C֑VqnDFFobccԶ+KX-{ r+0#==|*`&L{J,ަ۶m?`vwqXXXСC>7̙3t޽E9s331vߖTV K.F)XTmll>}:}0C?v7wxxxPj"mkCÃQe+*U񶠶^a熹9;vSFrKrm)|{_T9cD.]Ύ5k>,,ߥy^UZAqFصkkkfE4-nK$ܭHzJe+4@1CQ,m^ LMˡ1)=ܻ# ;^TZ(.]T`w3f0m4,--Y|9;vTB|izWh߿?ժUq\~kkkx ڰaC&Mرc3g/ϟJ*кuk8q"&MĄ'O;3l0^ʠA8x`ի/fرcnnv~UQ`ÇL>P]?~S i׮\zϳpB7oN߾}9r$SNܜE1qDߏ?oF `޽4kLIeh{888Bll_JRZ|}};w.׿իqqqܸqѣGӠA֮]Kz`ٲeznٱo>"##qssL2ѹڗaҤI4i҄5kҽ{w9q%)ܶm| Zʕ+̘1CzZK/ѹsgBCC7ny^jHaggg6oތVzyN(А*Uj*ٳg߿?իW? **QFU qttdռ̝;|{9>-ZЬY|bccShL*TE?O>cǎ\й}<7o~5Tdo>-m۶CkԪU'N R 3fPv\2QQQ4k www֭9rVVVxzzI$*_|/~j֬=vi֬}{nV^Mڵ6[)wԉ;ҧOTׯ_?fѢETP(gPH֭Kzm)|d#CEDIJJbԇ뒞WZ ڴiCjՈٳֲ_Vꫯ7o6;zn{k"Q"IbU;V0+S2)cU2nX۵mCٝ IDATCcZ?Crr:N/T)ȬY8z(sg;;;zApp0fӓ˗ {ߨ[nlذ]v͛ży3f K.ח={AYKKK6l؀+'Ndڴi$$$`bb̙3OMEQlYv؁;AAA3؏o@č &yfӯ_?:ϣ0/6mbݛ˗Y&wĉs fΜʕ+Dze˸y&>Z777݋=f177̌ÇٷoF3f`oooݺuҼysƏ[yL6I&q٧rܻwf͚ѣGӬY3Ν;Gff&*T ,, bbb8tuFqӧOMƂ hٲ^@C ƏϼyXv-SLښ~Ƚ&Nӧqpp`ҥL8cǎѲeK233133cŊ0g,X@.] 5z*TڵkL:???Z-ׯWX2f{t/e4!Ņ'Np}… tt:}]ڷoϔ)SXh{{1wo߾TZ@ѹ/K/̙3gϏ9sJԃW\aȑ9r={wvl29r$6l3!kkk֯_ӧ ȷUiԅh׮Ǐ^c9-[d ޏvZBCCYfM벳y>ZXڴiÇ9{,cƌܹs?~'r2tnѶm[OJJjɘ}[|||Xٻʴÿ3Ij :AAAPT, Xu?t]eZ]ZAQPB - FzO&1ɐI2̜9噷|βexgX,-vV={6:3g$11ѶL#<#h4|̜9/9s搘_|wa+{lڴ}ȑ#~;kKSGk5i$;w.}>,fͲ}ޚas~z=]v?dڴi[[L00Oy뭷XhOPЅ}Q[:ڵq]x.HIq>y_b6(jj *X0w:߄EMjǥ%`JKKy/w(B̙3DEEܪG!Gbb"%%%׷>u-IBofnYf]ʲ}vnb46 ]Jb۶mͶuV{srr" ^ޏQYGYQ*U*T Nx 7O` BߟyXbE[1cƴ !O:w[Eղl2z쉟Zm.گ6i ֎j]\\pq?0rða8t4Ѿ~W4 NgÅsW'n t:*++y1 ?ЮB!B!BnnB!B!BAB!B!BQ$MB!B!v3"QUUM&WU~#B!B!B鑣hZ^בO܀92!B!B!IIɌJh4( U** cG.gB!B!BPbh4b08{,deeAff&`r ?@\\|IM̙Ûo @~~>sYz5]th46;ݒ%K>}FkmJII v7{|B!B!о(ڤ, .k2V+B!B!wMm۶jժF_۶mkUYj]5VKyy9RVVFyy9:Z mmz=kٽb ƏϠAx饗(..<==k{/%%9sĉjoh"رcY|] ׊ ,Yȑ#0a_}]>I&O?M^^^ufȐ!,\ @rr2?8̜9Ӯ)\\GᮻBQkgIII>}:{[VFFͣo߾}vn޼ÇqFMF||>'ڶ[~~>[{wX͛GNNNB!B!;i֭[$LnJtttQk_niߘSNSOpB6owߍ`}LTT]v#Gҭ[7{=NjMؽ{7,^3fpwqFL&?0,^W_}e\G}ŋ燛]%%%< 4RHNNfĈ;Э[7 FBB~)QQQk:tQF۴iC>gϞL8,ѣ˖-cĉ?$ L4'|oo۹6|F#ߙ4i{/}R/^woAzz:SL-B!B!D λy~`` = sت%Gí$HU*eXi{CKdd$cƌ 11Ѯ?ӷz'x9spڕ7a?0rHm͛3f lذ ["7x3fpa^{5222l`***x^C׿ٳgpuQ\\̒%Koݻ7nnntɮ,9s8p K,a߾}tؑ/QF0x`Xv]9~-ƴnv{9[UUUocɶi-ZİaׯL0 !B!Bd i@@@`YXX(O~~>呓CIII~N/N:1fRRRkwDlgMQ&lhh(EEE8qBQW'Oرc;v>}4|;v,?#K.Ț5k5j(֯_NsxҩS'[(꫶uXZlN=prn@9y&B!B!DK.@PڿBR X,T*]&Mqvv&$$ЧOt: hND>>>۷VC4eeeI3ϰxbV\iUj2gyzz6HH6_~ivV˶>'''\]].S!B!B\c```@MbT^6TIIj/xuEEEl߾#GK.5Q8j(צeuޝRjkFFFra233,cʔ)|,,[oÇw^LbK:iӦO?ْ,_hΝ˳>Kiiiڡ* 0j(x󉏏g˖-߿YfDBB{ǃ>ƍӓks=ǬY8ydMEw֭6`<<<3gk׮oaȑvN2?gy(>CB!B!K)..n]v1nܸH⾽h8;84^Ó.ZLyyypqjd[d wf~Opp0 :G'B!B!ړ۷3|p@ߵjVTl۶!CX~׻Wo 򩮪r***0 L&j5IHp:t1T!B!Bqk7IS77w:ED^0 6CIo!B!B!~G$ \\.@~σCU!B!B } %B!B!5M/Iq[fSYZܡ!)łr"B!B.s -0Ur!h٨.wB!B!~Ih ,MUFeU. *4-B!B!O+t!r*'WNIC2\]-HT29O!.uZ5Ov%dpK!B!uU'MT]: *@THHDhwl.jY!Y&w޺N8!BqjXCv *X\`X0V/xas5Ⱦٗ]' yu[&*=),1z>r5c;0O k-ED;~܆м۔(O :3b9UƋ.TB\u{;S7WXmqψH_b}j]Fyp:yQ8vn:ñcc5:_Q>H^9#>dULI)M{{?&Ɔ䁻"-&dTI3ֆ|rtRuLH;.H0/ ޯ6=eu pu"Tg{/ׅw&odNbEJ}I6:o$˫kr4r/KאWZN0?: ?”'r\49{9Sa#cQn0793;%r^\zĞd6:n΁at pÙJ=K׎L|\<|)ݏ'EWIlΪhp 8J]JsJvbt & _?.2 РV 1B!DmXYo$%#A`6,* wOdI)ܮk|K;?mJ +05!shMMy^l9Vs7P3f!ګdž\^$?.̛ӯpf)fčX29)bWF9rn7bkR^YXX,UɄQ:p$ŸjK6z׼#o2(js浜iq6s0^h=!pr=O.?̖*3r[t[?xgg^ˤiql?~CN-pgI; ڄYm-Ӏ܋iuE3=kUb(xb2YH8Y;R۠G# E,:N ssM.j2xwK:}ִ_/LΙ|-^ ¸Ho +z~LeEMSfIŘksM0?]g .?U.,ߞ8,vp; c̱{5: 5!&Wg5OF-¸e@fQ1&>Y<׌>,ZsGyuDyP\=Y|t5ElN݄)@vi|оp$|7ύMؚR*BQ9Ծ5gwӅJ4۳5N eTt |ЛL+ϐ[mjuсxi8W`}\yzt:x՛ؑR-*ďQ)&n[؟#Ye&ܜFGrcl]ϧFV1 kM؛ZD ")쩱=j^#ZZPzI ӺzG= @W !{]REրJQK/"6R=ׁ };BAY5JXDٕvji}jtiz ^-AF Z~˲iVS wGɭIN[Fp0&wuk0&‹Ӯot)aL/!.7g5I%s*Y5O ])( tM0|m<]δ/78_{Fߍ2&3:B\):U:~SCƷ ScesVt101rU8z UUAME_u(f\oFa\6#߃Oꉷӹ7 ̮^<=e|;CvN fݛ3%ہEUÊ]z$=;,6+7u#6‡MGY?*23ύF~ ۍ|'WgGޯ1L+fϧpV^'s+X+r]ۙ @[}]X0|=RfQ>]_(2Jv,Ʉis_*r4By6X"V%] vx_ є߭hu&i~JʡROkR09Uc-Zs?h-#1 oH gljBHHʼn%b 4uf ]ܩ6Y? ramѨ[x:#z9_F*O/a  r|+ȳ7/vDcBxcbt-+=nolKOF,_As|T:דQ=,Բ+WmQFrc yXQ@6m|̻g+x[%􍴿6.*ޟ֋^|7%˾_掁{QQ(_Su ~c#SsNKxOk*ME7ogۼ(@6;ROmK2*1^:>t p绽YJ̢O/ u}>ddb-M>ܑIZ~%U1x|yaR/2 |-rsnƃ5ϓ UAA a֭lݺm۶cHHH`׮]ܹo5Mk*K)!SɣC;b+PaeYl=ׯ:Qz"\ ᦾ|팭9|<4#sםjv˾{ xj|w5{ך,>YȠkM=(Բ3jR?Ojiev|~5>/85'l+ `wv׫-ׅ ^]svxq qA j/q-e[(# UM`2pxa<$z]z,`2ZqFo4c6譝ו]yZʫ t @9ȃǭ(E!ϝ}g^+ڈGMmȨwTk73Ǵ!-]yJ+R4,ahT6RfF351ws'ύ`7˩?4e9[.t3B@ݛc:JZl('P˳7`Bl N c<E3\tCAx XcRhJz^ߚ\;o>˜zzs<Ϸ1cD$YuFhߜU7v~3"7n_%7#^Ϋ-f䆹;0DAV)3P1%؃SvM6*fpMJ1g*{sɇ) 3vdُ}HJ/5+ t& F3^3 gjiSצbV8v9^s-'2ZV Om?y:|#p>cBه ieE*V-y{?&\LD5eՋn ,fƈH !7xnbOh0eP8<4wEt-_{Fju>ԝUƦ MSsSKVgK?泥̊s 䳦Ώ`pe]Ypj\7q}C>v=Ck U\iIZŀnFf]ύ_3w:n mn !ՠ]?E=|ԧLBO/LU1Xp ₳k8Sw j ij9`4YX, tY͡rXvVY_NSk_5/΀laYjsרQ_S9t>$tV)( Wuhb2YkXJ=1*f˹U+9PkdR]|Uo7/͹zxR55pÙm~߯Ƙ-f`s֞鯓t1zL9Uȵ=ş_4^ 5`WFeW^GRiT փ.!.nZ.jF bGJAĨ^A>YHzM> *e0Y0[,/}۔>.\>9\2*$kPJ/ҽ]1,ϵnR1NV)E(ӣ#LPT3qRp}>gT9֧^]NM?::k빾Պ4w#)%&򆵅xqQR;[..~Vۖ V(f~T8IGzcF8[d9{[ve-w9(rF o/[2uts9ъݙ"MZ6pK f˪й3JxZZSZ\5jnmOw}Zn^C#GWutf Z:zcQ_nZ+ ns 4HU7QXc@ы"- Ci[z7eTkBkBQiPQ'TjO4n#fgzdHHDc:yKzO Uh^AUh!`ghMu~H. cpΖ% ̿xZ{kS[=#"t&TGLez>Jgnׁ?ڃ9`pM7'dڕx[|\}P%U^gwD$v&)!Ą憓xJfrwJLYb̟%f3 oC9FxXa͈ Q*ཀྵp8WIT--n7OW5؝'tfH7ؙnW~!x)1::J)?fF{rpRPg`/Xa"(?f.Xkdk35Iy25uFL`zL [ϔ^B#xkj/VBg0/kؐ~7#1oW'fZ+(4CCֽ}quVe#}/ O'hup] XMKoϡR+)21!vSkoVG72_(Ncmq<ɃqsVVT>ޕlH#]-X{,5 ezN3k?֚95r*%L[%.tEZ,SY8 Og:TR όDJnSzCRH`USZ;3up8j2Z{;UaML,Jo$*؃[ھuLg[պo\GdU3FZPɵ=[{<ݜ4#[~LБWf-gpU9zrdQל˱>؟GlGonFl7K)4N@w\t*Z:;⛽Y+Nڒ?ȡ X\u=/> ړͬ/xjtgZEfͷړW'F&IWgpZJthVYT +t$gi&&HH'{ 7IE~w^bnjnmajW֦0{TG[Z͖\1:=Np s똘__V&3HfGRTgŮDlM){[NbWj=üٜ9 9,w QQɯb<8kcF9 `nTJ/E2;]I-teG[\xNjYۏX>?S̯22G.5'sʙEvVs՟Sy~|7& -(ݙyTetcu; 8n=(,QQmHʫ lH-^k 1!i ?ZH!wpF YwdZrkN2# at`\4* +H+߶8dف<4*ICɗJJ+%on]M__vaʠTLlkqZPa +pR)hYX}::Ԛi{O`3GخfNDM_ܐpǐp*oq"WJqqqUMv_7$X(JjBG7ܜqR+XTTT]\Iri+R<[{cb\,^FX""{̥ K-gZ0i]ƌ,\ur3+h3 8vm^#mo"*hFˬ86cgB!~Zpss)ٌlȑ# 2mM(*pyi6WaR0:QaPy4܂}hb VhB I92ؔrPճM !~_OI q;1(NURXgD7T &6.F!W6i վtsK5)ElY 8cFb$fԗ;~Pqqq|'X,̙Ûo @~~>sYz5]th46;ݒ%K>}CVkmJII v7{|w9ׯGQt:ESYY__fV\#|Q͝G/Ǝ9V0L|sj,YA? %Bqi]Ip 'u Y*&&C5`BJ|8K.o̙3('|‚ 6m?xoVgaŊL072Cvv6W[o%-- n͘13f\eINNfԩl߾Ç_ ?|Ym?ĉryiR{:F~M61eʔvNBq*kE5g1r1r00Tgba`2RU  Ns[{^{+V0~x K/ԠRzz: 66^JJ s!&&'ҜǢE:t(cǎevNWTTdFɄ ꫯ0L }&M"66~뒝͐!CXpvBrr2?8̜9Ӯ)\\GᮻBQkgIII>}:{[VFFͣo߾}vn޼ÇqFMF|||4nK,aѢE,^x&Nȷ~knn:^bcc7o999.GHIIOL4W_}NwO>!,, Vk?33EQ8x`7=O&z>gn4a Cii){/]lFQ֬Yc{/55EQ8~8EEE꫌9Ç3|;w$66'|츺VKQQQoۘ~3gr 7̖-[?ӷo_Nʻk- ?#sg޼yvy|ٲe=6ҥKQz.>ŋ}6n8-[O'!$DzQ!1sV$(g%MIAdypbekqg~p~6ݻwM6L6MOW+_|o&h}vJV֭[l۶wymV)z#@QQ6l GGG&Lǹs7QO߿Odd$4o?? ][mW_}3f Æ .Y/K߾}eٴmۖXyJNNFPpN8߿GGG53<ǎT/S̞=N:1h l٢\>LJXҥ K.42222&^EӢLP(*Kaoiv O2O4 OiOf%4+<>\ ]& MLLwUtEiҤ P2vuuiӦXSZ5SP*DDDː!CHHHJ=!Gڼy̙3  ""KKKjԨǏ3rH:t100ŋtGGGhڴ)?~ '%%EBħsάZ͛ί t=zYfDGGN~8Ǚ;w.iǦܦONQQÃ{;vHDDD͒%K}6Acz-jժŪUW^Xc۷/ԩSRgϞjXJѧ!?>}W^܋ﱷgݺu 8ݻ--hԮ]˗/+[pp0)))̙3yѨQ#I;vÇ3h ֬Y5&33ڵk?9pjӽdժUSSS|}}ټy֣Ùߟb"##b…j~lɓdѢE}mHLLdҤIn:e#######C$+n&gm ~>b%~( OW*N-RWN7NnX~W,X@!ă ?.ߺuKҳ9s`w@@O5Ʒh">ӧOBqaee%rss%;v?B!Ο?/qΝ aWO,bӦMj~#/_.~P*bΝB!"##Ň~,ϟ/СC,ʭl:bҥ7*Jc$taee%vޭ5^}pDrrFwMmgܸqbԩo777qF!{BnG/3۷B!M&M&o@L>]ʼn+W +++gI>SQn]~zqA1}tҰ5Q\\,k.ٵk RSSEaaP*bÆ PGOD1l0777g,?dbΝBTJ)WJ~ڵkEϞ=EaagA!Dvv {nnprr'\^ZtM=sL1a5?fffߢE RB_[SSSF||α[*Ǔ&qRݓ->>>j ;;;ɓ :uJRׯ*>JRM^zU TGh^xQ͛ҳoVB:^ܹsׯ_B1rH1o޼ 3s a1v(..V+me&ӧoJ%4!Dk+DAAB%KcV~yRQvs찰x4رc?~[ۡdgpjjjm47OMXZZ%/о}{ի'=k޼9-ZڧϸR~}z%~I޺u\x 5m@iϢR*999gڵkzզc*CWabb"V(Ksssٲe ~!=z`ƌ=R~׫W/n޼Irrd:JӮxV9+x&OL5k}Tvmh94Qn OKK{ۛ]v1x`bbbϟ?ϼy4hd >[TU5]$-O=zСCܾ}K.amm̈́ HJJʕ+\v .йsgi=wo&ַoߞLn߾]>}4ȼ̼AS\lUW EYUA9 JLL06mC#s 5#(Jiu=?N֭AZZZZnY-ۡcaaARR`OVVSNJDD_5cǎJT^33 (ۑ[ZZ3w\5?MhYʭ4 իWWXPm #_pe{S.9;;cmmǹ}6ǏWy"K.ի̟҇?˗3vXx _]Z~}LLL?~<ǏRa mgxyy߳vZ&O7|C˖-aڴi 4HMrѸqc166ϏYf4S2Q*w^+-Ӽ|mjMSy Ed|Glذ¥Meiڴ)Nƍҳwrԩ*FFFt҅Yf~z,Xfs]2jkk=7oTkc 6{_JhUȈ>}TsIǔ ItY;Jwfdda|}}ԩM4QO鋗_|۷ogرUPұ3gi3dׯׯ_GTҿymӦ AAA;P4h W={k׮t֍'N(0|p\/lْ)SJڵܹ3.\`…j˻vw}#4kLg===`&LȈSNѯ_?r!x>Hg%&&2e~m~7f̝0aC.]ڵ<͛|tKKKN>6 0I&ѴiS:u$gffɓ'qtt|v2IǼ+@>,waݺu5ÇBCCܹsi޼9u!-- 0yd|M?^zt\\\̴ipqq~s9䠠 ڷoOƍ ++'NLFFx{{kaff͛77oҎMcaaINNe˖0fi.]`aaChѢE"<<@}].$,,qƑ-v";;OOO걔^{h~GjԨQ̋ՕK2tP޽;VVV\r .d:uꄷ7Fbԩĉڥ6iݺ58p;biiv=2?~2jjjʂ =z4FFFmۖ[n^iР#<<t(v?LLL 'OWiԨ[nYf:L8kʕ|'mԬY˺>>ddd0}t155T>; qQZ5>S¨[F ###oijaa+M||EԨA VoHu&TьJQTkoWO괿6k,=ʜ9sԾ{РA1k,ڵkGttt"eۇ۷oW;ϪUaҢ [lEL8233+|600`̙t֍#G ?Νߟsq583f???>>DFF-[ٳn߾͢E ѣ^#GJkooolll&11)βXXX0j(bbbPTdnݺ$$$`hhH@@[nY7qFLRdUލɘ1c3f +WLJe&N IDATș3gbժUL8cǎLQQY={ŋ0`zQvm]ԩS9r$l޼Ynݺ$&&J2c&MLJm۶Uҏ^KKKFŖ-[$u|UqqqСC$%%1zh.^#޺tLV!220߿4lmmСTV ޽*zx{{榶~I_.!g&MW^L2(ԎMգzyi*Y/s>>#߼y3gΜ!00˗/c~bٲe̚5 .ŧO{/>m<Yו'(ܳgO%M6ŅѣGk ǃ Аdz}v>3^/gwܙstRƌ#/Ҥdddd-(222t O+WPV-;w[F#CZERqpttŅ`;&$?saԨQ\x1 -[Tp+e+66CRXX'OV󓘘BN5k֬ƍ,X7o_|E38w}֭[OO>cNeWs ?^yq_?B)oc ϡCKxMQQ ݻw?s+AAAiӆ>H hgȼi06Ūћ EUʀ| bO0mC~5rrE'CF_5:t ++wy%KеkWN8cT?=ɳWfgҰ_~yIÔoO/*oByOOT*#FԔUV1`^tddddd^dIJAg bPM) 'T٠(DU\HAMsQ#:zţP(XlY'`Ϟ=]FFFEs-\?5#GЬY36o̐!C* #::ss  İaèQz")){ҩS=eԩlݺ7779ӧOs=;ȭ[{ɭ2e/2x`=J.]^H8x Yx_ߥ=ծ]OXXlcZFFFKӴՌPݥ0>Q+yi<(w QY'Ͻ !yװaW{uVG3g (LVg 8::ѹsgz͖-[Ծxb\]]qsscK[ZjɓyAܻwN:1{l ŋ?~\dnŋ'駟ҹsg<<<ؾ};7n/:tK̈́͛+[AϔS6mСCi޼9dΝ@I_۸qcҦMʣOE7|ڵ#44ÇUzz:gϦSN 4-["۷/̞=m֞  :T>Kɓ'R;,͋vRkO.^Ȍ3޽;mڴaڴijzF;M6̘1*_Yxu֭ @ǎQ(DGG}"##yU:LiӇ~å0T*{fСja{yymjM\dddd^4/iQA&(Tޥ07 ro4;''xu?4'rxBA}fE.ԗk׮ٳILLwUWsEiҤ P2vuuiӦXSZ5SP*DDDː!CFqq1GDDDH66o,?sLkRmJ9:0}t x"]vё(6m3Ǐ &&{{{III[nRxsάZ͛ί t=zYfDGGN~8Ǚ;w.iǦܦONQQÃ{;vHDDD͒%K}6Z22cz-jժŪUW^Xc۷/ԩSRgϞjXJѧ,h*{^JNN'Ndٲe4lPg<:tm>S:LJJ sa޼y4jHcǎc 45k`mm̤vڄNjjj6[LVZE155ח͛7kdffҿˋ ٲe 'OӓE#zAzzG1bGfС0a57nGƍ+[|9...,_kkk:t3pwwgdgg3rAaa!wֹ#JAHH'((ƌ93IIIу޽{aׯ$N+V૯bҤI,Z .~^hݺ53gάQEt&233eذaX3fɎdrr2mۖ˗ӼysfϞ]i阎;Jm%99LO?DRR7orQiҥK888Έ#fR;v` 0+WҾ}{i\Mڵkh"7={y,Ym2`n޼we100ٳۗ(w9rҎ;v")Y)ʯ<_~%ӧOgȐ!DDDCK=;;wwwXp!o6\R-aÆR5k۷WsӖ7({ej'}eRZZfffL23gr/C aɒ%:?jmllHLL૯"%%wwwxK,www]J)Ǵi|҆ׯ͛7T8ݣ ]sE}]s6JFF'I>񤥥 @9rD$T*Ž{gjE{e3N}*˷'MyGf^ʒ#I7SkO:V*[6999VkE4NyʗQYcT*mڴI3FhBӞ={DN䪔 ]sE}ms6]Njk׮w&n߾-C;M P00"?7 2srr(..fϞ=kNO5MT2022DP(Ae>Cz3pw_~e˖ڪ322޾RsNp]V\ɐ!Cܹ3;vZY)ʯVGP*޽{?~֭[E추]377‚k׮qmٳ@1]N:_Kϊ|YWYT1ZKKK}޾}{r+MCe(@0<+|ݻ+++Ǖ쌵5ǏgjͲl5lؐÇh"?~L=6mxʨRJn/J7Ke~_yWTWiѵ2SLHHH`ԨQDGG(]T^Jx4g)L,=͍\BBBغuTU3mWR\rss+҅?Fv}t>G⬪+[PYd _|ni_l>ٳՋ-Z駟k.+<[lڴi&:t@nشij5S>sgUXiTјfdI# `dlKuesj[w̬bfO j`|_xGE@=#{{{^}$5jЭ[7)^{L=zDÆ ҝvvv\peedڵt{`#ٳxxxH2i/mڴرcYe.P2)O=x@-mϟo߾j&> L;&ՕEPٱc{.vgǎlܸWժU/$99YƆ%K,+W퍟'N+|^u; llle֭4jԈ}a``;gϞVJu )VVVKݛ-999!{r!>W8QP6M5BTߢ">\e{ BJG}Ć *\TMr)nܸ!={.NRjddD.]5kׯgjvrK%sM6ְaCJOdffVG FFFӧ˜Kע&S.P_Uf˟$*]`Æ ҩS'4i6_>/_NmnӪbff۷>ueyuF IDATqrrQFlQP9U~屵BuũҥKѨQ#]YhΝ;{hBm հaCJ:(;^^^j +EGG?RF Ǔe^<,]CHݱʕ+\p%KЩS'5jSNȈ(&N-j">>֭[clĺر#j7C)W*,XѣGcddD۶mu镞 Я_?  ++K]:Jc]?ɓ+եcJ̶nJfx7tqʕ+O8y$ 6}DGGRx1cƌL:J+BJJ ;vQ6}mۖ5jN޽~:_5nnnv&%%aiiI Xl{ҥKjomm۷o[[[֭լYSkOOF3g}:64S_kOtZnƍi֬jsժU0uP7iҤ \ J~zD}>c"##+=QXvsoߠmC:t#5kVCFFFR4&d>" jyG 7IhFuJ(5巫ɧQwu_5kGeΜ9ZXX0h ˜5kڵ#::Z:fpɞi)lٲ}۫tej*|||ؿ(kbb–-[hѢ'N$$$ ; 9s&ݺucȑ?s?~\Vό3OWfffٳGGGM?W^t77]䄩)ٳ۷'L:{{{֮][Ɨ̳`dd! ,ۛ~J74Wv^{5:uĸqLEtԉ8w3"ߧcǎǐ1cбcGΟ?ɓ'ر#ƍJ3wQoi ))4u&awvviq„ </^dv&MvZ|||$22Rlْg#FIhh(*>,0qDΜ9Vbĉ;v ggg044d͚5ٓP/^̀;ڵks5Nȑ#)..fw֭[DIFwIm6"::JАիWciiɨQزe Cu¡CHJJb\xWWWuVZCdd$aaaܿ_gigҡCLMMV111( ٽ{7AAAj>}:cǎ>cĈ={VZ|M>+ 4`$''3zh%w+++֯_g}_MƍĉL07n0sLɿRd͜9s@i׬rʧJ󇅅~zI޿+:tCCCƏt(OeyK5'J&MW^L2(7nTӦMښÇW?7׏(-[ƬY&t ʬsҳ^zh=_v믿+7h̺UFFF揢9M yGJJ k֬+++9BJJA$k׮믿Ҹqc e̘1D"H$"vafVUꔴ88::-K$rJ$ deevZn߾MΝ9x nI&Zy_~%sNf$+WtRjժE޽裏~ H$ɟH=/H$D"H$D"HJ'ox~J"H$D"H$D"H\4H$D"H$D"H$\ESD"H$D"H$D"?FǷYM P Fm-%-D"H$D"H$D" QM3R,&eh 5)w6*ʨgD"H$R3gHLL, *$H$D"HJz0=nC*)S1RIO/~YIY"H$PywƍoHD"H$tRjm3sztº97n֭ @رcYd T*N<7;vаaC uL Eނx%iiiZ\]]YrO")j;w^T*oPYYYO|2p@9FO?Mv $>>^yߧOT*߯jP%Ffذaĉ9~FW3g|0QT{g֭0Hff&^"))IMdd$*;)iTPH?ʝ;w?>>>>úuߛ󸺺r޽݄akke1Mzz:/_8yvY ߿>}\\qt*K޾Dm޽;;vH2$/^gϞ>|XyV뮣ֹ?ʨQ9sf#FQ7?IH$vE.,kc]#d=C1"K:3 PBxE'"ٹ"#(|gΜwޘ7vЁ~ŐHvȶS06l`ҥ8;;xbׯON8u&44@|||1c.]ˋ/^0c ;6mkצe˖QRZ)S`ooϒ%KMڵ*JIOOgڵlݺ\\>}zI!?ߟŋӡCN<1B4uR\up^{=x777zZjEVV%^_~q1m4LBzJTB(JJxefٲe?~ uI$H$8HKKb)j'3&FԩT ԯh@FIF]ғSR3܀YzbP<*e˖Y>!{ED"A^f޽|kɫ/Ν˸q2ˋOc:u%*3gC955:vHǎE!w_I&e K3fйsgZhi|:n::vرcyRtdd$ެ]={Dpp0="$$ݻӹsg6o\ϼsWWW֭[Ǹq㰳gVVckk˘1c8qDu9Ǐ  O߉bĈ4mڔO>BˬyKÆ ٿ?#GDR1x`-lٲbcc;w,0.WWW˜5kZ",,4RR%-S_}Zc_w!>>YfѮ];zExxVy2VNNN,YTH$r4+#T2;&E*$gIMHjERHMIABf?[8q"f"22A)6 {@oРo6tШQ#V\7ƊӧOcnn… eVOy1 .T}w@~F… Zl{JCӧcddĕ+WС,_Fȉ'شi 4 ((hEOoÃtBƍYf q%'/d„ :˷ӧܹs?d.\H~Xx1G* ˗/T| ~~~[l;<|ӧO+a9r]j}`vTZHIIôj ީOqsĉ:mEo޼O> ;0qD3gsΥnݺJ?vq>czڵkZ*$$$Precҥ;BYXߛzI+XfBB={DVB߾}Y`p&MD>}Xhϟ?K.Z6aC O?eDGG3fwaԩ̞=͛7b YbUV3f+HJJbС 33ݻwP;!߿ &?ȑ#&˗/Ғ|LTT]t{EժUuƗRGcʕ+o?~<-_Ҫݺuy*}"!!0ʕ+y1#GT .BVXb666̚5 cڶm .mP !x"QQQLwرc$%%ѡC֭[d􉎎Fl &p!OԩSMeȑL4h)[8,,,\̹їoB%K-[ Fxamm-v]h!\|Y… :j;GSLQwww7oB!&ߎJk׮7 ʳ/BTVMlܸQ8p@L>]/^]~MyQȍZصk֭[111"33SP===Ƒ,ѬY3ebbϞ=J ̞=;_عs077WғSn޼[֯_/v*23377n͚5B$ի':OII믿B܏^ZtI?00P3FMqFѤIhǙOks`LL(m&ի'^z/ӧO @ ƍgϞ sss͛J9:@a+W ޽<駟 ϟ?/qm!Cs͗?ڵKBVkOay&"55U_шD[W̝'B/t"w^#o_h"ku*~< ݺudhҺŋ gggp]G s>Ss(yɛ7Ç7MaؼGb:"oGCrț&OOO%+WF>LISμ1666_Xy 6777)_ IDAT<߶mXxE/_*n=Z(H$%ŭ[ă ʼnXo>tҹTmF5iH %, O7RžD2JZ8nݺt֍ׯ_݋#}|lΝ&15j(8rK+Gz_yԮ]wξ}Xl˗~޽;\:ub޽EڹSlY֭pk׮պzvۯ!{)@6mpVߟݻw+cKZZ;wdȐ!JpU411cǎlڴ;wA~6mZeٶm...pICddd~rv;'޽{4kLyV\9|G+_ǭ퐽38&&6O+UVVV@¸~:mڴz34iR蘚[qqqf͚tMe]9+ #%%-[*QFZǧH;/NZfϞ=nZM2et-)}y`jjCSRaiiip "ڶm[j֬ϫ >>lݺFÙ3g4h^^^\\N?;bd6mڐ@lllS7eʔ¢ nʊ>HKX 8#CA dO H֭#""kkk&L'/_,Џ#UVĉ/kMr:_ .зo_̙Cݵޕ-[~7v!C}6Z}dRR!!!5*aDa?7oёcooϕ+W(S 5j`ҥ.oׯ˗/gŅ֟űi&ߏ#xyyV.ddd3a`ffB_yzoHJ www""" iiiZ 9TP17wթS#Gh"^|I.]6mx @yY ]>>7ӮhHOO/ҢkA}LA˳7Ifdd+o 60{l<<d.;9sʼ S#DOT."镚8ʔOm16ƴl]LjajV26Tڎo`a튅fԼ?׀s;F&M4h@Æ+WN:iٲ)  <:u(.M>}X~=F@@ٽ{|6Ν;2()hтǏcii%s*U |)6$ߞ\?LFF#/FFFzw֨Q___nJݺuٻw/FFFxxxp9j׮Uv9}EcmmLt {:DڵINNV !|w-Sݺu1778rbwPT*U,H gԩ'75ܹsGyN>]1'''fΜƍ?ڵkӠA޽ԩc𮿜7 )/ #Υ>&G.^,їg9{;6lHtt4nzcΝӇΝ;ӤIBJCuy(.]RXZZ˙>}b?SW7G9~8;;;MMMٳmyKa… |GSn"/&|||ؾ};;vޞʕ+3|pw}Aq5oޜ}i٫WR^=֭[:ׯ_\_H$G!j ) XTnJe囂PĊy| *XY(45j?6m͌3lٲY=z(3Ξ=K>}'2`j֬=oܜ={Mk-0a}gϦVZ\|իӵkW:t@N7n&LȈӧO[㏹qDFFҴiS&O +W}\| pq_رcXZZҸqc2Ӈ+Vg1fLMM9}4nnnXyBcߏ|dݛG1cHHHvlڴ ''':t7 >޽_̙3ZJhAmg4jH8NֶXB\\ -- 777Ξ=ɯ/TB\\gҤItQqV7nr67Q,]իSlYj5ӦMٙ5k;v,&LM6ԯ_OOO9y$'Nŋxzzү_?y,,,{.s%((HTXp6mʦM9r+V4i$_۷/AAA;AHppʊy1zhRRR]6v"))>}R k֬~\rbJJ,YsX[[s ._ŋi׮O>aʔ)|rƍG- \v͛cff?L۶mҺM|W3|>SLMMiժ#>>aÆ$OZpss#((QFu>;Vѹ:tkشi&MV_Sn]nJƍiٲ<ӥgZ?SN;]yi۶-}eĈzva4oޜ͛7ӸqcřWWP]$Xr%*T&33cǎ)z:oҠAƍyEƏop FZXlݻwիƧ }iLJ#F(&Bz쉷7|vޱLJ`&N/^`Q|yʗ/oX=j(F1_|ͣZj:D"Q*wVX%e"IO_P',bReʾMr)c Q7$'S fΜɱcǘ3gb.oܽzb޼y̜9֭[f?3Ãp݋#8ꫯoLJ+e˖%<<&M0n8HHHEȈ@:uСCٙ_ǥK8q№m3f`eeŰaÔQaaa={eڴiqB氵UҦ/)_>>l۶Cѷo_֬YSĄիWcee'|Bxx8T㋊3&**O?+Ws>ΎM6¼yx<ӥgծ]|^Ƭ].]0sL/^L޽߿ŋurlCӭ[7&O۷Vy{}:|ѥK5k?3|pN{ァU9˗/gٲe̜95j+9;;/ŨQqgȼ0ׯϾ}8y$cƌΝ;Ƨ }iH0۷;V[>|eRб}|,Y#G$䟆ŋz5SNtDVV/_4Y TbdYc2AYvT ot@x o׮]~E.-siuZj8qBQT$wޥA\r`E_"y̛7'OdɒEBӢط싇N޿]WdwR`ffY*WSHtt< Htʕ+ 7KJ !YYY]۷oӹsg<Ȼ[ҢI$t^zŗ_~Iff&nnnܹS*!dyH$D"R|<_"H$D"H$D"NRyD"H$D"H$D"h*H$D"H$D"H$D"H$D"H$DčDom!92 " N[JZ$Y㋱3Mʠ@jSomTQ D"H$3gΐX$?*T I$H$D">JJa{>= UZSl9P'c$QP^7^Ńo2V%-D"H$I$&&;ύ7ސ4D"H$I4Ug&sx,ߥjCwTF#0T}5脅usoo6(֭[ cDzdQTyci4F?̰aðٙ'rqō3gΜeFRݟY8p @v$%%iDRy'? *Gs|||Xn]{?WWWݻW0lmm߸,W?IOO˗z'Ξ=ˀprr"44ӧO+..]BRZyۗ!ضmݻwӓcǎIŋٓÇ+JB~u:G5j3g,qpp`Ĉܿ5Jf'DW.ۅemvD;gh4FdQgjT\[$?;WxŒ̙3f:O?v+fϞ 9Æ |Zʟ~}εk۷//^P$%%1`bbb6m...xyyəx]cǎ)1cyU2e ,YԮ][+}E?][KPPӧO/i1$zppp 11xb:tɓ'=FFyN8kϳFϞ=CV"==D /0n8MƔ)SW^SBCIT{,[Ǐ€.i$DbWiwI?Ud: M2Ѩ3HKzyUjƓ0+_ӲU GRlٲ"'`Ϟ=r(H$T *Jyֶm[ZhALL -[$%%@V^888k.>#óg177̌Yf充۶m[&%%)SuVݕ~dǎxyyq޽[P׷D+Wͱcprr*9p@]չqϞ=o߾XXXмys"o_rE:uD.]tYBʕٷo_IQj>>L>}J/H$ߏRTcS 4YLLf#'3-̌'d?EǤ&"=*d&d=.R\&((H֭[qss9sh… Zׯ3vXlmm͛G޽;Z_ www;j>33 6鉝&MyÇk׎Yf)_\?vvv 2D(֭z*DRm6ݥKߟ3ghu}&OL-4hV899q!|||pvv.fA̼yXp!xxx?*OJ?Ύɓ'#H$?;;;<==?>:Nhh(5k$%%EJŋ:/]ɽ` RJ@˗/ӵkWMJ裏og6mWY07oܹsZa-[H2j4T*wVݾ}Jŵkx9'''Ou ɓ 6 ;;;&LKMKK#%%ϟa}.vŐ!Cڵ+ժU_՛0uTZh7+V:+`߾} [[[={vG c|lܹ7N)x);vSNXZZR8sL<1SSS cǎtQ _r%4i[lQ̬/ [1c;wEL6MΧ< ֭cǎ;V1 s!tdd$ެ]={Dpp0="$$ݻӹsg6o\ϼsWWW֭[Ǹq㰳gVVckk˘1c8qDu9Ǐ  O߉bĈ4mڔO>BˬyKÆ ٿ?#GDR1x`-lٲbccW>>quu%,,YfѪU+ MShh(*U2eW_akk5x{{5kڵW^k!caaaxxxĒ%KHMM5(~D"'P*M2@!+HCjR4NxԤ&]$%1h2tT)d?ߺu'2k,"##4hbs  oJ 5bʕx{{cll?}4,\___ ([jO?ǏpB>w} ~`ԨQ,\+++ʕ+%˗/:t(L>###\Be4jGGGN8d/J4hЀ e'>۷o~w {ҥ 7f͚5xxxƥK'N/ӓ &Ǧ/ߦ IDATONVVsӓ?۷+Xp!cҧO(H^ /_>RJ|7[oaffCN>qvs!ʣGزe Xٓ5j;RװaCN>^q2d~)$:::Y;w0uTfϞ͛ZbάXU3`Ŋ$%%1tPiݻqpp(ԝτ dȑZ˗ciiIPPP>}&**.]н{wBCCjժ:KLLM1`ʕ|?EqeinݺѼysiРA>^ 1x`V\Ǐ9rb… ЪU+VX f*0m*m… DGGgB.^HTT"ݻw9vIIItЁu})[a,`„ :tӧ3uTcSq9r$&M'::|ⰰ`r u~=z1cЯ_|Wl7n VZE5HKKcƌ1#Gigqttdʕ˂ g͚5mۖ+XQy ~!/ ecŋqqqe˖Zf͚iق͋yˮ]pttd̙DGGk722ܹs|r:wG-0h9s&mڴ)4M "jVV7oիDEE)a GRR`zرcYjƐz۶mDFF2~xذaCH$4/^~'ݏwĝscĵAq?K;E>J8PSY,wqN97^<аƍ}}}B?;wHHHϟ?αqJ+6l '/ϟ/*V(,XP|jZb׮]ʳ[n @ĈLann.BCCuGDf͔q"33SDFF @={(e/3{|ubΝ\xB͛ukk׮"33SqqFѬY3!III^zСCaoo/k!DիENŘ1cMHHЊI&J=;<}4XcbbDizu98G.**J*bbbE -0Q^=ի|>}ZJ7n,>{Lk7oT o\"q]O?$!>^~xy۷o !:t;wn >&UZSX !DFFHMMUh4"11Q?ms9'ݿ{ȝۗ,ZH?oʫ+C|nDFF&99YZ8~rY+9GnrQC=zϔ~87Ꜿ9J^ nB6oѣXzNțGѣGS ʕ+QF>SҔ3oVޱzÆ M+Ϸm&*V(^xQ˗G=J$IIrAq-~qqq"66V۷ t4UdadbMz#23HI8Kӽ$+'3#R16F;G[.ݺu@{d۹s'j5c9͕#I{Vмysj׮]`ݻwg߾},[_?wޝON:w")[,uU_pkjV=|Q א|˽M6\pA+y[JyiH QF :///fϞǵfۛדȭ[سg1ڎxxxÇY~=ފ +++nݺowЫWV9k4kuaȐ!ңGNʒ%KgV(g}?wVƖ4vɐ!Cr311իJibbBǎٴiwÃ~1mڴ"˲m6\\\ɓ.v}O߿Ͻ{h֬\rV[!`LLLm('tx? +++ \Ca\~6m(lllhҤIcjn}Ņ5kҭ[7euAد,޻wZlFhXb*ɩɨjC֭ܔ)SFЗgZ;4U* m۶f͚ڛ@4i={:YC:_reLMMyui]TR%Pzu=+)q׏]vͦMtʍ9 d ;w.zRLh0My˃Xj }l߾ж"'[n݊F̙3 4///T. S^Yϟ?Oǎ12|6mHHH 66HcuP)X2H$?RhiZmyPg%"4~QYYĬ| 2*X\>|ȉ'h޼9%+V֭[*}zo2e Ϟ=~PjEM2e({@⣏>Ғ766+NP7Htaffƺu눈ښ &˗/ HժU9q ZܼvWĄի3tPƏ͛>Z yajj{۷<}jԨQ`Z;o͛8::~zr eʔF,]4_u=_8Ϗ˗xBO^شiGGG +PB222#93АHcnn;4*TP蘛ԩÑ#GXh/_K.L6Mgiii[rL}JA.Nih4iѵ> ٛ$wZ322t֕7͆ ={6,^X15 Cʣu[śsٱcG^ʰa8u 4К;Eܹst֍&M_k.ůZ&!!::-ϟ?ݝغun( 0**7n~:uꄃׯ_'&&t4/;|155-XSH$rTIԤQ\}Ul5ebbV Sڔ1?b1pjx,5ř?Ҏ5jTb37glݺݻ[5j}VZrQ\]]ҥKڬY3F8q"kfϞ=ԨQ;Ҷm[ڷo9r$fT{9~y:DIIIQF3P*WL6mسg:6l0k۶-+V 4hPjݻwf 6 RSSܹ8, O=OfժU888zRRR3f ќ:u-[faۛEL۶mK|Ŗ-[>{+#FࡇuFEC7oL@@@u;wd̘1___رcf2T&MbРA\|???,YBjլ3ywyyIOOgԨQ̟?ߨ&;w.&$$OOO=kƜ9sߟԨQggg󉏏'$$Zj;`ȑhтuEVV6mbԨQ?(z?;?#'O&11Ѩ(7|Ν;iԨ-_Zdڵѣү_?HJJ2bʔ)KͿo޽{~yn:\\\n)w_hh(o}%%%:PJ:Ğ={x7iݺ5zb;f͚i֬MYz5iӦ899_ӪU+V©LNzרӦM^/Oddd0`]6;w&11!CeJiek|j۶-`ѢE=-tR4h>Z=+s֜9sxWؼyszZG׮]c甒^sW_}{G#F={˭Ԯ]3gҩS'nyvMP?oLҥKzI׮]-VĐĨQ7Dpssg!Cx饗^cʔ)T^?ӓ'BgR) V;ѹ>. ptmgLvq*.^O~ο6a֯_ϤIJ\aӓ'x)S0a7oμy_1iH,Yʕ+aٲe)nԩ 4wyVZe$eYd >8233ol&!!ϒAHH=;v`РA޽7Z͍3~x0`2ji/ >>Aq[+0|,[P~w·~رcc65Z43m4z7|c|~֭[K/YMѺuk9r$;v(ѽ~  , &&3f0c """hԨ۶mܹsI^:)))kt1ªMLL | k֬G̛7L̝;///Ȓ%K۷ aڵl߾^x{Zb[i1M4aѢE̘1)SpRYIiٲeD* ;;;ϟ1a|MEǏ~(Q47dY10ƌìYѣ9|_Q",}7ԪU{_~{̘wΝ;3k,fΜɄ Y&DDD"""""""""r˅DDDDDDDDDDKISb4)Ydɠ ?ߦf;3ժVG&"""""""""&L4m^rrrptt}nU222hG&"""""""""&L4++`6d2Xk61o YDDDDDDDDDC֜ yyy\v'Or ~?Ή'8~8gΜ!!͛7{ [ddd`2شiSXzꑗWk+&?? .Wxx8gϾkҤI 8ٱcOe>6%%DVVm!""""""""R4=sLb2IX,_iyQV[l!::{/=q^^^M&3g,{\ _|ƍ+F|7w; rJӳg2u۪8p!;;L233";;Wbgg6}'ILLڶtR:wL˖-4iϟ?sN4i@RRSLa鄄g}fT^|>}ҰaCuFrr2Ǐnݺ`28pqݻwK@@{f˖-ƾ{2~x:t@f͈ԩS$^}ULBpp0:ubɒ%F\OfƌDEEѰaC ?P}0LVQ IDAT  > //6l7n rM&۹s6m3n8?~i…tܙ͛3}t.]t"""""""""B4ĩΎ^)>Dvȑ#5W_}ǵk׌{ϏƍG^^'O&**z˗^̶mg֬YtЁnݺjժW_|rvE:u~׮]K6mxwhذ!/@~wwwƌCBBbРAVj֬YxxxHӧk֬ ++tz)|M$=={ҤI:vիmiii[N:3tP͛GV,5]v1i$&O*Usr1^~e&NŋU!,""""""""R6<ٳg?>|Kz{{n-Aj6b"Ӧi(LwmnϴGЮ];._kFtt4NNNFN:uV6l@hh(4hЀƍ[K~hѢIIIl߾ڵkNxxחVZqijԨ@ǎ6l[xᇙ| ,X@`` {o;n7a VUj&77l:*NTh|Νc;(*trssݻ Ã׳sN~+HNN{aѣу~,Ã"""Us1^͚5߿?{K.\.]TϩHyUXvUοUV}XfMvž}hԨQmƏO\\̛7ܘs֭ta)))3hN:EBB˖-v̜9N:ƢE=z4ժUחe˖wCۯ״iS/^L prrbܹ6݃"~~~|W{ԯ_+WZoaΝ4nGGGZj:iiiFٳgSR%ׯϵkX~=[BZ`ǎ0g^y6oLݺut="""""""""wB%M+$aj2df͚L&c'ou% &ɓ'nO<)Sx'4h1,믿楗^@܈Yf,[P֭W_}?ܹ3 ?c&Nڵk}pDDDDDDDDDXf ~~~ԾJ-}{f)ƍq&+pEx"׮]#??;;;ypwwRF;33ٲe 7o}w;=4uqqŧJPPva>?O?^x'EDDDDDDDDl prq x㍻|(i*"""""""""R""""""""""3s^|3lgZjDDDDDDDDDIKNN6ͽJFF[DDDDDDDDDIfeeߘf3yyyL&f3f|O!}ޚb!//k׮qIN8/9qǏ̙38yuo!66z L&6m*+VP^=n.))޽{_Y%?!C0a„’gΜ^LF5ib<->j˖-DGGco犈PaSOժU݇JSgggّϒL&fΜY, ƍWcEDDDDDDDD䏥*MϞ=ԩSoRPWppp ''l233$++l^'$11jҥKܹ3-[dҤI?j?Ν;iҤ@dd$׿}L26mЩS',YbUzE %""?jʁk׮pBhҤ G&==k_iݺ5*0Խ^z|'гgO.\HDD͛7g\t L&qѣG1L8ps1m4BCC fܸq?~hi& @&M9r$?U<۷o矧QF 8̓>G}d^cРA\|>}ҰaCuFrr.%%`֬YCLL !!!deepn%%%1eONHH|gs<_~SO=E&M3f N*<""""""""TsnlgGeVC;KrFūJJJ ڵk{G¤[hh(=gϦgϞߤ߿?}a͚5 /piOṉ'$$駟2dO...V1_pg}-[2nܸ*roO?%--1c0qD/^\QFk.&MɓRJlذgy'xSZ5zIff&?<̙3ƍvZ<<<4h˗/7uU>zlf۶m3k,:t@nc߸q#߉bȑF\ŕ܍7<&OLTTO=ULL>^zo?ӽ{w._l={[OyYϟ2kޅ~KfaXȴi||>>qA0rJ#IVPP@rr27Jؚ5kr9:ٳgquud*\*::~6mz_Nꫯ9s&nnnu+WfV܌= iӆ˗/RRui[UPݹs[jUBBB8|M? >VR2TZ'''\]]quu 777&M\]]dޯƍiڴ)P8dĉ7Lp+œlxzzr~gm6#x+cǎٳ|7+K\fKC`ٻw/ԬYV?өS'̼erޞb ch_Wp.ՈrrrcҥݻLZV<[ReEDDDDDDDD]}݄)[\.\`C>s*Uģ> }عs'O?42Y\Q5-]zz:׻w&<<cٳggffa"""""""""|6&LmQV-Kږ8Y&vb߾}4jԨ6Ǐ'..ggg͛?Npp0[n{|PR>}U=zWWWtRj<͚5cȑ <'RvmC5ر#m۶} >#Gb6IMM瞳gСCߟ5jc=FNh׮cӦM;v̦TF8;;'󉏏'$$Zj;AGI-[.QQQdeei&F##F 44ʕ+Ӷm[8E=zqӣGzomڵk0sL:uDZZ ǖdΜ9+l޼uܥ0f9u ,[̪aÆ7-"88mۖ>Ƚ*M+$aj2=5k֤vԩS___|}}qqqή0ijc X~=&Mڵk7'`ʔ)L0͛3oC͛G\\&{I\\IIISR~},X@LL 3f`ƌH5b۶m;wg}'RPP`,`·~2d"!!Znݺ|WlڴaÆqر7-[4E- ͍X -[fV~ᇌ;???,X`T?6Kq̈́@mJN%lj{77wҼ唞΃>ȁhР;HMMeɒ%%Ƞzlܸ6mщH5k燋M ((( --֭[)kܨ1g2r /^$;;/r5󱳳ٙxwww 5jԸ1]?+>u|vVصk^(x NNN896]񕶨TjհXJBDDDDDDDD~/ [JDDDDDDDDD~H1/"""""""""(/^ѣVS)[3I˗/q&|ڛTZ ;K (L%G- Em֭[gSL4m^rrrptt}nU222hG&"""""""""I듨JiVV&)/l6dl6c6)(g܇,v'Oĉ/?~'NpqΜ9CC7o[bccy뭷d2iӦRYb#//&Mmv]M .pʕM*̙3TZ6{1դEL&W[l!::{{8PkmۖsC̊+XbpvJDDD ԩSĩJSgggnk;;;o]Y̙3_|q|Vkt Š+wڕ]Z-e YNʙ3g݇+W@NNdffIVV\z;;;mӲz'ILLڶtR:wL˖-4iϟ?sN4ibl;x ɿϷw^ƏOh֬:uλiҤ {f˖-V}"##ϬX^ܹs 447xl>^}U}]ڵkGll,͛7'--}b2On[xx8/fԨQ4k֌~;v\""""""""r֭QQQE 9Mo7qjW_T΄9rQFꫯB~v횱޽Q~}0C=ٳٳga"'Nݝ1cƐC4h͚5 fϞMÆ _~ 33.]ό3ѣzooMjhٲ%Ǐ'22~l}Y^j3n8%G 5bPixڴi@&M#,, m۶Yg:c Fll,=M'<<חVZqijԨ /`٢E ؾ};k>ZjL68nnn^x޽{иqcf\SժUyGiذ!=z >>vqe^{5ٹs'ȑ#F 4ߟaÆ@XXJvOo[k$>>gy8~,^'^!** R!Uh)@*Uxul~~>gϞd2q222̙3s).\p<LJ0<&qW\i$ HNN&00Ц, WftHd, Pu1J*X`p%cղ-Z`Ν;wҢE #a аaC9|rV S[rOo[&:m۶2"""""""""RUTŠf3UV WWW, fd<hGGG~W6nHӦMd'l^~…,\I&1p@~W\y%J)pttʕ+VI"*U2Uy^q[ȟOeo7a 5Kvvԅ GNNNiZ<)y9֯_?iiiQ^=pyپ}M}'''ӽ{w:t耿#<ʕ+*S˒\,trsswMxx8Z6olS;Gbܱ7EDDDDDDDD^VHtŊt֭ljȟ[/4]bDEE8&N˴̊+JW|E*%NEDDD"uSZ,#XRYLŃONN&99j͎QTDDDDDDDDDwaQFIN-ljUnmNXb8Ʉb8ۥ""""[vJdddy;|;wڕ]VT,""""""""""w=XE*N5<_DDDDDDDDDadds[=< ߍ狈Jk,,hsTDDDD^R[DDDD)iZ,*DDDD)i*"""rϗ:v~ͿG,""""""""""wT֭KmcSTDDDDDDDDD|?[xSTi*""""gnnm_DDDDNQTDDDDDDDDD{[^N!"""""""""rDžƦ)@ttm#"""""""""r7-_ܦv6'MA"""""""""dKi2%Mz]jժ27ߡ8DDDDDDDDDD4)FISb4)FISb4)FISb*$i`nfȐ!L0ٳo**Y`&ɓ'ypW\1X,[o.Y7l]߶m[ӧb.#QwFpp`29sCDDDDDDDDs4ݾ};?<5b߿w%bbbK<ܹsL6P7nǏ7޽Xݻ7[l1ݻӡC5kF||IbbMmk """""""""tw$i?ʣ>ʜ9shܸ1k׮5_|T_{L~Xx1iiil߾_=z0t6mz\-ͬY 11ӧO֬YcOHHO?eȐ!L>///\\\Xh~~~$&&k.ڷoCߥ]1ݑ3gdܸq :vڱw^cժUٳg7'|ٳСoX@-HJJbԮ]pÍjՊӧOSFRc۹s'ȑ#ԨQ ϰa "**uѶm[>>deeWft҅޽{CmhH4l> @LL K.-[Я_?uƒ%Kv[l!:::u}4nb(K´$5k4ؿ?M65eeu“dff_>Ç bZU^xu…L8H|M}2v\rJ*vڱ}v:ĪUh߾=-[߿ojh:7h9Oї-]Z """"""""" O:88ХK{'rrrn$Y&gҥrJΟLСTZL=Cr1cɓ'IMMooobbbX|9+V 00ʕ+s_bSnnndff||}}ٳg'N(ql&77r"""""""""wdN#FJʕi۶-`ѢE=gҲeK &XO||#Gb6IMMÃm۲b AVz""""""""" 4 oe 2C`wvv&00??ͥ~,Xf̘3#F ,,1c0k,za,-6{{{ϟOǎ8q"III%11<VNi^zksΝ;///Ȓ%K۷opvvfɒ%3|p4KǏ (q{[[DDDDDDDD~g:|i^hV^m$׭Z0/_NXXXH""""""""""(i*"""""""""R""""""""""(i*"""""""""R""""""""""(i*"""""""""RL&M;ƴiӈaÆO?MΝo8fٲek.֭[3w\,Xd*qRc۽{7]tI&ƒfզ_֭ٓ[@FFU+W{io߾*GUXO?e˖deeѻwo|Mڶm˦M'""UVq)K6nȺu묶=zTm_GZJo888K/1~xΟ?OXXǎ3ڼ$$$ٽ{7ݺuFS30e^J.]8qDyoC+{ҳgO֯_Opppmڴi;ҥ YYY,Y>3d`֭4nܘF}ݴҼFݺuח???\ȑ#Yp!˗3`.]ĤIXrQ5Svmmۆwb{GT;HdY!MPjQQFҠYZUV " ٹ4W~^ӧ9'3f`/Mh)ROOO;;;ʕŋ/ SH=HJÉ'0`L6 aÆȒ%Kpqqm۶̝;3g@RR>>>(PwO?|MB!B!BO--Z?t/&88'OmX_jU\\\8~86lu)Z#L2ӧ58pM6ѳgOnݺEzPT:t72|p)BʕoС!*B!B!#A9e˖f͚,]KN[n7GRFlCՒ齔 ,,,pqq:2ooO>FuφMVST)^!B!B!Ļ絗ԯ_ooooKFpqq\pٳgcii @ʕc۶m̘1è///ΝK۶m)P@dv@屳GdL_PF |||([,ܻw)S0uTCtɓСCF4h___|}}qttʕ+̜9sP !B!B!޲I o֤{RyaT*bccXr%ޤqtt̴]gϞERqWjӻ4Fo&00N:Qti:uď?Hll,OSTBF?> @vGGG6l@׮]3-p)Ccĉ?$$$0g<<<~0BBB޽;KO>aժU9H\oܸSR%ƎO&L7|vwZ?o .0jԨL[dI6mڔi=QQQlGGG{ȟ?a9wF2{LJ ܿwmRRRغu+| ˧w1zw}Ghh(Gp%ʕ+Ǵiٳ'K,aņѬ\ݻgZ̙3ԯ_*UpBJ.̈́ L( 2`ƌȑ# I4/{:uƍŲehݺ5yͱ/_/.]0{lꎍ?&11ӧӶm[>3-Z@֭_Rw_5#,, 6РA&,_bŊ1m4Ν;GÆ 8t>>>iӆ~yҡC ew^gT^ݨ3tP7oιs8w&i\taÆ1qDVXa4{ƍӼys-ZD133gϞ͹s4hP_xuR|yϟOɒ%]66 B!B!ĿFddk׮UZc%3?R\L*EU 㕪U* ,PEQVX4jHIIIɴ;ʣGEQU*K.4WիzEQe…J޽MoQ<==dnݺ)ݻEQP 1۶mSÇ*gΜ1߸qè]/xbaÆ(ʅ y{{+ÇWEQRRR+*7o~a{ /쇢(JXX(6\;}(nʴL5|^)SѧOeܸq&%+3ܹ)\R)Zhrqqqdʖ-[EQYi֬ 1k׮UHC WVﯔ)SPǶm۔5k*:IϔիWg=z2dr)W6iժVUʕ+ڵKQEiҤ2y䗶oQufxMQ͛ \rEQ?'K.Uʔ)zE(/7n2h k({VMfoޤ6!B!;v(ZVYvmHiz}޽Kr lllh޼aShРA(Bpp0۷oҥKvIIIY>ˋs5J*ŪU/L9vΟ?͛9~8o0ZgwݻwrR@,dɒՋ֭[ӫW/Y&fffN:ѿƏϵk׸qV޵1*T>|i2<4>>-[k.nݺŋɓ'Iu:۶m_~1IhV}~:5j0~Y_> ӓ.]ФIʔ)cR<0\?߶ӧOau@Չ޽{TX={o>>CvM۶mqss#$$?ذ_VSs^Ebb"7of^LLgW~Fv8;;st:ڵk-[jlݺ5 6K./M߆kB!B!Wnrr2ao+++lll.?3?3&M_~m賋 }'88sR^mȑ#iР͚5sί؉dٲeݻ5k;I&߿͛Oe՗ŋs9n޼I%݇vԩS:u2dHcU .-'Op@Ƅ]ŊYf Ç7{%-J"E]6O<~E˗;;;t:/pF'_ԟ|bLα޽{$%%|8zB믿f8p SNeҥ=ynOHHn:f͚EΝIKT֪Up-mmکOV9 b֭9s___Μ9C߾};v,NNNݛ` uVɓ'W^L8^Z( 4:0bfΜIڵXb3f  `֬Yٓ'Ofök׎ӿ-Zc>}???Ο?ÇB!B!+TYfhٽ{M!z=~!~~~B!B! ӓ7-lT'ϒ%K(]4NNN۷x>裷4!B!B4 IJJBW_Bfؼy3s~MB!B!/!IS!''' ⋷!B!B 9vB!B!BHT!B!B!ґB!B!B#IS!B!B!HGB!B!B$MB!B!"I !B!B!D:-&!B!B!l%M===T;B!B! B!B!"I !B!B!D:4B!B!Bt$i*B!B!HT!B!B!1Nc7!B!B!xc)Uv!B!B!ksLL: gLB!B!B888tWz%MU*j5*X͏>9׉ٹ]l4nEL'?Mĺ_R$wؔƖNˆx.Yƨ nkuC;b~]>3vC7ьjtѳk׻h7ܹs9]0.]G1Lx{I,--ǁGM*Q3&^s7R!B!F̌^-I8 ml!>~-բ BĚFԶ8D⭫kM`[*AF<%W8{٤<t +KtX>ɓ9V+Xz͚}HϞ]0*GeY&ѣ'iݺnnY`ڛkB!B!L*:s0\> å/VKx"x.5*zK lWBs(Hw 2_c]}.*2C}Bdً}>]Bs3'M&zbbb%::}i +Rč=9s!ex(++72!B!B.:|Kw_NGyT_1mJh&D~nQ? %,Zպ-$7$>$vXm؆*EuLbR7mάK,֭lݺ= ׯoay۶BQ uf5#FL$**uc{{;||0wd>+8;;Q@>&O8x0HYaHzJɒŹq#aú9sdǎptMރ =}XGBC}]1f̔,= @gxѓ^߾}u~p=P|B`4+Oz51b"{ԩslݺ?l Q!B!wg͚#M:|sRQiⳌ1?gL0Z2^;\a˖-ō SHM׃%q{VN&""??rqC3;\ÇiԨ.7nBVS~-~a!zʜ={ gS@Sd1111z{Ue˺ۃRJH")[Iӛ7oӲeBC8q V5&o^"CҴYί)!B!puԩ;4R: yb [/E>Wbvat]mgOF͈cOuFcߠ -Ot:Jr2y]sNtYX[ۥޏĊ:* }wҘ$) W^GELLѵXý|VNt| Г F`cc͙3?>ǏòUWH6˒%˩Rsrp!7%J˰Z&o<&=Cqɭ …9~4,[ueɑW8q :>ignnNhh~B!B!ĻVZoIZ+cNDm[Gͫ/X }bI7gY0EA4_p yo*2g1gUt ظv`nU;:ơgF~) '%C]\x =4LKirYϼx.񲶶pڵ 2333jժر ؇q6͐4֭ Q\xgJc8p \oukR3f ))Iҥ3! QQL2)Sf^#1i(7Oxx PVF!B!945[.;TTJsŤ֕ߕ;7 y}'[:l]* nwLrsO,"J"nv!갧cPrԺV뷒ܬ1}.T ]*EeLUYEӮ^FOf K!u((iР%|P-7og +++٫a3PT>Wq18J"n&7y$FQpRSѲeS> }|k4xCN^жJsM֭̄ #077Gr9|}?T&-߇1e D !B!f4UYZ/x )¦\%=PR^[-#3=xJݛn ]d6bI|8F…3b0wɇ]=&x銺iU;Q}5$|֏=IeV7x2%@ٝ+L̉ݹz=v&<[ڟ\lɝ;u%r:C:<<]RRK,g`_%$t?$''m.z_bZ ,Ogla>+ͅ t ԇ$&&K~611ёӠAmg+VÇ)R^^ Yl({;޽vfڶm΂Xvѱ-Z&M2bD""mرnOߗ-صk|PZf͝;LRR))Z\nHh.Z ƍ;wﮜ:up>|sl|P^MB:hР6JAB!B!aҥo4U1pN>Fb"%ޥ6=If)?l}ƨmц?!b2΂kbEEF=6(I$\:Kf6-%eS1|oYmBӓةP?jXߚ4_&s_t-V}OZjƐ!~_M?*[ Uۻ[DddjSYԩcg?7o0YiBC0elzҩS}@ǎ='LN@6mZ#'Xb߳SEEEE~=pvv"<<5k~ggK3{IǎqVcw}93,--x&..wի72e`\zɓg1wLrcw3P|Q׋кG,X;Ŝ9ʏ9?W/oy4cNqu8JEytj*B!BS3rNSEFF|<`ݴmG˜gמB%c >}!B!ܹsʹ%*>7n3x+=E!ĿZydj!B!_JB[X~nB!BVLS!B!B!HGB!B!B$MB!B!"T®VlޯE蓓I}[F`V>ϴxD%*KKr7nMJmlIyJ$^lTƲPr7ie"(Z-o'_+by%g/4fPk_^aF/C\Mvx/35jT*TZMÆ֭HI՞z_Ovǘ1Sneн{GҢE׷ڎŋ2vl% `5Gf̘`xPKB!BJՒ?zbGآ(Z-'DYjTNmkcN$޺jд mSrUw,MJ^Iy?6R*JFUt XY+Um7dccͼyDEE3q7ҿZ~XaR7n(@t:= 99:uj{>6F]7p`@H/=z֭{V F !B!4Ut:BNEa}Kw_&E $\:kT.W:(:ٔQ4'|eƺiIyӥ Qt:4'Nً}>]BM4ޞ/1m\ܹ>//j3JB!Bw%M3aQ)fz "ňXz#VA h"Rg* gO69*ss ]t$1!; 1#a`o07C[c`XZ7#aP_Tx 6If IDAT0'%֫6SrO?",)y烙3⼽1dmapvvM07 99٤{{;/F3177O{3j`ƏLJŬY{GUV |)1 P>z͕ .8Os=z2.ly~DDwާjՊxxaʹkPrƌk~`iiA`\BBIFFIZaee޽3MB\ ={uڵ11&U*_x?99(I2֭{h7'0niJ.ŦM;wVVtڞٳ'ӵ@J>}>11`F1_=JʳhR?s6̙3{0:S,--%i*B!_%gj5?"-4ĶbUs3* K@!|"SgmR/Z9q][T]I {l*EVfHAVP? ͢eXBrsOHnՆmRY$&X[::p/MH6˒%˩Rsr [k3ᣣVǵk7_x?? pt?DDDų033V]s87~~=qrr|,N8%m6*gnnNhhXfU !B!:94*VǖڶW_g[npju4i(y{T*P2z'ϊcϪTq<5)RwtLC2&JI133:|TM6ѓ,ZpNb..NF+e7&::0O^v!CaffFZ;60n&CK=5Lۗ}EɰfZ9}6$ݺu`4jTlV)E\\lՏ\oukR3f`ҥ2U!FEE3elL{ FĤIh޼ : 0^?DUt CnZ7jV5Fυ*!PhI}6QW=SX-+]WoӬYcrqH( s9ZlÇ&ftL(!B!k%MUV>^Cʣ)WpO4džVEcHSF<%Mr7j*PsDx;Sۮ䎊 ml+V>7#N7;HWԍMPRgә>q!~$ Ib(/߶Zp)1XG^afN}6,=z _O(S$EvSBY$,Y(JKС%~~INN6)fʹmۜ v&c)Z0M4dĈDDDRXQsg>HBxy5d 1%fǎ`F`8/]iӦ9?*Ǔc۶g.L%+V`tf˖F+޽޽..{Sgtڞ;Qj*e+љرeoK^ڵ>L-3Νt&))-W\7-qrѻwWN:gy>ܹSYl E|yW&AA!F!4hPRs !B!Jmsas'{wlRk$MOQX6Ǿ^cֶhßvמ͸?{-v=]S",%%Ֆ wwKIn{q/_{VP$vj>>ԏðVf+&WI]bh˥8TԪ ;AڵquϜ9_ceeǙ7Fdjz4x{u돈"8x?Zm11;B!B!ۤrGݻwӶm[ cn>zB![Jxի3|;#B!?Ν3Tͭػ(-ÿ-M[HO"^JPj)HQAE.Hҋ H:雾zYX6E<\v|3M^N`ڵn:xezBչr%}r8Z!B!x"(!Mkl`͚ {B!B!DB!B!BqI !B!B!}$i*̵k߱=!B!BiP٠njh$emTYlj]czS EZvjMn#HܵyjGwXIy-,cjQ;_mi4֮]ɟqLχ{!!YnK(l^57o.]Okƌ\7,r(՗W6ժhhذ=!Fղ~R{#?wGl^sL$%_(UK; gΜ*ݘ8֭lg_,pB!B!V "m:cDµ|eB5Db/.z+ Hܷop)D>GPy2ۘ\=fg.:{!se0hԨ;wV^ RJ4h̊f{NLfN>iii\+C pnA/wtnѴiC&M΀ atB!B!W6SceH B[ Kgz1Q6ùS2j|0G`FVtǃ*c-Q ןנ]>}) ^z ?:m5lXÇ^ԯVϞ]9p 8}Ν`ZEkfƦM4+'44,cy\8q>Zg΋2eJ itB!B!W6Ct&*olsLZy{ %4ýfx`ۭSGu3R{YEގ۽?~s~kנDv=S…V-9$daas<(2G%()lݺ9s`2wtКkǛɓe:j6b=XV\ /ًyz/oo/m[;Lij Bn/KŊ䓵Ұa}ҢES>tVeZs'w#9w;S8ڽ{?;)pTq`H+Q !B!Kf!c;,U^>hʔ#vct= `Ǧ"H9y4OGRlX,uL X,1qMݺ}.ѱƌ \>̝;+W?~ %J0lkh4.L6;>sϵcwJ V3g~wXz6l%11!NP0kG\vq>ARQvuٓμ {Ç7J``%{]( 9NŊh߾JžK_ժʩS眚:uuk9BWlF#\cRROQ]Հ9|vN:G>/yf+-B!B(*ܤRe{0^zK{L& :o[EXSY-<#m'w !B!ߪP^:'ꛙƸp,q{&+0p˯pI"||PPP%BC S#G3p`_*T(VMkגJ]fTCݻ|bn)Mԧ[,] ?9sǐ!ĉ3Y{Ȳ\TP~mvL?رSX,6cO慟<^RNj(KHH`ذu*.cKN嗍5,!B!_ВǜP+AŽJOEZCK Y[؂];BVh4yOOu^*G])ӊʀb[w}ރ,uqQyy6RSWZ,z}O8ĉ3**?AA*pwwP6~ZL-T9¥KWrlיOP|YΞ…?ЄB!BPrMI2X w(w+JR st.N#D~'x̟dJ,P女obZ.>qbLL,2Wbyu@0̬[a5KW5T* 3ah|5>`j1 6 I2iһ9+3!!OO|A:] >>ltyd=z &KnX|maO!B!x 4UǷ$MC*U)lC%)E0GG:ݎq-ʔ)cLrr}%ΟLfoy[&+W:&$$b4裙|tځ_bh4qę,qwwcTȹs9koߓ*Y!8w齜ѠA.^ u8LRL0zjٷ;A̒%gZ|cdPl/Xr'ׯok׊%i*B!RBhAXS1ݹ[՚:stȻڲQyyr:#hҮ]HvXbq^?sͩK:6Ow$} .][y9ZnRvEeXX8]\hM3`@O>lKd߃ ~3gγbZFġCG:sSpmf͚Ǚ3ٯ.W,Ge۶ܾ}2eMgXde+7:'ժwKN㭷Ұa]3}:uRIߡ.qvL;ﭠs59JlĮXDV5isLct!4v#\ 6{..:osރ;ڵkp,cvG:O3jP|| FϞUÆC2e)))]9sy?ЬYC&Lr^ĉӜӣ8/>IJbů,Zy;yNzjT*9p ġjrQ4nܸEhh2i:hP?j׮a}" LK/u套{WFqtГTC!P-[Vc=Y0`x!N!B!^xx rSTz ֮]K֭sW!3 .g[Ua?oݺ-xxӨQ|ѫW7-[# lBgN>W);B!B!xLS!'kn=H#3Ϧ`Bg͚ Vtt,={wwBkW!B!x$i*bc㈍{B!B! l*XU !B!B< 4U(l#)T!>/AhJ?o~hjו'QrtƵRcwNШT?o_~j,+仹3&3dȀ,7s=BBѥKonZ-[V<}Yjῦ4oވm_AA3ߓm[a^欞=1,>^{?c|Ȟ=8v7.Uflݺ~JPչpr U+sc4b`c/={>kLx Tww"f);%]Tae{IugJLL"""UwR}֬5x*t։'΀ bرSf*eJ:tĞ0ͫ*U*dO9r"SY/KĤ߂cOUfРTTo"@z'ISHO-ˤ3}%'$%Ԟb?"8&Fўx0*W4K_PF}F|^,_KG8SlZsHQ'X;.ݱ1vHJb *2*CO !B!D~ZԽV=<=CYPb%Hر١\驣Hv7BXM Dq蚵%nbxh.L|?ABBCY||~իWe߾Cѕk7eJcݺ{=ȻNβ_5:':`# OY6^^EPorxFTx-;y8J8;K5̗e~ :5I;OOL&df#119 **+}}3}o$&&f;1g2ŋ?ȑL88?**_m߾~`٬[6{W4 {wh_Vg#!!Q 9LLaKUȺ%Q]lVlVld%X?8'F%_ !B!D~JT["ޝ_Bi5iW.f^#!.~@t\-ja?! xz IIɔ,YܡKg߷.fŊ,]:Gg:3&&AF?gX<ݺu}VLf֭iUfNؿ.S] FH_9ahΜ9Oziժ);wf.'γN1rE}ܹ]ϗ;#85qqQjO'X=2}/Ml>e:W7B`O1TήNJJfϞ.K_9O &&CTJ5CFӓWfeOn|gf?;[LֆE2ƎPw}FQ\CS'.}B!" 4UǷ$MC*U)lxoIJ:ixuёN8~~~>hla''8,/ӬY#[֭dsF1M|L v[ n48qL1wtU ܹ̜5۷ʶcNѹܾ㹡{wB's :=رoqYbbbr 3@ɒ3ЄGҹDG/͙l[yھ-vjΟ͂t :'+Wҥ+nn$'ؓ26cnƍqN奯;vA\q%K'HFeeyoݞ)Q!qfEu`+DJŸ~=yB!BVBhAXS1ݹ[՚:stȻڲQyyr:EhҮ]HvXbq^?s:Ffxeի:%O|ф-g+>O:Rr Y3 N~=h׮%.b4};ۦM0'}6KWQdq e*Ù3Yb-#F С#Nh4ܼyYq\sjzuW_Mgժu'Rliڶ}1c>$662xN~q-Z4fQy3bs!=;6?={dB̙ 9sfa^ʕpsNs⢦hQ? }uZDUII$&&q🄇WG܎39ΰaӱcoKݺOӽ{,c A=HKKd2sefsؐ?IKKs8!/}:tٳx nܸEbEiҤ>[f;w"!(( +ի<u&dVN Aٲepsss,B!%M/s}C]v[V#krҽUو]":k;(bW-p1B2KҘ:U*g:ƍ[ѨQly;vN5j(>>^]gρ@acL2֮p)SVf2ah8x&NB||>a^gطh\y")) Z͇ȑlڴ‡K[^NyVԫWLOSq3wycBPpqf̘U9)W,K:3~XƼyl$&Lxi&r5[Ą g:ӎ3.^ Og1p`_ƍŋ|,fϞeIә0a4@TҾ}L+! -[vү_|c?b~J/||!$YvFhlkܸ_I|KhWzz֭;B!BOP/;vЭ[7KV8!D޼RW^z9wa屖<7n\O>G=.jztКwy-=ے*U*Ҵi ǚ5Ia=ztͲ:s9I@U)U$VCB!B"(!DKKKc>;+X ZMZ7SB<|O>YcߢZ@Y7՚)E& :OxcN#ýF#ݻgǣ|2 B6!B!Y)JSB)X2st$~}-_åXSSH=w9:Y,Ν=UcI>z0JN5Pzx^#~%C}DRͱV!ģ]+VC՗ ,ZlӃ͛wp̅LmYGTknra^_χmmퟗ/_޽HHHWq^{m$:';waJ.ɚ5x9~V7^mgt>}g~#ڴiRb] !B!N+X4 Mw>O僦L9b,q(O9Op f}ljTG[ms6_aJ^^EbeDFFSCGR2s^gԨ!^Z7]Gvh4:y2,S㙨*xU{o'NҷϚ׮dܸOPTԮ]b31Kws'sR%8s<I.}e/7cΝ@jOObc\vڵkмy#VZwܹӸr%PDÆF´iؽwڶm4m V˞=᝙R̙a47,Hzʗ_N^qX= oh$i*B!_pJ%1^x=,L.u(߶$[,]dK_-T:TEhW y6}Pպ0d{T2p`_֬gͱmڴt̋/>w^}uQQDGWׇŋ39r8v31ܽPZ3#j0qN=gO>hCtԖ w&9$M;uj+c|h_YyҿOf^@JJ*wW^b򄆦]39q4qqzaz.]>}r/K/u!/.Mٲ]Y=(11$w>奣o7HKK…PhҤ>c|Ȟ=8v7.UfB!BVISVQlcjcx_2蚶ׯýF0~/N9"Ǒv fm0޹͐wf{G6mӫ*UwwwJjBfr xzzoIow&N9y=!pd;*V,GDDz}V>%$.JcCSGo?v5kK_[W5 iҮb3gRJI,\3&99F1r`J%//3;Br߿ɡ\T_[oСṱ[\C%JٿyNMƹJJJdeIww,Y-sE"#*uf~qwwwڞW%d3+[":O &&C|rrJ!R!B!(|NU& ܪpг$_@(AZx=^]4s+l61QVϟ B<\3TlذBv-!}ff9s):w~۷#nX8p SL b=I˖M)U=۸q=pr_YFx{{nZLf\j;}%%! ??_{YJb;ŠAsms/^O?<%QB!B!?@ISFK>$cs 5uLwퟵeˣ&LcIv"-`ŭzm~v/Q^*/,XԾElМGC ;|#0"ׯ;QzCLZZ ,aĈAl.yy{ .FѩUӭ[j:V#>>eKӶ3!q+Wѣmnn߾K2OЦ3,Y>gb~m'֛i>`ɒ,Y];pBOmڴzgXt%Kl)ƞ=ӧ;~~ٳߡgܸq#3c\x=ztO>W_xuVb˖f_yyҧϋǏС̞=ŋWp-+J&ٺuQ͚5RLeB!BQJ*=PyR}1wv[`IVf#v"ꀮIKc]å{଩)蚶F59Iܳ #eժ(_NEpc;-[dzz.]g}&cxQ :cǾF͛wطIIz=zCLL,+VʢE8IJJW?2l{=)S&ڵ3gA]~#4471pf~V3ƍ?s]-7|+/""5k}tw( ڶ}1OΚ5?ʕv~)S;ČWzMtt !!2V(sſb+!B!Y\߱cݺuۻ4jB ٻB!BzT ]֭[W/B!D!y\ξ}9-B!B |BQkl`͚ {B!B!4B!B!BHT!B!B!#IS!B!B!O4U(l jpj4v2 6`I@S,GfxĮ1)+X ZMZ7GBUVa.k_6/k~p(|{g҈B!B!DAhBHΘ#op-_Pc1QĮXKa%;kPuo$nO};JeqZI9}U?tį !Qf 3PfB!B!(ZijX=>^fį `tkj N:gŀ͖~i/[C QP C͠V:嬏l:AnUMsLM2b ^{/!B!B)EPj|I>z(@1;b:fEk媨I=**̀ \+W/˛} !2P@Z`lsኄ$,fjR ,eJHI<>,lEtش<%B!B!>Zih)g֔dLwnV)lyT^ޤ>e[E[I9y~1@]WoksLB6WT=cAT*?!V},'K:Q̸}X*>I#Q]n$R%@BzE1} !B!BOJwT7ߡ.qvL;]V#kriYeMMA״5*o_I$N➭1Nߴ]x4hUWfrUR:b"*A},Wb]SXT P]XT\EGp|%B!B!>?`ǎt oҨb\B!B!B 4B!B!B BgfUVIضKb<Re82SO'vMi]n j(j¯i {Kxo }Iܷ B<`ڴ/P l T͛?8arҺu3~)VaG!B!x$hBHΘ#op-_Pc1QĮXKa%;kPuo$nO};Jeі)\fdBNs|2 B6!B!U6SceH B[ KgzsuaX0;+Jo'&boŵJ5I!IcՃ 2t!y}=BHBB" h !B!l)00ݹ 'kz!oQP(0޼vȻX>YރVk+_mވL͛7"$d͚5d۷9sdNmY[Ć ?1r`J>(2 |ٺu%GEe]gMq+?c_ٹϟSOU\=#F r(h4ݻn:J`ٳ={1iһ' !!ۘ:bGoo/0tzƍ |tlIҥK;<*}wzf;@%Ћ "ҫ"**+.ʺ˪6u`P"E$ }&G2יIyɝsϹfrg{sΡMڦhx衅|ػ7~6xl7tcƌj.   Ӽ9Mj%/Rcisfu+YQ! ڽ.AT|?/Yn xpxJvzh!7n孷'$$IƢT0̈́bdff>K֑,ZtjEj)2W_et3( IddɿNrskO=__-m<ݻ5ի_E7ӯT*Yy4$v;ճvl5Cʔ)wbƤ~ȑ),,bٲ1 9cx'ݻ;!yy̜9_Sc_y~j5۷U   plT.X`>"^}qX,ӒmҚlUUx$9(Q`Ӕ4X Y9\q͚9s&{wp-7Ji4{.6mߔ'$|QxЭLJ7ee5 1o,xc CuRBBi*~Çoz.wwөS22je_-I0}X`T_QQ ooALLtr3=*j|)hڐ@ν@jjG`ذA<ؿٽ{G?F   עAơ W-cZ2`ӹLG܄b.c5ANaxPE,}pQ\\6d>.#~~lܲLѭ[g22HSÇqs1S-VK)++ KbI) rs9Z22 >|0ᄏN*ӿ<Ǐ'-$uxàNb=?/4ZKfua)`~zc6駟mz3u44+   -4)ZkiqeTmPEoVe=ENIt.L)A(P\\~~WJd[-f䩧C!1$˖=O^^Wuڵ믿5k>Ok^:v7\.'<1CMtiiӊ#%99_J+ػgVx޾[jx :ϳL"]{&lyy%wߣ -x   pu4;h oEȬT߅ȁ dh{xs8 ieh8)'{ ԑ^oGICʡ&Kpp8HIIgĈJC b%3lT^^FSos}hhCǝ;wh]ƾ}ػ 5 9r]w-ѣ'?~ Fj:*71MH:wu+s @FVyh*_H    5+h*Sksv^c)ȓ~t@!hu DeH~p0ML("^{Y1?*__5ٞj__-[G5ٍL&k|p,iYQf3 q睳xnڴiży3ٺu"P-іɓ)l6׻ؾ}7FQ/5=,̝;^3bbccxرcykM7]:vǜ9 anmmذɓo᭷^`Æ(/$&-G_cϲM]m YYym|XVΟj/gʔq< ~̙3cǒ8po,k~΅ 9DD1l o6Cܹ=zQAAAfM:_A5 ̘ZuRm~Unګ EPv}U~rv2V̼:ޘQ !Lp3=PG3 EΡC ?hȧnJmMaısV PӢEg`0k>˵唟_寱p̘1s粙>}PuS/W#[6FYYeee}\BB)..Ͽפs?1}22p/h_xQUdg_dϞTUyR yշ3ggOC.1zt.W~̙s|g[ͣ>ŏ?ڇ#   ee%6in]/olyyZA$33={.1    4{!(AAh̦M[ٴi> AAAA4AAAAAp!     .DTAAAAE4<m>#͘ΦSc+r1wN:FƏj4v2>]{ S*1eeR kim |cO>ŋY1۶pEڱ~#nۂnvEj۳t߈Ⱦ}X}H      nOuQ!vy )xV+BJ>m?NGϤIۂMŧKw*vlnpXBK[/]zP}Gm |FΞ=W_m[n",,oGy<=m7WvQw'S    4ox\@n^0e`7VDHdZOAv}kMlKc2Mժ&a?Ӕ[D^^꧟ydu{2bۧ`o,Vrl32^`kvMٌ֪2$#kұT*L&cAAAARbE`0vJd {͊"8m ^^|֑(gj1MÿxUS,'ũ(Μ. 5]2nqT/ݻ /@f6c6߷}p8     4K.X`>"^}qX,~No- UGb_6MIxҖ4yC8fQ'Ej*>xˀ>:Ţ8Q]]A.GtϿQS{_uLP*Ю@.'yH4"TAAAjhѠi PS+1-,mb:0_a&i8VyҖǕKee:,,u;g&ֹASz qhP8j!gjɰX*Bj2#ȩSWAAAAh-6<_g ~CdZ QEAmhSٖ qZ-y Á\jCf ˖I&O}5P*swh} X{`:,cX|7?YTjC"K;>naw5&'    4M3M9xA mϾO@` kq!|ANu 𢣣j{}BJi8ڈC4iq_3QYeo[zuFakPja|l684jp8PL4muJX{]qvΜ9ǻ`fxAAAAZN2Mej asnck֢|,yϚ(5CJP7: ǏH CyӖǧi(++z_ɄN0Νa: IDATb9˖WȺ}[^7 `)ZujCq.y^֠PfM5|00ː**Bnk^ᠬ___AAAAYASEP0!3湽V;,;~]_)+߈"(C?Q{{d2 ]ϧc8[(XQd]@y(:Yg^o`8r2@q<u鄥_/d#ѮH_y~غtdiMC3O    D JC-۲fFSObL="m |juJFrovfe)'y~u8-jTZ>AAAAs͞TZB`K\yywWwEAAAOGMߕ1cFo7qLf>⣏Ӷmv}O/ڇ#   Ҭ=ȴi),,d2]ùjIg) AAAAǷڇ 4QXX}+QQWAAAAĬ߼M1<_AAAAE2Me2@ۣHf3T؊ut wݫQ񣚪4>OȔJLY'lZZ,3M.(BqX̘2(kl.e gP,~~D$&+(zo'o_A;ְvef6%yyT jhe 簯kK/^$jV:ƌ/+c^ϲ(x^$YmL.[sNiƚLL.-%l hE ϟgED'tbL&y"ODEQR]?~< `ٲe^{}ѺuzvޝŋK/R*ҥ <K,z)N8͛|]WyƍhƵ*(()SУGT*|7?~e˖† o޽{xԩ6b<ȷ~j^!˖-#//_=yyy^իYf bǢT*7n& b϶m VR:}V6)h[lfePѶ-E-R*)mB]9j5 qF[Tpc6UPP_C* ``Vh­VnfĒ<24> Ec` dU .UYYI~~~MIIO>/td̙3-unL&Wlj.Ygn_`4ڵ+{?-[ ˉbܸqj6mu]6mm[YYYK׮z/`0\c{ڵ+| mۖx4}7P(vmd&quSQQ9d2-ZRޣM6L:*OVXd |'T*۷o=zm۶ zRRjرٓ?Hn-ۜߛSNtRyvsm6-ZĮ]5Z4ul,i9dOߔfxr_?tQKǧKiocEĽ1=9] PaoY? k~ LJ8m{G)8gw !fbn/.&G⿑koc7nݻw{&훒R׹sg8N>}łYYY|+{RYYɉ5߷M &YN-JWzM޽Yr%=z۷{Ui2}zRI&1jԨْzAtt4/";vƌsU#FzKN߿ZK]~>ծ];~= ӜLqq1ouөbW=Ah|-` ` "(taHn900gER]_}zAW' ٟG:r9n1)/',) jT山dz+UQQ(FO_08?jgn$&{SRB  { 7PjEWXH; KN`e8;=g)q{l Rg͢x6ov:~yZ,&vHG()ٌҍ2Pf6[5h sngri)TWl9`.=aoEDл~z=2'??6pGQêjUy*Uǖ3@'b.Jee3U84!*ɥ dj4|J^ fֆIS'}l_ "aay_a`HaDX,Y\LLWT6rrmnQfT*~AlOq 6 d2nVJ@@$&&~z)))!..N jbbb܂b}aԨQDGGVp6m7tnm۶$$$0m4"##)))7$??`|C=mKV>}PQQ-[8po$44B6oA:h =Z/#XL&cʔ)1֭[駟ײeHOOGP0p@Fcz4 gϦo߾z6nw6ޓ~0gz`8jXpN/'|▹33tRbccWj2O@pSoxs[oɓ ###Z޳gOfΜIpp0N";;k{L*͌3h߾=v6nٳg=?*--m۶,sT/~)}SNvzܱcG&MDZK1n6zL&#%%>Lmɶ.֊+dk?/8e̙tFCaa!;vpܽ9={6۷w${WϴiӧׯgÆ L0^5^z石i&&O`6\.GѠڵ+D ϠA(++7dN'i¼mڴ]v<{1{9r$?#+Wd޽ҥKYt)ڵKy˖-Rtޝ oɓ'3g cbb.kݺ5eee_7x{2k,V׮])**m0 z :713g66l[4` .Mhd~DDDh[P>Ν;3j(}]ڴi#rrrر#Æ k4P裏ҪU+֮]˺u눋cR+WtR8s߅Ξx饗Xtecƌa.^ȪU(--ot9UngժU{9sƓfc߾}tԉ={6ZƓT[n۷OPTTݦGfn:, Q.x:uħ~G}D۶myʖl̜9;駟b OX[;G%&&3ח83{/C%!!UVaYx1̤]v.1Gu]ǝw֮ oyuGM׫/cZZZs r1? lcmބL[Y aŹ"\Hjo6ó .$e&J%a'O0DFŔQֱ[榺~Nuh({hJ%rqD js9υJ%j35T__U*.z\A3m]Tx:'Gu: Zs9hݮ:2x;"c_N||x1;~|}͘`: #LƃYR~Ti{@It6m8W Z%#Ɠ~&b6mP[OH/1rUQQ%KҐPyΟ?Vdd2ջri[eee|8ܹ3#GxXZ[oe׮]Rɓ'⦛n?ƿ6UVV?UΟ?Ϯ]o[zZ-C/2 - }3ӗ~*&&@ֽ̑{wu,HUUtdlzVZR~?uo\=Wdd$Jmn6Ϟ=ˆ P(nݚ[oYfIvdРAl߾"##*aݺun\.Pؐ"`[yy9ҥ VB:͝۶!ڵϔƍ1{BT2zh @hh(J\6Լ4o9&&sJJ ǏM6n3 K.|7Wl:'g`0xVSUW[F;?F#/2;w&>>^z8K.ddd vΣ(rZLp{?k׎tϴT(\UUEEE&Mbǎ{p)&L/O<ߙ={4'`^jVJKKd_׻u^#"\9cbb܎m\ZJKKIMMa9~8}}ɓҴ]tbM'c4pm۶4u^_.uM]f%G \I-4)ZkiO?UmPEoVe=ENIt.} A_ ƌ4Z= R-A8\b6v 8mL~~9J$k)у=OJ># q9fU[mPɓ~y߂]&H&2Ar}e%v;]FJ ]^7 >jY]d:7iede6&j|R||x/, ?]7[ B?~v;*un&j٤fc.9ޛ~]qԩǿvNYWuσ'j&{~Gt:C aĉ 2GRR۷wCS.׿Bƍ)((n68T9zVL^YӀ4Brts`ںYsv]:foUw>C!qd2-Iɵ`uȴ4 wu;vqA)h:h Ο?/e:w\q=եj͏Q*T*)[r\YYyԩS2d7n$++ pfkÜΏ5W;v5={\``uu5!!!zinΝ;K4{pHnӧOsi֯_ԩS3f ۷o???Xr[rQ~+iX)Z IDATk\//̷v_I&1|4 ->|8~)zD>Lhh(iW?jLy!a8l6le%D>O6>dnc+/Cz_QXZ 0.3D[~J;u"_? {"}+4u]R{ç 2x/C\]Py32AwyEEL)-]ipz=ZrqwM< d6]93q_n`xs'bXfr7Jʓ~U*iߧ -9<v b„ L6$RRR֭;vDTe2FFFJ1f5iN7m՝׫GgfŊnҝ}())R6?N'Zv Xx/gFkֳJB׺d2[2M7Dtt4ojv^_SVwfd+87/VÛ]?.7dl߾m+ʺ^$wqG6[Baa!qqqnۜs6%kJطocƌ!<_[b'A~P"dBdh{x2ŅJeh8)5aB!e9)P`{?Vm:O]#999?9sPo'IZZJ~mo(utS?w5sz;ݙV?FʒK_va9Oz2:Ν#**[nXzzڵk֭ÇWZf?Tjj*:N:IzM^^^ 剆m)uO&::zu7Fe)i .бcG9uծv;III8q'2G7\l:uVxG8rHrf8C 5șmz툋CPxI[N΀bC& vd.\`{;Z$%%O||Qc++A0?"- !xLOZRׂÁ 47/cӇӦ9~>%%TA^'/7h'M]v~?p((Fk{/oU+ΌGA|RTl`Tק!!cV^^ѣٰa̞=ސcO ɤisEEWm1|)`9==^'??^d"!!3fș3gjt_|@BB,Y;v`2Kish08p&L`04i[v'2 ό7saX:ujmӫW/r9= . P8p3gܤ 4Nʰa8~8#F ((Q̛7PK.;nesssӧc0cw˶Ɯܹ|;cǎѽ{w⼞;رc$''s]wm6󉎎gϞ<@M69q3g6HOOcذaRQQ!ML\.o4Rp;￟۷7̾} T9s;w2}tݲJ%C ٳ$$$xvr]>>R ]R#+**<Ξѣn[n̙39|0EEEDFFҿo`{&::uֹMG≄f͚EQQGAQTTDLLYg'l£>ŋ9x ɜ={3g0{lZ-6)SP˵TYYI^^7|3fƹssƈ#8u:ѣGkkɒ%$&&V[n;x{=ʂ X,lܸѭtYx1~-ӫW/:ѣ CS82d:Ԣł M$OYASEPŐ^] SV۫ Q:ൕc>g_8LF9ƟL Bsu).&_?hKNFV%?'ޫVyTEE48~^~I#<)v(2N;nZl;0R޾=EuQ=!Λ7RI{mX|})6;ŰaUUp5XHRZrrƗ`QP?6p1=ø2ΫO|TZ-}jmd/* `Ϋ|2sAfmXxEPzn:"2-齰0H^r`I۶0#"XV-מmt^mՊ-*&qV]Rʈ(*baa!*_sGQܦx+";y!;br֭[Om{oϜ9u]ȑ# 7x b!33.][brJnv^}UJKKٺu+cǎڇ.^O?… k0"88 RXfm0 5cb08{l9__|EL¤IPT\x \[ӒpÆ pSUUņ X`['Z~=g'd2m۶ڵkǢE픖Ė-[.N8̙3=b˖-L`ܹnfb֬Y,\ɓ'y7nC uI/O9V\>|8 _nsgd2~~39B6m:={H yS&11ٳgh0R簢^{3grb4駟I/ݻwa!!!@Cp8X9s`z=7ofBۖ'<7t/W_ISKz^xLٳQTpz#GХKf̘?S\\̪U>}:-"99ݻwӻ1UUUTUU1~x(//~oWLX,pڳg61cpSZZJbb"[neĉ8qZg'Μ9+ĉ`ZZ[nʕ?;7a{ :jZV:V-ZuW}VVkjp@DQ@3#&r8ڞ^^^4nܘׯk=ݻMM׬Y,ޞ}baa'Oظq߿k($''e˖y!Æ {M5A' [툭-GXK_w}n:֭[!BfA7 GtA A m;* ž7]AAAyIAAAAA;4ɰnA(=i%ġKlILV̥ysENVf8|֋RPz )ߢ-CKqԻH U+cժE<0dH?o ZǟnCbii3:uKK mz˜9_שT>ؿKfe/MMse|A޾/ c/1!|(l}4ii_iUGe7؁/ ۳PeE|*UA22Çw0ac||ɽ~ŭnZm:̙s/dדmkRzBzҲe3}sLf˖/=^Juvqqbƌqn/E&:irB g&B^4aS=Aq`'Ia^:} 3VϊyJ :{qC']0Uwp ~~UzZQ/Y,_>kk+f[o B&1u\tGѷii9 5{Gӳ9)7b őW4u".xiRp&Ŝ` n&FI\7𨏏@~m#FLر,% MjjwztڑƍAlws<TUD] _MWa,ըQ& ++㟠fj4i֭广YwvVESP{ ? //oV* ԫWի0gVXu|5{}ܻwSry F&mT*.\ֻg>JFFfTX/ݻ{1dH ӧ/@.|| /74mڨu.:ڰvRիʕ0g#5QW͊4J(mYϞg0fkv*٩)^sUfJҮ]@ne"u>1Opz<5o^koĴ\ŢT_ڵWWg(B]ذaΏoGG8p:}<-[6\])YҕW KERP^…y=(0%P GQ,E:F<b)C(0wQeqXPտEՉǶm ĥKW Fڵ9s]>ݍ#Grpww#))U<}~8+-mֳg:H\MW*HBB" 'ڽ:ɈP*}Р^t;veǎo4Kn>ϕ95kVc_\W)**O>iF&=8t(kn۷?bոqC9w"M:9t:O{\ﳓʱc/UA(XRtO:K7NKUWd22#0XE'hjd;d"Z BAll;.$$${+=qС-{w|)8z˖!;[%9MR%5+}?-ZAJJ6U+3zjժNvv6oG?q i5jڀ)St)5kV#44-h ~Z'Oa̘166ӳ9NU:GVj$'~,_Luj۶u:u~X=ԔÇwf׮O˖1b%Jp%%FsvχRǚxꥱ!\O h,ASK8PXNR bK I8DSdȸ StXk%I„D`C&e%YjYIܤņM{3:DcC&p`$QxS_0H}ЖLfpKSK>/k2,.yḐ |ejE%2hEt޻Kg"(I*X38K ~KAxD&L-O)S$:)CHꌏ@6m)7nb\rMF&ܹiii]:Oݻ3Jh/'\Vnʕa͚bW mzW[3s\{[X_=`=,Z/.rwwdgVTZV;;[yYư7]0~pj+9|X*ɮۗ3U”)s=Z0p+WBB˜={1 YjYϻ"س˗iKH_F$$$}n~P e!(ٳ çOrFHu U̙D/q {h.˨Q_WcRf IDATsn UP{+%%ՠhA#EUbyjԨًS˲eѢя:A2333 O۶r [mO5…`,Y :ӳ9sLf̅yދbccѣ;Yp9ͩ_yʔt}}uJ/}}A۶BϹspwѣ'ؘKMOʖ-R$0KcݩlY)ի3vv6ܼy/MF|Kĉ3{|nw.,]ZPBY\\8sv۳gܼyƍ4mڴ=a,X0K/Թ};ر|LlmmPubiR odiJ|5CUJ݆J ڵ@JP ϓeeggof詬X>xu{ՙiX7ի466֬Y 1{bnL0RL&cɒY(LӦ- $%J\DGuٲ۹sfu'//oRSs*{7 ww7Zli|Xb>*ɓ/й'|0m?xz69I8~SL)ϟJx&LMll{w͇L&.ěE| *ueuFcSLKbI\CCqqܺVF5v>9sίܪU 5k̴i4.\K?~KϿKO6m %K RShѢ޾eK#я(SKK&8> V!Rꌃ=׮ݒgHHFFF4lX/4]gA*ZO|О{d܋3;Ln\nˊS0q)AY`dm,W0V?)j B~ߏfD݆ Cc_1h7WGQTt{{[ }bY,?\OqttdIWf^wfRXYYmnii<|X?&&NX}:thK:53f:;סC[7n& :peccM߾=Yl-))пoVLxٜ!)קK$&&1y\8G:5ɳyqtt-?|؟ɓ eff;`$`JO_~^AA8kF,A8lHſ;wcz*V,Odvv6x{#=]~??*8Bm 4kRRtNIIB.]ի7͛4i(߹ KJO{Z$?~J5:r4k N.<]84nݸ*#CMGؼ߆ѨRUVVMZ&î}'N#;M//jӦ%zulXZZ"066F&R^ V:;D3Q\|Ug UV(1dH_՛KJ";q4>>_pN*{GhATRk\Nt#ZjNxxr-ϛH>UV: ڴy_$$$/*%%r`C[x8 ָ -cD M]4ՉǛT![2=H E0- e]&gDS,x%>,>1d 1RQCș^!:kR9jJIh8MY?kH#?Z˒1纆1|5ߡ{w/1p`f!551cի LfoOppޏąeSRRShWDY:(R233cŊy:ӷ?YYY':)ȳgDDݽǎݍ'OqnpCSS>mzR+&&&gySD)߰oa^󶝗Բ>|0x2^^0;Ǽr4?o Aޘ.RPp;F* }p!>j}-;^QMa()v>ã>7,Ԑ;x!?y dddL͒­[T^>}nz]}Pby,͛sxƭ[9SDMS|Y{lIKKvݫ0U:{SZmѼNNzA3gki׮KL< uZuѓa1{|ݺyajjR eIsa~) z'7)Y-hjylWp0)QR$џIgNz~԰z>ɢv}Hx'2LPeHk !ZlƜ9Y~3$99faԨ(Jm/FM#&/RJ%l常{*JFС1c2m|m )iRfvdC(:tOK_'>>QO/O] akk_Aގ7u{*+K͛ݟ끏!bb ec I,Mgȼ2K2MP+, A83a,4FRV U1t[20%=2FœjzڑAf~s}5f?1`|!畖+ i'=~qV'g9*b_4M,gjQQXh#bpvv"&&NRls^钓St^gggkZjV]rNHNllN@jY\]]< ,Yċ?_ HT:C*| DF!n: OڰlZ]Aff&}~wgn&Mweydgc޼p}KjY =4=$M,111f`osv͏mg7R<,ҞNrpp^}Gx 3323 .'NGصk[_)Y )!EQiv>NNijj>0551<~>/ sLMM$=Y*GuyL=݇mii9YgpœOAΟF 둔Δ&%KrBΟbڴaooǚ5K쭫Yuw>׮ݠ}:5k^\8jƍtv?ՅRY^8zG҂2zP\t^R˲}a{LNN!++k7wRoG^x\[(=!7M4@YK'v O?֭شi'֙ursiA!EQ]VEZ3>>XO-55̗8s<'fʔo8q$AA!ڶ|Z cdnxO͛xx')):=ߛ7Ls=kAtaGl͋y5kJ*3P#255 ss3߭IF4RǏO$kkk VOA(BP.%q9~$_8B z]**Y1OQ*b1^c'Rîuu)HuN+O^ִ;:oܸMrr }].kTR\tj*(yQT*9u*s/ͅ )[vqPMJJ6`KX-V}0ƍ9 䐐0>UhٲN/Sv}4ðrUA z mlCE.tq܈.09YzS WVūI):W$> jzVv'HGDaO[.6Dp,zbM&Ui枏sȦbL)RK3=}DOu _(޽:??7o*&RHue6tYȳJ~n'%%2eJr~I-+==t8™3:u 11deei_+gHNN]ۥKWP*SS "oIbrxyr C#%%mv#^p^FjYJ>hЍz=sTRQs t<~T 5v͛sj7 kݻ''$'pv sFF&f-q|z1}XMuֈOO y/zw CnɆ?|ixy}}S\ij $FF#66NgggGU Saz?2xh+WѴiѮa?Zj;0ώ\.D W{o}ӧjy"4g0)d>|ESY$g7 1'%$0 "cGg$PxgfSPT]b/Y*=sԫWK'Mzz:kndT lS KFF4۷Sl߾D*T(G۶78*UC#:˗MO6nܦ4/?glܸҥKg~|+*ӯ_O.ΦM)]$}`C:-T*?E>]qrrqݠݻ }t㯿вe3<<4iff^#8|Ll4dzؕ5M ԍdL -}NSHl" Nd4gz9o i})M):RV +"`]J#ScD2naO2Bp[p&wyex8 걀,)Y!(/򪚞⺆q! k:jD:& #6S!\ÑtpLK Ƭ&Ip7'9Q/3fAFFqqv$JRRvKNj"~bbb_YYJ֮()4̨֯QCprr$$$ kk+6Grr +W/tY[fذ,Y,|}82GӶm+9xFLZv>4CUqO&&&$bcszz>;:@ Eѣ~{}&Lҥuzzq MC5ADM@cv4hPSQ(iăeKKJܹSذʗ/CǎLBjۑnwӯ\ipVN|T@=ԩ2i(.^LݺÖ|}΢BRRI;ww|DvOӦ(_,9m)W\C.Sz|/zЮ\NFFHm֭9r0gΜnbb'!4:ֽRs*J>NZZvs)!EqE+Xb+V篿beeСc+)Μes׭DE=ՙ{>_Yf1?0]RlG9{^I,XEfp OsUߏf lܸRI]xA MLqvVGncc\P'2*G޽:=Bde)ٲeNYi9|؟ƍе맒9/|8wihӦ%aa7-))3iR;ERi||n߾C^IJJ֛e6mLZZ:uPgcN0j\]#䬬,iݺ^)Ru6ӲesVMv:wKtP<RN > Nj[m4[Zadn;v﫳/a2 p ;9ȼWNMŇ;DRInBA(.۷T|ص+gK) )$w,F’%8s;vsT5koȑ4jԀows`X$U~2eh^+ܽ*Rۣ͙!Cѽgܽ{nSCދ8 ː!4iYYYjgW_п/y,K:#BC6l_}՗3Ƒ ܾo9|؟ Fh21x7rKk R lڤ@dn_~̪U=Ǐd*wE.Ѷm7^̗/ރ7™3g y3qHng%,[6WirҦMKm33S:w-ׯb -(פI#.^ [ Qдic+%J0oT<:t͓'Hs\G%Č{y.ټ;wA'fGYtk ^dd0vvv~޵kaaa\"]v/aNSAA )|O7K7]-Ab%͛KUcڵk0!v>eS, eϞA ?[mʖ-{MSbyشik 3ix  c/ i`qJ-\&OĔ)`ccMTT4s/9$JOOgƌEXXc- gҤ9o oڊ3t{4oН "h* oN*B ~3f,,<ϝXy R\ 4я$/R6:} 26lxe "AA7#*3g.z}=Љ Hadѓ7\!AAAAI&긕+ 9^Ludgd~ {Q&W2-[A<<5$23Ǿ}'kEflLzmIV\6WWGhO)}ld2lBeh nߎd y_{"#?"T0zPz@jj-[v|-微ktwnE҂iиqC|Qb    )h*32¶ͧ\")ؼӾCxz1,buqrKK?NZ 6O`^ {NIƶu{ɏ P)t$ оNK-)4٘<Fgf(_ldYMx0wMO*?o?vҮ]k}]_dlmIإر-Z4eܾY<AAAAG*Tje={SUAͫdzqVR*Iv5ng<רsR r*V4Iv)WL.c5y^3N $.NoY/RoDOx微kB֭)I@b    /hJ'` >FfAzx3ddܿϓGd''aV2## E#;tetI޽##;Hzh*œVZp<ܹ@_}0a-7e˖8С-7D@~QB.iתUkC111~%eDSvW{뤫Z2+W./ɚ5KSAp۶u9Xg){cRJ`4㻙>}Nr OH>OogAAAAGGMJsH &32@IRfaळ͢v}JO]Dpb/$8:ơO4?UNHSuœ+WMKݝI+WꫳWL6PƏŚ5Q^-Mptt`ŊT*&O/AΟÊ"XdJe6'~Ǵi  FI<=lk333? 5k,Br̞y[AX+WQFUll|*y   -"ړq/{y&tsGIڍmYqު&.%|#kmQdͼqĦŇ8̓ u="Oz02B<Әi=;6s/_jf}׬Yd=z~/gڍDD{ogffƠA^.\ݾam@C3nL n66ۓe֒ZleGGJteŜ?S ~~ߛU+yz6'88D;eBne=xvv6{ :T*1bCcƌqXXʴi?<=vF6NΖ*U*ͩ*qqq/#C`˖T\Wo~CAAAi%hjV*vGeAvZ*az=;:R*Q*b)1bA//\&Cf\ ›u, ''{ᤤdJ.MMJJ.ֲrTfzn޼7LȈ&M<2e4_%SΓ\gPM?+KU\z'Orz'p+~;6f.1zTԩcclU|AAAA(XıN|L e2,깓vJTdy}mV2Fvew0s@&-I|A0k)|Njh0NXۂ'Hv(!J7YjlK-LQ\˼rrիrMg%ޞXYYPT @ݻš4Yv##GFR/dggK׮2dX222ؿ0dlڴҥKҷo=TqRd/㤥iKu x{wcժ_ps7)R =__?Q|ڴdm_CJӧ+NN?4ݾ}:}̏?.`'RB9ڶdܸ6YIܹsWK\)AAAA^HASF_ٗx0Gs.p ;9R@`U7odzꌗW{=z,ܛ11L`̝;v:k >x)s,eȐ~twާ[z32zPvٳشi=^PP(4GGbbbٺ/6lRkxIuHxxQQS '0p7 dpCN8CR~OSdg}{   Pdqqq?r:u޾ƯnȰ jݻFuP lmm8xp+_="5aj֬F~Ëv   \ʖBŮ] L_9MA(~wʒfŠKO V)ÇOX%RnQuۜ9h׬!*6 ,{%Zs"#Xfc}M8cmmŞ=p7kk ٳ)Sfs܅.K!ok}KMutu)CR/a0`$,FI棇nۨv6]1YLaoV}2+ }[ C߉Bê5Q(PJT,cc#VX@|c‡Jl1b|f:::̜)g ɓ'3ݽ ) u+ONNE WUдE ĵصk?033ã- OsTK122bذ~?ă&&4hPWjAd<=+ <^4ot>+mii^Κ5,EMh۶ӦC&V-3}cX&O^$;5jǙ6vCœPf4pH>{*'G(7f2F5멎/ժ=U 5̩9!mV+WQ&Ʀ,{weƟT-] 33S?DX|y<D:trP|9.^̟N_~_^|'hܕ WT*i܍@7nX,~7nĉ9r;࣏}j^ySeeespL>{6J*p ?Ç9#RS`bbs+)̙.]J*NgzD@@ qqwSzU""!?._nxR0;WU4p$-Or.::G|σ{d''aXðz-ۮ _cnnƘ1qvv\$$$r| ;wU"..ÇY|-J5wur>/ZR5zx{vdggŲer4n܈軪)@@@ ]~HZ]E 7-\o~wwT~)&LMZyTv GQ=)QVXGFFNpvfٲc 8x.]Ν5nʕ>}}$;;#GNХKGʗ/ǽ{mF>4_~9 fF{@Xl>]"33󥮅m9=zfM{v[144w|zؖ-a f- %%-jOժo?gMdob~i߾1vPl9pg5ُRٴfZzrO^ʕ{V':. Q̙6.уݻ+Wo$*=ztb/sw#..Z ֪eܽGѴ CZZGРzb:UT;kJ;j֬ΐ!^TV/8>?رSt؆uk1agjAӎ`ddĤI_ӕ܌z|:RRR9zVބ &[oUPV|9wD6,+ET <~@bbR׭[?<k,LMFF˗nݧիEzY}űz"qYΞ=OBB"-Zm.5s!33'^_MˤI_@pp({nGzzzcjjJnRuϾoܸ/lre;n0_ѤprVܾ}C#?ukҭۇ顧С}ݭxdիWw̟Nn`&66>kȰ|򲵵Ѫ󉌌R{JTP5k6ի4V]⢧G׮}n6m Y߿g:uj7l < k۳gTPڵkp?FFTaa5:Ri=V={r^C _Hm+x&uy= $$ 'F[uhi>u*]ݧ={̛5 ^ ={vV GYťV( /_z>regg> B*MWq'ajjJMW^.v2T>"33B!- gikݺM\ܻۿ/ZIJJ?$'Q`@]ӧ<+W"q=:a``Ph;!믿xH}!ykkk\\\^*b Z=<\4 yr"O2vN8ODD\W}߰aV .srwޣe&DD\GPЬڢTOU4z-00X><ߋ~KIIA_"#om?g8.x 044ܹ jAZzI>},ODtswo©Sgx$'ؐt)\v5GԪeU>=P ;iu :Gyb`o[155!(ŋ]; L&َ{ҦMvvҥ]%ӧXt>DW^#9絳Z(&&ҥNY؀LQ(]V ;?00{YЌٻ /^ֺG'MF;U?z4 B;6Ms 뇑Y`2n|E ZMjgMSs]CţGuem]Z-O۶ptlYQ(XYY䛆 *6m^B!xyo|Zti_:b 4r͝kɌ{T`} 藳%|lǺlN nF/;-QOR*ze4xhƼyٰa+Crr nnxy@P9pgiժUĉ}j e沲7nF 'allDHHf-P$MRR j嘘GE?U9=Wqj_GOބu6ѨQ}J2Ze|eȇ["=#3?@/X1c3r@,{s.74QTO;044̔ Jdkqq||҂'O9c寧96b۶]>ORR wU_曅c֬aiiڵK Fq{<{])M ꅮ. eZu='^VrrJs$feeiz:vTހSzzO1 RūUs3!!}wQVqδiӧ+V|IvK }._{CqwowULMMc~ 4o˖-wT/] AI{*Rv=C x/WOu?Eعs_[闓O!Booo,əkkηXUcawBZBә4p$I*iˌy +]eVY،FjXΐϬi::P F>}5k69a!gᇂ{iN+/v7zj$DW7gތ3D@&iܹKʰ :WyEo rѣ'ӧ*e&\xE8h>qq111Qgjj/}QJbx.b+>1}ͼy_SF5&Mٳо}|is!?+))}}=TIMծE``0OѬ II(j >~G51_=5w˷(O&MEGGf5k jkOar{y{J^e擕Uy##Cԇ\44,LairRն*eNrrξ+SNMʖfh hvֽ5z/6D)4Ȗ\`3IYwo\~aLLر ޣ ZOSGs,Yw&=t233Yn\=^WQi痥{133+:a1o`۶l޼ӽ)ins B!ī88(^6)s0I'|&"*::wIx(>)d>zHV|,F5g]Kd?I%] XQ(зDF OfllwWDB۶Զ+ UMbo_wu[.ĉӔ+WpҜ9sJ*Vhęd._VThҥkT^KK 6'FdddyC…K<|-ҢE^Y\vKRjZܢ̞رS ]uz5_~CҖ*Lm6q랞qwo{Ω} 0O|"gq] cc|= UsJ))Thܟ2!%%EU<U  /pQDU@0}))89=LL[ILL;щs.CjժZNsy3 /wԩqhSWѪUsy{<_|ƍqvvIYr{󉍍Rm[jU -7%%ws=Hm9r{ͩ^Avv6_Z]eϫ0ڴsrrr=USn Cƒ)(/SD3={1NNԩ}yVPDž.hddgMTi,Bں=Mu )w8)d}: U2c*oWE’ 3i䌮Y.K RΟQ- pν)K4pD׼f_ӧ>?jU֭;tґkIKKcݺM|pʜE&,,Jѭۇ9tsZy8~6mܙ4 bcRmGqQ޽]EZvWYI?8̐!}X`&6mB[:ujφ [ ]Ew{pglK 0qJ?통uiԃg}^^#Xh%+ңG'^XXbذj(S&M ==]UߤdU@xO)VG˄]bʔO033%33coѣɤlZ}]ԩ3,_>~w(W M˟v-1vP:thprzn>|m>aÆxyں4.\̔FꓜPzzoߥ[7ȓ'Ox TyWW'>x GVg„L<αh|CO BTbjjQ 'fĈ  ++KbbZ\ʕ[°a:\`Μ%,_>HLLG ؋D.uΟ5~:[>SZSƜ=oxyIm5o݈M,!3f?c&~ӧ+\ɸqTȹW۷/mQ[DDw+B7N\\\:tΝ;cizzϭ$B!E޽ݻ{ЭIw;w*T[0=7ny(P!o aΝZs%fٹs'~BPB!BvAzɓA.zuf_ N=SB!+4#^=MB!B!⟦z~J !B!B!?ASm WΝ_wB^B!B!(hjeeEݺuf,<8quWCa6m*ŋqtt|UB!B!D1ҸnICN r_ iW!B!I㠩B@,r()]oɐכB!B$B!B!" !B!B!D,::: B!B!B#IOS!B!B!ȣăJ^zaccիW=ŋ\p=zƍ㫯z:u+V`С1tPnJRRR'O-deec233K!B!B;x422#Gpĉ.)J_w5^ףKLLp6m?ٳgӷoRO>aҥ%VUVL .P\9233ٱcv"::&MеkW7nu;m6~wh۶-L&44 6̻ヒ YYYL<Z^;5k'OO:t;>666\vp\\\JN:::,_eff xzzҦM6mJfh׮]EZO?T-`Z^߭[ʕ+9#Gn:<<>CP(ԩu۬Y3^z!B!B3jբZj?~<_cnn̙3ڵ+ÇWKJJ"&&>9s`iiI߾}ya|j׮MfWmz*OEҥKZh+h۶-ח+W2d/^JҺ J%_|GeĈ;}1qDtaaaxxxP~}+W΢E.E) z;Tue̙3rqU ܨZ*b̘18::ҪU+VZEIIDATjj*>DWW#G0zh5jĹs㣏>bƍt'''|||HNNVӯ_?.\ʊ;wҷo_֭W9h:t(g̞=>Lpuuˋ+Wegg}vt邛>>>$&&0gΝʕ+O?-]=\zӰaC<==Y`Zˋ4j4UVagg̙3I&|||믿2dE> RSS8p qqqL6#F`ddDٲeUSLlܸ???*V@!B!-%t[[[v?̐!CDl֬C ŅӧO@ͩR SNUuuue͚5\|e˪P(ݻ7K.eСs1  TT [P\\\hժ:::E+ŋٲe +WjժƐ!Chܸ1֭K\|{Q͛7gDFFbooOHHNNNԯ_>}pa:t裏ҥKԩSN:q].]Jff&|*֭[rJZjE*U aǎTRO?f̘޵ lٲ3tP[Fɮ]#$$͛W_ѿO1cҥKy FFFY兾>}SNeŊ 0;wĔ)ST,5iIϞ=Yz5$../ 45j`llLŊ]Z͛7gɒ%jϊ= ߏB`֬Y\^~5j ""BB!B![J,hĦMݻ7gϞrBN =ƆXߗ.]? $$[nv&,,?3g '`S|yuFΝqwwWSѣ]v j"""wyGT=7nܠQF:tHW#e PR%ɓtBϠA;w.iiiO?^uWFF[nU ߿> _3fPbbbXp!&MRd5qD}],--qtt$::ʕ+b OΘ1c{ŋ/eʔr㔔&MD@@ 6{{{f̘666R^=ڥ0wիt`ii {,}}??, UEy!B!⿥Ći߾=۷g֬YEN7`Jnݰgʔ)l޼ǖ)S~GXXM ؽ{79?K.̛7y\]]9~8z"88'''ݫm3^` J%iiijÙV????J%+nnñ %$$~뫎MMMחÇ3sL>|`eegYL._Lvvv Y>;;訖]X[RJu888. Kv). 8N:1{lN:jmellL֭9ts*.̋ ~i6!B!BXOӽ{ҧO,Y}O1BAAA4hЀ;9-cǎ;$jNNN899H=1bDѺukZn ?\2Ν֭[p=Ν;GݝSN-:7-[{ 6k׮zUZ[[ӯ_??Γ'OӧƌCL28pUVi]鳳yIϜ9|Bșn"whw˴!ׯҥK9rSLl٢ ZƎ'-[SN^mM萑Qd5jdffm[g)yX!B!M%4}[lQ-W˖-Yp!.\ѱȼ8r?3+Wȑ#j͉ ,,5kfff\'NA:u044ϏFaiioA7bnnNʕ 00B_GGG>C3f zzzlذaÆV6lXZZLdd$5)NNNN$$$0g틹j_`ڵqqq=UקM6x"砠 |||bŊTR7n!--MwquԡN:t҅J*q9Zl9iŅs2rHN>>l۶Zj`jjʂ 6l4jԈ7oàA֭[lj'011aÆZ XYY͏?VVV9sccc*W\| VaÆ~lْL51~x-Z (4hZԳΎu1j(~m>166fڵԩSz!B!E'..ȮxgϞw!++Ueff2|p:wlvX]]]=J׮]_l___zkڵk-6lP~eϟO`` M4YfÇԩS~~~jՊxBP?$=====K 4iRܼy*UZϏo .жm[.^H&M1cFsUٸqczYyfN:EZԩ %%E닛Wڵk v~?#>>> 4ݻ?pnܸ cƌMk믿ҲeDWWB!B!?ǿ2hz:t V~Si&W%'Od޽j=e&ټ{5Jޏ&4-94B!B!PK~SY<%% ZjŦM^ܠd)))[5kbee?)))muWM!B!B+뮂V,,,uW---dΝKFFڵcϞ=B!B!BhB鋬B.^ubڴiL6uW_/JB!BQ RB ;;$^BPqttt^směD^/fff"B!Bbq4::J*P($ R*(J_wUH^+MB!B!ĿFAӊ+IJJ*:dff֯* bmm~YbŊ*B!B!(f:qqq2)߿Q*8::̙3( V"B!B!AHx뮊xMB!B!$h*B!B!y(^wB!B!M"AS!B!B!CB!B!BMB!B!" !B!B!D4B!B!B<$h*B!B!yHT!B!B!򐠩B!B!B?$[m0IENDB`cppcheck-2.7/gui/help/images/severities-error.png000066400000000000000000000014511417746362400221610ustar00rootroot00000000000000PNG  IHDRĴl;IDATxՓ&gmԶ vA j6ڶ;~U'ya yhVG;}@xx(PsJNs$"+$v` ``;4О"<Da %Z 9&ggbK?r!Zm*C `aoX,~Aj5;B|RB?8dS_BlZ |4_{Vcf&4hO5"v =b67c8bYe(gb p v~T $܇ҭhiJ4K} 7 W/uPy:l=g ;fM7ۯ$] C_2k{j3SS{'\~< hz[3Ccttu9.zR&K"X1q5PFzԵmP̭P[O&u0}3ФrP 3>jmYPzD?ƞUy{!`@Q z͟+D|IENDB`cppcheck-2.7/gui/help/images/severities-information.png000066400000000000000000000017561417746362400233650ustar00rootroot00000000000000PNG  IHDRĴl;IDATxbS YSr+v&?h$ǥieH+ ñm;).Ķg۶m^aVcwT}W׾@ie@Bj ۞3pCuGɋeu(l XoyZy1&:U L,"&mVx`rTr9;Zcf#}ktU4|Xo¼r:W)rͦP0Eo04̽H#vȨ:U2m%3/PVC[Emo+5]ve^,8cMz FmnEG<]kuJXѡ*-%rs!;h)mtŒd|!vk4.գV^FzVaowrd[#>Ǜ:OqYY_o]v|,` D8NdmTe$n+5/i2-Ż;M8Q O{\L(iEQ9Y,UNzR62}֊6,fS۪[In4jcɌSXg(KNX6oy<πq[@oV񕡶d 4{ [8Gp8X}muY}O 5#lrnjq+ ;-F%n8 sz&aƕiJ޽w]dy>wXMj3:L60A0kr#kL|_KtyJ}~pYo~V"1i<((\w`*.1vz4md TTlT.z5ȃ'ӏ0xH_o+|?f.fF1%cU+>|f.f|]\w6"!#  w#7u$b՚,qIENDB`cppcheck-2.7/gui/help/images/severities-performance.png000066400000000000000000000015171417746362400233340ustar00rootroot00000000000000PNG  IHDRĴl;IDATxYU[\۶vm۶m#m'w::o|6zE=~Ŏ]̶[q:Zn?,μ=v(8a/E"W<әrR+df/-P!*1*p{@i՟涆`<HĦ@x]cr!ʚ*PXc<"qvT7B=w:t^z&REa:{"2oG`(a GB~: yfb*yXlBOo;m8/1JCV7Lnx{#kWT(*Yj[{@W;mCIg#x:*,1IpGt#`YMQ\V-ĵ3ظj>+˖I<kswV q"{)m?Gg|v ݹ rGE{{yZBuֈؙ PRNե a|.$D[){C&դKYxIޟ70L/| "YsVB@W- ·lxVI9?[m:~6!;gbhΥZ(|8/zZ'Dm,{Nݰ]hikD]cHhGw]hߘ=5 ^n @Md&@@ ܐ/6Wb&p`LH{FUIENDB`cppcheck-2.7/gui/help/images/severities-portability.png000066400000000000000000000024101417746362400233660ustar00rootroot00000000000000PNG  IHDRĴl;IDATxtkYF3mۓٶ}mV"f5䛿nGw}aoزEыO ֿwly8CZ9-g7GVS)W\l?.eĝ/Cw `G 'EGfFJt-;)A"u n8N~ T6o\|3c6Bs[=gذ?/zg ̘)wr/Ŋ!_|$* S)B.w G{XOpP58~YQKC0hz~[l2' ,}1Vldzȵb:~Ҭc򀧸F[5RsYq{Ja>ҥ.diq/qb6" F"@`1#z:j!7B,D"; ָYŦ~fy̷Cc,đv16M7Ҧ5@$#u11!~v1s5梍vCW`E*ӈlm ˑ&rbRN\4[Xt?͘IC2+}KrfGJi'E= @l gbM87`xhGǻ~b(KjwFo>(+ե}hdB\Mhn -ǠSU G_cgd߳#A^E|(uFrϯ.<ʬRˠQC]B}GbEtYv8'[=B 3=~Ze9uHT͐G3I:x-hkRX[೴@(ǠN CHy5دf&omy PȽ&L՚\w~#yYn Mͩ\BSR|sYl ύ u]k[^!sw(Ɗ2ʛ̃BwQhmeXy@ǭ?G^]tN3::|J?MN\%xYv\LDb2+q1!ўOL Fo_Mpu7EcK[bx-F <0q'qs zFxΩ=Kޗ6HJt?NOA)/"mJIENDB`cppcheck-2.7/gui/help/images/severities-style.png000066400000000000000000000022771417746362400221770ustar00rootroot00000000000000PNG  IHDRĴl;IDATxud2l۶mҳͳmXmc1EKRsma v_tXC ߶pl#)8`kzz6oqn#ӆ~V/uo.L=Q~54_ο|ZE947Ӫl޺GŨ&Or|=}/܇q=E:=Iؗ t<ƞQm8^ < 7m?^ @k@`XƋttE#kFH 28x/ȥ`6X61ZcAJk89 )G[Rd;NgmTtoFbt&hz^y]L]B$"SCc=%oZ5Pуōju8{+hg$LM  u ĉѪ51B a-kVԼ8|ȋ*$ kҦPlSfgOj8xgп8,W 蕎@e.KЪ k5YK18Z%.)k ,FmT6:*u ju ܬ22qFY` Co*a9zNи!!$eۗGe3Hb&Ck"V w1լ |ҽbH=SЊLB{CF*@bP۠ L|1`L$SI8:6+ܿ^ĸseK' (V-X2벃V C\abhԶE cFO}͕zoDu`B2 zcfuމpǟab[ /so|k 벎e'xX6PǁwSN9?7J@0~޹ӧgͼ'HGW [7]6>kQTk~eˎ{^7ŋq\U aj<~;6(ռD"B%yO cs?e͢EO?KB?8uxssq@0千}0 u*Q⯨QIENDB`cppcheck-2.7/gui/help/images/walkthrough-analysis.png000066400000000000000000004264571417746362400230510ustar00rootroot00000000000000PNG  IHDRF9 pHYs  ~ IDATx^UU430twJ (`+vv؊bE#HwL3=zp3޹3L Z'O$@$@$@$@$@$P<ϼG#sA :H`QZV.@y$Mu#z:Q9η8y@)Ǚ6 iބQ_]t[ dq Gz~ 6@ D uDRd$@$@$@$@$@$@$P:B!:QNӹFKgN /DOobh7j"-E9]IHHHHHHmJO_V-;s-!     ($3u7y<%1FVFbIB#ԓi vgn]- Bij @ x/GvKP[_ܛ?CKDuvG񹷘8Fa4ő #ZStK,$P_z\$@$@$@$@$@$@$@NA.Z<[vA^%Z"'qԺwZZeS\%(SYwzA}bb߿_RRR$33Srrr c2hj6HHHHHH4%FFFJLLT^]j׮-r\EN0TE < @$Z7S>tٸql޼I?8iڤDEEɼ#e D$@$@$@$@$@$@@~~dgg˖[dYҬYsiٲq,G֦8Z5SH F=Dq,))I/_.=w&M4ȐkQ&       AfDZV"qUe֭pbҥ$cI-Fǜ`+IB#3fJvP4Yx :i2ʗԔ Ŋ!      L Zu~L7ѣyG-Թ%yj$PZ5nzjiѼW1E3JCHHHHHHHD Tټeo^-J#8j^i'K$_+ac$@8]}md(l,jRIHHHHHH!TZ y&N2{r:!l(~97a/nEo߾}r%=-`IHHHHHH|?4{/R&NQԛ`o$N$@e'Qԛ[}rriBu%&       MNZN͈N7y3K$qz"CabOn5n-EBYYY ]&C$@$@$@$@$@$@$P´LhQK[Z)@jK) H .8inߡ:f ْ(uQHHHHHH+<52Z!Ql H킨] {-3&rLi9jugԓHj#R cl: ['*Gn3 @pnƄʜ=h9-сa$@$@$@$@$@$@$PXEXvQԓ+F)$PY -4@ u:ko      Hn1jG^b:7b+}eA3 SsNQr tM   "v+'2HcSM"  8 < BYi1 |f'(\}=#}ɌK%L`oȌ9se-! ՗a)1,xs%WHF  i=+̘=Wl.5W#UT9]w/S>RNtRHF?)֬5`9Pv-ؾ\zRn{=~`|}ƥk[ILLteČ "[9[LX'^ze/7m~>r:6)rٵ7y臋U,V.Od-pYda6eCdK& ,;p-JRO4{NkQ0ZgNY ŮЕl;$pȓϿ$.W4oR7o-[U 6^vq_?N>*vhFV]'$%'7pX8@mG.fݺHTT~1sK_ĩLMKddDGvgϴ7JxMڲm$q}ћikxx;  |l_;뿟Jl~ym,9bCQƑ  Lŕ>Ld|~Hءyf m=?_b_џ/riPZgʃ 7ɟm ~wuŪզ ];u#96ϲ<̘`DD˙CNZ86UbQ]#uz`m5(Xa³OoܠZS#phj| O"(K<=[1/Ka[H'Ob#POjl۾Cv>Jv-/XVP软c^yU&}?YcbgnFpk%lسzҡm[ILJ>D֬[o-;u>8~!;y=3ifg޻cѦZ*6nrۧjƬ]ֲW> >)iF&Ę_5±~/EbT? UZIJ`ɇ-4-!a=$P @(D^*Y7jOUOaWlth֔xZHh{C-#""],_X%’ԓOiT$Zj\7nljy2?|yg{5}y;T-qO _! $S6}cȇ~ 9oYݧbEv>O癰0]:w,,/&:Z^x1sW PwIF=p?AG?[嚑V_n`dnV!zeyr1 ξC臰Mp:m~aU0}=M?|V֘ש(.l޲lܵO[_M7Gj֌w_&j2d~B|&_,[-0?\Ъeu~׊8ǻrCdPZs$p8)Hjj]w3JvO5O¨U/QSFH$,@ޠLgHHI! AOμ,7zatټGzQrNKKW ? 69uoxRo0< ӦLH<ҲnȘ  %7`8:,"𰉯%~4!VaF΍/HU5qiVTU$^wW-+@OjLDX{s@M~4WDOGؾ*+aa&N|#RS7թqRMj0\ 5>Y#*2|8y_>CP\Q_ eGvY׃9 Nv#k^/?aP'C}PQo |?bfj EEK ]6ӸIVvHsuc%oڜ nM545Y/sS6|U+"+U n/Da\iE2sμ2ZmĆ8Vb1>"j hL֢ȃ_~Wkt[oV. |('5i\s_ qr>4/D˞YG8\z.O/'Og;5̝_WRV-7MX'k$oTu fw#E Źx. ub_}6nv;h~Ny}6m pӟVIGeh#HJg RzUe$@M7`C uͤZ&H'kxX~N/0iK&k>cλ:g%{+2vam66LFmd<|~z2oO7G5ػ1.EOO٧"p?Ȱ϶0}ǎZ֙}))E89>h$rJ`EC9}YxxnJ!Si$Β}{vTPya}Ûwi0}h0x @/vIɂsss?9J.MZ{66 Y"Md#MYS`A:Nx ,=x 6nHe}seʼn>cv T}^1}wJ.ם Un뫏VY؝(;=A?FZMRz0=GUH]&*VM[;?p8L* ?`䔕v#x@1A _gz(k zx s.\z\1B#~T$[8NX{꧛뵢3P:8]y^puVܶUZc]O_jX޹|w[u=>ñ`Q搇-is;+L]cJE`%$@Sشza{K󕝝O ^-N9dIM]v+3;~DJK,\rk7^*Uޒ/e5v_;^8Ycx5  (?~CN _x| axBLY/җqgKIRs*Z IDATahR v(I1Ƴ"B7>`(L,G~K;[ow .! )$@$P^ p8̟?]0OF߿ԭ[Wf͚U\>+/ l w-o\|r1[| d̡O?Tn6aP/v!@!q'>%c /B5v]eV     JBh%hvH_5 zyo]#py8J+SLsK<NAzK7on:sL#7uQƢՊÊ`}6cB/^l]VhKtڵ]0{=9S#,wޑի~sUW0Hw^#=RfMsÙO~К5kdrI'9_~Fɍ7hےs oO3m:eܸqRvm9ra-=䓒#<q|?@$@$@$@$@$@"@WPd9$@e]V HKV+J*&;66z׍h(ң>zX1SL^z Hb4""CLXyJpˏƍ ĹPp袋M7$2m4˥jժO6օW]aCX"DCXx BnܸQkJ3a 9s昶ʴUVk&+W4 0GAB3-1e+. X}B /,wygUp/Ėu*묳2 Zy30}c"     #MO$P`}-ƨƬ_^/ 6, j\O=T%_~(2YiwS;mڴ11GùD6XBd ua ̛q `.`b"!46^ЍǏ:!     R!]K3+!( faSlߚ. $5jpDJ| _7sğD\Ql΃|%䃨 KDkGPBα)L7kp ! \a Pc{0(i>>o_,!d[/+?v~ ֶsNVL{׈cƌ)ŘA FҟوD$@$@$@$@$@e@嗕ư$@/^iE/[/"6% *r%36&     %lf}.a /^0(" /l"  Lõ_~DD$@$@$@$@$@$P tM  oWJچvY\ܴ8c鯟1`gzy!9_ߐ4 @ 0ZpY4 @!Pŝܶ(x<e(>ZDL[&     (J_G#        9 !GIHHHHHHHH:җb*azM7رc=>>HF:o@$@$@$@$@$@$@$@$@6F9*<l3w\YvڵKbbb~ҧOiٲe{n1cرCRRRO8SOi˟)[ntiܸO6mT3'˖-[8 ̬UnZ+qqqA< Λ7Xo߾p߿&$c!$@G֟$ILL,6ԬYӬmGuTa=G, /s\;u`HHH*- v+~!>o޽{=vK.cYg<E=^Z5KdF,-A;/B233T&W_}ՕиrJOモ+V1S /Py#KǏd𙜜l޿[ 6m*RĨQnW, TPl^бc šשS'@H]ABHε#Yw(&~ 2 TLkזСCvheGeȐ!aM9cDOz Qajj>3#2²$ ),ZuA知GKV_k1֛Rvv|Gʧ~jH͕K/T+nI1 < NNNN6^7o.XOɺK,8sH]܁Xj)[n- q໿$Q*P摀ܱ `ۭw?_ڶm+4ߤI$##db߄ |}'(̃SO=w͛7 HpAӧKϞ=C:XYYYrQ83O6.[l/PP XXE}5k׮F3<+?^ڜ ;Oϟ/۷w?pDQX8PO\tOu%$#d8/s1Ү];B' +!u,K{GamLkFRN#T+o'ժDK5U<2M<,EKZE ,`ru-$9v{c%ez,엜̝zdI~^ԗT N}._λ;/0KBCQI/$e]x_~ʂe3Ue](kJFs[7{k ONb"f1裏znYJm^N祲hپ'Yb"͚/גc:5+z UxyFtjӢE2KÅT.?Pj7k洠t􅊠)]%F£I~nK'3FXHR}8'i3$+=K/x؈O|8_"4bvC;dgc ݤ:]Q>xtq?DU(IMːdӆ2qrوaҦuNw{,GfzW׬Ϛhaj {s|yڴic#A8DhVZIÆ ՞#q!HHH*0ZyRo zkQ`(?H^ cT= 147B5ݓ. b"C,adĉVr}YѬYkp'B+ +iVn8/ *);<ĵ@L"S0r”1hPQYk֬)K /y c0!AknqaMG=I2W݁^UJ^?:#6NoN:Iib]WN9sZ' .LI[ÃH](a9*`чUZ4?G}F:2G"+vS~IyOi֤\4 V>\غW(xAsqSip?ś$7F Wa1=u k`>|2-!2E;fd?FHvD.QŦ>3yjۮ}7_}*umƎÏCޡ.y2qH}XyĹń{xI//~Gl@瞹vu=59r\veHH F8. bf_)dV "n;[ozr/7|3(<$ b#+Zo¨?Q^6S'q37eW(ܹz.Gw6O"֍+.,Z- L&AQLVQ,G0׬6*"?9hܸ Zp99XsaC('>C ^wd@}(ҷm)yQT|}UQ7Aj1:od^ts@ N5ipǖH Fօq A eGO,t` p^iörJzf{|l[>o\Iޙ4_p"i6aOs!?Z^s(6zz*{ S ]k5 W֮*&%;A"]w|=,M fReKi ]#Ӈ,+AA_>LSJӮwJd4,kk!JxGDKCV*j(åytc՛;>A6o%~мh*kDHnNԮśR+pu2R׭-!;36&E VWup0ZSw'ko;dҳM a&LY})o&~ŨHSM?CgDQUSՓ ׃k+Hi>-,N6>N7n#"½rSw(և =n~Cj yel4d%tB6rHc a 14(>ko!…5!A\nKUꪫ碋.2#AHW>6ܬY\mR%!,Npse}Kp9ǓHX"3 cǎ-0ҥ^ ] 0pH D04 /#ؤ, "9c5!WI*!jbϡWZкEtS}^ .v5 ӪU+Y-dY'A 䉧%b-w#ДO#5ʇO]$oaw|(<22ˆ>w fvqq IDAT ɝ< H@Qge[t=;Lk8*j/Y,YUʹi$ScO7հN` CO Յ^}bI!陲@Q&N*8oؙmJ|Sn0K/Ǎ{Xsgv@ڸqypv1֬$c`\垔*O*jʐ$2hgqׇ@ʼ$@$@$@$@as@p6=r ~RU=n D07iС(P YZm 6>Bh k-) a (*@pFl^XBEo kQ$XC]p \H 9>C~_Q6suj%ZWcExǾTi>)'l)/}s2oVݩIUWKQmX]sժ  ӡho+r/u߭խێM7NU4L*.u/y;_ǥR̐TTυTpiDDC%kQQ!aa4O_/X[?{ F,KT I UPON=(U-) 5$K-IߛK^m@(Xh# Z &\kHWm>ԟ(cB8=XcOW$'#3&J1jTEpbkV/:fHS0ևPpMO}fl؜08ze]Xu ^Igg뗮ۃ>8j6Vݔjk?7I49Ci2pa82# "/d#2Jv`Qg/nZֈ8J6s˞`MK>XjN-md\g>OVvj,Z7x!rK[!#\9,!zM:O7e@qsu7h? D!C< q@Uc\8b8yU=h Mi*XP\mWNezZ@4\O8K +%6 nݺ1G|U<b)M30ୱuk}FLQRhKZDa qL kT׍ujyb2Yɾ dh󷺆nQ]R5N1:? $ʱy-]/]J}jh>j᝗Gg*VRf4ٶ~iܭ u Miݡ+NԈ,ILN7]5V22?[^r)]橳}QUTԹVʒ]O)39֝G"0j)kޮd|Q$\c6k.X/5+W əRf ٹcgPƿVaa9y)`Y\CѱV5`8c]7M_D&MI6ZV.f|vE}Ƹֵ]priڴlyir91v}gmW q<=|a $!n:󰙧rl/!l9<2^c ĺG?S)H>CX\rəvvȶm胓35ys٦Moܤ05]lnDXl&s.@lظXH0 3eVH.x(QvҨa}r][:7?30 b(lqխp@ƒs9UՋEboHH &MGlg%`Z, xڂc%{فQeMhu&y ƹM Zxj\|[ p7ƏyiMfm_+n jBDd7 d^c  08XUΜ90X*W:4LhTݩ#y4V <K6HĪ迱T1몘b?n)atbqXeC,vlaͲ [ a (,B#._SuZjÒ;eIdʜ%'7@U1ßIQ6sZ^|J9h]? vិr\Pi `Q>߭m;)ƌq~L`-F!z͓mkT U,nګ(Eժ 71Cij훤}4,w**bǩlܣV]$;c j!.amn[T?0aj2mdgnlnRZݾo7pY!oݡY-ѿg]5Þ}2s<}p݌(JfY&6۶g5PP+w>riF~^U;AC=HCd7USuȔh0_ tUk֭}UTV"&H§i07BT+M=8{Eq[C)h\hxLQ`5yؚ9m=АY_n&?M3V]KS†x1 . ˛ g<@ <$, Ir^v(c²meS;ݽ(6z衇.⋍5Puۖ̎ Vf(lQP@, TŹ/9xqǓ gW 8CXZ6k֬:7Pau~ؼCL7Ek*Hh[9dM }?V}l"h^ b/ouF ]>(:kL5x\\=chP wQ^q81Eo5aلIkcswWYV9nC9nٶ[E#ee+Ud[4]=Wt0- t}ߍ30in+!i>[.3f̐zѦ[rJ gx r X1߯7CzE8_tAA!^.^+Da<|Dhmv7Xx8q cȼ$@$@$PP-OŶ$aG66mꊚ:3~vHl2va*(fO?&"V:K}W~~+ ~7WꀨA%!7zB]V@>#fzj@,(1H#Ӎx DgKtP%F2Q:Hk3T 7i4ћKg'HZX&E% إي _+ƿCqB b@V ֫{/ e,>||7>ڭD{ZŨڥmy9XzQT-H?hW4udaFFgrdӮ~b{ٰuVFqnC40לeZ|y*L%\<^uU"( ^]&5>`޽kxROCoW].1*\茱,!e&k=>+eRI_~e@cLۼg_ڛ.{TTO̖ͅHb^5Z%Hy$+VXپ-Q6m.>ՌG}oѻpcٲB%Ҫ~F,T湶`2#Z΁KxVdłyr'ă*GHbvqAX,bI F4 C&Ӧ"[˖}T̚k׭TAYcK.`BnrpIB:WFo柧C'Fw}z;/Y?$;ڼ &+_K,Fu!VQai ۃp<XLB=AEyVSݘݤPn&>yuóuMu)n q W9;ļSn6nڎ<>7~"ц$X{HNQ0W3SP' |zU1 !`ًDOa׆s \gGÊ"1haqsk%ܴ’(e +  XUFRNrY'^b!~8uDaXD8ڛM4)k4BaG1GQNyjmZ"tXBEq.M7IJO"'mIWJ =BtWUѳV[$7k9k5'Y$}bi@nw0WLuT-\ZhZ0 aYg2b}Zv9}RWjY\U1=%%UEqHQQߍlU; "̖j#թ)Ro>DEO~Ҹd62tp s?7]tXk'MGl еUbx^-8goi,wNfVUvcd-S,̂8ڨ]+AZhYf*Fo2oRiެnTVۢu%*`ŪuoT7ɖ3,ݻ1(>;C" X)O>ߖ k5B9ҧ2{=a eE f!b{b9C.Ӈ5<P[ ļ$@$@$PQe\Qz~ThxR]_u赒' |f_Y.ʇ;=yFOq3 N;SL)4,~ۓ]qӄe1ꆇ>@̀iY}z Ǿ6} @1EC6 ,HH[98_G DB2! S >zC{u#+;fgP_ VZJJ({zO 'l%k{;fΘ nb j?nHqcmdYZ?݈8(dPXֿqpccov: ㏲!#A3bz#n!,VnDQcZĶS4\$O-*=MwO"b*?> E1A> !q쮮n|vQ"(cp 7۸ǫ8(drTz2g63Tp@f*:Q4.db)lkH-4c j\( ~ٮYm5|kVق`i IDATNaPDԞ;$~Y9;?v*q䓅|MPKu<믗!1>?,J̑Mo_ dgW cZ5 5 j\$y΍0/V˸˩Alƽ2d@p\*mɈN5{vn*^oݱMb교_nmǺ{eߛ% 3iEn苶jldjuh%VsbkOcuڵrEԤSIF 嵗{ cl٬2urCDbj;Pf͝/7\32p9JX䠝 .։~dbp޸$E? Y~5Stc<4ћ^h>"e9L\@*S (D%>Q~QxxAVpK$,qDSXBXQ rgFj)YeaAXbޥt0@qIn%*i+oO)-p &s[o[oIcЍ{:ЮƽXj Z B9cW7首ZV= FL} ^>Vrp]b3#lbguN)}YZ.ޠ[[d S&JϴϺR{IH/jDkc+jYLԍ'R|Yd $@$@$"n~C[-@ w ʙ+571zZ-Ҽ=FcYQjp&\cք du EgX\J@ N񋧄XK@7(*N920[YvCUY2])]t E5ק?֙Wㅹ5׮AE3UYdm!WQM#b C$g<7@:QTĮb{wڰݫbXP,"( *  7}9䤼I|eޛu^,fM[={cV0SN>e:Rԧ4Da'@"HӸ_eCR3xᏍo}/N^DX1!6a1\$h5~bIF\h DYv]s\jC}m7%0==c6Nlb2?:6kSE[lюNF!EwKQ@knّGfmb7m{`^PIf5^`}O(α~*g϶~)-HnVS˟4]G_\хfAV3j5Vz;&}šN/7j/K,f.8>m#J\LM[ В' 1Ҝ_VSm%gk y7435\j״32+_fz-iдmb4_ݶג? ,ciYL[K BvۨLVԚڰDc\BEIPCsyѐ((y- (x5UBmJ~E1cg:_/[~GKf0g}^=%H%E觃V[sK26CB. &0G|iAr8Fl"ޡ"lmQ6lp!:W["q~Gܓxݏe~gkoY7Zͳv4þhywg5C`Ț kLu;ZRtqsu/cemS<W -%l>~Q%,in-ŹsXɖܟ`s5kma5y Bwqqi]jId|,>rűQyL ifAf__b_Ώh=Kb2O%Xz(mfw[&fܧg-3+[6%xw]vqӳK+Ƞ9;4f0g?9tl<,cۻ nd˳&<8UhL5;ۛ`v`N.sk-̽gonϘgلw-m߻vjYrr3Jk[3frYdLǸo;'ҖV~}i1~״_!a{o o>yi0};s5:2P20oo;(ZqeL+qҸQܺ3?7kT-|6CXz dz|r0W ӭ&y(fs /wz k0<17P7Ӷ7W{c5W#HAs~2ӶYfv-͚2oN)sn뚽AK17ѺZ+3,,j6ifq;ӒXm֖ =oBKR6c>#J;}YgIj1[`FK_TRass8g~Q6Rǵ2͗=3g(GBBo4lik}863i ]>=}A=U}j| ӲICR 3kNbM,aЬ2[¬ձysW~0-e>C͒n |K?sɧ6Kۃ{UGοVk2BnA{N]L#=ٰKSU'=$}^x]Wln:#[-`kު=8 T,gZ˹k˵qYUx ny$9c ҂ cg}wF_ywܖh>j܏Պvnho~VKXvi7s:|wi0 qULfSK㱞?6үoie淽t輍Âw~/85d`Tsf'֖il 5o4s3S.yɆ!رcl4w%{C|\~Gs_~>?_',a/ve\q\k4qU3/bzn3tZDտTLns9W$m#4y.! Bn Pl%߃}ڃ5ix}S4&4F8ӭA(8GՅ6-+HKMholl&O\T\&D) f,<k6>0e/p˅l̇n_l-Ae%Cm$%\IYO$m$/Zht⊃;vL'{MCpSMIPS[u^H3-mks˛Om^kLeDyZϛ63mFdbj sKi1F}_yq}~>ߟ+n=g.r f| 4krrE8#ϖ-—=mf~yY0g=4m)yϬM|kZ߸YkbahWWM45[v`xB>?!ECp`@=q J+uu_‘P0U@E o~9<ɇ%OF,15'|{p;R&A/O56WYbn RDMI{OX#㴉<5c|$Vfl.1@3,>}T;6[9Ѿ~̥oifֹ%+tZ^zip{6Oܻr%}!F}z\n?}H]_.27/ c˧c-v2P)p,mV@M,zB5B@! "FZB!fղ9B@! B@! @:Ǝ[0pD Je$@M 0wܚ(Ve ! B@! WxB@! B@! B@䎀1B@! B@! B@T8"F+U}! B@! B@! rG@h! B@! B@! *ށB@! B@! #cV͊+͍52K,i߾ٳ~M߾}MÆ VjB@! B@! @ 髲ܹsɓW_}e~VZɼ+e[gUL! B@! Bn# bnZ?a5z]w e,B@! B@! E@"WG;餓_^hB;o6?cO<Ѭf֪cFNn~2\z!Pn",B@! (K-sعsb6_% oh-iD֭eu IDATka=P;?Х?׿e~nj_@~WS[nn*/@3sxtpX`kQ\ׯڍn.]2Č?c3-[t_s?tai֬Yfedj:[FNӬf]Bz13ΖKZ"B@! @e ["FKy/m63okYqk'?T!0! ̧~Z>}$F!}QsW:-@zO6hƕ^{͜q)ENpumРH@#HY dw5@9ꫯy@LGBX^s5{uR W]uab57_|4hPK/q_oZj2-m>IJشiSׯ]tYs5c3DMfVN,|n=B@Tz+TM! B@!_UMR@nݪT#y&馛vm)JL2{lg!%E_u$+Zq.sy#[yXqꑯ{N^Bk3Cꪎ̈́_mve_XLү_?[f%Ecʔ)H|`YgOd$E ^ug at Z<O J)B@! ƨ@QFE E2қZCuՙ=`2 Cj֘{o1O>3GnP &o` S]w݋$+bO É'}L6͙o{yΜ~ 鈶&Z^0Gi?1µ=1@3gwm7뚶mۺ>*;tK=_.?n ),UA{}if瞔駟̹瞛 3^zɑ`Ϛ:u]y0ۜ&,&>"6O:xIRS{H!Ѥ< k6$+:tH_3W}`Fmy2@}G2WG[ M#=/B@! (WDkTp~g =u n1c)/Yb%)Pz ~L!GgΜw/)}ݝ_ѣ:*zVXGiT.I1KR O?#% W?ThWW\qEr}{lMGy_T+ *C!PNdqRNUU]B@! ycV @(CmGLE?OhzSmC1)%U*Ep%Z^иĴ=zfʻ!bS 1QG ZHQw7xc*9J֐̡Y>ՑЇkuN=RJϜ^@-:(ܩ Rc}qVu -d8XŅL Xcq~C9_w^2-QĽN(C IY/vKUJSO=efN\rIs=OYgS&pݓJ8d怜gmU_#amY6W'ɛ{-n!X1%B@! *iVjPTcܹ3?)S:5Oޛ;hfC7xdp0/3}dyccMXIJhg1ߥqXJz6cqdM7uG&p(as|I&[7 2BlտuT\x{};ߑ~IĻᄏʻE;4rssg82'}W;K&ড়r?wx ]7޷o*r&>?/L:3<̧S/;裝x,Z8|,u w}F:I3׵{ y680ÿ ! @ br,jOByN ^\ #>U^;Cŗ(dnIѨ%8fIH-)\yO]HVNPU!lXƭ[Xv_}۩S'ɤIKQF@ʺ{*r-\ѣ;E[2뭷"~}۟y(較 t b9g?:H|#O<;43f I1щs`_՗_~i6|sC0l!4SIzNp8d?0ꪫvTI~0;wZ^"B)}X‘?6qdWfɴ(j Coi\-\Hʰnq=l0Z6/Qǧq!;ߟhzd? Q7cŝËK4]%u@f).GcǎU49xBkX{r۶mϬmxCtI* ]<L!F)+ӺW86iJK!2$$Ĭ;4a{챤y~hv,nX/plz} p@ д{Ǐw֐hzG;\p!}*5~I&>>ۛ={뮻9\w;2`֭|ߨ0Nqv7>6l[buĽFJf-'B@! e%W!ژrAR#̨9V(dA_A-6/%QqfJ9SN&UH`A э'r A;"y!x'B?k.6N5EcߠB@TJ_q]V2nWw(p<}cAqAh;BB&qXpѳ: PJCaf $X'$<!OǷb wHC !j!Gj֭9:&`T0O'$N |MBszOG+C@ޏ:n7["hBgcXCAZ%Fd::"UO^m|~GIW `L;."w$)k*-$;3 ֽ@,c؏7|Cy1! $DVRonJ(Q4m (i)_6G#n I]6`qFJA66=@˃}IEʆy0iBˀ7yav5BcM *hb߳0bBP mB9މXZxvmMv9 i=چ&LpK rCPH>!DII&F grBrBDH2H<jy{;0N;_ȱPD8V7te}N5N"V@{!/ђ`MPP !89TƔW_t} p|d‘&ZQamah:,kCu%@@]Y }B@䂀\RGfhq*&%}4/"z\Aò؛ql hc(AIJ|i!>ï|mI0B{ZL_xC$]w,!QsѢ͹2 *iV\Ub1q(hhsXwx>"ynDlӦ{o |9ͺ; D/_׬L.Z'Y}us&1)Kݢe~[=)qotݗXp/P {׹xg4@? @{LQx l=}\BI }B@! ! eHE_Ʃ$B w!H)N|҄'F=|Y5 ~CSH?&4]O04i=jQG_ju u"Qu"o'K̫C ǎ4 Si|>qlLpm')4o4* XyG_3@?/LoF8%$jjVFiٲFWxNq}!299l·f gh Xb@W ^2YRLcυ͵^! @hUECW^ίoh` Ek-jkL9J1WDG+taSG`x,kH_tb] !s(>?Uadr4 ˝T՘ͣ^{* m+4:Y@dEr2Dk X>s^ɛ5Dg?$$A05G*St|y|%>(1)OhYQzfZr.cC3o\'勦0zy\`CCNBjW Q+Q\r Rsq֚ Sʧ_0<5-k1g]L?r?T1E} ŝ z!m.x+B@F@hM|cybΗ^ziL! zw\\2dovLfaǏwb;>-q_j&l|?ku򊅃3XX)TdJ_=Wzwf\݈H*HGJ")xcAIDaLDr] n*8!ZĒ > :B 4;!ɏI ɅKo +ڎ/'>Q1Ou |B@Bd3ԋA/r! Ȇ@=KFH"Juw,L**=@D4n@!c: Y*p M/'{,gJ0!cqPX86PWN6DD+h)E?}60,n Inwʔ66M0O-MAJ8sOC*?.4T_>śZ6yֽfmv6&ΖT !PҦKs*2r%Bg/U)s=.k[B@ @|9gJ/\ /T2_9[a]a.]CE2,$E1JEr4<`J\賉 D.'  /| as!> Rr?0ңm]|B)$EE1&aeF1[ִ,Kh 1P9X rBMRrR@ j4Z%B@! *Wbμ 04&JN̟85thDMǜ &NJ'{ 2x3.7a„O/=4%CLOL$Lla)0?d.$yѣTy3s՛F|E3oz".èwcHל,o|ef7tQ! yoh_S{դ6!{֦v-B@!P{1Z{V DDoڊv(> A߇K=j>B !!E3e/8=9W&o c\iS\`IPpiMX\! j/zkoߪe"k5QGz&맲B@dC@e/!@׮]GD$j4}J|\܏&옡 )g#^ (~9IplW,ThakM\e$_RbSmfl&- `&..a1P@Q'e.VJ@M".B@!_ب5`:#|b0du@M^Y3J^L14i3g?nh\# J-[`%t,ڪEM h'uuuIy窧Lu62#J'@"jwP ;UY! B@ 8t=+ $Sp+A _LR f^0_qKZL+ W!`O\^Q(+[kthe>1¬jْw! *z0mV^B@! u!CV*$P"F s̚5?n#Iڜqߛͤi3ʯC7hsIB@! (kΝK^Y%\;cz)~&!B1LŽy޼:[i]g;_ B@! B@!_Ұ٥KCyUI1g4hM75=XvlS! B@! BhtXzfΜin6wɝA" *"8QhR_D! B@! B@\1 ZJ[' Q(h~w)ێHQ/IB@! B@! *Oe dMÝl[3a- i i޽{m^{NE ! B@! B@@Qy:V[m5%B@! B@! V ! '! B@! BXKZC%[ !PSlV5UB@=֭[׳86{("P1Xr rB@%/Q ! B@! B@! jipx! J@"(MA*E!PaԫW0Gy6PTaWn`ʩ*iNUB@! B@! Ȍ4F5BuM:jy" IDAT# хFjc躱F(,U95 B@! ѢAB@! B@! (W1Z=z !PPBxs1)ͤI̅^hn6˛7xL>4jԨRfB@r@ i`;'||gf%4z2ᄏ` jʏ?yW=c6lhfΜ4<[hQDq}qM7?|2H{gN:N:`u(tFɗ}'^\|Ņ3|ڥ{B-.]2E“ oTZje~7) B@U.sw?>f޼yo5ӦM1H.Qn~Mj"*XE(yL$净H" nܼyZO묳yW\-B*B(K! p~={9s#=B@!P\c*w! *CM:t2~xsy[zO?B@Eʦ_olVf]w5O=?6lCG-7s>Ҥkr;`nF[VXphP` ʼkI NF!P01υ$Jb!FM)M48 5jiҤkN͞{6̕l}]GtytV^ @2,A6;e\wu @S ۗ T8/3=biٲ#Q~GG~\NԻ%XkN 16ⴉ@/Bp-̆Y!ݻ;2g&;1'p&:y;e3!ܹ.bNjNwgv?WjE@haTnB@)9bjp4m/ҙeF7AtL%@ZT85hi rABdrD0&4ƌ6h-I>lGmVZi%!o@2:y:_\Aӕyzvs5qT8D5BӁՈ-\9uV M8uugkd˛oi G!^iZde@GD n0gg%|8@ %9Caڰ8?|;r|R1'd21kb'u{$B@fDUۄ( bT(^k(*S! @ !:oLD1ş'&~h*y%j'CіB[fB̢݅:u@[ڷo0bgk7qAZrw!P*B̀O%CRfTX{ {/ڴ^pA}tHU9 Te-K!P1ZzTB %NӝG5p58vͬN54itE7͚5s>RB M'40 σìN1g[{SiqʟZ[> ShdR?LYo9>L}4Bb(q&p_/*FrmW6`g;|cǎΜv&c>HUp>H[ҭGq&jR|-t't~HV3 !PzDs(@!f2d3ObM@ ~;ԩSNB@TE`ҤI.nHL@2  yw&bΎ[ !38}o=\HϞ=oO|9y,~QCAs0o k}q1&.5!uߎ:)_$B-!yV8T`݅{wjƙWڍCG"ׄ؄d%Z k+s 0ŹWiB`!"F5d:O?gdSi 5inb7l07 R=aN3[!H#PϚ7%_JB#nDr,|y9sL sD|)e,#B@EM\w q@kcҕ*]giVQ݅NcCB@1i7U* hY\چWT㲮ֻTcTc?Mh2c[T'! @M jt)B@!  fm۶-`J!P~;EjʯrBBht)@vB@N;:|?# ߗMa֥6j r;~N2%9ڢ! j 5BT""%ù.Sqmc^1Xr*Tg! ȌQ!  T#"0q|oUFjcjՠ),U9uD! @ вe˒+b䐫@! j=ܳ&UB@! B@! @ x D rB@! B@! Bht(@#0{l3yd`XߠitӴiXH! B@! (,"F rB"f֬YQF;w}SlQ6+k4'x 뮻 6\wu.3g-œ{|&NhV\qEg:P}ꩧ>cs;N3hzn#:^uUǰ?Kk>|诿27ptM͎;h*n0_+Fmd6dWGw5{キvvr ċ~S>nfsb*ݺusmxGa=4w}31t7m#u!9蠃gy'doGᵜĘ?~c z a׹֧V"! B@! j55*b>EQ\%<[j4 Ms1x;-Jf͚~9?7|ssz>|p=GC9bmڴq5Ȱ/جk~7oc9i"+C5wu;S‡ӝwّ7{i׮iܸq2!C_aAbo̘10%d /P;;2,^x#t}j-k̍7h~'%7HQ.]qXt .10@}璿ZRK.s ! B@!PȔfWB@0K/,R;~E(IP/:uTGXA.A!WH]t|EkO4oy $%D/_⒠FL''[o/wĘ4:i";,`(su.Q! ]aOQ-}>_W_)#!` fmfF4&/E$-}z^+0|gLɇ2{4-kH%d$&$7qOIXf^'_bCF%>wPڷo<0HU/Aܨdoh%3{葶( * qTRK\N! B@G@5e'GJK/3 0Cntq@ʵl2e|'(>e|!M[hj~L7Gi'_;vh{=Wj4`Ѩ#0p,*4'xM%CRKv(B@! 偀B2@ _B4$իoLD3$|<9 YּyXgosuy:$'9!B-I' qH8% >-[!VsAA4!C{kq' BCAW^)oڵ󻊆&$@9Nkܼ; %[}^{54HZ)qJ Nq\ ҂8a8 ߷w*3|C1 |WM/Of|K/q0T! B@! u̜Ѭ[HHf2 Cv@xz*%~!„T7L"iWv$D~F1Ǐ$=kW@'0]zBAS P>#0sG1$x GzΔ>KL[[o=3h wڟ|ߨ`yq9RbXoV..?v⦀6[駟҄L!}@1/V ! B@!PZcx4! j3DC" fpAB9b4;/ꐂ!9pD&*>=4E˒N)?b܏1(|Lgh"/Pr:t`K FQgcђ c*}MCs[ &:A`M V-h<.(8sy̾+M7Ӑ &Mxw;wNԱcǺ Y-~[*qȔC;dO>'b74M+Bz& K9 +~Ǘj(y֯-v_!B@! @g+N% !PWf>j QgOA\K3ZiD&ha~[L:l%+gZƱ;w%kZ{J_WAFq.j}EƃUHb /d6Zޔ>ZfɊh-vMe4Z4-8M%O!ؘ(! B@!P@ĻgN5R?z>^(` a, !P2V[u53yd]v| 3zbݮm6b*Y/Z.y1'ЕD! B@! G@hN! @M; DȒ<}D! B@! G@hN! (0K0(ݻHWf3/fڴiޅ,3Ș]S~)dB@! G@JB@! B@! B1Zf! B@! B@!P|Dc B@! B@! @! e!B 1 nkȴY}u`1M-B@! B@! E@|}B@"@oe>RSF#MgG"B@JB>3ӧOϩիWN(B@8! j'?nY#y IDAT'0aڍD! h׮]sѣsJB@! " qR:! @ "0M!CLyRք~ᣏJZ?%-3]a={4<@Nu9g4sO>dsM7w_{5S^=3ǺG('Y`Yݺix3VkڴiO>fE/sQG e^|f3?cyW4ڡ5ǜ|;|̳s\nx\sM77}7ϛ>=yI2sL3cƌUCusm+N!,~駋$e);t _{o7ۊ Lhj-d||͛77K,x̷Xp f}=#nn t{wxΤ \]| ={1~{yaSޛzj8s.ꩧ6lcve梋.JfϺ:]}7Ϲ\'-kn+ŬسVI)8+t;DQ'(0s.lƦ;v]H*lʁ{饗̆n, @>ـ$`䯺]gͪ$p/o[zjΞ=;m39=?S]0bC_ h-!v?]8xUWU1۾"k6cg`jf[ bW^y[!FOL0[W!^qnfL[n >80`:vfԁ0nfgE??Bgy'doGߢyxx3WC?1^C8_>߅LwG4o#A({챇#~7<6c^y޽{>B|bz3c͏ }8Ni7aԅr NYNn!)J{CD,}xRׯSڟM( o04B'c=̓́|wh0Nq/c@CTlߙ+x8߿{ۚQ]gߍw$gd2g2_3 >= _}:K\J9rH7tR"=)Es9J9ƶ<'t+XwGk 4sڻ`Ju@c-9cUɓgg01J;y^=?睔fY2Nch53v6XF~.(96Zl@u4hn O:խY2Gb1_gb)6KgK.vygÖT %qwu1]3ރo*fFNɃ4_5YMI}5Sk5oLTc^~O});*B@fnhaY {BxLӖ-MSK"iC`@nA"^N2~9>pIe3. E~>|^blyƛ]w]5C'UВ~ aW[m5WXHAn؁leCă6,>,BRB y3f[e A /8"06,VF䋍p5)6.,SE :w;XB@d@Ҳh_>lgb{%Tһ@ E:7<3}uweY- B Pli/&Řd5w}$xB5ic16bC A\S~),%/b3cY&Mrfm6%j|.s = ;6=oi?'|yq7?k 8岩>W 7QJl : "CP_y֝`eРAn_ag\s뭗/YMؗY2-ڗh.vaRnPi-Q'Uʱ՚I~8"]>aCR $D;Kl%E͚&˵䮻7lW%e],yΖ- wiS7qX9aτ]$7Y `7 K,9@"a1Hc K8/a7 %B\^k N噲UoI{2: N3:˒nc4RxOk\qgH/<3>E;<gƉ;έKV+OK;D~|4`ehl@ut> kZ=k7[Xyva,zBmp|gZfY[mU5=m%Ug]eA~ʈOt2a_9K%w|8ï)ر;mqppp 0.ad\Ws`cs^ nB5JKp! @zYsVt~qj"ԡũfc{5?&hQd97SQ9YHNZahz`BFSD05=/Zj L 2 :gBgM8.YB3$(*Bcc@S'ghYfMQAc-J4Li{0}V4( 53=/q'䍦S(v4`BcG|"i`>FCLc)MyG}e"δ.e-5}=uXb EKۯ_ٸeªfA챞|I;OJR!r9 0f+,ibM!ω9J\̚M5Ζ.~KA&EzGlL[L$t'i, ͢aD7 ?u fl`f6/-}.ib[*{6; HLdW<eaVQhAaԸ[TSyeo+LqLՇK/q BKF;(R|wD'7>+!'p\.$'_i;"L:o:AS8s?K¹4_} WE0sw6a;^0am>rgsߋUu^Bw`[Hߢrq "[çglm3}' 1F!]8e?HW?Tjz6gK` A~̷~q:!V_1!/d{ 0$p8{-,'δ.eL]C8m |FICtsʥ\] !_9#sY*t"FAM! \2ܞƷ'"%#ٍ1Dؠqooj74f}Vqo:% ,Ny_u68r7ߔjI?[旎 S^ڠcq , I}#-C:dJq/448 R #,B!@@wda oV{y>_%ѡ88dKj89JIG4/1 >6C)%~@f`AlZXl othgg6/f/X4Ed=Hg xh8>+-Ej%$U]f>F-P6>JjBMK[#%;ke^z{)oTO!--@t8D59 l8Z}&+VB2<CB{8 #-qۜ)]gb5p>+}KUw$s`F)G ؊+qBY/hgLUT /9̣N%PPl&0Eo6IE>h>5ח/WhN@ Vw[Ơ.L~lv̹h1ʳMАfA2ө3LhSK/lt0@>`^ s?s;Z'x&Lo"|hs8ㅃ* R~0]{aE@!BEON/A.gqpe 790ql8*kWWgEnN瀂61B97>}7ÕQͶ[? UE8$cH?Bl %L΀|f;rzUyd/jM+PbR\%{{'Uu{G:(Rl {]cM$j,QFc,QcCE"X@PҤ>w,w/3s,rs==oAKi^ĵLl̼_<'Y;E=?m$nRX3Kr$hyС/qc|J-yWD@Xm $eUh|(&&a( bDQE""{bFya\6J&(oq+). \6<#\d0!LK*ϳpf$(Q{ṩS(L9., p]/%Vw˥b-iw~!‚E q fbyV􂲔E/md*)^SXka3 %w{&X`f Gzm~ŸwyJFy簌GXEqZQ<[A` BW` o ~ EEõ<,l=O29 V2lPP>,nwPjݺuZ`w.}0`C@n/,p%,i(LI0ճWUulbRݯ}~[.p'-׿vذ>˒%KܢErvZצ$`g9/XIٶm[/'Tc}Ѵ3fL>hP,UtY3҅D@DrBϵ]> W]{˚5sWe]*[AS+vib0)X1Fsw}?~3,( X]tEzm5k ./ 쳏0aB^{5w駻_Wv{﯏2wW]uUNꩧO?tO ףGLQmݒ\*1:uJY>ޣFѿ&NO>dJ}{7VsײotӦM[YhGwuz(q) '7olG+<2ƀ' WW "  Hl ņ?䚚%,[Vr-t3wl1Y:(IO#G*tcQF.]]fDЬY3ony$ Y_ 'Xz?ޅ;dJTeXć?UA)P|?sb95`P2sQG *b醙<*ƲP9bt=nX*O`'[wj4[K/Ztcm=|XyިGރ}w3fB06o~Gq{GPGmhSa%"M[_Q& d}|衇fmww|"N:TaX)ʏ-?GV\>M /\~şCA'bs>w p}ez_8.xoO8s~R>,`+([\lQ 9Ź^gv/0P88ܞkWLkɇҍzXqmwիWwݺu-dmjl,S?Ó'~ߢfج`;wvu’iWwZ\g)sM)Q]~ ASG1}7( ըWz.%fSq)a18_EF9}>gf^H`|F[:Is σ.\0Zqy\i sm4u2n܋:e5_=n~c9v%/ٚ{026Ia=~p_%9圤ͤ1P]ND@rE8[D՛3mղY4S\'6l;& [(V׫W m|Ip=&(,&Xس@#FGpaB{d'X }wyLR !9gϞ@r#C ,hY8 Kr|>%%AXhfKKLP?Qڷo,%YL1abr_EX[@9k,&LҨ/1Qk*u҇ꫯ$'&,&U=q>}1CӾhJ:/^xowzqb H7(_Ľ/ƓxbzrW_}u03|GA9(]gh' =}ɭ!DE(p۾LQJKC]a9/t囔URYS<{xl֢x.T(OYPruϘN;g K}}:26w.›]Ά㫷;DޥƽQT{ӧCA?”~=,B$}L:1!,F4^PzZqSq)s7~C=1ORk/;%sfPѸqR8OsLy\n +xa[Gx<:㻰0e¸:GX[{0oIlٸ猻V6wNn "}@m@mۀ1˖"d&NE ("LaUdO-lZd"["Ȭ,E E6)Wdi-c?'s&L(zkעOڪhO¾\&l.Ϣ}>}߿hԩ[SBXFL9Zd"l^d cMQYdec")vq6E|Y+96,,L9L)Zd̽P ?W"Pɔq$&G 1es)?lZ{oi1(-l|gJ-*LQdslqSd ol$xb;xp=<C 1uhTxg9eO X,=&yEh,sMЋlb|ڢSlG>&EP-=:͟l "[)Ym6EOD9^ց/|wEoqA),eYpѢmƀG+)B 3k2}12נ_LcmſyBXz(I{[SJ\mC1gS#ol[S>-Nw۔Я~"}O7FהE- p1k2k6,}K^l-|5 6 x>~6,f-|6ic^bQ_fϞ]ɜÔFE - %95a8;B6xcc?׶}{ [<m}85;eeoJq2naeJ`m4:^'wl wGǛ'VѲEtm-]:t eE_H@IYY+s?- u.*" '𕹗6FfR,,GƎ_m̾oe}-ٞ\08 v#MNtM>,Ċ!jE>K\`kX{"!tʀ-ZH ++2Z_,0pX* WsX!}ƽ$3؉G®7X e؍r7,Z d<6`I\$ X$P_X6 s*7*q K1"V$ fLZ|KR޹>6{mArE:{$:u5 [rpTeB n\~}a(U\3zk,7 )XͻU,zEn>V,) $VxU,_R1Bߒi<}PER`I[,m|U]ܸ!l˻7J7`P*I^xiX5cqcR,o&IlQq=2vkx~8i>hE}9h?Xa.]XkK2W>Wg/ah u,*iqLu*VXaEʼ"m4X㥂5+t=86:ד! Vj027ULjV=NڟFT}A6^9Hxaܧ"sx?EtLW̑+7?oPX$8]#G1g}3dޟ69:V"눀@ Lu)(;k^DangLkT=縙)&qF eRH^醲dc~Dt5I.로FUh)31 Qxd[.p|Ѹ)?6I+&Ug?aQ AT!N zn< Z|ť.pdAxH$ 'jAP0LZ>xxNxeaV1u^ǡ@ ɿFŞp>%:sbڱQ^{ʉ! ~S%|oXX-nF>0((m2+bF7%:>t"U%x! E\Q:H'(`eT=I n݌+lN%?̙~LV)Wsid3-W8a| ǖmw7HŸߙ>ލ $m~x3B(-Tjܸ?\aJ!qi6ϖظf_!Tt.s?\' IH72ǛlѹH\?M{=w\;]93sTH1+҃3q=(FSĕfqNkm;MP˔lq" Kh XMvښ+[sn:ҋLt?a%(;,\I╋~ [IR \;|t $…ą獻v]`Ń';b?t')ccإ (R w=fWk2QGkX|GM&+$,qץ]W$aAC1ҏ`Tq J:;L av߱Q/q*wM`XU LYf ⋱P gcbտ̩>Ɓ o!@E~W8E&^2/IƓT4?Ɗ< @_UlaRW$u .(.,X'FiAu1l9,j ԿlA۶>v?{ U@laJXطM*O1Pl܂I `\ P|`I }d:DýpMoXg2ŚO,Z p /6&\KQ(gq Ux,rp +q5EapsU(y3|| Ó$MĊ% .(p2(Pa]klCJT򅭷p͕,}o< Ŋ1b"J zꑶcB Ev\{5604j?3b /yLaQAPoʮo28O|G^ǡ/D*ZlgzAqEJt۲nyՑ Kfo_}gJ[%omxHl"|,Fyy&@6x'P`?,2y2Ls ,xxŅK%nR9 VI L>),i>R|~ͦűʔ#X0d>LBr&z\yv&̡fcdM$>." ޺"m{ 6(V ֚)xMlq(ZSS)9&Cq=\$);s9G'yl$aJ{`  Lsl?qTc@ҾLI|&XW1>LpXX02!N[&".4&XIǝD)\(~i IDATٹQޢ).>S6< dQ0(K*ga"'B M@aqaTn 'ꎺ`qC #9rEu%H"/PX0G!6aLvXo݊=e)8mE ’<(PxeLeK.cy .ϴ//Wލ8cb (-((q$FX.˻{e'%.>Ž%- >)i0pm,ԥN}.(s>u,qK$CplPi/0GZn 'ݵDwD p{IgO _2{]rilPF8^>6U\߃< gQ(Di9aeUy@cD5HeC 0?&a;s]83u}Rz6ͱXe8IlSeDNQ 80X'3/d~{i.Oƀ؝pg:y NR=J@2'&|K\?8LpMnnS=C~ NΘS:쳃M[m/qϙm;) \"@bˈhq:v,f ĸJ%`DAgTb?(v pnquWTܵ2Gdf I^Bg أ)VX JMn_bi%0nAs ;1q3ĺ-Svkr*ND6$nCя4W?!ԮaC N\!=J#+~g+a8QWR";ya!L,?Oqf]AGlB+,EMO|u3p[nn4O(IEkbtizPHK Ib H8q2ʓ2X[qӅuyrQ/|Z87ƽ/WUZ7UWyD@D`c'@|gZK6C"?P.@&1HVXQ LB>AXr&JP'il+9{Ң9>7_D)F1MW,pG)x_\%" " " " " " " "P(-y7p]+?DBSBe>~Lчzh˒LĄܹsdP|IVr2 Fz!6&‡d0e]q %YnUSJzBW:9%" ". ;wF@;b4uJ @f˰# ic(:̙Sz(֝Uvԩn„ o3g >c.m殻kka)If4@`M:}c.$@?ȅ^p1J^|u1ݼgϞ,9o޼ IWZՓ~ՋZ-U":[D`'rtWo(C@Q5 Va;6GqDRXuQAo/Xwb?x@J"\Q"__ 'nV׶muN< 4U}nUW]),d*A+EIt 7!+^xdYU B/L5kuML5ҦƖcb" 9ٔo @/" " " ))F7ҊߐaKM\?>EP~ǁecTO?p_ti?+s|-[|$1?ӹw!PsClGP/guV8-4ۆƺFVnR}gxӮ .VTtlOl @/y#pIM5YoMkgu$MBITIĴ|W_F]ZF,Q>ǻ^zg!RHC9PQˁ;<`_~n(JQ;;CWP ]6YĵiZgonIvVJ޽{v':ZD@D@b4!(V8:IZD"n挅%?o]wuo}mAO>$p”^o ~3PkwꩧƝ߳$`x{tרf Wd^s~pgNrۗ s!]tVqWǟ-tn9MI\6- ;69'-ܜ+UML|lݑڹ[<}kۆŀӶr|79dmkZ;Yw6'nU>Sýy纳^zܓʾY`#=wh-ݎ"%lH>ۺ071hRwBo7pTwϗ?`7syv)(r\J6gul >@]W"c)={v6lĭ|gb'P o҆(²vە9bwgI&nmi8wa$!PmJa5"}ZbØD>;w;eMstܔ)Uj$c˺n c˟;bIKWu5eT[%W OM_^i}ZOؤL4w=x~bI%_\+;:GI.[g`ªFR:5kU2n#pSs:Ȳ2.g\Oۆn-8p`;?V~/oO?Y YvY-bIus#Z]ҝ{YDa{ؿ;ӗJϧ7gOY|g۟ޞP|YtrSfau4><|R蛉65GY:ab7r =7|q>yڴiٟۧm`ʳ!3*Q}l/Q{y^(ΖG{試n-wW\EۈWHμ(zJtl^*=t(UT;8UV)SC ]z%\{8p㓭,Xk֪|#=]qGUNrdkPe[hp<裃ŋx|M8 pGY,IF΂Y Mfn-t?կ_ rc[wqk7pk\ߡgul7,t[er?_~Wⶱu{Or;tkj%>[̂!ӂ{E3w`YMOv,<;6akӤ۬U}7xl2^M= fܰIت۩[SײQʴM]t»՘aEulm:nޟXݺ)bϋf4M76eCOsᡬo;m¸^gܱs ͕kϋܿ>M^:(?-;6/^^zxf]];4tV1+Jq=wKnww&{5wn톛2RO.twor)77~-YSlb-[uH?Oumf+71s?w o53}{1۴r[ijnwȳTB;e2o8=n긥>;;Pݴo\Xgu>73tEy%u{[{?'Nsh53,7n17Һ57q3.=?#uì]bpU);ϻ<ܫO|ZVzզL]r7|S"qSkWKeϏʫ;ώ ߇| +Eqbz/#a(׵whޒ.7U_KPGO0qaujߓf/q~4}R5kL;dwT;]6oaQߛBnݼiۺG?mjc-p,GouGS,i!\pֵ__g}qOofoe׻d.ۦ Rkɥ#wCzAw3,J2eX@{!̗lȠ;zomPs+wJvF'?-G_Hq1>q!!۴qjp7J湭;5--^ܻʁo_wgKsg|&{kw[qYb:8΍tW^jY?W'IĤu}J+uOǯ? W%w\ٳ۾ksW&6'_^e7x_;u[svM,lO qD&;c, 5i[ݮmyyTWkӍiX閎~6AߖWquXF {IsN]z޽\byGg#f엜[?k|++\:*@إKt2UW]ޤb^]ʨ޶m[7dwwYף½}]w駻^znH 2"*X8/ؤMn.8nɚtE+LҔ[wj.2a*!(l5E6Т\ ,V-5_Kr|>M7v{iش`q2ef ] {v.S<1d|8/\ڝcqj 3]:5[\qݝ7o.ک]jmIn1%u5 c?{UV+J _S=rձvY A%/¤,~ocJo&wsCJP,3Xa%ᖣ l69nϞܙ۶*=-^(kZZ{H%q %>T7eL;4q4[,tB%^R×lgJ&Z \۷*vYܶE`C{7ZZݡXdl)\.?h/[{w?--rڢvw6r,\wxvq~Ӕ @[ļ4m԰voIKWmNH 3js;dOXw(5_<}[wO[nʟ{#JTnNnoUfk}}S~\@ ;~nQۻ+CmՔ/7)XXD.s]E)]7Er[jߵ~#zqE!n4=qr_0Ղ1mۺ[m1;WX0b>haJ{1%J7/PDn}͍ꥒI}S*c M0Z>6<[oRVn-_}n\(ڵ ǰHQ0Ys׿}d+ m,-cL %?B[|\o{j6Whj^h?[mNEǠ:V='_vm\\mJ;6$FZyŸ6CSW4ms~am^osB:2?m*{xO'q-Zds628clyZ|6^LMMmڅ%C'q l>y8?mcMjWb;mZ)߲2e-NAEʚb^Cܥ߯% C 6礙2n|?=n) ;r9e.Nβ k_}0'^v iLTo~HRin׿8nұ𕯧O:s^%+V=C'GtcƐd1c\~ ԨaVK>y 2V|PR" rtP ]V|G >4+tٳgK.]QBAIc1˿3gL>wIjs}fS=WToիW>] ]menDnMT LaZf_[4'صooMsǸe6)x{wYf 2bYgVw+vfi z{k5y,{Ěl)2Ϸ6DW; mފ8}^̿%-X6gBa@g(L9'x(|Y$d{?)lld&j@Yia6A1MiqVZ;G"M/}yt'=4%%8Œ5(Р-#%׺A؄d dwcΎ[pfJREŌhHjmkrY~lѮ[?Y1K2xnI7 IDATM\n7=[ڴ '=`JN,;'y)L/sm[cvp*y剼e ScGYn6+]Bhf]ea᷏Y1}Iߕy:C$RiY}aYФJ ~mȖb{vfyu)HXtZ4osUUh\,7u8aspߵs,jb kw<#Np?Iznc̉.,Kgo4ߋ^/~GPRcɃm3V]mWǔZO4˝[m֓'mo>(б=,Y%^3+?\-O}r}ۻM <T _)|"[#ۼ`bwq텶 M ,z|yx2T߽9ialW $B -:~IlXC!Q7L~>6($d>S۩]7{Nެʬ~J20dܾmta @Ϟ!LuYu_lKm6*?bm5NF=q'<3Lsymzzu\i?$]6YN1CKtakZr6U gh٫kq=θRbc`:pƀ˾F=Nҟ>bm}k>!U2@S۞ W-^_и:I2&fS)k>~c&jlV<&ϧ& ck  $鶑16PFyN厷 ,,PIvx|1) ӘtNa@"(a3sN/Kr{۰a3:k2{Ye.heP5J[o5 Ee'[v?T< ++s]ve`w]w~8_{gܚI$&UMPAI-n!P" g]Պ^ejPnԷآ5J!d\j5E]\~p3i[*c{ ~},Lw)P 0/KuJ{OXEf-'(ƶ`w6]oS5[+X(JL_LݳUn) WάOI\2E4εRN%2XV52V6%3&N ߧ-YJ+aV(Yˊw;>w$k:ؒKLt#w["n{cbu8+7S=6(D7ĂcP0cdu%J .fݻ)okv_q){`"G%\د66 }=7h ܇-p?a;_{Ōх%}s|Ⱎ)@\7-oڦ W)S),pN:ݳEƱW=PC+ya˖-KcZ?! %ʏOFt"4ضSTQ$FNdnˢPhΙe=,~TwmWLbt7{F4JQm4\ⴞ`0ؐj`֭ @2xw@1bomOXF8G+V|4]^>1LM=R4'ORԗ%wgtܬ3ϬkcY(Lu7&(K\YTǾcZg'gmkJy3J7_=d\jjcT=5x嫉KoqiL"NquQ\<ߒ^ٌ寎F۶D2ļzWgqw]SrD# F|S 2`;Ibw]tQJ2[Z|x (Ӄ>k}T%JzPTQA{wN'zMƂnic\]MKUIJvԨQAQT7W}M:ʹ$L({ Dcq.ȟ&1$$`)X.b [񋵓_L5׽mY,Vd)2|Nbv-vCbt)Rz;OFn[Hmں+el );>hw5"r}M9Ȩ.sП"{~FkojV vÒ"7N}7B12剼O'2Eu{S8&a!r$ /hq.ٿMv}Yܟ#َ;a`b;=2JF?Xk%.;cmD6X{\=Y|lVp9P04t۩NaTa@R/%[_W%ɠyߚhn~Y4dޞ5+Ig27`qSXVXE[N^6eXm2=yɪ"t.xRM0!2h^y7 Ŧ8J\N,F\\rmN.ެyCmSsjTsobᏊ-TsGcM{q-<=i _.6Z5v=l+F\e^'{V_{ĵp@ͬҕ MvYƕkf~>%3]+U_<" (.ق%{u \N`qB~ֱz6JJ IYcI66Hp2M`C9Vkld ɈK6{׵ I[?k_;ymbͺm8 k`aY"'9Z~kIpZ#f+P%>twS‡c5k+GE$BV[آC%V$| >3,:7 Oc(>EC/5 P۱miͣ\ѝ;5vu_i>nL"o{ag_GsGD`$&2>&y\#\u Nw{v#Gg07vIa!#z[f `i3mJ]dquwk&۱;.fiY8W8)ԻŲ/kX-|s\825uS~k.[^(f.Xf u#76Ț >s,ؒvPW)2)7?-\UgM{b?⭎'0r`eߧc 153oJU;,lo'9}:%|$gMe<~}RB߷Mtr#-֖IAp"d%"w~a~܁\e;rV`ѫM}2Ȏŕ&o7Y-k33A|\d!1@,IMeb)rʳ$+t'6X\Xz;[-rO Mtu_d1 [u ^f 1 r}z{h#Khdj[{b׸JY23SjjI >7yKkrL\k-f^Zq"w?b%:@٦ ʻM6Iq{[I"M `0kh$luǹLA=-^( |2 [bMAb2cOv@rökn10CM\b6.{BZlmc}m­0#Sڪe^l Wa=d{[ =ۦinN$.™'_o[i/'$l/ܷD0uEڿO-fr(U9x2ޒ/U0U&t7Dyxc۶0E!m 5w]\[3dB±}l,޷IldN*Xb}.i?W]dIdq GZ_[ܽcU2}! 3dmbvm]A["PX{wѣtoAYu) f͘`5zcnTl|{ Khɜ%x&OIԧO Cg}Oۥco֥!Yܩf~Ƈ{%fY:I2&&hVoktX2R Z <S}>ɞmXfAu/(q>1l{b%ӘE=a/O[b‹$;ōh1;^#>}{\.h.iZ%W^YKK(BB"y׸sG ̙/JdcjG"s9miؒ%Kw= @=$wnܩ6q[wl˛cm2֝f篲 Zl!ʿ)]Cr%9e%Ksvƽ6ks=ł>-hu7b=Y2T>Ebt5=93- XdC/w2mDI|WںZէƺ y0 .^ޛ_0g6 Z1K=mcU6߉D(@lkJ ђsoo]L i)b '(S~_S6KN˔$B^gw)FO@ָаV"M3KˊB};oSSJ6JʖJBgJ[`)Ώ/t Sk_[/w[6}ؽ#14?8ʝ{'g:e0d:7U%E1J{ ^ht8\,nbX*S%_ڳ~gQv>UU+wBbCPQk3(4]l 3m̲z'%GmnMȁ#-*،B"$ɘݻkX]GqeM $;}Lqև771ɩ73+í=oc橶1w)E6fE\h!Uf2 AZ: 6I&cm`/2>7G$زuKgzit՛;Rjf1@:F*bGHO'pmVv݉; /0饁eFnm;6rEe'{衇$[om![;M_&N,9l݌ DGp:ˡH$'}'G}48G4hPww%ʳ|5j(PWQ?1z \aCw1E3O6ϕ2 u5QdPw|m`ˇDT_tImcȐ!#Sel= T_en@upՍI)JE>Glz6Tz WF-&5f^Vihт)ҤI'2~Y(E]_V/wEMABmRo6W&\e1P(oM-le<k^1j 䵭$)w.I˓zqe]k&~oX$i ްP " #P3wDu%HO@c:T_JTu<3TnkkP\ף>& 8^PĢH$6h&AE%XfI j*!;8J! =P*of5hؕ;|k6`شD հa๱ׯ_{)+J_uQz5n8lIdԩA(㊏Rwq@Qh]B )׷~ôO>o߾IcxVV?Q^LQniӉ])rjZej4J;+OV/0WIR>qRYE@D@D@D@*\+,H>w ԤJԸk;E▦KԔ:R|vSY|}6iĭ^`AC6Fzf lj@Q(Wzb( |}NsE@D@D@D@D@D@D`} @"|b書'" " " " " " " " "PpR T|b4u?bU䛀&@D@D@D@D@D@D@D@D@D 7qOD@D@D@D@D@D@D@D@D-x" " " " " " " " " & h~" " " " " " " " " ' h@7)FM\(8)F ^*@ H1o⺟@ H1Z*PD@D@D@D@D@D@D@D@D@M@|D@D@D@D@D@D@D@D@D@ N@тW " " " " " " " " "oR書'" " " " " " " " "PpR T|b4u?bU䛀&@D@D@D@D@D@D@D@D@D 7qOD@D@D@D@D@D@D@D@D-x" " " " " " " " " & h~" " " " " " " " " ' h@7)FM\(8)F ^*@ H1o⺟@ H1Z*PD@D@D@D@D@D@D@D@D@M@|D@D@D@D@D@D@D@D@D@ N@тW " " " " " " " " "oR書'" " " " " " " " "PpR T|b4u?bU䛀&@D@D@D@D@D@D@D@D@D 7qOD@D@D@D@D@D@D@D@D-x" " " " " " " " " & h~" " " " " " " " " ' h@7)FM\(8)F ^*@ H1o⺟@ H1Z*PD@D@D@D@D@D@D@D@D@M@|D@D@D@D@D@D@D@D@D@ N@тW " " " " " " " " "oR書'" " " " " " " " "PpR T|b4u?bU䛀&@D@D@D@D@D@D@D@D@D 7qOD@D@D@D@D@D@D@D@D-x" " " " " " " " " & h~" " " " " " " " " ' h@7)FM\(8)F ^*@ H1o⺟@ H1Z*PD@D@D@D@D@D@D@D@D@M@|D@D@D@D@D@D@D@D@D@ N@тW " " " " " " " " "oR書'" " " " " " " " "PpR T|b4u?bU䛀&@D@D@D@D@D@D@D@D@D 7qOD@D@D@D@D@D@D@D@D-x" " " " " " " " " & h~" " " " " " " " " ' h@7)FM\(8)F ^*@ H1o⺟@ H1Z*PD@D@D@D@D@D@D@D@D@M@|D@D@D@D@D@D@D@D@D@ N@тW " " " " " " " " "oR書'" " " " " " " " "PpR T|b4u?bUwQU߄@h7Ŋر"bmZPw]`w;  *0;pLfR'{g{=sL@@@@ִ8C@@@@ M)     PFkZ!    @@@@@i5-@@@@@ F~ h    Դњx     vi?4@@@@jZhMs<@@@@HѴ    5-@`9    ]hO @@@@@ 0Z@@@@.@`4     @M iq    i 0S@@@@@@ִ8C@@@@ M)     PFkZ!    @@@@@i5-@@@@@ F~ h    Դњx     vi?4@@@@jZhMs<@@@@HѴ    5-@`9    ]hO @@@@@ 0Z@@@@.@`4     @M iq    i 0S@@@@@@ִ8C@@@@ d4#G(k֭֬[{?v@-ܺZ Q 33VZe͛7Oc+R?4ԭ@޽/zU@ |֣GZ Y`ԩ>|xhs@H@(*aOE+@@@z"P ֓n @e8z-ׯ_ re!   P>{WsnM`j= j@>~m;sG@@@@KM)  w/甘u@)=   -v/Nhݺ~h-TP 63&c   `ʺMF%JhX*9ɱw)ޭ[7{KmۥKn."*E?O5jTzmpYV   \h=?tNe|ի‡8'ڶn?-k  peXP   @`(}<-4 -GѣiHnR~~r) /$l2 iγ*… -//φjg}뮳yEէ}.5\c3f~Gvmnϟ?ySQAF;n8̙3} Pvf0ܽОy;cxV\>փ>3x`;蠃+LB!{OT=gu}6dϢ|{&MLC駟կ_?A3rHKl]v~!6~'>h}uNYb *   @ 2J*B"`=S~u|rWo|w_/Û/_~;v ' _|a{~HyN2׭[>s=}@o>ت:5Tci>o؏?hn)p?ن 惯_|;(|OosP$V7c?ogv)dɒrnnj8ڢ@/UpR%33Ӿ;<9 va;7MvlՎy>,-[b}+kU_@@/ /g~ @ OeI*v?l +=xv饗BC ]* nvGM͋:SQSY~)rwM7Մ+ڭOv衇UfʬTuʔ)֩SVAР}:A,;;;xb ^PK/d QGqYcF>}حj'O4^US%''g1cAQ7 |GO(ڳg *s5V֪T#x@@@ d#@mPQMkܸq͛7E+klņl0n Pq V}ʲ &Рm۶UpTA0^ FU[ҦMRAQlZeRF>U}-SFJ5CAaA 7|]}ʜ9s|2OT֫%тJN6FEUG%Y_oej۵N{@@@.@`_JJJpuH~V* Q PsjjΝ;ۣ>G>-h2;TɊz c}]Ab`|PP<=p +v~/lzjySӪҍ@@@r -7; @}PNCG6t~e ZjX|dŋP0VCT&>ޭ@G-kej#-ؤjRTT~ZjiӦSv W|T?(H*ÇO<჻+/6vX;s|6lJm+ҧ괪H{@@@r1Z9?Fz(pW+5|\sbjvAy*^{Z(h` |w|Fᩀ_Cu<-!ZI^U B HԂCgsȐ![E 0usVWk1%-8GW]u_=Y _N On]vc+gj$?r]]47R |$k_TE[W*#G޽{GmZHJs#Y;-+OѹUyŶ   / Ua84t`uB=Se2F>4OԿ3<3)~}jyi ZJ^MU*+Uj    @6lRMg#-{D[?MYr     @} 0Z(A@@@@1 @"HVg#վ4 Z  4l2F    4H i@@@@ц}=     Rh3<g-###~Kz~';O>N;ٕW^i&LoݺuoI'}7… )((!Cر_~Iz|6@@@@ og @^{5klŊvꩧwo쫯2~1ذal޼yQ:ԾK=ztSNowݺu?ԣK.I|[vv]~vM7ҥK3ss`F~mrw /`{ 8 }u֬YI   'TD`>RAKe+={O> K/䃦]z>#Te̘1[=5k,aڭgPUSQF[lak_Wnfo]po~#8ºvj}m&ɚ   2Fͩ# PQecr-e-7l3iP4|~7[oFi{?8 mj뮻쪫2 Ï-AQVTT=۴i7U&˭s][nmguFY۶m}&iP/^lzj?@@@ {] mD),(0y'!vx~~jl}>Ss*EY6mj첋7Νk~|lYz;25O6e|RdN=4_u6iĞ|I?U3|O<{g9@@@-@`n?ZX`4%IsjIl5Ogy*PS4(9^%%%qV ) A{M .l/t 7Xff'ZQ(0cE O    $̙3'i%/tw*RA:|AIU7oU|2E핍6UUPWR뮻? kG}ԷUV~]ԩ]d=~ѩz+* @@@1ZO G !Z]>ܞk駟bEw0}wogj>(USkNϖ-[`W\aG;ϦNaRFZ7͙? fD@@@6 g6!@ /1!ɂ }WGuTpogUVgPcKU@4^6m|/u({뭷6_JA@@@! 0!mq8@T~~躆kzla|g}͟?U9^)ЪaߢE }ƨ wym֮];5ks=v!*.?y믿j7@@@$@`!m *?\~i#<үF5k3CGW,Ah߾}'KPI45YtGycXh>~޽{MS.]|)S|T~ŘTU/{ꩧ|TC=@@@%nC t" Qȇz]?B%0رc}2Y}~*IA@@@!QZGCkb^ /Gs wFJCo?k@@@@@jNhYs$@@@@%Fkɉ     PsFkΚ#!    @- 0ZKN@@@@@ 0Zs @@@@jZr"h    ԜњH PK}Yk֬͞=;n Cr)vYgUk|A;SQ /`~%O> [bEmov o3gk5kw}w ͷ~[cC;CB\#rʨ}bFK.>}Sl2+((c3+~F{~ɟ3fQXXXmsxyّGiqۻxbر 4*Q.3&1@@F+nǞ PV^m?|}+ԣ߮4nw.;9sn(^g ,Hs}~Dos @@VweO@~ s=v9X.]M>Z:ZŽ93MP"m7pCT3+{kCu*{$:BkӦ}/s|T:u>v[V+[JGW9f%:ו@@f`G!޽{ՐoDudݺuK/o={?jJj('isϵ1cƘq]tv|嗗 kx~uUVpv1ǘJJJ6 .ZGi\r.?0d Uwᷟ?Ϟ4Qp~]zO3^S`}zӵz}F{yw4߳Dj>ۺuom];1eʔe}F%|  4Hi h.ʹsì5sQM?a/D)و7}O>!^{$As T>xp9SbĈe@TUN7 Dvygm-ԢE }7ʤ<}S7g}֭[FT4,]7 Pl2 \)СϢ~Q&*Te*#˓N:ɖ/_^jM^h zf&sPJm AxF WN}]donUTװaߓ?4e߾}}駟YApݬ';'fu}ƴi‡-9L[x5@?胚*[m)J|}`{キj*QKi. hmՎ}7hz)@ آ@k}0\I&#[Y#s}}O;4U}C_)},VjԨQWoHc*D6dL>rKSV[ED]5 W u]kve ֱU}%/Jz.g牾ЗH ߖȿ?{VuCkLs볡SO=u9`-b{M]w5'_Ϩd# @ x`55P׀h vڐ ƅ Jl\&JeoB. r N Phɓ'~嗐[#߇\0+4z萻ɤ?\ƞ/]\be j0܍euw\ ?6ƎLmt߾*7ï/Zȿ\0?9w:䓣肆!\Ϲbm۶! on2Cx]n/Q?V4\ןآ紭Z/m.1jS]rROApes?_w7ԡ!C׹~,\V[mw sn~C.rPpnd:j|[rTxskws&z2w!(n^KJp-E^ O{!=zc]\6[tNk$Y[]'Ce5?2C.#ϟJ~{ (ANrY!DCs˒N{PaYɜ{?!Wd/MYIY\p)x%|:_s{-,ݐ cF:r$ܗ<^u,G}wqA y{&Ql.\־>SA=41}zw}Mꌽ}~lڐ_.[=u@&>OSW,@1t?)fr3~W)s%P,BTl9Fk8@ (3GsʼS戆*GYxE+0+kPY$E):u*|YzuP)%rhsAO?OYJ4S& Fկ!Ƒ%QvQ2a肰a͚5Uhi } AƠ"Klhlg5\]uhTldh\y.= YZ>ޱdU 2")%rnEiȼ ol>:jXdΑT4V^yV9uȹu#3֪ښBI]z*S^}$*xOj2}]YLKe[ 4G>ӪvdYj,&joLQIRczU2zkޠ呴ly͔g_erʠT TgY_ej&l%^r,9YQʘ;=+u_hȩ8]U;c/s @Cɉ P5ALsi,%Ljn"oy܆Ļٍ\u in5Y1y)SP=SÒa Щ [n?Q[5'^+O.9TKz= :kZISiPt-*{xu+Y#X,}"9 d/.:=F~n,\́9TI~GAk}(7Y?SmQeQjQxo>S4~kͽYVhTkMЦSLzomE&}~(Vs^,o}y";}\Lz@@jV'!@=PL VĮltMA*e]&ZHGYj)ZYYSZICpUH2ͪ 4tCzhʇUEA"-`AG^|>@7݆j!PPI7 @jȢlMB4@RvHUTURVT)eH)lxAײګ VV&`M՚qz뭷2H5퀆+:Uqs_LZkGW_>}]ZZ(-ȅCעCnt<~YHtN(5e6iįJYR9 .l_ ,eU)pEܜA>0Sb\V"E &)0ҦMٳ`]Py>/(fm JEږe) dOQuy-tv@AI~ x+;X@Ǻn^g/g +HɜuYז|d4UW]?+ g)M gTt,} 7oT*w4uCo@3o-ttD~L_M:LO=# @J>{T@7Q-+D7Wbx )D]׍^x irۯhJb4S%r8oRM7 ji5\MVWVP7ÑEMAu#a ƩOA:nbB/ Me(SVg*(*tM}J2j?eCQã՞*(s ZjSACiAαjz^׆"T^(`/?e9*&;'yW/ r倲q#bKYmն +Ȭ-T}`H+=*RtNd[]xS.rX}y/E#l@e ee)T_狎ӫD׽^A_b/"C}٤Wd0LA`IAȇU̯nxIyݪ^?#&ݑ h ,*[LC+xolۜZu:\cUm)  @2%(7Hi^xO d @%@/7C铝^G@ (QC]5k<@Pwa:Y4@@5  }SaN)uGsXw-E@=FkϹ% E@FAú{ccܫ}O  P?X|~Gz     0Z,6E@@@@!@`~Gz     0Z,6E@@@@!@`~Gz     0Z,6E@@@@!@`~Gz    *ǶlyaÆ>@@@R;f"   @u 0@@@@jZwJh    T~@@@@uFk)A     PF[@@@@@ *};%4K /oꪚz@@@@˖ͬۍh@VVNk1E@@@ L>܍8p=ޯv 0ZUԃ    @hժUʽ2dHV׆FKz@NdwԚsl5kc+G#nl{F5y5q7n֭]k&يXZrֲZm3}-,]ަ[r)7:aE#'+'_pNkӧϴ1cJ:`;9\Aft@>g#@@@/x\]ǨzY|$@f[XM7)YT25E lmYXs/-ewbwۼf+>nٝ7q\j+`!aoI7_`.]:Eؾ};;JpI^{Z%Sΰ+.*A@@ LO+ѶQܝ(h%˖_SYYNm`xڙӭ,[w+KeKm̈́qz*P\\bp̴vY駟vڅZ?#  Qh}< RXmC6.;(u}e4iRje\ة-k_g7ήrt.;_i7ѣ {n3ﯼ >Փ#<.k޼ =V!Cv/O4~ۀX۸KnM2yi?W7?ϦN?Y]f'O?[zh.{ݭCbJ?EOk|Gghm-]Fe>{]wݭv=C 7=ƛoum䢩 zW{dEEQ9Py NP@@:"x)ڶm[ZL`֝] SWDU\`FV5jZ-|j.˞"~ebӾ5˵ .8ڒ%K]]y>cT"l_cm_}«w"KN@ r<ƍT-Z뭷 Cn3f̲Nkモu!hժeŽu*jiӦy_M^7|oqAmXg_ YvmKum᠜~ v5ثcOX7:whW\ζ{y4v-\g~TO6! 4xEz!0`Z%0/Q@\.٢w[T,MwڜtJC~֭*5 ~J/! /j :_m_ ;H,=~i)L}I[80ϠDWO;xо].+Kψ])9lG>kKϷO>ܵպu[ìYs\rr]pp`4''.l{w o{#gcǔ3gGU'2N?ol ~ty)!   D .Y~j:Hk[ph&T@CmSpD }̭LgmmN)8Z>nl;Z4w_I?~_5 ěKUMsAѠ̞=g8(اUe\j(ر?!)+VpY7$emL 3vնj/lڴix?Ȕ&],77ך2A0x>fKߟ>( + +>?ypPT([na?4!3#R"   4i+Bզ!FT@rwۚ:癸׭ߐIjVwp5k?[هq!77}[fuITsc+藕ը\uj>+eggݶ~r+3։$4vz:;5'tP/C5t^zo/ ZOZ.^4 (()"k@@@Ϟ=+s I`9 m-ز^)Qd2Xv]\˄?iY\s<칇tܞJZpJvO=5$i^{E3۶m'+2IWʷ.]:E]@@w;U_|lc,U_e^Cx#6l*ϭZM0eŮBd-#-3gf*Ꚁ>^W=k{';Z>mw~m$=nAהQLx;/LWy\")Ӣvi$+} vu@@@'&"@ d6kn9ݶ\Tjr5nɖ8ڹ9AZPͷ,k7kT[3a/YdB@i_ͳ;t'ik~k%+\&i|n?J:u[}?ʹ\b4Һu[8('yV2mE776|Ǧ9K7۬~/z$曚믿.:ǭl;cmzD zޮ"}j'ͧz}UuV5win[?yg{;Zx)}q1?J`4嫇 @@!CT ֩Ec@ zyjc[A~᠇-3\MS΍:Q#hPb}N[fV*,g؊x+7{lQ uYOjOY d>n+o?roj?gP2Ce}8-[ZLHC}7mȐī̧܀jPry䑻-'}ctٚ:/o4/*5r(+..JeR x 77T:u+ڧ|6D@@ 84x@0ӿSs"3:]ZQرc3L?n٘atG6@6rJ?Յ.{0S@@@ Z`"d.cKRx;찤u 뮻Fu,vzhȇ#íNz46@@ho=h>  @`(}:@A惘Z@@)@h:96    EhZ9(    Sh:96    E9FA@dwԚsl5kc+G#nl[y鎻YvnZ+6V JV.o~նBEnmwd⨮6s;K& 7\e    ( [UߌU_iRTtdْk ="龭L_?];suxtn~}-o<D֬)if[o}`G}k_X   u\4:~i>TR P(*(͝(uƒ__hdO]vgq3S+ً{AA7h8[   9 hޜh7$>#+le< Noզj|n}l1"   @ apu\?[ckXOiں5Mg>-̔˞ֺV`^8 뤭AhfikF@@[ ["@ZzemoK_TK Ltlkۺ5 ,I۹U Mnq&lYɊe|ܫٳʕclg   /@`9%/?gKպ>tŘZf{[WԢL/-;Zuc"{㏱>{?:}   uB:qh$[ g,)WpyC* ,_fY;JnX~}ƢLPx=@ff]we6fX{lvu@@@.@h>$j_~~ؒѨQx|ZVyn6~& /?rmqw7_i[h`26٤u~~eZ   e 1 Z YJjn[4)8ڝu)TZP]@S 'ewfjk&%Lu6z7T>ۯcRK0ߧY{X--9殟uٲeUQu   4 $EH,lS okgͰf.)FUr+9ԭ@o٢ lb`+^1t1nu\kAq ꒗\NQ%26,nUj@@@ m@'"@im$)qٝeKV,o^>^ezPVql_aqqIVLm   V9Fc @hxsOoXat^"  TBJ+ @z<n|p   @*FSQb@Z)0zvI…VF!  N*@W@@@+c{@@@@dSH@2]6`9oiQ#lȡȰ=;f;ںkp$[1+Y<]#YVZV^[ mjGk%Kmo_xuܦ[+ E@@@r -'#@l kV07uRp+<{slk-?{-xA };u_[񒅖8S۝{-x~ - mKC͵X*A@@@'&"@ fj?@N{:Pȭv>сVlIE YYNm`xڙӭ,[w+[LM={/cu@@@@ sra @XBQAQm\4wߧQ^__h$6;a]д`M>@@@4M:D-ݩ@Ѽ9ucN0G '&ߨUkkvC)    P ysuY?[ckXOi!JλEC)    Pd֬7GC:.c,m{[zR8e >fK_{os٤i7u U)    PFkޜ#"@mok`[sVtq^[HW_)uk~Ų;tf}h6   @ 0Z?#@jb+{-{U+&(HZ|eXeZtIZx    0wuH }'?,~[/K> `5q7{ͭر`Ae]fg϶zִ)QC*cغuk{kc~ٷ~k }F~뮻ZFlĉD6OM:{JI&vyvmg%%%vVN*@@s63O@ l5ts+ flp7-kŋز۪?&ݷv^bY[ ~yV|:ܲ;oⶹ2焫lul>dbks,SWF-^ڍo]@RZ` N?[ݡC֭G՚6Ր0T`tw F HZ'4O>N;?oO=͝;.w'|;~g;S=쳖9**@@,@hOG fjoDN{7o IDATjh%˖_SYYNm`xڙӭ,[pFhv￶1_m>:ow5nG+7; 4w/4@޽~+W֡VWꯂcƌ1em֦MVJkޛm}wL0j0i$[xٳg;v9s؏?X5@@ m'#XlP(*(4kpukncddZpWr 9 X6Kõ|}Sٟ*sf|ic9˖Yѣm> 3=77(]ۢv)yHf\'oϗ7lnm^wd-g̰oP컯:@[8?ڹ@ICͪ[(3ںl]okeK2gk̙wŬη("g77(=*M`[ؔcU]Z^q=AɱwMq6=g먝 *vwÙ3{;6S>ڻ]v)( ]efv…ȑk0UTi懛+@@iKwo0le~` ^R`W_m{mX7}9ìNa=j, 7/buR7WoytM)ֹ쮺\K/dz^C52Ok/k>/ے ip-5o]~W_粈XU]}{ڈxy@@ Z\ @9E|fY:X1' o,w׽438p|X=nL9[h2|S0OP/Lk=ynv'ָn>F [MnK ,ֹxI:'xqY 4rTRm̃'胢|%n~o9'n@T¼<< 6vK~l y4r~*JU+vW5sF6,^{TS-Cs+F-\ V)Sӏmnl{umZ{TRe*kTٌm\=z*dmۺ̳.?SRmOPg]f }s}zUpnS'Ne.q (P|C\PzISEuHA Ym5 gT:Mm,jz~S]u-q;e($$]\HYT&*PrUQm/^ [*lr[Gm^U:(ZR0TٞvԐ]pPO?U] |k&U{߾}SP@3(_:ly[+YWo ڡƻLpf۹kT597wUwv`RY(;A-/[U}2YywM6%cx @hq~[1=ƲڶO=Xj).H712}5߯9[hm3O/[j;am-|1+^8ak#>0o |Ѷn謊c\TJU:}] *4YQ0E4|j'7z]\v[ymo(CS_e*Q.{RYʈ s*ʄ uȕUJŋ}t ^աUֺ穖^fh63gYgxYRʵO6+7ɕrMWPqmUpPXsVWQ@PD /t_ %?eEwp׋2m{T P }QjyO|]i R&LLj`8M7d7py6h _S֫JȔ~S;2XU}2ggM7~Wn pWU߃k5rڃ^: 8+MA@RC=H77܈#DYh)G7Sm\n4O3骇t)B wym#&]InNnf{g+?2W+KԉOuCꏰ=uj8 ,s7e-0/fv}∬ȆfO4|jHW_m\rIyj}i`J9[ley}OejS6hgIK/+g\&^df/^4߲7lRC(J@-eiGe)R (REQ$5Zѐ f)3ҟ8Cug`'bzZ۞ ~ mvm `eUr).ȶ"EsC,cxS[lV;pSt.Pzk+TE5UÇ,b-LI.8Z]QWxeo7255: (Z)NEY/YII 2j xk* {wgῘTJ 'Ui4׼E T37ܢ}ľ4u   P9͊_c7Z`[0c7TEuC&J)0Ҹr důce3ˆ̱FܪmL*%geuq nJVl\e܍c s.28ǨEq2EP]!ȢgnϞlUٴJSR~geIpfaKYZ-Py-z5@*Hu' O:th.#R24719=ฮ`h!/ͩsa=^x_/:Z*eX) $N1{Qs< fT4;(ak@Pv<^ A)(1nT6280pcZw͉"EʠU{H  #@(N@hYVk]VX>ꨣAnn_v~-;_ Ys~etjEMʷi*κ/T4w-(,Z0ϲvN5YE:!VW3*%KXӝv.`cVW}-԰y0aAQpJY|\Z^SEU6#ʏn!d ϭ?lXź{k^JZtIkTԫ?Gyg8ڭgO&opxeSeR@l d_֢C?ޯpi vx9?@77~h:\-j)I, NX,͗9uC 4WsvtnVc R*hyf5dUYS7'X^2u37,V,Tl;4Ǧ2%wMمZ )hXZ^Ÿ||0|NkNPej:} yGYCO;4 U TsoM]S 'L]MwzUbom׻ͮr 5ʿ)4=RwE9}TZ-8EA@RC ] s9CY^0֠ZA 顛7|׿$=lez^ |v [8am2?msʹQ\9jV_u,TX`kgϰ#o:K^b-=Z2Zⅶ`bj7uOfZ5$^y VW; 65 g w!pC l.Ê;c ww-B~4I&M!-i![N<wGR"gG }&ɿD~US!8 Qw~AoQAE0aBᓙ`L 0&` gvsY^m csA Qڜ-'@{8I@vxN`֬Y;Q1xsɂS ߟ "d9WWC5] ȃ Cu O\p,XFAC5U5;~xm`B%aM٘`Ǘ,Y"+1{%J+Ej[رcǨ((11&`L XGzȺ,&`WL\rQҥd~cj:ujʔ)!b*9ݐ%7\̑MW؅jldq1OxZ"?żwQ`B.拾agQ9@ ŋ%6&`8`A ſVwM;bL 0&@"hL^eP`bJvE8'"'as9r]p:u$k3Tm߾VEaxb( L%Jo^HB\ȫrm {#8svJֺ 9rI\m%uZѦ+9$*S%ΕJ-'g_Jڲ3a E! =Egra֡|AHH9_BNXJtԿFO-Qծ]n =&}C'Ç'GfSP&r|7|^Q|Rԩ&pi_&S6=l'w`L 0&>71wlQv mDQaZgϞeDD>}Z#UԩSK,)NjUb'bHm!*W^BP*x0?(햯p4KΚ@A$k|M>2w(E!3_L䖿0{#sL2DsL hf7[w}:|?_uf׀}G˗G=ذ3'VIΜh)lʙn۶[nEYfEn^ʕMzܹf^Hazc=y.]F5kVҞkF$J"?P/> 0&`L XtiZ jx$ݫWQt%˜JC[E}:J麯 `{|QM-1FrB`j"qxKdO"887yW-EϏ74./\a.8:wE=#=(~J" NyEEϰG uJ" rݢ ]^z$iM'mֹsrvv>gOCw>!C*RLaSax{{"JeND"RXQS爂vCă7>|2Dʕwx{{bu+ٳ,YR. ;9ҴisSiڴ>E/Kχ֭.5Do=zt'UR$ 0eΜIRt:tőI-ܹ&sϪeÿklL 0&`Lb ex6'9sf>ϝ;G*U8cJ1KBu~yOd&mӞ:grx[ IDAT_<">hֈV.S Ӈw'OqxQmEy{!T2,S=p0 JwX ä4(\|,DXSQO:͐!T/T+j() إJƎuzH®!zAb;V\`8^O^B~V|n Wz;h׮mi#tI!^޵$<bFtVETQ|w֭ؒBSֶC1zjqXx8Z<@{#֭ӅDax6n\WQ5wj`A6Hy܆ 0&`L!?Zعs<[¨-(r@…Nm!#|X⋰EҰRLc5 Ԅk1s 6fwŚ49=?-t?޼&٢y@~M (kx&yL!QO)R?v~4/]bڙ?I{Çg%g^1CWzMv c H֪UR5e)NrexyTXe H7t$YG>M=Lk>|(6(5KLxPכ6ׯj|`L 0&[\n' \+VL;۵;yڮn߾[@z(*D[t"` Ν;DncB2eJm_nbYEȣD9zzr>Hz?wFcr#oKj{ ؼZD);R(UQǻ7^61ci $]2<}S @Oۛ?D~Ŏt (Nd@HK2\T׿ZO/_nلu8U@_*ydUiUc ^$ɤkܸqe>s6,_tھoU+VО/ٳGK.Q᯦&zT4crƯ7)_Sɰ(h떅D9y~ !&s4q2,s_ K^ eԩbޠnnQa߶~EÇOȰt[Jpzcǒ%U w^H䃅A$=zG(TU}Eq$Cŋ՛2$Z zG)._NvEzP Xoތ&M*97C/S㡰8q-4n`L 0&F6$ gϞR05kҕ+WT/Ӌ-")Zh! ^:f̘ŸK,Ѷi׮]/| Ν[f1yMTwv;۷Oy񹑣%ۉbIET9bQ\ 'LF5 =LӦWG2e<ɒ%H={Fׯ_={ Տ_x!M]Cxxy޸U5B ԬY3ʜ93Z'BbDSVzu~rʉ/DnR=x(1G]ʟ?|-1իR5_|£[ IRMh&c'~qYj޼+{{j=7`#d ~oS!x7Dx6Q  r&o `L 0&sU\Ҧ%4T>/_. J9wġq:XR~e%gN0I7D=C U*C`DnP1Y ("*0y~~~}"Ȏ~1鍉>!,IFoBzk !Js KpʚJIE2)dTLq(M;aL |V~~ԃ> 0&WE~=I`L 0&PICUfL 0&`L 0&`L $\|)% 0+IGEKk,G{G$'x+CI$))"$>޾NowmwoVmJϞVq/R}R&n(Sl8_&ӷHQ2Μ9GInݵzKQDzTer?'>M4q,=EI5k6)R̙oQ],^T,k@2,ߊ֧iynIǹre9Pm}]|i[l :uv[˗ + ?ҽ{DݢhFa՘H0ry?kiz}.5͛7WoB/_jkN:p`7m!O_p}uh] 08jɓ'Sr ߼yG_AƆsUg|2`L 0;$¨n O 0OG5}&J.#߼JnDA\>?CAc7,UBOH3V%" )cp|j~O.iNLAn<ɿZ/i³-o//Q:hɒY(]?ŝ!7hރB,DB~>zDQ) 2Vq!t1ڽ{d5O{wŒ :%խ[b%Z<>!8q"_yƒOas.K["yv1"C5s[}MQrL`LM%ܱF-7fL 0& 07p=1xx歍{6Q6m~ӧ uQ,]x <̘,'~ Cѣݻ- k֭ZQz)ffK_(xn>~Tx&dΟ,<',ҵk7ȑ&OؤIGKy{e}ijewX?(m׮9߷>ٵk,`L LQB ՋE+W/)EDx^V]spMLD}PB`}W^,0D«y7l'NSH.P|;w1~wiTTMm?S~LEjD@:uI/b˖ԫWG!\?:M;9_Tz֯-^RlL&E}:YuLN8C7nVWif>H}^r^qG\QP{ڹs͞te̘#dBQaJ֭-URN\Ot%KEzM4"]EEƍ[߁_ i[BUx6&`_y<&=Tώ'+עb9&%mWOTtH*s /> rr%&WXan~M:V{{{pR3g$JQϞ|2hğ _#@d(۩G HC:ƒq VΝ˿M-[6am۞Rܹ5Bt.EJQb| tB`@zSR3 r%B=сM#=n!رWچ6q0)g3gN gg>goB-9͓G>J!!Z*#G67 4FiSx9c&Ձby*!If ʵ۶|2#B;v#TŦM-Ҕ)i„={bZ?,BгQT)KK֥vLsGw|B\Wq$e̘-z==SM??0T: ~uG߇ ԑ԰a; 'h(f>K*&X|rKc\o{ZbOh֬RTL͜լu~=_ﺵ|n`L l?ΆgL ;%S wK%J\墧3]c= սHiMnQlL .@ i۶,`JϞ=|62WWF~5lXGӃX|c/y)c /;~p`%,s64eɐS)u.Qب4hPKzAD^CxɓS&K94_/!3\8p(̤i u&B,Y(=x/"E-JFDFNsJ**C[WPŊeK3fQHʘAhV ;vJzfBptb>|n߾Gk.^z)p`PCd0:ԆenG֕+yG.魪@(D1eCW1(\oGh]j3&qbWСYI-y)۬Y!l x _>|w<++KxjA9YD7-'6Xc1( LR@Ěcle"p !YڭY`Ls`as籙w+֔E% O6(?E7_$%B~ ( .Vx'@x`%0 3%(k+rvss4Qg+C1xr9keeܨ9hnk:PI1sǥbja^=Wr|o ^&Q!vRx;f3\EE J 1?x=RGf$LfN6l9‚>H9Bus2"}@N-b)+IJa O/׊8DBUDQA q I010ߏ;-++fn]c]gS^7fDpvI+y{^|YoO4x!Lԩ(/e7o4 ba]!mv3=p@<},6ȣ 1Ҙ'&"SvTW~Ǿ*39!#CP@MhscsQ< oxx޽ ڵ~-,AghOW.ckk6wT,NNiF~B2:Qy5w0ϯ4;G9j̔{~)5Yծ1&,N<kOFm:UoR{n 4B/u"v '/ @E^9su TZ^z͟T>A4nG 5᥆aSTn޼#XO\mh~hxpŅ]zԩ*TQxI+ J. 5wwhڮ]D[i52ܜJ,*=BɦY9r_2O4iR?sM B8==ݵެ  I[ }Y2VL*^xݷh1=pLb`nM?jYNy@Ӻ `mmǽ;Ә)a.^ xLz}`L 0&`z?`LpN(h?{>S #EGf ytgM4a.҈>V>ryF"tEK܃%Ucܒ \;Q0$Xt xڵ Vc9aM?jƲ {ki ޻wgjҤ$ޮ IDAT,*X ]|]Vˆgg Eg`3PEӕ[OSV⥃6f\ }2q_z>0pp<\X/pzxrEoR2() 'CZZu!D{{WECc5R:U /A VWΖ-|Pxĉ3kOMoz@ ݻ}toߕ#7;޽{&s{/(o Б#'|y\jfOټ|ZY 0&&&1&`W!fNKޟ$N'kI }PX؋RDzJW)?5n#/զwȳ|5 {/+&!K-<q7o5Os˖k5ZjԨ,EkTWC!aد C6l{P=rn?~2TЊ{w͞+6hܹT[/^OH56?~,F5dHo8vt)n@y@T>]WnpbZSqh!5R9r2 W<*sWD+ ypݺu~/q- W  !BuNx8++WnsWU/oҥ)(ֱcskW3K?I YX7`(C=^|^?,]ǼyK͛79\_IznC͜լ]ECclL 0&>vf? xL&B r ~O{GT~wgϞonv{(WF:#܀ 0&`L NڵFzXCecL 0OΝV-"m\}~UTl˗/ .i?q 8Z)7;7`L 0&`L 0&`C32 0&"<`L 0&B ܿd_yL 0&`/Xy0&`L (]̓=zRacL ~H+j׮Yx,`L^0j/;`L 0&@#pq@'pCQkj_( 0&1`Lx<`ZyDJ`L 0&`L@`a/&h.iґofpj hJT(^w8RM ZInnȾЯ ^g' ?7Q5RD ϞiM:ticĒ%P:Uc5'wwUUjNΘ1acsWر%YDM&dʔЏC6|pݻ͛JJx3g%Kfdźb5trL{a͔4G L7zj˖ i)1v5s`L 0&` L $h3Qt)U V!yUMa/QxğgNZw!H%" )cp̍A>/r9zJ%ķ"rAF%&+ry̙+LVmzREvju1wA֖9sz):8g߾iƌ] M>ֵ0K|GAԺu:{Bb]NVj8+iӄcS֭JL(\k]iv>܎ 0&`L $8'Sr]n G[ҧ&Px+{IDsPzxHo^b+Hr19Dքػ>KC'jMԥ#GND'A`-#r[wѰa}:Ӯ]{EΝFQej]qs_r9!v;{ޘaSu\#`L $\,&ܽ1&ф+mcהީN޾^=!Zuޭ̗0 D$OJoU 3>6C:Ҿ-ZX< "k7-(IT|i?>zXڳ-[nݺ#ů~э6T_Խ{;*RH޾}'"w1ͥV*ԬYvރ4k||Źk+ƍiM\٩o߮g6 z/}4{B S%KfQ޼9 u.5iQwkƂ0XF%ܹ?ݾ}W:uJ){۷0M:G+-[B%ݭ[;J&E9r2zZd:/ػwPÆ)/$#Z|-m۶[ѣ=jX{ޱc;WҐDK~=$۸5*UʉkœΟD'Ϣ;wq _N7"[ϔ)\y'4n4 37 fD...›vdo&;V4Kr2HGׯ -{ѳgTmE@ƎBD-f<";~MVpO35mvL 0&PKCՒvL 0pI-#=&ޕkOR>8!!8L}ǧe!Z(XJ7o.);w~~R+X0 >|"!drd{oo/qxܐ+5gbP%0]ӦiĈpР1"/_.rvv6n(Xmٳ 2g$) :~/_k-G-it5;=kf,CsV`\2cĉ?SBiѦܲe#qԶmOJ.W[[,vEr-+Wn0y.:S̑"Q-[J1ylh}dϱ7Yfֶ8qA1dX)Μ9AYPPǏJVُSjQ.4n۶_C Gqy37#GV6md2r)Zƽk-Z|/ޛ/׍ӧQ[;T1˗)S\{J$ϟ4>294M6jyr;&`L %jIq;&8:!r<K6fEOggNLn8yK? ͢*UFHxzS;4'gY>'׀\AD+U36kk׶~{!i8BNӦ͕wDu1N# z)\˷ %NZغuyPuTAAW+9CtfSKQ6#4X~" a _v`gΜk[!qm[X10P.ۉȱC_Qr AR|̭Q8v R 7mܸN0kݾ/]Bs]tWMC/ow+[LRtUgl5 2W^db+ __^YÝ_6By f3;_NP,lUZ*EB}Mxa5Q++!vmQt իWCzҰ&СCǵCz:qMą]~[& VQۏr@>rsw+Wik) sR2"l_CQZyP W x+c25m͝gL 0&%cZܖ 0/["Qs~e$월9N\5G.UhKޝ@u,7 mjȩ()xz{k<_T/H` ;|F,+_t1j`Æmz޷H5Mcg+\4i ^97nk P#ޝ0lz4*o;/88XHMM棶E.r.6ZXն =i̘"t.˜9Ea!׮]Dko(j`'j>G6W5uex=zT42ʽ{kE<7露hfL 0&PECUaFL |"&(> :},gOų7)e_2>^F޽"\WPP)^^WWޔmt ^q̍uYGWHۂ"=u }W)d[0YilX_s̘iԭ[C;&rjf˖Yxe>uca]IknbO4U%u)Fs2+j°vMC պc|V`4ϫ[-(6mYI^ ӗ*ULHصk?E{aiҤqZ2e Q Jޘicn>`L 0&,%L $(ttBT'B#>7d(|g=^}:+[ڏyUPZA᭶mBQyvq 0&`L 0&`L 0xMx}޾Nowmworp+X|6gO kE? 2/ c2 EDDJt9ZOuի*_ށ*YwܹO&ʹzKN:uݼy_ji6ҥKC̼ut5U#Ae#&m߮]3ҥ|ݻ@v&-_VO:c2g@Æٳ 7;\-RRnOdL 0&@\`a4rL 3Qt)Ur+pyUMΟSG1yHZwgQdX:%" )c-'^I2/"HԩFK̢Νի7,&  ޏ=H#GN @=_Xܿ5'̙+L&l|N,x u&Fs.G/}|L4}X7<`ۋBD?^n+mی ? zL 0&{,=c 0;&x0?(g薯pFY(<(iN9_wDӏ7IU,ǎԘG͛tQM"<[ϗP{=t}[{B&uȑ 9ۧ%CiU -.fxnݺKz{Cgڵk_4e5cOvg/mohԸq]F6L 0&'%'̓1&`w""bRd(ơ5w||u%"ټUW~-񄘀TRƍ&NڷoA^^R0a yzzPDUB"Erzl  vZCҥQŊeޘz9sl5:z IDATkfۅr.B#d̙ŋWNRBEjwNcR 4lIzu$__heK~GiiuCׯ_JiӦ_-Z(ݭ[;J&E}:^zm$IL˗Ge7\<ʚ5y泭_tm{6Mrrrh1c>=}\sS׮m)wb/C鳢z+W."Դi} lSBZ5wz2eHcP:=|D\jsRUU[I_ɓgT 5kƏJGO1[v.^ kn CNZa(=~iqOBc&X‹2&`L@ .aL pI|D?ѻr-zI T? $t Ԗ[J2Azi%0oB ,s, JQϞbA?W2fӧC9'KA*e~ ,X0]!;M g@DYds?w-];MkTso1ym˖lj垄<;j]y"]29Ҵi(R<9rGb9rdT)^Θvyy~ސ4iC-Q['c>R@UlڴB+EMٳgbRk۶)jРL]0r@K Cݿy!X x«v~ <^RTy"# .hw+ajx  )GeLpPQ(_400C'lL 0&@'_X0&` !ş (K<}R?!b^Gh3Տ.'lL@Э򻟟94a%Jy@! iϟG݋D^~9-ШQe8Qg+ޑϒ%>Mo'yd&W!CQ&p=ݰlKoШܭsRD(OOxΝ;Ez-hE 'JͭԞB K={$oK-88*S8YڛヽH(ܬ7=q/]cd9sfB+V,-'s؛ĉ]g[pq1&`L0jOsaLn fJ>Q|TE:Y rts-azQa"h](F.Fb(~lU۝%B!k,YTz%,G? ȡp혼u6Xuqq"c{zkYYsԬKX xv: ZsdEa %L<¸3g{Ӳ՜ ׄ Wh&1&`L^0j/;`Ln 8'OE~MQ}tXy?{>S ;"??nĘ {۶ݲoZVWrQB2 .^Hf#GN OV°k׮* +xzfȢ'B#>IZt {TBoX>RFj 4糂'$ J2y>5a:q ~Z/_.g)SQƽOVO+BZך5^Қ5;YXJrXaj-S8HԩNSS,"ݴpDZV/#u]?o2}LF=fʴ{QAh34Z`&҆ +WvKǏ6Fq\{y;t֧Ogn0v]&AرӢ! " uL;w}ª$< 0&`L 0&HXXMXɫaL 60xD^zh<ո$n\徙`L 0&M/{yL Ky/byNNNt{(U߿qŋpq;w`L 0& Glѹ1܈O9݌xbrrr/^ҥKTT)Zre3Zhgk~ݛ~8~V *РAdXhyW'lݺTB޽uرcH"tirv]&Zjю;jGQ֭[q:w`L 0&4f2&Eؿ?}wRPYz5nZ @f5k۴>| (WbE'OcǎQԩI&ڿxז>}zZ|9a,gϞ%E 7n9:{9e 3gX4z*oߞ ~wuk`L 0&H8XM8{+aL At(s4tP;wnH͓v...{nrssu!z9mܸHөS'ʓ'O^xQZPUއ(gFȑ#rN *$ׯ__Z*!>E2vrlח,Y4h@|ŔBBBhժU+kVjՊ<<<};w,YBgΜEc\[9$G ;w5kJO>s]~MZX8~֬Yqֿ1@7𒄧lҥzq2O>k'/P~DQc}ߕ+W ]t… 彯W_*ӧ޳gqOW[) Yf5y`-7> 0&`L |iԹ|iTxL 0A-3gNʒ% :t(r{zzåSi!| 6>>>R`jhrbDTŐĉTlY#o"ʤI+xDΙ3Gz!)C///^G}QΝGm60`6t… R ʗ/U&5q[@;2(2@ĝx)bd .)݉&#^BJ$U,k=}g眽^^|w~6>{m$"F8~! R0G(ft?D;ٱcG~Coo.?lp3=\Vj{aKzo{ͫ LLXr??\xᅩn? \ﴕ-.|DžE <`#4.(o*C6w=\< ~{/=<}ksnj~f׮]y;#O B'\{5\\zɝY+8qsK!vė?CQ/"p\DA|;@4FLmlGFh˛o9"楗^JN(wy?￧0hŶ ZksφZӖPøTq~I$\U sn3NH1mi:M b?h?Gޜ{ (YL+ ]on({Ў2SZ041ԘK@$  H`] ~]G~K@pH"t'+LN.J_p"r.bB&"n_|ZlQf,(kC9S^{ٽ{wO?m>`JS_wuI,=I"5Fl-co;c\{8CF^yjܓ N;-Q? =@DTE:i#ycS1{A|E(ͯq8a7+#"=13.m~%I˘q͟s+ATCے׷rjzz$  H@ցue( &Vؼ!l &%9SآW8X 7"'"5iK ^"qr{7׆'#15pRnUڃ+=Fږ.첔[oM 8،P˸⎌Cd{;&[Ή9 iۼfbp1{G"aGp Y؜8$ϊCGXca+?sq66}s{q_[ B>Fhy }m{ H@$  ;u_܅8lc/ B ۥ0W !y!i[zq]-85ٞnNR&%&H#4I|HB1,'QL^pǫ:mG(C!<# (g>IOv[~XLOBGK#X<C0elqϯk;*c&7#0#_%A.^Axe\ʁP&ڤ H@$  L@&ЦtMatJ#o[%fʸtY믛۷od."|D)ΗI@$  ,Z076ҩ?7?~U[߄ 0=P9a^> Cl$  H@(:6^tZatro%ʅ/,&ՙg|g;}~u:fOF)(|H@$  H@ Bh[!*Q$%XD;]Iwjѱ鷤t:M_$  H@$hmyJCV=@M[s_Futm$  H@$  H`qzɚ% M(C\6$  H@$  H@$PJߖ 6ZCO;4_W=$  H@$  H`( 0U[ mNRѡ=O8h NQ?mH$  H@$  H`)ƩAYat)FH`}ↄ8:d[T$  H@$rMK7(I4՟/P!ЕY.%(?P H@$  H@f&ЦVPy8@گ>o H@$  H@A {Ӷ貭NE诿F(]>3ʹ$  H@$  5m2+p&!Ʊm6/-`R$  H@$B(1u>).H6 6q4DѰȇ8{m?LG97g|\hQ5+ H@$  H@y$}VRDkѸG[sm[H c)J` !hgGoRg$  H@$  H`6mh !ZڶyyWK@3]Tk4WkBf,DR{%3K@$  H@Z !Jdh6Ka&?Eat-%`*R A4R Aߥ oϝȪ05s»J@$  H@#D\9Bbt)ܳG ;HZ[Ɗ *6X$  H@&@`0:D2L9> L&(U$orhI-N 2}}$  H@$  H`+"iK?||+So~S݊=% *_-"g^A,xhmZQ'$  H@$  H`Qy\-ZkM ^+.~$+KC,㋖ h$n/cֲ=MT$  H@$  ̟hn~m y*%  2h0klϳЇ[&5/$  H@$  62'sGg) X1VSJ`zrh3:=YQTatg$  H@$ ErGܮ-e}y?F=/ T ѡhW&Q*?+hm靰$  H@$ 5&`mhQ4W8[$0'5Vu)bk!n9 H@$  H@&PFsr8&~jAPuJ@[F5 Y3fϓ59CϻDѾ 7$  H@$  L@0:D#m[k\m$" ?ɑ] d7$  H@$  H`RM'mz66VI/K=rr ~H"hԷD$  H@$  H` t99LjvWR=Il։@_24}W:$  H@$ e"ꬹGmՙ߭4l$e=s~W  H@$  H@ tnC]5qv}g [$0؟CDS IDATxy\Mm ebHv1c!d,`4}1l_R!Y!Q_{Qz<9ss(j   : AAh8  DYAA ΂  `pAA   @4AA,  0DZ :-oŋ$%% xzzw>?7^z縞={fPoݻ9}4&&&;w.GϟSlYKbb";v䯿z4sALLLxLzz;a۶mZl~FVZڵ>d?瀀|}}-yϛǏ111ٳo|_S-J6mX`>̗cy|+/~~~̜9Sgd~,ZwhҴl'''Gpp0d{}Oozmӧ|ݛ5jӧt1A_D#G(^8Fgrq BBB/Yv-~T,^aYd PZ\ġ$%%$IzCФIݛGݽ{hj֬)*xy]懂*ۂ˕+ǩS8t#FĉӇ҂I6m8}4͛7gŊtܙ{ٳ/^Ƚ{tƙӧ >.\ȩSXv-kצC۷sPǏӽ{w;F׮]oqqqвf޼y)A:S"EhԨzԯ____6mݛ޽{G r1$II&ѿ;v,rC1`lllؿ{K&?8::RB|JUzH_)R ȟquu]:C2d|||8p FFYVZEӦMu^wwÃtLLUVMMreON :JA}yfJ%ZRBO* V=˖-c֬Y̙3ƍŮ]ߟ н{wٳ'?3:uݝNgF$ꫯpqqaĉZǏݝcǎѳgO<<<ؽ{7;w̠8K͚5#66V>ٳgO?ѼysF ˗/Y~=4k֌Kj  ]hرc]vq,]Tc;v_Mdd$̙3UVabbsMU6l u%y7nSlbb±cPl޼OOOjժŨQ8s 9 $GrJprrLJH;:u… y%DGG퍉 ;w`9n'W\Y2eիG׮]9tV@Zl)w&44//<Ԑc?~=z?ұcGYl>dʕmۖ-[uVǨT*8@~S'N&ssY|||Sƍy]W/퍓]ṱl_}4hЀmGZlիꔼ5ȼml25k'۷o'##C<;ccc+&{z-{ʺC>}QcƌZ\pSF |}}zjsN j4/.]Z~^F_No׮?3gŅm۶iퟘ3d-[Z6(/Ax޺6\z5* Ү];zI6mZ*K.N:tؑ۷oڵFń ظq#f͒ر &ХK-ZD||~\wV%}%gggEݻki߾4w\sZZTdIiZ=*R||&$$D[nIIIIRrÇSSSuJ+WԊΝ;Z޻wH/_4+WJח>>gOOO)((H Y qqwu}r;[nIt\u]kǏT**۵kԤI\9vIo~^խ[W| ɫSN$@qㆤVHZq 0 w+kiР|r啐kd-Ǐjzjbbbr^VTJׯ7~W?黶jZjݺ4k֬<)iժUSJt19܃$@zXa-6^GG ի<{w$oW*o^k@ѢE*Z\===ٴi:J5Nڵk`kk+R ժUƍ9zܹCZZZȼ5שV[CŋkoѢCҥKTR 6{|WѹsgڴiCɒ% Nŋٷo]Cz:+UD~믿UV4h@}QQQ4mTluZwZ<Ǻ޻w/2}cAf&$$hƍZ^>/^wwarξ}طoږI˾} Ν;DEEn>crhٲ%ׯ_UV<?#Gp-k$$$K^*,r9j4bccߟ 6Q4 =}FÁ¼^eWjU;瀀ׯO:իWӧOogg1Ka۷ogРAԪU+zM黶Yɯ*}Ѯ];Zlɭ[ؼy3@R |k8k͙1433sL؛*W[lȈRJaoow/rBhѢ,\0TZUk-.]z33%dϧ+_ERRVVV:T<<|}-ZKKKBCCǯ籮2ύ+VdȐ!ՋUV[6yddd#ԩS5k5fu&&&ԭ[W\D AfJz^zoL d֯cلuܻ WDv\PРAA擙E>U[vJʕ+R$<<\ޮV9qDJ8*fhذ!U+V<9*Q2e۷Z\6 ЫW/l޽{8pޱE}\Sn:y;wxE>}Z~F:{rP(prrϏ]vq.\df_Zܸ5j 88X+~HZ s2}s.322CSLH"9㘘"""۷/u֥\rë5dnt&$$p)VJbb"6mw޸Qby]k+W&%%Ds/S2GGGJeySSSڵk?'={ŋ,]4_V/ SSSW_k[_n{{{voݻwx@~5 >Æ ؘ9s0w\y1bϟ?L2<}.]茳lٲ@+WN:@_|?SSSV^ȑ#]v f޼ybjjJݺus?2m4&MF֖(\]]^:*U"00'ObaaAzj>~+jmkڴ)DEE֭['zӧOS>թSRɢEhݺ5ڵK]ٸq#+VZqgUgϞYl޼>bŊR|4jԈggg,--\2M4m۶xxxٳg/Xp!GW^|<ϽXbot=|'OeL6ĨQHNNޞ-[gѸq\ZYYqQ"##qrrbԨQ')))9s1c?V^M˖-r 7ofܸqrek>ӧOg„ H6mhԨ$Q~} 99K=z4Æ c̙)SK.QT)Zh̳ d6>|ŋ6mo|Ύ(bbb +]FIfPT4jԈW֖A=ŋ 4ʕ+kSb<{(7&넆"I=zJ%?<~.{WBP0~xZl M6͛,_\Fߴ3B87hЀsb mС̝;[2i$J%{w͚5ٸq#+V`֐},--b̜9zn:066fӦM( ƟɨQ}]kY~={r!y!e3>4lؐ oƬYo[>}ʔ)7qwwȑ#3|pnܸ7͛s)<<<طo޽rw^7qDy o^T¦M 1~x6lH-3goOtC“'O7naaaiշo~o  Z^^/vqZlIBBGL:D֬Y^l2Çx.]ʣGXhǏԩSZ/LJX§M$zAj#B>Y?RϟgÆ ˋ;wgG}K]t={Э[:) +Qpp06lx?*T SNiB\(K"|D [=;#‡ WZZMQ& |$I"-- :) ΂  `1YAA ΂  `|?XTAA>dddPD\bPѣGXZZbaax MAA$HF!--8/2ljjJ…5ւ  !Q(<\g8^fAAᓥP(I6@pAA>UuŬ  `pAA   @4AA,   gAAA0A/@yS/_&))^AؠRprrbe/px_'Vg5^Zz666og_|Ipppȏ  Xqvv3Ltt4IIIUwJiƘvu Vty礤$7o  +J_h222(tsʕ<{'4 Sx͡|3bx   ]햤o4ԫW'N͇hDC[ d  ɓ'ﭗWP f x gAAE&p̼@ӡ!oh9  x#llkaW7'Ih8DYA L} /naeYxؘ տ,1a(ԯ_7>rH[?~B̙3zٻw/*T@Vhz APqI~|nʗ_~ɫW A/nHH3Ժ"Ƥw]YM3|>Cҡѱ<"RVM|С`24~KA{$Ο?O.] KZA!?ְޏX%g0)2N%p(]kB{i-FOx/Ⱦ `<5jDz .\WgmA=MPBʕ+x?I?6mZJx_! ˭jRȂpN%KB~ŝ1(GV`ڞe,bB?7zQum\ \\hΕh]P… ر?WWWfϞMbbwA͚5u׮]cȑT^;otR͛ŋqwwcǎ޽[o۶-[laرԮ] .h311sҨQ#Yp!FNYx1Ϟ=i4oߎ 6dŤ_zEPP:uf͚7yҠAfΜul *.> /yW^Z/ǏP( $$=zuV:vHFoIKKʿ[2sL֭֭[x?:tI&,YD'5j<Ac<>ǧabQt:qnRK^j-ϟlߓ3W I:pPʮyՊX0o.7nm4~<đÇ3Ɵ?u*G **bE qϑÇi3gugϜt$%%Khצ5mZ `L9>I7t1u$޽fG!"IDATιy&cǎe̙лwoq/_ё+&MPR%֬YCn066O6 Zܹsԩ}eϞ=Zǜ2e kfŊx9^^^3w\ƌReWll,&Mb֬YlٲE'}͚5l۶ѣGd.]ɓٵkÆ c+V R$+ӦM:6@V%""B^wI5kF|HRRcǎeڵԩJ_Fĉ bƌZӧo$Ibĉ3f&NݻAEnM YSevycfHR=_:['S|E"BIIc  퓨(صW:6Οӧ8x^^h$]v?u2:uf֜y$$өc;?yL5'gjծÙ3gxN8՘+:uJ^wi~EQK+M˗<͟i̠}Y -F8{ C}ҾCG] x1`xʔ)4l5kR|y?NV w1bF >_|SLiӦ@.])_LB~cbbزe fbϞ=hѢMڵRJL>'OpBݻ=9{S .4˗/ϗ_~ѣG۳gH9x{{~ϟ+WfZa,YŋI&Q|y_p@n޼IRR ժUcĈ|g  |raVR-)Q1:%_yqW(B&OZ*ꙑXsdٖXw"_ґ};I㫕J%aaTsr DfYb%&&틧O2{tV^Cѡm~۽ѫwo;>~=v2lHػgdv^ݳ%˿{_InRƌ@v];΅0<5{;O-*rѪU+]df䆗F^zyz3 $$$^3QPмys€w{xx J"&& ܹϩS BAJHMM%&&ZjɍܴnݚrJ .g/M6KvA˖-)g֭xz@搙,666Z]hZ*ժUƍy  |t*Dao8`b]̷I~bEoڰI£ȷUCwZSKL?p1#Bccyq=T*+TȜVpE$I½'w{C8ؗnݼA[\K}flƏŤc >xgϞ!Iiii3fff@8߿Zjh߿Fsd sȊ37-ZA~_odZXdya޽888w„ g׮]:5i҄x"##9y$ޔ-[֠s *8Y+\zzx,:]A>dg"RĴ,MFH)$0.NI@Uf,15+v gFϒެJz]2\FZxWZM(TFR媸5 <,P:uJ2|޾aa?O/F33awqcM|P[R]hţis&pȐSV8::RB Cf38^|)x"m۶=Ӹf>ĉ'jh{{{}6e˖ZLMM)_<.]yѵkW~' Ƒ#G5 رcٳ={tFa̙4nܘuh^v LsJ8wp9,ASSXe^UFI򥚌 /_HIHO!ul魎c\{LMMPT-L2(J"##ujOFZHB=91:[ÆHD8v0ǎeZ,iKnYF9z0 mA);;J…$W^"=]+>7Uc숊ʕ+89~3qDY~=mڴg_ k׮ZCƎK^(]4֭[XXXЮ]; %%ǏK.<|ӧsNcΚ5 KKK8y$'Odɒ%уٳg3c vJ||<=… `)uΝ;<~иqc5kƨQ3f FFF;wx&$$'''֮]ˌ38{܈qXXXvZy]k.ٳgIOO͍֭[ӥKyaÆ燱1̛7%Ky 4{ 4 &`jjʪU5j<[A>Fy5y%w*:*&S(#5j"\dpGxߐWn:͛7(^ tw<b*6j,DB;.K++Ôxvv9|(OҮCG9l#ww&O ~D#Iwuca̞$9qkRHbT$]ؗ-KۑJxy|abb<~ϿĄXp>lr9>E16FɃشiSVVVxyy1o<2d|Q:t???}:vٰa ,ŅZ^^= .ȑ#]6;wI&Zqۗ(h޼9<>ё3gΰb zM ԩCуbŊyf̙;wܜ۷w1j(J%[!ӧO͛ߟ}aooָgWWWjժ>sqC~t,_???ׯ/߰aC̙[9ի5kŊK.W#IRBA>dy\|Iʓ`U*/^X!eLU^*TTF)R$1CnYfM|Rŵ4!h6WV$$$мE+6lE䰎*Ѹk\$Ay 5Ubmy/_Q_m8Vy hѪ *Wl d?Ѭii-jF*U)Radd$A)Ԯ[ ¹~֗'IVFlll zX[n8lmmz*UT1hKr9oߞgmСC%7oNrrYx? _'EAY֤D 嘚â`*Ǣ5JRy-1qƻψ2xZ8uA6m$9- ]) 033H"*U5[ϐu =>e PRP!Se7n8a6If͚_ӧO8A T*4 ނkddFAR WPD|>Y5AA>F*h mh4\"h8g  ‡JWٙ˗/s'JJ90J=162BPϡDƛ gJEXXf  P=իWO[r%X~ џXR~2 |2gHLL$>>( lllPTyCp.{BuR> JPס5xC+   |L i8׸  DYAA GñTkKIENDB`cppcheck-2.7/gui/help/images/walkthrough-library.png000066400000000000000000000112071417746362400226510ustar00rootroot00000000000000PNG  IHDRbN'sBIT|dtEXtSoftwaregnome-screenshot>IDATx{TMƟT]/% Z҅iZc1&+"eh(d$HF#L$d\* ]$)3d`"(9j:Nmײw~޶RMM սu;u5&99M￴~zJJJ PH7o$DB666dnnN'NT#T\\LDDE#"|@єIuuuDDEFFF@yyyo>&ӊ+(..ѣ矓 =\,tPRRBnݰqFܼy&L@RRصk}X|SSS PWWKCtt40dC[[AAAطov܉k׮OOO_~Aaa! aooٳgCII} H$z[;wO>ZMKK%JԄB4ş }}}CGGUiڼk-t<Os}>E[{<,|<5ڼAUUB稫Ȩ"y_hsUUU),6}]]uuuQSSǏ=6މc޽~YY6lKKK!((HP>_pp0V^v-FFFH$􄧧' LvxFjqppa4 555dggcС̱>}`ڴiذaCù/006l@xx8g1,--gxzzBOOsEvv6ʕ+r _&aL9Vb1l0ݻ@\\oooØ;w.ƍHJנ1<G\۷oCϞ=̻}v\666HLL_|\aԩHNNΝáC`ddCCCXYYaҤI 9PYY*8pPVV֭[1l0b…Ǻus[YuU5 ڭ,UDD!w,22͙ӧӎ;OP(cǎQLL 3< %_iԨQt""w .0y hER I,~>|Hhpm)***:t((..F^^&+興 4ɲ5557{ƭ[PWWƸ={=s}[kv߅.]ֿ,ZZL8IWVVƐ!CBPk H$py@UUUpNO ٳg\Μ9sjAEEIۿ?._IIIxk6 pa;;077g;N '|ő#Gh" 8P:%[la;NMx؅k6zm<^KJJ ɚmWJ̜9iii-.C"@ }>T ;;;3@D~PÇQXX(++z{ƩSZ\wk5T*E`` TUUbŊf32 ӦM\9s ""555HII9s_}`eecͨѣ3g f͚W⨨u`jjYfEc^|7o>#8::bz4k,ڼy3K缎cǎz!]~-x]@W^'O̴4@˗/T'}}} bw^եCQJJ yxx'"j211+Wҙ3gH,S駟~jQ.]"$… Gvsvߓ'OhРAF#DBG&UUU*//9$  v_->)) rzm\IIIla\ͧE=,,ɕ###1x`ٳI+++رck;M diiI^,0>j(ڹs'effRLL S~~>ݿ Q~~>QsN|Zԉ^Yٳrb 9q]֯_OD~ٙ_o߾ͤ_|Y0rqq+cժU̜x g({5-7Xd >cdggC" 12 'Nwޝabb3^a̶qF<|gQlUTT`᯿> [[[$''O>}3G* R8ߺp&QZZ*6yNGsR|Z_^YٴiӰo>$&&bOp$$$`r73g"##Cx!#G"??EEEMދ ַo__aZ/// 1 H]]RSS,,,חy#]]]SODsٳg^5ܹsg LO"-Ӊ'(55|?&SSSSVVm߾nܸADD)33())oNI>>>dbbWd4Ϣ򪪪 Mݻ ~!3]RII ̓Ͱ&MBjj*rssׯߟIڵ+"##1uT`ӦM9r$={DBBƎ ???,^7n`P===DGG#,, "w0{F$&& zFƤ#F@uu5D"l@SN!++ ƍʕ+~3Ë2e Lv%}>xXoae/++@ +Wp/]YO>w&:P@ @xx8a4j{y_f޼y;v!̘1 TO̾?ݻccfU p1}:gNMM,5[|~~~FOO:::HOOc\@EE<<<cc3tPXXXix'GDŋsyBPnOI?~wFhhܜxÇqyf<̀s};w...(((bV[sss,ˬYPQQ]0}t=s5_RRJJJm6;ѧObȑ#=_]]f͂H$B@@Ə]v5׊xZF55x/>xXka ^|<5x/>xXkmQ,Oͤ<<ͅovyXk%NWIENDB`cppcheck-2.7/gui/help/images/walkthrough-new-project.png000066400000000000000000000247301417746362400234470ustar00rootroot00000000000000PNG  IHDR{psBIT|dtEXtSoftwaregnome-screenshot> IDATxyxLgdEdE,KT jZmZBiRTS"MZVFmՒE_ GClU%-d3Gk:,&ճ};yΙ](!@O$Atf 5 IAt#6 NA:NѶ9?5t T Wgjr^D :DD"D"AgN$~/Xq*[)=ʘ3ܘnp*u|fҲWhh~g<==P*ZNnnnxq,--9}2\΋/ȯZjѲe˰dU={`iiI~~~ﻪ_^BBB*?VUh5B]m6!%=Om޹[r%tMmy OpqqtEmYBBYYYѣ8j|6lmሄVDrjG.^s@h׮ݻwmϫ8166fذalܸTߏ5xqTgϒ?QHNNjP74TX7&ӺNӆ,Y/+ȍ8`_}0#""///FՖO4 (ܼp&LҒ_~Eׯ/^… yr͛7ꫯ@vv6˖-c1M6QTTj[N[dժyXZZrI`ݼӇf͚q1˖-Xh ח-[T*>˗/gԨQxxxK}cii\P0x`~'Km޿sWN:E@@{fΜ9$''k]wٲe,Z%K0h FɶmT6t"pZ٩Q3֗== X7_O_/wJիW9uddd0b ^+E6m ..^z 9rD5ٳ(JKݦ3gJXXǏgl޼U9.\UDڴiǐO?D111aȐ!?_SSSx7xw9|0PNMMϏ,,,3f <(yyy%+WG߾} $..8vލ#'OƦsWrrr4>oN8'N/w%##CϟOQQgۛ ~*YtfGSiRsnK4o/0/B=#=Nٹ25beeo%vAA˗//u_ԫWMҡC*O__???[L!gذa4lذmN:E\\gΜ~rJN/>vvv@TToGGzt>cǎQXXHΝ"::7nЪU+zݻwgҤIxxxp 9[&,,L_5/^VtR&Mdܸq4nXm%K`jjJpp0zzz8qsׯ]겉'ҥ0p@:u֭[IHH˫Jpp0z"// jJ'/h#) z2;6_ş~I𤤤$<<̜9$ڴiæMԚ!r]4i6Yf@M6$$$лwov9{,gϞޞ_/J߶m;w?8Dwرc;o"$$ƍO9}4l߾]T޹Uǎծ.Ko&qqqжm[cccnݚK/T:}eܸq3``4k ___ե.:#qqqٳaÆq5ڶm[~N< C 9!00\[ovp3K$W]Zs?Ii Ŕ-ilnytr0^z>+W{^zǓv1tP={6deebŊ2ץKvڅ3 6ɩJի&LWu@j /0eBBB5k\x;;;U333"##Uk߾=̛7;vR7jI[[[9RDҢE bcchٲMO>35sΘd?Z,Zŋs]޽ @ӦM+t{0`YYY:uSbll /^ʕ+kNu\} |~^k3뵹(gњsI i[Ks$<<oooWɭQ[[[6oތ>g~ce/88sssf̘Q27nѣ֭ۗ?\G}[oϤI8xZ_1o&۷CCC^{5Yseߟ?kgaggjyΝ $<9^0HIIQM( rssQf1[ZJ3g<)GbIgEVPPܹ&c1xdgghHAk8ŚԖƌȑ X{si6 ~gtA;sw]-ʘǺ9>/c}MM]^"5R.O/ h55k@;/0US|OV}w9 ؀` ]gIwʣ(n4mWc;ϗy5u7!%=Om޹[#˪"WI^9sFcv_κө,?v:2g3x/N+4&K]6buR2jU"z2!C٩?{uMG;gX ?$zGKޖ϶6Ez3WSVJodʹAˌK_ѽ cА{{o/Wk-o|1mS ^z½)PJf7>z HJ]z½W=kce8 Hɏ)|5xtO; 0K칦J6[r+5(uyy ԲD)1}]Ҙr ^nfM=|˅IHx@&jMf~.3cNDҥƆtkcJ$m,~7$R@"*kh3H%!߼݁Yw-l3=&>eDmO5{41ף zdL XNCm54ܘ@2c:\+'#'KZ4$$]DK$\p@ }McMIyˇ[Gkl? LhĔ˷3y #=J=RՖ} y+R2q sѭ)!+_}`/Yز㿽1v&3ɳ H0qeՄLI#TmZX")/>A(Q+ɝBVIOk[%ri@s 6ҭKbGsl͌W(X/%c;һ)$Q;߅ٜQ$ %=X/cR vWJ"P?۽)'lLГA zW,'%je"!!>x%칡L^PG9,̼/\ slgECɱd93d _X{MFwr*22C_O.HsM~/;IY1I=,}WCDM$on+o{ս~/;0[Q@)qZ[Zа1op4^jU\_wT]K--07'ӵ)E >K)rd233bc)Ϩ=dd~.U^|v&<)"i៩V'|w0ɯ׵]y8jt;6O!Pkk ~Kxs!ȑc^IХ p+!f>.vX9֙݉idbPw{Bl꫟Ii Ŕ-ilnytr0^z>+|֊(8S#l|C:b'\.<)dե93 ʍ]z|7'g01j?W'p!Y3Nv!}D|q`+-0V{kߟ |'9gWȍr-Ky'a$ώkdtE  :MAt& :Dil͹{:+5L gԩ uh3HAЙH$ L$At& :DUG{$qQ󰶶SNU uGH믿deeAzz:N"5rCӄһwo|||7o7nyzdd27o? K-w&77Wu͟?x>c&Lsl?WH COOBRDTǚ5kWꃩT*yWpvvоWZŇ~OHHoxyyfUرc0a;wFPPP ҥK]W^j󬭭eh?S'### *Hy2 ==={nɹs8q"ǎGÆ #22iӦq!lll*ri޼9={o߾XZZ;Swь=¬ձԆxMLFQQ7HJoBW^䖢\"7 ^=I .s%CCCMFaa![n >'/&7W}xƍ3fPk" 0իW#Fɓ:ݻw۪}N.] ??+VC>}XpZc͚5xzzJ%6l_gϞ|͕{2vX:t1cHHH?O?eٲed2f̘Q)rr:FHS*dHth vFaZSr!>R[YYѳgOWzƍ1}tOIIIa…=z'ĉٷo#F`ݪ+o`חx5kV_{,111\p{۶mK:u*j~۷Ou'''/? ^IDATΝ9aaa|O-Ƹy |Ƽ+Wжm2*tnS=G"Idi}I+06ҧ^i֬>Ժν{h^}G?:t 99ӧchh7oÇʜ9sP*ّ;;vɉuqׯO.]t>sssOBB:t(2f#' L$At& :DD"D"Ag"3HAЙH$ L$At& :D՚Df$/_\6EEEd2o>`U|+>>>DEEY'}UR3GJo6m47mڴBKRTM2d E㡔R͛,\GnݺZ3\BEKq PcRI *=:wݻuz_OOO:v숛&M㩉җe PGSk6!I[lϏΝ;ɓd2޽ˎ;TWj$yWWWZhׁB\^}2}t|h#Jq OV]6~׭[ǢE QFZqwwgҥL>J XXXHVVZݚO>c2ejm)ΒROrppPPZZZT}/EEEIm>|$I-jJڵkj< m&I$8qBI$i۶m )2U5)EEEIݺu$I JRAAj3gHVVV$ItHGUW^$IRjjdee%%$$?TrEڹsgqΝ;W6mZpرM6z~"""ǫ:tvZtQQ*ƖP6e$7l[~:...u-P"11Qǔ={wߩ"Tx8Νq}}}NtQ6e)((Mʒ6zzz4nܘ͛jh#Jq Ԫ+888`eeř3gTҕt^]=zвer,Q?쬱8^j8Kul޼Z|OV8jە7-<jIi;v‚0f̘\.EƖ?~gڶm[U{Jq[zS|~ժDr5<<<4_t ggg077gرcՕu_J\$]=8+)Jq>*]S8QSx^Ԫ+QSxԊDRNkn8IH$u-IT()ih(IENDB`cppcheck-2.7/gui/help/images/walkthrough-toolbar-severities.png000066400000000000000000000150451417746362400250330ustar00rootroot00000000000000PNG  IHDR!ssBIT|dtEXtSoftwaregnome-screenshot>IDATx{xU՝?{\rr!!rEXjQG:ێվ::Q;v2C;o[|#xG. @Kr={'@Ny}$9gw뷾֎J$qF_v8hhR8m۶ B+yaa!EEEL:q|hjj"򔔔 GI&m۶ǩAw"p6|>ӦMRGSS0gΜ/wEUOkp8Lmm-`YZ UUaH)TY`7g^}O˛P(sw:۶4mHʌ۱mwуoX}XN \X L>mňl$]Np3Fx'?[W]y-e._I8k־HaU̘VǙ O[my76oo|ǂ_q>Wr٫Q ӅнH[ QIA:|vy_n`W}x2E&ݩC'R&DQ~I}gR{|7ߺV+FU~ygy4\N,/wҏx9&e |`'t-_N5$#TO}=3ϤPњ D]ǜ3er\nSnb E& RĔsסe6~sA}ge~9}j`/^[O?pB4%IN-8Lt6iE{} wŔGݯ]@*yG<,$$7qLct?W,yL?b vD YU(qnX`q ]=Qb~ [fid3tp4 EQL(hjUy $ұA8t$P\{!ƕt*h@]tҀxAG98ۈl6;d2{"X'őΐ;rr>ǰ7of꼓䰍]jV>eȶrҬY>>}k[m|)CXHiXH)q ƱSV0p%DAan{GΙ;nVl4tTUPǰ\tt:u (+VKW[䱁q~n"TE77[qU n"t}&@&ں㣡4  M蚂 ,[RX`P7OGrLhZ~kٮ_S'BF"HA:9>G0t-_O>xWX嗣VVc\tŊBiǞr)(mWr;mŰ2X>l+cű8Le`[Il'E*Xr|e'9G/jʼHG_'Qyk36O_9yL]/ .NPQ,Q Vh p1V"tm^zAcH)..vٛ5kY7{g3H md`J9382)WM˚5Tt1u@O[ݻwsт۷ַ5k%%%Մ.yN|jD6MAtl+t(" vt 88(LF6ݎ6@"mƶ=}0kL#ȐHdH-#l؛n[uhiEU&Hd,dUS ܸFz(*2cEebz tыY~z'@3=k7v}]TN[-%D4 LMt64ࡻ 㫇w&܌wl)PkװeZˑ4 nI;:ptf=+|?>bݏ!T7B(BGQɹtiD4vTtE5sGŻmOW림$H*+!ZWƽٟ܀׭pZtvz_Iu|Z$w@+;Q}=oPY<X1cC¶n̰$s9zv_v ITPu1&%v$d6c<|cwFɦRɫ3ͤRNoں:pq( >?7W].ފ`~Qq9VtR$cIF6yXiz5 Q+b|u l;,λ[vMo_824V2ĒCod!.^ttu2.3~C{{4u4sYaὖ<E%/|K'qťi߷H8@EQR%ņ 󲛷ch8558@:Gz~HKTUbci?6aJ}=J(yECN"K/%khhnrpb;QE$Bo's d,2~EQNۖĢar5[ ˭g'O( n-dYׇx \<'py#X(B?WP5^x7{› ӹd z^Ll#D|A>cSU[v#c\TL R"].IO*k9 kF|≨qCv,Ϧ>csyDKN ٹc_Ć#D"! 8`I8GtlYD G*#ֶ+أSR% 榃̚Jo8ʁ䱁QBshPUvtVM3Ȣ9GE~Z5˖.b\t g/gEXgYr#eGr .u1wO9,-D#sHR ĉ5(3t4߇ d˲eL6?>TViTTTPVV6?W3xmL|睼;ƶm© <=Z W9)0KH,Ik+ %1~&:#ܼp2sh1ж8*P (PN>ܞ<]ّ8}B 9qO)]Md ۶G,-u`Ҥ܁x$nm5G,?iR%n|2vk;߉Kzz|@mgj%Ķm2 5u֭Xآ"JVQ+L&M?Dۇ}x,!LiZn3`/tݜXWٽQbg)lяvzK0kIG Ld ұm(6/A.a(F.((Wz)`g]$ߦ*3wRoHF޿m2R Z{,^M]uﷴrax,¶-FŢd2 B)SԴ͛~˄ڙcQ!ؖ ߐGL&Cl<&꫘;v`l݆Ja&ɒӦa-\HQEPHW^~1iz۶ijZKcc\2Mi[$`j'kΣiaJI.^LԲ<&vS<;x^\.Wޕ8wM7s`m8y<~衼Bx@Wѽ>8_陨ꀂmq4{'6^.{p\#~%Sxi>^oSdYN3x_@`_AY^9χ{/򔗗si yh%Lx;4Dd2IcUl;4@$gtpݸn 0 uK :UU1 t:=4m~CU{ '݉t(dQmb\"%)\c 窪bnΚ=:?t-9zCv1MsiNDjHoQ .|!7KA~?#Z|ea TPWw>_qLƢTBА麞W;g ib?ybfM>ӟ@ d22 RJt]G!wf0 t]R\| ԍR)2 drt]r !|S\B4t&;84ƹw C}rie~.cǒ:|~>ZiSPPqbԟfscM Hm-`D"x#}.ڛ|C[},8\;?;GXg BQ<oÜ 83*=R8/8x忋Yr~*?<8y2G `̘֭1r@SsΝ; ÄϥR7es"}}}+OQQ@ZBMIENDB`cppcheck-2.7/gui/help/images/walkthrough-warning-menu.png000066400000000000000000000216101417746362400236130ustar00rootroot00000000000000PNG  IHDR5~ pHYs  ~ IDATx^EƋ9cZs53&L(*&@59k5`Ί99Ӫ+,f1+y3]3s9sTW*ݺ};`y53QS {Mb-V5E8L?lfaB L.m"SN-6_-mj(Fw,@ 8Fw,[ 6p㏗fL6l6桇7믿6FVf ^tבl[_uF lx{3Z|#/xAnr뭷vɬfĈf2Ǐ|`jV[m5ӯ_?O /` `vjh^?>|Gmn\6;UW]e9뮻N;|嗆}f63/MϞ=[omH^zlFfW7\p\pb6n8RKQF_~tZD#߼EM:(sW=zƎ뮻NAuYG&Մ *"ix&ɓ'G&,b_|q^M ?]Yt|%\"n9|W?0'N.l;w$F `;bJI'T]ǯWM2m v_uE5s5yD''|1*tp3{T>h,q p4j/r V_xO4張K}1tA2y `,2Caugo!@1dȐ;0+LX#j@ȳqY gRr) >묳JL lg@@|7pC3i$Cȝz-sqVc7w|09SOY=lԩfE{MܗRS<pq8#4+[qO"G@&3+Ncƌw]@ bgVXuYiIVꎀ>{@r"Д#MziʾF?SS" w%hn@#C QdxxbKh#O?^[׷u2 `;qǏ]z饼V\u8+SP!;i﮾`M7 ҮVQgo0,PݩnX.rW;P`vlݵ?.k`'}NKi=@X FVW n_̅0ac xeIb梡Ů{Ɏ PpIꪫKٖ[nio>B伨D+Hpp0pW^yeQaի|'hjq.Kj1]6(x@nVSKS= *iQq #PWs!sDn V?$,s?xiE ,M PPZ(^z"PyKMPjDjn0ꮥA-J1][<57E@E tJTR!6EAT@QQ"TV!g,@  @,L<ݮݠ4:G=@@XD! Tm"E yB4KEd;K/d cPA.;aПҷo_ᬀ\4ZUF e k1yd;'xEhV@cAtyǗTӧO7]tn j$~zgaN9!ƞ={ }/n׾K Y.4hPA 4?^_AcyR29{E3i3 De=vmBTBI]c5dP& ##Fػwok|7,kfmVmzE 1d_YeB2S)eV>U> r?UX/W#ԯL6MWS|"MfI&,knݚڮ"YZ0dȐY!*GM@>Mr-hGT66o6VAS8(( E2"DX*PZ(t\() Y*D-ZjC@ô ô @[q_dg,H3WZi`= L)<6{/jZL?+Gֺ]-o2#;رcC"L+:fԨQJO6l|`r|`*MZ45E7uK؀_0L+;˜1c υlذa"~54Ӗ+ެ*Z| 뭷^r:蠢ߣG}d?I"ݻwK:DŽ tby..4ATt[oe(nR]TACg3rHIPS|"E8ڋ %ۿhjO摦^ڼfʔ)%!`aDwҥOL,E4 Kn  0w}^!v>}Ot`7u (i k8Oq~ /!ꈳg]Jߛ78V+ P5OE}l(ސ";d4SsQ#j#Caa@#!FGtݱ:^ ;E Z14KE *<&MT4UV!gD0i"E2tbUV!+LH }V24"ݱЉU^ZHbO4̃;%6[mU{믿.H 8Iyy+DG ӄJ1M´s*h-!?$BL1k&S--n]C9|E%x|kqk_J;@]uӜŸ(|A>#o\/o.bӳgO 7j Pgy?t6lf}5wuW{μoh2zAU`rBgy2I?S~lwFIy\p$f+5?ԅ3 8[b%i[?T0yO>dفWeq!*x9/|8]vE&a&\ѿ]D#8`"bLln,袱n A =@6pC3i$Cȝ'.zT@'y~JR@@(E Mlb;WV?Ea%71_ib,i@ݱt0() ShfxA@]A/0k!@È{Zh{ݱE@'VF;F ?}FG21ZF@c5vi3NvVh ^=x=<(x!x'},GؙxjZ\?sUYA'@Ə.iӦ E@5\# l_~[#Gzup;wÆ 7j:,sGt_xeoOPvJ {8s9ͅ^XIzIvmod[˵oIRA uBp!f1*)3^`r}W%,e6!Yg?PfKgAA 2Ç 54d|gA hpkీҌrZ+kdlJ)HR~reB !(d2}p5}M7Iָp Bzi._,;m&!`yN)å#o؋A31Rƽ[p9G)S=z/",b,DҘ  $/~Mʗn$d1'tP9`GZn$f%V> '`Nj1bВ1 oРA2:}݅2+Bf!*~ߨQh^IC +q!eYE ̀rB}G uz1[+ cTq0GhE_Ba,hp$Eȟ4;J`WV8+|0a„fR K,߿JΓ {*XƦ7wvpOc  ,M%-HcK`wwvu,?mw29v%nuIKP8˃:ƕo骥 v"ʥ=VɲI]w]7,fu|`Y$; @v B~w]߮ %-lcQgv D,acq#cӻ> +;#&b\Xxo>Y8.ugP9"H\:bͩt,mژ1c ;h;S wQh~Soف7:.\;H@wJ_fv]vBgY8}/I}u U`0{XmЮ0C;؅MRo 0ZlD>vq q%d,< H(W~zU~˂AT6\ET7.ho8/%JEFO,:%:&΢z^{K8ǹ3Лo);S(73%A1c @ VN: >lZN,,Gu*RW2v-8qp`!*d+,6.gװa_HR(h^TM Ru Dp0҉H5J&apeeF\>c8ܲ8סXT1I&dg٥jƽ)-T֬O:Io:.=`erQvdK*g& f"nKT0P1_$Vx29  ))Bs4Qj"*hi|Kf㢂vU[^IdҞS˖l@]lZ $*d4/]ZʝctRL꘤}1 J+1Mwzh^+0O^$O$`q`/%*HExT+֐+`jj@IEI>oLX"4chE 3h"3]i&ډ{"$G\Yb)EqRLf莥c@HzB{4KE (t&A+l(t'+nJ4ld=+! sCWy>~ tRDBI ` lGIM]wUHu`NbkƥXHeǺK D`Df31KԼ."ڻep@.T"M i#$1C &Q }ᷬkp;dرB'-sDͰ8G+K[ O*58q_’駟!H;R< ޷o_!,eDH?}tbu`Z rX܎SJ/^Gg凿" aPACv뭷 X׮]mp:Y)v!'VM%)A6eI G .:tP^a]cjY0 3T\tn3,x 5guBEGbB.~2Pq3ZR| @-@G:A$ |h<ŀc"kpqbPOCoP .| \h(&m[zıa@~uй!VNW-hLY`2a1NHxpѣG uؘ w& j9n8WI8ֺO.#{c B`o5Ji'M`D$P!wf[d*Jfw׺;!NDdu8v;+Y Q!b@IINiߖ^ʴ*0~m.z}9 .첲bz~A];;9E1݁e$`,I 0"쑡`|}(RjWVXJQRv*H!d(@b ~Dv:L J U(ٞ+.IDAT^8V Yq hi)Qbb%(n+(/dׇnp$X /-W.+w;f\;ܘA[X) ~W0ln˧T&NDI\LNcaM,&n'y!h.[RKRF400VK5uED-FxR <=xE1>:guI$ԫWaq~7Ġ̂}GJuJXD 9rƁصZTqogO;RKX )g byg,j8<0 k $`<}cᗰ:3V"V/Vi>HFVV@E,#dT+`sK]vVav"r "`"QALħ p'VP>D刊;ggK)]vk,'ЧONOY`[.c!`$X?jQ)7ر*@%Zm+q"5 B܅~ ÝCai{\:׉Vr )oYns{g` mr o>̍>g+M}bꄸ+Bc8gEjU'v(hq~VX۞Fr>i4@\+"z^x ju LsA:ҫ8Vਹ((E ږ^z,F Ǔ |Evt wfjWT_$KF{;`f3U#k*]N?IENDB`cppcheck-2.7/gui/help/index.html000066400000000000000000000005371417746362400166760ustar00rootroot00000000000000 Cppcheck GUI

Cppcheck GUI

With the Cppcheck GUI you can analyze your code.

Contents

If you want to get started, look at the walk through.

Example

cppcheck-2.7/gui/help/investigating-warnings.html000066400000000000000000000011241417746362400222610ustar00rootroot00000000000000 Investigating warnings

Investigating warnings

When you have run the analysis it is time to look at the results.

If you click on a warning then the corresponding code will be shown in the "Warning details" at the bottom.

You can right click warnings to get options. The difference of "hiding" a warning and "suppressing" a warning is that the suppression is permanent and hiding the warning is only temporary. When suppressing warning(s), that is saved in the project file. cppcheck-2.7/gui/help/manual.html000066400000000000000000000410031417746362400170350ustar00rootroot00000000000000 Cppcheck 1.46


Chapter 1. Introduction

Cppcheck is an analysis tool for C/C++ code. Unlike C/C++ compilers and many other analysis tools, it doesn't detect syntax errors. Cppcheck only detects the types of bugs that the compilers normally fail to detect. The goal is no false positives.

Supported code and platforms:

  • You can check non-standard code that includes various compiler extensions, inline assembly code, etc.

  • Cppcheck should be compilable by any C++ compiler that handles the latest C++ standard.

  • Cppcheck should work on any platform that has sufficient cpu and memory.

Accuracy

Please understand that there are limits of Cppcheck. Cppcheck is rarely wrong about reported errors. But there are many bugs that it doesn't detect.

You will find more bugs in your software by testing your software carefully, than by using Cppcheck. You will find more bugs in your software by instrumenting your software, than by using Cppcheck. But Cppcheck can still detect some of the bugs that you miss when testing and instrumenting your software.


Chapter 2. Getting started

2.1. First test

Here is a simple code

int main()
{
    char a[10];
    a[10] = 0;
    return 0;
}

If you save that into file1.c and execute:

cppcheck file1.c

The output from cppcheck will then be:

Checking file1.c...
[file1.c:4]: (error) Array 'a[10]' index 10 out of bounds

2.2. Checking all files in a folder

Normally a program has many sourcefiles. And you want to check them all. Cppcheck can check all sourcefiles in a directory:

cppcheck path

If "path" is a folder then cppcheck will check all sourcefiles in this folder.

Checking path/file1.cpp...
1/2 files checked 50% done
Checking path/file2.cpp...
2/2 files checked 100% done

2.3. Excluding a file or folder from checking

There is no command to exclude a file or folder from checking. But you can exclude a file or folder by being more careful when including files and folders in the checking.

Imagine for example that the folder "src" contain the folders "a", "b" and "c". To exclude "c" this command can be used:

cppcheck src/a src/b

All files under "src/a" and "src/b" are then checked.

The flag --file-list might also be useful.


2.4. Severities

The possible severities for messages are:

error

used when bugs are found

warning

suggestions about defensive programming to prevent bugs

style

stylistic issues related to code cleanup (unused functions, redundant code, constness, and such)

performance

suggestions for making the code faster


2.5. Enable messages

By default only error messages are shown. Through the --enable command more checks can be enabled.


2.5.1. Stylistic issues

With --enable=style you enable most warning, style and performance messages.

Here is a simple code example:

void f(int x)
{
    int i;
    if (x == 0)
    {
        i = 0;
    }
}

There are no bugs in that code so Cppcheck won't report anything by default. To enable the stylistic messages, use the --enable=style command:

cppcheck --enable=style file3.c

The output from Cppcheck is now:

Checking file3.c...
[file3.c:3]: (style) Variable 'i' is assigned a value that is never used
[file3.c:3]: (style) The scope of the variable i can be reduced


2.5.2. Unused functions

This check will try to find unused functions. It is best to use this when the whole program is checked, so that all usages is seen by cppcheck.

cppcheck --enable=unusedFunction path

2.5.3. Enable all checks

To enable all checks your can use the --enable=all flag:

cppcheck --enable=all path

2.6. Saving results in file

Many times you will want to save the results in a file. You can use the normal shell redirection for piping error output to a file.

cppcheck file1.c 2> err.txt

2.7. Multithreaded checking

To use 4 threads to check the files in a folder:

cppcheck -j 4 path

Chapter 3. Preprocessor configurations

By default Cppcheck will check all preprocessor configurations (except those that have #error in them). This is the recommended behaviour.

But if you want to manually limit the checking you can do so with -D.

Beware that only the macros, which are given here and the macros defined in source files and known header files are considered. That excludes all the macros defined in some system header files, which are by default not examined by cppcheck.

The usage: if you, for example, want to limit the checking so the only configuration to check should be "DEBUG=1;__cplusplus" then something like this can be used:

cppcheck -DDEBUG=1 -D__cplusplus path

Chapter 4. XML output

Cppcheck can generate the output in XML format.

Use the --xml flag when you execute cppcheck:

cppcheck --xml file1.cpp

The xml format is:

<?xml version="1.0"?>
<results>
  <error file="file1.cpp" line="123" id="someError"
               severity="error" msg="some error text"/>
</results>

Attributes:

file

filename. Both relative and absolute paths are possible

line

a number

id

id of error. These are always valid symbolnames.

severity

either error or style. warning and performance are saved as style.

msg

the error message


Chapter 5. Reformatting the output

If you want to reformat the output so it looks different you can use templates.

To get Visual Studio compatible output you can use "--template vs":

cppcheck --template vs gui/test.cpp

This output will look like this:

Checking gui/test.cpp...
gui/test.cpp(31): error: Memory leak: b
gui/test.cpp(16): error: Mismatching allocation and deallocation: k

To get gcc compatible output you can use "--template gcc":

cppcheck --template gcc gui/test.cpp

The output will look like this:

Checking gui/test.cpp...
gui/test.cpp:31: error: Memory leak: b
gui/test.cpp:16: error: Mismatching allocation and deallocation: k

You can write your own pattern (for example a comma-separated format):

cppcheck --template "{file},{line},{severity},{id},{message}" gui/test.cpp

The output will look like this:

Checking gui/test.cpp...
gui/test.cpp,31,error,memleak,Memory leak: b
gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocation: k


Chapter 6. Suppressions

If you want to filter out certain errors you can suppress these. First you need to create a suppressions file. The format is:

[error id]:[filename]:[line]
[error id]:[filename2]
[error id]

The error id is the id that you want to suppress. The easiest way to get it is to use the --xml command line flag. Copy and paste the id string from the xml output.

Here is an example:

memleak:file1.cpp
exceptNew:file1.cpp
uninitvar

You can then use the suppressions file:

cppcheck --suppressions suppressions.txt src/


Chapter 7. Exception safety

Cppcheck has a few checks that ensure that you don't break the basic guarantee of exception safety. It doesn't have any checks for the strong guarantee yet.

Example:

Fred::Fred() : a(new int[20]), b(new int[20])
{
}

By default cppcheck will not detect any problems in that code.

To enable the exception safety checking you can use --enable:

cppcheck --enable=exceptNew --enable=exceptRealloc fred.cpp

The output will be:

[fred.cpp:3]: (style) Upon exception there is memory leak: a

If an exception occurs when b is allocated, a will leak.

Here is another example:

int *p;

int a(int sz)
{
    delete [] p;
    if (sz <= 0)
        throw std::runtime_error("size <= 0");
    p = new int[sz];
}

Check that with Cppcheck:

cppcheck --enable=exceptNew --enable=exceptRealloc except2.cpp

The output from Cppcheck is:

[except2.cpp:7]: (error) Throwing exception in invalid state, p points at deallocated memory

Chapter 8. Html report

You can convert the xml output from cppcheck into a html report. You'll need python and the pygments module (http://pygments.org/) for this to work. In the Cppcheck source tree there is a folder "htmlreport" that contains a script that transforms a Cppcheck xml file into html output.

This command generates the help screen:

htmlreport/cppcheck-htmlreport -h

The output screen says:

Usage: cppcheck-htmlreport [options]

Options:
  -h, --help      show this help message and exit
  --file=FILE     The cppcheck xml output file to read defects from.
                  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.

An example usage:

./cppcheck gui/test.cpp --xml 2> err.xml
htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=.

Chapter 9. Graphical user interface

9.1. Introduction

A Cppcheck GUI is available.

The main screen is shown immediately when the GUI is started.


9.2. Check source code

Use the Check menu.


9.3. Inspecting results

The results are shown in a list.

You can show/hide certain types of messages through the View menu.

Results can be saved to an xml file that can later be opened. See Save results to file and Open XML.


9.4. Settings

The language can be changed at any time by using the Language menu.

More settings are available in Edit>Preferences.


9.5. Project files

The project files are used to store project specific settings. These settings are:

  • include folders

  • preprocessor defines

It isn't recommended to provide the paths to the standard C/C++ headers - Cppcheck has internal knowledge about ANSI C/C++ and it isn't recommended that this known functionality is redefined. But feel free to try it.

As you can read in chapter 3 in this manual the default is that Cppcheck checks all configurations. So only provide preprocessor defines if you want to limit the checking.

cppcheck-2.7/gui/help/online-help.qhcp000066400000000000000000000006241417746362400177650ustar00rootroot00000000000000 online-help.qhp online-help.qch online-help.qch cppcheck-2.7/gui/help/online-help.qhp000066400000000000000000000046631417746362400176310ustar00rootroot00000000000000 cppcheck.sourceforge.io doc
index.html images/index-mainwindow.png investigating-warnings.html preferences.html projectfiledialog.html severities.html images/severities-error.png images/severities-warning.png images/severities-style.png images/severities-performance.png images/severities-portability.png images/severities-information.png standalone-analysis.html tagging.html walkthrough.html images/walkthrough-analysis.png images/walkthrough-library.png images/walkthrough-toolbar-severities.png images/walkthrough-import-project.png images/walkthrough-new-project.png images/walkthrough-warning-menu.png cppcheck-2.7/gui/help/preferences.html000066400000000000000000000053411417746362400200660ustar00rootroot00000000000000 Preferences

Preferences

Number of threads
Number of threads to use in analysis. Each thread checks its own source file.

Force checking of all #ifdef configurations
Cppcheck try to check all code and will therefore guess different preprocessor configurations. The maximum number of configurations that is checked is 14 by default.

Show full path of files
Show the full paths in the results.

Show "No errors found" message when no errors found
If you want to get a message box about this.

Display error id column "Id"
Show error id in results

Enable inline suppressions
You can suppress warnings with comments. See the Cppcheck manual (https://cppcheck.sourceforge.io/manual.pdf) for more information about inline suppressions.

Check for inconclusive errors also
When full analysis of the code can not determine if there should be a warning or not, it is inconclusive. Normally Cppcheck does not warn then.

Show statistics on check completion
Show statistics in a window when analysis finish.

Show internal warnings in log
Internal warnings (for debugging) is shown in the Analysis log.

Applications
Configure external editor to open from context menu when you right click on a warning.

Save all errors when creating report
If hidden warnings should be saved or not.

Save full path to files in report
If you use Root path the warnings on the screen will not have the full path.

Language
Configure language to use for GUI.

Python binary
To be able to execute addons, Cppcheck needs to know where python is. Unless you configure something, Cppcheck will try to execute python in your PATH.

MISRA rule texts
Only needed if you want to use the MISRA addon. Cppcheck is not legally allowed to distribute the MISRA rule texts and these must be provided by users. The MISRA rule texts are proprietary. An example rule text file:

Appendix A Summary of guidelines
Rule 1.1
Text of rule 1.1
Rule 1.2
Text of rule 1.2

Clang path
The path to clang binary. If no path is provided then system PATH is used.

Visual studio headers
If you want to use the Visual Studio headers in the analysis you can provide the path(s) here. Hint: Open a visual studio command prompt and type SET INCLUDE. Then copy/paste the paths.

Code editor style
The visual theme to use for the code editor that is used when you investigate results.

cppcheck-2.7/gui/help/projectfiledialog.html000066400000000000000000000121761417746362400212570ustar00rootroot00000000000000 Project File Dialog

Project File Dialog

The Project file dialog contains 4 tabs:

  • Paths and defines; paths to check and basic preprocessor settings.
  • Types and Functions; configuration of platform and 3rd party libraries
  • Analysis; analysis options
  • Warning options; formatting warnings, suppressing warnings, etc
  • Addons; extra analysis with addons

Paths and defines

It is recommended to import a project file.

Import project

Project to import. Cppcheck will get:
  • What files to check
  • Preprocessor defines
  • Preprocessor include paths
  • Language standard if set

Paths (If you do not import project)

What paths to check.

Defines (If you do not import project)

Cppcheck automatically checks the code with different preprocessor configurations.

#ifdef A
code1
#endif
#ifdef B
code2
#endif

Cppcheck will automatically perform analysis both when A is defined and B is defined. So any bugs in both code1 and code2 will be detected.

If you want to configure that A will always be defined in Cppcheck analysis you can do that here.

Defines are separated by semicolon. So you can for instance write:

A;B=3;C

Undefines (If you do not import project)

Cppcheck automatically checks the code with different preprocessor configurations.

#ifdef A
code1
#endif
#ifdef B
code2
#endif

Cppcheck will automatically perform analysis both when A is defined and B is defined. So any bugs in both code1 and code2 will be detected.

If you want to configure that A is never defined in Cppcheck analysis you can do that here.

Undefines are separated by semicolon. So you can for instance write:

A;C

Include paths (If you do not import project)

Specify include paths.

Types and Functions

Cppcheck uses the Platform setting to determine size of short/int/long/pointer/etc.

Check the libraries that you use in the Libraries listbox.

Analysis

Cppcheck build dir

This is a work-folder that Cppcheck uses. Each Cppcheck project should have a separate build dir. It is used for:

  • whole program analysis
  • debug output
  • faster analysis (if a source file has changed check it, if source file is not changed then reuse old results)
  • statistics

Parser

It is in general recommended to use Cppcheck parser. However you can choose to use Clang parser; Clang will be executed with a command line flag that tells it to dump its AST and Cppcheck will read that AST and convert it into a corresponding Cppcheck AST and use that.

Analysis

Configure what kind of analysis you want.

The Normal analysis is recommended for most use cases. Especially if you use Cppcheck in CI.

The Bug hunting can be used if you really want to find a bug in your code and can invest time looking at bad results and providing extra configuration.

Limit analysis

You can turn off checking of headers. That could be interesting if Cppcheck is very slow. But normally, you should check the code in headers.

It is possible to check the code in unused templates. However the Cppcheck AST will be incomplete/wrong. The recommendation is that you do not check unused templates to avoid wrong warnings. The templates will be checked properly when you do use them.

Max CTU depth: How deep should the whole program analysis be. The risk with a "too high" value is that Cppcheck will be slow.

Max recursion in template instantiation: Max recursion when Cppcheck instantiates templates. The risk with a "too high" value is that Cppcheck will be slow and can require much memory.

Warning options

Root path

The root path for warnings. Cppcheck will strip away this part of the path from warnings. For instance if there is a warning in

../myproject/foo/bar/file.cpp
and the root path is
../myproject/foo
then the path for the warning will be
bar/file.cpp
.

Warning Tags

Tags allow you to manually categorize warnings.

Exclude source files

Excluded source files will not be analyzed by Cppcheck.

Suppressions

List of suppressions. These warnings will not be shown.

Addons

Y2038
32-bit timers that count number of seconds since 1970 will overflow in year 2038. Check that the code does not use such timers.

Thread safety
Check that the code is thread safe

CERT
Ensure that the CERT coding standard is followed

MISRA
Ensure that the MISRA coding standard is followed. Please note you need to have a textfile with the misra rule texts to get proper warning messages. Cppcheck is not legally allowed to distribute the misra rule texts.

Clang-tidy
Run Clang-tidy

cppcheck-2.7/gui/help/severities.html000066400000000000000000000024741417746362400177530ustar00rootroot00000000000000 Severities

Severities

error


when code is executed there is some bad behavior (undefined behavior, leak)

warning


when code is executed there might be undefined behavior

style


point out possible mistakes, and suggest more defensive programming. Examples:

  • unused code/variables/functions
  • conditions that are always true/false
  • constness
  • operator precedence

performance


Suggestions for making the code faster. These suggestions are only based on common knowledge. It is not certain you'll get any measurable difference in speed by fixing these messages.

portability


portability warnings. Implementation defined behavior. 64-bit portability. Some undefined behavior that probably works "as you want". etc.

information


Configuration problems. If you get such output then your code is ok but your cppcheck configuration could be improved.

cppcheck-2.7/gui/help/standalone-analysis.html000066400000000000000000000005711417746362400215360ustar00rootroot00000000000000 Standalone analysis

Standalone analysis

It is possible to quickly analyze files. Open the Analyze menu and click on either Files... or Directory....

It is recommended that you create a project for analysis. A properly configured project will give you better analysis.

cppcheck-2.7/gui/help/tagging.html000066400000000000000000000006751417746362400172120ustar00rootroot00000000000000 Tagging warnings

Tagging warnings

You can manually categorize warnings.

You choose the names of the categories yourself in the project file dialog.

If tag names are configured, then you can tag the warnings by right-clicking on them and selecting the proper tag in the context menu.

Tags are saved in the project file and will be permanent.

cppcheck-2.7/gui/help/walkthrough.html000066400000000000000000000040011417746362400201140ustar00rootroot00000000000000 Walk through

Quick walk through

This is a quick and short walk through to get you started.

Step 1: Create a project.

Create a new project:

In the Paths and Defines tab, it is recommended that you import your project file at the top.

In the Types and Functions tab, try to activate all 3rd party libraries you use (windows, posix, ...).

In the Analysis tab, leave the default settings to start with.

In the Warnings options tab, leave the default settings to start with.

In the Addons tab, leave the default settings to start with.

Step 2: Analyze code.

When the project file has been created, the analysis will start automatically.

While analysis is performed in the background, you can investigate the results.

Step 3: Investigate warnings.

In the toolbar you choose what types of warnings you want to see (error/warning/style/performance/portability/information).

All warnings are shown in a list. If you select a warning in the list, then details about that warning is shown.

If you right click on warning(s) then you get a context menu.

The difference of "Hide" and "Suppress" is that suppressions are saved in the project file. The suppressed warnings will not be shown again unless you remove the suppression. When you hide a warning then they will be temporarily hidden; the next time you analyze your code these warnings will be shown again.

cppcheck-2.7/gui/helpdialog.cpp000066400000000000000000000064161417746362400165670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "helpdialog.h" #include "ui_helpdialog.h" #include "common.h" #include #include #include #include #include void HelpBrowser::setHelpEngine(QHelpEngine *helpEngine) { mHelpEngine = helpEngine; } QVariant HelpBrowser::loadResource(int type, const QUrl &name) { if (name.scheme() == "qthelp") { QString url(name.toString()); while (url.indexOf("/./") > 0) url.remove(url.indexOf("/./"), 2); return QVariant(mHelpEngine->fileData(QUrl(url))); } return QTextBrowser::loadResource(type, name); } static QString getHelpFile() { const QString datadir = getDataDir(); QStringList paths; paths << (datadir + "/help") << datadir << (QApplication::applicationDirPath() + "/help") << QApplication::applicationDirPath(); #ifdef FILESDIR const QString filesdir = FILESDIR; paths << (filesdir + "/help") << filesdir; #endif for (QString p: paths) { QString filename = p + "/online-help.qhc"; if (QFileInfo(filename).exists()) return filename; } return QString(); } HelpDialog::HelpDialog(QWidget *parent) : QDialog(parent), mUi(new Ui::HelpDialog) { mUi->setupUi(this); QString helpFile = getHelpFile(); if (helpFile.isEmpty()) { const QString msg = tr("Helpfile '%1' was not found").arg("online-help.qhc"); QMessageBox msgBox(QMessageBox::Warning, tr("Cppcheck"), msg, QMessageBox::Ok, this); msgBox.exec(); mHelpEngine = nullptr; return; } mHelpEngine = new QHelpEngine(helpFile); // Disable the timestamp check of online-help.qhc by setting _q_readonly mHelpEngine->setProperty("_q_readonly", QVariant::fromValue(true)); mHelpEngine->setupData(); mUi->contents->addWidget(mHelpEngine->contentWidget()); mUi->index->addWidget(mHelpEngine->indexWidget()); mUi->textBrowser->setHelpEngine(mHelpEngine); mUi->textBrowser->setSource(QUrl("qthelp://cppcheck.sourceforge.io/doc/index.html")); connect(mHelpEngine->contentWidget(), SIGNAL(linkActivated(QUrl)), mUi->textBrowser, SLOT(setSource(QUrl))); connect(mHelpEngine->indexWidget(), SIGNAL(linkActivated(QUrl,QString)), mUi->textBrowser, SLOT(setSource(QUrl))); } HelpDialog::~HelpDialog() { delete mUi; } cppcheck-2.7/gui/helpdialog.h000066400000000000000000000025711417746362400162320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef HELPDIALOG_H #define HELPDIALOG_H #include #include namespace Ui { class HelpDialog; } class QHelpEngine; class HelpBrowser : public QTextBrowser { public: HelpBrowser(QWidget* parent = 0) : QTextBrowser(parent), mHelpEngine(nullptr) {} void setHelpEngine(QHelpEngine *helpEngine); QVariant loadResource(int type, const QUrl& name); private: QHelpEngine* mHelpEngine; }; class HelpDialog : public QDialog { Q_OBJECT public: explicit HelpDialog(QWidget *parent = nullptr); ~HelpDialog(); private: Ui::HelpDialog *mUi; QHelpEngine* mHelpEngine; }; #endif // HELPDIALOG_H cppcheck-2.7/gui/helpdialog.ui000066400000000000000000000034421417746362400164160ustar00rootroot00000000000000 HelpDialog 0 0 635 446 Cppcheck GUI help Qt::Horizontal 200 16777215 0 Contents Index HelpBrowser QTextBrowser
helpdialog.h
cppcheck-2.7/gui/images/000077500000000000000000000000001417746362400152115ustar00rootroot00000000000000cppcheck-2.7/gui/images/applications-development.png000066400000000000000000000022771417746362400227350ustar00rootroot00000000000000PNG  IHDRĴl;IDATxud2l۶mҳͳmXmc1EKRsma v_tXC ߶pl#)8`kzz6oqn#ӆ~V/uo.L=Q~54_ο|ZE947Ӫl޺GŨ&Or|=}/܇q=E:=Iؗ t<ƞQm8^ < 7m?^ @k@`XƋttE#kFH 28x/ȥ`6X61ZcAJk89 )G[Rd;NgmTtoFbt&hz^y]L]B$"SCc=%oZ5Pуōju8{+hg$LM  u ĉѪ51B a-kVԼ8|ȋ*$ kҦPlSfgOj8xgп8,W 蕎@e.KЪ k5YK18Z%.)k ,FmT6:*u ju ܬ22qFY` Co*a9zNи!!$eۗGe3Hb&Ck"V w1լ |ҽbH=SЊLB{CF*@bP۠ L|1`L$SI8:6+ܿ^ĸseK' (V-X2벃V C\abhԶE cFO}͕zoDu`B2 zcfuމpǟab[ /so|k 벎e'xX6PǁwSN9?7J@0~޹ӧgͼ'HGW [7ҥ.diq/qb6" F"@`1#z:j!7B,D"; ָYŦ~fy̷Cc,đv16M7Ҧ5@$#u11!~v1s5梍vCW`E*ӈlm ˑ&rbRN\4[Xt?͘IC2+}KrfGJi'E= @l gbM87`xhGǻ~b(KjwFo>(+ե}hdB\Mhn -ǠSU G_cgd߳#A^E|(uFrϯ.<ʬRˠQC]B}GbEtYv8'[=B 3=~Ze9uHT͐G3I:x-hkRX[೴@(ǠN CHy5دf&omy PȽ&L՚\w~#yYn Mͩ\BSR|sYl ύ u]k[^!sw(Ɗ2ʛ̃BwQhmeXy@ǭ?G^]tN3::|J?MN\%xYv\LDb2+q1!ўOL Fo_Mpu7EcK[bx-F <0q'qs zFxΩ=Kޗ6HJt?NOA)/"mJIENDB`cppcheck-2.7/gui/images/dialog-error.png000066400000000000000000000014511417746362400203060ustar00rootroot00000000000000PNG  IHDRĴl;IDATxՓ&gmԶ vA j6ڶ;~U'ya yhVG;}@xx(PsJNs$"+$v` ``;4О"<Da %Z 9&ggbK?r!Zm*C `aoX,~Aj5;B|RB?8dS_BlZ |4_{Vcf&4hO5"v =b67c8bYe(gb p v~T $܇ҭhiJ4K} 7 W/uPy:l=g ;fM7ۯ$] C_2k{j3SS{'\~< hz[3Ccttu9.zR&K"X1q5PFzԵmP̭P[O&u0}3ФrP 3>jmYPzD?ƞUy{!`@Q z͟+D|IENDB`cppcheck-2.7/gui/images/dialog-information.png000066400000000000000000000017561417746362400215120ustar00rootroot00000000000000PNG  IHDRĴl;IDATxbS YSr+v&?h$ǥieH+ ñm;).Ķg۶m^aVcwT}W׾@ie@Bj ۞3pCuGɋeu(l XoyZy1&:U L,"&mVx`rTr9;Zcf#}ktU4|Xo¼r:W)rͦP0Eo04̽H#vȨ:U2m%3/PVC[Emo+5]ve^,8cMz FmnEG<]kuJXѡ*-%rs!;h)mtŒd|!vk4.գV^FzVaowrd[#>Ǜ:OqYY_o]v|,` D8NdmTe$n+5/i2-Ż;M8Q O{\L(iEQ9Y,UNzR62}֊6,fS۪[In4jcɌSXg(KNX6oy<πq[@oV񕡶d 4{ [8Gp8X}muY}O 5#lrnjq+ ;-F%n8 sz&aƕiJ޽w]dy>wXMj3:L60A0kr#kL|_KtyJ}~pYo~V"1i<((\w`*.1vz4md TTlT.z5ȃ'ӏ0xH_o+|?f.fF1%cU+>|f.f|]\w6"!#  w#7u$b՚,qIENDB`cppcheck-2.7/gui/images/dialog-warning.png000066400000000000000000000014451417746362400206250ustar00rootroot00000000000000PNG  IHDRĴl;IDATxVsN{sgjgj۶95Am^f.&X3m7[@b`S&6H)*V_& QQ! ;mB,j,۽9u5C@P5MSi;v*%Ĺ=y՘]ܼ :`n7ޭ,iΚE#P"HJ#Mv B1-j9k/Bgɓp#oקRR:3|nQd8y /vívyR.[F0BB`x"+ΥWK q[7LF\#t=+x s9<\EE*l@?V5&D(D|p!R֡!֬YCgOL+ufMrfh]L@=#qZ*EqǏgmfxk`^.)A54`twޮZ=ܓ#8,%}X ZV:z` ό;Vgx"M(// o?UI{{aKxxv>]6>kQTk~eˎ{^7ŋq\U aj<~;6(ռD"B%yO cs?e͢EO?KB?8uxssq@0千}0 u*Q⯨QIENDB`cppcheck-2.7/gui/images/edit-clear.png000066400000000000000000000021531417746362400177310ustar00rootroot00000000000000PNG  IHDRĴl;2IDATxKW̵mĶm۶m/c[k۶m;عU߰d'f [O-.=5K` ;9Kxk+B\Y!]FM'mzk <|?A?%%$4Z#p6kqmn9\s՜uiqigޤUq{5QSW +߲޺QeKG_Z*UBڍŃv\%ifxfoTreBu0̼F£˪ȏ_MpA# ~wvL|bCz9uq[˶A*n*B! 9|'RCYr{keu)bT ! Zc$x4C rBQ8ֈ@쪅`7_pmv_"3D!1.w05F,#3,c?\pi-r<4ꌨՈukŋbuEwDQ.c ׻Z ŶV,>o|t81ݛ _$ePa~ͷFvF\V}ULzث>-d$v#է-P(OMN /:hSm $סfٱ QBĻE^xgE>{Jg!ݯj!yj(MTBes8!9Q;6C,!+ѝQ_[~C8&&O0B" ٣̿KUfŬE5%rCuPIPV%qQg|s_%CD?:^ӯ͊YFrev@< ׎\)+|Fw U .ёGzn>!#b:6 c(Өd&,0v~|FwH'Մ'4 cK#6钙8ubC v6e8ߗkC6}Dܒl(ml)iT6J ?S hDIENDB`cppcheck-2.7/gui/images/go-down.png000066400000000000000000000013561417746362400172760ustar00rootroot00000000000000PNG  IHDRĴl;IDATxe9=X6`m6;gl۶m<>|W(r q1Z KL8h)c%\4(5 \C}[AAT./eAdF'*R-o+%5bCbm&|?JInו~or&!~zk؎l89ԗYj΂i;7We:nj'V"Ma S]x0)nւ)ogN){} H͇r^?Ǿ֧.kKKJ^7lIt kÄOi4MqRjn'Jm]#}M)XB5"Q2 Obf̐A};a™ eV63MII!F?6xz(,,=vpNN[ 2~7 Kz(((=Dh(o|FbVV98rN-a^ۏ>w! yyy@JEqT?ZWJlu _TnC E]u IENDB`cppcheck-2.7/gui/images/go-previous.png000066400000000000000000000015211417746362400201750ustar00rootroot00000000000000PNG  IHDRĴl;IDATx,GF۶۶Bl۶mvlֲgng:5>5,Q/ᚸJ-/\e>0b[nW!7^_-9kqb˥1+7=GE.^箔V ;yQ.weSG ãFr+-=N\jV P4S7b!7>{0sΗOE,bcA1V#hhYǿ' 6xmzlP@\o4XސJybQ?>~9t;u0Qa# QS1)?3*D$2?7^{烚Zu1j8jT+o>"0 "RQՊ RāL68kѫmX6)?<2v ;k"TJ|Wx(#a6;-I.lR!_d.I)ݗEM{u4#|Ѽ U"  մ3y|K)g%Ƅ=AHf{d6  L2ZU=!A%fb{o;G}b"FlBĬ;mN"qj1/A]K#(j61h~b'5hdN9m +|2&p(@{ 1a "|3vyǵ=pǜFKaA?lG̱9֞EpR9Bݵ|XwVa|?L٩ t m}1T4t=I bIuegb$ẁSd)a6!rӍ$rmq&U 1~Ib b!NH$?B%OW=cIENDB`cppcheck-2.7/gui/images/llvm-dragon.png000066400000000000000000000224121417746362400201420ustar00rootroot00000000000000PNG  IHDReLty1bKGD pHYs B(xtIME  /!} IDATxw|VU疷.E"*("6EљaFqXѝ8*v&i{{?4HgƓyys/c^bCPsȃ{.c]3ۿެ@;,Ir7XJ)#`~{PwgΜȱ GQ%j_V-6huTP,lة3?#'Ђ*kc={KsNoɌ]\Ai!˫Ҡ/oԨQ jP>VJY&9gV4>DD 5200d"FJΜ:vl*(_3a72@NP<>uҸDJ)9[;.2(m޺=|6HAqKq#ty>+8~*李@43sKW; Xs)GO>d1۸lܸI KrVA Y//񿂄%KM{Gx,̿~wB ټlϧn1{917 IjFaY+x#G^@֡w bY1eΟ?sϷ5:ӸB8,廙PG -[Ij@h+4~ai[9+!OU(˖WF_o  x}ZDBBY -Jkv\qV:wߜӷ J9o^MPlvz z1L:uI$z-Zdmo,qpY0 ͖&BNҪiҤs~5cV?нقg,', 6f?GV"2蒪 9k?LG @Ë2`x}>DXh4"lj(oB$|m{o9lp<~;>gź=t. Fd2'O3.OP(?,\FqQ(Lr9;V+Z]U^RS fYJ0~ڿX^"=7"[Ӫ:'XPz]|R;PC Kn,뇤[0X;TƯ+s^KvæM*&3 %-z3i2<8 ΃Awy7冭/h43 {/Y<)(c4b 捷> 5WnLWZ#8بv2u=Kp-aH}㐙9ϙv;n[lY ˖RrJfʕݻӃ.KӅU @f}نFY Ae,:2=/m/CW~[(9gb0%%z0cGy Ԕ4|TS(RXKS|9%Kn<Ŭ}eʤ Lz>R,Shrco؟tI2Le,o1 >EJd3UEgڢm!eIN8.c.O,Fr~vu7?WI#3OLs$X`vγ/OK.M={j/ۇ Mg@@p&%PZVKnmr8]?e秇/wy^!YȲ P@r՟!+zFgsI4JnL!v W->mQ_ ҫY4?7'L)Ne6·R^,,.%Z<} o̜$$$aB!0 Nqc'bw%̞1ޣ9k~ɲ˅r,zM-6B,xOEEtШ }]ږNwHj}i%!}OI :JT TcGM88w/V @:Pe:o5,3l8s.UrUM]*Rk!2h0 A4[>tYKo}jUPJ)l6;+kq̩=*ۥBIH%?ӧe/1ABbRT+^[P[S&noR\(\~ !7fr`C[v1=|ʕ+B(xE(6*S[Y~oҝC$$, ,mB{*bA:l :=QTWlniJM.yU{ov=E XxMv>vԨQϪ7|k;j zİxۄ3>p_o_R `U.S@ͽ~o箽`hbS+jFݳ^HyR*0ot8jQ:v lܥ={^QVR>݀`@=}d}jܹ0,5r_?K7soS҆+ygd0ݳs^O $Vҹzb۷VNfH JI1)]8sq^&RVWİm/7cF!33N'N NͰꔚ \N<VGN|5D*+d(%uuVըÄ>;0 JK[;C3N⁎ Z[\~8{7 a!ҡѲëUZFU]N ;`ybGƢn/6w2V O,G+-ɊP$̴)6nܦw@7+* E}OzUjH mFoH9pXNG}xIF$RPJ=XPbtL6 d9qF1P^YP)9W%n,^ N @(aYE'irӽc<C+*!I4j \EB%YPoEOUJZ9?G;S>A|'=#J)2O/QӍD/&(P JCuF͋ں= FUu5MM(R~GO,͵23'2Ģ|>J%苝So/(% .;UF)$KEBQ$ g+9\?'S8xq:\mna =WtlY%XQk IΗ夲ݻwqՃRsDsbz^EݣDiUj;ƎZ[ JUQ^9{ʢ2]{{ڛuA4gYrNsW HJNlބZ|Μ9zb*sL .'$Iò,E)&[{j62IMklNFHfZLɋKJG(lٶi SSDi iܳâzR(WcgѠQ"=z(Z!F455e =;@2hh1atAJJ!SpU-xk }Dw-1*l„^?pp HU$޻sZy580R)x|D"Ro0,[ntʀΤt;. >ک?(]ߔ帛3lN Jݶ咍%8Նts'=t(NI]՗$Kصk&KBkkϗ?qaB((X ..*ce`xџ{zam@ d8.+4HS}9)77/ D >l!QiS])pGT*3hnb@՗,EkoU" G!`cXvPJ&M?ywu3tI2tz7"qgzmӖng&%aUnnocyW3 .'Jw7?tilӣѰ=h4/+/fOZ z| ~W;e E(-ެo^ԛ;ƆBax>X`?pcǾh^VV2hѝ]k:AvS}˂2=:#sR>jzrB{hep'X0 ,8#fgNPyyo5R!#?t,)Gm^w-~"I  $ @h2ntehTkt {[jMvi!LiYek{peNNW(NוK)m,)'ZoR,ey[Oug,eYFRb]AD0ƑC̹}WIl5ïhGrJJ۪EVBك55guSKPoy{CO!r~CE'Ӯܘ5WbxlQփrrrE!U.s\gJ),ffϞhڈ_X"hO+q@TǛ v(a<^ Ubc*˚7ވ7z^OF\l؝e~ݓwu%J΍6wI6JQP5[#z+ p{!] 0l$%$BaШ54*׏TdA( Y򑮃RECe0}zSDCuā#(zJB2dd3dȐ|cлmIksw%-5 \. 6X} [~Wضsy<˽z|ܜ_pn%|*>%ԌJûnԁsOB6(&ږp,@%% %(x=B{2VJ 66p󊘒0F)%g#n JEEETVF#UIP__V ǒ{s^]0:chcK$TR]'8Bb2C  "1)PbDĪ?KUWH:SX(stE& q|D) =/Fm}muD=v:l|0b0^pQ*xB!(Zkg^GM5huzu:uzxGPpkmDQaIKXDr:]jJkgpe}R3zm* + >I(໓*ˑ E%i 6|Ϡ&t蚫BbcbPQQV~DR*q8(JP(U ": : Tj5 %N=AQ[[F<%lU?kZ:55u'#4MHY]rY:y+FZc~3)7:`5A˩-҂cI"'ɲL%gsv?pDMM= \.'***2,(P( pe@ ǍE$Veتίεq8W[lr+P ~QAk3Q雔R9E6X-4/'xVʩ,:T-$闒$[E`8q<8@pi; q`9 Qa5  ]r}yVn23AGCI>`Y1/wCMM_Ne9udwP֓'c+ν.3(Ie BV!u@ Xl6;j<,F B0<ZmYԣҩ3W\6vID#\u%iht9^PrjZ [w?-oh6v˄k@eU5GRB45(%HINB Erb `uuuph5I ;;;۶/YD@93O\7 !Zp !>6,lM .fDzwV_2w'$#11QLINF0,p[PAQ]SX d Q ۍmpǟ?{s3,bJ/aΟ\TJVVa Itm2RYS7ogOe%pD8v4UH^WYV&AiP*0MШU50bJ!ĉU2 Ƒ.'F}mWU5 MW;f1&,b63Z T}ՒcU9u*oRQM97mkmAѹq]%Q)`2Ԅ$CVzX b-hjʃ 蔚3GȜIH aN;^z?^dy^eYs6nli>i x~O^uP[qq1?%/ssl4$'p5H?bՊN <'O0fK,nK H%%'&9Y5kְ~/%%zdTYo24A%p, J^e&,\X`qVV*>1soQi#r,MQL~-!A!n) 5b]N;Nr+z:ΚW !5Xm-RS˗7_>Фf#TJŜI13zQYSp$!B(CiF{bU1 yaw _83gNhUV77b6o(= JQ)U^e Z-dxhT PPBU>"geeq|Dy.J(%!vŤI?BD29\.(<@IQZZ(D ]:,>hѢe"UEY@j6_5#K?47fZh@`X(A,? ȯ=>ي5kְ:sÔǵ* _G]^7%?*F"Jty7)>#0K, ^߾{a47tNo8RyBPWUlҭchakW&&&%#{'0pVרGȲ" I,Y0Xa2Zf`M[`\j-IENDB`cppcheck-2.7/gui/images/llvm-dragon.svg000066400000000000000000003476761417746362400202030ustar00rootroot00000000000000Dragoncppcheck-2.7/gui/images/media-floppy.png000066400000000000000000000012371417746362400203100ustar00rootroot00000000000000PNG  IHDRĴl;fIDATxDGF]ն6TAݘ6m 8UP۶$Scbw|olc9 GD1p̎o'5uvzRp7$PN=րbT^K<tzw̸Xyfu@?L;}VX:Ɵ"f}`FHBf5 , ,,[m`uD/냻IAIENDB`cppcheck-2.7/gui/images/openproject.png000066400000000000000000000017751417746362400202610ustar00rootroot00000000000000PNG  IHDR szzIDATxĖ5V{ᙇac`҇zmnmR2Uˌap1>z5cws%\I80W)>+X~ 11ر8Z>3ϸQZ C>vW^i׮]BIV'N@ۥ&y%kVNm#o[l?TI]a&%?DS0.%2;I]͛Ipd`RkWMnoJ aNMb4hP3Ξ]\woR߳T㗔hLe~`kޜVoɒJZ^B_Pƶm;gʕZy>8x0mAfNNto\1jTW)DIIuժ~Z_VGo uRR޺bȐ4=e̞(I&=zqŊټŪ*_7P xsRÓ ksگ ^˪YB}Jb!HME# Sإap\YqJFl$SyELD[Q_xY"/ <IENDB`cppcheck-2.7/gui/images/scratchpad.png000066400000000000000000000004111417746362400200270ustar00rootroot00000000000000PNG  IHDR Tg-PLTEIIIOO>qtM̔ɰ|tRNS@fIDATx!@EgvѠ"<l- $+TT5{9:^~k}`Ӳf6u0 C 0 C=p`o0$'eWlIENDB`cppcheck-2.7/gui/images/showerrors.png000066400000000000000000000014511417746362400201350ustar00rootroot00000000000000PNG  IHDRĴl;IDATxՓ&gmԶ vA j6ڶ;~U'ya yhVG;}@xx(PsJNs$"+$v` ``;4О"<Da %Z 9&ggbK?r!Zm*C `aoX,~Aj5;B|RB?8dS_BlZ |4_{Vcf&4hO5"v =b67c8bYe(gb p v~T $܇ҭhiJ4K} 7 W/uPy:l=g ;fM7ۯ$] C_2k{j3SS{'\~< hz[3Ccttu9.zR&K"X1q5PFzԵmP̭P[O&u0}3ФrP 3>jmYPzD?ƞUy{!`@Q z͟+D|IENDB`cppcheck-2.7/gui/images/showperformance.png000066400000000000000000000015171417746362400211250ustar00rootroot00000000000000PNG  IHDRĴl;IDATxYU[\۶vm۶m#m'w::o|6zE=~Ŏ]̶[q:Zn?,μ=v(8a/E"W<әrR+df/-P!*1*p{@i՟涆`<HĦ@x]cr!ʚ*PXc<"qvT7B=w:t^z&REa:{"2oG`(a GB~: yfb*yXlBOo;m8/1JCV7Lnx{#kWT(*Yj[{@W;mCIg#x:*,1IpGt#`YMQ\V-ĵ3ظj>+˖I<kswV q"{)m?Gg|v ݹ rGE{{yZBuֈؙ PRNե a|.$D[){C&դKYxIޟ70L/| "YsVB@W- ·lxVI9?[m:~6!;gbhΥZ(|8/zZ'Dm,{Nݰ]hikD]cHhGw]hߘ=5 ^n @Md&@@ ܐ/6Wb&p`LH{FUIENDB`cppcheck-2.7/gui/images/showstylewarnings.png000066400000000000000000000017561417746362400215420ustar00rootroot00000000000000PNG  IHDRĴl;IDATxbS YSr+v&?h$ǥieH+ ñm;).Ķg۶m^aVcwT}W׾@ie@Bj ۞3pCuGɋeu(l XoyZy1&:U L,"&mVx`rTr9;Zcf#}ktU4|Xo¼r:W)rͦP0Eo04̽H#vȨ:U2m%3/PVC[Emo+5]ve^,8cMz FmnEG<]kuJXѡ*-%rs!;h)mtŒd|!vk4.գV^FzVaowrd[#>Ǜ:OqYY_o]v|,` D8NdmTe$n+5/i2-Ż;M8Q O{\L(iEQ9Y,UNzR62}֊6,fS۪[In4jcɌSXg(KNX6oy<πq[@oV񕡶d 4{ [8Gp8X}muY}O 5#lrnjq+ ;-F%n8 sz&aƕiJ޽w]dy>wXMj3:L60A0kr#kL|_KtyJ}~pYo~V"1i<((\w`*.1vz4md TTlT.z5ȃ'ӏ0xH_o+|?f.fF1%cU+>|f.f|]\w6"!#  w#7u$b՚,qIENDB`cppcheck-2.7/gui/images/showwarnings.png000066400000000000000000000014451417746362400204540ustar00rootroot00000000000000PNG  IHDRĴl;IDATxVsN{sgjgj۶95Am^f.&X3m7[@b`S&6H)*V_& QQ! ;mB,j,۽9u5C@P5MSi;v*%Ĺ=y՘]ܼ :`n7ޭ,iΚE#P"HJ#Mv B1-j9k/Bgɓp#oקRR:3|nQd8y /vívyR.[F0BB`x"+ΥWK q[7LF\#t=+x s9<\EE*l@?V5&D(D|p!R֡!֬YCgOL+ufMrfh]L@=#qZ*EqǏgmfxk`^.)A54`twޮZ=ܓ#8,%}X ZV:z` ό;Vgx"M(// o?UI{{aKxxv>]6>kQTk~eˎ{^7ŋq\U aj<~;6(ռD"B%yO cs?e͢EO?KB?8uxssq@0千}0 u*Q⯨QIENDB`cppcheck-2.7/gui/images/text-x-generic.png000066400000000000000000000006171417746362400205660ustar00rootroot00000000000000PNG  IHDRĴl;VIDATxb9jmΖ&l(%k$b -G7߇,33DsՉG MLZ eYbSyê4t7b*^Q( mH]UA  @^:qa#P4M߿SkyAN-rmdY]~!-^#1k̎DxvbZ1R}LgRF!|6zE=~Ŏ]̶[q:Zn?,μ=v(8a/E"W<әrR+df/-P!*1*p{@i՟涆`<HĦ@x]cr!ʚ*PXc<"qvT7B=w:t^z&REa:{"2oG`(a GB~: yfb*yXlBOo;m8/1JCV7Lnx{#kWT(*Yj[{@W;mCIg#x:*,1IpGt#`YMQ\V-ĵ3ظj>+˖I<kswV q"{)m?Gg|v ݹ rGE{{yZBuֈؙ PRNե a|.$D[){C&դKYxIޟ70L/| "YsVB@W- ·lxVI9?[m:~6!;gbhΥZ(|8/zZ'Dm,{Nݰ]hikD]cHhGw]hߘ=5 ^n @Md&@@ ܐ/6Wb&p`LH{FUIENDB`cppcheck-2.7/gui/images/verify.svg000066400000000000000000000044211417746362400172370ustar00rootroot00000000000000 image/svg+xml cppcheck-2.7/gui/images/view-recheck.png000066400000000000000000000023451417746362400202770ustar00rootroot00000000000000PNG  IHDRĴl;IDATxŕ{PTu5JL+D0*r 3,o#ede>xbл?ʲLTsv2HL.²sekgsw{]~bcn%.HS{,+8"(kP>+4K@F3ӏ,|'8m zb35}P4-à<Bf^uWF4[@9BMbcDr G eI`R_',B%2UuBq^oMPթ WuPujDuAQ $r$O#çVhđ8@$ f3x!X$A0JyN)sqX|*_ugzIQ"< Mr:w!,AyP&#GN}ӞWS-N=IcdvË ܌`c=f: l41|jʆ4gBC- 6A] :ȶXN?X$޴g5pk܀2zG 6̂N=:AO=[ҳ{X_m:tW>=m_;We(K;/%#=ꃂƝ&8{~~>Zj:gx , _CcWv|Xlv3 rnm}z(D;_Ꙙv+G0eƑ{iR.&o KPюH+aQ*i.>8Q]F׾7'Ԝ9\T /Ԋ >i3c~ Dl|x/:ˬQIJ36nM39b1E7B-su-i;|#*BqQann_w_42P^B ҬfX@ygcrOk :h#IENDB`cppcheck-2.7/gui/images/view-refresh.png000066400000000000000000000011421417746362400203230ustar00rootroot00000000000000PNG  IHDRĴl;)IDATx-YEѶg۶նm۶m۶m߶+LVp- X]9ˀ3Bjp2ʶwb#Zzft,ezӔY&FgX6RblIl^P XV^\4նG#qK\hf⦞ ׆MTT{)t15U-.|Pʴ1$cMN1NPCju ȕz`jv=,ىvj%`τbvl,Sb7=Wm7`9b" wd;f-<[Q~Ȑ6/yfՑY˺%MΉpWOrV)۞e&krfOunv.#*F%ΖZ$hhIENDB`cppcheck-2.7/gui/libraryaddfunctiondialog.cpp000066400000000000000000000026171417746362400215210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "libraryaddfunctiondialog.h" #include "ui_libraryaddfunctiondialog.h" #include LibraryAddFunctionDialog::LibraryAddFunctionDialog(QWidget *parent) : QDialog(parent), mUi(new Ui::LibraryAddFunctionDialog) { mUi->setupUi(this); QRegExp rx(NAMES); QValidator *validator = new QRegExpValidator(rx, this); mUi->functionName->setValidator(validator); } LibraryAddFunctionDialog::~LibraryAddFunctionDialog() { delete mUi; } QString LibraryAddFunctionDialog::functionName() const { return mUi->functionName->text(); } int LibraryAddFunctionDialog::numberOfArguments() const { return mUi->numberOfArguments->value(); } cppcheck-2.7/gui/libraryaddfunctiondialog.h000066400000000000000000000031551417746362400211640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LIBRARYADDFUNCTIONDIALOG_H #define LIBRARYADDFUNCTIONDIALOG_H #include #define SIMPLENAME "[_a-zA-Z][_a-zA-Z0-9]*" // just a name #define SCOPENAME SIMPLENAME "(::" SIMPLENAME ")*" // names with optional scope #define NAMES SCOPENAME "(," SCOPENAME ")*" // names can be separated by comma namespace Ui { class LibraryAddFunctionDialog; } class LibraryAddFunctionDialog : public QDialog { Q_OBJECT public: explicit LibraryAddFunctionDialog(QWidget *parent = nullptr); LibraryAddFunctionDialog(const LibraryAddFunctionDialog &) = delete; ~LibraryAddFunctionDialog(); LibraryAddFunctionDialog &operator=(const LibraryAddFunctionDialog &) = delete; QString functionName() const; int numberOfArguments() const; private: Ui::LibraryAddFunctionDialog *mUi; }; #endif // LIBRARYADDFUNCTIONDIALOG_H cppcheck-2.7/gui/libraryaddfunctiondialog.ui000066400000000000000000000056531417746362400213570ustar00rootroot00000000000000 LibraryAddFunctionDialog Qt::WindowModal 0 0 353 89 0 0 Add function true Function name(s) Number of arguments Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() LibraryAddFunctionDialog accept() 57 71 71 87 buttonBox rejected() LibraryAddFunctionDialog reject() 132 69 156 87 validateName(QString) cppcheck-2.7/gui/librarydialog.cpp000066400000000000000000000260741417746362400173050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "librarydialog.h" #include "ui_librarydialog.h" #include "common.h" #include "libraryaddfunctiondialog.h" #include "libraryeditargdialog.h" #include "path.h" #include #include #include #include // TODO: get/compare functions from header class FunctionListItem : public QListWidgetItem { public: FunctionListItem(QListWidget *view, CppcheckLibraryData::Function *function, bool selected) : QListWidgetItem(view), function(function) { setText(function->name); setFlags(flags() | Qt::ItemIsEditable); setSelected(selected); } CppcheckLibraryData::Function *function; }; LibraryDialog::LibraryDialog(QWidget *parent) : QDialog(parent), mUi(new Ui::LibraryDialog), mIgnoreChanges(false) { mUi->setupUi(this); mUi->buttonSave->setEnabled(false); mUi->buttonSaveAs->setEnabled(false); mUi->sortFunctions->setEnabled(false); mUi->filter->setEnabled(false); mUi->addFunction->setEnabled(false); //As no function selected, this disables function editing widgets selectFunction(); } LibraryDialog::~LibraryDialog() { delete mUi; } CppcheckLibraryData::Function *LibraryDialog::currentFunction() { QList selitems = mUi->functions->selectedItems(); if (selitems.count() != 1) return nullptr; return static_cast(selitems.first())->function; } void LibraryDialog::openCfg() { const QString datadir = getDataDir(); QString selectedFilter; const QString filter(tr("Library files (*.cfg)")); const QString selectedFile = QFileDialog::getOpenFileName(this, tr("Open library file"), datadir, filter, &selectedFilter); if (selectedFile.isEmpty()) return; QFile file(selectedFile); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("Cannot open file %1.").arg(selectedFile), QMessageBox::Ok, this); msg.exec(); return; } CppcheckLibraryData tempdata; const QString errmsg = tempdata.open(file); if (!errmsg.isNull()) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("Failed to load %1. %2.").arg(selectedFile).arg(errmsg), QMessageBox::Ok, this); msg.exec(); return; } mIgnoreChanges = true; mData.swap(tempdata); mFileName = selectedFile; mUi->buttonSave->setEnabled(false); mUi->buttonSaveAs->setEnabled(true); mUi->filter->clear(); mUi->functions->clear(); for (CppcheckLibraryData::Function &function : mData.functions) { mUi->functions->addItem(new FunctionListItem(mUi->functions, &function, false)); } mUi->sortFunctions->setEnabled(!mData.functions.empty()); mUi->filter->setEnabled(!mData.functions.empty()); mUi->addFunction->setEnabled(true); mIgnoreChanges = false; } void LibraryDialog::saveCfg() { if (mFileName.isNull()) return; QFile file(mFileName); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream ts(&file); ts << mData.toString() << '\n'; mUi->buttonSave->setEnabled(false); } else { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("Cannot save file %1.").arg(mFileName), QMessageBox::Ok, this); msg.exec(); } } void LibraryDialog::saveCfgAs() { const QString filter(tr("Library files (*.cfg)")); const QString path = Path::getPathFromFilename(mFileName.toStdString()).c_str(); QString selectedFile = QFileDialog::getSaveFileName(this, tr("Save the library as"), path, filter); if (selectedFile.isEmpty()) return; if (!selectedFile.endsWith(".cfg", Qt::CaseInsensitive)) selectedFile += ".cfg"; mFileName = selectedFile; saveCfg(); } void LibraryDialog::addFunction() { LibraryAddFunctionDialog *d = new LibraryAddFunctionDialog; if (d->exec() == QDialog::Accepted && !d->functionName().isEmpty()) { CppcheckLibraryData::Function f; f.name = d->functionName(); int args = d->numberOfArguments(); for (int i = 1; i <= args; i++) { CppcheckLibraryData::Function::Arg arg; arg.nr = i; f.args.append(arg); } mData.functions.append(f); mUi->functions->addItem(new FunctionListItem(mUi->functions, &mData.functions.back(), false)); mUi->buttonSave->setEnabled(true); mUi->sortFunctions->setEnabled(!mData.functions.empty()); mUi->filter->setEnabled(!mData.functions.empty()); } delete d; } void LibraryDialog::editFunctionName(QListWidgetItem* item) { if (mIgnoreChanges) return; QString functionName = item->text(); CppcheckLibraryData::Function * const function = dynamic_cast(item)->function; if (functionName != function->name) { if (QRegExp(NAMES).exactMatch(functionName)) { function->name = functionName; mUi->buttonSave->setEnabled(true); } else { mIgnoreChanges = true; item->setText(function->name); mIgnoreChanges = false; } } } void LibraryDialog::selectFunction() { const CppcheckLibraryData::Function * const function = currentFunction(); if (function == nullptr) { mUi->comments->clear(); mUi->comments->setEnabled(false); mUi->noreturn->setCurrentIndex(0); mUi->noreturn->setEnabled(false); mUi->useretval->setChecked(false); mUi->useretval->setEnabled(false); mUi->leakignore->setChecked(false); mUi->leakignore->setEnabled(false); mUi->arguments->clear(); mUi->arguments->setEnabled(false); mUi->editArgButton->setEnabled(false); return; } mIgnoreChanges = true; mUi->comments->setPlainText(function->comments); mUi->comments->setEnabled(true); mUi->noreturn->setCurrentIndex(function->noreturn); mUi->noreturn->setEnabled(true); mUi->useretval->setChecked(function->useretval); mUi->useretval->setEnabled(true); mUi->leakignore->setChecked(function->leakignore); mUi->leakignore->setEnabled(true); updateArguments(*function); mUi->arguments->setEnabled(true); mUi->editArgButton->setEnabled(true); mIgnoreChanges = false; } void LibraryDialog::sortFunctions(bool sort) { if (sort) { mUi->functions->sortItems(); } else { mIgnoreChanges = true; CppcheckLibraryData::Function *selfunction = currentFunction(); mUi->functions->clear(); for (CppcheckLibraryData::Function &function : mData.functions) { mUi->functions->addItem(new FunctionListItem(mUi->functions, &function, selfunction == &function)); } if (!mUi->filter->text().isEmpty()) filterFunctions(mUi->filter->text()); mIgnoreChanges = false; } } void LibraryDialog::filterFunctions(const QString& filter) { QList allItems = mUi->functions->findItems(QString(), Qt::MatchContains); if (filter.isEmpty()) { foreach (QListWidgetItem *item, allItems) { item->setHidden(false); } } else { foreach (QListWidgetItem *item, allItems) { item->setHidden(!item->text().startsWith(filter)); } } } void LibraryDialog::changeFunction() { if (mIgnoreChanges) return; CppcheckLibraryData::Function *function = currentFunction(); if (!function) return; function->comments = mUi->comments->toPlainText(); function->noreturn = (CppcheckLibraryData::Function::TrueFalseUnknown)mUi->noreturn->currentIndex(); function->useretval = mUi->useretval->isChecked(); function->leakignore = mUi->leakignore->isChecked(); mUi->buttonSave->setEnabled(true); } void LibraryDialog::editArg() { CppcheckLibraryData::Function *function = currentFunction(); if (!function) return; if (mUi->arguments->selectedItems().count() != 1) return; CppcheckLibraryData::Function::Arg &arg = function->args[mUi->arguments->row(mUi->arguments->selectedItems().first())]; LibraryEditArgDialog d(nullptr, arg); if (d.exec() == QDialog::Accepted) { unsigned number = arg.nr; arg = d.getArg(); arg.nr = number; mUi->arguments->selectedItems().first()->setText(getArgText(arg)); } mUi->buttonSave->setEnabled(true); } QString LibraryDialog::getArgText(const CppcheckLibraryData::Function::Arg &arg) { QString s("arg"); if (arg.nr != CppcheckLibraryData::Function::Arg::ANY) s += QString::number(arg.nr); s += "\n not bool: " + QString(arg.notbool ? "true" : "false"); s += "\n not null: " + QString(arg.notnull ? "true" : "false"); s += "\n not uninit: " + QString(arg.notuninit ? "true" : "false"); s += "\n format string: " + QString(arg.formatstr ? "true" : "false"); s += "\n strz: " + QString(arg.strz ? "true" : "false"); s += "\n valid: " + QString(arg.valid.isEmpty() ? "any" : arg.valid); foreach (const CppcheckLibraryData::Function::Arg::MinSize &minsize, arg.minsizes) { s += "\n minsize: " + minsize.type + " " + minsize.arg + " " + minsize.arg2; } return s; } void LibraryDialog::updateArguments(const CppcheckLibraryData::Function &function) { mUi->arguments->clear(); foreach (const CppcheckLibraryData::Function::Arg &arg, function.args) { mUi->arguments->addItem(getArgText(arg)); } } cppcheck-2.7/gui/librarydialog.h000066400000000000000000000034321417746362400167430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LIBRARYDIALOG_H #define LIBRARYDIALOG_H #include "cppchecklibrarydata.h" #include class QListWidgetItem; namespace Ui { class LibraryDialog; } class LibraryDialog : public QDialog { Q_OBJECT public: explicit LibraryDialog(QWidget *parent = nullptr); LibraryDialog(const LibraryDialog &) = delete; ~LibraryDialog(); LibraryDialog &operator=(const LibraryDialog &) = delete; private slots: void openCfg(); void saveCfg(); void saveCfgAs(); void addFunction(); void changeFunction(); void editArg(); void editFunctionName(QListWidgetItem*); void filterFunctions(const QString&); void selectFunction(); void sortFunctions(bool); private: Ui::LibraryDialog *mUi; CppcheckLibraryData mData; QString mFileName; bool mIgnoreChanges; static QString getArgText(const CppcheckLibraryData::Function::Arg &arg); CppcheckLibraryData::Function *currentFunction(); void updateArguments(const CppcheckLibraryData::Function &function); }; #endif // LIBRARYDIALOG_H cppcheck-2.7/gui/librarydialog.ui000066400000000000000000000323361417746362400171360ustar00rootroot00000000000000 LibraryDialog 0 0 869 588 Library Editor Open Save Save as Qt::Horizontal 40 20 Functions Sort true false Qt::Horizontal 40 20 QAbstractItemView::DoubleClicked QAbstractItemView::SelectRows Add Qt::Horizontal 40 20 Filter: 0 32 16777215 80 Comments Qt::Vertical 20 40 QPlainTextEdit::NoWrap 0 0 noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Qt::Horizontal 40 20 buttonOpen clicked() LibraryDialog openCfg() 59 18 66 34 useretval clicked() LibraryDialog changeFunction() 550 157 561 157 leakignore clicked() LibraryDialog changeFunction() 531 183 540 182 buttonSave clicked() LibraryDialog saveCfg() 118 16 130 32 addFunction clicked() LibraryDialog addFunction() 53 564 63 582 editArgButton clicked() LibraryDialog editArg() 488 580 497 580 arguments itemDoubleClicked(QListWidgetItem*) LibraryDialog editArg() 529 223 543 223 sortFunctions toggled(bool) LibraryDialog sortFunctions(bool) 45 66 58 66 filter textChanged(QString) LibraryDialog filterFunctions(QString) 429 575 273 582 functions itemSelectionChanged() LibraryDialog selectFunction() 190 141 203 140 noreturn currentIndexChanged(int) LibraryDialog changeFunction() 545 137 552 138 functions itemChanged(QListWidgetItem*) LibraryDialog editFunctionName(QListWidgetItem*) 217 104 235 104 noreturn editTextChanged(QString) LibraryDialog changeFunction() 548 128 555 128 comments textChanged() LibraryDialog changeFunction() 567 50 584 48 buttonSaveAs clicked() LibraryDialog saveCfgAs() 211 31 232 33 addFunction() argumentChanged(QListWidgetItem*) changeFunction() editArg() filterFunctions(QString) openCfg() saveCfg() selectFunction() sortFunctions(bool) editFunctionName(QListWidgetItem*) saveCfgAs() cppcheck-2.7/gui/libraryeditargdialog.cpp000066400000000000000000000110701417746362400206330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "libraryeditargdialog.h" #include "ui_libraryeditargdialog.h" LibraryEditArgDialog::LibraryEditArgDialog(QWidget *parent, const CppcheckLibraryData::Function::Arg &arg) : QDialog(parent), mUi(new Ui::LibraryEditArgDialog), mMinSizes(arg.minsizes) { mUi->setupUi(this); mUi->notbool->setChecked(arg.notbool); mUi->notnull->setChecked(arg.notnull); mUi->notuninit->setChecked(arg.notuninit); mUi->strz->setChecked(arg.strz); mUi->formatstr->setChecked(arg.formatstr); mUi->valid->setText(arg.valid); mUi->minsize1type->setEnabled(true); mUi->minsize1arg->setEnabled(arg.minsizes.count() >= 1); mUi->minsize1arg2->setEnabled(arg.minsizes.count() >= 1 && arg.minsizes[0].type == "mul"); mUi->minsize2type->setEnabled(arg.minsizes.count() >= 1); mUi->minsize2arg->setEnabled(arg.minsizes.count() >= 2); mUi->minsize2arg2->setEnabled(arg.minsizes.count() >= 2 && arg.minsizes[1].type == "mul"); QStringList items; items << "None" << "argvalue" << "mul" << "sizeof" << "strlen"; mUi->minsize1type->clear(); mUi->minsize1type->addItems(items); if (arg.minsizes.count() >= 1) { mUi->minsize1type->setCurrentIndex(items.indexOf(mMinSizes[0].type)); mUi->minsize1arg->setValue(mMinSizes[0].arg.toInt()); if (arg.minsizes[0].type == "mul") mUi->minsize1arg2->setValue(mMinSizes[0].arg2.toInt()); else mUi->minsize1arg2->setValue(0); } else { mUi->minsize1type->setCurrentIndex(0); mUi->minsize1arg->setValue(0); mUi->minsize1arg2->setValue(0); } mUi->minsize2type->clear(); mUi->minsize2type->addItems(items); if (arg.minsizes.count() >= 2) { mUi->minsize2type->setCurrentIndex(items.indexOf(mMinSizes[1].type)); mUi->minsize2arg->setValue(mMinSizes[1].arg.toInt()); if (arg.minsizes[1].type == "mul") mUi->minsize2arg2->setValue(mMinSizes[1].arg2.toInt()); else mUi->minsize2arg2->setValue(0); } else { mUi->minsize2type->setCurrentIndex(0); mUi->minsize2arg->setValue(0); mUi->minsize2arg2->setValue(0); } } LibraryEditArgDialog::~LibraryEditArgDialog() { delete mUi; } CppcheckLibraryData::Function::Arg LibraryEditArgDialog::getArg() const { CppcheckLibraryData::Function::Arg ret; ret.notbool = mUi->notbool->isChecked(); ret.notnull = mUi->notnull->isChecked(); ret.notuninit = mUi->notuninit->isChecked(); ret.strz = mUi->strz->isChecked(); ret.formatstr = mUi->formatstr->isChecked(); if (mUi->minsize1type->currentIndex() != 0) { CppcheckLibraryData::Function::Arg::MinSize minsize1; minsize1.type = mUi->minsize1type->currentText(); minsize1.arg = QString::number(mUi->minsize1arg->value()); if (minsize1.type == "mul") minsize1.arg2 = QString::number(mUi->minsize1arg2->value()); ret.minsizes.append(minsize1); if (mUi->minsize2type->currentIndex() != 0) { CppcheckLibraryData::Function::Arg::MinSize minsize2; minsize2.type = mUi->minsize2type->currentText(); minsize2.arg = QString::number(mUi->minsize2arg->value()); if (minsize2.type == "mul") minsize2.arg2 = QString::number(mUi->minsize2arg2->value()); ret.minsizes.append(minsize2); } } ret.valid = mUi->valid->text(); return ret; } void LibraryEditArgDialog::minsizeChanged(int) { mUi->minsize1arg->setEnabled(mUi->minsize1type->currentIndex() != 0); mUi->minsize1arg2->setEnabled(mUi->minsize1type->currentText() == "mul"); mUi->minsize2type->setEnabled(mUi->minsize1type->currentIndex() != 0); mUi->minsize2arg->setEnabled(mUi->minsize2type->currentIndex() != 0); mUi->minsize2arg2->setEnabled(mUi->minsize2type->currentText() == "mul"); } cppcheck-2.7/gui/libraryeditargdialog.h000066400000000000000000000027311417746362400203040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef LIBRARYEDITARGDIALOG_H #define LIBRARYEDITARGDIALOG_H #include "cppchecklibrarydata.h" #include namespace Ui { class LibraryEditArgDialog; } class LibraryEditArgDialog : public QDialog { Q_OBJECT public: LibraryEditArgDialog(QWidget *parent, const CppcheckLibraryData::Function::Arg &arg); LibraryEditArgDialog(const LibraryEditArgDialog &) = delete; ~LibraryEditArgDialog(); LibraryEditArgDialog &operator=(const LibraryEditArgDialog &) = delete; CppcheckLibraryData::Function::Arg getArg() const; private slots: void minsizeChanged(int); private: Ui::LibraryEditArgDialog *mUi; QList mMinSizes; }; #endif // LIBRARYEDITARGDIALOG_H cppcheck-2.7/gui/libraryeditargdialog.ui000066400000000000000000000247571417746362400205060ustar00rootroot00000000000000 LibraryEditArgDialog 0 0 448 465 Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String 0 0 Format string Qt::Vertical 20 40 Min size of buffer Type None argvalue mul strlen Arg Qt::Horizontal 40 20 Arg2 Qt::Horizontal 40 20 0 0 and Type true None argvalue mul strlen Arg Qt::Horizontal 40 20 Arg2 Qt::Horizontal 40 20 Qt::Vertical 20 40 Valid values Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() LibraryEditArgDialog accept() 226 460 157 274 buttonBox rejected() LibraryEditArgDialog reject() 290 439 286 274 minsize1type currentIndexChanged(int) LibraryEditArgDialog minsizeChanged(int) 413 194 446 175 minsize2type currentIndexChanged(int) LibraryEditArgDialog minsizeChanged(int) 436 299 447 297 minsizeChanged(int) cppcheck-2.7/gui/main.cpp000066400000000000000000000105121417746362400153730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppcheck.h" #include "common.h" #include "mainwindow.h" #include "erroritem.h" #include "translationhandler.h" #ifdef _WIN32 #include "aboutdialog.h" #include #else #include #endif #include #include #include #include static void ShowUsage(); static void ShowVersion(); static bool CheckArgs(const QStringList &args); int main(int argc, char *argv[]) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif QApplication app(argc, argv); QCoreApplication::setOrganizationName("Cppcheck"); QCoreApplication::setApplicationName("Cppcheck-GUI"); QSettings* settings = new QSettings("Cppcheck", "Cppcheck-GUI", &app); // Set data dir.. foreach (const QString arg, app.arguments()) { if (arg.startsWith("--data-dir=")) { settings->setValue("DATADIR", arg.mid(11)); return 0; } } TranslationHandler* th = new TranslationHandler(&app); th->setLanguage(settings->value(SETTINGS_LANGUAGE, th->suggestLanguage()).toString()); if (!CheckArgs(QApplication::arguments())) return 0; QApplication::setWindowIcon(QIcon(":cppcheck-gui.png")); // Register this metatype that is used to transfer error info qRegisterMetaType("ErrorItem"); MainWindow window(th, settings); window.show(); return QApplication::exec(); } // Check only arguments needing action before GUI is shown. // Rest of the arguments are handled in MainWindow::HandleCLIParams() static bool CheckArgs(const QStringList &args) { if (args.contains("-h") || args.contains("--help")) { ShowUsage(); return false; } if (args.contains("-v") || args.contains("--version")) { ShowVersion(); return false; } return true; } static void ShowUsage() { QString helpMessage = MainWindow::tr( "Cppcheck GUI.\n\n" "Syntax:\n" " cppcheck-gui [OPTIONS] [files or paths]\n\n" "Options:\n" " -h, --help Print this help\n" " -p Open given project file and start checking it\n" " -l Open given results xml file\n" " -d Specify the directory that was checked to generate the results xml specified with -l\n" " -v, --version Show program version\n" " --data-dir= This option is for installation scripts so they can configure the directory where\n" " datafiles are located (translations, cfg). The GUI is not started when this option\n" " is used."); #if defined(_WIN32) QMessageBox msgBox(QMessageBox::Information, MainWindow::tr("Cppcheck GUI - Command line parameters"), helpMessage, QMessageBox::Ok ); (void)msgBox.exec(); #else std::cout << helpMessage.toStdString() << std::endl; #endif } static void ShowVersion() { #if defined(_WIN32) AboutDialog *dlg = new AboutDialog(CppCheck::version(), CppCheck::extraVersion(), 0); dlg->exec(); delete dlg; #else std::string versionMessage("Cppcheck "); versionMessage += CppCheck::version(); const char * extraVersion = CppCheck::extraVersion(); if (*extraVersion != 0) versionMessage += std::string(" (") + extraVersion + ")"; std::cout << versionMessage << std::endl; #endif } cppcheck-2.7/gui/mainwindow.cpp000066400000000000000000002143131417746362400166300ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mainwindow.h" #include "applicationlist.h" #include "aboutdialog.h" #include "common.h" #include "cppcheck.h" #include "filelist.h" #include "fileviewdialog.h" #include "functioncontractdialog.h" #include "helpdialog.h" #include "librarydialog.h" #include "projectfile.h" #include "projectfiledialog.h" #include "report.h" #include "scratchpad.h" #include "showtypes.h" #include "statsdialog.h" #include "settingsdialog.h" #include "threadhandler.h" #include "threadresult.h" #include "translationhandler.h" #include "variablecontractsdialog.h" #include #include #include #include #include #include #include #include #include #include #include static const QString OnlineHelpURL("https://cppcheck.sourceforge.io/manual.html"); static const QString compile_commands_json("compile_commands.json"); MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) : mSettings(settings), mApplications(new ApplicationList(this)), mTranslation(th), mScratchPad(nullptr), mProjectFile(nullptr), mPlatformActions(new QActionGroup(this)), mCStandardActions(new QActionGroup(this)), mCppStandardActions(new QActionGroup(this)), mSelectLanguageActions(new QActionGroup(this)), mExiting(false), mIsLogfileLoaded(false) { mUI.setupUi(this); mThread = new ThreadHandler(this); mThread->setDataDir(getDataDir()); mUI.mResults->initialize(mSettings, mApplications, mThread); // Filter timer to delay filtering results slightly while typing mFilterTimer = new QTimer(this); mFilterTimer->setInterval(500); mFilterTimer->setSingleShot(true); connect(mFilterTimer, &QTimer::timeout, this, &MainWindow::filterResults); // "Filter" toolbar mLineEditFilter = new QLineEdit(mUI.mToolBarFilter); mLineEditFilter->setPlaceholderText(tr("Quick Filter:")); mLineEditFilter->setClearButtonEnabled(true); mUI.mToolBarFilter->addWidget(mLineEditFilter); connect(mLineEditFilter, SIGNAL(textChanged(const QString&)), mFilterTimer, SLOT(start())); connect(mLineEditFilter, &QLineEdit::returnPressed, this, &MainWindow::filterResults); connect(mUI.mActionPrint, SIGNAL(triggered()), mUI.mResults, SLOT(print())); connect(mUI.mActionPrintPreview, SIGNAL(triggered()), mUI.mResults, SLOT(printPreview())); connect(mUI.mActionQuit, &QAction::triggered, this, &MainWindow::close); connect(mUI.mActionAnalyzeFiles, &QAction::triggered, this, &MainWindow::analyzeFiles); connect(mUI.mActionAnalyzeDirectory, &QAction::triggered, this, &MainWindow::analyzeDirectory); connect(mUI.mActionSettings, &QAction::triggered, this, &MainWindow::programSettings); connect(mUI.mActionClearResults, &QAction::triggered, this, &MainWindow::clearResults); connect(mUI.mActionOpenXML, &QAction::triggered, this, &MainWindow::openResults); connect(mUI.mActionShowStyle, &QAction::toggled, this, &MainWindow::showStyle); connect(mUI.mActionShowErrors, &QAction::toggled, this, &MainWindow::showErrors); connect(mUI.mActionShowWarnings, &QAction::toggled, this, &MainWindow::showWarnings); connect(mUI.mActionShowPortability, &QAction::toggled, this, &MainWindow::showPortability); connect(mUI.mActionShowPerformance, &QAction::toggled, this, &MainWindow::showPerformance); connect(mUI.mActionShowInformation, &QAction::toggled, this, &MainWindow::showInformation); connect(mUI.mActionShowCppcheck, &QAction::toggled, mUI.mResults, &ResultsView::showCppcheckResults); connect(mUI.mActionShowClang, &QAction::toggled, mUI.mResults, &ResultsView::showClangResults); connect(mUI.mActionCheckAll, &QAction::triggered, this, &MainWindow::checkAll); connect(mUI.mActionUncheckAll, &QAction::triggered, this, &MainWindow::uncheckAll); connect(mUI.mActionCollapseAll, &QAction::triggered, mUI.mResults, &ResultsView::collapseAllResults); connect(mUI.mActionExpandAll, &QAction::triggered, mUI.mResults, &ResultsView::expandAllResults); connect(mUI.mActionShowHidden, &QAction::triggered, mUI.mResults, &ResultsView::showHiddenResults); connect(mUI.mActionViewStats, &QAction::triggered, this, &MainWindow::showStatistics); connect(mUI.mActionLibraryEditor, &QAction::triggered, this, &MainWindow::showLibraryEditor); connect(mUI.mActionReanalyzeModified, &QAction::triggered, this, &MainWindow::reAnalyzeModified); connect(mUI.mActionReanalyzeAll, &QAction::triggered, this, &MainWindow::reAnalyzeAll); connect(mUI.mActionCheckLibrary, &QAction::triggered, this, &MainWindow::checkLibrary); connect(mUI.mActionCheckConfiguration, &QAction::triggered, this, &MainWindow::checkConfiguration); connect(mUI.mActionStop, &QAction::triggered, this, &MainWindow::stopAnalysis); connect(mUI.mActionSave, &QAction::triggered, this, &MainWindow::save); // About menu connect(mUI.mActionAbout, &QAction::triggered, this, &MainWindow::about); connect(mUI.mActionLicense, &QAction::triggered, this, &MainWindow::showLicense); // View > Toolbar menu connect(mUI.mActionToolBarMain, SIGNAL(toggled(bool)), this, SLOT(toggleMainToolBar())); connect(mUI.mActionToolBarView, SIGNAL(toggled(bool)), this, SLOT(toggleViewToolBar())); connect(mUI.mActionToolBarFilter, SIGNAL(toggled(bool)), this, SLOT(toggleFilterToolBar())); connect(mUI.mActionAuthors, &QAction::triggered, this, &MainWindow::showAuthors); connect(mThread, &ThreadHandler::done, this, &MainWindow::analysisDone); connect(mThread, &ThreadHandler::log, mUI.mResults, &ResultsView::log); connect(mThread, &ThreadHandler::debugError, mUI.mResults, &ResultsView::debugError); connect(mThread, &ThreadHandler::bughuntingReportLine, mUI.mResults, &ResultsView::bughuntingReportLine); connect(mUI.mResults, &ResultsView::gotResults, this, &MainWindow::resultsAdded); connect(mUI.mResults, &ResultsView::resultsHidden, mUI.mActionShowHidden, &QAction::setEnabled); connect(mUI.mResults, &ResultsView::checkSelected, this, &MainWindow::performSelectedFilesCheck); connect(mUI.mResults, &ResultsView::suppressIds, this, &MainWindow::suppressIds); connect(mUI.mResults, &ResultsView::editFunctionContract, this, &MainWindow::editFunctionContract); connect(mUI.mResults, &ResultsView::editVariableContract, this, &MainWindow::editVariableContract); connect(mUI.mResults, &ResultsView::deleteFunctionContract, this, &MainWindow::deleteFunctionContract); connect(mUI.mResults, &ResultsView::deleteVariableContract, this, &MainWindow::deleteVariableContract); connect(mUI.mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu); // File menu connect(mUI.mActionNewProjectFile, &QAction::triggered, this, &MainWindow::newProjectFile); connect(mUI.mActionOpenProjectFile, &QAction::triggered, this, &MainWindow::openProjectFile); connect(mUI.mActionShowScratchpad, &QAction::triggered, this, &MainWindow::showScratchpad); connect(mUI.mActionCloseProjectFile, &QAction::triggered, this, &MainWindow::closeProjectFile); connect(mUI.mActionEditProjectFile, &QAction::triggered, this, &MainWindow::editProjectFile); connect(mUI.mActionHelpContents, &QAction::triggered, this, &MainWindow::openHelpContents); loadSettings(); mThread->initialize(mUI.mResults); if (mProjectFile) formatAndSetTitle(tr("Project:") + ' ' + mProjectFile->getFilename()); else formatAndSetTitle(); enableCheckButtons(true); mUI.mActionPrint->setShortcut(QKeySequence::Print); enableResultsButtons(); enableProjectOpenActions(true); enableProjectActions(false); // Must setup MRU menu before CLI param handling as it can load a // project file and update MRU menu. for (int i = 0; i < MaxRecentProjects; ++i) { mRecentProjectActs[i] = new QAction(this); mRecentProjectActs[i]->setVisible(false); connect(mRecentProjectActs[i], SIGNAL(triggered()), this, SLOT(openRecentProject())); } mRecentProjectActs[MaxRecentProjects] = nullptr; // The separator mUI.mActionProjectMRU->setVisible(false); updateMRUMenuItems(); QStringList args = QCoreApplication::arguments(); //Remove the application itself args.removeFirst(); if (!args.isEmpty()) { handleCLIParams(args); } mUI.mActionCloseProjectFile->setEnabled(mProjectFile != nullptr); mUI.mActionEditProjectFile->setEnabled(mProjectFile != nullptr); for (int i = 0; i < mPlatforms.getCount(); i++) { Platform platform = mPlatforms.mPlatforms[i]; QAction *action = new QAction(this); platform.mActMainWindow = action; mPlatforms.mPlatforms[i] = platform; action->setText(platform.mTitle); action->setData(platform.mType); action->setCheckable(true); action->setActionGroup(mPlatformActions); mUI.mMenuAnalyze->insertAction(mUI.mActionPlatforms, action); connect(action, SIGNAL(triggered()), this, SLOT(selectPlatform())); } mUI.mActionC89->setActionGroup(mCStandardActions); mUI.mActionC99->setActionGroup(mCStandardActions); mUI.mActionC11->setActionGroup(mCStandardActions); mUI.mActionCpp03->setActionGroup(mCppStandardActions); mUI.mActionCpp11->setActionGroup(mCppStandardActions); mUI.mActionCpp14->setActionGroup(mCppStandardActions); mUI.mActionCpp17->setActionGroup(mCppStandardActions); mUI.mActionCpp20->setActionGroup(mCppStandardActions); mUI.mActionEnforceC->setActionGroup(mSelectLanguageActions); mUI.mActionEnforceCpp->setActionGroup(mSelectLanguageActions); mUI.mActionAutoDetectLanguage->setActionGroup(mSelectLanguageActions); // For Windows platforms default to Win32 checked platform. // For other platforms default to unspecified/default which means the // platform Cppcheck GUI was compiled on. #if defined(_WIN32) const Settings::PlatformType defaultPlatform = Settings::Win32W; #else const Settings::PlatformType defaultPlatform = Settings::Unspecified; #endif Platform &platform = mPlatforms.get((Settings::PlatformType)mSettings->value(SETTINGS_CHECKED_PLATFORM, defaultPlatform).toInt()); platform.mActMainWindow->setChecked(true); } MainWindow::~MainWindow() { delete mProjectFile; delete mScratchPad; } void MainWindow::handleCLIParams(const QStringList ¶ms) { int index; if (params.contains("-p")) { index = params.indexOf("-p"); if ((index + 1) < params.length()) loadProjectFile(params[index + 1]); } else if (params.contains("-l")) { QString logFile; index = params.indexOf("-l"); if ((index + 1) < params.length()) logFile = params[index + 1]; if (params.contains("-d")) { QString checkedDir; index = params.indexOf("-d"); if ((index + 1) < params.length()) checkedDir = params[index + 1]; loadResults(logFile, checkedDir); } else { loadResults(logFile); } } else if ((index = params.indexOf(QRegExp(".*\\.cppcheck$", Qt::CaseInsensitive), 0)) >= 0 && index < params.length() && QFile(params[index]).exists()) { loadProjectFile(params[index]); } else if ((index = params.indexOf(QRegExp(".*\\.xml$", Qt::CaseInsensitive), 0)) >= 0 && index < params.length() && QFile(params[index]).exists()) { loadResults(params[index],QDir::currentPath()); } else doAnalyzeFiles(params); } void MainWindow::loadSettings() { // Window/dialog sizes if (mSettings->value(SETTINGS_WINDOW_MAXIMIZED, false).toBool()) { showMaximized(); } else { resize(mSettings->value(SETTINGS_WINDOW_WIDTH, 800).toInt(), mSettings->value(SETTINGS_WINDOW_HEIGHT, 600).toInt()); } ShowTypes *types = mUI.mResults->getShowTypes(); mUI.mActionShowStyle->setChecked(types->isShown(ShowTypes::ShowStyle)); mUI.mActionShowErrors->setChecked(types->isShown(ShowTypes::ShowErrors)); mUI.mActionShowWarnings->setChecked(types->isShown(ShowTypes::ShowWarnings)); mUI.mActionShowPortability->setChecked(types->isShown(ShowTypes::ShowPortability)); mUI.mActionShowPerformance->setChecked(types->isShown(ShowTypes::ShowPerformance)); mUI.mActionShowInformation->setChecked(types->isShown(ShowTypes::ShowInformation)); mUI.mActionShowCppcheck->setChecked(true); mUI.mActionShowClang->setChecked(true); Standards standards; standards.setC(mSettings->value(SETTINGS_STD_C, QString()).toString().toStdString()); mUI.mActionC89->setChecked(standards.c == Standards::C89); mUI.mActionC99->setChecked(standards.c == Standards::C99); mUI.mActionC11->setChecked(standards.c == Standards::C11); standards.setCPP(mSettings->value(SETTINGS_STD_CPP, QString()).toString().toStdString()); mUI.mActionCpp03->setChecked(standards.cpp == Standards::CPP03); mUI.mActionCpp11->setChecked(standards.cpp == Standards::CPP11); mUI.mActionCpp14->setChecked(standards.cpp == Standards::CPP14); mUI.mActionCpp17->setChecked(standards.cpp == Standards::CPP17); mUI.mActionCpp20->setChecked(standards.cpp == Standards::CPP20); // Main window settings const bool showMainToolbar = mSettings->value(SETTINGS_TOOLBARS_MAIN_SHOW, true).toBool(); mUI.mActionToolBarMain->setChecked(showMainToolbar); mUI.mToolBarMain->setVisible(showMainToolbar); const bool showViewToolbar = mSettings->value(SETTINGS_TOOLBARS_VIEW_SHOW, true).toBool(); mUI.mActionToolBarView->setChecked(showViewToolbar); mUI.mToolBarView->setVisible(showViewToolbar); const bool showFilterToolbar = mSettings->value(SETTINGS_TOOLBARS_FILTER_SHOW, true).toBool(); mUI.mActionToolBarFilter->setChecked(showFilterToolbar); mUI.mToolBarFilter->setVisible(showFilterToolbar); Settings::Language enforcedLanguage = (Settings::Language)mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt(); if (enforcedLanguage == Settings::CPP) mUI.mActionEnforceCpp->setChecked(true); else if (enforcedLanguage == Settings::C) mUI.mActionEnforceC->setChecked(true); else mUI.mActionAutoDetectLanguage->setChecked(true); bool succeeded = mApplications->loadSettings(); if (!succeeded) { const QString msg = tr("There was a problem with loading the editor application settings.\n\n" "This is probably because the settings were changed between the Cppcheck versions. " "Please check (and fix) the editor application settings, otherwise the editor " "program might not start correctly."); QMessageBox msgBox(QMessageBox::Warning, tr("Cppcheck"), msg, QMessageBox::Ok, this); msgBox.exec(); } const QString projectFile = mSettings->value(SETTINGS_OPEN_PROJECT, QString()).toString(); if (!projectFile.isEmpty() && QCoreApplication::arguments().size()==1) { QFileInfo inf(projectFile); if (inf.exists() && inf.isReadable()) { setPath(SETTINGS_LAST_PROJECT_PATH, projectFile); mProjectFile = new ProjectFile(this); mProjectFile->setActiveProject(); mProjectFile->read(projectFile); loadLastResults(); QDir::setCurrent(inf.absolutePath()); } } updateFunctionContractsTab(); updateVariableContractsTab(); } void MainWindow::saveSettings() const { // Window/dialog sizes mSettings->setValue(SETTINGS_WINDOW_WIDTH, size().width()); mSettings->setValue(SETTINGS_WINDOW_HEIGHT, size().height()); mSettings->setValue(SETTINGS_WINDOW_MAXIMIZED, isMaximized()); // Show * states mSettings->setValue(SETTINGS_SHOW_STYLE, mUI.mActionShowStyle->isChecked()); mSettings->setValue(SETTINGS_SHOW_ERRORS, mUI.mActionShowErrors->isChecked()); mSettings->setValue(SETTINGS_SHOW_WARNINGS, mUI.mActionShowWarnings->isChecked()); mSettings->setValue(SETTINGS_SHOW_PORTABILITY, mUI.mActionShowPortability->isChecked()); mSettings->setValue(SETTINGS_SHOW_PERFORMANCE, mUI.mActionShowPerformance->isChecked()); mSettings->setValue(SETTINGS_SHOW_INFORMATION, mUI.mActionShowInformation->isChecked()); if (mUI.mActionC89->isChecked()) mSettings->setValue(SETTINGS_STD_C, "C89"); if (mUI.mActionC99->isChecked()) mSettings->setValue(SETTINGS_STD_C, "C99"); if (mUI.mActionC11->isChecked()) mSettings->setValue(SETTINGS_STD_C, "C11"); if (mUI.mActionCpp03->isChecked()) mSettings->setValue(SETTINGS_STD_CPP, "C++03"); if (mUI.mActionCpp11->isChecked()) mSettings->setValue(SETTINGS_STD_CPP, "C++11"); if (mUI.mActionCpp14->isChecked()) mSettings->setValue(SETTINGS_STD_CPP, "C++14"); if (mUI.mActionCpp17->isChecked()) mSettings->setValue(SETTINGS_STD_CPP, "C++17"); if (mUI.mActionCpp20->isChecked()) mSettings->setValue(SETTINGS_STD_CPP, "C++20"); // Main window settings mSettings->setValue(SETTINGS_TOOLBARS_MAIN_SHOW, mUI.mToolBarMain->isVisible()); mSettings->setValue(SETTINGS_TOOLBARS_VIEW_SHOW, mUI.mToolBarView->isVisible()); mSettings->setValue(SETTINGS_TOOLBARS_FILTER_SHOW, mUI.mToolBarFilter->isVisible()); if (mUI.mActionEnforceCpp->isChecked()) mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Settings::CPP); else if (mUI.mActionEnforceC->isChecked()) mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Settings::C); else mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Settings::None); mApplications->saveSettings(); mSettings->setValue(SETTINGS_LANGUAGE, mTranslation->getCurrentLanguage()); mSettings->setValue(SETTINGS_OPEN_PROJECT, mProjectFile ? mProjectFile->getFilename() : QString()); mUI.mResults->saveSettings(mSettings); } void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, const bool checkConfiguration) { clearResults(); mIsLogfileLoaded = false; if (mProjectFile) { std::vector v; foreach (const QString &i, mProjectFile->getExcludedPaths()) { v.push_back(i.toStdString()); } p.ignorePaths(v); if (!mProjectFile->getAnalyzeAllVsConfigs()) { Settings::PlatformType platform = (Settings::PlatformType) mSettings->value(SETTINGS_CHECKED_PLATFORM, 0).toInt(); p.selectOneVsConfig(platform); } } else { enableProjectActions(false); } mUI.mResults->clear(true); mThread->clearFiles(); mUI.mResults->checkingStarted(p.fileSettings.size()); QDir inf(mCurrentDirectory); const QString checkPath = inf.canonicalPath(); setPath(SETTINGS_LAST_CHECK_PATH, checkPath); checkLockDownUI(); // lock UI while checking mUI.mResults->setCheckDirectory(checkPath); Settings checkSettings = getCppcheckSettings(); checkSettings.force = false; checkSettings.checkLibrary = checkLibrary; checkSettings.checkConfiguration = checkConfiguration; if (mProjectFile) qDebug() << "Checking project file" << mProjectFile->getFilename(); if (!checkSettings.buildDir.empty()) { checkSettings.loadSummaries(); std::list sourcefiles; AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, checkSettings.userDefines, p.fileSettings); } //mThread->SetanalyzeProject(true); if (mProjectFile) { mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools()); QString clangHeaders = mSettings->value(SETTINGS_VS_INCLUDE_PATHS).toString(); mThread->setClangIncludePaths(clangHeaders.split(";")); mThread->setSuppressions(mProjectFile->getSuppressions()); } mThread->setProject(p); mThread->check(checkSettings); } void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrary, const bool checkConfiguration) { if (files.isEmpty()) { return; } clearResults(); mIsLogfileLoaded = false; FileList pathList; pathList.addPathList(files); if (mProjectFile) { pathList.addExcludeList(mProjectFile->getExcludedPaths()); } else { enableProjectActions(false); } QStringList fileNames = pathList.getFileList(); mUI.mResults->clear(true); mThread->clearFiles(); if (fileNames.isEmpty()) { QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), tr("No suitable files found to analyze!"), QMessageBox::Ok, this); msg.exec(); return; } mUI.mResults->checkingStarted(fileNames.count()); mThread->setFiles(fileNames); if (mProjectFile && !checkConfiguration) mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools()); mThread->setSuppressions(mProjectFile ? mProjectFile->getSuppressions() : QList()); QDir inf(mCurrentDirectory); const QString checkPath = inf.canonicalPath(); setPath(SETTINGS_LAST_CHECK_PATH, checkPath); checkLockDownUI(); // lock UI while checking mUI.mResults->setCheckDirectory(checkPath); Settings checkSettings = getCppcheckSettings(); checkSettings.checkLibrary = checkLibrary; checkSettings.checkConfiguration = checkConfiguration; checkSettings.loadCppcheckCfg(QCoreApplication::applicationFilePath().toStdString()); if (mProjectFile) qDebug() << "Checking project file" << mProjectFile->getFilename(); if (!checkSettings.buildDir.empty()) { checkSettings.loadSummaries(); std::list sourcefiles; for (const QString& s: fileNames) sourcefiles.push_back(s.toStdString()); AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, checkSettings.userDefines, checkSettings.project.fileSettings); } mThread->setCheckFiles(true); mThread->check(checkSettings); } void MainWindow::analyzeCode(const QString& code, const QString& filename) { // Initialize dummy ThreadResult as ErrorLogger ThreadResult result; result.setFiles(QStringList(filename)); connect(&result, SIGNAL(progress(int,const QString&)), mUI.mResults, SLOT(progress(int,const QString&))); connect(&result, SIGNAL(error(const ErrorItem&)), mUI.mResults, SLOT(error(const ErrorItem&))); connect(&result, SIGNAL(log(const QString&)), mUI.mResults, SLOT(log(const QString&))); connect(&result, SIGNAL(debugError(const ErrorItem&)), mUI.mResults, SLOT(debugError(const ErrorItem&))); // Create CppCheck instance CppCheck cppcheck(result, true, nullptr); cppcheck.settings() = getCppcheckSettings(); // Check checkLockDownUI(); clearResults(); mUI.mResults->checkingStarted(1); cppcheck.check(filename.toStdString(), code.toStdString()); analysisDone(); // Expand results if (mUI.mResults->hasVisibleResults()) mUI.mResults->expandAllResults(); } QStringList MainWindow::selectFilesToAnalyze(QFileDialog::FileMode mode) { if (mProjectFile) { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Cppcheck")); const QString msg(tr("You must close the project file before selecting new files or directories!")); msgBox.setText(msg); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); return QStringList(); } QStringList selected; // NOTE: we use QFileDialog::getOpenFileNames() and // QFileDialog::getExistingDirectory() because they show native Windows // selection dialog which is a lot more usable than Qt:s own dialog. if (mode == QFileDialog::ExistingFiles) { QMap filters; filters[tr("C/C++ Source")] = FileList::getDefaultFilters().join(" "); filters[tr("Compile database")] = compile_commands_json; filters[tr("Visual Studio")] = "*.sln *.vcxproj"; filters[tr("Borland C++ Builder 6")] = "*.bpr"; QString lastFilter = mSettings->value(SETTINGS_LAST_ANALYZE_FILES_FILTER).toString(); selected = QFileDialog::getOpenFileNames(this, tr("Select files to analyze"), getPath(SETTINGS_LAST_CHECK_PATH), toFilterString(filters), &lastFilter); mSettings->setValue(SETTINGS_LAST_ANALYZE_FILES_FILTER, lastFilter); if (selected.isEmpty()) mCurrentDirectory.clear(); else { QFileInfo inf(selected[0]); mCurrentDirectory = inf.absolutePath(); } formatAndSetTitle(); } else if (mode == QFileDialog::DirectoryOnly) { QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory to analyze"), getPath(SETTINGS_LAST_CHECK_PATH)); if (!dir.isEmpty()) { qDebug() << "Setting current directory to: " << dir; mCurrentDirectory = dir; selected.append(dir); dir = QDir::toNativeSeparators(dir); formatAndSetTitle(dir); } } setPath(SETTINGS_LAST_CHECK_PATH, mCurrentDirectory); return selected; } void MainWindow::updateFunctionContractsTab() { QStringList addedContracts; if (mProjectFile) { for (const auto& it: mProjectFile->getFunctionContracts()) { addedContracts << QString::fromStdString(it.first); } } mUI.mResults->setAddedFunctionContracts(addedContracts); } void MainWindow::updateVariableContractsTab() { QStringList added; if (mProjectFile) { for (auto vc: mProjectFile->getVariableContracts()) { QString line = vc.first; if (!vc.second.minValue.empty()) line += " min:" + QString::fromStdString(vc.second.minValue); if (!vc.second.maxValue.empty()) line += " max:" + QString::fromStdString(vc.second.maxValue); added << line; } } mUI.mResults->setAddedVariableContracts(added); } void MainWindow::analyzeFiles() { Settings::terminate(false); QStringList selected = selectFilesToAnalyze(QFileDialog::ExistingFiles); const QString file0 = (selected.size() ? selected[0].toLower() : QString()); if (file0.endsWith(".sln") || file0.endsWith(".vcxproj") || file0.endsWith(compile_commands_json) || file0.endsWith(".bpr")) { ImportProject p; p.import(selected[0].toStdString()); if (file0.endsWith(".sln")) { QStringList configs; for (std::list::const_iterator it = p.fileSettings.begin(); it != p.fileSettings.end(); ++it) { const QString cfg(QString::fromStdString(it->cfg)); if (!configs.contains(cfg)) configs.push_back(cfg); } configs.sort(); bool ok = false; const QString cfg = QInputDialog::getItem(this, tr("Select configuration"), tr("Select the configuration that will be analyzed"), configs, 0, false, &ok); if (!ok) return; p.ignoreOtherConfigs(cfg.toStdString()); } doAnalyzeProject(p); return; } doAnalyzeFiles(selected); } void MainWindow::analyzeDirectory() { QStringList dir = selectFilesToAnalyze(QFileDialog::DirectoryOnly); if (dir.isEmpty()) return; QDir checkDir(dir[0]); QStringList filters; filters << "*.cppcheck"; checkDir.setFilter(QDir::Files | QDir::Readable); checkDir.setNameFilters(filters); QStringList projFiles = checkDir.entryList(); if (!projFiles.empty()) { if (projFiles.size() == 1) { // If one project file found, suggest loading it QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Cppcheck")); const QString msg(tr("Found project file: %1\n\nDo you want to " "load this project file instead?").arg(projFiles[0])); msgBox.setText(msg); msgBox.setIcon(QMessageBox::Warning); msgBox.addButton(QMessageBox::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int dlgResult = msgBox.exec(); if (dlgResult == QMessageBox::Yes) { QString path = checkDir.canonicalPath(); if (!path.endsWith("/")) path += "/"; path += projFiles[0]; loadProjectFile(path); } else { doAnalyzeFiles(dir); } } else { // If multiple project files found inform that there are project // files also available. QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Cppcheck")); const QString msg(tr("Found project files from the directory.\n\n" "Do you want to proceed analysis without " "using any of these project files?")); msgBox.setText(msg); msgBox.setIcon(QMessageBox::Warning); msgBox.addButton(QMessageBox::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int dlgResult = msgBox.exec(); if (dlgResult == QMessageBox::Yes) { doAnalyzeFiles(dir); } } } else { doAnalyzeFiles(dir); } } void MainWindow::addIncludeDirs(const QStringList &includeDirs, Settings &result) { QString dir; foreach (dir, includeDirs) { QString incdir; if (!QDir::isAbsolutePath(dir)) incdir = mCurrentDirectory + "/"; incdir += dir; incdir = QDir::cleanPath(incdir); // include paths must end with '/' if (!incdir.endsWith("/")) incdir += "/"; result.includePaths.push_back(incdir.toStdString()); } } Library::Error MainWindow::loadLibrary(Library *library, const QString &filename) { Library::Error ret; // Try to load the library from the project folder.. if (mProjectFile) { QString path = QFileInfo(mProjectFile->getFilename()).canonicalPath(); ret = library->load(nullptr, (path+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; } // Try to load the library from the application folder.. const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); ret = library->load(nullptr, (appPath+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(nullptr, (appPath+"/cfg/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; #ifdef FILESDIR // Try to load the library from FILESDIR/cfg.. const QString filesdir = FILESDIR; if (!filesdir.isEmpty()) { ret = library->load(nullptr, (filesdir+"/cfg/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(nullptr, (filesdir+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; } #endif // Try to load the library from the cfg subfolder.. const QString datadir = getDataDir(); if (!datadir.isEmpty()) { ret = library->load(nullptr, (datadir+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(nullptr, (datadir+"/cfg/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; } return ret; } bool MainWindow::tryLoadLibrary(Library *library, const QString& filename) { const Library::Error error = loadLibrary(library, filename); if (error.errorcode != Library::ErrorCode::OK) { if (error.errorcode == Library::ErrorCode::UNKNOWN_ELEMENT) { QMessageBox::information(this, tr("Information"), tr("The library '%1' contains unknown elements:\n%2").arg(filename).arg(error.reason.c_str())); return true; } QString errmsg; switch (error.errorcode) { case Library::ErrorCode::OK: break; case Library::ErrorCode::FILE_NOT_FOUND: errmsg = tr("File not found"); break; case Library::ErrorCode::BAD_XML: errmsg = tr("Bad XML"); break; case Library::ErrorCode::MISSING_ATTRIBUTE: errmsg = tr("Missing attribute"); break; case Library::ErrorCode::BAD_ATTRIBUTE_VALUE: errmsg = tr("Bad attribute value"); break; case Library::ErrorCode::UNSUPPORTED_FORMAT: errmsg = tr("Unsupported format"); break; case Library::ErrorCode::DUPLICATE_PLATFORM_TYPE: errmsg = tr("Duplicate platform type"); break; case Library::ErrorCode::PLATFORM_TYPE_REDEFINED: errmsg = tr("Platform type redefined"); break; case Library::ErrorCode::UNKNOWN_ELEMENT: errmsg = tr("Unknown element"); break; default: errmsg = tr("Unknown issue"); break; } if (!error.reason.empty()) errmsg += " '" + QString::fromStdString(error.reason) + "'"; QMessageBox::information(this, tr("Information"), tr("Failed to load the selected library '%1'.\n%2").arg(filename).arg(errmsg)); return false; } return true; } Settings MainWindow::getCppcheckSettings() { saveSettings(); // Save settings Settings result; result.exename = QCoreApplication::applicationFilePath().toStdString(); const bool std = tryLoadLibrary(&result.library, "std.cfg"); bool posix = true; if (result.posix()) posix = tryLoadLibrary(&result.library, "posix.cfg"); bool windows = true; if (result.isWindowsPlatform()) windows = tryLoadLibrary(&result.library, "windows.cfg"); if (!std || !posix || !windows) QMessageBox::critical(this, tr("Error"), tr("Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir= at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured.").arg(!std ? "std.cfg" : !posix ? "posix.cfg" : "windows.cfg")); // If project file loaded, read settings from it if (mProjectFile) { QStringList dirs = mProjectFile->getIncludeDirs(); addIncludeDirs(dirs, result); const QStringList defines = mProjectFile->getDefines(); foreach (QString define, defines) { if (!result.userDefines.empty()) result.userDefines += ";"; result.userDefines += define.toStdString(); } result.clang = mProjectFile->clangParser; result.bugHunting = mProjectFile->bugHunting; result.bugHuntingReport = " "; result.functionContracts = mProjectFile->getFunctionContracts(); for (const auto& vc: mProjectFile->getVariableContracts()) result.variableContracts[vc.first.toStdString()] = vc.second; const QStringList undefines = mProjectFile->getUndefines(); foreach (QString undefine, undefines) result.userUndefs.insert(undefine.toStdString()); const QStringList libraries = mProjectFile->getLibraries(); foreach (QString library, libraries) { result.libraries.emplace_back(library.toStdString()); const QString filename = library + ".cfg"; tryLoadLibrary(&result.library, filename); } foreach (const Suppressions::Suppression &suppression, mProjectFile->getSuppressions()) { result.nomsg.addSuppression(suppression); } // Only check the given -D configuration if (!defines.isEmpty()) result.maxConfigs = 1; // If importing a project, only check the given configuration if (!mProjectFile->getImportProject().isEmpty()) result.checkAllConfigurations = false; const QString &buildDir = mProjectFile->getBuildDir(); if (!buildDir.isEmpty()) { if (QDir(buildDir).isAbsolute()) { result.buildDir = buildDir.toStdString(); } else { QString prjpath = QFileInfo(mProjectFile->getFilename()).absolutePath(); result.buildDir = (prjpath + '/' + buildDir).toStdString(); } } const QString platform = mProjectFile->getPlatform(); if (platform.endsWith(".xml")) { const QString applicationFilePath = QCoreApplication::applicationFilePath(); result.loadPlatformFile(applicationFilePath.toStdString().c_str(), platform.toStdString()); } else { for (int i = cppcheck::Platform::Native; i <= cppcheck::Platform::Unix64; i++) { const cppcheck::Platform::PlatformType p = (cppcheck::Platform::PlatformType)i; if (platform == cppcheck::Platform::platformString(p)) { result.platform(p); break; } } } result.maxCtuDepth = mProjectFile->getMaxCtuDepth(); result.maxTemplateRecursion = mProjectFile->getMaxTemplateRecursion(); result.checkHeaders = mProjectFile->getCheckHeaders(); result.checkUnusedTemplates = mProjectFile->getCheckUnusedTemplates(); result.safeChecks.classes = mProjectFile->safeChecks.classes; result.safeChecks.externalFunctions = mProjectFile->safeChecks.externalFunctions; result.safeChecks.internalFunctions = mProjectFile->safeChecks.internalFunctions; result.safeChecks.externalVariables = mProjectFile->safeChecks.externalVariables; foreach (QString s, mProjectFile->getCheckUnknownFunctionReturn()) result.checkUnknownFunctionReturn.insert(s.toStdString()); QString filesDir(getDataDir()); const QString pythonCmd = mSettings->value(SETTINGS_PYTHON_PATH).toString(); foreach (QString addon, mProjectFile->getAddons()) { QString addonFilePath = ProjectFile::getAddonFilePath(filesDir, addon); if (addonFilePath.isEmpty()) continue; addonFilePath.replace(QChar('\\'), QChar('/')); QString json; json += "{ \"script\":\"" + addonFilePath + "\""; if (!pythonCmd.isEmpty()) json += ", \"python\":\"" + pythonCmd + "\""; QString misraFile = mSettings->value(SETTINGS_MISRA_FILE).toString(); if (addon == "misra" && !misraFile.isEmpty()) { QString arg; if (misraFile.endsWith(".pdf", Qt::CaseInsensitive)) arg = "--misra-pdf=" + misraFile; else arg = "--rule-texts=" + misraFile; json += ", \"args\":[\"" + arg + "\"]"; } json += " }"; result.addons.push_back(json.toStdString()); } } // Include directories (and files) are searched in listed order. // Global include directories must be added AFTER the per project include // directories so per project include directories can override global ones. const QString globalIncludes = mSettings->value(SETTINGS_GLOBAL_INCLUDE_PATHS).toString(); if (!globalIncludes.isEmpty()) { QStringList includes = globalIncludes.split(";"); addIncludeDirs(includes, result); } result.severity.enable(Severity::warning); result.severity.enable(Severity::style); result.severity.enable(Severity::performance); result.severity.enable(Severity::portability); result.severity.enable(Severity::information); result.checks.enable(Checks::missingInclude); if (!result.buildDir.empty()) result.checks.enable(Checks::unusedFunction); result.debugwarnings = mSettings->value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool(); result.quiet = false; result.verbose = true; result.force = mSettings->value(SETTINGS_CHECK_FORCE, 1).toBool(); result.xml = false; result.jobs = mSettings->value(SETTINGS_CHECK_THREADS, 1).toInt(); result.inlineSuppressions = mSettings->value(SETTINGS_INLINE_SUPPRESSIONS, false).toBool(); result.certainty.setEnabled(Certainty::inconclusive, mSettings->value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool()); if (!mProjectFile || result.platformType == cppcheck::Platform::Unspecified) result.platform((cppcheck::Platform::PlatformType) mSettings->value(SETTINGS_CHECKED_PLATFORM, 0).toInt()); result.standards.setCPP(mSettings->value(SETTINGS_STD_CPP, QString()).toString().toStdString()); result.standards.setC(mSettings->value(SETTINGS_STD_C, QString()).toString().toStdString()); result.enforcedLang = (Settings::Language)mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt(); if (result.jobs <= 1) { result.jobs = 1; } Settings::terminate(false); return result; } void MainWindow::analysisDone() { if (mExiting) { close(); return; } mUI.mResults->checkingFinished(); enableCheckButtons(true); mUI.mActionSettings->setEnabled(true); mUI.mActionOpenXML->setEnabled(true); if (mProjectFile) { enableProjectActions(true); } else if (mIsLogfileLoaded) { mUI.mActionReanalyzeModified->setEnabled(false); mUI.mActionReanalyzeAll->setEnabled(false); } enableProjectOpenActions(true); mPlatformActions->setEnabled(true); mCStandardActions->setEnabled(true); mCppStandardActions->setEnabled(true); mSelectLanguageActions->setEnabled(true); mUI.mActionPosix->setEnabled(true); if (mScratchPad) mScratchPad->setEnabled(true); mUI.mActionViewStats->setEnabled(true); if (mProjectFile && !mProjectFile->getBuildDir().isEmpty()) { const QString prjpath = QFileInfo(mProjectFile->getFilename()).absolutePath(); const QString buildDir = prjpath + '/' + mProjectFile->getBuildDir(); if (QDir(buildDir).exists()) { mUI.mResults->saveStatistics(buildDir + "/statistics.txt"); mUI.mResults->updateFromOldReport(buildDir + "/lastResults.xml"); mUI.mResults->save(buildDir + "/lastResults.xml", Report::XMLV2); } } enableResultsButtons(); for (QAction* recentProjectAct : mRecentProjectActs) { if (recentProjectAct != nullptr) recentProjectAct->setEnabled(true); } // Notify user - if the window is not active - that check is ready QApplication::alert(this, 3000); if (mSettings->value(SETTINGS_SHOW_STATISTICS, false).toBool()) showStatistics(); } void MainWindow::checkLockDownUI() { enableCheckButtons(false); mUI.mActionSettings->setEnabled(false); mUI.mActionOpenXML->setEnabled(false); enableProjectActions(false); enableProjectOpenActions(false); mPlatformActions->setEnabled(false); mCStandardActions->setEnabled(false); mCppStandardActions->setEnabled(false); mSelectLanguageActions->setEnabled(false); mUI.mActionPosix->setEnabled(false); if (mScratchPad) mScratchPad->setEnabled(false); for (QAction* recentProjectAct : mRecentProjectActs) { if (recentProjectAct != nullptr) recentProjectAct->setEnabled(false); } } void MainWindow::programSettings() { SettingsDialog dialog(mApplications, mTranslation, this); if (dialog.exec() == QDialog::Accepted) { dialog.saveSettingValues(); mSettings->sync(); mUI.mResults->updateSettings(dialog.showFullPath(), dialog.saveFullPath(), dialog.saveAllErrors(), dialog.showNoErrorsMessage(), dialog.showErrorId(), dialog.showInconclusive()); mUI.mResults->updateStyleSetting(mSettings); const QString newLang = mSettings->value(SETTINGS_LANGUAGE, "en").toString(); setLanguage(newLang); } } void MainWindow::reAnalyzeModified() { reAnalyze(false); } void MainWindow::reAnalyzeAll() { if (mProjectFile) analyzeProject(mProjectFile); else reAnalyze(true); } void MainWindow::checkLibrary() { if (mProjectFile) analyzeProject(mProjectFile, true); } void MainWindow::checkConfiguration() { if (mProjectFile) analyzeProject(mProjectFile, false, true); } void MainWindow::reAnalyzeSelected(QStringList files) { if (files.empty()) return; if (mThread->isChecking()) return; // Clear details, statistics and progress mUI.mResults->clear(false); for (int i = 0; i < files.size(); ++i) mUI.mResults->clearRecheckFile(files[i]); mCurrentDirectory = mUI.mResults->getCheckDirectory(); FileList pathList; pathList.addPathList(files); if (mProjectFile) pathList.addExcludeList(mProjectFile->getExcludedPaths()); QStringList fileNames = pathList.getFileList(); checkLockDownUI(); // lock UI while checking mUI.mResults->checkingStarted(fileNames.size()); mThread->setCheckFiles(fileNames); // Saving last check start time, otherwise unchecked modified files will not be // considered in "Modified Files Check" performed after "Selected Files Check" // TODO: Should we store per file CheckStartTime? QDateTime saveCheckStartTime = mThread->getCheckStartTime(); mThread->check(getCppcheckSettings()); mThread->setCheckStartTime(saveCheckStartTime); } void MainWindow::reAnalyze(bool all) { const QStringList files = mThread->getReCheckFiles(all); if (files.empty()) return; // Clear details, statistics and progress mUI.mResults->clear(all); // Clear results for changed files for (int i = 0; i < files.size(); ++i) mUI.mResults->clear(files[i]); checkLockDownUI(); // lock UI while checking mUI.mResults->checkingStarted(files.size()); if (mProjectFile) qDebug() << "Rechecking project file" << mProjectFile->getFilename(); mThread->setCheckFiles(all); mThread->check(getCppcheckSettings()); } void MainWindow::clearResults() { if (mProjectFile && !mProjectFile->getBuildDir().isEmpty()) { QDir dir(QFileInfo(mProjectFile->getFilename()).absolutePath() + '/' + mProjectFile->getBuildDir()); for (const QString& f: dir.entryList(QDir::Files)) { if (!f.endsWith("files.txt") && !QRegExp(".*.s[0-9]+$").exactMatch(f)) dir.remove(f); } } mUI.mResults->clear(true); Q_ASSERT(false == mUI.mResults->hasResults()); enableResultsButtons(); } void MainWindow::openResults() { if (mUI.mResults->hasResults()) { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Cppcheck")); const QString msg(tr("Current results will be cleared.\n\n" "Opening a new XML file will clear current results.\n" "Do you want to proceed?")); msgBox.setText(msg); msgBox.setIcon(QMessageBox::Warning); msgBox.addButton(QMessageBox::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int dlgResult = msgBox.exec(); if (dlgResult == QMessageBox::No) { return; } } QString selectedFilter; const QString filter(tr("XML files (*.xml)")); QString selectedFile = QFileDialog::getOpenFileName(this, tr("Open the report file"), getPath(SETTINGS_LAST_RESULT_PATH), filter, &selectedFilter); if (!selectedFile.isEmpty()) { loadResults(selectedFile); } } void MainWindow::loadResults(const QString &selectedFile) { if (selectedFile.isEmpty()) return; if (mProjectFile) closeProjectFile(); mIsLogfileLoaded = true; mUI.mResults->clear(true); mUI.mActionReanalyzeModified->setEnabled(false); mUI.mActionReanalyzeAll->setEnabled(false); mUI.mResults->readErrorsXml(selectedFile); setPath(SETTINGS_LAST_RESULT_PATH, selectedFile); formatAndSetTitle(selectedFile); } void MainWindow::loadResults(const QString &selectedFile, const QString &sourceDirectory) { loadResults(selectedFile); mUI.mResults->setCheckDirectory(sourceDirectory); } void MainWindow::enableCheckButtons(bool enable) { mUI.mActionStop->setEnabled(!enable); mUI.mActionAnalyzeFiles->setEnabled(enable); if (mProjectFile) { mUI.mActionReanalyzeModified->setEnabled(false); mUI.mActionReanalyzeAll->setEnabled(enable); } else if (!enable || mThread->hasPreviousFiles()) { mUI.mActionReanalyzeModified->setEnabled(enable); mUI.mActionReanalyzeAll->setEnabled(enable); } mUI.mActionAnalyzeDirectory->setEnabled(enable); } void MainWindow::enableResultsButtons() { bool enabled = mUI.mResults->hasResults(); mUI.mActionClearResults->setEnabled(enabled); mUI.mActionSave->setEnabled(enabled); mUI.mActionPrint->setEnabled(enabled); mUI.mActionPrintPreview->setEnabled(enabled); } void MainWindow::showStyle(bool checked) { mUI.mResults->showResults(ShowTypes::ShowStyle, checked); } void MainWindow::showErrors(bool checked) { mUI.mResults->showResults(ShowTypes::ShowErrors, checked); } void MainWindow::showWarnings(bool checked) { mUI.mResults->showResults(ShowTypes::ShowWarnings, checked); } void MainWindow::showPortability(bool checked) { mUI.mResults->showResults(ShowTypes::ShowPortability, checked); } void MainWindow::showPerformance(bool checked) { mUI.mResults->showResults(ShowTypes::ShowPerformance, checked); } void MainWindow::showInformation(bool checked) { mUI.mResults->showResults(ShowTypes::ShowInformation, checked); } void MainWindow::checkAll() { toggleAllChecked(true); } void MainWindow::uncheckAll() { toggleAllChecked(false); } void MainWindow::closeEvent(QCloseEvent *event) { // Check that we aren't checking files if (!mThread->isChecking()) { saveSettings(); event->accept(); } else { const QString text(tr("Analyzer is running.\n\n" \ "Do you want to stop the analysis and exit Cppcheck?")); QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), text, QMessageBox::Yes | QMessageBox::No, this); msg.setDefaultButton(QMessageBox::No); int rv = msg.exec(); if (rv == QMessageBox::Yes) { // This isn't really very clean way to close threads but since the app is // exiting it doesn't matter. mThread->stop(); saveSettings(); mExiting = true; } event->ignore(); } } void MainWindow::toggleAllChecked(bool checked) { mUI.mActionShowStyle->setChecked(checked); showStyle(checked); mUI.mActionShowErrors->setChecked(checked); showErrors(checked); mUI.mActionShowWarnings->setChecked(checked); showWarnings(checked); mUI.mActionShowPortability->setChecked(checked); showPortability(checked); mUI.mActionShowPerformance->setChecked(checked); showPerformance(checked); mUI.mActionShowInformation->setChecked(checked); showInformation(checked); } void MainWindow::about() { AboutDialog *dlg = new AboutDialog(CppCheck::version(), CppCheck::extraVersion(), this); dlg->exec(); } void MainWindow::showLicense() { FileViewDialog *dlg = new FileViewDialog(":COPYING", tr("License"), this); dlg->resize(570, 400); dlg->exec(); } void MainWindow::showAuthors() { FileViewDialog *dlg = new FileViewDialog(":AUTHORS", tr("Authors"), this); dlg->resize(350, 400); dlg->exec(); } void MainWindow::performSelectedFilesCheck(const QStringList &selectedFilesList) { reAnalyzeSelected(selectedFilesList); } void MainWindow::save() { QString selectedFilter; const QString filter(tr("XML files (*.xml);;Text files (*.txt);;CSV files (*.csv)")); QString selectedFile = QFileDialog::getSaveFileName(this, tr("Save the report file"), getPath(SETTINGS_LAST_RESULT_PATH), filter, &selectedFilter); if (!selectedFile.isEmpty()) { Report::Type type = Report::TXT; if (selectedFilter == tr("XML files (*.xml)")) { type = Report::XMLV2; if (!selectedFile.endsWith(".xml", Qt::CaseInsensitive)) selectedFile += ".xml"; } else if (selectedFilter == tr("Text files (*.txt)")) { type = Report::TXT; if (!selectedFile.endsWith(".txt", Qt::CaseInsensitive)) selectedFile += ".txt"; } else if (selectedFilter == tr("CSV files (*.csv)")) { type = Report::CSV; if (!selectedFile.endsWith(".csv", Qt::CaseInsensitive)) selectedFile += ".csv"; } else { if (selectedFile.endsWith(".xml", Qt::CaseInsensitive)) type = Report::XMLV2; else if (selectedFile.endsWith(".txt", Qt::CaseInsensitive)) type = Report::TXT; else if (selectedFile.endsWith(".csv", Qt::CaseInsensitive)) type = Report::CSV; } mUI.mResults->save(selectedFile, type); setPath(SETTINGS_LAST_RESULT_PATH, selectedFile); } } void MainWindow::resultsAdded() {} void MainWindow::toggleMainToolBar() { mUI.mToolBarMain->setVisible(mUI.mActionToolBarMain->isChecked()); } void MainWindow::toggleViewToolBar() { mUI.mToolBarView->setVisible(mUI.mActionToolBarView->isChecked()); } void MainWindow::toggleFilterToolBar() { mUI.mToolBarFilter->setVisible(mUI.mActionToolBarFilter->isChecked()); mLineEditFilter->clear(); // Clearing the filter also disables filtering } void MainWindow::formatAndSetTitle(const QString &text) { QString nameWithVersion = QString("Cppcheck %1").arg(CppCheck::version()); QString extraVersion = CppCheck::extraVersion(); if (!extraVersion.isEmpty()) { nameWithVersion += " (" + extraVersion + ")"; } QString title; if (text.isEmpty()) title = nameWithVersion; else title = QString("%1 - %2").arg(nameWithVersion, text); setWindowTitle(title); } void MainWindow::setLanguage(const QString &code) { const QString currentLang = mTranslation->getCurrentLanguage(); if (currentLang == code) return; if (mTranslation->setLanguage(code)) { //Translate everything that is visible here mUI.retranslateUi(this); mUI.mResults->translate(); mLineEditFilter->setPlaceholderText(QCoreApplication::translate("MainWindow", "Quick Filter:")); if (mProjectFile) formatAndSetTitle(tr("Project:") + ' ' + mProjectFile->getFilename()); if (mScratchPad) mScratchPad->translate(); } } void MainWindow::aboutToShowViewMenu() { mUI.mActionToolBarMain->setChecked(mUI.mToolBarMain->isVisible()); mUI.mActionToolBarView->setChecked(mUI.mToolBarView->isVisible()); mUI.mActionToolBarFilter->setChecked(mUI.mToolBarFilter->isVisible()); } void MainWindow::stopAnalysis() { mThread->stop(); mUI.mResults->disableProgressbar(); const QString &lastResults = getLastResults(); if (!lastResults.isEmpty()) { mUI.mResults->updateFromOldReport(lastResults); } } void MainWindow::openHelpContents() { openOnlineHelp(); } void MainWindow::openOnlineHelp() { HelpDialog *helpDialog = new HelpDialog; helpDialog->showMaximized(); } void MainWindow::openProjectFile() { const QString filter = tr("Project files (*.cppcheck);;All files(*.*)"); const QString filepath = QFileDialog::getOpenFileName(this, tr("Select Project File"), getPath(SETTINGS_LAST_PROJECT_PATH), filter); if (!filepath.isEmpty()) { const QFileInfo fi(filepath); if (fi.exists() && fi.isFile() && fi.isReadable()) { setPath(SETTINGS_LAST_PROJECT_PATH, filepath); loadProjectFile(filepath); } } } void MainWindow::showScratchpad() { if (!mScratchPad) mScratchPad = new ScratchPad(*this); mScratchPad->show(); if (!mScratchPad->isActiveWindow()) mScratchPad->activateWindow(); } void MainWindow::loadProjectFile(const QString &filePath) { QFileInfo inf(filePath); const QString filename = inf.fileName(); formatAndSetTitle(tr("Project:") + ' ' + filename); addProjectMRU(filePath); mIsLogfileLoaded = false; mUI.mActionCloseProjectFile->setEnabled(true); mUI.mActionEditProjectFile->setEnabled(true); delete mProjectFile; mProjectFile = new ProjectFile(filePath, this); mProjectFile->setActiveProject(); mUI.mResults->showContracts(mProjectFile->bugHunting); updateFunctionContractsTab(); updateVariableContractsTab(); if (!loadLastResults()) analyzeProject(mProjectFile); } QString MainWindow::getLastResults() const { if (!mProjectFile || mProjectFile->getBuildDir().isEmpty()) return QString(); return QFileInfo(mProjectFile->getFilename()).absolutePath() + '/' + mProjectFile->getBuildDir() + "/lastResults.xml"; } bool MainWindow::loadLastResults() { const QString &lastResults = getLastResults(); if (lastResults.isEmpty()) return false; if (!QFileInfo(lastResults).exists()) return false; mUI.mResults->readErrorsXml(lastResults); mUI.mResults->setCheckDirectory(mSettings->value(SETTINGS_LAST_CHECK_PATH,QString()).toString()); mUI.mActionViewStats->setEnabled(true); enableResultsButtons(); return true; } void MainWindow::analyzeProject(const ProjectFile *projectFile, const bool checkLibrary, const bool checkConfiguration) { Settings::terminate(false); QFileInfo inf(projectFile->getFilename()); const QString rootpath = projectFile->getRootPath(); QDir::setCurrent(inf.absolutePath()); mThread->setAddonsAndTools(projectFile->getAddonsAndTools()); // If the root path is not given or is not "current dir", use project // file's location directory as root path if (rootpath.isEmpty() || rootpath == ".") mCurrentDirectory = inf.canonicalPath(); else if (rootpath.startsWith("./")) mCurrentDirectory = inf.canonicalPath() + rootpath.mid(1); else mCurrentDirectory = rootpath; if (!projectFile->getBuildDir().isEmpty()) { QString buildDir = projectFile->getBuildDir(); if (!QDir::isAbsolutePath(buildDir)) buildDir = inf.canonicalPath() + '/' + buildDir; if (!QDir(buildDir).exists()) { QMessageBox msg(QMessageBox::Question, tr("Cppcheck"), tr("Build dir '%1' does not exist, create it?").arg(buildDir), QMessageBox::Yes | QMessageBox::No, this); if (msg.exec() == QMessageBox::Yes) { QDir().mkpath(buildDir); } else if (!projectFile->getAddons().isEmpty()) { QMessageBox m(QMessageBox::Critical, tr("Cppcheck"), tr("To check the project using addons, you need a build directory."), QMessageBox::Ok, this); m.exec(); return; } } } if (!projectFile->getImportProject().isEmpty()) { ImportProject p; QString prjfile; if (QFileInfo(projectFile->getImportProject()).isAbsolute()) { prjfile = projectFile->getImportProject(); } else { prjfile = inf.canonicalPath() + '/' + projectFile->getImportProject(); } try { p.import(prjfile.toStdString()); } catch (InternalError &e) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("Failed to import '%1', analysis is stopped").arg(prjfile), QMessageBox::Ok, this); msg.exec(); return; } doAnalyzeProject(p, checkLibrary, checkConfiguration); return; } QStringList paths = projectFile->getCheckPaths(); // If paths not given then check the root path (which may be the project // file's location, see above). This is to keep the compatibility with // old "silent" project file loading when we checked the director where the // project file was located. if (paths.isEmpty()) { paths << mCurrentDirectory; } doAnalyzeFiles(paths, checkLibrary, checkConfiguration); } void MainWindow::newProjectFile() { const QString filter = tr("Project files (*.cppcheck)"); QString filepath = QFileDialog::getSaveFileName(this, tr("Select Project Filename"), getPath(SETTINGS_LAST_PROJECT_PATH), filter); if (filepath.isEmpty()) return; if (!filepath.endsWith(".cppcheck", Qt::CaseInsensitive)) filepath += ".cppcheck"; setPath(SETTINGS_LAST_PROJECT_PATH, filepath); QFileInfo inf(filepath); const QString filename = inf.fileName(); formatAndSetTitle(tr("Project:") + QString(" ") + filename); delete mProjectFile; mProjectFile = new ProjectFile(this); mProjectFile->setActiveProject(); mProjectFile->setFilename(filepath); mProjectFile->setBuildDir(filename.left(filename.indexOf(".")) + "-cppcheck-build-dir"); ProjectFileDialog dlg(mProjectFile, this); if (dlg.exec() == QDialog::Accepted) { addProjectMRU(filepath); mUI.mResults->showContracts(mProjectFile->bugHunting); analyzeProject(mProjectFile); } else { closeProjectFile(); } updateFunctionContractsTab(); updateVariableContractsTab(); } void MainWindow::closeProjectFile() { delete mProjectFile; mProjectFile = nullptr; mUI.mResults->clear(true); mUI.mResults->clearContracts(); mUI.mResults->showContracts(false); enableProjectActions(false); enableProjectOpenActions(true); formatAndSetTitle(); } void MainWindow::editProjectFile() { if (!mProjectFile) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), QString(tr("No project file loaded")), QMessageBox::Ok, this); msg.exec(); return; } ProjectFileDialog dlg(mProjectFile, this); if (dlg.exec() == QDialog::Accepted) { mProjectFile->write(); mUI.mResults->showContracts(mProjectFile->bugHunting); analyzeProject(mProjectFile); } } void MainWindow::showStatistics() { StatsDialog statsDialog(this); // Show a dialog with the previous scan statistics and project information statsDialog.setProject(mProjectFile); statsDialog.setPathSelected(mCurrentDirectory); statsDialog.setNumberOfFilesScanned(mThread->getPreviousFilesCount()); statsDialog.setScanDuration(mThread->getPreviousScanDuration() / 1000.0); statsDialog.setStatistics(mUI.mResults->getStatistics()); statsDialog.exec(); } void MainWindow::showLibraryEditor() { LibraryDialog libraryDialog(this); libraryDialog.exec(); } void MainWindow::filterResults() { mUI.mResults->filterResults(mLineEditFilter->text()); } void MainWindow::enableProjectActions(bool enable) { mUI.mActionCloseProjectFile->setEnabled(enable); mUI.mActionEditProjectFile->setEnabled(enable); mUI.mActionCheckLibrary->setEnabled(enable); mUI.mActionCheckConfiguration->setEnabled(enable); } void MainWindow::enableProjectOpenActions(bool enable) { mUI.mActionNewProjectFile->setEnabled(enable); mUI.mActionOpenProjectFile->setEnabled(enable); } void MainWindow::openRecentProject() { QAction *action = qobject_cast(sender()); if (!action) return; const QString project = action->data().toString(); QFileInfo inf(project); if (inf.exists()) { if (inf.suffix() == "xml") loadResults(project); else { loadProjectFile(project); loadLastResults(); } } else { const QString text(tr("The project file\n\n%1\n\n could not be found!\n\n" "Do you want to remove the file from the recently " "used projects -list?").arg(project)); QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), text, QMessageBox::Yes | QMessageBox::No, this); msg.setDefaultButton(QMessageBox::No); int rv = msg.exec(); if (rv == QMessageBox::Yes) { removeProjectMRU(project); } } } void MainWindow::updateMRUMenuItems() { for (QAction* recentProjectAct : mRecentProjectActs) { if (recentProjectAct != nullptr) mUI.mMenuFile->removeAction(recentProjectAct); } QStringList projects = mSettings->value(SETTINGS_MRU_PROJECTS).toStringList(); // Do a sanity check - remove duplicates and non-existing projects int removed = projects.removeDuplicates(); for (int i = projects.size() - 1; i >= 0; i--) { if (!QFileInfo(projects[i]).exists()) { projects.removeAt(i); removed++; } } if (removed) mSettings->setValue(SETTINGS_MRU_PROJECTS, projects); const int numRecentProjects = qMin(projects.size(), (int)MaxRecentProjects); for (int i = 0; i < numRecentProjects; i++) { const QString filename = QFileInfo(projects[i]).fileName(); const QString text = QString("&%1 %2").arg(i + 1).arg(filename); mRecentProjectActs[i]->setText(text); mRecentProjectActs[i]->setData(projects[i]); mRecentProjectActs[i]->setVisible(true); mUI.mMenuFile->insertAction(mUI.mActionProjectMRU, mRecentProjectActs[i]); } if (numRecentProjects > 1) mRecentProjectActs[numRecentProjects] = mUI.mMenuFile->insertSeparator(mUI.mActionProjectMRU); } void MainWindow::addProjectMRU(const QString &project) { QStringList files = mSettings->value(SETTINGS_MRU_PROJECTS).toStringList(); files.removeAll(project); files.prepend(project); while (files.size() > MaxRecentProjects) files.removeLast(); mSettings->setValue(SETTINGS_MRU_PROJECTS, files); updateMRUMenuItems(); } void MainWindow::removeProjectMRU(const QString &project) { QStringList files = mSettings->value(SETTINGS_MRU_PROJECTS).toStringList(); files.removeAll(project); mSettings->setValue(SETTINGS_MRU_PROJECTS, files); updateMRUMenuItems(); } void MainWindow::selectPlatform() { QAction *action = qobject_cast(sender()); if (action) { const Settings::PlatformType platform = (Settings::PlatformType) action->data().toInt(); mSettings->setValue(SETTINGS_CHECKED_PLATFORM, platform); } } void MainWindow::suppressIds(QStringList ids) { if (!mProjectFile) return; ids.removeDuplicates(); QList suppressions = mProjectFile->getSuppressions(); foreach (QString id, ids) { // Remove all matching suppressions std::string id2 = id.toStdString(); for (int i = 0; i < suppressions.size();) { if (suppressions[i].errorId == id2) suppressions.removeAt(i); else ++i; } Suppressions::Suppression newSuppression; newSuppression.errorId = id2; suppressions << newSuppression; } mProjectFile->setSuppressions(suppressions); mProjectFile->write(); } void MainWindow::editFunctionContract(QString function) { if (!mProjectFile) return; QString expects; const auto it = mProjectFile->getFunctionContracts().find(function.toStdString()); if (it != mProjectFile->getFunctionContracts().end()) expects = QString::fromStdString(it->second); FunctionContractDialog dlg(nullptr, function, expects); if (dlg.exec() == QDialog::Accepted) { mProjectFile->setFunctionContract(function, dlg.getExpects()); mProjectFile->write(); } updateFunctionContractsTab(); } void MainWindow::editVariableContract(QString var) { if (!mProjectFile) return; VariableContractsDialog dlg(nullptr, var); if (dlg.exec() == QDialog::Accepted) { mProjectFile->setVariableContracts(dlg.getVarname(), dlg.getMin(), dlg.getMax()); mProjectFile->write(); } updateVariableContractsTab(); } void MainWindow::deleteFunctionContract(const QString& function) { if (mProjectFile) { mProjectFile->deleteFunctionContract(function); mProjectFile->write(); } } void MainWindow::deleteVariableContract(const QString& var) { if (mProjectFile) { mProjectFile->deleteVariableContract(var); mProjectFile->write(); } } cppcheck-2.7/gui/mainwindow.h000066400000000000000000000334451417746362400163020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "ui_mainwindow.h" #include "settings.h" #include "platforms.h" #include #include #include class ThreadHandler; class TranslationHandler; class ScratchPad; class ProjectFile; class QAction; class QActionGroup; class QSettings; class QTimer; /// @addtogroup GUI /// @{ /** * @brief Main window for cppcheck-gui * */ class MainWindow : public QMainWindow { Q_OBJECT public: /** * @brief Maximum number of MRU project items in File-menu. */ enum { MaxRecentProjects = 5 }; MainWindow(TranslationHandler* th, QSettings* settings); MainWindow(const MainWindow &) = delete; virtual ~MainWindow(); MainWindow &operator=(const MainWindow &) = delete; /** * List of checked platforms. */ Platforms mPlatforms; /** * @brief Analyze given code * * @param code Content of the (virtual) file to be analyzed * @param filename Name of the (virtual) file to be analyzed - determines language. */ void analyzeCode(const QString& code, const QString& filename); public slots: /** Update "Functions" tab */ void updateFunctionContractsTab(); /** Update "Variables" tab */ void updateVariableContractsTab(); /** @brief Slot for analyze files menu item */ void analyzeFiles(); /** @brief Slot to reanalyze all files */ void reAnalyzeAll(); /** @brief Slot to reanalyze with checking library configuration */ void checkLibrary(); /** @brief Slot to check configuration */ void checkConfiguration(); /** * @brief Slot to reanalyze selected files * @param selectedFilesList list of selected files */ void performSelectedFilesCheck(const QStringList &selectedFilesList); /** @brief Slot to reanalyze modified files */ void reAnalyzeModified(); /** @brief Slot to clear all search results */ void clearResults(); /** @brief Slot to open XML report file */ void openResults(); /** * @brief Show errors with type "style" * @param checked Should errors be shown (true) or hidden (false) */ void showStyle(bool checked); /** * @brief Show errors with type "error" * @param checked Should errors be shown (true) or hidden (false) */ void showErrors(bool checked); /** * @brief Show errors with type "warning" * @param checked Should errors be shown (true) or hidden (false) */ void showWarnings(bool checked); /** * @brief Show errors with type "portability" * @param checked Should errors be shown (true) or hidden (false) */ void showPortability(bool checked); /** * @brief Show errors with type "performance" * @param checked Should errors be shown (true) or hidden (false) */ void showPerformance(bool checked); /** * @brief Show errors with type "information" * @param checked Should errors be shown (true) or hidden (false) */ void showInformation(bool checked); /** @brief Slot to check all "Show errors" menu items */ void checkAll(); /** @brief Slot to uncheck all "Show errors" menu items */ void uncheckAll(); /** @brief Slot for analyze directory menu item */ void analyzeDirectory(); /** @brief Slot to open program's settings dialog */ void programSettings(); /** @brief Slot to open program's about dialog */ void about(); /** @brief Slot to to show license text */ void showLicense(); /** @brief Slot to to show authors list */ void showAuthors(); /** @brief Slot to save results */ void save(); /** @brief Slot to create new project file */ void newProjectFile(); /** @brief Slot to open project file and start analyzing contained paths. */ void openProjectFile(); /** @brief Slot to show scratchpad. */ void showScratchpad(); /** @brief Slot to close open project file. */ void closeProjectFile(); /** @brief Slot to edit project file. */ void editProjectFile(); /** @brief Slot for showing the scan and project statistics. */ void showStatistics(); /** @brief Slot for showing the library editor */ void showLibraryEditor(); protected slots: /** @brief Slot for checkthread's done signal */ void analysisDone(); /** @brief Lock down UI while analyzing */ void checkLockDownUI(); /** @brief Slot for enabling save and clear button */ void resultsAdded(); /** @brief Slot for showing/hiding standard toolbar */ void toggleMainToolBar(); /** @brief Slot for showing/hiding Categories toolbar */ void toggleViewToolBar(); /** @brief Slot for showing/hiding Filter toolbar */ void toggleFilterToolBar(); /** @brief Slot for updating View-menu before it is shown. */ void aboutToShowViewMenu(); /** @brief Slot when stop analysis button is pressed */ void stopAnalysis(); /** @brief Open help file contents */ void openHelpContents(); /** @brief Filters the results in the result list. */ void filterResults(); /** @brief Opens recently opened project file. */ void openRecentProject(); /** @brief Selects the platform as analyzed platform. */ void selectPlatform(); /** Suppress error ids */ void suppressIds(QStringList ids); /** Edit contract for function */ void editFunctionContract(QString function); /** Edit constraints for variable */ void editVariableContract(QString var); /** Delete contract for function */ void deleteFunctionContract(const QString& function); /** Edit constraints for variable */ void deleteVariableContract(const QString& var); private: /** Get filename for last results */ QString getLastResults() const; /** @brief Reanalyzes files */ void reAnalyze(bool all); /** * @brief Reanalyze selected files * @param files list of selected files */ void reAnalyzeSelected(QStringList files); /** * @brief Analyze the project. * @param projectFile Pointer to the project to analyze. * @param checkLibrary Flag to indicate if the library should be checked. * @param checkConfiguration Flag to indicate if the configuration should be checked. */ void analyzeProject(const ProjectFile *projectFile, const bool checkLibrary = false, const bool checkConfiguration = false); /** * @brief Set current language * @param code Language code of the language to set (e.g. "en"). */ void setLanguage(const QString &code); /** @brief Event coming when application is about to close. */ void closeEvent(QCloseEvent *event) override; /** * @brief Helper function to toggle all show error menu items * @param checked Should all errors be shown (true) or hidden (false) */ void toggleAllChecked(bool checked); /** @brief Helper function to enable/disable all check,recheck buttons */ void enableCheckButtons(bool enable); /** @brief Helper function to enable/disable results buttons (clear,save,print) */ void enableResultsButtons(); /** * @brief Select files/or directory to analyze. * Helper function to open a dialog to ask user to select files or * directory to analyze. Use native dialogs instead of Qt:s own dialogs. * * @param mode Dialog open mode (files or directories) * @return QStringList of files or directories that were selected to analyze */ QStringList selectFilesToAnalyze(QFileDialog::FileMode mode); /** * @brief Analyze project * @param p imported project * @param checkLibrary Flag to indicate if library should be checked * @param checkConfiguration Flag to indicate if the configuration should be checked. */ void doAnalyzeProject(ImportProject p, const bool checkLibrary = false, const bool checkConfiguration = false); /** * @brief Analyze all files specified in parameter files * * @param files List of files and/or directories to analyze * @param checkLibrary Flag to indicate if library should be checked * @param checkConfiguration Flag to indicate if the configuration should be checked. */ void doAnalyzeFiles(const QStringList &files, const bool checkLibrary = false, const bool checkConfiguration = false); /** * @brief Get our default cppcheck settings and read project file. * * @return Default cppcheck settings */ Settings getCppcheckSettings(); /** @brief Load program settings */ void loadSettings(); /** @brief Save program settings */ void saveSettings() const; /** * @brief Format main window title. * @param text Text added to end of the title. */ void formatAndSetTitle(const QString &text = QString()); /** @brief Show help contents */ void openOnlineHelp(); /** * @brief Enable or disable project file actions. * Project editing and closing actions should be only enabled when project is * open and we are not analyzing files. * @param enable If true then actions are enabled. */ void enableProjectActions(bool enable); /** * @brief Enable or disable project file actions. * Project opening and creating actions should be disabled when analyzing. * @param enable If true then actions are enabled. */ void enableProjectOpenActions(bool enable); /** * @brief Add include directories. * @param includeDirs List of include directories to add. * @param result Settings class where include directories are added. */ void addIncludeDirs(const QStringList &includeDirs, Settings &result); /** * @brief Handle command line parameters given to GUI. * @param params List of string given to command line. */ void handleCLIParams(const QStringList ¶ms); /** * @brief Load XML file to the GUI. * @param selectedFile Filename (inc. path) of XML file to load. */ void loadResults(const QString &selectedFile); /** * @brief Load XML file to the GUI. * @param selectedFile Filename (inc. path) of XML file to load. * @param sourceDirectory Path to the directory that the results were generated for. */ void loadResults(const QString &selectedFile, const QString &sourceDirectory); /** * @brief Load last project results to the GUI. * @return Returns true if last results was loaded */ bool loadLastResults(); /** * @brief Load project file to the GUI. * @param filePath Filename (inc. path) of project file to load. */ void loadProjectFile(const QString &filePath); /** * @brief Load library file * @param library library to use * @param filename filename (no path) * @return error code */ Library::Error loadLibrary(Library *library, const QString &filename); /** * @brief Tries to load library file, prints message on error * @param library library to use * @param filename filename (no path) * @return True if no error */ bool tryLoadLibrary(Library *library, const QString& filename); /** * @brief Update project MRU items in File-menu. */ void updateMRUMenuItems(); /** * @brief Add project file (path) to the MRU list. * @param project Full path to the project file to add. */ void addProjectMRU(const QString &project); /** * @brief Remove project file (path) from the MRU list. * @param project Full path of the project file to remove. */ void removeProjectMRU(const QString &project); /** @brief Program settings */ QSettings *mSettings; /** @brief Thread to analyze files */ ThreadHandler *mThread; /** @brief List of user defined applications to open errors with */ ApplicationList *mApplications; /** @brief Class to handle translation changes */ TranslationHandler *mTranslation; /** @brief Class holding all UI components */ Ui::MainWindow mUI; /** @brief Current analyzed directory. */ QString mCurrentDirectory; /** @brief Scratchpad. */ ScratchPad* mScratchPad; /** @brief Project (file). */ ProjectFile *mProjectFile; /** @brief Filter field in the Filter toolbar. */ QLineEdit* mLineEditFilter; /** @brief Timer to delay filtering while typing. */ QTimer* mFilterTimer; /** @brief GUI actions for selecting the analyzed platform. */ QActionGroup *mPlatformActions; /** @brief GUI actions for selecting the coding standard. */ QActionGroup *mCStandardActions, *mCppStandardActions; /** @brief GUI actions for selecting language. */ QActionGroup *mSelectLanguageActions; /** * @brief Are we exiting the cppcheck? * If this is true then the cppcheck is waiting for check threads to exit * so that the application can be closed. */ bool mExiting; /** @brief Set to true in case of loading log file. */ bool mIsLogfileLoaded; /** * @brief Project MRU menu actions. * List of MRU menu actions. Needs also to store the separator. */ QAction *mRecentProjectActs[MaxRecentProjects + 1]; }; /// @} #endif // MAINWINDOW_H cppcheck-2.7/gui/mainwindow.ui000066400000000000000000000567661417746362400165030ustar00rootroot00000000000000 MainWindow 0 0 640 480 0 0 640 480 Cppcheck 22 22 0 0 0 0 16777215 16777215 0 0 640 28 &File &View &Toolbars &Help A&nalyze C++ standard &C standard &Edit Standard TopToolBarArea false Categories TopToolBarArea false Filter TopToolBarArea false &License... A&uthors... :/images/help-browser.png:/images/help-browser.png &About... &Files... Analyze files Analyze files Ctrl+F :/cppcheck-gui.png:/cppcheck-gui.png &Directory... Analyze directory Analyze directory Ctrl+D :/images/view-refresh.png:/images/view-refresh.png &Reanalyze modified files Ctrl+R :/images/view-recheck.png:/images/view-recheck.png Reanal&yze all files :/images/process-stop.png:/images/process-stop.png &Stop Stop analysis Stop analysis Esc :/images/media-floppy.png:/images/media-floppy.png &Save results to file... Ctrl+S &Quit Ctrl+Q :/images/edit-clear.png:/images/edit-clear.png &Clear results :/images/preferences-system.png:/images/preferences-system.png &Preferences true :/images/applications-development.png:/images/applications-development.png Style war&nings Show style warnings Show style warnings true :/images/showerrors.png:/images/showerrors.png E&rrors Show errors Show errors &Check all &Uncheck all Collapse &all &Expand all true &Standard Standard items &Contents Open the help contents F1 Toolbar true &Categories Error categories &Open XML... :/images/openproject.png:/images/openproject.png Open P&roject File... Ctrl+Shift+O :/images/scratchpad.png:/images/scratchpad.png Sh&ow Scratchpad... &New Project File... Ctrl+Shift+N &Log View Log View false C&lose Project File false &Edit Project File... false :/images/text-x-generic.png:/images/text-x-generic.png &Statistics true :/images/showwarnings.png:/images/showwarnings.png &Warnings Show warnings Show warnings true :/images/showperformance.png:/images/showperformance.png Per&formance warnings Show performance warnings Show performance warnings false Show &hidden true :/images/dialog-information.png:/images/dialog-information.png &Information Show information messages true :/images/applications-system.png:/images/applications-system.png &Portability Show portability warnings true :/cppcheck-gui.png:/cppcheck-gui.png Cppcheck Show Cppcheck results true :/images/llvm-dragon.png:/images/llvm-dragon.png Clang Show Clang results true &Filter Filter results Project &MRU placeholder true true Windows 32-bit ANSI true Windows 32-bit Unicode true Unix 32-bit true Unix 64-bit true Windows 64-bit false P&latforms false true false C++&11 true true C&99 true &Posix true C&11 true &C89 true &C++03 &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results &Library Editor... Open library editor true &Auto-detect language true &Enforce C++ true E&nforce C true false C++14 false Reanalyze and check library false Check configuration (defines, includes) true C++17 true true C++20 ResultsView QWidget
resultsview.h
1
cppcheck-2.7/gui/newsuppressiondialog.cpp000066400000000000000000000053371417746362400207440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "newsuppressiondialog.h" #include "ui_newsuppressiondialog.h" #include "cppcheck.h" #include "color.h" #include "errorlogger.h" #include "suppressions.h" NewSuppressionDialog::NewSuppressionDialog(QWidget *parent) : QDialog(parent), mUI(new Ui::NewSuppressionDialog) { mUI->setupUi(this); class QErrorLogger : public ErrorLogger { public: void reportOut(const std::string & /*outmsg*/, Color) override {} void reportErr(const ErrorMessage &msg) override { errorIds << QString::fromStdString(msg.id); } void bughuntingReport(const std::string & /*str*/) override {} QStringList errorIds; }; QErrorLogger errorLogger; CppCheck cppcheck(errorLogger, false, nullptr); cppcheck.getErrorMessages(); errorLogger.errorIds.sort(); mUI->mComboErrorId->addItems(errorLogger.errorIds); mUI->mComboErrorId->setCurrentIndex(-1); mUI->mComboErrorId->setCurrentText(""); } NewSuppressionDialog::~NewSuppressionDialog() { delete mUI; } Suppressions::Suppression NewSuppressionDialog::getSuppression() const { Suppressions::Suppression ret; ret.errorId = mUI->mComboErrorId->currentText().toStdString(); if (ret.errorId.empty()) ret.errorId = "*"; ret.fileName = mUI->mTextFileName->text().toStdString(); if (!mUI->mTextLineNumber->text().isEmpty()) ret.lineNumber = mUI->mTextLineNumber->text().toInt(); ret.symbolName = mUI->mTextSymbolName->text().toStdString(); return ret; } void NewSuppressionDialog::setSuppression(const Suppressions::Suppression &suppression) { setWindowTitle(tr("Edit suppression")); mUI->mComboErrorId->setCurrentText(QString::fromStdString(suppression.errorId)); mUI->mTextFileName->setText(QString::fromStdString(suppression.fileName)); mUI->mTextLineNumber->setText(suppression.lineNumber > 0 ? QString::number(suppression.lineNumber) : QString()); mUI->mTextSymbolName->setText(QString::fromStdString(suppression.symbolName)); } cppcheck-2.7/gui/newsuppressiondialog.h000066400000000000000000000032361417746362400204050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef NEWSUPPRESSIONDIALOG_H #define NEWSUPPRESSIONDIALOG_H #include "suppressions.h" #include namespace Ui { class NewSuppressionDialog; } class NewSuppressionDialog : public QDialog { Q_OBJECT public: explicit NewSuppressionDialog(QWidget *parent = nullptr); NewSuppressionDialog(const NewSuppressionDialog &) = delete; ~NewSuppressionDialog(); NewSuppressionDialog &operator=(const NewSuppressionDialog &) = delete; /** * @brief Translate the user input in the GUI into a suppression * @return Cppcheck suppression */ Suppressions::Suppression getSuppression() const; /** * @brief Update the GUI so it corresponds with the given * Cppcheck suppression * @param suppression Cppcheck suppression */ void setSuppression(const Suppressions::Suppression &suppression); private: Ui::NewSuppressionDialog *mUI; }; #endif // NEWSUPPRESSIONDIALOG_H cppcheck-2.7/gui/newsuppressiondialog.ui000066400000000000000000000057631417746362400206020ustar00rootroot00000000000000 NewSuppressionDialog Qt::ApplicationModal 0 0 334 184 New suppression Error ID File name Line number Symbol name true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok mComboErrorId mTextFileName mTextLineNumber mTextSymbolName buttonBox accepted() NewSuppressionDialog accept() 248 254 157 274 buttonBox rejected() NewSuppressionDialog reject() 316 260 286 274 cppcheck-2.7/gui/platforms.cpp000066400000000000000000000033121417746362400164560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "platforms.h" Platforms::Platforms(QObject *parent) : QObject(parent) { init(); } void Platforms::add(const QString &title, Settings::PlatformType platform) { Platform plat; plat.mTitle = title; plat.mType = platform; plat.mActMainWindow = nullptr; mPlatforms << plat; } void Platforms::init() { add(tr("Native"), Settings::Native); add(tr("Unix 32-bit"), Settings::Unix32); add(tr("Unix 64-bit"), Settings::Unix64); add(tr("Windows 32-bit ANSI"), Settings::Win32A); add(tr("Windows 32-bit Unicode"), Settings::Win32W); add(tr("Windows 64-bit"), Settings::Win64); } int Platforms::getCount() const { return mPlatforms.count(); } Platform& Platforms::get(Settings::PlatformType platform) { QList::iterator iter = mPlatforms.begin(); while (iter != mPlatforms.end()) { if ((*iter).mType == platform) { return *iter; } ++iter; } return mPlatforms.first(); } cppcheck-2.7/gui/platforms.h000066400000000000000000000030201417746362400161170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PLATFORMS_H #define PLATFORMS_H #include "settings.h" #include #include #include class QAction; /// @addtogroup GUI /// @{ /** * @brief Checked platform GUI-data. */ struct Platform { QString mTitle; /**< Text visible in the GUI. */ Settings::PlatformType mType; /**< Type in the core. */ QAction *mActMainWindow; /**< Pointer to main window action item. */ }; /** * @brief List of checked platforms. */ class Platforms : public QObject { Q_OBJECT public: explicit Platforms(QObject *parent = nullptr); void add(const QString &title, Settings::PlatformType platform); int getCount() const; void init(); Platform& get(Settings::PlatformType platform); QList mPlatforms; }; /// @} #endif // PLATFORMS_H cppcheck-2.7/gui/precompiled_qmake.h000066400000000000000000000015161417746362400176010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "precompiled.h" #include #include #include cppcheck-2.7/gui/printablereport.cpp000066400000000000000000000031021417746362400176600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "printablereport.h" #include "erroritem.h" #include PrintableReport::PrintableReport() : Report(QString()) {} PrintableReport::~PrintableReport() {} bool PrintableReport::create() { return true; } void PrintableReport::writeHeader() { // No header for printable report } void PrintableReport::writeFooter() { // No footer for printable report } void PrintableReport::writeError(const ErrorItem &error) { const QString file = QDir::toNativeSeparators(error.errorPath.back().file); QString line = QString("%1,%2,").arg(file).arg(error.errorPath.back().line); line += QString("%1,%2").arg(GuiSeverity::toString(error.severity)).arg(error.summary); mFormattedReport += line; mFormattedReport += "\n"; } QString PrintableReport::getFormattedReportText() const { return mFormattedReport; } cppcheck-2.7/gui/printablereport.h000066400000000000000000000034331417746362400173340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PRINTABLE_REPORT_H #define PRINTABLE_REPORT_H #include "report.h" /// @addtogroup GUI /// @{ /** * @brief Printable (in-memory) report. * This report formats results and exposes them for printing. */ class PrintableReport : public Report { public: PrintableReport(); virtual ~PrintableReport(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create() override; /** * @brief Write report header. */ virtual void writeHeader() override; /** * @brief Write report footer. */ virtual void writeFooter() override; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) override; /** * @brief Returns the formatted report. */ QString getFormattedReportText() const; private: /** * @brief Stores the formatted report contents. */ QString mFormattedReport; }; /// @} #endif // PRINTABLE_REPORT_H cppcheck-2.7/gui/projectfile.cpp000066400000000000000000001173731417746362400167720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "projectfile.h" #include "common.h" #include "importproject.h" #include "settings.h" #include #include #include ProjectFile *ProjectFile::mActiveProject; ProjectFile::ProjectFile(QObject *parent) : QObject(parent) { clear(); } ProjectFile::ProjectFile(const QString &filename, QObject *parent) : QObject(parent), mFilename(filename) { clear(); read(); } void ProjectFile::clear() { const Settings settings; clangParser = false; bugHunting = false; mRootPath.clear(); mBuildDir.clear(); mImportProject.clear(); mAnalyzeAllVsConfigs = true; mIncludeDirs.clear(); mDefines.clear(); mUndefines.clear(); mPaths.clear(); mExcludedPaths.clear(); mFunctionContracts.clear(); mVariableContracts.clear(); mLibraries.clear(); mPlatform.clear(); mSuppressions.clear(); mAddons.clear(); mClangAnalyzer = mClangTidy = false; mAnalyzeAllVsConfigs = false; mCheckHeaders = true; mCheckUnusedTemplates = true; mMaxCtuDepth = settings.maxCtuDepth; mMaxTemplateRecursion = settings.maxTemplateRecursion; mCheckUnknownFunctionReturn.clear(); safeChecks.clear(); mVsConfigurations.clear(); mTags.clear(); mWarningTags.clear(); } bool ProjectFile::read(const QString &filename) { if (!filename.isEmpty()) mFilename = filename; QFile file(mFilename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return false; clear(); QXmlStreamReader xmlReader(&file); bool insideProject = false; bool projectTagFound = false; while (!xmlReader.atEnd()) { switch (xmlReader.readNext()) { case QXmlStreamReader::StartElement: if (xmlReader.name() == CppcheckXml::ProjectElementName) { insideProject = true; projectTagFound = true; break; } if (!insideProject) break; // Read root path from inside project element if (xmlReader.name() == CppcheckXml::RootPathName) readRootPath(xmlReader); // Read root path from inside project element if (xmlReader.name() == CppcheckXml::BuildDirElementName) readBuildDir(xmlReader); // Find paths to check from inside project element if (xmlReader.name() == CppcheckXml::PathsElementName) readCheckPaths(xmlReader); if (xmlReader.name() == CppcheckXml::ImportProjectElementName) readImportProject(xmlReader); if (xmlReader.name() == CppcheckXml::AnalyzeAllVsConfigsElementName) mAnalyzeAllVsConfigs = readBool(xmlReader); if (xmlReader.name() == CppcheckXml::Parser) clangParser = true; if (xmlReader.name() == CppcheckXml::BugHunting) bugHunting = true; if (xmlReader.name() == CppcheckXml::CheckHeadersElementName) mCheckHeaders = readBool(xmlReader); if (xmlReader.name() == CppcheckXml::CheckUnusedTemplatesElementName) mCheckUnusedTemplates = readBool(xmlReader); // Find include directory from inside project element if (xmlReader.name() == CppcheckXml::IncludeDirElementName) readIncludeDirs(xmlReader); // Find preprocessor define from inside project element if (xmlReader.name() == CppcheckXml::DefinesElementName) readDefines(xmlReader); // Find preprocessor define from inside project element if (xmlReader.name() == CppcheckXml::UndefinesElementName) readStringList(mUndefines, xmlReader, CppcheckXml::UndefineName); // Find exclude list from inside project element if (xmlReader.name() == CppcheckXml::ExcludeElementName) readExcludes(xmlReader); // Find ignore list from inside project element // These are read for compatibility if (xmlReader.name() == CppcheckXml::IgnoreElementName) readExcludes(xmlReader); // Function contracts if (xmlReader.name() == CppcheckXml::FunctionContracts) readFunctionContracts(xmlReader); // Variable constraints if (xmlReader.name() == CppcheckXml::VariableContractsElementName) readVariableContracts(xmlReader); // Find libraries list from inside project element if (xmlReader.name() == CppcheckXml::LibrariesElementName) readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName); if (xmlReader.name() == CppcheckXml::PlatformElementName) readPlatform(xmlReader); // Find suppressions list from inside project element if (xmlReader.name() == CppcheckXml::SuppressionsElementName) readSuppressions(xmlReader); // Unknown function return values if (xmlReader.name() == CppcheckXml::CheckUnknownFunctionReturn) readStringList(mCheckUnknownFunctionReturn, xmlReader, CppcheckXml::Name); // check all function parameter values if (xmlReader.name() == Settings::SafeChecks::XmlRootName) safeChecks.loadFromXml(xmlReader); // Addons if (xmlReader.name() == CppcheckXml::AddonsElementName) readStringList(mAddons, xmlReader, CppcheckXml::AddonElementName); // Tools if (xmlReader.name() == CppcheckXml::ToolsElementName) { QStringList tools; readStringList(tools, xmlReader, CppcheckXml::ToolElementName); mClangAnalyzer = tools.contains(CLANG_ANALYZER); mClangTidy = tools.contains(CLANG_TIDY); } if (xmlReader.name() == CppcheckXml::TagsElementName) readStringList(mTags, xmlReader, CppcheckXml::TagElementName); if (xmlReader.name() == CppcheckXml::TagWarningsElementName) readTagWarnings(xmlReader, xmlReader.attributes().value(QString(), CppcheckXml::TagAttributeName).toString()); if (xmlReader.name() == CppcheckXml::MaxCtuDepthElementName) mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth); if (xmlReader.name() == CppcheckXml::MaxTemplateRecursionElementName) mMaxTemplateRecursion = readInt(xmlReader, mMaxTemplateRecursion); // VSConfiguration if (xmlReader.name() == CppcheckXml::VSConfigurationElementName) readVsConfigurations(xmlReader); break; case QXmlStreamReader::EndElement: if (xmlReader.name() == CppcheckXml::ProjectElementName) insideProject = false; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } file.close(); return projectTagFound; } void ProjectFile::readRootPath(QXmlStreamReader &reader) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), CppcheckXml::RootPathNameAttrib).toString(); if (!name.isEmpty()) mRootPath = name; } void ProjectFile::readBuildDir(QXmlStreamReader &reader) { mBuildDir.clear(); do { const QXmlStreamReader::TokenType type = reader.readNext(); switch (type) { case QXmlStreamReader::Characters: mBuildDir = reader.text().toString(); FALLTHROUGH; case QXmlStreamReader::EndElement: return; // Not handled case QXmlStreamReader::StartElement: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } void ProjectFile::readImportProject(QXmlStreamReader &reader) { mImportProject.clear(); do { const QXmlStreamReader::TokenType type = reader.readNext(); switch (type) { case QXmlStreamReader::Characters: mImportProject = reader.text().toString(); FALLTHROUGH; case QXmlStreamReader::EndElement: return; // Not handled case QXmlStreamReader::StartElement: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } bool ProjectFile::readBool(QXmlStreamReader &reader) { bool ret = false; do { const QXmlStreamReader::TokenType type = reader.readNext(); switch (type) { case QXmlStreamReader::Characters: ret = (reader.text().toString() == "true"); FALLTHROUGH; case QXmlStreamReader::EndElement: return ret; // Not handled case QXmlStreamReader::StartElement: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } int ProjectFile::readInt(QXmlStreamReader &reader, int defaultValue) { int ret = defaultValue; do { const QXmlStreamReader::TokenType type = reader.readNext(); switch (type) { case QXmlStreamReader::Characters: ret = reader.text().toString().toInt(); FALLTHROUGH; case QXmlStreamReader::EndElement: return ret; // Not handled case QXmlStreamReader::StartElement: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } void ProjectFile::readIncludeDirs(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; bool allRead = false; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read dir-elements if (reader.name().toString() == CppcheckXml::DirElementName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), CppcheckXml::DirNameAttrib).toString(); if (!name.isEmpty()) mIncludeDirs << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == CppcheckXml::IncludeDirElementName) allRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (!allRead); } void ProjectFile::readDefines(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; bool allRead = false; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read define-elements if (reader.name().toString() == CppcheckXml::DefineName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), CppcheckXml::DefineNameAttrib).toString(); if (!name.isEmpty()) mDefines << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == CppcheckXml::DefinesElementName) allRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (!allRead); } void ProjectFile::readCheckPaths(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; bool allRead = false; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read dir-elements if (reader.name().toString() == CppcheckXml::PathName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), CppcheckXml::PathNameAttrib).toString(); if (!name.isEmpty()) mPaths << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == CppcheckXml::PathsElementName) allRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (!allRead); } void ProjectFile::readExcludes(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; bool allRead = false; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read exclude-elements if (reader.name().toString() == CppcheckXml::ExcludePathName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), CppcheckXml::ExcludePathNameAttrib).toString(); if (!name.isEmpty()) mExcludedPaths << name; } // Read ignore-elements - deprecated but support reading them else if (reader.name().toString() == CppcheckXml::IgnorePathName) { QXmlStreamAttributes attribs = reader.attributes(); QString name = attribs.value(QString(), CppcheckXml::IgnorePathNameAttrib).toString(); if (!name.isEmpty()) mExcludedPaths << name; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == CppcheckXml::IgnoreElementName) allRead = true; if (reader.name().toString() == CppcheckXml::ExcludeElementName) allRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (!allRead); } void ProjectFile::readFunctionContracts(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; bool allRead = false; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: if (reader.name().toString() == CppcheckXml::FunctionContract) { QXmlStreamAttributes attribs = reader.attributes(); QString function = attribs.value(QString(), CppcheckXml::ContractFunction).toString(); QString expects = attribs.value(QString(), CppcheckXml::ContractExpects).toString(); if (!function.isEmpty() && !expects.isEmpty()) mFunctionContracts[function.toStdString()] = expects.toStdString(); } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == CppcheckXml::FunctionContracts) allRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (!allRead); } void ProjectFile::readVariableContracts(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; while (true) { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: if (reader.name().toString() == CppcheckXml::VariableContractItemElementName) { QXmlStreamAttributes attribs = reader.attributes(); QString varname = attribs.value(QString(), CppcheckXml::VariableContractVarName).toString(); QString minValue = attribs.value(QString(), CppcheckXml::VariableContractMin).toString(); QString maxValue = attribs.value(QString(), CppcheckXml::VariableContractMax).toString(); setVariableContracts(varname, minValue, maxValue); } break; case QXmlStreamReader::EndElement: if (reader.name().toString() == CppcheckXml::VariableContractsElementName) return; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } } void ProjectFile::readVsConfigurations(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read library-elements if (reader.name().toString() == CppcheckXml::VSConfigurationName) { QString config; type = reader.readNext(); if (type == QXmlStreamReader::Characters) { config = reader.text().toString(); } mVsConfigurations << config; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() != CppcheckXml::VSConfigurationName) return; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } void ProjectFile::readPlatform(QXmlStreamReader &reader) { do { const QXmlStreamReader::TokenType type = reader.readNext(); switch (type) { case QXmlStreamReader::Characters: mPlatform = reader.text().toString(); FALLTHROUGH; case QXmlStreamReader::EndElement: return; // Not handled case QXmlStreamReader::StartElement: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } void ProjectFile::readSuppressions(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read library-elements if (reader.name().toString() == CppcheckXml::SuppressionElementName) { Suppressions::Suppression suppression; if (reader.attributes().hasAttribute(QString(),"fileName")) suppression.fileName = reader.attributes().value(QString(),"fileName").toString().toStdString(); if (reader.attributes().hasAttribute(QString(),"lineNumber")) suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt(); if (reader.attributes().hasAttribute(QString(),"symbolName")) suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString(); if (reader.attributes().hasAttribute(QString(),"hash")) suppression.hash = reader.attributes().value(QString(),"hash").toULongLong(); type = reader.readNext(); if (type == QXmlStreamReader::Characters) { suppression.errorId = reader.text().toString().toStdString(); } mSuppressions << suppression; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() != CppcheckXml::SuppressionElementName) return; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } void ProjectFile::readTagWarnings(QXmlStreamReader &reader, const QString &tag) { QXmlStreamReader::TokenType type; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read library-elements if (reader.name().toString() == CppcheckXml::WarningElementName) { std::size_t hash = reader.attributes().value(QString(), CppcheckXml::HashAttributeName).toULongLong(); mWarningTags[hash] = tag; } break; case QXmlStreamReader::EndElement: if (reader.name().toString() != CppcheckXml::WarningElementName) return; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } void ProjectFile::readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[]) { QXmlStreamReader::TokenType type; bool allRead = false; do { type = reader.readNext(); switch (type) { case QXmlStreamReader::StartElement: // Read library-elements if (reader.name().toString() == elementname) { type = reader.readNext(); if (type == QXmlStreamReader::Characters) { QString text = reader.text().toString(); stringlist << text; } } break; case QXmlStreamReader::EndElement: if (reader.name().toString() != elementname) allRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (!allRead); } void ProjectFile::setIncludes(const QStringList &includes) { mIncludeDirs = includes; } void ProjectFile::setDefines(const QStringList &defines) { mDefines = defines; } void ProjectFile::setUndefines(const QStringList &undefines) { mUndefines = undefines; } void ProjectFile::setCheckPaths(const QStringList &paths) { mPaths = paths; } void ProjectFile::setExcludedPaths(const QStringList &paths) { mExcludedPaths = paths; } void ProjectFile::setLibraries(const QStringList &libraries) { mLibraries = libraries; } void ProjectFile::setFunctionContract(const QString& function, const QString& expects) { mFunctionContracts[function.toStdString()] = expects.toStdString(); } void ProjectFile::setPlatform(const QString &platform) { mPlatform = platform; } void ProjectFile::setSuppressions(const QList &suppressions) { mSuppressions = suppressions; } void ProjectFile::addSuppression(const Suppressions::Suppression &suppression) { mSuppressions.append(suppression); } void ProjectFile::setAddons(const QStringList &addons) { mAddons = addons; } void ProjectFile::setVSConfigurations(const QStringList &vsConfigs) { mVsConfigurations = vsConfigs; } void ProjectFile::setWarningTags(std::size_t hash, const QString& tags) { if (tags.isEmpty()) mWarningTags.erase(hash); else if (hash > 0) mWarningTags[hash] = tags; } QString ProjectFile::getWarningTags(std::size_t hash) const { auto it = mWarningTags.find(hash); return (it != mWarningTags.end()) ? it->second : QString(); } bool ProjectFile::write(const QString &filename) { if (!filename.isEmpty()) mFilename = filename; QFile file(mFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false; QXmlStreamWriter xmlWriter(&file); xmlWriter.setAutoFormatting(true); xmlWriter.writeStartDocument("1.0"); xmlWriter.writeStartElement(CppcheckXml::ProjectElementName); xmlWriter.writeAttribute(CppcheckXml::ProjectVersionAttrib, CppcheckXml::ProjectFileVersion); if (!mRootPath.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::RootPathName); xmlWriter.writeAttribute(CppcheckXml::RootPathNameAttrib, mRootPath); xmlWriter.writeEndElement(); } if (!mBuildDir.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::BuildDirElementName); xmlWriter.writeCharacters(mBuildDir); xmlWriter.writeEndElement(); } if (!mPlatform.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::PlatformElementName); xmlWriter.writeCharacters(mPlatform); xmlWriter.writeEndElement(); } if (!mImportProject.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::ImportProjectElementName); xmlWriter.writeCharacters(mImportProject); xmlWriter.writeEndElement(); } xmlWriter.writeStartElement(CppcheckXml::AnalyzeAllVsConfigsElementName); xmlWriter.writeCharacters(mAnalyzeAllVsConfigs ? "true" : "false"); xmlWriter.writeEndElement(); if (clangParser) { xmlWriter.writeStartElement(CppcheckXml::Parser); xmlWriter.writeCharacters("clang"); xmlWriter.writeEndElement(); } if (bugHunting) { xmlWriter.writeStartElement(CppcheckXml::BugHunting); xmlWriter.writeEndElement(); } xmlWriter.writeStartElement(CppcheckXml::CheckHeadersElementName); xmlWriter.writeCharacters(mCheckHeaders ? "true" : "false"); xmlWriter.writeEndElement(); xmlWriter.writeStartElement(CppcheckXml::CheckUnusedTemplatesElementName); xmlWriter.writeCharacters(mCheckUnusedTemplates ? "true" : "false"); xmlWriter.writeEndElement(); xmlWriter.writeStartElement(CppcheckXml::MaxCtuDepthElementName); xmlWriter.writeCharacters(QString::number(mMaxCtuDepth)); xmlWriter.writeEndElement(); xmlWriter.writeStartElement(CppcheckXml::MaxTemplateRecursionElementName); xmlWriter.writeCharacters(QString::number(mMaxTemplateRecursion)); xmlWriter.writeEndElement(); if (!mIncludeDirs.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::IncludeDirElementName); foreach (QString incdir, mIncludeDirs) { xmlWriter.writeStartElement(CppcheckXml::DirElementName); xmlWriter.writeAttribute(CppcheckXml::DirNameAttrib, incdir); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mDefines.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::DefinesElementName); foreach (QString define, mDefines) { xmlWriter.writeStartElement(CppcheckXml::DefineName); xmlWriter.writeAttribute(CppcheckXml::DefineNameAttrib, define); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mVsConfigurations.isEmpty()) { writeStringList(xmlWriter, mVsConfigurations, CppcheckXml::VSConfigurationElementName, CppcheckXml::VSConfigurationName); } writeStringList(xmlWriter, mUndefines, CppcheckXml::UndefinesElementName, CppcheckXml::UndefineName); if (!mPaths.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::PathsElementName); foreach (QString path, mPaths) { xmlWriter.writeStartElement(CppcheckXml::PathName); xmlWriter.writeAttribute(CppcheckXml::PathNameAttrib, path); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mExcludedPaths.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::ExcludeElementName); foreach (QString path, mExcludedPaths) { xmlWriter.writeStartElement(CppcheckXml::ExcludePathName); xmlWriter.writeAttribute(CppcheckXml::ExcludePathNameAttrib, path); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } writeStringList(xmlWriter, mLibraries, CppcheckXml::LibrariesElementName, CppcheckXml::LibraryElementName); if (!mFunctionContracts.empty()) { xmlWriter.writeStartElement(CppcheckXml::FunctionContracts); for (const auto& contract: mFunctionContracts) { xmlWriter.writeStartElement(CppcheckXml::FunctionContract); xmlWriter.writeAttribute(CppcheckXml::ContractFunction, QString::fromStdString(contract.first)); xmlWriter.writeAttribute(CppcheckXml::ContractExpects, QString::fromStdString(contract.second)); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mVariableContracts.empty()) { xmlWriter.writeStartElement(CppcheckXml::VariableContractsElementName); for (auto vc: mVariableContracts) { xmlWriter.writeStartElement(CppcheckXml::VariableContractItemElementName); xmlWriter.writeAttribute(CppcheckXml::VariableContractVarName, vc.first); xmlWriter.writeAttribute(CppcheckXml::VariableContractMin, QString::fromStdString(vc.second.minValue)); xmlWriter.writeAttribute(CppcheckXml::VariableContractMax, QString::fromStdString(vc.second.maxValue)); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!mSuppressions.isEmpty()) { xmlWriter.writeStartElement(CppcheckXml::SuppressionsElementName); foreach (const Suppressions::Suppression &suppression, mSuppressions) { xmlWriter.writeStartElement(CppcheckXml::SuppressionElementName); if (!suppression.fileName.empty()) xmlWriter.writeAttribute("fileName", QString::fromStdString(suppression.fileName)); if (suppression.lineNumber > 0) xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber)); if (!suppression.symbolName.empty()) xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName)); if (suppression.hash > 0) xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(suppression.hash)); if (!suppression.errorId.empty()) xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId)); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } writeStringList(xmlWriter, mCheckUnknownFunctionReturn, CppcheckXml::CheckUnknownFunctionReturn, CppcheckXml::Name); safeChecks.saveToXml(xmlWriter); writeStringList(xmlWriter, mAddons, CppcheckXml::AddonsElementName, CppcheckXml::AddonElementName); QStringList tools; if (mClangAnalyzer) tools << CLANG_ANALYZER; if (mClangTidy) tools << CLANG_TIDY; writeStringList(xmlWriter, tools, CppcheckXml::ToolsElementName, CppcheckXml::ToolElementName); writeStringList(xmlWriter, mTags, CppcheckXml::TagsElementName, CppcheckXml::TagElementName); if (!mWarningTags.empty()) { QStringList tags; for (const auto& wt: mWarningTags) { if (!tags.contains(wt.second)) tags.append(wt.second); } for (const QString &tag: tags) { xmlWriter.writeStartElement(CppcheckXml::TagWarningsElementName); xmlWriter.writeAttribute(CppcheckXml::TagAttributeName, tag); for (const auto& wt: mWarningTags) { if (wt.second == tag) { xmlWriter.writeStartElement(CppcheckXml::WarningElementName); xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(wt.first)); xmlWriter.writeEndElement(); } } xmlWriter.writeEndElement(); } } xmlWriter.writeEndDocument(); file.close(); return true; } void ProjectFile::writeStringList(QXmlStreamWriter &xmlWriter, const QStringList &stringlist, const char startelementname[], const char stringelementname[]) { if (stringlist.isEmpty()) return; xmlWriter.writeStartElement(startelementname); foreach (QString str, stringlist) { xmlWriter.writeStartElement(stringelementname); xmlWriter.writeCharacters(str); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } QStringList ProjectFile::fromNativeSeparators(const QStringList &paths) { QStringList ret; foreach (const QString &path, paths) ret << QDir::fromNativeSeparators(path); return ret; } QStringList ProjectFile::getAddonsAndTools() const { QStringList ret(mAddons); if (mClangAnalyzer) ret << CLANG_ANALYZER; if (mClangTidy) ret << CLANG_TIDY; return ret; } void ProjectFile::SafeChecks::loadFromXml(QXmlStreamReader &xmlReader) { classes = externalFunctions = internalFunctions = externalVariables = false; int level = 0; do { const QXmlStreamReader::TokenType type = xmlReader.readNext(); switch (type) { case QXmlStreamReader::StartElement: ++level; if (xmlReader.name() == Settings::SafeChecks::XmlClasses) classes = true; else if (xmlReader.name() == Settings::SafeChecks::XmlExternalFunctions) externalFunctions = true; else if (xmlReader.name() == Settings::SafeChecks::XmlInternalFunctions) internalFunctions = true; else if (xmlReader.name() == Settings::SafeChecks::XmlExternalVariables) externalVariables = true; break; case QXmlStreamReader::EndElement: if (level <= 0) return; level--; break; // Not handled case QXmlStreamReader::Characters: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } while (true); } void ProjectFile::SafeChecks::saveToXml(QXmlStreamWriter &xmlWriter) const { if (!classes && !externalFunctions && !internalFunctions && !externalVariables) return; xmlWriter.writeStartElement(Settings::SafeChecks::XmlRootName); if (classes) { xmlWriter.writeStartElement(Settings::SafeChecks::XmlClasses); xmlWriter.writeEndElement(); } if (externalFunctions) { xmlWriter.writeStartElement(Settings::SafeChecks::XmlExternalFunctions); xmlWriter.writeEndElement(); } if (internalFunctions) { xmlWriter.writeStartElement(Settings::SafeChecks::XmlInternalFunctions); xmlWriter.writeEndElement(); } if (externalVariables) { xmlWriter.writeStartElement(Settings::SafeChecks::XmlExternalVariables); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon) { if (!filesDir.endsWith("/")) filesDir += "/"; QStringList searchPaths; searchPaths << filesDir << (filesDir + "addons/") << (filesDir + "../addons/") #ifdef FILESDIR << (QLatin1String(FILESDIR) + "/addons/") #endif ; foreach (QString path, searchPaths) { QString f = path + addon + ".py"; if (QFile(f).exists()) return f; } return QString(); } cppcheck-2.7/gui/projectfile.h000066400000000000000000000355301417746362400164310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PROJECT_FILE_H #define PROJECT_FILE_H #include "settings.h" #include "suppressions.h" #include #include #include #include class QXmlStreamReader; class QXmlStreamWriter; /// @addtogroup GUI /// @{ /** * @brief A class that reads and writes project files. * The project files contain project-specific settings for checking. For * example a list of include paths. */ class ProjectFile : public QObject { Q_OBJECT public: explicit ProjectFile(QObject *parent = nullptr); explicit ProjectFile(const QString &filename, QObject *parent = nullptr); ~ProjectFile() { if (this == mActiveProject) mActiveProject = nullptr; } static ProjectFile* getActiveProject() { return mActiveProject; } void setActiveProject() { mActiveProject = this; } /** * @brief Read the project file. * @param filename Filename (can be also given to constructor). */ bool read(const QString &filename = QString()); /** * @brief Get project root path. * @return project root path. */ QString getRootPath() const { return mRootPath; } QString getBuildDir() const { return mBuildDir; } QString getImportProject() const { return mImportProject; } bool getAnalyzeAllVsConfigs() const { return mAnalyzeAllVsConfigs; } bool getCheckHeaders() const { return mCheckHeaders; } void setCheckHeaders(bool b) { mCheckHeaders = b; } bool getCheckUnusedTemplates() const { return mCheckUnusedTemplates; } void setCheckUnusedTemplates(bool b) { mCheckUnusedTemplates = b; } /** * @brief Get list of include directories. * @return list of directories. */ QStringList getIncludeDirs() const { return ProjectFile::fromNativeSeparators(mIncludeDirs); } /** * @brief Get list of defines. * @return list of defines. */ QStringList getDefines() const { return mDefines; } /** * @brief Get list of undefines. * @return list of undefines. */ QStringList getUndefines() const { return mUndefines; } /** * @brief Get list of paths to check. * @return list of paths. */ QStringList getCheckPaths() const { return ProjectFile::fromNativeSeparators(mPaths); } /** * @brief Get list of paths to exclude from the check. * @return list of paths. */ QStringList getExcludedPaths() const { return ProjectFile::fromNativeSeparators(mExcludedPaths); } /** * @brief Get list of paths to exclude from the check. * @return list of paths. */ QStringList getVsConfigurations() const { return mVsConfigurations; } /** * @brief Get list libraries. * @return list of libraries. */ QStringList getLibraries() const { return mLibraries; } /** * @brief Get platform. * @return Current platform. If it ends with .xml then it is a file. Otherwise it must match one of the return values from @sa cppcheck::Platform::platformString() ("win32A", "unix32", ..) */ QString getPlatform() const { return mPlatform; } /** * @brief Get "raw" suppressions. * @return list of suppressions. */ QList getSuppressions() const { return mSuppressions; } /** * @brief Get list addons. * @return list of addons. */ QStringList getAddons() const { return mAddons; } /** * @brief Get path to addon python script * @param filesDir Data files folder set by --data-dir * @param addon addon i.e. "misra" to lookup */ static QString getAddonFilePath(QString filesDir, const QString &addon); /** * @brief Get list of addons and tools. * @return list of addons and tools. */ QStringList getAddonsAndTools() const; bool getClangAnalyzer() const { return false; //mClangAnalyzer; } void setClangAnalyzer(bool c) { mClangAnalyzer = c; } bool getClangTidy() const { return mClangTidy; } void setClangTidy(bool c) { mClangTidy = c; } QStringList getTags() const { return mTags; } int getMaxCtuDepth() const { return mMaxCtuDepth; } void setMaxCtuDepth(int maxCtuDepth) { mMaxCtuDepth = maxCtuDepth; } int getMaxTemplateRecursion() const { return mMaxTemplateRecursion; } void setMaxTemplateRecursion(int maxTemplateRecursion) { mMaxTemplateRecursion = maxTemplateRecursion; } const std::map& getFunctionContracts() const { return mFunctionContracts; } const std::map& getVariableContracts() const { return mVariableContracts; } void setVariableContracts(QString var, const QString& min, const QString& max) { mVariableContracts[var] = Settings::VariableContracts{min.toStdString(), max.toStdString()}; } void deleteFunctionContract(const QString& function) { mFunctionContracts.erase(function.toStdString()); } void deleteVariableContract(const QString& var) { mVariableContracts.erase(var); } /** * @brief Get filename for the project file. * @return file name. */ QString getFilename() const { return mFilename; } /** * @brief Set project root path. * @param rootpath new project root path. */ void setRootPath(const QString &rootpath) { mRootPath = rootpath; } void setBuildDir(const QString &buildDir) { mBuildDir = buildDir; } void setImportProject(const QString &importProject) { mImportProject = importProject; } void setAnalyzeAllVsConfigs(bool b) { mAnalyzeAllVsConfigs = b; } /** * @brief Set list of includes. * @param includes List of defines. */ void setIncludes(const QStringList &includes); /** * @brief Set list of defines. * @param defines List of defines. */ void setDefines(const QStringList &defines); /** * @brief Set list of undefines. * @param undefines List of undefines. */ void setUndefines(const QStringList &undefines); /** * @brief Set list of paths to check. * @param paths List of paths. */ void setCheckPaths(const QStringList &paths); /** * @brief Set list of paths to exclude from the check. * @param paths List of paths. */ void setExcludedPaths(const QStringList &paths); /** * @brief Set list of libraries. * @param libraries List of libraries. */ void setLibraries(const QStringList &libraries); /** Set contract for a function */ void setFunctionContract(const QString& function, const QString& expects); /** * @brief Set platform. * @param platform platform. */ void setPlatform(const QString &platform); /** * @brief Set list of suppressions. * @param suppressions List of suppressions. */ void setSuppressions(const QList &suppressions); /** Add suppression */ void addSuppression(const Suppressions::Suppression &suppression); /** * @brief Set list of addons. * @param addons List of addons. */ void setAddons(const QStringList &addons); /** @brief Set list of Visual Studio configurations to be checked * @param vsConfigs List of configurations */ void setVSConfigurations(const QStringList &vsConfigs); /** * @brief Set tags. * @param tags tag list */ void setTags(const QStringList &tags) { mTags = tags; } /** Set tags for a warning */ void setWarningTags(std::size_t hash, const QString& tags); /** Get tags for a warning */ QString getWarningTags(std::size_t hash) const; /** * @brief Write project file (to disk). * @param filename Filename to use. */ bool write(const QString &filename = QString()); /** * @brief Set filename for the project file. * @param filename Filename to use. */ void setFilename(const QString &filename) { mFilename = filename; } /** Do not only check how interface is used. Also check that interface is safe. */ class SafeChecks : public Settings::SafeChecks { public: SafeChecks() : Settings::SafeChecks() {} void loadFromXml(QXmlStreamReader &xmlReader); void saveToXml(QXmlStreamWriter &xmlWriter) const; }; SafeChecks safeChecks; /** Check unknown function return values */ QStringList getCheckUnknownFunctionReturn() const { return mCheckUnknownFunctionReturn; } void setCheckUnknownFunctionReturn(const QStringList &s) { mCheckUnknownFunctionReturn = s; } /** Use Clang parser */ bool clangParser; /** Bug hunting */ bool bugHunting; protected: /** * @brief Read optional root path from XML. * @param reader XML stream reader. */ void readRootPath(QXmlStreamReader &reader); void readBuildDir(QXmlStreamReader &reader); /** * @brief Read importproject from XML. * @param reader XML stream reader. */ void readImportProject(QXmlStreamReader &reader); bool readBool(QXmlStreamReader &reader); int readInt(QXmlStreamReader &reader, int defaultValue); /** * @brief Read list of include directories from XML. * @param reader XML stream reader. */ void readIncludeDirs(QXmlStreamReader &reader); /** * @brief Read list of defines from XML. * @param reader XML stream reader. */ void readDefines(QXmlStreamReader &reader); /** * @brief Read list paths to check. * @param reader XML stream reader. */ void readCheckPaths(QXmlStreamReader &reader); /** * @brief Read lists of excluded paths. * @param reader XML stream reader. */ void readExcludes(QXmlStreamReader &reader); /** * @brief Read function contracts. * @param reader XML stream reader. */ void readFunctionContracts(QXmlStreamReader &reader); /** * @brief Read variable constraints. * @param reader XML stream reader. */ void readVariableContracts(QXmlStreamReader &reader); /** * @brief Read lists of Visual Studio configurations * @param reader XML stream reader. */ void readVsConfigurations(QXmlStreamReader &reader); /** * @brief Read platform text. * @param reader XML stream reader. */ void readPlatform(QXmlStreamReader &reader); /** * @brief Read suppressions. * @param reader XML stream reader. */ void readSuppressions(QXmlStreamReader &reader); /** * @brief Read tag warnings, what warnings are tagged with a specific tag * @param reader XML stream reader. */ void readTagWarnings(QXmlStreamReader &reader, const QString &tag); /** * @brief Read string list * @param stringlist destination string list * @param reader XML stream reader * @param elementname elementname for each string */ void readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[]); /** * @brief Write string list * @param xmlWriter xml writer * @param stringlist string list to write * @param startelementname name of start element * @param stringelementname name of each string element */ static void writeStringList(QXmlStreamWriter &xmlWriter, const QStringList &stringlist, const char startelementname[], const char stringelementname[]); private: void clear(); /** * @brief Convert paths */ static QStringList fromNativeSeparators(const QStringList &paths); /** * @brief Filename (+path) of the project file. */ QString mFilename; /** * @brief Root path (optional) for the project. * This is the project root path. If it is present then all relative paths in * the project file are relative to this path. Otherwise paths are relative * to project file's path. */ QString mRootPath; /** Cppcheck build dir */ QString mBuildDir; /** Visual studio project/solution , compile database */ QString mImportProject; /** * Should all visual studio configurations be analyzed? * If this is false then only the Debug configuration * for the set platform is analyzed. */ bool mAnalyzeAllVsConfigs; /** Check only a selected VS configuration */ QStringList mVsConfigurations; /** Check code in headers */ bool mCheckHeaders; /** Check code in unused templates */ bool mCheckUnusedTemplates; /** * @brief List of include directories used to search include files. */ QStringList mIncludeDirs; /** * @brief List of defines. */ QStringList mDefines; /** * @brief List of undefines. */ QStringList mUndefines; /** * @brief List of paths to check. */ QStringList mPaths; /** * @brief Paths excluded from the check. */ QStringList mExcludedPaths; /** * @brief List of libraries. */ QStringList mLibraries; std::map mFunctionContracts; std::map mVariableContracts; /** * @brief Platform */ QString mPlatform; /** * @brief List of suppressions. */ QList mSuppressions; /** * @brief List of addons. */ QStringList mAddons; /** @brief Execute clang analyzer? */ bool mClangAnalyzer; /** @brief Execute clang-tidy? */ bool mClangTidy; /** * @brief Tags */ QStringList mTags; /** * @brief Warning tags */ std::map mWarningTags; /** Max CTU depth */ int mMaxCtuDepth; /** Max template instantiation recursion */ int mMaxTemplateRecursion; QStringList mCheckUnknownFunctionReturn; static ProjectFile *mActiveProject; }; /// @} #endif // PROJECT_FILE_H cppcheck-2.7/gui/projectfile.txt000066400000000000000000000035211417746362400170140ustar00rootroot00000000000000Project files ------------- cppcheck GUI handles per-project settings in project files instead of global program settings. This allows customizing cppcheck for each project's needs. The project file is simple XML file easy to edit with your favourite editor program. The format is: where: - optional root element defines the root directory for the project. All relative paths are considered to be relative to this path. If the root element is missing or it contains "." as value then the project file's location is considered to be the root path. - paths element contains a list of checked paths. The paths can be relative or absolute paths. - indludedir element contains a list of additional include paths. These include paths are used when finding local include files ("#include "file.h") for source files. The paths can be relative or absolute paths. It is highly recommended that relative paths are used for paths inside the project root folder for better portability. - defines element contains a list of C/C++ preprocessor defines. - exclude element contains list of paths to exclude from the check. The path can be a directory (must end with path separator) or a file. For real life examples see the cppcheck.cppcheck-file in the Cppcheck sources root-directory or gui.cppcheck file in gui-directory. cppcheck-2.7/gui/projectfiledialog.cpp000066400000000000000000001003501417746362400201350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "projectfiledialog.h" #include "checkthread.h" #include "common.h" #include "importproject.h" #include "library.h" #include "newsuppressiondialog.h" #include "platforms.h" #include "projectfile.h" #include #include #include #include /** Return paths from QListWidget */ static QStringList getPaths(const QListWidget *list) { const int count = list->count(); QStringList paths; for (int i = 0; i < count; i++) { QListWidgetItem *item = list->item(i); paths << QDir::fromNativeSeparators(item->text()); } return paths; } /** Platforms shown in the platform combobox */ static const cppcheck::Platform::PlatformType builtinPlatforms[] = { cppcheck::Platform::Native, cppcheck::Platform::Win32A, cppcheck::Platform::Win32W, cppcheck::Platform::Win64, cppcheck::Platform::Unix32, cppcheck::Platform::Unix64 }; static const int numberOfBuiltinPlatforms = sizeof(builtinPlatforms) / sizeof(builtinPlatforms[0]); QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName) { if (!fileName.endsWith(".sln") && !fileName.endsWith(".vcxproj")) return QStringList(); QStringList ret; ImportProject importer; Settings projSettings; importer.import(fileName.toStdString(), &projSettings); for (const std::string &cfg : importer.getVSConfigs()) ret << QString::fromStdString(cfg); return ret; } ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent) : QDialog(parent) , mProjectFile(projectFile) { mUI.setupUi(this); mUI.mToolClangAnalyzer->hide(); const QFileInfo inf(projectFile->getFilename()); QString filename = inf.fileName(); QString title = tr("Project file: %1").arg(filename); setWindowTitle(title); loadSettings(); // Checkboxes for the libraries.. const QString applicationFilePath = QCoreApplication::applicationFilePath(); const QString appPath = QFileInfo(applicationFilePath).canonicalPath(); const QString datadir = getDataDir(); QStringList searchPaths; searchPaths << appPath << appPath + "/cfg" << inf.canonicalPath(); #ifdef FILESDIR if (FILESDIR[0]) searchPaths << FILESDIR << FILESDIR "/cfg"; #endif if (!datadir.isEmpty()) searchPaths << datadir << datadir + "/cfg"; QStringList libs; // Search the std.cfg first since other libraries could depend on it QString stdLibraryFilename; foreach (const QString sp, searchPaths) { QDir dir(sp); dir.setSorting(QDir::Name); dir.setNameFilters(QStringList("*.cfg")); dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); foreach (QFileInfo item, dir.entryInfoList()) { QString library = item.fileName(); if (library.compare("std.cfg", Qt::CaseInsensitive) != 0) continue; Library lib; const QString fullfilename = sp + "/" + library; const Library::Error err = lib.load(nullptr, fullfilename.toLatin1()); if (err.errorcode != Library::ErrorCode::OK) continue; // Working std.cfg found stdLibraryFilename = fullfilename; break; } if (!stdLibraryFilename.isEmpty()) break; } // Search other libraries foreach (const QString sp, searchPaths) { QDir dir(sp); dir.setSorting(QDir::Name); dir.setNameFilters(QStringList("*.cfg")); dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); foreach (QFileInfo item, dir.entryInfoList()) { QString library = item.fileName(); { Library lib; const QString fullfilename = sp + "/" + library; Library::Error err = lib.load(nullptr, fullfilename.toLatin1()); if (err.errorcode != Library::ErrorCode::OK) { // Some libraries depend on std.cfg so load it first and test again lib.load(nullptr, stdLibraryFilename.toLatin1()); err = lib.load(nullptr, fullfilename.toLatin1()); } if (err.errorcode != Library::ErrorCode::OK) continue; } library.chop(4); if (library.compare("std", Qt::CaseInsensitive) == 0) continue; if (libs.indexOf(library) == -1) libs << library; } } libs.sort(); mUI.mLibraries->clear(); for (const QString &lib : libs) { QListWidgetItem* item = new QListWidgetItem(lib, mUI.mLibraries); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag item->setCheckState(Qt::Unchecked); // AND initialize check state } // Platforms.. Platforms platforms; for (cppcheck::Platform::PlatformType builtinPlatform : builtinPlatforms) mUI.mComboBoxPlatform->addItem(platforms.get(builtinPlatform).mTitle); QStringList platformFiles; foreach (QString sp, searchPaths) { if (sp.endsWith("/cfg")) sp = sp.mid(0,sp.length()-3) + "platforms"; QDir dir(sp); dir.setSorting(QDir::Name); dir.setNameFilters(QStringList("*.xml")); dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); foreach (QFileInfo item, dir.entryInfoList()) { const QString platformFile = item.fileName(); cppcheck::Platform plat2; if (!plat2.loadPlatformFile(applicationFilePath.toStdString().c_str(), platformFile.toStdString())) continue; if (platformFiles.indexOf(platformFile) == -1) platformFiles << platformFile; } } platformFiles.sort(); mUI.mComboBoxPlatform->addItems(platformFiles); mUI.mEditTags->setValidator(new QRegExpValidator(QRegExp("[a-zA-Z0-9 ;]*"),this)); const QRegExp undefRegExp("\\s*([a-zA-Z_][a-zA-Z0-9_]*[; ]*)*"); mUI.mEditUndefines->setValidator(new QRegExpValidator(undefRegExp, this)); connect(mUI.mButtons, &QDialogButtonBox::accepted, this, &ProjectFileDialog::ok); connect(mUI.mBtnBrowseBuildDir, &QPushButton::clicked, this, &ProjectFileDialog::browseBuildDir); connect(mUI.mBtnClearImportProject, &QPushButton::clicked, this, &ProjectFileDialog::clearImportProject); connect(mUI.mBtnBrowseImportProject, &QPushButton::clicked, this, &ProjectFileDialog::browseImportProject); connect(mUI.mBtnAddCheckPath, SIGNAL(clicked()), this, SLOT(addCheckPath())); connect(mUI.mBtnEditCheckPath, &QPushButton::clicked, this, &ProjectFileDialog::editCheckPath); connect(mUI.mBtnRemoveCheckPath, &QPushButton::clicked, this, &ProjectFileDialog::removeCheckPath); connect(mUI.mBtnAddInclude, SIGNAL(clicked()), this, SLOT(addIncludeDir())); connect(mUI.mBtnEditInclude, &QPushButton::clicked, this, &ProjectFileDialog::editIncludeDir); connect(mUI.mBtnRemoveInclude, &QPushButton::clicked, this, &ProjectFileDialog::removeIncludeDir); connect(mUI.mBtnAddIgnorePath, SIGNAL(clicked()), this, SLOT(addExcludePath())); connect(mUI.mBtnAddIgnoreFile, SIGNAL(clicked()), this, SLOT(addExcludeFile())); connect(mUI.mBtnEditIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::editExcludePath); connect(mUI.mBtnRemoveIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::removeExcludePath); connect(mUI.mBtnIncludeUp, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathUp); connect(mUI.mBtnIncludeDown, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathDown); connect(mUI.mBtnAddSuppression, &QPushButton::clicked, this, &ProjectFileDialog::addSuppression); connect(mUI.mBtnRemoveSuppression, &QPushButton::clicked, this, &ProjectFileDialog::removeSuppression); connect(mUI.mListSuppressions, &QListWidget::doubleClicked, this, &ProjectFileDialog::editSuppression); connect(mUI.mBtnBrowseMisraFile, &QPushButton::clicked, this, &ProjectFileDialog::browseMisraFile); connect(mUI.mChkAllVsConfigs, &QCheckBox::clicked, this, &ProjectFileDialog::checkAllVSConfigs); connect(mUI.mBtnNormalAnalysis, &QCheckBox::toggled, mUI.mBtnSafeClasses, &QCheckBox::setEnabled); loadFromProjectFile(projectFile); } ProjectFileDialog::~ProjectFileDialog() { saveSettings(); } void ProjectFileDialog::loadSettings() { QSettings settings; resize(settings.value(SETTINGS_PROJECT_DIALOG_WIDTH, 470).toInt(), settings.value(SETTINGS_PROJECT_DIALOG_HEIGHT, 330).toInt()); } void ProjectFileDialog::saveSettings() const { QSettings settings; settings.setValue(SETTINGS_PROJECT_DIALOG_WIDTH, size().width()); settings.setValue(SETTINGS_PROJECT_DIALOG_HEIGHT, size().height()); } static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, const QString &dataDir, const QString &addon) { if (projectFile) cb->setChecked(projectFile->getAddons().contains(addon)); if (ProjectFile::getAddonFilePath(dataDir, addon).isEmpty()) { cb->setEnabled(false); cb->setText(cb->text() + QObject::tr(" (Not found)")); } } void ProjectFileDialog::checkAllVSConfigs() { if (mUI.mChkAllVsConfigs->isChecked()) { for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) { QListWidgetItem *item = mUI.mListVsConfigs->item(row); item->setCheckState(Qt::Checked); } } mUI.mListVsConfigs->setEnabled(!mUI.mChkAllVsConfigs->isChecked()); } void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) { setRootPath(projectFile->getRootPath()); setBuildDir(projectFile->getBuildDir()); setIncludepaths(projectFile->getIncludeDirs()); setDefines(projectFile->getDefines()); setUndefines(projectFile->getUndefines()); setCheckPaths(projectFile->getCheckPaths()); setImportProject(projectFile->getImportProject()); mUI.mChkAllVsConfigs->setChecked(projectFile->getAnalyzeAllVsConfigs()); setProjectConfigurations(getProjectConfigs(mUI.mEditImportProject->text())); for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) { QListWidgetItem *item = mUI.mListVsConfigs->item(row); if (projectFile->getAnalyzeAllVsConfigs() || projectFile->getVsConfigurations().contains(item->text())) item->setCheckState(Qt::Checked); else item->setCheckState(Qt::Unchecked); } mUI.mCheckHeaders->setChecked(projectFile->getCheckHeaders()); mUI.mCheckUnusedTemplates->setChecked(projectFile->getCheckUnusedTemplates()); mUI.mMaxCtuDepth->setValue(projectFile->getMaxCtuDepth()); mUI.mMaxTemplateRecursion->setValue(projectFile->getMaxTemplateRecursion()); if (projectFile->clangParser) mUI.mBtnClangParser->setChecked(true); else mUI.mBtnCppcheckParser->setChecked(true); mUI.mBtnSafeClasses->setChecked(projectFile->safeChecks.classes); mUI.mBtnBugHunting->setChecked(projectFile->bugHunting); setExcludedPaths(projectFile->getExcludedPaths()); setLibraries(projectFile->getLibraries()); const QString platform = projectFile->getPlatform(); if (platform.endsWith(".xml")) { int i; for (i = numberOfBuiltinPlatforms; i < mUI.mComboBoxPlatform->count(); ++i) { if (mUI.mComboBoxPlatform->itemText(i) == platform) break; } if (i < mUI.mComboBoxPlatform->count()) mUI.mComboBoxPlatform->setCurrentIndex(i); else { mUI.mComboBoxPlatform->addItem(platform); mUI.mComboBoxPlatform->setCurrentIndex(i); } } else { int i; for (i = 0; i < numberOfBuiltinPlatforms; ++i) { const cppcheck::Platform::PlatformType p = builtinPlatforms[i]; if (platform == cppcheck::Platform::platformString(p)) break; } if (i < numberOfBuiltinPlatforms) mUI.mComboBoxPlatform->setCurrentIndex(i); else mUI.mComboBoxPlatform->setCurrentIndex(-1); } mUI.mComboBoxPlatform->setCurrentText(projectFile->getPlatform()); setSuppressions(projectFile->getSuppressions()); // Human knowledge.. /* mUI.mListUnknownFunctionReturn->clear(); mUI.mListUnknownFunctionReturn->addItem("rand()"); for (int row = 0; row < mUI.mListUnknownFunctionReturn->count(); ++row) { QListWidgetItem *item = mUI.mListUnknownFunctionReturn->item(row); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag const bool unknownValues = projectFile->getCheckUnknownFunctionReturn().contains(item->text()); item->setCheckState(unknownValues ? Qt::Checked : Qt::Unchecked); // AND initialize check state } mUI.mCheckSafeClasses->setChecked(projectFile->getSafeChecks().classes); mUI.mCheckSafeExternalFunctions->setChecked(projectFile->getSafeChecks().externalFunctions); mUI.mCheckSafeInternalFunctions->setChecked(projectFile->getSafeChecks().internalFunctions); mUI.mCheckSafeExternalVariables->setChecked(projectFile->getSafeChecks().externalVariables); */ // Addons.. QSettings settings; const QString dataDir = getDataDir(); updateAddonCheckBox(mUI.mAddonThreadSafety, projectFile, dataDir, "threadsafety"); updateAddonCheckBox(mUI.mAddonY2038, projectFile, dataDir, "y2038"); updateAddonCheckBox(mUI.mAddonCert, projectFile, dataDir, "cert"); updateAddonCheckBox(mUI.mAddonMisra, projectFile, dataDir, "misra"); const QString &misraFile = settings.value(SETTINGS_MISRA_FILE, QString()).toString(); mUI.mEditMisraFile->setText(misraFile); if (!mUI.mAddonMisra->isEnabled()) { mUI.mEditMisraFile->setEnabled(false); mUI.mBtnBrowseMisraFile->setEnabled(false); } else if (misraFile.isEmpty()) { mUI.mAddonMisra->setEnabled(false); mUI.mAddonMisra->setText(mUI.mAddonMisra->text() + ' ' + tr("(no rule texts file)")); } mUI.mToolClangAnalyzer->setChecked(projectFile->getClangAnalyzer()); mUI.mToolClangTidy->setChecked(projectFile->getClangTidy()); if (CheckThread::clangTidyCmd().isEmpty()) { mUI.mToolClangTidy->setText(tr("Clang-tidy (not found)")); mUI.mToolClangTidy->setEnabled(false); } mUI.mEditTags->setText(projectFile->getTags().join(';')); updatePathsAndDefines(); } void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const { projectFile->setRootPath(getRootPath()); projectFile->setBuildDir(getBuildDir()); projectFile->setImportProject(getImportProject()); projectFile->setAnalyzeAllVsConfigs(mUI.mChkAllVsConfigs->isChecked()); projectFile->setVSConfigurations(getProjectConfigurations()); projectFile->setCheckHeaders(mUI.mCheckHeaders->isChecked()); projectFile->setCheckUnusedTemplates(mUI.mCheckUnusedTemplates->isChecked()); projectFile->setMaxCtuDepth(mUI.mMaxCtuDepth->value()); projectFile->setMaxTemplateRecursion(mUI.mMaxTemplateRecursion->value()); projectFile->setIncludes(getIncludePaths()); projectFile->setDefines(getDefines()); projectFile->setUndefines(getUndefines()); projectFile->setCheckPaths(getCheckPaths()); projectFile->setExcludedPaths(getExcludedPaths()); projectFile->setLibraries(getLibraries()); projectFile->clangParser = mUI.mBtnClangParser->isChecked(); projectFile->safeChecks.classes = mUI.mBtnSafeClasses->isChecked(); projectFile->bugHunting = mUI.mBtnBugHunting->isChecked(); if (mUI.mComboBoxPlatform->currentText().endsWith(".xml")) projectFile->setPlatform(mUI.mComboBoxPlatform->currentText()); else { int i = mUI.mComboBoxPlatform->currentIndex(); if (i < numberOfBuiltinPlatforms) projectFile->setPlatform(cppcheck::Platform::platformString(builtinPlatforms[i])); else projectFile->setPlatform(QString()); } projectFile->setSuppressions(getSuppressions()); // Human knowledge /* QStringList unknownReturnValues; for (int row = 0; row < mUI.mListUnknownFunctionReturn->count(); ++row) { QListWidgetItem *item = mUI.mListUnknownFunctionReturn->item(row); if (item->checkState() == Qt::Checked) unknownReturnValues << item->text(); } projectFile->setCheckUnknownFunctionReturn(unknownReturnValues); ProjectFile::SafeChecks safeChecks; safeChecks.classes = mUI.mCheckSafeClasses->isChecked(); safeChecks.externalFunctions = mUI.mCheckSafeExternalFunctions->isChecked(); safeChecks.internalFunctions = mUI.mCheckSafeInternalFunctions->isChecked(); safeChecks.externalVariables = mUI.mCheckSafeExternalVariables->isChecked(); projectFile->setSafeChecks(safeChecks); */ // Addons QStringList list; if (mUI.mAddonThreadSafety->isChecked()) list << "threadsafety"; if (mUI.mAddonY2038->isChecked()) list << "y2038"; if (mUI.mAddonCert->isChecked()) list << "cert"; if (mUI.mAddonMisra->isChecked()) list << "misra"; projectFile->setAddons(list); projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked()); projectFile->setClangTidy(mUI.mToolClangTidy->isChecked()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) projectFile->setTags(mUI.mEditTags->text().split(";", Qt::SkipEmptyParts)); #else projectFile->setTags(mUI.mEditTags->text().split(";", QString::SkipEmptyParts)); #endif } void ProjectFileDialog::ok() { saveToProjectFile(mProjectFile); mProjectFile->write(); accept(); } QString ProjectFileDialog::getExistingDirectory(const QString &caption, bool trailingSlash) { const QFileInfo inf(mProjectFile->getFilename()); const QString rootpath = inf.absolutePath(); QString selectedDir = QFileDialog::getExistingDirectory(this, caption, rootpath); if (selectedDir.isEmpty()) return QString(); // Check if the path is relative to project file's path and if so // make it a relative path instead of absolute path. const QDir dir(rootpath); const QString relpath(dir.relativeFilePath(selectedDir)); if (!relpath.startsWith("../..")) selectedDir = relpath; // Trailing slash.. if (trailingSlash && !selectedDir.endsWith('/')) selectedDir += '/'; return selectedDir; } void ProjectFileDialog::browseBuildDir() { const QString dir(getExistingDirectory(tr("Select Cppcheck build dir"), false)); if (!dir.isEmpty()) mUI.mEditBuildDir->setText(dir); } void ProjectFileDialog::updatePathsAndDefines() { const QString &fileName = mUI.mEditImportProject->text(); bool importProject = !fileName.isEmpty(); bool hasConfigs = fileName.endsWith(".sln") || fileName.endsWith(".vcxproj"); mUI.mBtnClearImportProject->setEnabled(importProject); mUI.mListCheckPaths->setEnabled(!importProject); mUI.mListIncludeDirs->setEnabled(!importProject); mUI.mBtnAddCheckPath->setEnabled(!importProject); mUI.mBtnEditCheckPath->setEnabled(!importProject); mUI.mBtnRemoveCheckPath->setEnabled(!importProject); mUI.mEditDefines->setEnabled(!importProject); mUI.mEditUndefines->setEnabled(!importProject); mUI.mBtnAddInclude->setEnabled(!importProject); mUI.mBtnEditInclude->setEnabled(!importProject); mUI.mBtnRemoveInclude->setEnabled(!importProject); mUI.mBtnIncludeUp->setEnabled(!importProject); mUI.mBtnIncludeDown->setEnabled(!importProject); mUI.mChkAllVsConfigs->setEnabled(hasConfigs); mUI.mListVsConfigs->setEnabled(hasConfigs && !mUI.mChkAllVsConfigs->isChecked()); if (!hasConfigs) mUI.mListVsConfigs->clear(); } void ProjectFileDialog::clearImportProject() { mUI.mEditImportProject->clear(); updatePathsAndDefines(); } void ProjectFileDialog::browseImportProject() { const QFileInfo inf(mProjectFile->getFilename()); const QDir &dir = inf.absoluteDir(); QMap filters; filters[tr("Visual Studio")] = "*.sln *.vcxproj"; filters[tr("Compile database")] = "compile_commands.json"; filters[tr("Borland C++ Builder 6")] = "*.bpr"; QString fileName = QFileDialog::getOpenFileName(this, tr("Import Project"), dir.canonicalPath(), toFilterString(filters)); if (!fileName.isEmpty()) { mUI.mEditImportProject->setText(dir.relativeFilePath(fileName)); updatePathsAndDefines(); setProjectConfigurations(getProjectConfigs(fileName)); for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) { QListWidgetItem *item = mUI.mListVsConfigs->item(row); item->setCheckState(Qt::Checked); } } } QStringList ProjectFileDialog::getProjectConfigurations() const { QStringList configs; for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) { QListWidgetItem *item = mUI.mListVsConfigs->item(row); if (item->checkState() == Qt::Checked) configs << item->text(); } return configs; } void ProjectFileDialog::setProjectConfigurations(const QStringList &configs) { mUI.mListVsConfigs->clear(); mUI.mListVsConfigs->setEnabled(!configs.isEmpty() && !mUI.mChkAllVsConfigs->isChecked()); foreach (const QString &cfg, configs) { QListWidgetItem* item = new QListWidgetItem(cfg, mUI.mListVsConfigs); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag item->setCheckState(Qt::Unchecked); } } QString ProjectFileDialog::getImportProject() const { return mUI.mEditImportProject->text(); } void ProjectFileDialog::addIncludeDir(const QString &dir) { if (dir.isNull() || dir.isEmpty()) return; const QString newdir = QDir::toNativeSeparators(dir); QListWidgetItem *item = new QListWidgetItem(newdir); item->setFlags(item->flags() | Qt::ItemIsEditable); mUI.mListIncludeDirs->addItem(item); } void ProjectFileDialog::addCheckPath(const QString &path) { if (path.isNull() || path.isEmpty()) return; const QString newpath = QDir::toNativeSeparators(path); QListWidgetItem *item = new QListWidgetItem(newpath); item->setFlags(item->flags() | Qt::ItemIsEditable); mUI.mListCheckPaths->addItem(item); } void ProjectFileDialog::addExcludePath(const QString &path) { if (path.isNull() || path.isEmpty()) return; const QString newpath = QDir::toNativeSeparators(path); QListWidgetItem *item = new QListWidgetItem(newpath); item->setFlags(item->flags() | Qt::ItemIsEditable); mUI.mListExcludedPaths->addItem(item); } QString ProjectFileDialog::getRootPath() const { QString root = mUI.mEditProjectRoot->text(); root = root.trimmed(); root = QDir::fromNativeSeparators(root); return root; } QString ProjectFileDialog::getBuildDir() const { return mUI.mEditBuildDir->text(); } QStringList ProjectFileDialog::getIncludePaths() const { return getPaths(mUI.mListIncludeDirs); } QStringList ProjectFileDialog::getDefines() const { #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) return mUI.mEditDefines->text().trimmed().split(QRegExp("\\s*;\\s*"), Qt::SkipEmptyParts); #else return mUI.mEditDefines->text().trimmed().split(QRegExp("\\s*;\\s*"), QString::SkipEmptyParts); #endif } QStringList ProjectFileDialog::getUndefines() const { const QString undefine = mUI.mEditUndefines->text().trimmed(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList undefines = undefine.split(QRegExp("\\s*;\\s*"), Qt::SkipEmptyParts); #else QStringList undefines = undefine.split(QRegExp("\\s*;\\s*"), QString::SkipEmptyParts); #endif undefines.removeDuplicates(); return undefines; } QStringList ProjectFileDialog::getCheckPaths() const { return getPaths(mUI.mListCheckPaths); } QStringList ProjectFileDialog::getExcludedPaths() const { return getPaths(mUI.mListExcludedPaths); } QStringList ProjectFileDialog::getLibraries() const { QStringList libraries; for (int row = 0; row < mUI.mLibraries->count(); ++row) { QListWidgetItem *item = mUI.mLibraries->item(row); if (item->checkState() == Qt::Checked) libraries << item->text(); } return libraries; } void ProjectFileDialog::setRootPath(const QString &root) { mUI.mEditProjectRoot->setText(QDir::toNativeSeparators(root)); } void ProjectFileDialog::setBuildDir(const QString &buildDir) { mUI.mEditBuildDir->setText(buildDir); } void ProjectFileDialog::setImportProject(const QString &importProject) { mUI.mEditImportProject->setText(importProject); } void ProjectFileDialog::setIncludepaths(const QStringList &includes) { foreach (QString dir, includes) { addIncludeDir(dir); } } void ProjectFileDialog::setDefines(const QStringList &defines) { mUI.mEditDefines->setText(defines.join(";")); } void ProjectFileDialog::setUndefines(const QStringList &undefines) { mUI.mEditUndefines->setText(undefines.join(";")); } void ProjectFileDialog::setCheckPaths(const QStringList &paths) { foreach (QString path, paths) { addCheckPath(path); } } void ProjectFileDialog::setExcludedPaths(const QStringList &paths) { foreach (QString path, paths) { addExcludePath(path); } } void ProjectFileDialog::setLibraries(const QStringList &libraries) { for (int row = 0; row < mUI.mLibraries->count(); ++row) { QListWidgetItem *item = mUI.mLibraries->item(row); item->setCheckState(libraries.contains(item->text()) ? Qt::Checked : Qt::Unchecked); } } void ProjectFileDialog::addSingleSuppression(const Suppressions::Suppression &suppression) { QString suppression_name; static char sep = QDir::separator().toLatin1(); bool found_relative = false; // Replace relative file path in the suppression with the absolute one if ((suppression.fileName.find("*") == std::string::npos) && (suppression.fileName.find(sep) == std::string::npos)) { QFileInfo inf(mProjectFile->getFilename()); QString rootpath = inf.absolutePath(); if (QFile::exists(QString{"%1%2%3"}.arg(rootpath, QDir::separator(), QString::fromStdString(suppression.fileName)))) { Suppressions::Suppression sup = suppression; sup.fileName = rootpath.toLatin1().constData(); sup.fileName += sep; sup.fileName += suppression.fileName; mSuppressions += sup; suppression_name = QString::fromStdString(sup.getText()); found_relative = true; } } if (!found_relative) { mSuppressions += suppression; suppression_name = QString::fromStdString(suppression.getText()); } mUI.mListSuppressions->addItem(suppression_name); } void ProjectFileDialog::setSuppressions(const QList &suppressions) { mUI.mListSuppressions->clear(); QList new_suppressions = suppressions; mSuppressions.clear(); foreach (const Suppressions::Suppression &suppression, new_suppressions) { addSingleSuppression(suppression); } mUI.mListSuppressions->sortItems(); } void ProjectFileDialog::addCheckPath() { QString dir = getExistingDirectory(tr("Select a directory to check"), false); if (!dir.isEmpty()) addCheckPath(dir); } void ProjectFileDialog::editCheckPath() { QListWidgetItem *item = mUI.mListCheckPaths->currentItem(); mUI.mListCheckPaths->editItem(item); } void ProjectFileDialog::removeCheckPath() { const int row = mUI.mListCheckPaths->currentRow(); QListWidgetItem *item = mUI.mListCheckPaths->takeItem(row); delete item; } void ProjectFileDialog::addIncludeDir() { const QString dir = getExistingDirectory(tr("Select include directory"), true); if (!dir.isEmpty()) addIncludeDir(dir); } void ProjectFileDialog::removeIncludeDir() { const int row = mUI.mListIncludeDirs->currentRow(); QListWidgetItem *item = mUI.mListIncludeDirs->takeItem(row); delete item; } void ProjectFileDialog::editIncludeDir() { QListWidgetItem *item = mUI.mListIncludeDirs->currentItem(); mUI.mListIncludeDirs->editItem(item); } void ProjectFileDialog::addExcludePath() { addExcludePath(getExistingDirectory(tr("Select directory to ignore"), true)); } void ProjectFileDialog::addExcludeFile() { const QFileInfo inf(mProjectFile->getFilename()); const QDir &dir = inf.absoluteDir(); QMap filters; filters[tr("Source files")] = "*.c *.cpp"; filters[tr("All files")] = "*.*"; addExcludePath(QFileDialog::getOpenFileName(this, tr("Exclude file"), dir.canonicalPath(), toFilterString(filters))); } void ProjectFileDialog::editExcludePath() { QListWidgetItem *item = mUI.mListExcludedPaths->currentItem(); mUI.mListExcludedPaths->editItem(item); } void ProjectFileDialog::removeExcludePath() { const int row = mUI.mListExcludedPaths->currentRow(); QListWidgetItem *item = mUI.mListExcludedPaths->takeItem(row); delete item; } void ProjectFileDialog::moveIncludePathUp() { int row = mUI.mListIncludeDirs->currentRow(); QListWidgetItem *item = mUI.mListIncludeDirs->takeItem(row); row = row > 0 ? row - 1 : 0; mUI.mListIncludeDirs->insertItem(row, item); mUI.mListIncludeDirs->setCurrentItem(item); } void ProjectFileDialog::moveIncludePathDown() { int row = mUI.mListIncludeDirs->currentRow(); QListWidgetItem *item = mUI.mListIncludeDirs->takeItem(row); const int count = mUI.mListIncludeDirs->count(); row = row < count ? row + 1 : count; mUI.mListIncludeDirs->insertItem(row, item); mUI.mListIncludeDirs->setCurrentItem(item); } void ProjectFileDialog::addSuppression() { NewSuppressionDialog dlg; if (dlg.exec() == QDialog::Accepted) { addSingleSuppression(dlg.getSuppression()); } } void ProjectFileDialog::removeSuppression() { const int row = mUI.mListSuppressions->currentRow(); QListWidgetItem *item = mUI.mListSuppressions->takeItem(row); if (!item) return; int suppressionIndex = getSuppressionIndex(item->text()); if (suppressionIndex >= 0) mSuppressions.removeAt(suppressionIndex); delete item; } void ProjectFileDialog::editSuppression(const QModelIndex &) { const int row = mUI.mListSuppressions->currentRow(); QListWidgetItem *item = mUI.mListSuppressions->item(row); int suppressionIndex = getSuppressionIndex(item->text()); if (suppressionIndex >= 0) { // TODO what if suppression is not found? NewSuppressionDialog dlg; dlg.setSuppression(mSuppressions[suppressionIndex]); if (dlg.exec() == QDialog::Accepted) { mSuppressions[suppressionIndex] = dlg.getSuppression(); setSuppressions(mSuppressions); } } } int ProjectFileDialog::getSuppressionIndex(const QString &shortText) const { const std::string s = shortText.toStdString(); for (int i = 0; i < mSuppressions.size(); ++i) { if (mSuppressions[i].getText() == s) return i; } return -1; } void ProjectFileDialog::browseMisraFile() { const QString fileName = QFileDialog::getOpenFileName(this, tr("Select MISRA rule texts file"), QDir::homePath(), tr("MISRA rule texts file (%1)").arg("*.txt")); if (!fileName.isEmpty()) { QSettings settings; mUI.mEditMisraFile->setText(fileName); settings.setValue(SETTINGS_MISRA_FILE, fileName); mUI.mAddonMisra->setText("MISRA C 2012"); mUI.mAddonMisra->setEnabled(true); updateAddonCheckBox(mUI.mAddonMisra, nullptr, getDataDir(), "misra"); } } cppcheck-2.7/gui/projectfiledialog.h000066400000000000000000000177551417746362400176220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PROJECTFILE_DIALOG_H #define PROJECTFILE_DIALOG_H #include "ui_projectfiledialog.h" #include "suppressions.h" #include #include #include class QWidget; class QCheckBox; /// @addtogroup GUI /// @{ class ProjectFile; /** * @brief A dialog for editing project file data. */ class ProjectFileDialog : public QDialog { Q_OBJECT public: explicit ProjectFileDialog(ProjectFile *projectFile, QWidget *parent = nullptr); virtual ~ProjectFileDialog(); private: void loadFromProjectFile(const ProjectFile *projectFile); void saveToProjectFile(ProjectFile *projectFile) const; /** Enable and disable widgets in the 'Paths and Defines' tab */ void updatePathsAndDefines(); /** * @brief Return project root path from the dialog control. * @return Project root path. */ QString getRootPath() const; QStringList getProjectConfigurations() const; void setProjectConfigurations(const QStringList &configs); QString getImportProject() const; /** Get Cppcheck build dir */ QString getBuildDir() const; /** * @brief Return include paths from the dialog control. * @return List of include paths. */ QStringList getIncludePaths() const; /** * @brief Return define names from the dialog control. * @return List of define names. */ QStringList getDefines() const; /** * @brief Return undefine names from the dialog control. * @return List of undefine names. */ QStringList getUndefines() const; /** * @brief Return check paths from the dialog control. * @return List of check paths. */ QStringList getCheckPaths() const; /** * @brief Return excluded paths from the dialog control. * @return List of excluded paths. */ QStringList getExcludedPaths() const; /** * @brief Return selected libraries from the dialog control. * @return List of libraries. */ QStringList getLibraries() const; /** * @brief Return suppressions from the dialog control. * @return List of suppressions. */ QList getSuppressions() const { return mSuppressions; } /** * @brief Set project root path to dialog control. * @param root Project root path to set to dialog control. */ void setRootPath(const QString &root); /** Set build dir */ void setBuildDir(const QString &buildDir); void setImportProject(const QString &importProject); /** * @brief Set include paths to dialog control. * @param includes List of include paths to set to dialog control. */ void setIncludepaths(const QStringList &includes); /** * @brief Set define names to dialog control. * @param defines List of define names to set to dialog control. */ void setDefines(const QStringList &defines); /** * @brief Set undefine names to dialog control. * @param undefines List of undefine names to set to dialog control. */ void setUndefines(const QStringList &undefines); /** * @brief Set check paths to dialog control. * @param paths List of path names to set to dialog control. */ void setCheckPaths(const QStringList &paths); /** * @brief Set excluded paths to dialog control. * @param paths List of path names to set to dialog control. */ void setExcludedPaths(const QStringList &paths); /** * @brief Set libraries to dialog control. * @param libraries List of libraries to set to dialog control. */ void setLibraries(const QStringList &libraries); /** * @brief Add a single suppression to dialog control. * @param suppression A suppressions to add to dialog control. */ void addSingleSuppression(const Suppressions::Suppression &suppression); /** * @brief Set suppressions to dialog control. * @param suppressions List of suppressions to set to dialog control. */ void setSuppressions(const QList &suppressions); protected slots: /** ok button pressed, save changes and accept */ void ok(); /** * @brief Browse for build dir. */ void browseBuildDir(); /** * @brief Clear 'import project'. */ void clearImportProject(); /** * @brief Browse for solution / project / compile database. */ void browseImportProject(); /** * @brief Add new path to check. */ void addCheckPath(); /** * @brief Edit path in the list. */ void editCheckPath(); /** * @brief Remove path from the list. */ void removeCheckPath(); /** * @brief Browse for include directory. * Allow user to add new include directory to the list. */ void addIncludeDir(); /** * @brief Remove include directory from the list. */ void removeIncludeDir(); /** * @brief Edit include directory in the list. */ void editIncludeDir(); /** * @brief Add new path to exclude list. */ void addExcludePath(); /** * @brief Add new file to exclude list. */ void addExcludeFile(); /** * @brief Edit excluded path in the list. */ void editExcludePath(); /** * @brief Remove excluded path from the list. */ void removeExcludePath(); /** * @brief Move include path up in the list. */ void moveIncludePathUp(); /** * @brief Move include path down in the list. */ void moveIncludePathDown(); /** * @brief Add suppression to the list */ void addSuppression(); /** * @brief Remove selected suppression from the list */ void removeSuppression(); /** * @brief Edit suppression (double clicking on suppression) */ void editSuppression(const QModelIndex &index); /** * @brief Browse for misra file */ void browseMisraFile(); /** * @brief Check for all VS configurations */ void checkAllVSConfigs(); protected: /** * @brief Save dialog settings. */ void loadSettings(); /** * @brief Load dialog settings. */ void saveSettings() const; /** * @brief Add new indlude directory. * @param dir Directory to add. */ void addIncludeDir(const QString &dir); /** * @brief Add new path to check. * @param path Path to add. */ void addCheckPath(const QString &path); /** * @brief Add new path to ignore list. * @param path Path to add. */ void addExcludePath(const QString &path); /** * @brief Get mSuppressions index that match the * given short text * @param shortText text as generated by Suppression::getText * @return index of matching suppression, -1 if not found */ int getSuppressionIndex(const QString &shortText) const; private: QStringList getProjectConfigs(const QString &fileName); Ui::ProjectFile mUI; /** * @brief Projectfile path. */ ProjectFile *mProjectFile; /** @brief Library checkboxes */ QList mLibraryCheckboxes; QString getExistingDirectory(const QString &caption, bool trailingSlash); QList mSuppressions; }; /// @} #endif // PROJECTFILE_DIALOG_H cppcheck-2.7/gui/projectfiledialog.ui000066400000000000000000000731471417746362400200050ustar00rootroot00000000000000 ProjectFile 0 0 940 617 Project File 0 Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) true false :/images/edit-clear.png Browse... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Analyze all Visual Studio configurations Qt::Vertical 20 40 Qt::Horizontal 40 20 Selected VS Configurations Qt::Vertical 20 40 Paths: Qt::Vertical 20 40 16777215 140 Add... Edit Remove Qt::Vertical 20 40 Defines: mEditDefines Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Undefines: mEditUndefines Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Qt::Vertical 20 40 QAbstractItemView::SelectRows Add... Edit Remove Qt::Vertical 20 40 Up Down Qt::Vertical 20 0 Types and Functions Platform Libraries Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. true Analysis Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) This is a workfolder that Cppcheck will use for various purposes. Browse... Parser Cppcheck (built in) true Clang (experimental) Analysis Normal analysis -- Avoid false positives. true Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs. If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. Check that each class has a safe public interface 0 0 Limit analysis Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) true Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) true Max CTU depth 10 Qt::Horizontal 40 20 Max recursion in template instantiation 1000 100 Qt::Horizontal 40 20 Qt::Vertical 20 73 Warning options Root path: Filepaths in warnings will be relative to this path Warning tags (separated by semicolon) If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. Exclude source files Exclude folder... Exclude file... Edit Remove Qt::Vertical 20 40 Suppressions Add Remove Qt::Vertical 20 40 Qt::Vertical 20 96 Addons Addons Note: Addons require <a href="https://www.python.org/">Python</a> being installed. true Y2038 Thread safety Coding standards CERT MISRA C 2012 MISRA rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... External tools Clang-tidy Clang analyzer Qt::Vertical 20 368 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok mButtons mButtons accepted() ProjectFile accept() 270 352 157 158 mButtons rejected() ProjectFile reject() 338 352 286 158 cppcheck-2.7/gui/readme.txt000066400000000000000000000055241417746362400157500ustar00rootroot00000000000000Cppcheck GUI ============ This is a GUI for cppcheck. It allows selecting folder or set of files to check with cppcheck and shows list of found errors. Running ------- You need Qt5 libraries installed in your system. Packages/files to install depends on your operating system: - Windows: download Qt from http://www.qt.io/download/ - Linux: install Qt using your package manager, look for packages having Qt in their name, e.g. for Ubuntu install libqt5core5a, libqt5gui5, libqt5widgets5 and libqt5printsupport5. Compiling --------- Windows: - The easy ways are: -- download Qt SDK from http://www.qt.io/download/ and use QtCreator to build the GUI. -- Download precompiled libraries for your platform and use your preferred IDE/environment to build GUI. Be careful to download the correct version of library for your compiler! - The harder way is to download Qt sources and build Qt. Compiling Qt alone may take over 4 hours! Linux: - Install Qt development packages (make sure qmake -tool gets installed!). The names depend on distribution, but e.g. for Ubuntu the needed packages are: * qt5-default After you have needed libraries and tools installed, open command prompt/console, go to gui directory and run command: - qmake (in Linux and in Windows if build with MinGW/gcc or nmake) - qmake -tp vc (to generate Visual Studio project file) - qmake -tp vc LINKCORE=yes (to generate Visual Studio project file, linking dynamically to core. Recommended.) On Windows, you have to either call qtvars.bat in Qt folder or use the Qt command line prompt shortcut added in the start menu by Qt installation. These commands generate makefiles to actually build the software. After that the actual building is done in IDE or command line as usual. Note that you don't need to run qmake again unless you add/remove files from the project. The Visual Studio solution does not contain a configuration for x64 platform, but it can be added easily. Tests ----- There are tests for the GUI in gui/test -directory. There is test.pro -projectfile for building all the tests. Each test is in own subdirectory and builds own binary. Test is run by simple running that binary. The binary also has several options to select tests etc. You can get the help by running "binaryname -help" -command. Translations ------------ The GUI is translated to several languages. Qt comes with two tools to update and compile the translations. lupdate updates translations files from the code and lrelease compiles translation files use with the executable. To update translations: - run lupdate gui.pro to update the translation files to match the code. This command updates all the .ts files. Which can be then edited to translate the application. To compile translations: - run lrelease gui.pro to compile .ts files to .qm files which are used by the executable. cppcheck-2.7/gui/report.cpp000066400000000000000000000026371417746362400157730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "report.h" Report::Report(const QString &filename) : QObject(), mFilename(filename) {} Report::~Report() { close(); } bool Report::create() { bool succeed = false; if (!mFile.isOpen()) { mFile.setFileName(mFilename); succeed = mFile.open(QIODevice::WriteOnly | QIODevice::Text); } return succeed; } bool Report::open() { bool succeed = false; if (!mFile.isOpen()) { mFile.setFileName(mFilename); succeed = mFile.open(QIODevice::ReadOnly | QIODevice::Text); } return succeed; } void Report::close() { if (mFile.isOpen()) mFile.close(); } QFile* Report::getFile() { return &mFile; } cppcheck-2.7/gui/report.h000066400000000000000000000041161417746362400154320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef REPORT_H #define REPORT_H #include #include #include class ErrorItem; /// @addtogroup GUI /// @{ /** * @brief A base class for reports. */ class Report : public QObject { public: enum Type { TXT, XMLV2, CSV, }; explicit Report(const QString &filename); virtual ~Report(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create(); /** * @brief Open the existing report (file). * @return true if succeeded, false if file could not be created. */ virtual bool open(); /** * @brief Close the report (file). */ void close(); /** * @brief Write report header. */ virtual void writeHeader() = 0; /** * @brief Write report footer. */ virtual void writeFooter() = 0; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) = 0; protected: /** * @brief Get the file object where the report is written to. */ QFile* getFile(); private: /** * @brief Filename of the report. */ QString mFilename; /** * @brief Fileobject for the report file. */ QFile mFile; }; /// @} #endif // REPORT_H cppcheck-2.7/gui/resultstree.cpp000066400000000000000000001353321417746362400170400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "resultstree.h" #include "application.h" #include "applicationlist.h" #include "common.h" #include "erroritem.h" #include "path.h" #include "projectfile.h" #include "report.h" #include "showtypes.h" #include "threadhandler.h" #include "xmlreportv2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char COLUMN[] = "column"; static const char CWE[] = "cwe"; static const char ERRORID[] = "id"; static const char FILENAME[] = "file"; static const char FILE0[] = "file0"; static const char FUNCTION[] = "function"; static const char HASH[] = "hash"; static const char HIDE[] = "hide"; static const char INCOMPLETE[] = "incomplete"; static const char INCONCLUSIVE[] = "inconclusive"; static const char LINE[] = "line"; static const char MESSAGE[] = "message"; static const char SEVERITY[] = "severity"; static const char SINCEDATE[] = "sinceDate"; static const char SYMBOLNAMES[] = "symbolNames"; static const char SUMMARY[] = "summary"; static const char TAGS[] = "tags"; // These must match column headers given in ResultsTree::translate() static const int COLUMN_SINCE_DATE = 6; static const int COLUMN_TAGS = 7; static QString getFunction(QStandardItem *item) { return item->data().toMap().value("function").toString(); } ResultsTree::ResultsTree(QWidget * parent) : QTreeView(parent), mSettings(nullptr), mApplications(nullptr), mContextItem(nullptr), mShowFullPath(false), mSaveFullPath(false), mSaveAllErrors(true), mShowErrorId(false), mVisibleErrors(false), mSelectionModel(nullptr), mThread(nullptr), mShowCppcheck(true), mShowClang(true) { setModel(&mModel); translate(); // Adds columns to grid setExpandsOnDoubleClick(false); setSortingEnabled(true); connect(this, &ResultsTree::doubleClicked, this, &ResultsTree::quickStartApplication); } ResultsTree::~ResultsTree() {} void ResultsTree::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { quickStartApplication(this->currentIndex()); } QTreeView::keyPressEvent(event); } void ResultsTree::initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler) { mSettings = settings; mApplications = list; mThread = checkThreadHandler; loadSettings(); } QStandardItem *ResultsTree::createNormalItem(const QString &name) { QStandardItem *item = new QStandardItem(name); item->setData(name, Qt::ToolTipRole); item->setEditable(false); return item; } QStandardItem *ResultsTree::createCheckboxItem(bool checked) { QStandardItem *item = new QStandardItem; item->setCheckable(true); item->setCheckState(checked ? Qt::Checked : Qt::Unchecked); item->setEnabled(false); return item; } QStandardItem *ResultsTree::createLineNumberItem(const QString &linenumber) { QStandardItem *item = new QStandardItem(); item->setData(QVariant(linenumber.toInt()), Qt::DisplayRole); item->setToolTip(linenumber); item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); item->setEditable(false); return item; } bool ResultsTree::addErrorItem(const ErrorItem &item) { if (item.errorPath.isEmpty()) { return false; } const QErrorPathItem &loc = item.errorId.startsWith("clang") ? item.errorPath.front() : item.errorPath.back(); QString realfile = stripPath(loc.file, false); if (realfile.isEmpty()) { realfile = tr("Undefined file"); } bool hide = false; // Ids that are temporarily hidden.. if (mHiddenMessageId.contains(item.errorId)) hide = true; //If specified, filter on summary, message, filename, and id if (!hide && !mFilter.isEmpty()) { if (!item.summary.contains(mFilter, Qt::CaseInsensitive) && !item.message.contains(mFilter, Qt::CaseInsensitive) && !item.errorPath.back().file.contains(mFilter, Qt::CaseInsensitive) && !item.errorId.contains(mFilter, Qt::CaseInsensitive)) { hide = true; } } //if there is at least one error that is not hidden, we have a visible error if (!hide) { mVisibleErrors = true; } ErrorLine line; line.file = realfile; line.line = loc.line; line.errorId = item.errorId; line.incomplete = item.incomplete; line.cwe = item.cwe; line.hash = item.hash; line.inconclusive = item.inconclusive; line.summary = item.summary; line.message = item.message; line.severity = item.severity; line.sinceDate = item.sinceDate; if (const ProjectFile *activeProject = ProjectFile::getActiveProject()) { line.tags = activeProject->getWarningTags(item.hash); } //Create the base item for the error and ensure it has a proper //file item as a parent QStandardItem* fileItem = ensureFileItem(loc.file, item.file0, hide); QStandardItem* stditem = addBacktraceFiles(fileItem, line, hide, severityToIcon(line.severity), false); if (!stditem) return false; //Add user data to that item QMap data; data[SEVERITY] = ShowTypes::SeverityToShowType(item.severity); data[SUMMARY] = item.summary; data[MESSAGE] = item.message; data[FILENAME] = loc.file; data[LINE] = loc.line; data[COLUMN] = loc.column; data[ERRORID] = item.errorId; data[INCOMPLETE] = item.incomplete; data[CWE] = item.cwe; data[HASH] = item.hash; data[INCONCLUSIVE] = item.inconclusive; data[FILE0] = stripPath(item.file0, true); data[FUNCTION] = item.function; data[SINCEDATE] = item.sinceDate; data[SYMBOLNAMES] = item.symbolNames; data[TAGS] = line.tags; data[HIDE] = hide; stditem->setData(QVariant(data)); //Add backtrace files as children if (item.errorPath.size() > 1) { for (int i = 0; i < item.errorPath.size(); i++) { const QErrorPathItem &e = item.errorPath[i]; line.file = e.file; line.line = e.line; line.message = line.summary = e.info; QStandardItem *child_item; child_item = addBacktraceFiles(stditem, line, hide, ":images/go-down.png", true); if (!child_item) continue; // Add user data to that item QMap child_data; child_data[SEVERITY] = ShowTypes::SeverityToShowType(line.severity); child_data[SUMMARY] = line.summary; child_data[MESSAGE] = line.message; child_data[FILENAME] = e.file; child_data[LINE] = e.line; child_data[COLUMN] = e.column; child_data[ERRORID] = line.errorId; child_data[INCOMPLETE] = line.incomplete; child_data[CWE] = line.cwe; child_data[HASH] = line.hash; child_data[INCONCLUSIVE] = line.inconclusive; child_data[SYMBOLNAMES] = item.symbolNames; child_item->setData(QVariant(child_data)); } } // Partially refresh the tree: Unhide file item if necessary if (!hide) { setRowHidden(fileItem->row(), QModelIndex(), !mShowSeverities.isShown(item.severity)); } return true; } QStandardItem *ResultsTree::addBacktraceFiles(QStandardItem *parent, const ErrorLine &item, const bool hide, const QString &icon, bool childOfMessage) { if (!parent) { return nullptr; } QList list; // Ensure shown path is with native separators list << createNormalItem(QDir::toNativeSeparators(item.file)) << createNormalItem(childOfMessage ? tr("note") : severityToTranslatedString(item.severity)) << createLineNumberItem(QString::number(item.line)) << createNormalItem(childOfMessage ? QString() : item.errorId) << (childOfMessage ? createNormalItem(QString()) : createCheckboxItem(item.inconclusive)) << createNormalItem(item.summary) << createNormalItem(item.sinceDate) << createNormalItem(item.tags); //TODO message has parameter names so we'll need changes to the core //cppcheck so we can get proper translations // Check for duplicate rows and don't add them if found for (int i = 0; i < parent->rowCount(); i++) { // The first column is the file name and is always the same // the third column is the line number so check it first if (parent->child(i, 2)->text() == list[2]->text()) { // the second column is the severity so check it next if (parent->child(i, 1)->text() == list[1]->text()) { // the sixth column is the summary so check it last if (parent->child(i, 5)->text() == list[5]->text()) { // this row matches so don't add it return nullptr; } } } } parent->appendRow(list); setRowHidden(parent->rowCount() - 1, parent->index(), hide); if (!icon.isEmpty()) { list[0]->setIcon(QIcon(icon)); } //TODO Does this leak memory? Should items from list be deleted? return list[0]; } QString ResultsTree::severityToTranslatedString(Severity::SeverityType severity) { switch (severity) { case Severity::style: return tr("style"); case Severity::error: return tr("error"); case Severity::warning: return tr("warning"); case Severity::performance: return tr("performance"); case Severity::portability: return tr("portability"); case Severity::information: return tr("information"); case Severity::debug: return tr("debug"); case Severity::none: default: return QString(); } } QStandardItem *ResultsTree::findFileItem(const QString &name) const { // The first column contains the file name. In Windows we can get filenames // "header.h" and "Header.h" and must compare them as identical. for (int i = 0; i < mModel.rowCount(); i++) { #ifdef _WIN32 if (QString::compare(mModel.item(i, 0)->text(), name, Qt::CaseInsensitive) == 0) #else if (mModel.item(i, 0)->text() == name) #endif return mModel.item(i, 0); } return nullptr; } void ResultsTree::clear() { mModel.removeRows(0, mModel.rowCount()); } void ResultsTree::clear(const QString &filename) { const QString stripped = stripPath(filename, false); for (int i = 0; i < mModel.rowCount(); ++i) { const QStandardItem *fileItem = mModel.item(i, 0); if (!fileItem) continue; QVariantMap data = fileItem->data().toMap(); if (stripped == data[FILENAME].toString() || filename == data[FILE0].toString()) { mModel.removeRow(i); break; } } } void ResultsTree::clearRecheckFile(const QString &filename) { for (int i = 0; i < mModel.rowCount(); ++i) { const QStandardItem *fileItem = mModel.item(i, 0); if (!fileItem) continue; QString actualfile((!mCheckPath.isEmpty() && filename.startsWith(mCheckPath)) ? filename.mid(mCheckPath.length() + 1) : filename); QVariantMap data = fileItem->data().toMap(); QString storedfile = data[FILENAME].toString(); storedfile = ((!mCheckPath.isEmpty() && storedfile.startsWith(mCheckPath)) ? storedfile.mid(mCheckPath.length() + 1) : storedfile); if (actualfile == storedfile) { mModel.removeRow(i); break; } } } void ResultsTree::loadSettings() { for (int i = 0; i < mModel.columnCount(); i++) { QString temp = QString(SETTINGS_RESULT_COLUMN_WIDTH).arg(i); setColumnWidth(i, qMax(20, mSettings->value(temp, 800 / mModel.columnCount()).toInt())); } mSaveFullPath = mSettings->value(SETTINGS_SAVE_FULL_PATH, false).toBool(); mSaveAllErrors = mSettings->value(SETTINGS_SAVE_ALL_ERRORS, false).toBool(); mShowFullPath = mSettings->value(SETTINGS_SHOW_FULL_PATH, false).toBool(); showIdColumn(mSettings->value(SETTINGS_SHOW_ERROR_ID, false).toBool()); showInconclusiveColumn(mSettings->value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool()); } void ResultsTree::saveSettings() const { for (int i = 0; i < mModel.columnCount(); i++) { QString temp = QString(SETTINGS_RESULT_COLUMN_WIDTH).arg(i); mSettings->setValue(temp, columnWidth(i)); } } void ResultsTree::showResults(ShowTypes::ShowType type, bool show) { if (type != ShowTypes::ShowNone && mShowSeverities.isShown(type) != show) { mShowSeverities.show(type, show); refreshTree(); } } void ResultsTree::showCppcheckResults(bool show) { mShowCppcheck = show; refreshTree(); } void ResultsTree::showClangResults(bool show) { mShowClang = show; refreshTree(); } void ResultsTree::filterResults(const QString& filter) { mFilter = filter; refreshTree(); } void ResultsTree::showHiddenResults() { //Clear the "hide" flag for each item mHiddenMessageId.clear(); int filecount = mModel.rowCount(); for (int i = 0; i < filecount; i++) { QStandardItem *fileItem = mModel.item(i, 0); if (!fileItem) continue; QVariantMap data = fileItem->data().toMap(); data[HIDE] = false; fileItem->setData(QVariant(data)); int errorcount = fileItem->rowCount(); for (int j = 0; j < errorcount; j++) { QStandardItem *child = fileItem->child(j, 0); if (child) { data = child->data().toMap(); data[HIDE] = false; child->setData(QVariant(data)); } } } refreshTree(); emit resultsHidden(false); } void ResultsTree::refreshTree() { mVisibleErrors = false; //Get the amount of files in the tree int filecount = mModel.rowCount(); for (int i = 0; i < filecount; i++) { //Get file i QStandardItem *fileItem = mModel.item(i, 0); if (!fileItem) { continue; } //Get the amount of errors this file contains int errorcount = fileItem->rowCount(); //By default it shouldn't be visible bool show = false; for (int j = 0; j < errorcount; j++) { //Get the error itself QStandardItem *child = fileItem->child(j, 0); if (!child) { continue; } //Get error's user data QVariant userdata = child->data(); //Convert it to QVariantMap QVariantMap data = userdata.toMap(); //Check if this error should be hidden bool hide = (data[HIDE].toBool() || !mShowSeverities.isShown(ShowTypes::VariantToShowType(data[SEVERITY]))); //If specified, filter on summary, message, filename, and id if (!hide && !mFilter.isEmpty()) { if (!data[SUMMARY].toString().contains(mFilter, Qt::CaseInsensitive) && !data[MESSAGE].toString().contains(mFilter, Qt::CaseInsensitive) && !data[FILENAME].toString().contains(mFilter, Qt::CaseInsensitive) && !data[ERRORID].toString().contains(mFilter, Qt::CaseInsensitive)) { hide = true; } } // Tool filter if (!hide) { if (data[ERRORID].toString().startsWith("clang")) hide = !mShowClang; else hide = !mShowCppcheck; } if (!hide) { mVisibleErrors = true; } //Hide/show accordingly setRowHidden(j, fileItem->index(), hide); //If it was shown then the file itself has to be shown as well if (!hide) { show = true; } } //Hide the file if its "hide" attribute is set if (fileItem->data().toMap()["hide"].toBool()) { show = false; } //Show the file if any of it's errors are visible setRowHidden(i, QModelIndex(), !show); } } QStandardItem *ResultsTree::ensureFileItem(const QString &fullpath, const QString &file0, bool hide) { QString name = stripPath(fullpath, false); // Since item has path with native separators we must use path with // native separators to find it. QStandardItem *item = findFileItem(QDir::toNativeSeparators(name)); if (item) { return item; } // Ensure shown path is with native separators name = QDir::toNativeSeparators(name); item = createNormalItem(name); item->setIcon(QIcon(":images/text-x-generic.png")); //Add user data to that item QMap data; data[FILENAME] = fullpath; data[FILE0] = file0; item->setData(QVariant(data)); mModel.appendRow(item); setRowHidden(mModel.rowCount() - 1, QModelIndex(), hide); return item; } void ResultsTree::contextMenuEvent(QContextMenuEvent * e) { QModelIndex index = indexAt(e->pos()); if (index.isValid()) { bool multipleSelection = false; mSelectionModel = selectionModel(); if (mSelectionModel->selectedRows().count() > 1) multipleSelection = true; mContextItem = mModel.itemFromIndex(index); //Create a new context menu QMenu menu(this); //Store all applications in a list QList actions; //Create a signal mapper so we don't have to store data to class //member variables QSignalMapper *signalMapper = new QSignalMapper(this); if (mContextItem && mApplications->getApplicationCount() > 0 && mContextItem->parent()) { //Create an action for the application int defaultApplicationIndex = mApplications->getDefaultApplication(); if (defaultApplicationIndex < 0) defaultApplicationIndex = 0; const Application& app = mApplications->getApplication(defaultApplicationIndex); QAction *start = new QAction(app.getName(), &menu); if (multipleSelection) start->setDisabled(true); //Add it to our list so we can disconnect later on actions << start; //Add it to context menu menu.addAction(start); //Connect the signal to signal mapper connect(start, SIGNAL(triggered()), signalMapper, SLOT(map())); //Add a new mapping signalMapper->setMapping(start, defaultApplicationIndex); connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(context(int))); } // Add popup menuitems if (mContextItem) { if (mApplications->getApplicationCount() > 0) { menu.addSeparator(); } const bool bughunting = !multipleSelection && mContextItem->data().toMap().value("id").toString().startsWith("bughunting"); if (bughunting && !getFunction(mContextItem).isEmpty()) { QAction *editContract = new QAction(tr("Edit contract.."), &menu); connect(editContract, &QAction::triggered, this, &ResultsTree::editContract); menu.addAction(editContract); menu.addSeparator(); } //Create an action for the application QAction *recheckSelectedFiles = new QAction(tr("Recheck"), &menu); QAction *copy = new QAction(tr("Copy"), &menu); QAction *hide = new QAction(tr("Hide"), &menu); QAction *hideallid = new QAction(tr("Hide all with id"), &menu); QAction *opencontainingfolder = new QAction(tr("Open containing folder"), &menu); if (multipleSelection) { hideallid->setDisabled(true); opencontainingfolder->setDisabled(true); } if (mThread->isChecking()) recheckSelectedFiles->setDisabled(true); else recheckSelectedFiles->setDisabled(false); menu.addAction(recheckSelectedFiles); menu.addSeparator(); menu.addAction(copy); menu.addSeparator(); menu.addAction(hide); menu.addAction(hideallid); if (!bughunting) { QAction *suppress = new QAction(tr("Suppress selected id(s)"), &menu); menu.addAction(suppress); connect(suppress, &QAction::triggered, this, &ResultsTree::suppressSelectedIds); } else { QAction *suppress = new QAction(tr("Suppress"), &menu); menu.addAction(suppress); connect(suppress, &QAction::triggered, this, &ResultsTree::suppressHash); } menu.addSeparator(); menu.addAction(opencontainingfolder); connect(recheckSelectedFiles, SIGNAL(triggered()), this, SLOT(recheckSelectedFiles())); connect(copy, SIGNAL(triggered()), this, SLOT(copy())); connect(hide, SIGNAL(triggered()), this, SLOT(hideResult())); connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult())); connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder())); const ProjectFile *currentProject = ProjectFile::getActiveProject(); if (currentProject && !currentProject->getTags().isEmpty()) { menu.addSeparator(); QMenu *tagMenu = menu.addMenu(tr("Tag")); { QAction *action = new QAction(tr("No tag"), tagMenu); tagMenu->addAction(action); connect(action, &QAction::triggered, [=]() { tagSelectedItems(QString()); }); } foreach (const QString tagstr, currentProject->getTags()) { QAction *action = new QAction(tagstr, tagMenu); tagMenu->addAction(action); connect(action, &QAction::triggered, [=]() { tagSelectedItems(tagstr); }); } } } //Start the menu menu.exec(e->globalPos()); index = indexAt(e->pos()); if (index.isValid()) { mContextItem = mModel.itemFromIndex(index); if (mContextItem && mApplications->getApplicationCount() > 0 && mContextItem->parent()) { //Disconnect all signals for (QAction* action : actions) { disconnect(action, SIGNAL(triggered()), signalMapper, SLOT(map())); } disconnect(signalMapper, SIGNAL(mapped(int)), this, SLOT(context(int))); //And remove the signal mapper delete signalMapper; } } } } void ResultsTree::startApplication(QStandardItem *target, int application) { //If there are no applications specified, tell the user about it if (mApplications->getApplicationCount() == 0) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("No editor application configured.\n\n" "Configure the editor application for Cppcheck in preferences/Applications."), QMessageBox::Ok, this); msg.exec(); return; } if (application == -1) application = mApplications->getDefaultApplication(); if (application == -1) { QMessageBox msg(QMessageBox::Critical, tr("Cppcheck"), tr("No default editor application selected.\n\n" "Please select the default editor application in preferences/Applications."), QMessageBox::Ok, this); msg.exec(); return; } if (target && application >= 0 && application < mApplications->getApplicationCount() && target->parent()) { // Make sure we are working with the first column if (target->column() != 0) target = target->parent()->child(target->row(), 0); QVariantMap data = target->data().toMap(); //Replace (file) with filename QString file = data[FILENAME].toString(); file = QDir::toNativeSeparators(file); qDebug() << "Opening file: " << file; QFileInfo info(file); if (!info.exists()) { if (info.isAbsolute()) { QMessageBox msgbox(this); msgbox.setWindowTitle("Cppcheck"); msgbox.setText(tr("Could not find the file!")); msgbox.setIcon(QMessageBox::Critical); msgbox.exec(); } else { QDir checkdir(mCheckPath); if (checkdir.isAbsolute() && checkdir.exists()) { file = mCheckPath + "/" + file; } else { QString dir = askFileDir(file); dir += '/'; file = dir + file; } } } if (file.indexOf(" ") > -1) { file.insert(0, "\""); file.append("\""); } const Application& app = mApplications->getApplication(application); QString params = app.getParameters(); params.replace("(file)", file, Qt::CaseInsensitive); QVariant line = data[LINE]; params.replace("(line)", QString("%1").arg(line.toInt()), Qt::CaseInsensitive); params.replace("(message)", data[MESSAGE].toString(), Qt::CaseInsensitive); params.replace("(severity)", data[SEVERITY].toString(), Qt::CaseInsensitive); QString program = app.getPath(); // In Windows we must surround paths including spaces with quotation marks. #ifdef Q_OS_WIN if (program.indexOf(" ") > -1) { if (!program.startsWith('"') && !program.endsWith('"')) { program.insert(0, "\""); program.append("\""); } } #endif // Q_OS_WIN const QString cmdLine = QString("%1 %2").arg(program).arg(params); bool success = QProcess::startDetached(cmdLine); if (!success) { QString text = tr("Could not start %1\n\nPlease check the application path and parameters are correct.").arg(program); QMessageBox msgbox(this); msgbox.setWindowTitle("Cppcheck"); msgbox.setText(text); msgbox.setIcon(QMessageBox::Critical); msgbox.exec(); } } } QString ResultsTree::askFileDir(const QString &file) { QString text = tr("Could not find file:") + '\n' + file + '\n'; QString title; if (file.indexOf('/')) { QString folderName = file.mid(0, file.indexOf('/')); text += tr("Please select the folder '%1'").arg(folderName); title = tr("Select Directory '%1'").arg(folderName); } else { text += tr("Please select the directory where file is located."); title = tr("Select Directory"); } QMessageBox msgbox(this); msgbox.setWindowTitle("Cppcheck"); msgbox.setText(text); msgbox.setIcon(QMessageBox::Warning); msgbox.exec(); QString dir = QFileDialog::getExistingDirectory(this, title, getPath(SETTINGS_LAST_SOURCE_PATH), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (dir.isEmpty()) return QString(); // User selected root path if (QFileInfo(dir + '/' + file).exists()) mCheckPath = dir; // user selected checked folder else if (file.indexOf('/') > 0) { dir += '/'; QString folderName = file.mid(0, file.indexOf('/')); if (dir.indexOf('/' + folderName + '/')) dir = dir.mid(0, dir.lastIndexOf('/' + folderName + '/')); if (QFileInfo(dir + '/' + file).exists()) mCheckPath = dir; } // Otherwise; return else return QString(); setPath(SETTINGS_LAST_SOURCE_PATH, mCheckPath); return mCheckPath; } void ResultsTree::copy() { if (!mSelectionModel) return; QModelIndexList selectedRows = mSelectionModel->selectedRows(); QString text; foreach (QModelIndex index, selectedRows) { QStandardItem *item = mModel.itemFromIndex(index); if (!item->parent()) { text += item->text() + '\n'; continue; } if (item->parent()->parent()) item = item->parent(); QVariantMap data = item->data().toMap(); if (!data.contains("id")) continue; QString inconclusive = data[INCONCLUSIVE].toBool() ? ",inconclusive" : ""; text += '[' + data[FILENAME].toString() + ':' + QString::number(data[LINE].toInt()) + "] (" + QString::fromStdString(Severity::toString(ShowTypes::ShowTypeToSeverity((ShowTypes::ShowType)data[SEVERITY].toInt()))) + inconclusive + ") " + data[MESSAGE].toString() + " [" + data[ERRORID].toString() + "]\n"; } QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(text); } void ResultsTree::hideResult() { if (!mSelectionModel) return; QModelIndexList selectedRows = mSelectionModel->selectedRows(); foreach (QModelIndex index, selectedRows) { QStandardItem *item = mModel.itemFromIndex(index); //Set the "hide" flag for this item QVariantMap data = item->data().toMap(); data[HIDE] = true; item->setData(QVariant(data)); refreshTree(); emit resultsHidden(true); } } void ResultsTree::recheckSelectedFiles() { if (!mSelectionModel) return; QModelIndexList selectedRows = mSelectionModel->selectedRows(); QStringList selectedItems; foreach (QModelIndex index, selectedRows) { QStandardItem *item = mModel.itemFromIndex(index); while (item->parent()) item = item->parent(); QVariantMap data = item->data().toMap(); QString currentFile = data[FILENAME].toString(); if (!currentFile.isEmpty()) { QString fileNameWithCheckPath; QFileInfo curfileInfo(currentFile); if (!curfileInfo.exists() && !mCheckPath.isEmpty() && currentFile.indexOf(mCheckPath) != 0) fileNameWithCheckPath = mCheckPath + "/" + currentFile; else fileNameWithCheckPath = currentFile; const QFileInfo fileInfo(fileNameWithCheckPath); if (!fileInfo.exists()) { askFileDir(currentFile); return; } if (Path::isHeader(currentFile.toStdString())) { if (!data[FILE0].toString().isEmpty() && !selectedItems.contains(data[FILE0].toString())) { selectedItems<<((!mCheckPath.isEmpty() && (data[FILE0].toString().indexOf(mCheckPath) != 0)) ? (mCheckPath + "/" + data[FILE0].toString()) : data[FILE0].toString()); if (!selectedItems.contains(fileNameWithCheckPath)) selectedItems<parent()) return; // Make sure we are working with the first column if (mContextItem->column() != 0) mContextItem = mContextItem->parent()->child(mContextItem->row(), 0); QVariantMap data = mContextItem->data().toMap(); QString messageId = data[ERRORID].toString(); mHiddenMessageId.append(messageId); // hide all errors with that message Id int filecount = mModel.rowCount(); for (int i = 0; i < filecount; i++) { //Get file i QStandardItem *file = mModel.item(i, 0); if (!file) { continue; } //Get the amount of errors this file contains int errorcount = file->rowCount(); for (int j = 0; j < errorcount; j++) { //Get the error itself QStandardItem *child = file->child(j, 0); if (!child) { continue; } QVariantMap userdata = child->data().toMap(); if (userdata[ERRORID].toString() == messageId) { userdata[HIDE] = true; child->setData(QVariant(userdata)); } } } refreshTree(); emit resultsHidden(true); } void ResultsTree::suppressSelectedIds() { if (!mSelectionModel) return; QModelIndexList selectedRows = mSelectionModel->selectedRows(); QSet selectedIds; foreach (QModelIndex index, selectedRows) { QStandardItem *item = mModel.itemFromIndex(index); if (!item->parent()) continue; if (item->parent()->parent()) item = item->parent(); QVariantMap data = item->data().toMap(); if (!data.contains("id")) continue; selectedIds << data[ERRORID].toString(); } // delete all errors with selected message Ids for (int i = 0; i < mModel.rowCount(); i++) { QStandardItem * const file = mModel.item(i, 0); for (int j = 0; j < file->rowCount();) { QStandardItem *errorItem = file->child(j, 0); QVariantMap userdata = errorItem->data().toMap(); if (selectedIds.contains(userdata[ERRORID].toString())) { file->removeRow(j); } else { j++; } } if (file->rowCount() == 0) mModel.removeRow(file->row()); } emit suppressIds(selectedIds.values()); } void ResultsTree::suppressHash() { if (!mSelectionModel) return; // Extract selected warnings QSet selectedWarnings; foreach (QModelIndex index, mSelectionModel->selectedRows()) { QStandardItem *item = mModel.itemFromIndex(index); if (!item->parent()) continue; while (item->parent()->parent()) item = item->parent(); selectedWarnings.insert(item); } bool changed = false; ProjectFile *projectFile = ProjectFile::getActiveProject(); for (QStandardItem *item: selectedWarnings) { QStandardItem *fileItem = item->parent(); const QVariantMap data = item->data().toMap(); if (projectFile && data.contains(HASH)) { Suppressions::Suppression suppression; suppression.hash = data[HASH].toULongLong(); suppression.errorId = data[ERRORID].toString().toStdString(); suppression.fileName = data[FILENAME].toString().toStdString(); suppression.lineNumber = data[LINE].toInt(); projectFile->addSuppression(suppression); changed = true; } fileItem->removeRow(item->row()); if (fileItem->rowCount() == 0) mModel.removeRow(fileItem->row()); } if (changed) projectFile->write(); } void ResultsTree::openContainingFolder() { QString filePath = getFilePath(mContextItem, true); if (!filePath.isEmpty()) { filePath = QFileInfo(filePath).absolutePath(); QDesktopServices::openUrl(QUrl::fromLocalFile(filePath)); } } void ResultsTree::editContract() { emit editFunctionContract(getFunction(mContextItem)); } void ResultsTree::tagSelectedItems(const QString &tag) { if (!mSelectionModel) return; bool isTagged = false; ProjectFile *currentProject = ProjectFile::getActiveProject(); foreach (QModelIndex index, mSelectionModel->selectedRows()) { QStandardItem *item = mModel.itemFromIndex(index); QVariantMap data = item->data().toMap(); if (data.contains("tags")) { data[TAGS] = tag; item->setData(QVariant(data)); item->parent()->child(index.row(), COLUMN_TAGS)->setText(tag); if (currentProject && data.contains(HASH)) { isTagged = true; currentProject->setWarningTags(data[HASH].toULongLong(), tag); } } } if (isTagged) currentProject->write(); } void ResultsTree::context(int application) { startApplication(mContextItem, application); } void ResultsTree::quickStartApplication(const QModelIndex &index) { startApplication(mModel.itemFromIndex(index)); } QString ResultsTree::getFilePath(QStandardItem *target, bool fullPath) { if (target) { // Make sure we are working with the first column if (target->column() != 0) target = target->parent()->child(target->row(), 0); QVariantMap data = target->data().toMap(); QString pathStr; //Replace (file) with filename QString file = data[FILENAME].toString(); pathStr = QDir::toNativeSeparators(file); if (!fullPath) { QFileInfo fi(pathStr); pathStr = fi.fileName(); } return pathStr; } return QString(); } QString ResultsTree::severityToIcon(Severity::SeverityType severity) const { switch (severity) { case Severity::error: return ":images/dialog-error.png"; case Severity::style: return ":images/applications-development.png"; case Severity::warning: return ":images/dialog-warning.png"; case Severity::portability: return ":images/applications-system.png"; case Severity::performance: return ":images/utilities-system-monitor.png"; case Severity::information: return ":images/dialog-information.png"; default: return QString(); } } void ResultsTree::saveResults(Report *report) const { report->writeHeader(); for (int i = 0; i < mModel.rowCount(); i++) { if (mSaveAllErrors || !isRowHidden(i, QModelIndex())) saveErrors(report, mModel.item(i, 0)); } report->writeFooter(); } void ResultsTree::saveErrors(Report *report, const QStandardItem *fileItem) const { if (!fileItem) { return; } for (int i = 0; i < fileItem->rowCount(); i++) { const QStandardItem *error = fileItem->child(i, 0); if (!error) { continue; } if (isRowHidden(i, fileItem->index()) && !mSaveAllErrors) { continue; } ErrorItem item; readErrorItem(error, &item); report->writeError(item); } } static int indexOf(const QList &list, const ErrorItem &item) { for (int i = 0; i < list.size(); i++) { if (ErrorItem::sameCID(item, list[i])) { return i; } } return -1; } void ResultsTree::updateFromOldReport(const QString &filename) { QList oldErrors; XmlReportV2 oldReport(filename); if (oldReport.open()) { oldErrors = oldReport.read(); oldReport.close(); } // Read current results.. for (int i = 0; i < mModel.rowCount(); i++) { QStandardItem *fileItem = mModel.item(i,0); for (int j = 0; j < fileItem->rowCount(); j++) { QStandardItem *error = fileItem->child(j,0); ErrorItem errorItem; readErrorItem(error, &errorItem); int oldErrorIndex = indexOf(oldErrors, errorItem); QVariantMap data = error->data().toMap(); // New error .. set the "sinceDate" property if (oldErrorIndex >= 0 && !oldErrors[oldErrorIndex].sinceDate.isEmpty()) { data[SINCEDATE] = oldErrors[oldErrorIndex].sinceDate; error->setData(data); fileItem->child(j, COLUMN_SINCE_DATE)->setText(oldErrors[oldErrorIndex].sinceDate); } else if (oldErrorIndex < 0 || data[SINCEDATE].toString().isEmpty()) { const QString sinceDate = QDate::currentDate().toString(Qt::SystemLocaleShortDate); data[SINCEDATE] = sinceDate; error->setData(data); fileItem->child(j, COLUMN_SINCE_DATE)->setText(sinceDate); if (oldErrorIndex < 0) continue; } if (!errorItem.tags.isEmpty()) continue; const ErrorItem &oldErrorItem = oldErrors[oldErrorIndex]; data[TAGS] = oldErrorItem.tags; error->setData(data); } } } void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) const { // Get error's user data QVariantMap data = error->data().toMap(); item->severity = ShowTypes::ShowTypeToSeverity(ShowTypes::VariantToShowType(data[SEVERITY])); item->summary = data[SUMMARY].toString(); item->message = data[MESSAGE].toString(); item->errorId = data[ERRORID].toString(); item->incomplete = data[INCOMPLETE].toBool(); item->cwe = data[CWE].toInt(); item->hash = data[HASH].toULongLong(); item->inconclusive = data[INCONCLUSIVE].toBool(); item->file0 = data[FILE0].toString(); item->sinceDate = data[SINCEDATE].toString(); item->tags = data[TAGS].toString(); if (error->rowCount() == 0) { QErrorPathItem e; e.file = stripPath(data[FILENAME].toString(), true); e.line = data[LINE].toInt(); e.info = data[MESSAGE].toString(); item->errorPath << e; } for (int j = 0; j < error->rowCount(); j++) { const QStandardItem *child_error = error->child(j, 0); //Get error's user data QVariant child_userdata = child_error->data(); //Convert it to QVariantMap QVariantMap child_data = child_userdata.toMap(); QErrorPathItem e; e.file = stripPath(child_data[FILENAME].toString(), true); e.line = child_data[LINE].toInt(); e.info = child_data[MESSAGE].toString(); item->errorPath << e; } } void ResultsTree::updateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showErrorId, bool showInconclusive) { if (mShowFullPath != showFullPath) { mShowFullPath = showFullPath; refreshFilePaths(); } mSaveFullPath = saveFullPath; mSaveAllErrors = saveAllErrors; showIdColumn(showErrorId); showInconclusiveColumn(showInconclusive); } void ResultsTree::setCheckDirectory(const QString &dir) { mCheckPath = dir; } QString ResultsTree::getCheckDirectory() { return mCheckPath; } QString ResultsTree::stripPath(const QString &path, bool saving) const { if ((!saving && mShowFullPath) || (saving && mSaveFullPath)) { return QString(path); } QDir dir(mCheckPath); return dir.relativeFilePath(path); } void ResultsTree::refreshFilePaths(QStandardItem *item) { if (!item) { return; } //Mark that this file's path hasn't been updated yet bool updated = false; //Loop through all errors within this file for (int i = 0; i < item->rowCount(); i++) { //Get error i QStandardItem *error = item->child(i, 0); if (!error) { continue; } //Get error's user data QVariant userdata = error->data(); //Convert it to QVariantMap QVariantMap data = userdata.toMap(); //Get list of files QString file = data[FILENAME].toString(); //Update this error's text error->setText(stripPath(file, false)); //If this error has backtraces make sure the files list has enough filenames if (error->hasChildren()) { //Loop through all files within the error for (int j = 0; j < error->rowCount(); j++) { //Get file QStandardItem *child = error->child(j, 0); if (!child) { continue; } //Get child's user data QVariant child_userdata = child->data(); //Convert it to QVariantMap QVariantMap child_data = child_userdata.toMap(); //Get list of files QString child_files = child_data[FILENAME].toString(); //Update file's path child->setText(stripPath(child_files, false)); } } //if the main file hasn't been updated yet, update it now if (!updated) { updated = true; item->setText(error->text()); } } } void ResultsTree::refreshFilePaths() { qDebug("Refreshing file paths"); //Go through all file items (these are parent items that contain the errors) for (int i = 0; i < mModel.rowCount(); i++) { refreshFilePaths(mModel.item(i, 0)); } } bool ResultsTree::hasVisibleResults() const { return mVisibleErrors; } bool ResultsTree::hasResults() const { return mModel.rowCount() > 0; } void ResultsTree::translate() { QStringList labels; labels << tr("File") << tr("Severity") << tr("Line") << tr("Id") << tr("Inconclusive") << tr("Summary") << tr("Since date") << tr("Tag"); mModel.setHorizontalHeaderLabels(labels); //TODO go through all the errors in the tree and translate severity and message } void ResultsTree::showIdColumn(bool show) { mShowErrorId = show; if (show) showColumn(3); else hideColumn(3); } void ResultsTree::showInconclusiveColumn(bool show) { if (show) showColumn(4); else hideColumn(4); } void ResultsTree::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { QTreeView::currentChanged(current, previous); emit treeSelectionChanged(current); } cppcheck-2.7/gui/resultstree.h000066400000000000000000000337301417746362400165040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef RESULTSTREE_H #define RESULTSTREE_H #include "errortypes.h" #include "showtypes.h" #include #include class ApplicationList; class Report; class ErrorItem; class ErrorLine; class QModelIndex; class QWidget; class QItemSelectionModel; class ThreadHandler; class QContextMenuEvent; class QSettings; /// @addtogroup GUI /// @{ /** * @brief Cppcheck's results are shown in this tree * */ class ResultsTree : public QTreeView { Q_OBJECT public: explicit ResultsTree(QWidget * parent = nullptr); virtual ~ResultsTree(); void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler); /** * @brief Add a new item to the tree * * @param item Error item data */ bool addErrorItem(const ErrorItem &item); /** * @brief Clear all errors from the tree * */ void clear(); /** * @brief Clear errors for a specific file from the tree */ void clear(const QString &filename); /** * @brief Clear errors of a file selected for recheck */ void clearRecheckFile(const QString &filename); /** * @brief Function to filter the displayed list of errors. * Refreshes the tree. * * @param filter String that must be found in the summary, description, file or id */ void filterResults(const QString& filter); /** * @brief Function to show results that were previous hidden with HideResult() */ void showHiddenResults(); /** * @brief Refresh tree by checking which of the items should be shown * and which should be hidden */ void refreshTree(); /** * @brief Save results to a text stream * */ void saveResults(Report *report) const; /** * @brief Update items from old report (tag, sinceDate) */ void updateFromOldReport(const QString &filename); /** * @brief Update tree settings * * @param showFullPath Show full path of files in the tree * @param saveFullPath Save full path of files in reports * @param saveAllErrors Save all visible errors * @param showErrorId Show error id * @param showInconclusive Show inconclusive column */ void updateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showErrorId, bool showInconclusive); /** * @brief Set the directory we are checking * * This is used to split error file path to relative if necessary * @param dir Directory we are checking */ void setCheckDirectory(const QString &dir); /** * @brief Get the directory we are checking * * @return Directory containing source files */ QString getCheckDirectory(); /** * @brief Check if there are any visible results in view. * @return true if there is at least one visible warning/error. */ bool hasVisibleResults() const; /** * @brief Do we have results from check? * @return true if there is at least one warning/error, hidden or visible. */ bool hasResults() const; /** * @brief Save all settings * Column widths */ void saveSettings() const; /** * @brief Change all visible texts language * */ void translate(); /** * @brief Show optional column "Id" */ void showIdColumn(bool show); /** * @brief Show optional column "Inconclusve" */ void showInconclusiveColumn(bool show); /** * @brief Returns true if column "Id" is shown */ bool showIdColumn() const { return mShowErrorId; } /** * @brief GUI severities. */ ShowTypes mShowSeverities; void keyPressEvent(QKeyEvent *event) override; signals: /** * @brief Signal that results have been hidden or shown * * @param hidden true if there are some hidden results, or false if there are not */ void resultsHidden(bool hidden); /** * @brief Signal to perform selected files recheck * * @param selectedItems list of selected files */ void checkSelected(QStringList selectedItems); /** * @brief Signal for selection change in result tree. * * @param current Model index to specify new selected item. */ void treeSelectionChanged(const QModelIndex ¤t); /** Suppress Ids */ void suppressIds(QStringList ids); /** Edit contract for function */ void editFunctionContract(QString function); public slots: /** * @brief Function to show/hide certain type of errors * Refreshes the tree. * * @param type Type of error to show/hide * @param show Should specified errors be shown (true) or hidden (false) */ void showResults(ShowTypes::ShowType type, bool show); /** * @brief Show/hide cppcheck errors. * Refreshes the tree. * * @param show Should specified errors be shown (true) or hidden (false) */ void showCppcheckResults(bool show); /** * @brief Show/hide clang-tidy/clang-analyzer errors. * Refreshes the tree. * * @param show Should specified errors be shown (true) or hidden (false) */ void showClangResults(bool show); protected slots: /** * @brief Slot to quickstart an error with default application * * @param index Model index to specify which error item to open */ void quickStartApplication(const QModelIndex &index); /** * @brief Slot for context menu item to open an error with specified application * * @param application Index of the application to open the error */ void context(int application); /** * @brief Slot for context menu item to copy selection to clipboard */ void copy(); /** * @brief Slot for context menu item to hide the current error message * */ void hideResult(); /** * @brief Slot for rechecking selected files * */ void recheckSelectedFiles(); /** * @brief Slot for context menu item to hide all messages with the current message Id * */ void hideAllIdResult(); /** Slot for context menu item to suppress all messages with the current message id */ void suppressSelectedIds(); /** Slot for context menu item to suppress message with hash */ void suppressHash(); /** * @brief Slot for context menu item to open the folder containing the current file. */ void openContainingFolder(); /** * @brief Allow user to edit contract to fix bughunting warning */ void editContract(); /** * @brief Slot for selection change in the results tree. * * @param current Model index to specify new selected item. * @param previous Model index to specify previous selected item. */ void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; protected: /** * @brief Hides/shows full file path on all error file items according to mShowFullPath * */ void refreshFilePaths(); /** * @brief Hides/shows full file path on all error file items according to mShowFullPath * @param item Parent item whose children's paths to change */ void refreshFilePaths(QStandardItem *item); /** * @brief Removes checking directory from given path if mShowFullPath is false * * @param path Path to remove checking directory * @param saving are we saving? Check mSaveFullPath instead * @return Path that has checking directory removed */ QString stripPath(const QString &path, bool saving) const; /** * @brief Save all errors under specified item * @param report Report that errors are saved to * @param fileItem Item whose errors to save */ void saveErrors(Report *report, const QStandardItem *fileItem) const; /** * @brief Convert a severity string to a icon filename * * @param severity Severity */ QString severityToIcon(Severity::SeverityType severity) const; /** * @brief Helper function to open an error within target with application* * * @param target Error tree item to open * @param application Index of the application to open with. Giving -1 * (default value) will open the default application. */ void startApplication(QStandardItem *target, int application = -1); /** * @brief Helper function to copy filename/full path to the clipboard * * @param target Error tree item to open * @param fullPath Are we copying full path or only filename? */ void copyPathToClipboard(QStandardItem *target, bool fullPath); /** * @brief Helper function returning the filename/full path of the error tree item \a target. * * @param target The error tree item containing the filename/full path * @param fullPath Whether or not to retrieve the full path or only the filename. */ QString getFilePath(QStandardItem *target, bool fullPath); /** * @brief Context menu event (user right clicked on the tree) * * @param e Event */ void contextMenuEvent(QContextMenuEvent * e) override; /** * @brief Add a new error item beneath a file or a backtrace item beneath an error * * @param parent Parent for the item. Either a file item or an error item * @param item Error line data * @param hide Should this be hidden (true) or shown (false) * @param icon Should a default backtrace item icon be added * @param childOfMessage Is this a child element of a message? * @return newly created QStandardItem * */ QStandardItem *addBacktraceFiles(QStandardItem *parent, const ErrorLine &item, const bool hide, const QString &icon, bool childOfMessage); /** * @brief Convert Severity to translated string for GUI. * @param severity Severity to convert * @return Severity as translated string */ static QString severityToTranslatedString(Severity::SeverityType severity); /** * @brief Load all settings * Column widths */ void loadSettings(); /** * @brief Ask directory where file is located. * @param file File name. * @return Directory user chose. */ QString askFileDir(const QString &file); /** * @brief Create new normal item. * * Normal item has left alignment and text set also as tooltip. * @param name name for the item * @return new QStandardItem */ static QStandardItem *createNormalItem(const QString &name); /** * @brief Create new normal item. * * Normal item has left alignment and text set also as tooltip. * @param checked checked * @return new QStandardItem */ static QStandardItem *createCheckboxItem(bool checked); /** * @brief Create new line number item. * * Line number item has right align and text set as tooltip. * @param linenumber name for the item * @return new QStandardItem */ static QStandardItem *createLineNumberItem(const QString &linenumber); /** * @brief Finds a file item * * @param name name of the file item to find * @return pointer to file item or null if none found */ QStandardItem *findFileItem(const QString &name) const; /** * @brief Ensures there's a item in the model for the specified file * * @param fullpath Full path to the file item. * @param file0 Source file * @param hide is the error (we want this file item for) hidden? * @return QStandardItem to be used as a parent for all errors for specified file */ QStandardItem *ensureFileItem(const QString &fullpath, const QString &file0, bool hide); /** * @brief Item model for tree * */ QStandardItemModel mModel; /** * @brief Program settings * */ QSettings *mSettings; /** * @brief A string used to filter the results for display. * */ QString mFilter; /** * @brief List of applications to open errors with * */ ApplicationList *mApplications; /** * @brief Right clicked item (used by context menu slots) * */ QStandardItem *mContextItem; /** * @brief Should full path of files be shown (true) or relative (false) * */ bool mShowFullPath; /** * @brief Should full path of files be saved * */ bool mSaveFullPath; /** * @brief Save all errors (true) or only visible (false) * */ bool mSaveAllErrors; /** * @brief true if optional column "Id" is shown * */ bool mShowErrorId; /** * @brief Path we are currently checking * */ QString mCheckPath; /** * @brief Are there any visible errors * */ bool mVisibleErrors; private: /** tag selected items */ void tagSelectedItems(const QString &tag); /** @brief Convert GUI error item into data error item */ void readErrorItem(const QStandardItem *error, ErrorItem *item) const; QStringList mHiddenMessageId; QItemSelectionModel *mSelectionModel; ThreadHandler *mThread; bool mShowCppcheck; bool mShowClang; }; /// @} #endif // RESULTSTREE_H cppcheck-2.7/gui/resultsview.cpp000066400000000000000000000450061417746362400170510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "resultsview.h" #include "checkstatistics.h" #include "codeeditorstyle.h" #include "common.h" #include "csvreport.h" #include "erroritem.h" #include "path.h" #include "printablereport.h" #include "txtreport.h" #include "xmlreport.h" #include "xmlreportv2.h" #include #include #include #include #include #include #include #include #include #include #include ResultsView::ResultsView(QWidget * parent) : QWidget(parent), mShowNoErrorsMessage(true), mStatistics(new CheckStatistics(this)) { mUI.setupUi(this); connect(mUI.mTree, &ResultsTree::resultsHidden, this, &ResultsView::resultsHidden); connect(mUI.mTree, &ResultsTree::checkSelected, this, &ResultsView::checkSelected); connect(mUI.mTree, &ResultsTree::treeSelectionChanged, this, &ResultsView::updateDetails); connect(mUI.mTree, &ResultsTree::suppressIds, this, &ResultsView::suppressIds); connect(mUI.mTree, &ResultsTree::editFunctionContract, this, &ResultsView::editFunctionContract); connect(this, &ResultsView::showResults, mUI.mTree, &ResultsTree::showResults); connect(this, &ResultsView::showCppcheckResults, mUI.mTree, &ResultsTree::showCppcheckResults); connect(this, &ResultsView::showClangResults, mUI.mTree, &ResultsTree::showClangResults); connect(this, &ResultsView::collapseAllResults, mUI.mTree, &ResultsTree::collapseAll); connect(this, &ResultsView::expandAllResults, mUI.mTree, &ResultsTree::expandAll); connect(this, &ResultsView::showHiddenResults, mUI.mTree, &ResultsTree::showHiddenResults); // Function contracts connect(mUI.mListAddedContracts, &QListWidget::itemDoubleClicked, this, &ResultsView::contractDoubleClicked); connect(mUI.mListMissingContracts, &QListWidget::itemDoubleClicked, this, &ResultsView::contractDoubleClicked); mUI.mListAddedContracts->installEventFilter(this); // Variable contracts connect(mUI.mListAddedVariables, &QListWidget::itemDoubleClicked, this, &ResultsView::variableDoubleClicked); connect(mUI.mListMissingVariables, &QListWidget::itemDoubleClicked, this, &ResultsView::variableDoubleClicked); connect(mUI.mEditVariablesFilter, &QLineEdit::textChanged, this, &ResultsView::editVariablesFilter); mUI.mListAddedVariables->installEventFilter(this); mUI.mListLog->setContextMenuPolicy(Qt::CustomContextMenu); mUI.mListAddedContracts->setSortingEnabled(true); mUI.mListMissingContracts->setSortingEnabled(true); } void ResultsView::initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler) { mUI.mProgress->setMinimum(0); mUI.mProgress->setVisible(false); CodeEditorStyle theStyle(CodeEditorStyle::loadSettings(settings)); mUI.mCode->setStyle(theStyle); QByteArray state = settings->value(SETTINGS_MAINWND_SPLITTER_STATE).toByteArray(); mUI.mVerticalSplitter->restoreState(state); mShowNoErrorsMessage = settings->value(SETTINGS_SHOW_NO_ERRORS, true).toBool(); mUI.mTree->initialize(settings, list, checkThreadHandler); } ResultsView::~ResultsView() { //dtor } void ResultsView::setAddedFunctionContracts(const QStringList &addedContracts) { mUI.mListAddedContracts->clear(); mUI.mListAddedContracts->addItems(addedContracts); for (const QString& f: addedContracts) { auto res = mUI.mListMissingContracts->findItems(f, Qt::MatchExactly); if (!res.empty()) delete res.front(); } } void ResultsView::setAddedVariableContracts(const QStringList &added) { mUI.mListAddedVariables->clear(); mUI.mListAddedVariables->addItems(added); for (const QString& var: added) { for (auto *item: mUI.mListMissingVariables->findItems(var, Qt::MatchExactly)) delete item; mVariableContracts.insert(var); } } void ResultsView::clear(bool results) { if (results) { mUI.mTree->clear(); } mUI.mDetails->setText(QString()); mStatistics->clear(); //Clear the progressbar mUI.mProgress->setMaximum(PROGRESS_MAX); mUI.mProgress->setValue(0); mUI.mProgress->setFormat("%p%"); } void ResultsView::clear(const QString &filename) { mUI.mTree->clear(filename); } void ResultsView::clearRecheckFile(const QString &filename) { mUI.mTree->clearRecheckFile(filename); } void ResultsView::clearContracts() { mUI.mListAddedContracts->clear(); mUI.mListAddedVariables->clear(); mUI.mListMissingContracts->clear(); mUI.mListMissingVariables->clear(); mFunctionContracts.clear(); mVariableContracts.clear(); } void ResultsView::showContracts(bool visible) { mUI.mTabFunctionContracts->setVisible(visible); mUI.mTabVariableContracts->setVisible(visible); } void ResultsView::progress(int value, const QString& description) { mUI.mProgress->setValue(value); mUI.mProgress->setFormat(QString("%p% (%1)").arg(description)); } void ResultsView::error(const ErrorItem &item) { if (mUI.mTree->addErrorItem(item)) { emit gotResults(); mStatistics->addItem(item.tool(), ShowTypes::SeverityToShowType(item.severity)); } } void ResultsView::filterResults(const QString& filter) { mUI.mTree->filterResults(filter); } void ResultsView::saveStatistics(const QString &filename) const { QFile f(filename); if (!f.open(QIODevice::Text | QIODevice::Append)) return; QTextStream ts(&f); ts << '[' << QDate::currentDate().toString("dd.MM.yyyy") << "]\n"; ts << QDateTime::currentMSecsSinceEpoch() << '\n'; foreach (QString tool, mStatistics->getTools()) { ts << tool << "-error:" << mStatistics->getCount(tool, ShowTypes::ShowErrors) << '\n'; ts << tool << "-warning:" << mStatistics->getCount(tool, ShowTypes::ShowWarnings) << '\n'; ts << tool << "-style:" << mStatistics->getCount(tool, ShowTypes::ShowStyle) << '\n'; ts << tool << "-performance:" << mStatistics->getCount(tool, ShowTypes::ShowPerformance) << '\n'; ts << tool << "-portability:" << mStatistics->getCount(tool, ShowTypes::ShowPortability) << '\n'; } } void ResultsView::updateFromOldReport(const QString &filename) const { mUI.mTree->updateFromOldReport(filename); } void ResultsView::save(const QString &filename, Report::Type type) const { Report *report = nullptr; switch (type) { case Report::CSV: report = new CsvReport(filename); break; case Report::TXT: report = new TxtReport(filename); break; case Report::XMLV2: report = new XmlReportV2(filename); break; } if (report) { if (report->create()) mUI.mTree->saveResults(report); else { QMessageBox msgBox; msgBox.setText(tr("Failed to save the report.")); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); } delete report; report = nullptr; } else { QMessageBox msgBox; msgBox.setText(tr("Failed to save the report.")); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); } } void ResultsView::print() { QPrinter printer; QPrintDialog dialog(&printer, this); dialog.setWindowTitle(tr("Print Report")); if (dialog.exec() != QDialog::Accepted) return; print(&printer); } void ResultsView::printPreview() { QPrinter printer; QPrintPreviewDialog dialog(&printer, this); connect(&dialog, SIGNAL(paintRequested(QPrinter*)), SLOT(print(QPrinter*))); dialog.exec(); } void ResultsView::print(QPrinter* printer) { if (!hasResults()) { QMessageBox msgBox; msgBox.setText(tr("No errors found, nothing to print.")); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); return; } PrintableReport report; mUI.mTree->saveResults(&report); QTextDocument doc(report.getFormattedReportText()); doc.print(printer); } void ResultsView::updateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showNoErrorsMessage, bool showErrorId, bool showInconclusive) { mUI.mTree->updateSettings(showFullPath, saveFullPath, saveAllErrors, showErrorId, showInconclusive); mShowNoErrorsMessage = showNoErrorsMessage; } void ResultsView::updateStyleSetting(QSettings *settings) { CodeEditorStyle theStyle(CodeEditorStyle::loadSettings(settings)); mUI.mCode->setStyle(theStyle); } void ResultsView::setCheckDirectory(const QString &dir) { mUI.mTree->setCheckDirectory(dir); } QString ResultsView::getCheckDirectory() { return mUI.mTree->getCheckDirectory(); } void ResultsView::checkingStarted(int count) { mUI.mProgress->setVisible(true); mUI.mProgress->setMaximum(PROGRESS_MAX); mUI.mProgress->setValue(0); mUI.mProgress->setFormat(tr("%p% (%1 of %2 files checked)").arg(0).arg(count)); } void ResultsView::checkingFinished() { mUI.mProgress->setVisible(false); mUI.mProgress->setFormat("%p%"); // TODO: Items can be mysteriously hidden when checking is finished, this function // call should be redundant but it "unhides" the wrongly hidden items. mUI.mTree->refreshTree(); //Should we inform user of non visible/not found errors? if (mShowNoErrorsMessage) { //Tell user that we found no errors if (!hasResults()) { QMessageBox msg(QMessageBox::Information, tr("Cppcheck"), tr("No errors found."), QMessageBox::Ok, this); msg.exec(); } //If we have errors but they aren't visible, tell user about it else if (!mUI.mTree->hasVisibleResults()) { QString text = tr("Errors were found, but they are configured to be hidden.\n" \ "To toggle what kind of errors are shown, open view menu."); QMessageBox msg(QMessageBox::Information, tr("Cppcheck"), text, QMessageBox::Ok, this); msg.exec(); } } } bool ResultsView::hasVisibleResults() const { return mUI.mTree->hasVisibleResults(); } bool ResultsView::hasResults() const { return mUI.mTree->hasResults(); } void ResultsView::saveSettings(QSettings *settings) { mUI.mTree->saveSettings(); QByteArray state = mUI.mVerticalSplitter->saveState(); settings->setValue(SETTINGS_MAINWND_SPLITTER_STATE, state); mUI.mVerticalSplitter->restoreState(state); } void ResultsView::translate() { mUI.retranslateUi(this); mUI.mTree->translate(); } void ResultsView::disableProgressbar() { mUI.mProgress->setEnabled(false); } void ResultsView::readErrorsXml(const QString &filename) { const int version = XmlReport::determineVersion(filename); if (version == 0) { QMessageBox msgBox; msgBox.setText(tr("Failed to read the report.")); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); return; } if (version == 1) { QMessageBox msgBox; msgBox.setText(tr("XML format version 1 is no longer supported.")); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); return; } XmlReportV2 report(filename); QList errors; if (report.open()) { errors = report.read(); } else { QMessageBox msgBox; msgBox.setText(tr("Failed to read the report.")); msgBox.setIcon(QMessageBox::Critical); msgBox.exec(); } ErrorItem item; foreach (item, errors) { mUI.mTree->addErrorItem(item); } QString dir; if (!errors.isEmpty() && !errors[0].errorPath.isEmpty()) { QString relativePath = QFileInfo(filename).canonicalPath(); if (QFileInfo(relativePath + '/' + errors[0].errorPath[0].file).exists()) dir = relativePath; } mUI.mTree->setCheckDirectory(dir); } void ResultsView::updateDetails(const QModelIndex &index) { QStandardItemModel *model = qobject_cast(mUI.mTree->model()); QStandardItem *item = model->itemFromIndex(index); if (!item) { mUI.mCode->clear(); mUI.mDetails->setText(QString()); return; } // Make sure we are working with the first column if (item->parent() && item->column() != 0) item = item->parent()->child(item->row(), 0); QVariantMap data = item->data().toMap(); // If there is no severity data then it is a parent item without summary and message if (!data.contains("severity")) { mUI.mCode->clear(); mUI.mDetails->setText(QString()); return; } const QString message = data["message"].toString(); QString formattedMsg = message; const QString file0 = data["file0"].toString(); if (!file0.isEmpty() && Path::isHeader(data["file"].toString().toStdString())) formattedMsg += QString("\n\n%1: %2").arg(tr("First included by")).arg(QDir::toNativeSeparators(file0)); if (data["cwe"].toInt() > 0) formattedMsg.prepend("CWE: " + QString::number(data["cwe"].toInt()) + "\n"); if (mUI.mTree->showIdColumn()) formattedMsg.prepend(tr("Id") + ": " + data["id"].toString() + "\n"); if (data["incomplete"].toBool()) formattedMsg += "\n" + tr("Bug hunting analysis is incomplete"); mUI.mDetails->setText(formattedMsg); const int lineNumber = data["line"].toInt(); QString filepath = data["file"].toString(); if (!QFileInfo(filepath).exists() && QFileInfo(mUI.mTree->getCheckDirectory() + '/' + filepath).exists()) filepath = mUI.mTree->getCheckDirectory() + '/' + filepath; QStringList symbols; if (data.contains("symbolNames")) symbols = data["symbolNames"].toString().split("\n"); if (filepath == mUI.mCode->getFileName()) { mUI.mCode->setError(lineNumber, symbols); return; } QFile file(filepath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { mUI.mCode->clear(); return; } QTextStream in(&file); mUI.mCode->setError(in.readAll(), lineNumber, symbols); mUI.mCode->setFileName(filepath); } void ResultsView::log(const QString &str) { mUI.mListLog->addItem(str); } void ResultsView::debugError(const ErrorItem &item) { mUI.mListLog->addItem(item.toString()); } void ResultsView::bughuntingReportLine(const QString& line) { for (const QString& s: line.split("\n")) { if (s.startsWith("[intvar] ")) { const QString varname = s.mid(9); if (!mVariableContracts.contains(varname)) { mVariableContracts.insert(varname); mUI.mListMissingVariables->addItem(varname); } } else if (s.startsWith("[missing contract] ")) { const QString functionName = s.mid(19); if (!mFunctionContracts.contains(functionName)) { mFunctionContracts.insert(functionName); mUI.mListMissingContracts->addItem(functionName); } } } } void ResultsView::logClear() { mUI.mListLog->clear(); } void ResultsView::logCopyEntry() { const QListWidgetItem * item = mUI.mListLog->currentItem(); if (nullptr != item) { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(item->text()); } } void ResultsView::logCopyComplete() { QString logText; for (int i=0; i < mUI.mListLog->count(); ++i) { const QListWidgetItem * item = mUI.mListLog->item(i); if (nullptr != item) { logText += item->text(); } } QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(logText); } void ResultsView::contractDoubleClicked(QListWidgetItem* item) { emit editFunctionContract(item->text()); } void ResultsView::variableDoubleClicked(QListWidgetItem* item) { emit editVariableContract(item->text()); } void ResultsView::editVariablesFilter(const QString &text) { for (auto *item: mUI.mListAddedVariables->findItems(".*", Qt::MatchRegExp)) { QString varname = item->text().mid(0, item->text().indexOf(" ")); item->setHidden(!varname.contains(text)); } for (auto *item: mUI.mListMissingVariables->findItems(".*", Qt::MatchRegExp)) item->setHidden(!item->text().contains(text)); } void ResultsView::on_mListLog_customContextMenuRequested(const QPoint &pos) { if (mUI.mListLog->count() <= 0) return; const QPoint globalPos = mUI.mListLog->mapToGlobal(pos); QMenu contextMenu; contextMenu.addAction(tr("Clear Log"), this, SLOT(logClear())); contextMenu.addAction(tr("Copy this Log entry"), this, SLOT(logCopyEntry())); contextMenu.addAction(tr("Copy complete Log"), this, SLOT(logCopyComplete())); contextMenu.exec(globalPos); } bool ResultsView::eventFilter(QObject *target, QEvent *event) { if (event->type() == QEvent::KeyPress) { if (target == mUI.mListAddedVariables) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Delete) { for (auto *i: mUI.mListAddedVariables->selectedItems()) { emit deleteVariableContract(i->text().mid(0, i->text().indexOf(" "))); delete i; } return true; } } if (target == mUI.mListAddedContracts) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Delete) { for (auto *i: mUI.mListAddedContracts->selectedItems()) { emit deleteFunctionContract(i->text()); delete i; } return true; } } } return QObject::eventFilter(target, event); } cppcheck-2.7/gui/resultsview.h000066400000000000000000000231141417746362400165120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef RESULTSVIEW_H #define RESULTSVIEW_H #include "ui_resultsview.h" #include "report.h" #include "showtypes.h" class ErrorItem; class ApplicationList; class QModelIndex; class QPrinter; class QSettings; class CheckStatistics; /// @addtogroup GUI /// @{ /** * @brief Widget to show cppcheck progressbar and result * */ class ResultsView : public QWidget { Q_OBJECT public: explicit ResultsView(QWidget * parent = nullptr); void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler); ResultsView(const ResultsView &) = delete; virtual ~ResultsView(); ResultsView &operator=(const ResultsView &) = delete; void setAddedFunctionContracts(const QStringList &addedContracts); void setAddedVariableContracts(const QStringList &added); /** * @brief Clear results and statistics and reset progressinfo. * @param results Remove all the results from view? */ void clear(bool results); /** * @brief Remove a file from the results. */ void clear(const QString &filename); /** * @brief Remove a recheck file from the results. */ void clearRecheckFile(const QString &filename); /** Clear the contracts */ void clearContracts(); /** * @brief Write statistics in file * * @param filename Filename to save statistics to */ void saveStatistics(const QString &filename) const; /** * @brief Save results to a file * * @param filename Filename to save results to * @param type Type of the report. */ void save(const QString &filename, Report::Type type) const; /** * @brief Update results from old report (tag, sinceDate) */ void updateFromOldReport(const QString &filename) const; /** * @brief Update tree settings * * @param showFullPath Show full path of files in the tree * @param saveFullPath Save full path of files in reports * @param saveAllErrors Save all visible errors * @param showNoErrorsMessage Show "no errors"? * @param showErrorId Show error id? * @param showInconclusive Show inconclusive? */ void updateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showNoErrorsMessage, bool showErrorId, bool showInconclusive); /** * @brief Update Code Editor Style * * Function will read updated Code Editor styling from * stored program settings. * * @param settings Pointer to QSettings Object */ void updateStyleSetting(QSettings *settings); /** * @brief Set the directory we are checking * * This is used to split error file path to relative if necessary * @param dir Directory we are checking */ void setCheckDirectory(const QString &dir); /** * @brief Get the directory we are checking * * @return Directory containing source files */ QString getCheckDirectory(); /** * @brief Inform the view that checking has started * * @param count Count of files to be checked. */ void checkingStarted(int count); /** * @brief Inform the view that checking finished. * */ void checkingFinished(); /** * @brief Do we have visible results to show? * * @return true if there is at least one warning/error to show. */ bool hasVisibleResults() const; /** * @brief Do we have results from check? * * @return true if there is at least one warning/error, hidden or visible. */ bool hasResults() const; /** * @brief Save View's settings * * @param settings program settings. */ void saveSettings(QSettings *settings); /** * @brief Translate this view * */ void translate(); void disableProgressbar(); /** * @brief Read errors from report XML file. * @param filename Report file to read. * */ void readErrorsXml(const QString &filename); /** * @brief Return checking statistics. * @return Pointer to checking statistics. */ CheckStatistics *getStatistics() const { return mStatistics; } /** * @brief Return Showtypes. * @return Pointer to Showtypes. */ ShowTypes * getShowTypes() const { return &mUI.mTree->mShowSeverities; } /** Show/hide the contract tabs */ void showContracts(bool visible); signals: /** * @brief Signal to be emitted when we have results * */ void gotResults(); /** * @brief Signal that results have been hidden or shown * * @param hidden true if there are some hidden results, or false if there are not */ void resultsHidden(bool hidden); /** * @brief Signal to perform recheck of selected files * * @param selectedFilesList list of selected files */ void checkSelected(QStringList selectedFilesList); /** Suppress Ids */ void suppressIds(QStringList ids); /** Edit contract for function */ void editFunctionContract(QString function); /** Delete contract for function */ void deleteFunctionContract(QString function); /** Edit contract for variable */ void editVariableContract(QString var); /** Delete variable contract */ void deleteVariableContract(QString var); /** * @brief Show/hide certain type of errors * Refreshes the tree. * * @param type Type of error to show/hide * @param show Should specified errors be shown (true) or hidden (false) */ void showResults(ShowTypes::ShowType type, bool show); /** * @brief Show/hide cppcheck errors. * Refreshes the tree. * * @param show Should specified errors be shown (true) or hidden (false) */ void showCppcheckResults(bool show); /** * @brief Show/hide clang-tidy/clang-analyzer errors. * Refreshes the tree. * * @param show Should specified errors be shown (true) or hidden (false) */ void showClangResults(bool show); /** * @brief Collapse all results in the result list. */ void collapseAllResults(); /** * @brief Expand all results in the result list. */ void expandAllResults(); /** * @brief Show hidden results in the result list. */ void showHiddenResults(); public slots: /** * @brief Slot for updating the checking progress * * @param value Current progress value * @param description Description to accompany the progress */ void progress(int value, const QString& description); /** * @brief Slot for new error to be displayed * * @param item Error data */ void error(const ErrorItem &item); /** * @brief Filters the results in the result list. */ void filterResults(const QString& filter); /** * @brief Update detailed message when selected item is changed. * * @param index Position of new selected item. */ void updateDetails(const QModelIndex &index); /** * @brief Slot opening a print dialog to print the current report */ void print(); /** * @brief Slot printing the current report to the printer. * @param printer The printer used for printing the report. */ void print(QPrinter* printer); /** * @brief Slot opening a print preview dialog */ void printPreview(); /** * \brief Log message */ void log(const QString &str); /** * \brief debug message */ void debugError(const ErrorItem &item); /** * \brief bughunting report line */ void bughuntingReportLine(const QString& line); /** * \brief Clear log messages */ void logClear(); /** * \brief Copy selected log message entry */ void logCopyEntry(); /** * \brief Copy all log messages */ void logCopyComplete(); /** \brief Contract was double clicked => edit it */ void contractDoubleClicked(QListWidgetItem* item); /** \brief Variable was double clicked => edit it */ void variableDoubleClicked(QListWidgetItem* item); void editVariablesFilter(const QString &text); protected: /** * @brief Should we show a "No errors found dialog" every time no errors were found? */ bool mShowNoErrorsMessage; Ui::ResultsView mUI; CheckStatistics *mStatistics; bool eventFilter(QObject *target, QEvent *event); private slots: /** * @brief Custom context menu for Analysis Log * @param pos Mouse click position */ void on_mListLog_customContextMenuRequested(const QPoint &pos); private: QSet mFunctionContracts; QSet mVariableContracts; /** Current file shown in the code editor */ QString mCurrentFileName; }; /// @} #endif // RESULTSVIEW_H cppcheck-2.7/gui/resultsview.ui000066400000000000000000000202011417746362400166720ustar00rootroot00000000000000 ResultsView 0 0 459 391 0 0 16777215 16777215 Results 0 0 0 0 0 0 24 Qt::Vertical 0 0 QAbstractItemView::NoEditTriggers QAbstractItemView::ExtendedSelection QTabWidget::South 1 Analysis Log 0 0 0 0 Warning Details 0 0 0 0 0 1 false true 0 4 false QPlainTextEdit::NoWrap true Functions Configured contracts: QAbstractItemView::NoEditTriggers Missing contracts: QAbstractItemView::NoEditTriggers true Variables Qt::Horizontal 40 20 Only show variable names that contain text: Configured contracts: QAbstractItemView::NoEditTriggers Missing contracts: QAbstractItemView::NoEditTriggers true ResultsTree QTreeView
resultstree.h
CodeEditor QPlainTextEdit
codeeditor.h
mTree mDetails
cppcheck-2.7/gui/scratchpad.cpp000066400000000000000000000024351417746362400165700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "scratchpad.h" #include "mainwindow.h" ScratchPad::ScratchPad(MainWindow& mainWindow) : QDialog(&mainWindow) , mMainWindow(mainWindow) { mUI.setupUi(this); connect(mUI.mCheckButton, &QPushButton::clicked, this, &ScratchPad::checkButtonClicked); } void ScratchPad::translate() { mUI.retranslateUi(this); } void ScratchPad::checkButtonClicked() { QString filename = mUI.lineEdit->text(); if (filename.isEmpty()) filename = "test.cpp"; mMainWindow.analyzeCode(mUI.plainTextEdit->toPlainText(), filename); } cppcheck-2.7/gui/scratchpad.h000066400000000000000000000024611417746362400162340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SCRATCHPAD_H #define SCRATCHPAD_H #include "ui_scratchpad.h" #include class MainWindow; /// @addtogroup GUI /// @{ /** * @brief A window with a text field that . */ class ScratchPad : public QDialog { Q_OBJECT public: explicit ScratchPad(MainWindow& mainWindow); /** * @brief Translate dialog */ void translate(); private slots: /** * @brief Called when check button is clicked. */ void checkButtonClicked(); private: Ui::ScratchPad mUI; MainWindow& mMainWindow; }; /// @} #endif // SCRATCHPAD_H cppcheck-2.7/gui/scratchpad.ui000066400000000000000000000064271417746362400164300ustar00rootroot00000000000000 ScratchPad 0 0 574 600 Scratchpad Copy or write some C/C++ code here: Courier New 10 Optionally enter a filename (mainly for automatic language detection) and click on "Check": 0 0 0 0 Qt::Horizontal 40 20 filename Check 0 0 Qt::Horizontal QDialogButtonBox::Close CodeEditor QPlainTextEdit
codeeditor.h
mButtonBox rejected() ScratchPad reject() 20 20 20 20
cppcheck-2.7/gui/settings.ui000066400000000000000000000431301417746362400161440ustar00rootroot00000000000000 Settings 0 0 589 346 Preferences 0 General QLayout::SetDefaultConstraint 0 0 Number of threads: mJobs 0 0 100 20 100 20 009 3 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Ideal count: TextLabel Qt::Horizontal 40 20 Force checking all #ifdef configurations Show full path of files Show "No errors found" message when no errors found Display error Id in column "Id" Enable inline suppressions Check for inconclusive errors also Show statistics on check completion Qt::Vertical 20 40 Show internal warnings in log Applications Add... Edit... Remove Set as default Qt::Vertical 20 40 Reports Save all errors when creating report Save full path to files in reports Qt::Vertical 20 40 Language QAbstractItemView::SelectRows Addons Python binary (leave this empty to use python in the PATH) ... MISRA addon MISRA rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... Qt::Vertical 20 168 Clang Clang path (leave empty to use system PATH) false ... Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> true Qt::Vertical 20 50 Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Edit... Qt::Horizontal 40 20 Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok tabWidget mJobs mForce mShowFullPath mShowNoErrorsMessage mInlineSuppressions mListWidget mBtnAddApplication mBtnEditApplication mBtnRemoveApplication mBtnDefaultApplication mSaveAllErrors mSaveFullPath mListLanguages mEnableInconclusive mShowStatistics mShowDebugWarnings mButtons mButtons accepted() Settings accept() 257 336 157 274 mButtons rejected() Settings reject() 325 336 286 274 cppcheck-2.7/gui/settingsdialog.cpp000066400000000000000000000351461417746362400175010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "settingsdialog.h" #include "applicationdialog.h" #include "applicationlist.h" #include "codeeditorstyle.h" #include "codeeditstyledialog.h" #include "common.h" #include "translationhandler.h" #include #include #include #include #include #include SettingsDialog::SettingsDialog(ApplicationList *list, TranslationHandler *translator, QWidget *parent) : QDialog(parent), mApplications(list), mTempApplications(new ApplicationList(this)), mTranslator(translator) { mUI.setupUi(this); mUI.mPythonPathWarning->setStyleSheet("color: red"); QSettings settings; mTempApplications->copy(list); mUI.mJobs->setText(settings.value(SETTINGS_CHECK_THREADS, 1).toString()); mUI.mForce->setCheckState(boolToCheckState(settings.value(SETTINGS_CHECK_FORCE, false).toBool())); mUI.mShowFullPath->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_FULL_PATH, false).toBool())); mUI.mShowNoErrorsMessage->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_NO_ERRORS, false).toBool())); mUI.mShowDebugWarnings->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool())); mUI.mSaveAllErrors->setCheckState(boolToCheckState(settings.value(SETTINGS_SAVE_ALL_ERRORS, false).toBool())); mUI.mSaveFullPath->setCheckState(boolToCheckState(settings.value(SETTINGS_SAVE_FULL_PATH, false).toBool())); mUI.mInlineSuppressions->setCheckState(boolToCheckState(settings.value(SETTINGS_INLINE_SUPPRESSIONS, false).toBool())); mUI.mEnableInconclusive->setCheckState(boolToCheckState(settings.value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool())); mUI.mShowStatistics->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_STATISTICS, false).toBool())); mUI.mShowErrorId->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_ERROR_ID, false).toBool())); mUI.mEditPythonPath->setText(settings.value(SETTINGS_PYTHON_PATH, QString()).toString()); validateEditPythonPath(); mUI.mEditMisraFile->setText(settings.value(SETTINGS_MISRA_FILE, QString()).toString()); #ifdef Q_OS_WIN //mUI.mTabClang->setVisible(true); mUI.mEditClangPath->setText(settings.value(SETTINGS_CLANG_PATH, QString()).toString()); mUI.mEditVsIncludePaths->setText(settings.value(SETTINGS_VS_INCLUDE_PATHS, QString()).toString()); connect(mUI.mBtnBrowseClangPath, &QPushButton::released, this, &SettingsDialog::browseClangPath); #else mUI.mTabClang->setVisible(false); #endif mCurrentStyle = new CodeEditorStyle(CodeEditorStyle::loadSettings(&settings)); manageStyleControls(); connect(mUI.mEditPythonPath, SIGNAL(textEdited(const QString&)), this, SLOT(validateEditPythonPath())); connect(mUI.mButtons, &QDialogButtonBox::accepted, this, &SettingsDialog::ok); connect(mUI.mButtons, &QDialogButtonBox::rejected, this, &SettingsDialog::reject); connect(mUI.mBtnAddApplication, SIGNAL(clicked()), this, SLOT(addApplication())); connect(mUI.mBtnRemoveApplication, SIGNAL(clicked()), this, SLOT(removeApplication())); connect(mUI.mBtnEditApplication, SIGNAL(clicked()), this, SLOT(editApplication())); connect(mUI.mBtnDefaultApplication, SIGNAL(clicked()), this, SLOT(defaultApplication())); connect(mUI.mListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(editApplication())); connect(mUI.mBtnBrowsePythonPath, &QPushButton::clicked, this, &SettingsDialog::browsePythonPath); connect(mUI.mBtnBrowseMisraFile, &QPushButton::clicked, this, &SettingsDialog::browseMisraFile); connect(mUI.mBtnEditTheme, SIGNAL(clicked()), this, SLOT(editCodeEditorStyle())); connect(mUI.mThemeSystem, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault())); connect(mUI.mThemeDark, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault())); connect(mUI.mThemeLight, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault())); connect(mUI.mThemeCustom, SIGNAL(toggled(bool)), mUI.mBtnEditTheme, SLOT(setEnabled(bool))); mUI.mListWidget->setSortingEnabled(false); populateApplicationList(); const int count = QThread::idealThreadCount(); if (count != -1) mUI.mLblIdealThreads->setText(QString::number(count)); else mUI.mLblIdealThreads->setText(tr("N/A")); loadSettings(); initTranslationsList(); } SettingsDialog::~SettingsDialog() { saveSettings(); } void SettingsDialog::initTranslationsList() { const QString current = mTranslator->getCurrentLanguage(); QList translations = mTranslator->getTranslations(); foreach (TranslationInfo translation, translations) { QListWidgetItem *item = new QListWidgetItem; item->setText(translation.mName); item->setData(mLangCodeRole, QVariant(translation.mCode)); mUI.mListLanguages->addItem(item); if (translation.mCode == current || translation.mCode == current.mid(0, 2)) mUI.mListLanguages->setCurrentItem(item); } } Qt::CheckState SettingsDialog::boolToCheckState(bool yes) { if (yes) { return Qt::Checked; } return Qt::Unchecked; } bool SettingsDialog::checkStateToBool(Qt::CheckState state) { if (state == Qt::Checked) { return true; } return false; } void SettingsDialog::loadSettings() { QSettings settings; resize(settings.value(SETTINGS_CHECK_DIALOG_WIDTH, 800).toInt(), settings.value(SETTINGS_CHECK_DIALOG_HEIGHT, 600).toInt()); } void SettingsDialog::saveSettings() const { QSettings settings; settings.setValue(SETTINGS_CHECK_DIALOG_WIDTH, size().width()); settings.setValue(SETTINGS_CHECK_DIALOG_HEIGHT, size().height()); } void SettingsDialog::saveSettingValues() const { int jobs = mUI.mJobs->text().toInt(); if (jobs <= 0) { jobs = 1; } QSettings settings; settings.setValue(SETTINGS_CHECK_THREADS, jobs); saveCheckboxValue(&settings, mUI.mForce, SETTINGS_CHECK_FORCE); saveCheckboxValue(&settings, mUI.mSaveAllErrors, SETTINGS_SAVE_ALL_ERRORS); saveCheckboxValue(&settings, mUI.mSaveFullPath, SETTINGS_SAVE_FULL_PATH); saveCheckboxValue(&settings, mUI.mShowFullPath, SETTINGS_SHOW_FULL_PATH); saveCheckboxValue(&settings, mUI.mShowNoErrorsMessage, SETTINGS_SHOW_NO_ERRORS); saveCheckboxValue(&settings, mUI.mShowDebugWarnings, SETTINGS_SHOW_DEBUG_WARNINGS); saveCheckboxValue(&settings, mUI.mInlineSuppressions, SETTINGS_INLINE_SUPPRESSIONS); saveCheckboxValue(&settings, mUI.mEnableInconclusive, SETTINGS_INCONCLUSIVE_ERRORS); saveCheckboxValue(&settings, mUI.mShowStatistics, SETTINGS_SHOW_STATISTICS); saveCheckboxValue(&settings, mUI.mShowErrorId, SETTINGS_SHOW_ERROR_ID); settings.setValue(SETTINGS_PYTHON_PATH, mUI.mEditPythonPath->text()); settings.setValue(SETTINGS_MISRA_FILE, mUI.mEditMisraFile->text()); #ifdef Q_OS_WIN settings.setValue(SETTINGS_CLANG_PATH, mUI.mEditClangPath->text()); settings.setValue(SETTINGS_VS_INCLUDE_PATHS, mUI.mEditVsIncludePaths->text()); #endif const QListWidgetItem *currentLang = mUI.mListLanguages->currentItem(); if (currentLang) { const QString langcode = currentLang->data(mLangCodeRole).toString(); settings.setValue(SETTINGS_LANGUAGE, langcode); } CodeEditorStyle::saveSettings(&settings, *mCurrentStyle); } void SettingsDialog::saveCheckboxValue(QSettings *settings, QCheckBox *box, const QString &name) { settings->setValue(name, checkStateToBool(box->checkState())); } void SettingsDialog::validateEditPythonPath() { const auto pythonPath = mUI.mEditPythonPath->text(); if (pythonPath.isEmpty()) { mUI.mEditPythonPath->setStyleSheet(""); mUI.mPythonPathWarning->hide(); return; } QFileInfo pythonPathInfo(pythonPath); if (!pythonPathInfo.exists() || !pythonPathInfo.isFile() || !pythonPathInfo.isExecutable()) { mUI.mEditPythonPath->setStyleSheet("QLineEdit {border: 1px solid red}"); mUI.mPythonPathWarning->setText(tr("The executable file \"%1\" is not available").arg(pythonPath)); mUI.mPythonPathWarning->show(); } else { mUI.mEditPythonPath->setStyleSheet(""); mUI.mPythonPathWarning->hide(); } } void SettingsDialog::addApplication() { Application app; ApplicationDialog dialog(tr("Add a new application"), app, this); if (dialog.exec() == QDialog::Accepted) { mTempApplications->addApplication(app); mUI.mListWidget->addItem(app.getName()); } } void SettingsDialog::removeApplication() { QList selected = mUI.mListWidget->selectedItems(); foreach (QListWidgetItem *item, selected) { const int removeIndex = mUI.mListWidget->row(item); const int currentDefault = mTempApplications->getDefaultApplication(); mTempApplications->removeApplication(removeIndex); if (removeIndex == currentDefault) // If default app is removed set default to unknown mTempApplications->setDefault(-1); else if (removeIndex < currentDefault) // Move default app one up if earlier app was removed mTempApplications->setDefault(currentDefault - 1); } mUI.mListWidget->clear(); populateApplicationList(); } void SettingsDialog::editApplication() { QList selected = mUI.mListWidget->selectedItems(); QListWidgetItem *item = nullptr; foreach (item, selected) { int row = mUI.mListWidget->row(item); Application& app = mTempApplications->getApplication(row); ApplicationDialog dialog(tr("Modify an application"), app, this); if (dialog.exec() == QDialog::Accepted) { QString name = app.getName(); if (mTempApplications->getDefaultApplication() == row) name += tr(" [Default]"); item->setText(name); } } } void SettingsDialog::defaultApplication() { QList selected = mUI.mListWidget->selectedItems(); if (!selected.isEmpty()) { int index = mUI.mListWidget->row(selected[0]); mTempApplications->setDefault(index); mUI.mListWidget->clear(); populateApplicationList(); } } void SettingsDialog::populateApplicationList() { const int defapp = mTempApplications->getDefaultApplication(); for (int i = 0; i < mTempApplications->getApplicationCount(); i++) { const Application& app = mTempApplications->getApplication(i); QString name = app.getName(); if (i == defapp) { name += " "; name += tr("[Default]"); } mUI.mListWidget->addItem(name); } // Select default application, or if there is no default app then the // first item. if (defapp == -1) mUI.mListWidget->setCurrentRow(0); else { if (mTempApplications->getApplicationCount() > defapp) mUI.mListWidget->setCurrentRow(defapp); else mUI.mListWidget->setCurrentRow(0); } } void SettingsDialog::ok() { mApplications->copy(mTempApplications); accept(); } bool SettingsDialog::showFullPath() const { return checkStateToBool(mUI.mShowFullPath->checkState()); } bool SettingsDialog::saveFullPath() const { return checkStateToBool(mUI.mSaveFullPath->checkState()); } bool SettingsDialog::saveAllErrors() const { return checkStateToBool(mUI.mSaveAllErrors->checkState()); } bool SettingsDialog::showNoErrorsMessage() const { return checkStateToBool(mUI.mShowNoErrorsMessage->checkState()); } bool SettingsDialog::showErrorId() const { return checkStateToBool(mUI.mShowErrorId->checkState()); } bool SettingsDialog::showInconclusive() const { return checkStateToBool(mUI.mEnableInconclusive->checkState()); } void SettingsDialog::browsePythonPath() { QString fileName = QFileDialog::getOpenFileName(this, tr("Select python binary"), QDir::rootPath()); if (fileName.contains("python", Qt::CaseInsensitive)) mUI.mEditPythonPath->setText(fileName); } void SettingsDialog::browseMisraFile() { const QString fileName = QFileDialog::getOpenFileName(this, tr("Select MISRA File"), QDir::homePath(), "Misra File (*.pdf *.txt)"); if (!fileName.isEmpty()) mUI.mEditMisraFile->setText(fileName); } // Slot to set default light style void SettingsDialog::setCodeEditorStyleDefault() { if (mUI.mThemeSystem->isChecked()) *mCurrentStyle = CodeEditorStyle::getSystemTheme(); if (mUI.mThemeLight->isChecked()) *mCurrentStyle = defaultStyleLight; if (mUI.mThemeDark->isChecked()) *mCurrentStyle = defaultStyleDark; manageStyleControls(); } // Slot to edit custom style void SettingsDialog::editCodeEditorStyle() { StyleEditDialog dlg(*mCurrentStyle, this); int nResult = dlg.exec(); if (nResult == QDialog::Accepted) { *mCurrentStyle = dlg.getStyle(); manageStyleControls(); } } void SettingsDialog::browseClangPath() { QString selectedDir = QFileDialog::getExistingDirectory(this, tr("Select clang path"), QDir::rootPath()); if (!selectedDir.isEmpty()) { mUI.mEditClangPath->setText(selectedDir); } } void SettingsDialog::manageStyleControls() { bool isSystemTheme = mCurrentStyle->isSystemTheme(); bool isDefaultLight = !isSystemTheme && *mCurrentStyle == defaultStyleLight; bool isDefaultDark = !isSystemTheme && *mCurrentStyle == defaultStyleDark; mUI.mThemeSystem->setChecked(isSystemTheme); mUI.mThemeLight->setChecked(isDefaultLight && !isDefaultDark); mUI.mThemeDark->setChecked(!isDefaultLight && isDefaultDark); mUI.mThemeCustom->setChecked(!isSystemTheme && !isDefaultLight && !isDefaultDark); mUI.mBtnEditTheme->setEnabled(!isSystemTheme && !isDefaultLight && !isDefaultDark); } cppcheck-2.7/gui/settingsdialog.h000066400000000000000000000126321417746362400171410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H #include "ui_settings.h" #include class QSettings; class QWidget; class ApplicationList; class TranslationHandler; class CodeEditorStyle; /// @addtogroup GUI /// @{ /** * @brief Settings dialog * */ class SettingsDialog : public QDialog { Q_OBJECT public: SettingsDialog(ApplicationList *list, TranslationHandler *translator, QWidget *parent = nullptr); SettingsDialog(const SettingsDialog &) = delete; virtual ~SettingsDialog(); SettingsDialog &operator=(const SettingsDialog &) = delete; /** * @brief Save all values to QSettings * */ void saveSettingValues() const; /** * @brief Get checkbox value for mShowFullPath * * @return should full path of errors be shown in the tree */ bool showFullPath() const; /** * @brief Get checkbox value for mSaveFullPath * * @return should full path of files be saved when creating a report */ bool saveFullPath() const; /** * @brief Get checkbox value for mNoErrorsMessage * * @return Should "no errors message" be hidden */ bool showNoErrorsMessage() const; /** * @brief Get checkbox value for mShowIdColumn * * @return Should error id column be displayed */ bool showErrorId() const; /** * @brief Get checkbox value for mEnableInconclusive * * @return Should inconclusive column be displayed */ bool showInconclusive() const; /** * @brief Get checkbox value for mSaveAllErrors * * @return should all errors be saved to report */ bool saveAllErrors() const; protected slots: /** * @brief Slot for clicking OK. * */ void ok(); /** @brief Slot for validating input value in @c editPythonPath */ void validateEditPythonPath(); /** * @brief Slot for adding a new application to the list * */ void addApplication(); /** * @brief Slot for deleting an application from the list * */ void removeApplication(); /** * @brief Slot for modifying an application in the list * */ void editApplication(); /** * @brief Slot for making the selected application as the default (first) * */ void defaultApplication(); /** @brief Slot for browsing for the python binary */ void browsePythonPath(); /** @brief Slot for browsing for the clang binary */ void browseClangPath(); /** * @brief Browse for MISRA file */ void browseMisraFile(); /** * @brief Set Code Editor Style to Default */ void setCodeEditorStyleDefault(); /** * @brief Edit Custom Code Editor Style */ void editCodeEditorStyle(); protected: /** * @brief Clear all applications from the list and re insert them from mTempApplications * */ void populateApplicationList(); /** * @brief Load saved values * Loads dialog size and column widths. * */ void loadSettings(); /** * @brief Save settings * Save dialog size and column widths. */ void saveSettings() const; /** * @brief Save a single checkboxes value * * @param settings Pointer to Settings. * @param box checkbox to save * @param name name for QSettings to store the value */ static void saveCheckboxValue(QSettings *settings, QCheckBox *box, const QString &name); /** * @brief Convert bool to Qt::CheckState * * @param yes value to convert * @return value converted to Qt::CheckState */ static Qt::CheckState boolToCheckState(bool yes); /** * @brief Converts Qt::CheckState to bool * * @param state Qt::CheckState to convert * @return converted value */ static bool checkStateToBool(Qt::CheckState state); /** * @brief Populate the translations list. */ void initTranslationsList(); /** * @brief Current Code Editor Style */ CodeEditorStyle *mCurrentStyle; /** * @brief List of applications user has specified * */ ApplicationList *mApplications; /** * @brief Temporary list of applications * This will be copied to actual list of applications (mApplications) * when user clicks ok. */ ApplicationList *mTempApplications; /** * @brief List of translations. * */ TranslationHandler *mTranslator; /** * @brief Dialog from UI designer * */ Ui::Settings mUI; private: void manageStyleControls(); static const int mLangCodeRole = Qt::UserRole; }; /// @} #endif // SETTINGSDIALOG_H cppcheck-2.7/gui/showtypes.cpp000066400000000000000000000073371417746362400165270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "showtypes.h" #include "common.h" #include ShowTypes::ShowTypes() { load(); } ShowTypes::~ShowTypes() { save(); } ShowTypes::ShowType ShowTypes::SeverityToShowType(Severity::SeverityType severity) { switch (severity) { case Severity::none: return ShowTypes::ShowNone; case Severity::error: return ShowTypes::ShowErrors; case Severity::style: return ShowTypes::ShowStyle; case Severity::warning: return ShowTypes::ShowWarnings; case Severity::performance: return ShowTypes::ShowPerformance; case Severity::portability: return ShowTypes::ShowPortability; case Severity::information: return ShowTypes::ShowInformation; default: return ShowTypes::ShowNone; } } Severity::SeverityType ShowTypes::ShowTypeToSeverity(ShowTypes::ShowType type) { switch (type) { case ShowTypes::ShowStyle: return Severity::style; case ShowTypes::ShowErrors: return Severity::error; case ShowTypes::ShowWarnings: return Severity::warning; case ShowTypes::ShowPerformance: return Severity::performance; case ShowTypes::ShowPortability: return Severity::portability; case ShowTypes::ShowInformation: return Severity::information; case ShowTypes::ShowNone: default: return Severity::none; } } ShowTypes::ShowType ShowTypes::VariantToShowType(const QVariant &data) { const int value = data.toInt(); if (value < ShowTypes::ShowStyle || value > ShowTypes::ShowErrors) { return ShowTypes::ShowNone; } return (ShowTypes::ShowType)value; } void ShowTypes::load() { QSettings settings; mVisible[ShowStyle] = settings.value(SETTINGS_SHOW_STYLE, true).toBool(); mVisible[ShowErrors] = settings.value(SETTINGS_SHOW_ERRORS, true).toBool(); mVisible[ShowWarnings] = settings.value(SETTINGS_SHOW_WARNINGS, true).toBool(); mVisible[ShowPortability] = settings.value(SETTINGS_SHOW_PORTABILITY, true).toBool(); mVisible[ShowPerformance] = settings.value(SETTINGS_SHOW_PERFORMANCE, true).toBool(); mVisible[ShowInformation] = settings.value(SETTINGS_SHOW_INFORMATION, true).toBool(); } void ShowTypes::save() const { QSettings settings; settings.setValue(SETTINGS_SHOW_STYLE, mVisible[ShowStyle]); settings.setValue(SETTINGS_SHOW_ERRORS, mVisible[ShowErrors]); settings.setValue(SETTINGS_SHOW_WARNINGS, mVisible[ShowWarnings]); settings.setValue(SETTINGS_SHOW_PORTABILITY, mVisible[ShowPortability]); settings.setValue(SETTINGS_SHOW_PERFORMANCE, mVisible[ShowPerformance]); settings.setValue(SETTINGS_SHOW_INFORMATION, mVisible[ShowInformation]); } bool ShowTypes::isShown(ShowTypes::ShowType category) const { return mVisible[category]; } bool ShowTypes::isShown(Severity::SeverityType severity) const { return isShown(ShowTypes::SeverityToShowType(severity)); } void ShowTypes::show(ShowTypes::ShowType category, bool showing) { mVisible[category] = showing; } cppcheck-2.7/gui/showtypes.h000066400000000000000000000066351417746362400161740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SHOWTYPES_H #define SHOWTYPES_H #include "errortypes.h" #include /// @addtogroup GUI /// @{ /** * @brief A class for different show types we have. * This class contains enum type for the different show types we have. Each * show type presents one severity selectable in the GUI. In addition there * are several supporting functions. * * Notice that the "visibility" settings are automatically loaded when the * class is constructed and saved when the class is destroyed. */ class ShowTypes { public: /** * @brief Show types we have (i.e. severities in the GUI). */ enum ShowType { ShowStyle = 0, ShowWarnings, ShowPerformance, ShowPortability, ShowInformation, ShowErrors, // Keep this as last real item ShowNone }; /** * @brief Constructor. * @note Loads visibility settings. */ ShowTypes(); /** * @brief Destructor. * @note Saves visibility settings. */ ~ShowTypes(); /** * @brief Load visibility settings from the platform's settings storage. */ void load(); /** * @brief Save visibility settings to the platform's settings storage. */ void save() const; /** * @brief Is the showtype visible in the GUI? * @param category Showtype to check. * @return true if the showtype is visible. */ bool isShown(ShowTypes::ShowType category) const; /** * @brief Is the severity visible in the GUI? * @param severity severity to check. * @return true if the severity is visible. */ bool isShown(Severity::SeverityType severity) const; /** * @brief Show/hide the showtype. * @param category Showtype whose visibility to set. * @param showing true if the severity is set visible. */ void show(ShowTypes::ShowType category, bool showing); /** * @brief Convert severity string to ShowTypes value * @param severity Error severity * @return Severity converted to ShowTypes value */ static ShowTypes::ShowType SeverityToShowType(Severity::SeverityType severity); /** * @brief Convert ShowType to severity string * @param type ShowType to convert * @return ShowType converted to severity */ static Severity::SeverityType ShowTypeToSeverity(ShowTypes::ShowType type); /** * @brief Convert QVariant (that contains an int) to Showtypes value * * @param data QVariant (that contains an int) to be converted * @return data converted to ShowTypes */ static ShowTypes::ShowType VariantToShowType(const QVariant &data); bool mVisible[ShowNone]; }; /// @} #endif // SHOWTYPES_H cppcheck-2.7/gui/stats.ui000066400000000000000000000331531417746362400154460ustar00rootroot00000000000000 StatsDialog 0 0 502 274 Statistics QTabWidget::Rounded 0 Project Project: 0 0 true Paths: true 0 0 true false true true Include paths: true 0 0 true Defines: true 0 0 true Undefines: 0 0 true Qt::Vertical 20 40 Previous Scan Path Selected: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Number of Files Scanned: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing Scan Duration: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing Qt::Vertical 20 40 0 0 true Statistics Errors: TextLabel Warnings: TextLabel Stylistic warnings: TextLabel Portability warnings: TextLabel Performance issues: TextLabel Information messages: TextLabel History File: 0 0 0 0 Qt::Horizontal 40 20 Copy to Clipboard Pdf Export 0 0 Qt::Horizontal QDialogButtonBox::Close mTabWidget mProject mPaths mIncludePaths mDefines mCopyToClipboard mButtonBox mPath mButtonBox accepted() StatsDialog accept() 483 310 157 274 mButtonBox rejected() StatsDialog reject() 483 310 286 274 cppcheck-2.7/gui/statsdialog.cpp000066400000000000000000000377341417746362400170040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "statsdialog.h" #include "checkstatistics.h" #include "common.h" #include "projectfile.h" #include #include #include #include #include #include #include #include #ifdef HAVE_QCHART #include #include #include #include #include #include using namespace QtCharts; #endif static const QString CPPCHECK("cppcheck"); StatsDialog::StatsDialog(QWidget *parent) : QDialog(parent), mStatistics(nullptr) { mUI.setupUi(this); setWindowFlags(Qt::Window); connect(mUI.mCopyToClipboard, &QPushButton::pressed, this, &StatsDialog::copyToClipboard); connect(mUI.mPDFexport, &QPushButton::pressed, this, &StatsDialog::pdfExport); } void StatsDialog::setProject(const ProjectFile* projectFile) { if (projectFile) { mUI.mProject->setText(projectFile->getRootPath()); mUI.mPaths->setText(projectFile->getCheckPaths().join(";")); mUI.mIncludePaths->setText(projectFile->getIncludeDirs().join(";")); mUI.mDefines->setText(projectFile->getDefines().join(";")); mUI.mUndefines->setText(projectFile->getUndefines().join(";")); #ifndef HAVE_QCHART mUI.mTabHistory->setVisible(false); #else QString statsFile; if (!projectFile->getBuildDir().isEmpty()) { const QString prjpath = QFileInfo(projectFile->getFilename()).absolutePath(); const QString buildDir = prjpath + '/' + projectFile->getBuildDir(); if (QDir(buildDir).exists()) { statsFile = buildDir + "/statistics.txt"; } } mUI.mLblHistoryFile->setText(tr("File: ") + (statsFile.isEmpty() ? tr("No cppcheck build dir") : statsFile)); if (!statsFile.isEmpty()) { QChartView *chartView; chartView = createChart(statsFile, "cppcheck"); mUI.mTabHistory->layout()->addWidget(chartView); if (projectFile->getClangAnalyzer()) { chartView = createChart(statsFile, CLANG_ANALYZER); mUI.mTabHistory->layout()->addWidget(chartView); } if (projectFile->getClangTidy()) { chartView = createChart(statsFile, CLANG_TIDY); mUI.mTabHistory->layout()->addWidget(chartView); } } #endif } else { mUI.mProject->setText(QString()); mUI.mPaths->setText(QString()); mUI.mIncludePaths->setText(QString()); mUI.mDefines->setText(QString()); mUI.mUndefines->setText(QString()); } } void StatsDialog::setPathSelected(const QString& path) { mUI.mPath->setText(path); } void StatsDialog::setNumberOfFilesScanned(int num) { mUI.mNumberOfFilesScanned->setText(QString::number(num)); } void StatsDialog::setScanDuration(double seconds) { // Factor the duration into units (days/hours/minutes/seconds) int secs = seconds; int days = secs / (24 * 60 * 60); secs -= days * (24 * 60 * 60); int hours = secs / (60 * 60); secs -= hours * (60 * 60); int mins = secs / 60; secs -= mins * 60; // Concatenate the two most significant units (e.g. "1 day and 3 hours") QStringList parts; if (days) parts << ((days == 1) ? tr("1 day") : tr("%1 days").arg(days)); if (hours) parts << ((hours == 1) ? tr("1 hour") : tr("%1 hours").arg(hours)); if (mins && parts.size() < 2) parts << ((mins == 1) ? tr("1 minute") : tr("%1 minutes").arg(mins)); if (secs && parts.size() < 2) parts << ((secs == 1) ? tr("1 second") : tr("%1 seconds").arg(secs)); // For durations < 1s, show the fraction of a second (e.g. "0.7 seconds") if (parts.isEmpty()) parts << tr("0.%1 seconds").arg(int(10.0 *(seconds - secs))); mUI.mScanDuration->setText(parts.join(tr(" and "))); } void StatsDialog::pdfExport() { const QString Stat = QString( "

%1 %2

\n" "

%3 : %4

\n" "

%5 : %6

\n" "

%7 : %8

\n" "

%9 : %10

\n" "

%11 : %12

\n" "

%13 : %14

\n") .arg(tr("Statistics")) .arg(QDate::currentDate().toString("dd.MM.yyyy")) .arg(tr("Errors")) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors)) .arg(tr("Warnings")) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings)) .arg(tr("Style warnings")) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle)) .arg(tr("Portability warnings")) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability)) .arg(tr("Performance warnings")) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance)) .arg(tr("Information messages")) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation)); QString fileName = QFileDialog::getSaveFileName((QWidget*)nullptr, tr("Export PDF"), QString(), "*.pdf"); if (QFileInfo(fileName).suffix().isEmpty()) { fileName.append(".pdf"); } QPrinter printer(QPrinter::PrinterResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setPageSize(QPageSize(QPageSize::A4)); printer.setOutputFileName(fileName); QTextDocument doc; doc.setHtml(Stat); // doc.setPageSize(printer.pageRect().size()); doc.print(&printer); } void StatsDialog::copyToClipboard() { QClipboard *clipboard = QApplication::clipboard(); if (!clipboard) return; const QString projSettings(tr("Project Settings")); const QString project(tr("Project")); const QString paths(tr("Paths")); const QString incPaths(tr("Include paths")); const QString defines(tr("Defines")); const QString undefines(tr("Undefines")); const QString prevScan(tr("Previous Scan")); const QString selPath(tr("Path selected")); const QString numFiles(tr("Number of files scanned")); const QString duration(tr("Scan duration")); const QString stats(tr("Statistics")); const QString errors(tr("Errors")); const QString warnings(tr("Warnings")); const QString style(tr("Style warnings")); const QString portability(tr("Portability warnings")); const QString performance(tr("Performance warnings")); const QString information(tr("Information messages")); // Plain text summary const QString settings = QString( "%1\n" "\t%2:\t%3\n" "\t%4:\t%5\n" "\t%6:\t%7\n" "\t%8:\t%9\n" "\t%10:\t%11\n" ) .arg(projSettings) .arg(project) .arg(mUI.mProject->text()) .arg(paths) .arg(mUI.mPaths->text()) .arg(incPaths) .arg(mUI.mIncludePaths->text()) .arg(defines) .arg(mUI.mDefines->text()) .arg(undefines) .arg(mUI.mUndefines->text()); const QString previous = QString( "%1\n" "\t%2:\t%3\n" "\t%4:\t%5\n" "\t%6:\t%7\n" ) .arg(prevScan) .arg(selPath) .arg(mUI.mPath->text()) .arg(numFiles) .arg(mUI.mNumberOfFilesScanned->text()) .arg(duration) .arg(mUI.mScanDuration->text()); const QString statistics = QString( "%1\n" "\t%2:\t%3\n" "\t%4:\t%5\n" "\t%6:\t%7\n" "\t%8:\t%9\n" "\t%10:\t%11\n" "\t%12:\t%13\n" ) .arg(stats) .arg(errors) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors)) .arg(warnings) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings)) .arg(style) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle)) .arg(portability) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability)) .arg(performance) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance)) .arg(information) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation)); const QString textSummary = settings + previous + statistics; // HTML summary const QString htmlSettings = QString( "

%1

\n" "\n" " \n" " \n" " \n" " \n" " \n" "
%2:%3
%4:%5
%6:%7
%8:%9
%10:%11
\n" ) .arg(projSettings) .arg(project) .arg(mUI.mProject->text()) .arg(paths) .arg(mUI.mPaths->text()) .arg(incPaths) .arg(mUI.mIncludePaths->text()) .arg(defines) .arg(mUI.mDefines->text()) .arg(undefines) .arg(mUI.mUndefines->text()); const QString htmlPrevious = QString( "

%1

\n" "\n" " \n" " \n" " \n" "
%2:%3
%4:%5
%6:%7
\n" ) .arg(prevScan) .arg(selPath) .arg(mUI.mPath->text()) .arg(numFiles) .arg(mUI.mNumberOfFilesScanned->text()) .arg(duration) .arg(mUI.mScanDuration->text()); const QString htmlStatistics = QString( "

%1

\n" " %2:%3\n" " %4:%5\n" " %6:%7\n" " %8:%9\n" " %10:%11\n" " %12:%13\n" "\n" ) .arg(stats) .arg(errors) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors)) .arg(warnings) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings)) .arg(style) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle)) .arg(portability) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability)) .arg(performance) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance)) .arg(information) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation)); const QString htmlSummary = htmlSettings + htmlPrevious + htmlStatistics; QMimeData *mimeData = new QMimeData(); mimeData->setText(textSummary); mimeData->setHtml(htmlSummary); clipboard->setMimeData(mimeData); } void StatsDialog::setStatistics(const CheckStatistics *stats) { mStatistics = stats; mUI.mLblErrors->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowErrors))); mUI.mLblWarnings->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings))); mUI.mLblStyle->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowStyle))); mUI.mLblPortability->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPortability))); mUI.mLblPerformance->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance))); mUI.mLblInformation->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowInformation))); } #ifdef HAVE_QCHART QChartView *StatsDialog::createChart(const QString &statsFile, const QString &tool) { QChart *chart = new QChart; chart->addSeries(numberOfReports(statsFile, tool + "-error")); chart->addSeries(numberOfReports(statsFile, tool + "-warning")); chart->addSeries(numberOfReports(statsFile, tool + "-style")); chart->addSeries(numberOfReports(statsFile, tool + "-performance")); chart->addSeries(numberOfReports(statsFile, tool + "-portability")); QDateTimeAxis *axisX = new QDateTimeAxis; axisX->setTitleText("Date"); chart->addAxis(axisX, Qt::AlignBottom); foreach (QAbstractSeries *s, chart->series()) { s->attachAxis(axisX); } QValueAxis *axisY = new QValueAxis; axisY->setLabelFormat("%i"); axisY->setTitleText("Count"); chart->addAxis(axisY, Qt::AlignLeft); qreal maxY = 0; foreach (QAbstractSeries *s, chart->series()) { s->attachAxis(axisY); if (QLineSeries *ls = dynamic_cast(s)) { foreach (QPointF p, ls->points()) { if (p.y() > maxY) maxY = p.y(); } } } axisY->setMax(maxY); //chart->createDefaultAxes(); chart->setTitle(tool); QChartView *chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); return chartView; } QLineSeries *StatsDialog::numberOfReports(const QString &fileName, const QString &severity) const { QLineSeries *series = new QLineSeries(); series->setName(severity); QFile f(fileName); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { quint64 t = 0; QTextStream in(&f); while (!in.atEnd()) { QString line = in.readLine(); QRegExp rxdate("\\[(\\d\\d)\\.(\\d\\d)\\.(\\d\\d\\d\\d)\\]"); if (rxdate.exactMatch(line)) { int y = rxdate.cap(3).toInt(); int m = rxdate.cap(2).toInt(); int d = rxdate.cap(1).toInt(); QDateTime dt; dt.setDate(QDate(y,m,d)); if (t == dt.toMSecsSinceEpoch()) t += 1000; else t = dt.toMSecsSinceEpoch(); } if (line.startsWith(severity + ':')) { int y = line.mid(1+severity.length()).toInt(); series->append(t, y); } } } return series; } #endif cppcheck-2.7/gui/statsdialog.h000066400000000000000000000042771417746362400164450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef STATSDIALOG_H #define STATSDIALOG_H #include "ui_stats.h" #include class ProjectFile; class CheckStatistics; #ifdef HAVE_QCHART namespace QtCharts { class QChartView; class QLineSeries; } #endif /// @addtogroup GUI /// @{ /** * @brief A dialog that shows project and scan statistics. * */ class StatsDialog : public QDialog { Q_OBJECT public: explicit StatsDialog(QWidget *parent = nullptr); /** * @brief Sets the project to extract statistics from */ void setProject(const ProjectFile *projectFile); /** * @brief Sets the string to display beside "Path Selected:" */ void setPathSelected(const QString& path); /** * @brief Sets the number to display beside "Number of Files Scanned:" */ void setNumberOfFilesScanned(int num); /** * @brief Sets the number of seconds to display beside "Scan Duration:" */ void setScanDuration(double seconds); /** * @brief Sets the numbers of different error/warnings found." */ void setStatistics(const CheckStatistics *stats); private slots: void copyToClipboard(); void pdfExport(); #ifdef HAVE_QCHART QtCharts::QChartView *createChart(const QString &statsFile, const QString &tool); QtCharts::QLineSeries *numberOfReports(const QString &fileName, const QString &severity) const; #endif private: Ui::StatsDialog mUI; const CheckStatistics *mStatistics; }; /// @} #endif // STATSDIALOG_H cppcheck-2.7/gui/test/000077500000000000000000000000001417746362400147235ustar00rootroot00000000000000cppcheck-2.7/gui/test/CMakeLists.txt000066400000000000000000000003341417746362400174630ustar00rootroot00000000000000add_subdirectory(benchmark) add_subdirectory(cppchecklibrarydata) add_subdirectory(filelist) add_subdirectory(projectfile) add_subdirectory(translationhandler) add_subdirectory(xmlreportv2) # TODO: add all tests to CTestcppcheck-2.7/gui/test/benchmark/000077500000000000000000000000001417746362400166555ustar00rootroot00000000000000cppcheck-2.7/gui/test/benchmark/CMakeLists.txt000066400000000000000000000000301417746362400214060ustar00rootroot00000000000000add_subdirectory(simple)cppcheck-2.7/gui/test/benchmark/benchmark.pro000066400000000000000000000000701417746362400213260ustar00rootroot00000000000000CONFIG += ordered TEMPLATE = subdirs SUBDIRS = simple cppcheck-2.7/gui/test/benchmark/simple/000077500000000000000000000000001417746362400201465ustar00rootroot00000000000000cppcheck-2.7/gui/test/benchmark/simple/CMakeLists.txt000066400000000000000000000014701417746362400227100ustar00rootroot00000000000000qt5_wrap_cpp(test-benchmark-simple_SRC benchmarksimple.h) add_custom_target(build-testbenchmark-simple-deps SOURCES ${test-benchmark-simple_SRC}) add_dependencies(gui-build-deps build-testbenchmark-simple-deps) add_executable(benchmark-simple ${test-benchmark-simple_SRC} benchmarksimple.cpp $ $ $ ) target_include_directories(benchmark-simple PRIVATE ${CMAKE_SOURCE_DIR}/lib) target_compile_definitions(benchmark-simple PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") target_link_libraries(benchmark-simple Qt5::Core Qt5::Test) if (HAVE_RULES) target_link_libraries(benchmark-simple ${PCRE_LIBRARY}) endif() if (USE_Z3) target_link_libraries(benchmark-simple ${Z3_LIBRARIES}) endif()cppcheck-2.7/gui/test/benchmark/simple/benchmarksimple.cpp000066400000000000000000000042101417746362400240130ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "benchmarksimple.h" #include "settings.h" #include "tokenize.h" #include #include #include #include #include void BenchmarkSimple::tokenize() { QFile file(QString(SRCDIR) + "/../../data/benchmark/simple.cpp"); QByteArray data = file.readAll(); Settings settings; settings.debugwarnings = true; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(data.constData()); QBENCHMARK { tokenizer.tokenize(istr, "test.cpp"); } } void BenchmarkSimple::simplify() { QFile file(QString(SRCDIR) + "/../../data/benchmark/simple.cpp"); QByteArray data = file.readAll(); Settings settings; settings.debugwarnings = true; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(data.constData()); tokenizer.tokenize(istr, "test.cpp"); QBENCHMARK { tokenizer.simplifyTokenList2(); } } void BenchmarkSimple::tokenizeAndSimplify() { QFile file(QString(SRCDIR) + "/../../data/benchmark/simple.cpp"); QByteArray data = file.readAll(); Settings settings; settings.debugwarnings = true; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(data.constData()); QBENCHMARK { tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); } } QTEST_MAIN(BenchmarkSimple) cppcheck-2.7/gui/test/benchmark/simple/benchmarksimple.h000066400000000000000000000024341417746362400234660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "errorlogger.h" #include class BenchmarkSimple : public QObject, public ErrorLogger { Q_OBJECT private slots: void tokenize(); void simplify(); void tokenizeAndSimplify(); private: // Empty implementations of ErrorLogger methods. // We don't care about the output in the benchmark tests. void reportOut(const std::string & /*outmsg*/, Color /*c*/ = Color::Reset) override {} void reportErr(const ErrorMessage & /*msg*/) override {} void bughuntingReport(const std::string & /*str*/) override {} }; cppcheck-2.7/gui/test/benchmark/simple/simple.pro000066400000000000000000000003741417746362400221650ustar00rootroot00000000000000TEMPLATE = app TARGET = benchmark-simple DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../../build MOC_DIR = ../../build include(../../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += benchmarksimple.cpp HEADERS += benchmarksimple.h cppcheck-2.7/gui/test/common.pri000066400000000000000000000003021417746362400167220ustar00rootroot00000000000000QT += testlib INCLUDEPATH += $${PWD}/.. \ $${PWD}/../../lib contains(QMAKE_CC, gcc) { QMAKE_CXXFLAGS += -std=c++11 } contains(QMAKE_CXX, clang++) { QMAKE_CXXFLAGS += -std=c++11 } cppcheck-2.7/gui/test/cppchecklibrarydata/000077500000000000000000000000001417746362400207225ustar00rootroot00000000000000cppcheck-2.7/gui/test/cppchecklibrarydata/CMakeLists.txt000066400000000000000000000010551417746362400234630ustar00rootroot00000000000000qt5_wrap_cpp(test-cppchecklibrarydata_SRC testcppchecklibrarydata.h) add_custom_target(build-cppchecklibrarydata-deps SOURCES ${test-cppchecklibrarydata_SRC}) add_dependencies(gui-build-deps build-cppchecklibrarydata-deps) add_executable(test-cppchecklibrarydata ${test-cppchecklibrarydata_SRC} testcppchecklibrarydata.cpp ${CMAKE_SOURCE_DIR}/gui/cppchecklibrarydata.cpp ) target_include_directories(test-cppchecklibrarydata PRIVATE ${CMAKE_SOURCE_DIR}/gui) target_link_libraries(test-cppchecklibrarydata Qt5::Core Qt5::Test)cppcheck-2.7/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro000066400000000000000000000006251417746362400254460ustar00rootroot00000000000000TEMPLATE = app TARGET = test-cppchecklibrarydata DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build QT -= gui QT += core CONFIG += console include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" SOURCES += testcppchecklibrarydata.cpp \ ../../cppchecklibrarydata.cpp HEADERS += testcppchecklibrarydata.h \ ../../cppchecklibrarydata.h \ RESOURCES += \ resources.qrc cppcheck-2.7/gui/test/cppchecklibrarydata/files/000077500000000000000000000000001417746362400220245ustar00rootroot00000000000000cppcheck-2.7/gui/test/cppchecklibrarydata/files/container_unhandled_element.cfg000066400000000000000000000005301417746362400302200ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/define_valid.cfg000066400000000000000000000001421417746362400251130ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg000066400000000000000000000001371417746362400303200ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg000066400000000000000000000002451417746362400316770ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg000066400000000000000000000017071417746362400275440ustar00rootroot00000000000000 READ READ WRITE NOTIFY connect cppcheck-2.7/gui/test/cppchecklibrarydata/files/markup_valid.cfg000066400000000000000000000016431417746362400251670ustar00rootroot00000000000000 READ READ WRITE NOTIFY connect cppcheck-2.7/gui/test/cppchecklibrarydata/files/memory_resource_unhandled_element.cfg000066400000000000000000000007171417746362400314640ustar00rootroot00000000000000 malloc calloc aligned_alloc realloc reallocarray free cppcheck-2.7/gui/test/cppchecklibrarydata/files/memory_resource_valid.cfg000066400000000000000000000006601417746362400271050ustar00rootroot00000000000000 malloc calloc realloc UuidToString HeapFree fclose _wfopen_s cppcheck-2.7/gui/test/cppchecklibrarydata/files/platform_type_unhandled_element.cfg000066400000000000000000000002771417746362400311330ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/platform_type_valid.cfg000066400000000000000000000006531417746362400265550ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/podtype_valid.cfg000066400000000000000000000002051417746362400253450ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/reflection_mandatory_attribute_missing.cfg000066400000000000000000000001521417746362400325270ustar00rootroot00000000000000 invokeMethod cppcheck-2.7/gui/test/cppchecklibrarydata/files/reflection_unhandled_element.cfg000066400000000000000000000002361417746362400303730ustar00rootroot00000000000000 invokeMethod cppcheck-2.7/gui/test/cppchecklibrarydata/files/reflection_valid.cfg000066400000000000000000000002511417746362400260140ustar00rootroot00000000000000 invokeMethod callFunction cppcheck-2.7/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg000066400000000000000000000002671417746362400255450ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg000066400000000000000000000004641417746362400260320ustar00rootroot00000000000000 std::insert_iterator std::pair std::tuple cppcheck-2.7/gui/test/cppchecklibrarydata/files/undefine_valid.cfg000066400000000000000000000001311417746362400254540ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/unhandled_element.cfg000066400000000000000000000002441417746362400261600ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg000066400000000000000000000002241417746362400260360ustar00rootroot00000000000000 cppcheck-2.7/gui/test/cppchecklibrarydata/resources.qrc000066400000000000000000000020651417746362400234460ustar00rootroot00000000000000 files/xml_reader_error.cfg files/mandatory_attribute_missing.cfg files/unhandled_element.cfg files/typechecks_valid.cfg files/podtype_valid.cfg files/smartptr_valid.cfg files/platform_type_valid.cfg files/platform_type_unhandled_element.cfg files/memory_resource_unhandled_element.cfg files/memory_resource_valid.cfg files/define_valid.cfg files/undefine_valid.cfg files/container_unhandled_element.cfg files/reflection_unhandled_element.cfg files/reflection_valid.cfg files/reflection_mandatory_attribute_missing.cfg files/markup_mandatory_attribute_missing.cfg files/markup_valid.cfg files/markup_unhandled_element.cfg cppcheck-2.7/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp000066400000000000000000000554651417746362400263440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testcppchecklibrarydata.h" #include #include const QString TestCppcheckLibraryData::TempCfgFile = "./tmp.cfg"; void TestCppcheckLibraryData::init() { result.clear(); libraryData.clear(); fileLibraryData.clear(); } void TestCppcheckLibraryData::xmlReaderError() { loadCfgFile(":/files/xml_reader_error.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; } void TestCppcheckLibraryData::unhandledElement() { loadCfgFile(":/files/unhandled_element.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; loadCfgFile(":/files/platform_type_unhandled_element.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; loadCfgFile(":/files/memory_resource_unhandled_element.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; loadCfgFile(":/files/container_unhandled_element.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; loadCfgFile(":/files/reflection_unhandled_element.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; loadCfgFile(":/files/markup_unhandled_element.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; } void TestCppcheckLibraryData::mandatoryAttributeMissing() { loadCfgFile(":/files/mandatory_attribute_missing.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; loadCfgFile(":/files/reflection_mandatory_attribute_missing.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; loadCfgFile(":/files/markup_mandatory_attribute_missing.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), false); qDebug() << result; } void TestCppcheckLibraryData::podtypeValid() { // Load library data from file loadCfgFile(":/files/podtype_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.podtypes.size(), 2); QCOMPARE(libraryData.podtypes[0].name, QString("bool")); QCOMPARE(libraryData.podtypes[0].stdtype.isEmpty(), true); QCOMPARE(libraryData.podtypes[0].sign.isEmpty(), true); QCOMPARE(libraryData.podtypes[0].size.isEmpty(), true); QCOMPARE(libraryData.podtypes[1].name, QString("ulong")); QCOMPARE(libraryData.podtypes[1].stdtype, QString("uint32_t")); QCOMPARE(libraryData.podtypes[1].sign, QString("u")); QCOMPARE(libraryData.podtypes[1].size, QString("4")); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.podtypes.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.podtypes.size(), fileLibraryData.podtypes.size()); QCOMPARE(libraryData.podtypes.size(), 2); for (int i=0; i < libraryData.podtypes.size(); i++) { QCOMPARE(libraryData.podtypes[i].name, fileLibraryData.podtypes[i].name); QCOMPARE(libraryData.podtypes[i].stdtype, fileLibraryData.podtypes[i].stdtype); QCOMPARE(libraryData.podtypes[i].sign, fileLibraryData.podtypes[i].sign); QCOMPARE(libraryData.podtypes[i].size, fileLibraryData.podtypes[i].size); } } void TestCppcheckLibraryData::typechecksValid() { // Load library data from file loadCfgFile(":/files/typechecks_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.typeChecks.size(), 3); CppcheckLibraryData::TypeChecks check = libraryData.typeChecks[0]; QCOMPARE(check.size(), 2); QCOMPARE(check[0].first, QString("suppress")); QCOMPARE(check[0].second, QString("std::insert_iterator")); QCOMPARE(check[1].first, QString("check")); QCOMPARE(check[1].second, QString("std::pair")); check = libraryData.typeChecks[1]; QCOMPARE(check.isEmpty(), true); check = libraryData.typeChecks[2]; QCOMPARE(check.size(), 1); QCOMPARE(check[0].first, QString("check")); QCOMPARE(check[0].second, QString("std::tuple")); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.typeChecks.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.typeChecks.size(), fileLibraryData.typeChecks.size()); QCOMPARE(libraryData.typeChecks.size(), 3); for (int idx=0; idx < libraryData.typeChecks.size(); idx++) { CppcheckLibraryData::TypeChecks lhs = libraryData.typeChecks[idx]; CppcheckLibraryData::TypeChecks rhs = fileLibraryData.typeChecks[idx]; QCOMPARE(lhs.size(), rhs.size()); QCOMPARE(lhs, rhs); } } void TestCppcheckLibraryData::smartPointerValid() { // Load library data from file loadCfgFile(":/files/smartptr_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.smartPointers.size(), 3); QCOMPARE(libraryData.smartPointers[0], QString("wxObjectDataPtr")); QCOMPARE(libraryData.smartPointers[1], QString("wxScopedArray")); QCOMPARE(libraryData.smartPointers[2], QString("wxScopedPtr")); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.smartPointers.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.smartPointers.size(), fileLibraryData.smartPointers.size()); QCOMPARE(libraryData.smartPointers.size(), 3); QCOMPARE(libraryData.smartPointers, fileLibraryData.smartPointers); } void TestCppcheckLibraryData::platformTypeValid() { // Load library data from file loadCfgFile(":/files/platform_type_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.platformTypes.size(), 3); QCOMPARE(libraryData.platformTypes[0].name, QString("platform")); QCOMPARE(libraryData.platformTypes[0].value, QString("with attribute and empty")); QCOMPARE(libraryData.platformTypes[0].types.size(), 0); QCOMPARE(libraryData.platformTypes[0].platforms.size(), 2); QCOMPARE(libraryData.platformTypes[0].platforms[0], QString("win64")); QCOMPARE(libraryData.platformTypes[0].platforms[1].isEmpty(), true); QCOMPARE(libraryData.platformTypes[1].name, QString("types")); QCOMPARE(libraryData.platformTypes[1].value, QString("all")); QCOMPARE(libraryData.platformTypes[1].types.size(), 5); QCOMPARE(libraryData.platformTypes[1].types, QStringList({"unsigned", "long", "pointer", "const_ptr", "ptr_ptr"})); QCOMPARE(libraryData.platformTypes[1].platforms.isEmpty(), true); QCOMPARE(libraryData.platformTypes[2].name, QString("types and platform")); QCOMPARE(libraryData.platformTypes[2].value.isEmpty(), true); QCOMPARE(libraryData.platformTypes[2].types.size(), 2); QCOMPARE(libraryData.platformTypes[2].types, QStringList({"pointer", "ptr_ptr"})); QCOMPARE(libraryData.platformTypes[2].platforms.size(), 1); QCOMPARE(libraryData.platformTypes[2].platforms[0], QString("win32")); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.platformTypes.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.platformTypes.size(), fileLibraryData.platformTypes.size()); QCOMPARE(libraryData.platformTypes.size(), 3); for (int idx=0; idx < libraryData.platformTypes.size(); idx++) { CppcheckLibraryData::PlatformType lhs = libraryData.platformTypes[idx]; CppcheckLibraryData::PlatformType rhs = fileLibraryData.platformTypes[idx]; QCOMPARE(lhs.name, rhs.name); QCOMPARE(lhs.value, rhs.value); QCOMPARE(lhs.types.size(), rhs.types.size()); QCOMPARE(lhs.types, rhs.types); QCOMPARE(lhs.platforms.size(), rhs.platforms.size()); QCOMPARE(lhs.platforms, rhs.platforms); } } void TestCppcheckLibraryData::memoryResourceValid() { // Load library data from file loadCfgFile(":/files/memory_resource_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.memoryresource.size(), 2); QCOMPARE(libraryData.memoryresource[0].type, QString("memory")); QCOMPARE(libraryData.memoryresource[0].alloc.size(), 4); QCOMPARE(libraryData.memoryresource[0].dealloc.size(), 1); QCOMPARE(libraryData.memoryresource[0].use.size(), 0); QCOMPARE(libraryData.memoryresource[0].alloc[0].name, QString("malloc")); QCOMPARE(libraryData.memoryresource[0].alloc[0].bufferSize, QString("malloc")); QCOMPARE(libraryData.memoryresource[0].alloc[0].isRealloc, false); QCOMPARE(libraryData.memoryresource[0].alloc[0].init, false); QCOMPARE(libraryData.memoryresource[0].alloc[0].arg, -1); QCOMPARE(libraryData.memoryresource[0].alloc[0].reallocArg, -1); QCOMPARE(libraryData.memoryresource[0].alloc[1].name, QString("calloc")); QCOMPARE(libraryData.memoryresource[0].alloc[1].bufferSize, QString("calloc")); QCOMPARE(libraryData.memoryresource[0].alloc[1].isRealloc, false); QCOMPARE(libraryData.memoryresource[0].alloc[1].init, true); QCOMPARE(libraryData.memoryresource[0].alloc[1].arg, -1); QCOMPARE(libraryData.memoryresource[0].alloc[1].reallocArg, -1); QCOMPARE(libraryData.memoryresource[0].alloc[2].name, QString("realloc")); QCOMPARE(libraryData.memoryresource[0].alloc[2].bufferSize, QString("malloc:2")); QCOMPARE(libraryData.memoryresource[0].alloc[2].isRealloc, true); QCOMPARE(libraryData.memoryresource[0].alloc[2].init, false); QCOMPARE(libraryData.memoryresource[0].alloc[2].arg, -1); QCOMPARE(libraryData.memoryresource[0].alloc[2].reallocArg, -1); QCOMPARE(libraryData.memoryresource[0].alloc[3].name, QString("UuidToString")); QCOMPARE(libraryData.memoryresource[0].alloc[3].bufferSize.isEmpty(), true); QCOMPARE(libraryData.memoryresource[0].alloc[3].isRealloc, false); QCOMPARE(libraryData.memoryresource[0].alloc[3].init, false); QCOMPARE(libraryData.memoryresource[0].alloc[3].arg, 2); QCOMPARE(libraryData.memoryresource[0].alloc[3].reallocArg, -1); QCOMPARE(libraryData.memoryresource[0].dealloc[0].name, QString("HeapFree")); QCOMPARE(libraryData.memoryresource[0].dealloc[0].arg, 3); QCOMPARE(libraryData.memoryresource[1].type, QString("resource")); QCOMPARE(libraryData.memoryresource[1].alloc.size(), 1); QCOMPARE(libraryData.memoryresource[1].dealloc.size(), 1); QCOMPARE(libraryData.memoryresource[1].use.size(), 0); QCOMPARE(libraryData.memoryresource[1].alloc[0].name, QString("_wfopen_s")); QCOMPARE(libraryData.memoryresource[1].alloc[0].bufferSize.isEmpty(), true); QCOMPARE(libraryData.memoryresource[1].alloc[0].isRealloc, false); QCOMPARE(libraryData.memoryresource[1].alloc[0].init, true); QCOMPARE(libraryData.memoryresource[1].alloc[0].arg, 1); QCOMPARE(libraryData.memoryresource[1].alloc[0].reallocArg, -1); QCOMPARE(libraryData.memoryresource[1].dealloc[0].name, QString("fclose")); QCOMPARE(libraryData.memoryresource[1].dealloc[0].arg, -1); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.memoryresource.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.memoryresource.size(), fileLibraryData.memoryresource.size()); QCOMPARE(libraryData.memoryresource.size(), 2); for (int idx=0; idx < libraryData.memoryresource.size(); idx++) { CppcheckLibraryData::MemoryResource lhs = libraryData.memoryresource[idx]; CppcheckLibraryData::MemoryResource rhs = fileLibraryData.memoryresource[idx]; QCOMPARE(lhs.type, rhs.type); QCOMPARE(lhs.alloc.size(), rhs.alloc.size()); QCOMPARE(lhs.dealloc.size(), rhs.dealloc.size()); QCOMPARE(lhs.use, rhs.use); for (int num=0; num < lhs.alloc.size(); num++) { QCOMPARE(lhs.alloc[num].name, rhs.alloc[num].name); QCOMPARE(lhs.alloc[num].bufferSize, rhs.alloc[num].bufferSize); QCOMPARE(lhs.alloc[num].isRealloc, rhs.alloc[num].isRealloc); QCOMPARE(lhs.alloc[num].init, rhs.alloc[num].init); QCOMPARE(lhs.alloc[num].arg, rhs.alloc[num].arg); QCOMPARE(lhs.alloc[num].reallocArg, rhs.alloc[num].reallocArg); } for (int num=0; num < lhs.dealloc.size(); num++) { QCOMPARE(lhs.dealloc[num].name, rhs.dealloc[num].name); QCOMPARE(lhs.dealloc[num].arg, rhs.dealloc[num].arg); } } } void TestCppcheckLibraryData::defineValid() { // Load library data from file loadCfgFile(":/files/define_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.defines.size(), 2); QCOMPARE(libraryData.defines[0].name, QString("INT8_MIN")); QCOMPARE(libraryData.defines[0].value, QString("-128")); QCOMPARE(libraryData.defines[1].name.isEmpty(), true); QCOMPARE(libraryData.defines[1].value.isEmpty(), true); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.defines.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.defines.size(), fileLibraryData.defines.size()); QCOMPARE(libraryData.defines.size(), 2); for (int idx=0; idx < libraryData.defines.size(); idx++) { QCOMPARE(libraryData.defines[idx].name, fileLibraryData.defines[idx].name); QCOMPARE(libraryData.defines[idx].value, fileLibraryData.defines[idx].value); } } void TestCppcheckLibraryData::undefineValid() { // Load library data from file loadCfgFile(":/files/undefine_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.undefines.size(), 2); QCOMPARE(libraryData.undefines[0], QString("INT8_MIN")); QCOMPARE(libraryData.undefines[1].isEmpty(), true); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.undefines.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.undefines.size(), fileLibraryData.undefines.size()); QCOMPARE(libraryData.undefines.size(), 2); QCOMPARE(libraryData.undefines, fileLibraryData.undefines); } void TestCppcheckLibraryData::reflectionValid() { // Load library data from file loadCfgFile(":/files/reflection_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.reflections.size(), 2); QCOMPARE(libraryData.reflections[0].calls.size(), 2); QCOMPARE(libraryData.reflections[0].calls[0].arg, 2); QCOMPARE(libraryData.reflections[0].calls[0].name, QString("invokeMethod")); QCOMPARE(libraryData.reflections[0].calls[1].arg, 1); QCOMPARE(libraryData.reflections[0].calls[1].name, QString("callFunction")); QCOMPARE(libraryData.reflections[1].calls.isEmpty(), true); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.reflections.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.reflections.size(), fileLibraryData.reflections.size()); QCOMPARE(libraryData.reflections.size(), 2); for (int idx=0; idx < libraryData.reflections.size(); idx++) { CppcheckLibraryData::Reflection lhs = libraryData.reflections[idx]; CppcheckLibraryData::Reflection rhs = fileLibraryData.reflections[idx]; QCOMPARE(lhs.calls.size(), rhs.calls.size()); for (int num=0; num < lhs.calls.size(); num++) { QCOMPARE(lhs.calls[num].arg, rhs.calls[num].arg); QCOMPARE(lhs.calls[num].name, rhs.calls[num].name); } } } void TestCppcheckLibraryData::markupValid() { // Load library data from file loadCfgFile(":/files/markup_valid.cfg", fileLibraryData, result); QCOMPARE(result.isNull(), true); // Swap library data read from file to other object libraryData.swap(fileLibraryData); // Do size and content checks against swapped data. QCOMPARE(libraryData.markups.size(), 1); QCOMPARE(libraryData.markups[0].ext, QString(".qml")); QCOMPARE(libraryData.markups[0].reportErrors, false); QCOMPARE(libraryData.markups[0].afterCode, true); QCOMPARE(libraryData.markups[0].keywords.size(), 4); QCOMPARE(libraryData.markups[0].keywords, QStringList({"if", "while", "typeof", "for"})); QCOMPARE(libraryData.markups[0].importer.size(), 1); QCOMPARE(libraryData.markups[0].importer, QStringList("connect")); QCOMPARE(libraryData.markups[0].exporter.size(), 1); QCOMPARE(libraryData.markups[0].exporter[0].prefix, QString("Q_PROPERTY")); QCOMPARE(libraryData.markups[0].exporter[0].suffixList.size(), 1); QCOMPARE(libraryData.markups[0].exporter[0].suffixList, QStringList("READ")); QCOMPARE(libraryData.markups[0].exporter[0].prefixList.size(), 3); QCOMPARE(libraryData.markups[0].exporter[0].prefixList, QStringList({"READ", "WRITE", "NOTIFY"})); QCOMPARE(libraryData.markups[0].codeBlocks.size(), 2); QCOMPARE(libraryData.markups[0].codeBlocks[0].blocks.size(), 5); QCOMPARE(libraryData.markups[0].codeBlocks[0].blocks, QStringList({"onClicked", "onFinished", "onTriggered", "onPressed", "onTouch"})); QCOMPARE(libraryData.markups[0].codeBlocks[0].offset, 3); QCOMPARE(libraryData.markups[0].codeBlocks[0].start, QString("{")); QCOMPARE(libraryData.markups[0].codeBlocks[0].end, QString("}")); QCOMPARE(libraryData.markups[0].codeBlocks[1].blocks.size(), 1); QCOMPARE(libraryData.markups[0].codeBlocks[1].blocks, QStringList("function")); QCOMPARE(libraryData.markups[0].codeBlocks[1].offset, 2); QCOMPARE(libraryData.markups[0].codeBlocks[1].start, QString("{")); QCOMPARE(libraryData.markups[0].codeBlocks[1].end, QString("}")); // Save library data to file saveCfgFile(TempCfgFile, libraryData); fileLibraryData.clear(); QCOMPARE(fileLibraryData.markups.size(), 0); // Reload library data from file loadCfgFile(TempCfgFile, fileLibraryData, result, true); QCOMPARE(result.isNull(), true); // Verify no data got lost or modified QCOMPARE(libraryData.markups.size(), fileLibraryData.markups.size()); for (int idx=0; idx < libraryData.markups.size(); idx++) { CppcheckLibraryData::Markup lhs = libraryData.markups[idx]; CppcheckLibraryData::Markup rhs = fileLibraryData.markups[idx]; QCOMPARE(lhs.ext, rhs.ext); QCOMPARE(lhs.reportErrors, rhs.reportErrors); QCOMPARE(lhs.afterCode, rhs.afterCode); QCOMPARE(lhs.keywords, rhs.keywords); QCOMPARE(lhs.importer, rhs.importer); for (int num=0; num < lhs.exporter.size(); num++) { QCOMPARE(lhs.exporter[num].prefix, rhs.exporter[num].prefix); QCOMPARE(lhs.exporter[num].suffixList, rhs.exporter[num].suffixList); QCOMPARE(lhs.exporter[num].prefixList, rhs.exporter[num].prefixList); } for (int num=0; num < lhs.codeBlocks.size(); num++) { QCOMPARE(lhs.codeBlocks[num].blocks, rhs.codeBlocks[num].blocks); QCOMPARE(lhs.codeBlocks[num].offset, rhs.codeBlocks[num].offset); QCOMPARE(lhs.codeBlocks[num].start, rhs.codeBlocks[num].start); QCOMPARE(lhs.codeBlocks[num].end, rhs.codeBlocks[num].end); } } } void TestCppcheckLibraryData::loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile) { QFile file(filename); QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); result = data.open(file); file.close(); if (removeFile) { file.remove(); } } void TestCppcheckLibraryData::saveCfgFile(QString filename, CppcheckLibraryData &data) { QFile file(filename); QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); QTextStream textStream(&file); textStream << data.toString() << '\n'; file.close(); } QTEST_MAIN(TestCppcheckLibraryData) cppcheck-2.7/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h000066400000000000000000000030421417746362400257710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppchecklibrarydata.h" #include class TestCppcheckLibraryData : public QObject { Q_OBJECT private slots: void init(); void xmlReaderError(); void unhandledElement(); void mandatoryAttributeMissing(); void podtypeValid(); void typechecksValid(); void smartPointerValid(); void platformTypeValid(); void memoryResourceValid(); void defineValid(); void undefineValid(); void reflectionValid(); void markupValid(); private: void loadCfgFile(QString filename, CppcheckLibraryData &data, QString &result, bool removeFile = false); void saveCfgFile(QString filename, CppcheckLibraryData &data); CppcheckLibraryData libraryData; CppcheckLibraryData fileLibraryData; QString result; static const QString TempCfgFile; }; cppcheck-2.7/gui/test/data/000077500000000000000000000000001417746362400156345ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/benchmark/000077500000000000000000000000001417746362400175665ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/benchmark/simple.cpp000066400000000000000000004104461417746362400215740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Remove includes from the benchmark run // Included files aren't found anyway //#include "checkother.h" //#include "mathlib.h" //#include "symboldatabase.h" //#include // std::isupper //#include // fabs() //#include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckOther instance; } //--------------------------------------------------------------------------- void CheckOther::checkIncrementBoolean() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%var% ++")) { if (tok->varId()) { const Token *declTok = Token::findmatch(_tokenizer->tokens(), "bool %varid%", tok->varId()); if (declTok) incrementBooleanError(tok); } } } } void CheckOther::incrementBooleanError(const Token *tok) { reportError( tok, Severity::style, "incrementboolean", "The use of a variable of type bool with the ++ postfix operator is always true and deprecated by the C++ Standard.\n" "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true\n" ); } //--------------------------------------------------------------------------- void CheckOther::clarifyCalculation() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->strAt(1) == "?") { // condition const Token *cond = tok; if (cond->isName() || cond->isNumber()) cond = cond->previous(); else if (cond->str() == ")") cond = cond->link()->previous(); else continue; // calculation if (!cond->isArithmeticalOp()) continue; const std::string &op = cond->str(); cond = cond->previous(); // skip previous multiplications.. while (cond && cond->strAt(-1) == "*" && (cond->isName() || cond->isNumber())) cond = cond->tokAt(-2); if (!cond) continue; // first multiplication operand if (cond->str() == ")") { clarifyCalculationError(cond, op); } else if (cond->isName() || cond->isNumber()) { if (Token::Match(cond->previous(),("return|=|+|-|,|(|"+op).c_str())) clarifyCalculationError(cond, op); } } } } void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) { // suspicious calculation const std::string calc("'a" + op + "b?c:d'"); // recommended calculation #1 const std::string s1("'(a" + op + "b)?c:d'"); // recommended calculation #2 const std::string s2("'a" + op + "(b?c:d)'"); reportError(tok, Severity::style, "clarifyCalculation", "Clarify calculation precedence for " + op + " and ?\n" "Suspicious calculation. Please use parentheses to clarify the code. " "The code " + calc + " should be written as either " + s1 + " or " + s2 + "."); } // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' void CheckOther::clarifyCondition() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "( %var% =")) { for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); else if (Token::Match(tok2, "&&|%oror%|?|)")) break; else if (Token::Match(tok2, "<|<=|==|!=|>|>= %num% )")) { clarifyConditionError(tok); break; } } } } } void CheckOther::clarifyConditionError(const Token *tok) { reportError(tok, Severity::style, "clarifyCondition", "Suspicious condition (assignment+comparison), it can be clarified with parentheses"); } void CheckOther::warningOldStylePointerCast() { if (!_settings->isEnabled("style") || (_tokenizer->tokens() && _tokenizer->fileLine(_tokenizer->tokens()).find(".cpp") == std::string::npos)) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Old style pointer casting.. if (!Token::Match(tok, "( const| %type% * ) %var%") && !Token::Match(tok, "( const| %type% * ) (| new")) continue; int addToIndex = 0; if (tok->tokAt(1)->str() == "const") addToIndex = 1; if (tok->tokAt(4 + addToIndex)->str() == "const") continue; // Is "type" a class? const std::string pattern("class " + tok->tokAt(1 + addToIndex)->str()); if (!Token::findmatch(_tokenizer->tokens(), pattern.c_str())) continue; cstyleCastError(tok); } } //--------------------------------------------------------------------------- // fflush(stdin) <- fflush only applies to output streams in ANSI C //--------------------------------------------------------------------------- void CheckOther::checkFflushOnInputStream() { const Token *tok = _tokenizer->tokens(); while (tok && ((tok = Token::findsimplematch(tok, "fflush ( stdin )")) != NULL)) { fflushOnInputStreamError(tok, tok->strAt(2)); tok = tok->tokAt(4); } } void CheckOther::checkSizeofForNumericParameter() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %num% )") || Token::Match(tok, "sizeof ( - %num% )") || Token::Match(tok, "sizeof %num%") || Token::Match(tok, "sizeof - %num%") ) { sizeofForNumericParameterError(tok); } } } void CheckOther::checkSizeofForArrayParameter() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %var% )") || Token::Match(tok, "sizeof %var%")) { int tokIdx = 1; if (tok->tokAt(tokIdx)->str() == "(") { ++tokIdx; } if (tok->tokAt(tokIdx)->varId() > 0) { const Token *declTok = Token::findmatch(_tokenizer->tokens(), "%varid%", tok->tokAt(tokIdx)->varId()); if (declTok) { if (Token::simpleMatch(declTok->next(), "[")) { declTok = declTok->next()->link(); // multidimensional array while (Token::simpleMatch(declTok->next(), "[")) { declTok = declTok->next()->link(); } if (!(Token::Match(declTok->next(), "= %str%")) && !(Token::simpleMatch(declTok->next(), "= {")) && !(Token::simpleMatch(declTok->next(), ";"))) { if (Token::simpleMatch(declTok->next(), ",")) { declTok = declTok->next(); while (!Token::simpleMatch(declTok, ";")) { if (Token::simpleMatch(declTok, ")")) { sizeofForArrayParameterError(tok); break; } if (Token::Match(declTok, "(|[|{")) { declTok = declTok->link(); } declTok = declTok->next(); } } } if (Token::simpleMatch(declTok->next(), ")")) { sizeofForArrayParameterError(tok); } } } } } } } //--------------------------------------------------------------------------- // switch (x) // { // case 2: // y = a; // <- this assignment is redundant // case 3: // y = b; // <- case 2 falls through and sets y twice // } //--------------------------------------------------------------------------- void CheckOther::checkRedundantAssignmentInSwitch() { const char switchPattern[] = "switch ( %any% ) { case"; const char breakPattern[] = "break|continue|return|exit|goto|throw"; const char functionPattern[] = "%var% ("; // Find the beginning of a switch. E.g.: // switch (var) { ... const Token *tok = Token::findmatch(_tokenizer->tokens(), switchPattern); while (tok) { // Check the contents of the switch statement std::map varsAssigned; int indentLevel = 0; for (const Token *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.: // case 3: b = 1; // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) { const Token* endOfConditional = tok2->link(); for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) { if (tok3->varId() != 0) varsAssigned.erase(tok3->varId()); else if (Token::Match(tok3, functionPattern) || Token::Match(tok3, breakPattern)) varsAssigned.clear(); } tok2 = endOfConditional; } else ++indentLevel; } else if (tok2->str() == "}") { --indentLevel; // End of the switch block if (indentLevel < 0) break; } // Variable assignment. Report an error if it's assigned to twice before a break. E.g.: // case 3: b = 1; // <== redundant // case 4: b = 2; if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;") && tok2->varId() != 0) { std::map::iterator i = varsAssigned.find(tok2->varId()); if (i == varsAssigned.end()) varsAssigned[tok2->varId()] = tok2; else redundantAssignmentInSwitchError(i->second, i->second->str()); } // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: // case 3: b = 1; // case 4: b++; else if (tok2->varId() != 0) varsAssigned.erase(tok2->varId()); // Reset our record of assignments if there is a break or function call. E.g.: // case 3: b = 1; break; if (Token::Match(tok2, functionPattern) || Token::Match(tok2, breakPattern)) varsAssigned.clear(); } tok = Token::findmatch(tok->next(), switchPattern); } } void CheckOther::checkSwitchCaseFallThrough() { if (!(_settings->isEnabled("style") && _settings->experimental)) return; const char switchPattern[] = "switch ("; const char breakPattern[] = "break|continue|return|exit|goto|throw"; // Find the beginning of a switch. E.g.: // switch (var) { ... const Token *tok = Token::findmatch(_tokenizer->tokens(), switchPattern); while (tok) { // Check the contents of the switch statement std::stack> ifnest; std::stack loopnest; std::stack scopenest; bool justbreak = true; bool firstcase = true; for (const Token *tok2 = tok->tokAt(1)->link()->tokAt(2); tok2; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "if (")) { tok2 = tok2->tokAt(1)->link()->next(); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched if in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } ifnest.push(std::make_pair(tok2->link(), false)); justbreak = false; } else if (Token::simpleMatch(tok2, "while (")) { tok2 = tok2->tokAt(1)->link()->next(); // skip over "do { } while ( ) ;" case if (tok2->str() == "{") { if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched while in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); } justbreak = false; } else if (Token::simpleMatch(tok2, "do {")) { tok2 = tok2->tokAt(1); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched do in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); justbreak = false; } else if (Token::simpleMatch(tok2, "for (")) { tok2 = tok2->tokAt(1)->link()->next(); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched for in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); justbreak = false; } else if (Token::Match(tok2, switchPattern)) { // skip over nested switch, we'll come to that soon tok2 = tok2->tokAt(1)->link()->next()->link(); } else if (Token::Match(tok2, breakPattern)) { if (loopnest.empty()) { justbreak = true; } tok2 = Token::findsimplematch(tok2, ";"); } else if (Token::Match(tok2, "case|default")) { if (!justbreak && !firstcase) { switchCaseFallThrough(tok2); } tok2 = Token::findsimplematch(tok2, ":"); justbreak = true; firstcase = false; } else if (tok2->str() == "{") { scopenest.push(tok2->link()); } else if (tok2->str() == "}") { if (!ifnest.empty() && tok2 == ifnest.top().first) { if (tok2->next()->str() == "else") { tok2 = tok2->tokAt(2); ifnest.pop(); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched if in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } ifnest.push(std::make_pair(tok2->link(), justbreak)); justbreak = false; } else { justbreak &= ifnest.top().second; ifnest.pop(); } } else if (!loopnest.empty() && tok2 == loopnest.top()) { loopnest.pop(); } else if (!scopenest.empty() && tok2 == scopenest.top()) { scopenest.pop(); } else { if (!ifnest.empty() || !loopnest.empty() || !scopenest.empty()) { std::ostringstream errmsg; errmsg << "unexpected end of switch: "; errmsg << "ifnest=" << ifnest.size(); if (!ifnest.empty()) errmsg << "," << ifnest.top().first->linenr(); errmsg << ", loopnest=" << loopnest.size(); if (!loopnest.empty()) errmsg << "," << loopnest.top()->linenr(); errmsg << ", scopenest=" << scopenest.size(); if (!scopenest.empty()) errmsg << "," << scopenest.top()->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); } // end of switch block break; } } else if (tok2->str() != ";") { justbreak = false; } } tok = Token::findmatch(tok->next(), switchPattern); } } //--------------------------------------------------------------------------- // int x = 1; // x = x; // <- redundant assignment to self // // int y = y; // <- redundant initialization to self //--------------------------------------------------------------------------- void CheckOther::checkSelfAssignment() { if (!_settings->isEnabled("style")) return; // POD variables.. std::set pod; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->isStandardType() && Token::Match(tok->tokAt(2), "[,);]") && tok->next()->varId()) pod.insert(tok->next()->varId()); } const char selfAssignmentPattern[] = "%var% = %var% ;|=|)"; const Token *tok = Token::findmatch(_tokenizer->tokens(), selfAssignmentPattern); while (tok) { if (Token::Match(tok->previous(), "[;{}]") && tok->varId() && tok->varId() == tok->tokAt(2)->varId() && pod.find(tok->varId()) != pod.end()) { selfAssignmentError(tok, tok->str()); } tok = Token::findmatch(tok->next(), selfAssignmentPattern); } } //--------------------------------------------------------------------------- // int a = 1; // assert(a = 2); // <- assert should not have a side-effect //--------------------------------------------------------------------------- void CheckOther::checkAssignmentInAssert() { if (!_settings->isEnabled("style")) return; const char assertPattern[] = "assert ( %any%"; const Token *tok = Token::findmatch(_tokenizer->tokens(), assertPattern); const Token *endTok = tok ? tok->next()->link() : NULL; while (tok && endTok) { const Token* varTok = Token::findmatch(tok->tokAt(2), "%var% --|++|+=|-=|*=|/=|&=|^=|=", endTok); if (varTok) { assignmentInAssertError(tok, varTok->str()); } else if (NULL != (varTok = Token::findmatch(tok->tokAt(2), "--|++ %var%", endTok))) { assignmentInAssertError(tok, varTok->strAt(1)); } tok = Token::findmatch(endTok->next(), assertPattern); endTok = tok ? tok->next()->link() : NULL; } } //--------------------------------------------------------------------------- // if ((x != 1) || (x != 3)) // <- always true // if ((x == 1) && (x == 3)) // <- always false // if ((x < 1) && (x > 3)) // <- always false // if ((x > 3) || (x < 10)) // <- always true //--------------------------------------------------------------------------- void CheckOther::checkIncorrectLogicOperator() { if (!_settings->isEnabled("style")) return; const char conditionPattern[] = "if|while ("; const Token *tok = Token::findmatch(_tokenizer->tokens(), conditionPattern); const Token *endTok = tok ? tok->next()->link() : NULL; while (tok && endTok) { // Find a pair of OR'd terms, with or without parentheses // e.g. if (x != 3 || x != 4) const Token *logicTok = NULL, *term1Tok = NULL, *term2Tok = NULL; const Token *op1Tok = NULL, *op2Tok = NULL, *op3Tok = NULL, *nextTok = NULL; if (NULL != (logicTok = Token::findmatch(tok, "( %any% !=|==|<|>|>=|<= %any% ) &&|%oror% ( %any% !=|==|<|>|>=|<= %any% ) %any%", endTok))) { term1Tok = logicTok->next(); term2Tok = logicTok->tokAt(7); op1Tok = logicTok->tokAt(2); op2Tok = logicTok->tokAt(5); op3Tok = logicTok->tokAt(8); nextTok = logicTok->tokAt(11); } else if (NULL != (logicTok = Token::findmatch(tok, "%any% !=|==|<|>|>=|<= %any% &&|%oror% %any% !=|==|<|>|>=|<= %any% %any%", endTok))) { term1Tok = logicTok; term2Tok = logicTok->tokAt(4); op1Tok = logicTok->tokAt(1); op2Tok = logicTok->tokAt(3); op3Tok = logicTok->tokAt(5); nextTok = logicTok->tokAt(7); } if (logicTok) { // Find the common variable and the two different-valued constants unsigned int variableTested = 0; std::string firstConstant, secondConstant; bool varFirst1, varFirst2; unsigned int varId; if (Token::Match(term1Tok, "%var% %any% %num%")) { varId = term1Tok->varId(); if (!varId) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst1 = true; firstConstant = term1Tok->tokAt(2)->str(); } else if (Token::Match(term1Tok, "%num% %any% %var%")) { varId = term1Tok->tokAt(2)->varId(); if (!varId) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst1 = false; firstConstant = term1Tok->str(); } else { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } if (Token::Match(term2Tok, "%var% %any% %num%")) { const unsigned int varId2 = term2Tok->varId(); if (!varId2 || varId != varId2) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst2 = true; secondConstant = term2Tok->tokAt(2)->str(); variableTested = varId; } else if (Token::Match(term2Tok, "%num% %any% %var%")) { const unsigned int varId2 = term1Tok->tokAt(2)->varId(); if (!varId2 || varId != varId2) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst2 = false; secondConstant = term2Tok->str(); variableTested = varId; } else { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } if (variableTested == 0 || firstConstant.empty() || secondConstant.empty()) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } enum Position { First, Second, NA }; enum Relation { Equal, NotEqual, Less, LessEqual, More, MoreEqual }; struct Condition { const char *before; Position position1; const char *op1TokStr; const char *op2TokStr; Position position2; const char *op3TokStr; const char *after; Relation relation; bool state; } conditions[] = { { "!!&&", NA, "!=", "||", NA, "!=", "!!&&", NotEqual, true }, // (x != 1) || (x != 3) <- always true { "(", NA, "==", "&&", NA, "==", ")", NotEqual, false }, // (x == 1) && (x == 3) <- always false { "(", First, "<", "&&", First, ">", ")", LessEqual, false }, // (x < 1) && (x > 3) <- always false { "(", First, ">", "&&", First, "<", ")", MoreEqual, false }, // (x > 3) && (x < 1) <- always false { "(", Second, ">", "&&", First, ">", ")", LessEqual, false }, // (1 > x) && (x > 3) <- always false { "(", First, ">", "&&", Second, ">", ")", MoreEqual, false }, // (x > 3) && (1 > x) <- always false { "(", First, "<", "&&", Second, "<", ")", LessEqual, false }, // (x < 1) && (3 < x) <- always false { "(", Second, "<", "&&", First, "<", ")", MoreEqual, false }, // (3 < x) && (x < 1) <- always false { "(", Second, ">", "&&", Second, "<", ")", LessEqual, false }, // (1 > x) && (3 < x) <- always false { "(", Second, "<", "&&", Second, ">", ")", MoreEqual, false }, // (3 < x) && (1 > x) <- always false { "(", First, ">|>=", "||", First, "<|<=", ")", Less, true }, // (x > 3) || (x < 10) <- always true { "(", First, "<|<=", "||", First, ">|>=", ")", More, true }, // (x < 10) || (x > 3) <- always true { "(", Second, "<|<=", "||", First, "<|<=", ")", Less, true }, // (3 < x) || (x < 10) <- always true { "(", First, "<|<=", "||", Second, "<|<=", ")", More, true }, // (x < 10) || (3 < x) <- always true { "(", First, ">|>=", "||", Second, ">|>=", ")", Less, true }, // (x > 3) || (10 > x) <- always true { "(", Second, ">|>=", "||", First, ">|>=", ")", More, true }, // (10 > x) || (x > 3) <- always true { "(", Second, "<|<=", "||", Second, ">|<=", ")", Less, true }, // (3 < x) || (10 > x) <- always true { "(", Second, ">|>=", "||", Second, "<|<=", ")", More, true }, // (10 > x) || (3 < x) <- always true }; for (unsigned int i = 0; i < (sizeof(conditions) / sizeof(conditions[0])); i++) { if (!((conditions[i].position1 == NA) || (((conditions[i].position1 == First) && varFirst1) || ((conditions[i].position1 == Second) && !varFirst1)))) continue; if (!((conditions[i].position2 == NA) || (((conditions[i].position2 == First) && varFirst2) || ((conditions[i].position2 == Second) && !varFirst2)))) continue; if (!Token::Match(op1Tok, conditions[i].op1TokStr)) continue; if (!Token::Match(op2Tok, conditions[i].op2TokStr)) continue; if (!Token::Match(op3Tok, conditions[i].op3TokStr)) continue; if (!Token::Match(logicTok->previous(), conditions[i].before)) continue; if (!Token::Match(nextTok, conditions[i].after)) continue; if ((conditions[i].relation == Equal && MathLib::isEqual(firstConstant, secondConstant)) || (conditions[i].relation == NotEqual && MathLib::isNotEqual(firstConstant, secondConstant)) || (conditions[i].relation == Less && MathLib::isLess(firstConstant, secondConstant)) || (conditions[i].relation == LessEqual && MathLib::isLessEqual(firstConstant, secondConstant)) || (conditions[i].relation == More && MathLib::isGreater(firstConstant, secondConstant)) || (conditions[i].relation == MoreEqual && MathLib::isGreaterEqual(firstConstant, secondConstant))) incorrectLogicOperatorError(term1Tok, conditions[i].state); } } tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; } } //--------------------------------------------------------------------------- // try {} catch (std::exception err) {} <- Should be "std::exception& err" //--------------------------------------------------------------------------- void CheckOther::checkCatchExceptionByValue() { if (!_settings->isEnabled("style")) return; const char catchPattern[] = "} catch ("; const Token *tok = Token::findmatch(_tokenizer->tokens(), catchPattern); const Token *endTok = tok ? tok->tokAt(2)->link() : NULL; while (tok && endTok) { // Find a pass-by-value declaration in the catch(), excluding basic types // e.g. catch (std::exception err) const Token *tokType = Token::findmatch(tok, "%type% %var% )", endTok); if (tokType && !tokType->isStandardType()) { catchExceptionByValueError(tokType); } tok = Token::findmatch(endTok->next(), catchPattern); endTok = tok ? tok->tokAt(2)->link() : NULL; } } //--------------------------------------------------------------------------- // strtol(str, 0, radix) <- radix must be 0 or 2-36 //--------------------------------------------------------------------------- void CheckOther::invalidFunctionUsage() { // strtol and strtoul.. for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "strtol|strtoul (")) continue; // Locate the third parameter of the function call.. int param = 1; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")") break; else if (tok2->str() == ",") { ++param; if (param == 3) { if (Token::Match(tok2, ", %num% )")) { const MathLib::bigint radix = MathLib::toLongNumber(tok2->next()->str()); if (!(radix == 0 || (radix >= 2 && radix <= 36))) { dangerousUsageStrtolError(tok2); } } break; } } } } // sprintf|snprintf overlapping data for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Get variable id of target buffer.. unsigned int varid = 0; if (Token::Match(tok, "sprintf|snprintf ( %var% ,")) varid = tok->tokAt(2)->varId(); else if (Token::Match(tok, "sprintf|snprintf ( %var% . %var% ,")) varid = tok->tokAt(4)->varId(); if (varid == 0) continue; // goto "," const Token *tok2 = tok->tokAt(3); while (tok2 && tok2->str() != ",") tok2 = tok2->next(); if (!tok2) continue; // is any source buffer overlapping the target buffer? int parlevel = 0; while ((tok2 = tok2->next()) != NULL) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { --parlevel; if (parlevel < 0) break; } else if (parlevel == 0 && Token::Match(tok2, ", %varid% [,)]", varid)) { sprintfOverlappingDataError(tok2->next(), tok2->next()->str()); break; } } } } //--------------------------------------------------------------------------- void CheckOther::invalidScanf() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { const Token *formatToken = 0; if (Token::Match(tok, "scanf|vscanf ( %str% ,")) formatToken = tok->tokAt(2); else if (Token::Match(tok, "fscanf|vfscanf ( %var% , %str% ,")) formatToken = tok->tokAt(4); else continue; bool format = false; // scan the string backwards, so we don't need to keep states const std::string &formatstr(formatToken->str()); for (unsigned int i = 1; i < formatstr.length(); i++) { if (formatstr[i] == '%') format = !format; else if (!format) continue; else if (std::isdigit(formatstr[i])) { format = false; } else if (std::isalpha(formatstr[i])) { invalidScanfError(tok); format = false; } } } } //--------------------------------------------------------------------------- // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- void CheckOther::checkComparisonOfBoolWithInt() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "( ! %var% ==|!= %num% )")) { const Token *numTok = tok->tokAt(4); if (numTok && numTok->str() != "0") { comparisonOfBoolWithIntError(numTok, tok->strAt(2)); } } else if (Token::Match(tok, "( %num% ==|!= ! %var% )")) { const Token *numTok = tok->tokAt(1); if (numTok && numTok->str() != "0") { comparisonOfBoolWithIntError(numTok, tok->strAt(4)); } } } } //--------------------------------------------------------------------------- // switch (x) // { // case 2: // y = a; // break; // break; // <- Redundant break // case 3: // y = b; // } //--------------------------------------------------------------------------- void CheckOther::checkDuplicateBreak() { if (!_settings->isEnabled("style")) return; const char breakPattern[] = "break|continue ; break|continue ;"; // Find consecutive break or continue statements. e.g.: // break; break; const Token *tok = Token::findmatch(_tokenizer->tokens(), breakPattern); while (tok) { duplicateBreakError(tok); tok = Token::findmatch(tok->next(), breakPattern); } } void CheckOther::sizeofForNumericParameterError(const Token *tok) { reportError(tok, Severity::error, "sizeofwithnumericparameter", "Using sizeof with a numeric constant as function " "argument might not be what you intended.\n" "It is unusual to use constant value with sizeof. For example, this code:\n" " int f() {\n" " return sizeof(10);\n" " }\n" " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10." ); } void CheckOther::sizeofForArrayParameterError(const Token *tok) { reportError(tok, Severity::error, "sizeofwithsilentarraypointer", "Using sizeof for array given as function argument " "returns the size of pointer.\n" "Giving array as function parameter and then using sizeof-operator for the array " "argument. In this case the sizeof-operator returns the size of pointer (in the " "system). It does not return the size of the whole array in bytes as might be " "expected. For example, this code:\n" " int f(char a[100]) {\n" " return sizeof(a);\n" " }\n" " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the " "size of the array in bytes)." ); } void CheckOther::invalidScanfError(const Token *tok) { reportError(tok, Severity::warning, "invalidscanf", "scanf without field width limits can crash with huge input data\n" "scanf without field width limits can crash with huge input data. To fix this error " "message add a field width specifier:\n" " %s => %20s\n" " %i => %3i\n" "\n" "Sample program that can crash:\n" "\n" "#include \n" "int main()\n" "{\n" " int a;\n" " scanf(\"%i\", &a);\n" " return 0;\n" "}\n" "\n" "To make it crash:\n" "perl -e 'print \"5\"x2100000' | ./a.out"); } //--------------------------------------------------------------------------- // Check for unsigned divisions //--------------------------------------------------------------------------- void CheckOther::checkUnsignedDivision() { if (!_settings->isEnabled("style")) return; // Check for "ivar / uvar" and "uvar / ivar" std::map varsign; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[{};(,] %type% %var% [;=,)]")) { if (tok->tokAt(1)->isUnsigned()) varsign[tok->tokAt(2)->varId()] = 'u'; else varsign[tok->tokAt(2)->varId()] = 's'; } else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / %num%")) { if (tok->strAt(3)[0] == '-') { char sign1 = varsign[tok->tokAt(1)->varId()]; if (sign1 == 'u') { udivError(tok->next()); } } } else if (Token::Match(tok, "(|[|=|%op% %num% / %var%")) { if (tok->strAt(1)[0] == '-') { char sign2 = varsign[tok->tokAt(3)->varId()]; if (sign2 == 'u') { udivError(tok->next()); } } } } } //--------------------------------------------------------------------------- // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted //--------------------------------------------------------------------------- void CheckOther::checkMemsetZeroBytes() { const Token *tok = _tokenizer->tokens(); while (tok && ((tok = Token::findmatch(tok, "memset ( %var% , %num% , 0 )")) != NULL)) { memsetZeroBytesError(tok, tok->strAt(2)); tok = tok->tokAt(8); } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- /** * @brief This class is used to capture the control flow within a function. */ class ScopeInfo { public: ScopeInfo() : _token(NULL), _parent(NULL) {} ScopeInfo(const Token *token, ScopeInfo *parent_) : _token(token), _parent(parent_) {} ~ScopeInfo(); ScopeInfo *parent() { return _parent; } ScopeInfo *addChild(const Token *token); void remove(ScopeInfo *scope); private: const Token *_token; ScopeInfo *_parent; std::list _children; }; ScopeInfo::~ScopeInfo() { while (!_children.empty()) { delete *_children.begin(); _children.pop_front(); } } ScopeInfo *ScopeInfo::addChild(const Token *token) { ScopeInfo *temp = new ScopeInfo(token, this); _children.push_back(temp); return temp; } void ScopeInfo::remove(ScopeInfo *scope) { std::list::iterator it; for (it = _children.begin(); it != _children.end(); ++it) { if (*it == scope) { delete *it; _children.erase(it); break; } } } /** * @brief This class is used create a list of variables within a function. */ class Variables { public: enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer }; /** Store information about variable usage */ class VariableUsage { public: VariableUsage(const Token *name = 0, VariableType type = standard, ScopeInfo *scope = NULL, bool read = false, bool write = false, bool modified = false, bool allocateMemory = false) : _name(name), _type(type), _scope(scope), _read(read), _write(write), _modified(modified), _allocateMemory(allocateMemory) {} /** variable is used.. set both read+write */ void use() { _read = true; _write = true; } /** is variable unused? */ bool unused() const { return (!_read && !_write); } const Token *_name; VariableType _type; ScopeInfo *_scope; bool _read; bool _write; bool _modified; // read/modify/write bool _allocateMemory; std::set _aliases; std::set _assignments; }; typedef std::map VariableMap; void clear() { _varUsage.clear(); } VariableMap &varUsage() { return _varUsage; } void addVar(const Token *name, VariableType type, ScopeInfo *scope, bool write_); void allocateMemory(unsigned int varid); void read(unsigned int varid); void readAliases(unsigned int varid); void readAll(unsigned int varid); void write(unsigned int varid); void writeAliases(unsigned int varid); void writeAll(unsigned int varid); void use(unsigned int varid); void modified(unsigned int varid); VariableUsage *find(unsigned int varid); void alias(unsigned int varid1, unsigned int varid2, bool replace); void erase(unsigned int varid) { _varUsage.erase(varid); } void eraseAliases(unsigned int varid); void eraseAll(unsigned int varid); void clearAliases(unsigned int varid); private: VariableMap _varUsage; }; /** * Alias the 2 given variables. Either replace the existing aliases if * they exist or merge them. You would replace an existing alias when this * assignment is in the same scope as the previous assignment. You might * merge the aliases when this assignment is in a different scope from the * previous assignment depending on the relationship of the 2 scopes. */ void Variables::alias(unsigned int varid1, unsigned int varid2, bool replace) { VariableUsage *var1 = find(varid1); VariableUsage *var2 = find(varid2); // alias to self if (varid1 == varid2) { if (var1) var1->use(); return; } std::set::iterator i; if (replace) { // remove var1 from all aliases for (i = var1->_aliases.begin(); i != var1->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(var1->_name->varId()); } // remove all aliases from var1 var1->_aliases.clear(); } // var1 gets all var2s aliases for (i = var2->_aliases.begin(); i != var2->_aliases.end(); ++i) { if (*i != varid1) var1->_aliases.insert(*i); } // var2 is an alias of var1 var2->_aliases.insert(varid1); var1->_aliases.insert(varid2); if (var2->_type == Variables::pointer) var2->_read = true; } void Variables::clearAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { // remove usage from all aliases std::set::iterator i; for (i = usage->_aliases.begin(); i != usage->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(usage->_name->varId()); } // remove all aliases from usage usage->_aliases.clear(); } } void Variables::eraseAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) erase(*aliases); } } void Variables::eraseAll(unsigned int varid) { eraseAliases(varid); erase(varid); } void Variables::addVar(const Token *name, VariableType type, ScopeInfo *scope, bool write_) { if (name->varId() > 0) _varUsage.insert(std::make_pair(name->varId(), VariableUsage(name, type, scope, false, write_, false))); } void Variables::allocateMemory(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) usage->_allocateMemory = true; } void Variables::read(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) usage->_read = true; } void Variables::readAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_read = true; } } } void Variables::readAll(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->_read = true; std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_read = true; } } } void Variables::write(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) usage->_write = true; } void Variables::writeAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_write = true; } } } void Variables::writeAll(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->_write = true; std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_write = true; } } } void Variables::use(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->use(); std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->use(); } } } void Variables::modified(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->_modified = true; std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_modified = true; } } } Variables::VariableUsage *Variables::find(unsigned int varid) { if (varid) { VariableMap::iterator i = _varUsage.find(varid); if (i != _varUsage.end()) return &i->second; } return 0; } static int doAssignment(Variables &variables, const Token *tok, bool dereference, ScopeInfo *scope) { int next = 0; // a = a + b; if (Token::Match(tok, "%var% = %var% !!;") && tok->str() == tok->strAt(2)) { return 2; } // check for aliased variable const unsigned int varid1 = tok->varId(); Variables::VariableUsage *var1 = variables.find(varid1); if (var1) { int start = 1; // search for '=' while (tok->tokAt(start)->str() != "=") start++; start++; if (Token::Match(tok->tokAt(start), "&| %var%") || Token::Match(tok->tokAt(start), "( const| struct|union| %type% *| ) &| %var%") || Token::Match(tok->tokAt(start), "( const| struct|union| %type% *| ) ( &| %var%") || Token::Match(tok->tokAt(start), "%any% < const| struct|union| %type% *| > ( &| %var%")) { unsigned char offset = 0; unsigned int varid2; bool addressOf = false; if (Token::Match(tok->tokAt(start), "%var% .")) variables.use(tok->tokAt(start)->varId()); // use = read + write // check for C style cast if (tok->tokAt(start)->str() == "(") { if (tok->tokAt(start + 1)->str() == "const") offset++; if (Token::Match(tok->tokAt(start + 1 + offset), "struct|union")) offset++; if (tok->tokAt(start + 2 + offset)->str() == "*") offset++; if (tok->tokAt(start + 3 + offset)->str() == "&") { addressOf = true; next = start + 4 + offset; } else if (tok->tokAt(start + 3 + offset)->str() == "(") { if (tok->tokAt(start + 4 + offset)->str() == "&") { addressOf = true; next = start + 5 + offset; } else next = start + 4 + offset; } else next = start + 3 + offset; } // check for C++ style cast else if (tok->tokAt(start)->str().find("cast") != std::string::npos && tok->tokAt(start + 1)->str() == "<") { if (tok->tokAt(start + 2)->str() == "const") offset++; if (Token::Match(tok->tokAt(start + 2 + offset), "struct|union")) offset++; if (tok->tokAt(start + 3 + offset)->str() == "*") offset++; if (tok->tokAt(start + 5 + offset)->str() == "&") { addressOf = true; next = start + 6 + offset; } else next = start + 5 + offset; } // check for var ? ... else if (Token::Match(tok->tokAt(start), "%var% ?")) { next = start; } // no cast else { if (tok->tokAt(start)->str() == "&") { addressOf = true; next = start + 1; } else if (tok->tokAt(start)->str() == "new") return 0; else next = start; } // check if variable is local varid2 = tok->tokAt(next)->varId(); Variables::VariableUsage *var2 = variables.find(varid2); if (var2) { // local variable (alias or read it) if (var1->_type == Variables::pointer) { if (dereference) variables.read(varid2); else { if (addressOf || var2->_type == Variables::array || var2->_type == Variables::pointer) { bool replace = true; // check if variable declared in same scope if (scope == var1->_scope) replace = true; // not in same scope as declaration else { std::set::iterator assignment; // check for an assignment in this scope assignment = var1->_assignments.find(scope); // no other assignment in this scope if (assignment == var1->_assignments.end()) { // nothing to replace if (var1->_assignments.empty()) replace = false; // this variable has previous assignments else { /** * @todo determine if existing aliases should be replaced or merged */ replace = false; } } // assignment in this scope else { // replace when only one other assignment if (var1->_assignments.size() == 1) replace = true; // otherwise, merge them else replace = false; } } variables.alias(varid1, varid2, replace); } else if (tok->tokAt(next + 1)->str() == "?") { if (var2->_type == Variables::reference) variables.readAliases(varid2); else variables.read(varid2); } } } else if (var1->_type == Variables::reference) { variables.alias(varid1, varid2, true); } else { if (var2->_type == Variables::pointer && tok->tokAt(next + 1)->str() == "[") variables.readAliases(varid2); variables.read(varid2); } } else { // not a local variable (or an unsupported local variable) if (var1->_type == Variables::pointer && !dereference) { // check if variable declaration is in this scope if (var1->_scope == scope) variables.clearAliases(varid1); else { std::set::iterator assignment; // check for an assignment in this scope assignment = var1->_assignments.find(scope); // no other assignment in this scope if (assignment == var1->_assignments.end()) { /** * @todo determine if existing aliases should be discarded */ } // this assignment replaces the last assignment in this scope else { // aliased variables in a larger scope are not supported // remove all aliases variables.clearAliases(varid1); } } } } } var1->_assignments.insert(scope); } // check for alias to struct member // char c[10]; a.b = c; else if (Token::Match(tok->tokAt(-2), "%var% .")) { if (Token::Match(tok->tokAt(2), "%var%")) { unsigned int varid2 = tok->tokAt(2)->varId(); Variables::VariableUsage *var2 = variables.find(varid2); // struct member aliased to local variable if (var2 && (var2->_type == Variables::array || var2->_type == Variables::pointer)) { // erase aliased variable and all variables that alias it // to prevent false positives variables.eraseAll(varid2); } } } return next; } static bool nextIsStandardType(const Token *tok) { tok = tok->next(); if (tok->str() == "static") tok = tok->next(); return tok->isStandardType(); } static bool nextIsStandardTypeOrVoid(const Token *tok) { tok = tok->next(); if (tok->str() == "static") tok = tok->next(); if (tok->str() == "const") tok = tok->next(); return tok->isStandardType() || tok->str() == "void"; } bool CheckOther::isRecordTypeWithoutSideEffects(const Token *tok) { const Variable * var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok->varId()); // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check base class for side effects */ /** @todo false negative: check constructors for side effects */ if (var && var->type() && var->type()->numConstructors == 0 && (var->type()->varlist.empty() || var->type()->needInitialization == Scope::True) && var->type()->derivedFrom.empty()) return true; return false; } void CheckOther::functionVariableUsage() { if (!_settings->isEnabled("style")) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // First token for the current scope.. const Token *const tok1 = scope->classStart; // varId, usage {read, write, modified} Variables variables; // scopes ScopeInfo scopes; ScopeInfo *info = &scopes; unsigned int indentlevel = 0; for (const Token *tok = tok1; tok; tok = tok->next()) { if (tok->str() == "{") { // replace the head node when found if (indentlevel == 0) scopes = ScopeInfo(tok, NULL); // add the new scope else info = info->addChild(tok); ++indentlevel; } else if (tok->str() == "}") { --indentlevel; info = info->parent(); if (indentlevel == 0) break; } else if (Token::Match(tok, "struct|union|class {") || Token::Match(tok, "struct|union|class %type% {|:")) { while (tok->str() != "{") tok = tok->next(); tok = tok->link(); if (!tok) break; } if (Token::Match(tok, "[;{}] asm ( ) ;")) { variables.clear(); break; } // standard type declaration with possible initialization // int i; int j = 0; static int k; if (Token::Match(tok, "[;{}] static| %type% %var% ;|=") && !Token::Match(tok->next(), "return|throw")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->isStandardType() || isRecordTypeWithoutSideEffects(tok->next())) { variables.addVar(tok->next(), Variables::standard, info, tok->tokAt(2)->str() == "=" || isStatic); } tok = tok->next(); } // standard const type declaration // const int i = x; else if (Token::Match(tok, "[;{}] const %type% %var% =")) { tok = tok->next()->next(); if (tok->isStandardType() || isRecordTypeWithoutSideEffects(tok->next())) variables.addVar(tok->next(), Variables::standard, info, true); tok = tok->next(); } // std::string declaration with possible initialization // std::string s; std::string s = "string"; else if (Token::Match(tok, "[;{}] static| std :: string %var% ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); tok = tok->tokAt(3); variables.addVar(tok, Variables::standard, info, tok->next()->str() == "=" || isStatic); } // standard struct type declaration with possible initialization // struct S s; struct S s = { 0 }; static struct S s; else if (Token::Match(tok, "[;{}] static| struct %type% %var% ;|=") && (isRecordTypeWithoutSideEffects(tok->strAt(1) == "static" ? tok->tokAt(4) : tok->tokAt(3)))) { tok = tok->next(); bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); tok = tok->next(); variables.addVar(tok->next(), Variables::standard, info, tok->tokAt(2)->str() == "=" || isStatic); tok = tok->next(); } // standard type declaration and initialization using constructor // int i(0); static int j(0); else if (Token::Match(tok, "[;{}] static| %type% %var% ( %any% ) ;") && nextIsStandardType(tok)) { tok = tok->next(); if (tok->str() == "static") tok = tok->next(); variables.addVar(tok->next(), Variables::standard, info, true); // check if a local variable is used to initialize this variable if (tok->tokAt(3)->varId() > 0) variables.readAll(tok->tokAt(3)->varId()); tok = tok->tokAt(4); } // standard type declaration of array of with possible initialization // int i[10]; int j[2] = { 0, 1 }; static int k[2] = { 2, 3 }; else if (Token::Match(tok, "[;{}] static| const| %type% *| %var% [ %any% ] ;|=") && nextIsStandardType(tok)) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->str() != "return" && tok->str() != "throw") { bool isPointer = bool(tok->strAt(1) == "*"); const Token * const nametok = tok->tokAt(isPointer ? 2 : 1); variables.addVar(nametok, isPointer ? Variables::pointerArray : Variables::array, info, nametok->tokAt(4)->str() == "=" || isStatic); // check for reading array size from local variable if (nametok->tokAt(2)->varId() != 0) variables.read(nametok->tokAt(2)->varId()); // look at initializers if (Token::simpleMatch(nametok->tokAt(4), "= {")) { tok = nametok->tokAt(6); while (tok->str() != "}") { if (Token::Match(tok, "%var%")) variables.read(tok->varId()); tok = tok->next(); } } else tok = nametok->tokAt(3); } } // pointer or reference declaration with possible initialization // int * i; int * j = 0; static int * k = 0; else if (Token::Match(tok, "[;{}] static| const| %type% *|& %var% ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->strAt(1) == "::") tok = tok->tokAt(2); if (tok->str() != "return" && tok->str() != "throw") { Variables::VariableType type; if (tok->next()->str() == "*") type = Variables::pointer; else type = Variables::reference; bool written = tok->tokAt(3)->str() == "="; variables.addVar(tok->tokAt(2), type, info, written || isStatic); int offset = 0; // check for assignment if (written) offset = doAssignment(variables, tok->tokAt(2), false, info); tok = tok->tokAt(2 + offset); } } // pointer to pointer declaration with possible initialization // int ** i; int ** j = 0; static int ** k = 0; else if (Token::Match(tok, "[;{}] static| const| %type% * * %var% ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->str() != "return") { bool written = tok->tokAt(4)->str() == "="; variables.addVar(tok->tokAt(3), Variables::pointerPointer, info, written || isStatic); int offset = 0; // check for assignment if (written) offset = doAssignment(variables, tok->tokAt(3), false, info); tok = tok->tokAt(3 + offset); } } // pointer or reference of struct or union declaration with possible initialization // struct s * i; struct s * j = 0; static struct s * k = 0; else if (Token::Match(tok, "[;{}] static| const| struct|union %type% *|& %var% ;|=")) { Variables::VariableType type; tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->strAt(2) == "*") type = Variables::pointer; else type = Variables::reference; const bool written = tok->strAt(4) == "="; variables.addVar(tok->tokAt(3), type, info, written || isStatic); int offset = 0; // check for assignment if (written) offset = doAssignment(variables, tok->tokAt(3), false, info); tok = tok->tokAt(3 + offset); } // pointer or reference declaration with initialization using constructor // int * i(j); int * k(i); static int * l(i); else if (Token::Match(tok, "[;{}] static| const| %type% &|* %var% ( %any% ) ;") && nextIsStandardTypeOrVoid(tok)) { Variables::VariableType type; tok = tok->next(); if (tok->str() == "static") tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->next()->str() == "*") type = Variables::pointer; else type = Variables::reference; unsigned int varid = 0; // check for aliased variable if (Token::Match(tok->tokAt(4), "%var%")) varid = tok->tokAt(4)->varId(); variables.addVar(tok->tokAt(2), type, info, true); // check if a local variable is used to initialize this variable if (varid > 0) { Variables::VariableUsage *var = variables.find(varid); if (type == Variables::pointer) { variables.use(tok->tokAt(4)->varId()); if (var && (var->_type == Variables::array || var->_type == Variables::pointer)) var->_aliases.insert(tok->varId()); } else { variables.readAll(tok->tokAt(4)->varId()); if (var) var->_aliases.insert(tok->varId()); } } tok = tok->tokAt(5); } // array of pointer or reference declaration with possible initialization // int * p[10]; int * q[10] = { 0 }; static int * * r[10] = { 0 }; else if (Token::Match(tok, "[;{}] static| const| %type% *|& %var% [ %any% ] ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->str() != "return") { variables.addVar(tok->tokAt(2), tok->next()->str() == "*" ? Variables::pointerArray : Variables::referenceArray, info, tok->tokAt(6)->str() == "=" || isStatic); // check for reading array size from local variable if (tok->tokAt(4)->varId() != 0) variables.read(tok->tokAt(4)->varId()); tok = tok->tokAt(5); } } // array of pointer or reference of struct or union declaration with possible initialization // struct S * p[10]; struct T * q[10] = { 0 }; static struct S * r[10] = { 0 }; else if (Token::Match(tok, "[;{}] static| const| struct|union %type% *|& %var% [ %any% ] ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); variables.addVar(tok->tokAt(3), tok->tokAt(2)->str() == "*" ? Variables::pointerArray : Variables::referenceArray, info, tok->tokAt(7)->str() == "=" || isStatic); // check for reading array size from local variable if (tok->tokAt(5)->varId() != 0) variables.read(tok->tokAt(5)->varId()); tok = tok->tokAt(6); } // Freeing memory (not considered "using" the pointer if it was also allocated in this function) else if (Token::Match(tok, "free|g_free|kfree|vfree ( %var% )") || Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")) { unsigned int varid = 0; if (tok->str() != "delete") { varid = tok->tokAt(2)->varId(); tok = tok->tokAt(3); } else if (tok->strAt(1) == "[") { varid = tok->tokAt(3)->varId(); tok = tok->tokAt(4); } else { varid = tok->next()->varId(); tok = tok->tokAt(2); } Variables::VariableUsage *var = variables.find(varid); if (var && !var->_allocateMemory) { variables.readAll(varid); } } else if (Token::Match(tok, "return|throw %var%")) variables.readAll(tok->next()->varId()); // assignment else if (Token::Match(tok, "*| (| ++|--| %var% ++|--| )| =") || Token::Match(tok, "*| ( const| %type% *| ) %var% =")) { bool dereference = false; bool pre = false; bool post = false; if (tok->str() == "*") { dereference = true; tok = tok->next(); } if (Token::Match(tok, "( const| %type% *| ) %var% =")) tok = tok->link()->next(); else if (tok->str() == "(") tok = tok->next(); if (Token::Match(tok, "++|--")) { pre = true; tok = tok->next(); } if (Token::Match(tok->next(), "++|--")) post = true; const unsigned int varid1 = tok->varId(); const Token *start = tok; tok = tok->tokAt(doAssignment(variables, tok, dereference, info)); if (pre || post) variables.use(varid1); if (dereference) { Variables::VariableUsage *var = variables.find(varid1); if (var && var->_type == Variables::array) variables.write(varid1); variables.writeAliases(varid1); variables.read(varid1); } else { Variables::VariableUsage *var = variables.find(varid1); if (var && var->_type == Variables::reference) { variables.writeAliases(varid1); variables.read(varid1); } // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable else if (var && var->_type == Variables::pointer && Token::Match(start, "%var% = new|malloc|calloc|g_malloc|kmalloc|vmalloc")) { bool allocate = true; if (start->strAt(2) == "new") { // is it a user defined type? if (!start->tokAt(3)->isStandardType()) { if (!isRecordTypeWithoutSideEffects(start)) allocate = false; } } if (allocate) variables.allocateMemory(varid1); else variables.write(varid1); } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) { variables.use(varid1); } else { variables.write(varid1); } Variables::VariableUsage *var2 = variables.find(tok->varId()); if (var2) { if (var2->_type == Variables::reference) { variables.writeAliases(tok->varId()); variables.read(tok->varId()); } else if (tok->varId() != varid1 && Token::Match(tok, "%var% .")) variables.read(tok->varId()); else if (tok->varId() != varid1 && var2->_type == Variables::standard && tok->strAt(-1) != "&") variables.use(tok->varId()); } } const Token *equal = tok->next(); if (Token::Match(tok->next(), "[ %any% ]")) equal = tok->tokAt(4); // checked for chained assignments if (tok != start && equal->str() == "=") { Variables::VariableUsage *var = variables.find(tok->varId()); if (var && var->_type != Variables::reference) var->_read = true; tok = tok->previous(); } } // assignment else if (Token::Match(tok, "%var% [") && Token::simpleMatch(tok->next()->link(), "] =")) { unsigned int varid = tok->varId(); const Variables::VariableUsage *var = variables.find(varid); if (var) { // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable if (var->_type == Variables::pointer && Token::Match(tok->next()->link(), "] = new|malloc|calloc|g_malloc|kmalloc|vmalloc")) { variables.allocateMemory(varid); } else if (var->_type == Variables::pointer || var->_type == Variables::reference) { variables.read(varid); variables.writeAliases(varid); } else variables.writeAll(varid); } } else if (Token::Match(tok, ">>|& %var%")) variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "[;{}] %var% >>")) variables.use(tok->next()->varId()); // use = read + write // function parameter else if (Token::Match(tok, "[(,] %var% [")) variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) variables.use(tok->next()->link()->next()->varId()); // use = read + write // function else if (Token::Match(tok, "%var% (")) { variables.read(tok->varId()); if (Token::Match(tok->tokAt(2), "%var% =")) variables.read(tok->tokAt(2)->varId()); } else if (Token::Match(tok, "[{,] %var% [,}]")) variables.read(tok->next()->varId()); else if (Token::Match(tok, "%var% .")) variables.use(tok->varId()); // use = read + write else if ((Token::Match(tok, "[(=&!]") || tok->isExtendedOp()) && (Token::Match(tok->next(), "%var%") && !Token::Match(tok->next(), "true|false|new"))) variables.readAll(tok->next()->varId()); else if (Token::Match(tok, "%var%") && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) variables.readAll(tok->varId()); else if (Token::Match(tok, "; %var% ;")) variables.readAll(tok->next()->varId()); if (Token::Match(tok, "++|-- %var%")) { if (tok->strAt(-1) != ";") variables.use(tok->next()->varId()); else variables.modified(tok->next()->varId()); } else if (Token::Match(tok, "%var% ++|--")) { if (tok->strAt(-1) != ";") variables.use(tok->varId()); else variables.modified(tok->varId()); } else if (tok->isAssignmentOp()) { for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId()) { variables.read(tok2->varId()); if (tok2->next()->isAssignmentOp()) variables.write(tok2->varId()); } } } } // Check usage of all variables in the current scope.. Variables::VariableMap::const_iterator it; for (it = variables.varUsage().begin(); it != variables.varUsage().end(); ++it) { const Variables::VariableUsage &usage = it->second; const std::string &varname = usage._name->str(); // variable has been marked as unused so ignore it if (usage._name->isUnused()) continue; // skip things that are only partially implemented to prevent false positives if (usage._type == Variables::pointerPointer || usage._type == Variables::pointerArray || usage._type == Variables::referenceArray) continue; // variable has had memory allocated for it, but hasn't done // anything with that memory other than, perhaps, freeing it if (usage.unused() && !usage._modified && usage._allocateMemory) allocatedButUnusedVariableError(usage._name, varname); // variable has not been written, read, or modified else if (usage.unused() && !usage._modified) unusedVariableError(usage._name, varname); // variable has not been written but has been modified else if (usage._modified & !usage._write) unassignedVariableError(usage._name, varname); // variable has been written but not read else if (!usage._read && !usage._modified) unreadVariableError(usage._name, varname); // variable has been read but not written else if (!usage._write && !usage._allocateMemory) unassignedVariableError(usage._name, varname); } } } void CheckOther::unusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedVariable", "Unused variable: " + varname); } void CheckOther::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedAllocatedMemory", "Variable '" + varname + "' is allocated memory that is never used"); } void CheckOther::unreadVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is assigned a value that is never used"); } void CheckOther::unassignedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unassignedVariable", "Variable '" + varname + "' is not assigned a value"); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check scope of variables.. //--------------------------------------------------------------------------- void CheckOther::checkVariableScope() { if (!_settings->isEnabled("information")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // Walk through all tokens.. int indentlevel = 0; for (const Token *tok = scope->classStart; tok; tok = tok->next()) { // Skip function local class and struct declarations.. if ((tok->str() == "class") || (tok->str() == "struct") || (tok->str() == "union")) { for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { tok = tok2->link(); break; } if (Token::Match(tok2, "[,);]")) { break; } } if (!tok) break; } else if (tok->str() == "{") { ++indentlevel; } else if (tok->str() == "}") { --indentlevel; if (indentlevel == 0) break; } if (indentlevel > 0 && Token::Match(tok, "[{};]")) { // First token of statement.. const Token *tok1 = tok->next(); if (!tok1) continue; if ((tok1->str() == "return") || (tok1->str() == "throw") || (tok1->str() == "delete") || (tok1->str() == "goto") || (tok1->str() == "else")) continue; // Variable declaration? if (Token::Match(tok1, "%type% %var% ; %var% = %num% ;")) { // Tokenizer modify "int i = 0;" to "int i; i = 0;", // so to handle this situation we just skip // initialization (see ticket #272). const unsigned int firstVarId = tok1->next()->varId(); const unsigned int secondVarId = tok1->tokAt(3)->varId(); if (firstVarId > 0 && firstVarId == secondVarId) { lookupVar(tok1->tokAt(6), tok1->strAt(1)); } } else if (tok1->isStandardType() && Token::Match(tok1, "%type% %var% [;=]")) { lookupVar(tok1, tok1->strAt(1)); } } } } } //--------------------------------------------------------------------------- void CheckOther::lookupVar(const Token *tok1, const std::string &varname) { const Token *tok = tok1; // Skip the variable declaration.. while (tok && tok->str() != ";") tok = tok->next(); // Check if the variable is used in this indentlevel.. bool used1 = false; // used in one sub-scope -> reducable bool used2 = false; // used in more sub-scopes -> not reducable int indentlevel = 0; int parlevel = 0; bool for_or_while = false; // is sub-scope a "for/while/etc". anything that is not "if" while (tok) { if (tok->str() == "{") { if (tok->strAt(-1) == "=") { if (Token::findmatch(tok, varname.c_str(), tok->link())) { return; } tok = tok->link(); } else ++indentlevel; } else if (tok->str() == "}") { if (indentlevel == 0) break; --indentlevel; if (indentlevel == 0) { if (for_or_while && used2) return; used2 |= used1; used1 = false; } } else if (tok->str() == "(") { ++parlevel; } else if (tok->str() == ")") { --parlevel; } // Bail out if references are used else if (Token::simpleMatch(tok, (std::string("& ") + varname).c_str())) { return; } else if (tok->str() == varname) { if (indentlevel == 0) return; used1 = true; if (for_or_while && !Token::simpleMatch(tok->next(), "=")) used2 = true; if (used1 && used2) return; } else if (indentlevel == 0) { // %unknown% ( %any% ) { // If %unknown% is anything except if, we assume // that it is a for or while loop or a macro hiding either one if (Token::simpleMatch(tok->next(), "(") && Token::simpleMatch(tok->next()->link(), ") {")) { if (tok->str() != "if") for_or_while = true; } else if (Token::simpleMatch(tok, "do {")) for_or_while = true; // possible unexpanded macro hiding for/while.. else if (tok->str() != "else" && Token::Match(tok->previous(), "[;{}] %type% {")) { for_or_while = true; } if (parlevel == 0 && (tok->str() == ";")) for_or_while = false; } tok = tok->next(); } // Warning if this variable: // * not used in this indentlevel // * used in lower indentlevel if (used1 || used2) variableScopeError(tok1, varname); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check for constant function parameters //--------------------------------------------------------------------------- void CheckOther::checkConstantFunctionParameter() { if (!_settings->isEnabled("style")) return; const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // TODO: False negatives. This pattern only checks for string. // Investigate if there are other classes in the std // namespace and add them to the pattern. There are // streams for example (however it seems strange with // const stream parameter). if (Token::Match(tok, "[,(] const std :: string %var% [,)]")) { passedByValueError(tok, tok->strAt(5)); } else if (Token::Match(tok, "[,(] const std :: %type% < %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(8)); } else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(10)); } else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% , std :: %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(14)); } else if (Token::Match(tok, "[,(] const std :: %type% < %type% , std :: %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(12)); } else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% , %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(12)); } else if (Token::Match(tok, "[,(] const std :: %type% < %type% , %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(10)); } else if (Token::Match(tok, "[,(] const %type% %var% [,)]")) { // Check if type is a struct or class. if (symbolDatabase->isClassOrStruct(tok->strAt(2))) { passedByValueError(tok, tok->strAt(3)); } } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check that all struct members are used //--------------------------------------------------------------------------- void CheckOther::checkStructMemberUsage() { if (!_settings->isEnabled("style")) return; std::string structname; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->fileIndex() != 0) continue; if (Token::Match(tok, "struct|union %type% {")) { structname.clear(); if (Token::simpleMatch(tok->previous(), "extern")) continue; if ((!tok->previous() || Token::simpleMatch(tok->previous(), ";")) && Token::Match(tok->tokAt(2)->link(), ("} ; " + tok->strAt(1) + " %var% ;").c_str())) continue; structname = tok->strAt(1); // Bail out if struct/union contain any functions for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") { structname.clear(); break; } if (tok2->str() == "}") break; } // bail out if struct is inherited if (!structname.empty() && Token::findmatch(tok, (",|private|protected|public " + structname).c_str())) structname.clear(); // Bail out if some data is casted to struct.. const std::string s("( struct| " + tok->next()->str() + " * ) & %var% ["); if (Token::findmatch(tok, s.c_str())) structname.clear(); // Try to prevent false positives when struct members are not used directly. if (Token::findmatch(tok, (structname + " *").c_str())) structname.clear(); else if (Token::findmatch(tok, (structname + " %type% *").c_str())) structname = ""; } if (tok->str() == "}") structname.clear(); if (!structname.empty() && Token::Match(tok, "[{;]")) { // Declaring struct variable.. std::string varname; // declaring a POD variable? if (!tok->next()->isStandardType()) continue; if (Token::Match(tok->next(), "%type% %var% [;[]")) varname = tok->strAt(2); else if (Token::Match(tok->next(), "%type% %type% %var% [;[]")) varname = tok->strAt(3); else if (Token::Match(tok->next(), "%type% * %var% [;[]")) varname = tok->strAt(3); else if (Token::Match(tok->next(), "%type% %type% * %var% [;[]")) varname = tok->strAt(4); else continue; // Check if the struct variable is used anywhere in the file const std::string usagePattern(". " + varname); bool used = false; for (const Token *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, usagePattern.c_str())) { used = true; break; } } if (!used) { unusedStructMemberError(tok->next(), structname, varname); } } } } //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- void CheckOther::checkCharVariable() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Declaring the variable.. if (Token::Match(tok, "[{};(,] const| char *| %var% [;=,)]") || Token::Match(tok, "[{};(,] const| char %var% [")) { // goto 'char' token tok = tok->next(); if (tok->str() == "const") tok = tok->next(); // Check for unsigned char if (tok->isUnsigned()) continue; // Set tok to point to the variable name tok = tok->next(); const bool isPointer(tok->str() == "*" || tok->strAt(1) == "["); if (tok->str() == "*") tok = tok->next(); // Check usage of char variable.. int indentlevel = 0; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { --indentlevel; if (indentlevel <= 0) break; } if (!isPointer) { std::string temp = "%var% [ " + tok->str() + " ]"; if ((tok2->str() != ".") && Token::Match(tok2->next(), temp.c_str())) { charArrayIndexError(tok2->next()); break; } } if (Token::Match(tok2, "[;{}] %var% = %any% [&|] %any% ;")) { // is the char variable used in the calculation? if (tok2->tokAt(3)->varId() != tok->varId() && tok2->tokAt(5)->varId() != tok->varId()) continue; // it's ok with a bitwise and where the other operand is 0xff or less.. if (tok2->strAt(4) == "&") { if (tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3))) continue; if (tok2->tokAt(5)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(5))) continue; } // is the result stored in a short|int|long? if (!Token::findmatch(_tokenizer->tokens(), "short|int|long %varid%", tok2->next()->varId())) continue; // This is an error.. charBitOpError(tok2); break; } if (isPointer && Token::Match(tok2, "[;{}] %var% = %any% [&|] ( * %varid% ) ;", tok->varId())) { // it's ok with a bitwise and where the other operand is 0xff or less.. if (tok2->strAt(4) == "&" && tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3))) continue; // is the result stored in a short|int|long? if (!Token::findmatch(_tokenizer->tokens(), "short|int|long %varid%", tok2->next()->varId())) continue; // This is an error.. charBitOpError(tok2); break; } } } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Incomplete statement.. //--------------------------------------------------------------------------- void CheckOther::checkIncompleteStatement() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "(") { tok = tok->link(); if (Token::simpleMatch(tok, ") {") && Token::simpleMatch(tok->next()->link(), "} ;")) tok = tok->next()->link(); } else if (Token::simpleMatch(tok, "= {")) tok = tok->next()->link(); else if (tok->str() == "{" && Token::Match(tok->tokAt(-2), "%type% %var%")) tok = tok->link(); else if (Token::Match(tok, "[;{}] %str%") || Token::Match(tok, "[;{}] %num%")) { // bailout if there is a "? :" in this statement bool bailout = false; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "?") bailout = true; else if (tok2->str() == ";") break; } if (bailout) continue; constStatementError(tok->next(), tok->next()->isNumber() ? "numeric" : "string"); } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // str plus char //--------------------------------------------------------------------------- void CheckOther::strPlusChar() { // Don't use this check for Java and C# programs.. if (_tokenizer->isJavaOrCSharp()) { return; } bool charVars[10000] = {0}; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Declaring char variable.. if (Token::Match(tok, "char|int|short %var% [;=]")) { unsigned int varid = tok->next()->varId(); if (varid > 0 && varid < 10000) charVars[varid] = true; } // else if (Token::Match(tok, "[=(] %str% + %any%")) { // char constant.. const std::string s = tok->strAt(3); if (s[0] == '\'') strPlusChar(tok->next()); // char variable.. unsigned int varid = tok->tokAt(3)->varId(); if (varid > 0 && varid < 10000 && charVars[varid]) strPlusChar(tok->next()); } } } void CheckOther::checkZeroDivision() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "/ %num%") && MathLib::isInt(tok->next()->str()) && MathLib::toLongNumber(tok->next()->str()) == 0L) { zerodivError(tok); } else if (Token::Match(tok, "div|ldiv|lldiv|imaxdiv ( %num% , %num% )") && MathLib::isInt(tok->tokAt(4)->str()) && MathLib::toLongNumber(tok->tokAt(4)->str()) == 0L) { zerodivError(tok); } } } void CheckOther::checkMathFunctions() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // case log(-2) if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isInt(tok->tokAt(2)->str()) && MathLib::toLongNumber(tok->tokAt(2)->str()) <= 0) { mathfunctionCallError(tok); } // case log(-2.0) else if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isFloat(tok->tokAt(2)->str()) && MathLib::toDoubleNumber(tok->tokAt(2)->str()) <= 0.) { mathfunctionCallError(tok); } // case log(0.0) else if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && !MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isFloat(tok->tokAt(2)->str()) && MathLib::toDoubleNumber(tok->tokAt(2)->str()) <= 0.) { mathfunctionCallError(tok); } // case log(0) else if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && !MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isInt(tok->tokAt(2)->str()) && MathLib::toLongNumber(tok->tokAt(2)->str()) <= 0) { mathfunctionCallError(tok); } // acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond else if (tok->varId() == 0 && Token::Match(tok, "acos|asin ( %num% )") && std::fabs(MathLib::toDoubleNumber(tok->tokAt(2)->str())) > 1.0) { mathfunctionCallError(tok); } // sqrt( x ): if x is negative the result is undefined else if (tok->varId() == 0 && Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )") && MathLib::isNegative(tok->tokAt(2)->str())) { mathfunctionCallError(tok); } // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined else if (tok->varId() == 0 && Token::Match(tok, "atan2 ( %num% , %num% )") && MathLib::isNullValue(tok->tokAt(2)->str()) && MathLib::isNullValue(tok->tokAt(4)->str())) { mathfunctionCallError(tok, 2); } // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). else if (tok->varId() == 0 && Token::Match(tok, "fmod ( %num% , %num% )") && MathLib::isNullValue(tok->tokAt(4)->str())) { mathfunctionCallError(tok, 2); } // pow ( x , y) If x is zero, and y is negative --> division by zero else if (tok->varId() == 0 && Token::Match(tok, "pow ( %num% , %num% )") && MathLib::isNullValue(tok->tokAt(2)->str()) && MathLib::isNegative(tok->tokAt(4)->str())) { mathfunctionCallError(tok, 2); } } } /** Is there a function with given name? */ static bool isFunction(const std::string &name, const Token *startToken) { const std::string pattern1(name + " ("); for (const Token *tok = startToken; tok; tok = tok->next()) { // skip executable scopes etc if (tok->str() == "(") { tok = tok->link(); if (Token::simpleMatch(tok, ") {")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, ") const {")) tok = tok->tokAt(2)->link(); } // function declaration/implementation found if ((tok->str() == "*" || (tok->isName() && tok->str().find(":") ==std::string::npos)) && Token::simpleMatch(tok->next(), pattern1.c_str())) return true; } return false; } void CheckOther::checkMisusedScopedObject() { // Skip this check for .c files { const std::string fname = _tokenizer->getFiles()->at(0); size_t position = fname.rfind("."); if (position != std::string::npos) { const std::string ext = fname.substr(position); if (ext == ".c" || ext == ".C") return; } } const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; unsigned int depth = 0; for (const Token *tok = scope->classStart; tok; tok = tok->next()) { if (tok->str() == "{") { ++depth; } else if (tok->str() == "}") { --depth; if (depth == 0) break; } if (Token::Match(tok, "[;{}] %var% (") && Token::simpleMatch(tok->tokAt(2)->link(), ") ;") && symbolDatabase->isClassOrStruct(tok->next()->str()) && !isFunction(tok->next()->str(), _tokenizer->tokens())) { tok = tok->next(); misusedScopeObjectError(tok, tok->str()); tok = tok->next(); } } } } void CheckOther::checkIncorrectStringCompare() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, ". substr ( %any% , %num% ) ==|!= %str%")) { size_t clen = MathLib::toLongNumber(tok->tokAt(5)->str()); size_t slen = Token::getStrLength(tok->tokAt(8)); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", tok->tokAt(8)->str(), tok->tokAt(5)->str()); } } if (Token::Match(tok, "%str% ==|!= %var% . substr ( %any% , %num% )")) { size_t clen = MathLib::toLongNumber(tok->tokAt(8)->str()); size_t slen = Token::getStrLength(tok); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", tok->str(), tok->tokAt(8)->str()); } } } } //----------------------------------------------------------------------------- // check for duplicate expressions in if statements // if (a) { } else if (a) { } //----------------------------------------------------------------------------- static const std::string stringifyTokens(const Token *start, const Token *end) { const Token *tok = start; std::string stringified; if (tok->isUnsigned()) stringified.append("unsigned "); else if (tok->isSigned()) stringified.append("signed "); if (tok->isLong()) stringified.append("long "); stringified.append(tok->str()); while (tok && tok->next() && tok != end) { if (tok->isUnsigned()) stringified.append("unsigned "); else if (tok->isSigned()) stringified.append("signed "); if (tok->isLong()) stringified.append("long "); tok = tok->next(); stringified.append(" "); stringified.append(tok->str()); } return stringified; } static bool expressionHasSideEffects(const Token *first, const Token *last) { for (const Token *tok = first; tok != last->next(); tok = tok->next()) { // check for assignment if (tok->isAssignmentOp()) return true; // check for inc/dec else if (Token::Match(tok, "++|--")) return true; // check for function call else if (Token::Match(tok, "%var% (") && !(Token::Match(tok, "c_str|string") || tok->isStandardType())) return true; } return false; } void CheckOther::checkDuplicateIf() { if (!_settings->isEnabled("style")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // check all the code in the function for if (...) and else if (...) statements for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::simpleMatch(tok, "if (") && tok->strAt(-1) != "else" && Token::simpleMatch(tok->next()->link(), ") {")) { std::map expressionMap; // get the expression from the token stream std::string expression = stringifyTokens(tok->tokAt(2), tok->next()->link()->previous()); // save the expression and its location expressionMap.insert(std::make_pair(expression, tok)); // find the next else if (...) statement const Token *tok1 = tok->next()->link()->next()->link(); // check all the else if (...) statements while (Token::simpleMatch(tok1, "} else if (") && Token::simpleMatch(tok1->tokAt(3)->link(), ") {")) { // get the expression from the token stream expression = stringifyTokens(tok1->tokAt(4), tok1->tokAt(3)->link()->previous()); // try to look up the expression to check for duplicates std::map::iterator it = expressionMap.find(expression); // found a duplicate if (it != expressionMap.end()) { // check for expressions that have side effects and ignore them if (!expressionHasSideEffects(tok1->tokAt(4), tok1->tokAt(3)->link()->previous())) duplicateIfError(it->second, tok1->next()); } // not a duplicate expression so save it and its location else expressionMap.insert(std::make_pair(expression, tok1->next())); // find the next else if (...) statement tok1 = tok1->tokAt(3)->link()->next()->link(); } tok = tok->next()->link()->next(); } } } } void CheckOther::duplicateIfError(const Token *tok1, const Token *tok2) { std::list toks; toks.push_back(tok2); toks.push_back(tok1); reportError(toks, Severity::style, "duplicateIf", "Found duplicate if expressions.\n" "Finding the same expression more than once is suspicious and might indicate " "a cut and paste or logic error. Please examine this code carefully to determine " "if it is correct."); } //----------------------------------------------------------------------------- // check for duplicate code in if and else branches // if (a) { b = true; } else { b = true; } //----------------------------------------------------------------------------- void CheckOther::checkDuplicateBranch() { if (!_settings->isEnabled("style")) return; if (!_settings->inconclusive) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // check all the code in the function for if (..) else for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::simpleMatch(tok, "if (") && tok->strAt(-1) != "else" && Token::simpleMatch(tok->next()->link(), ") {") && Token::simpleMatch(tok->next()->link()->next()->link(), "} else {")) { // save if branch code std::string branch1 = stringifyTokens(tok->next()->link()->tokAt(2), tok->next()->link()->next()->link()->previous()); // find else branch const Token *tok1 = tok->next()->link()->next()->link(); // save else branch code std::string branch2 = stringifyTokens(tok1->tokAt(3), tok1->tokAt(2)->link()->previous()); // check for duplicates if (branch1 == branch2) duplicateBranchError(tok, tok1->tokAt(2)); tok = tok->next()->link()->next(); } } } } void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2) { std::list toks; toks.push_back(tok2); toks.push_back(tok1); reportError(toks, Severity::style, "duplicateBranch", "Found duplicate branches for if and else.\n" "Finding the same code for an if branch and an else branch is suspicious and " "might indicate a cut and paste or logic error. Please examine this code " "carefully to determine if it is correct."); } //--------------------------------------------------------------------------- // check for the same expression on both sides of an operator // (x == x), (x && x), (x || x) // (x.y == x.y), (x.y && x.y), (x.y || x.y) //--------------------------------------------------------------------------- void CheckOther::checkDuplicateExpression() { if (!_settings->isEnabled("style")) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::Match(tok, "(|&&|%oror% %var% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %var% )|&&|%oror%") && tok->strAt(1) == tok->strAt(3)) { // float == float and float != float are valid NaN checks if (Token::Match(tok->tokAt(2), "==|!=") && tok->next()->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); if (var && var->typeStartToken() == var->typeEndToken()) { if (Token::Match(var->typeStartToken(), "float|double")) continue; } } duplicateExpressionError(tok->next(), tok->tokAt(3), tok->strAt(2)); } else if (Token::Match(tok, "(|&&|%oror% %var% . %var% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %var% . %var% )|&&|%oror%") && tok->strAt(1) == tok->strAt(5) && tok->strAt(3) == tok->strAt(7)) { duplicateExpressionError(tok->next(), tok->tokAt(6), tok->strAt(4)); } } } } void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op) { std::list toks; toks.push_back(tok2); toks.push_back(tok1); reportError(toks, Severity::style, "duplicateExpression", "Same expression on both sides of \'" + op + "\'.\n" "Finding the same expression on both sides of an operator is suspicious and might " "indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct."); } //--------------------------------------------------------------------------- // Check for string comparison involving two static strings. // if(strcmp("00FF00","00FF00")==0) // <- statement is always true //--------------------------------------------------------------------------- void CheckOther::checkAlwaysTrueOrFalseStringCompare() { if (!_settings->isEnabled("style")) return; const char pattern1[] = "strcmp|stricmp|strcmpi|strcasecmp|wcscmp ( %str% , %str% )"; const char pattern2[] = "QString :: compare ( %str% , %str% )"; const Token *tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, pattern1)) != NULL) { alwaysTrueFalseStringCompare(tok, tok->strAt(2), tok->strAt(4)); tok = tok->tokAt(5); } tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, pattern2)) != NULL) { alwaysTrueFalseStringCompare(tok, tok->strAt(4), tok->strAt(6)); tok = tok->tokAt(7); } } void CheckOther::alwaysTrueFalseStringCompare(const Token *tok, const std::string& str1, const std::string& str2) { const size_t stringLen = 10; const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + ".."); if (str1 == str2) { reportError(tok, Severity::warning, "staticStringCompare", "Comparison of always identical static strings.\n" "The compared strings, '" + string1 + "' and '" + string2 + "', are always identical. " "If the purpose is to compare these two strings, the comparison is unnecessary. " "If the strings are supposed to be different, then there is a bug somewhere."); } else { reportError(tok, Severity::performance, "staticStringCompare", "Unnecessary comparison of static strings.\n" "The compared strings, '" + string1 + "' and '" + string2 + "', are static and always different. " "If the purpose is to compare these two strings, the comparison is unnecessary."); } } //----------------------------------------------------------------------------- void CheckOther::cstyleCastError(const Token *tok) { reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting"); } void CheckOther::dangerousUsageStrtolError(const Token *tok) { reportError(tok, Severity::error, "dangerousUsageStrtol", "Invalid radix in call to strtol or strtoul. Must be 0 or 2-36"); } void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "sprintfOverlappingData", "Undefined behavior: variable is used as parameter and destination in s[n]printf().\n" "The variable '" + varname + "' is used both as a parameter and as a destination in " "s[n]printf(). The origin and destination buffers overlap. Quote from glibc (C-library) " "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): " "'If copying takes place between objects that overlap as a result of a call " "to sprintf() or snprintf(), the results are undefined.'"); } void CheckOther::udivError(const Token *tok) { reportError(tok, Severity::error, "udivError", "Unsigned division. The result will be wrong."); } void CheckOther::unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname) { reportError(tok, Severity::style, "unusedStructMember", "struct or union member '" + structname + "::" + varname + "' is never used"); } void CheckOther::passedByValueError(const Token *tok, const std::string &parname) { reportError(tok, Severity::performance, "passedByValue", "Function parameter '" + parname + "' should be passed by reference.\n" "Parameter '" + parname + "' is passed as a value. It could be passed " "as a (const) reference which is usually faster and recommended in C++."); } void CheckOther::constStatementError(const Token *tok, const std::string &type) { reportError(tok, Severity::warning, "constStatement", "Redundant code: Found a statement that begins with " + type + " constant"); } void CheckOther::charArrayIndexError(const Token *tok) { reportError(tok, Severity::warning, "charArrayIndex", "Using char type as array index\n" "Using signed char type as array index. If the value " "can be greater than 127 there will be a buffer overflow " "(because of sign extension)."); } void CheckOther::charBitOpError(const Token *tok) { reportError(tok, Severity::warning, "charBitOp", "When using char variables in bit operations, sign extension can generate unexpected results.\n" "When using char variables in bit operations, sign extension can generate unexpected results. For example:\n" " char c = 0x80;\n" " int i = 0 | c;\n" " if (i & 0x8000)\n" " printf(\"not expected\");\n" "The 'not expected' will be printed on the screen."); } void CheckOther::variableScopeError(const Token *tok, const std::string &varname) { reportError(tok, Severity::information, "variableScope", "The scope of the variable '" + varname + "' can be reduced\n" "The scope of the variable '" + varname + "' can be reduced. Warning: It can be unsafe " "to fix this message. Be careful. Especially when there are inner loops. Here is an " "example where cppcheck will write that the scope for 'i' can be reduced:\n" "void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {\n" " // it's safe to move 'int i = 0' here\n" " for (int n = 0; n < 10; ++n) {\n" " // it is possible but not safe to move 'int i = 0' here\n" " do_something(&i);\n" " }\n" " }\n" "}\n" "When you see this message it is always safe to reduce the variable scope 1 level."); } void CheckOther::conditionAlwaysTrueFalse(const Token *tok, const std::string &truefalse) { reportError(tok, Severity::style, "conditionAlwaysTrueFalse", "Condition is always " + truefalse); } void CheckOther::strPlusChar(const Token *tok) { reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic"); } void CheckOther::zerodivError(const Token *tok) { reportError(tok, Severity::error, "zerodiv", "Division by zero"); } void CheckOther::mathfunctionCallError(const Token *tok, const unsigned int numParam) { if (tok) { if (numParam == 1) reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->tokAt(2)->str() + " to " + tok->str() + "() leads to undefined result"); else if (numParam == 2) reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->tokAt(2)->str() + " and " + tok->tokAt(4)->str() + " to " + tok->str() + "() leads to undefined result"); } else reportError(tok, Severity::error, "wrongmathcall", "Passing value " " to " "() leads to undefined result"); } void CheckOther::fflushOnInputStreamError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "fflushOnInputStream", "fflush() called on input stream \"" + varname + "\" may result in undefined behaviour"); } void CheckOther::sizeofsizeof() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof (| sizeof")) { sizeofsizeofError(tok); tok = tok->next(); } } } void CheckOther::sizeofsizeofError(const Token *tok) { reportError(tok, Severity::warning, "sizeofsizeof", "Calling sizeof for 'sizeof'.\n" "Calling sizeof for 'sizeof looks like a suspicious code and " "most likely there should be just one 'sizeof'. The current " "code is equivalent to 'sizeof(size_t)'"); } void CheckOther::sizeofCalculation() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { unsigned int parlevel = 0; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { if (parlevel <= 1) break; --parlevel; } else if (Token::Match(tok2, "+|/")) { sizeofCalculationError(tok2); break; } } } } } void CheckOther::sizeofCalculationError(const Token *tok) { reportError(tok, Severity::warning, "sizeofCalculation", "Found calculation inside sizeof()"); } void CheckOther::redundantAssignmentInSwitchError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "redundantAssignInSwitch", "Redundant assignment of \"" + varname + "\" in switch"); } void CheckOther::switchCaseFallThrough(const Token *tok) { reportError(tok, Severity::style, "switchCaseFallThrough", "Switch falls through case without comment"); } void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", "Redundant assignment of \"" + varname + "\" to itself"); } void CheckOther::assignmentInAssertError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "assignmentInAssert", "Assert statement modifies '" + varname + "'.\n" "Variable '" + varname + "' is modified inside assert statement. " "Assert statements are removed from release builds so the code inside " "assert statement is not run. If the code is needed also in release " "builds this is a bug."); } void CheckOther::incorrectLogicOperatorError(const Token *tok, bool always) { if (always) reportError(tok, Severity::warning, "incorrectLogicOperator", "Mutual exclusion over || always evaluates to true. Did you intend to use && instead?"); else reportError(tok, Severity::warning, "incorrectLogicOperator", "Expression always evaluates to false. Did you intend to use || instead?"); } void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "unusedScopedObject", "instance of \"" + varname + "\" object destroyed immediately"); } void CheckOther::catchExceptionByValueError(const Token *tok) { reportError(tok, Severity::style, "catchExceptionByValue", "Exception should be caught by reference.\n" "The exception is caught as a value. It could be caught " "as a (const) reference which is usually recommended in C++."); } void CheckOther::memsetZeroBytesError(const Token *tok, const std::string &varname) { const std::string summary("memset() called to fill 0 bytes of \'" + varname + "\'"); const std::string verbose(summary + ". Second and third arguments might be inverted."); reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose); } void CheckOther::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string, const std::string &len) { reportError(tok, Severity::warning, "incorrectStringCompare", "String literal " + string + " doesn't match length argument for " + func + "(" + len + ")."); } void CheckOther::comparisonOfBoolWithIntError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "comparisonOfBoolWithInt", "Comparison of a boolean with a non-zero integer\n" "The expression \"!" + varname + "\" is of type 'bool' but is compared against a non-zero 'int'."); } void CheckOther::duplicateBreakError(const Token *tok) { reportError(tok, Severity::style, "duplicateBreak", "Consecutive break or continue statements are unnecessary\n" "The second of the two statements can never be executed, and so should be removed\n"); } void CheckOther::checkAssignBoolToPointer() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %var% = %bool% ;")) { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const Variable *var1(symbolDatabase->getVariableFromVarId(tok->next()->varId())); // Is variable a pointer? if (var1 && var1->nameToken()->strAt(-1) == "*") assignBoolToPointerError(tok->next()); } } } void CheckOther::assignBoolToPointerError(const Token *tok) { reportError(tok, Severity::error, "assignBoolToPointer", "Assigning bool value to pointer (converting bool value to address)"); } //--------------------------------------------------------------------------- // Check testing sign of unsigned variables. //--------------------------------------------------------------------------- void CheckOther::checkSignOfUnsignedVariable() { if (!_settings->isEnabled("style")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // check all the code in the function for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::Match(tok, "( %var% <|<= 0 )") && tok->next()->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZero(tok->next(), tok->next()->str()); } else if (Token::Match(tok, "( 0 > %var% )") && tok->tokAt(3)->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId()); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZero(tok->tokAt(3), tok->strAt(3)); } else if (Token::Match(tok, "( 0 <= %var% )") && tok->tokAt(3)->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId()); if (var && var->typeEndToken()->isUnsigned()) unsignedPositive(tok->tokAt(3), tok->strAt(3)); } } } } void CheckOther::unsignedLessThanZero(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unsignedLessThanZero", "Checking if unsigned variable '" + varname + "' is less than zero.\n" "An unsigned variable will never be negative so it is either pointless or " "an error to check if it is."); } void CheckOther::unsignedPositive(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unsignedPositive", "Checking if unsigned variable '" + varname + "' is positive is always true.\n" "An unsigned variable can't be negative so it is either pointless or " "an error to check if it is."); } cppcheck-2.7/gui/test/data/files/000077500000000000000000000000001417746362400167365ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/files/bar1000066400000000000000000000000211417746362400174770ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/bar1.foo000066400000000000000000000000211417746362400202610ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/dir1/000077500000000000000000000000001417746362400175755ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/files/dir1/dir11/000077500000000000000000000000001417746362400205155ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/files/dir1/dir11/foo11.cpp000066400000000000000000000000221417746362400221400ustar00rootroot00000000000000Dummy test file . cppcheck-2.7/gui/test/data/files/dir1/foo1.cpp000066400000000000000000000000221417746362400211370ustar00rootroot00000000000000Dummy test file . cppcheck-2.7/gui/test/data/files/dir2/000077500000000000000000000000001417746362400175765ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/files/dir2/foo1.cpp000066400000000000000000000000221417746362400211400ustar00rootroot00000000000000Dummy test file . cppcheck-2.7/gui/test/data/files/foo1.cpp000066400000000000000000000000221417746362400203000ustar00rootroot00000000000000Dummy test file . cppcheck-2.7/gui/test/data/files/foo2.cxx000066400000000000000000000000211417746362400203200ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/foo3.cc000066400000000000000000000000211417746362400201040ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/foo4.c000066400000000000000000000000221417746362400177430ustar00rootroot00000000000000Dummy test file . cppcheck-2.7/gui/test/data/files/foo5.c++000066400000000000000000000000211417746362400200710ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/foo6.txx000066400000000000000000000000211417746362400203450ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/foo7.tpp000066400000000000000000000000211417746362400203260ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/foo8.ipp000066400000000000000000000000211417746362400203140ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/files/foo9.ixx000066400000000000000000000000211417746362400203350ustar00rootroot00000000000000Dummy test file. cppcheck-2.7/gui/test/data/projectfiles/000077500000000000000000000000001417746362400203255ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/projectfiles/simple.cppcheck000066400000000000000000000006031417746362400233170ustar00rootroot00000000000000 cppcheck-2.7/gui/test/data/projectfiles/simple_ignore.cppcheck000066400000000000000000000006011417746362400246600ustar00rootroot00000000000000 cppcheck-2.7/gui/test/data/projectfiles/simple_noroot.cppcheck000066400000000000000000000005501417746362400247200ustar00rootroot00000000000000 cppcheck-2.7/gui/test/data/xmlfiles/000077500000000000000000000000001417746362400174575ustar00rootroot00000000000000cppcheck-2.7/gui/test/data/xmlfiles/xmlreport_v2.xml000066400000000000000000000040141417746362400226430ustar00rootroot00000000000000 cppcheck-2.7/gui/test/filelist/000077500000000000000000000000001417746362400165365ustar00rootroot00000000000000cppcheck-2.7/gui/test/filelist/CMakeLists.txt000066400000000000000000000013751417746362400213040ustar00rootroot00000000000000qt5_wrap_cpp(test-filelist_SRC testfilelist.h) add_custom_target(build-testfilelist-deps SOURCES ${test-filelist_SRC}) add_dependencies(gui-build-deps build-testfilelist-deps) add_executable(test-filelist ${test-filelist_SRC} testfilelist.cpp ${CMAKE_SOURCE_DIR}/gui/filelist.cpp ${CMAKE_SOURCE_DIR}/lib/pathmatch.cpp ${CMAKE_SOURCE_DIR}/lib/path.cpp ${CMAKE_SOURCE_DIR}/lib/utils.cpp $ ) target_include_directories(test-filelist PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib ${CMAKE_SOURCE_DIR}/externals/simplecpp) target_compile_definitions(test-filelist PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") target_link_libraries(test-filelist Qt5::Core Qt5::Test)cppcheck-2.7/gui/test/filelist/filelist.pro000066400000000000000000000011231417746362400210700ustar00rootroot00000000000000TEMPLATE = app TARGET = test-filelist DEPENDPATH += . INCLUDEPATH += . ../../../externals/simplecpp OBJECTS_DIR = ../build MOC_DIR = ../build QT += testlib include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testfilelist.cpp \ ../../filelist.cpp \ ../../../lib/pathmatch.cpp \ ../../../lib/path.cpp \ ../../../lib/utils.cpp \ ../../../externals/simplecpp/simplecpp.cpp HEADERS += testfilelist.h \ ../../filelist.h \ ../../../lib/pathmatch.h \ ../../../lib/path.h \ ../../../lib/utils.h \ ../../../externals/simplecpp/simplecpp.h cppcheck-2.7/gui/test/filelist/testfilelist.cpp000066400000000000000000000150331417746362400217570ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testfilelist.h" #include "filelist.h" #include #include #include void TestFileList::addFile() { // Accepted extensions: *.cpp, *.cxx, *.cc, *.c, *.c++, *.txx, *.tpp, *.ipp, *.ixx" FileList list; list.addFile(QString(SRCDIR) + "/../data/files/foo1.cpp"); list.addFile(QString(SRCDIR) + "/../data/files/foo2.cxx"); list.addFile(QString(SRCDIR) + "/../data/files/foo3.cc"); list.addFile(QString(SRCDIR) + "/../data/files/foo4.c"); list.addFile(QString(SRCDIR) + "/../data/files/foo5.c++"); list.addFile(QString(SRCDIR) + "/../data/files/foo6.txx"); list.addFile(QString(SRCDIR) + "/../data/files/foo7.tpp"); list.addFile(QString(SRCDIR) + "/../data/files/foo8.ipp"); list.addFile(QString(SRCDIR) + "/../data/files/foo9.ixx"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 9); } void TestFileList::addPathList() { // Accepted extensions: *.cpp, *.cxx, *.cc, *.c, *.c++, *.txx, *.tpp, *.ipp, *.ixx" QStringList paths; paths << QString(SRCDIR) + "/../data/files/foo1.cpp"; paths << QString(SRCDIR) + "/../data/files/foo2.cxx"; paths << QString(SRCDIR) + "/../data/files/foo3.cc"; paths << QString(SRCDIR) + "/../data/files/foo4.c"; paths << QString(SRCDIR) + "/../data/files/foo5.c++"; paths << QString(SRCDIR) + "/../data/files/foo6.txx"; paths << QString(SRCDIR) + "/../data/files/foo7.tpp"; paths << QString(SRCDIR) + "/../data/files/foo8.ipp"; paths << QString(SRCDIR) + "/../data/files/foo9.ixx"; FileList list; list.addPathList(paths); QStringList files = list.getFileList(); QCOMPARE(files.size(), 9); } void TestFileList::addFile_notexist() { FileList list; list.addFile(QString(SRCDIR) + "/../data/files/bar1.cpp"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 0); } void TestFileList::addFile_unknown() { FileList list; list.addFile(QString(SRCDIR) + "/../data/files/bar1"); list.addFile(QString(SRCDIR) + "/../data/files/bar1.foo"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 0); } void TestFileList::addDirectory() { FileList list; list.addDirectory(QString(SRCDIR) + "/../data/files"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 7); } void TestFileList::addDirectory_recursive() { FileList list; list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 10); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(files.contains(base + "/dir1/foo1.cpp")); QVERIFY(files.contains(base + "/dir1/dir11/foo11.cpp")); QVERIFY(files.contains(base + "/dir2/foo1.cpp")); } void TestFileList::filterFiles() { FileList list; QStringList filters; filters << "foo1.cpp" << "foo3.cc"; list.addExcludeList(filters); list.addFile(QString(SRCDIR) + "/../data/files/foo1.cpp"); list.addFile(QString(SRCDIR) + "/../data/files/foo2.cxx"); list.addFile(QString(SRCDIR) + "/../data/files/foo3.cc"); list.addFile(QString(SRCDIR) + "/../data/files/foo4.c"); list.addFile(QString(SRCDIR) + "/../data/files/foo5.c++"); list.addFile(QString(SRCDIR) + "/../data/files/foo6.txx"); list.addFile(QString(SRCDIR) + "/../data/files/foo7.tpp"); list.addFile(QString(SRCDIR) + "/../data/files/foo8.ipp"); list.addFile(QString(SRCDIR) + "/../data/files/foo9.ixx"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 7); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(!files.contains(base + "/foo1.cpp")); QVERIFY(!files.contains(base + "/foo3.cpp")); } void TestFileList::filterFiles2() { FileList list; QStringList filters; filters << "foo1.cpp" << "foo3.cc"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 5); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(!files.contains(base + "/foo1.cpp")); QVERIFY(!files.contains(base + "/foo3.cpp")); } void TestFileList::filterFiles3() { FileList list; QStringList filters; filters << "foo1.cpp" << "foo3.cc"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 6); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(!files.contains(base + "/foo1.cpp")); QVERIFY(!files.contains(base + "/foo3.cpp")); QVERIFY(!files.contains(base + "/dir1/foo1.cpp")); QVERIFY(!files.contains(base + "/dir2/foo1.cpp")); } void TestFileList::filterFiles4() { FileList list; QStringList filters; filters << "dir1/"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 8); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(!files.contains(base + "/dir1/foo1.cpp")); QVERIFY(!files.contains(base + "/dir1/dir11/foo11.cpp")); } /* void TestFileList::filterFiles5() { FileList list; QStringList filters; filters << QDir(QString(SRCDIR) + "/../data/files/dir1/").absolutePath() + "/"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 8); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(! files.contains(base + "/dir1/foo1.cpp")); QVERIFY(! files.contains(base + "/dir1/dir11/foo11.cpp")); } */ QTEST_MAIN(TestFileList) cppcheck-2.7/gui/test/filelist/testfilelist.h000066400000000000000000000021151417746362400214210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include class TestFileList : public QObject { Q_OBJECT private slots: void addFile(); void addPathList(); void addFile_notexist(); void addFile_unknown(); void addDirectory(); void addDirectory_recursive(); void filterFiles(); void filterFiles2(); void filterFiles3(); void filterFiles4(); }; cppcheck-2.7/gui/test/projectfile/000077500000000000000000000000001417746362400172315ustar00rootroot00000000000000cppcheck-2.7/gui/test/projectfile/CMakeLists.txt000066400000000000000000000011551417746362400217730ustar00rootroot00000000000000qt5_wrap_cpp(test-projectfile_SRC testprojectfile.h ${CMAKE_SOURCE_DIR}/gui/projectfile.h) add_custom_target(build-projectfile-deps SOURCES ${test-projectfile_SRC}) add_dependencies(gui-build-deps build-projectfile-deps) add_executable(test-projectfile ${test-projectfile_SRC} testprojectfile.cpp ${CMAKE_SOURCE_DIR}/gui/projectfile.cpp ) target_include_directories(test-projectfile PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib) target_compile_definitions(test-projectfile PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") target_link_libraries(test-projectfile Qt5::Core Qt5::Test)cppcheck-2.7/gui/test/projectfile/projectfile.pro000066400000000000000000000007221417746362400222620ustar00rootroot00000000000000TEMPLATE = app TARGET = test-projectfile DEPENDPATH += . INCLUDEPATH += . ../../../externals/simplecpp ../../../externals/tinyxml2 ../../../externals/picojson OBJECTS_DIR = ../build MOC_DIR = ../build QT -= gui QT += core CONFIG += console include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testprojectfile.cpp \ ../../projectfile.cpp HEADERS += testprojectfile.h \ ../../projectfile.h \ ../../../externals/picojson/picojson.h cppcheck-2.7/gui/test/projectfile/testprojectfile.cpp000066400000000000000000000102611417746362400231430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testprojectfile.h" #include "projectfile.h" #include "settings.h" #include // Mock... const char Settings::SafeChecks::XmlRootName[] = "safe-checks"; const char Settings::SafeChecks::XmlClasses[] = "class-public"; const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions"; const char Settings::SafeChecks::XmlInternalFunctions[] = "internal-functions"; const char Settings::SafeChecks::XmlExternalVariables[] = "external-variables"; Settings::Settings() : maxCtuDepth(10), maxTemplateRecursion(100) {} cppcheck::Platform::Platform() {} Library::Library() {} ImportProject::ImportProject() {} bool ImportProject::sourceFileExists(const std::string & /*file*/) { return true; } void TestProjectFile::loadInexisting() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/foo.cppcheck"); ProjectFile pfile(filepath); QCOMPARE(pfile.read(), false); } void TestProjectFile::loadSimple() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/simple.cppcheck"); ProjectFile pfile(filepath); QVERIFY(pfile.read()); QCOMPARE(pfile.getRootPath(), QString("../..")); QStringList includes = pfile.getIncludeDirs(); QCOMPARE(includes.size(), 2); QCOMPARE(includes[0], QString("lib/")); QCOMPARE(includes[1], QString("cli/")); QStringList paths = pfile.getCheckPaths(); QCOMPARE(paths.size(), 2); QCOMPARE(paths[0], QString("gui/")); QCOMPARE(paths[1], QString("test/")); QStringList excludes = pfile.getExcludedPaths(); QCOMPARE(excludes.size(), 1); QCOMPARE(excludes[0], QString("gui/temp/")); QStringList defines = pfile.getDefines(); QCOMPARE(defines.size(), 1); QCOMPARE(defines[0], QString("FOO")); } // Test that project file with old 'ignore' element works void TestProjectFile::loadSimpleWithIgnore() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/simple_ignore.cppcheck"); ProjectFile pfile(filepath); QVERIFY(pfile.read()); QCOMPARE(pfile.getRootPath(), QString("../..")); QStringList includes = pfile.getIncludeDirs(); QCOMPARE(includes.size(), 2); QCOMPARE(includes[0], QString("lib/")); QCOMPARE(includes[1], QString("cli/")); QStringList paths = pfile.getCheckPaths(); QCOMPARE(paths.size(), 2); QCOMPARE(paths[0], QString("gui/")); QCOMPARE(paths[1], QString("test/")); QStringList excludes = pfile.getExcludedPaths(); QCOMPARE(excludes.size(), 1); QCOMPARE(excludes[0], QString("gui/temp/")); QStringList defines = pfile.getDefines(); QCOMPARE(defines.size(), 1); QCOMPARE(defines[0], QString("FOO")); } void TestProjectFile::loadSimpleNoroot() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/simple_noroot.cppcheck"); ProjectFile pfile(filepath); QVERIFY(pfile.read()); QCOMPARE(pfile.getRootPath(), QString()); QStringList includes = pfile.getIncludeDirs(); QCOMPARE(includes.size(), 2); QCOMPARE(includes[0], QString("lib/")); QCOMPARE(includes[1], QString("cli/")); QStringList paths = pfile.getCheckPaths(); QCOMPARE(paths.size(), 2); QCOMPARE(paths[0], QString("gui/")); QCOMPARE(paths[1], QString("test/")); QStringList excludes = pfile.getExcludedPaths(); QCOMPARE(excludes.size(), 1); QCOMPARE(excludes[0], QString("gui/temp/")); QStringList defines = pfile.getDefines(); QCOMPARE(defines.size(), 1); QCOMPARE(defines[0], QString("FOO")); } QTEST_MAIN(TestProjectFile) cppcheck-2.7/gui/test/projectfile/testprojectfile.h000066400000000000000000000016741417746362400226200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include class TestProjectFile : public QObject { Q_OBJECT private slots: void loadInexisting(); void loadSimple(); void loadSimpleWithIgnore(); void loadSimpleNoroot(); }; cppcheck-2.7/gui/test/readme.txt000066400000000000000000000013071417746362400167220ustar00rootroot00000000000000GUI tests + benchmark tests =========================== As the GUI uses Qt framework, the GUI tests also use Qt's Testlib. This is totally different test framework than lib/cli is using. By principle each testcase is compiled as an own runnable binary. Compiling --------- To compile all the tests run in root directory of tests: - qmake ; make You can also (re)compile single test by CD:ing to the directory where test (source) resides and running: - qmake ; make Running ------- As each test is compiled as single executable binary you can run the test just by running the executable. You can get from http://bitbucket.org/kimmov/testrun a script which runs all the tests and collects the results. cppcheck-2.7/gui/test/test.pro000066400000000000000000000002661417746362400164300ustar00rootroot00000000000000#lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) CONFIG += ordered TEMPLATE = subdirs SUBDIRS = \ filelist \ projectfile \ xmlreportv2 cppcheck-2.7/gui/test/translationhandler/000077500000000000000000000000001417746362400206175ustar00rootroot00000000000000cppcheck-2.7/gui/test/translationhandler/CMakeLists.txt000066400000000000000000000012071417746362400233570ustar00rootroot00000000000000qt5_wrap_cpp(test-translationhandler_SRC testtranslationhandler.h ${CMAKE_SOURCE_DIR}/gui/translationhandler.h) add_custom_target(build-translationhandler-deps SOURCES ${test-translationhandler_SRC}) add_dependencies(gui-build-deps build-translationhandler-deps) add_executable(test-translationhandler ${test-translationhandler_SRC} testtranslationhandler.cpp ${CMAKE_SOURCE_DIR}/gui/common.cpp ${CMAKE_SOURCE_DIR}/gui/translationhandler.cpp ) target_include_directories(test-translationhandler PRIVATE ${CMAKE_SOURCE_DIR}/gui) target_link_libraries(test-translationhandler Qt5::Core Qt5::Widgets Qt5::Test)cppcheck-2.7/gui/test/translationhandler/testtranslationhandler.cpp000066400000000000000000000024751417746362400261270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testtranslationhandler.h" #include "translationhandler.h" #include static const QStringList getTranslationNames(const TranslationHandler& handler) { QStringList names; foreach (TranslationInfo translation, handler.getTranslations()) { names.append(translation.mName); } return names; } void TestTranslationHandler::construct() { TranslationHandler handler; QCOMPARE(getTranslationNames(handler).size(), 13); // 12 translations + english QCOMPARE(handler.getCurrentLanguage(), QString("en")); } QTEST_MAIN(TestTranslationHandler) cppcheck-2.7/gui/test/translationhandler/testtranslationhandler.h000066400000000000000000000015511417746362400255660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include class TestTranslationHandler : public QObject { Q_OBJECT private slots: void construct(); }; cppcheck-2.7/gui/test/translationhandler/translationhandler.pro000066400000000000000000000005451417746362400252410ustar00rootroot00000000000000TEMPLATE = app TARGET = test-translationhandler DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build QT += widgets include(../common.pri) # tests SOURCES += testtranslationhandler.cpp \ ../../translationhandler.cpp \ ../../common.cpp HEADERS += testtranslationhandler.h \ ../../translationhandler.h \ ../../common.h cppcheck-2.7/gui/test/xmlreportv2/000077500000000000000000000000001417746362400172275ustar00rootroot00000000000000cppcheck-2.7/gui/test/xmlreportv2/CMakeLists.txt000066400000000000000000000020451417746362400217700ustar00rootroot00000000000000qt5_wrap_cpp(test-xmlreportv2_SRC testxmlreportv2.h) add_custom_target(build-xmlreportv2-deps SOURCES ${test-xmlreportv2_SRC}) add_dependencies(gui-build-deps build-xmlreportv2-deps) if(USE_BUNDLED_TINYXML2) list(APPEND test-xmlreportv2_SRC $) endif() add_executable(test-xmlreportv2 ${test-xmlreportv2_SRC} testxmlreportv2.cpp ${CMAKE_SOURCE_DIR}/gui/erroritem.cpp ${CMAKE_SOURCE_DIR}/gui/report.cpp ${CMAKE_SOURCE_DIR}/gui/xmlreport.cpp ${CMAKE_SOURCE_DIR}/gui/xmlreportv2.cpp $ $ ) target_include_directories(test-xmlreportv2 PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib) target_compile_definitions(test-xmlreportv2 PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") target_link_libraries(test-xmlreportv2 Qt5::Core Qt5::Test) if (HAVE_RULES) target_link_libraries(test-xmlreportv2 ${PCRE_LIBRARY}) endif() if (USE_Z3) target_link_libraries(test-xmlreportv2 ${Z3_LIBRARIES}) endif()cppcheck-2.7/gui/test/xmlreportv2/testxmlreportv2.cpp000066400000000000000000000042501417746362400231400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "testxmlreportv2.h" #include "erroritem.h" #include "xmlreportv2.h" #include void TestXmlReportV2::readXml() { const QString filepath(QString(SRCDIR) + "/../data/xmlfiles/xmlreport_v2.xml"); XmlReportV2 report(filepath); QVERIFY(report.open()); QList errors = report.read(); QCOMPARE(errors.size(), 6); const ErrorItem &item = errors[0]; QCOMPARE(item.errorPath.size(), 1); QCOMPARE(item.errorPath[0].file, QString("test.cxx")); QCOMPARE(item.errorPath[0].line, 11); QCOMPARE(item.errorId, QString("unreadVariable")); QCOMPARE(GuiSeverity::toString(item.severity), QString("style")); QCOMPARE(item.summary, QString("Variable 'a' is assigned a value that is never used")); QCOMPARE(item.message, QString("Variable 'a' is assigned a value that is never used")); const ErrorItem &item2 = errors[3]; QCOMPARE(item2.errorPath.size(), 2); QCOMPARE(item2.errorPath[0].file, QString("test.cxx")); QCOMPARE(item2.errorPath[0].line, 16); QCOMPARE(item2.errorPath[1].file, QString("test.cxx")); QCOMPARE(item2.errorPath[1].line, 32); QCOMPARE(item2.errorId, QString("mismatchAllocDealloc")); QCOMPARE(GuiSeverity::toString(item2.severity), QString("error")); QCOMPARE(item2.summary, QString("Mismatching allocation and deallocation: k")); QCOMPARE(item2.message, QString("Mismatching allocation and deallocation: k")); } QTEST_MAIN(TestXmlReportV2) cppcheck-2.7/gui/test/xmlreportv2/testxmlreportv2.h000066400000000000000000000015401417746362400226040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include class TestXmlReportV2 : public QObject { Q_OBJECT private slots: void readXml(); }; cppcheck-2.7/gui/test/xmlreportv2/xmlreportv2.pro000066400000000000000000000007661417746362400222660ustar00rootroot00000000000000TEMPLATE = app TARGET = test-xmlreportv2 DEPENDPATH += . INCLUDEPATH += . ../../../externals/simplecpp OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) include(../../../lib/lib.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testxmlreportv2.cpp \ ../../erroritem.cpp \ ../../report.cpp \ ../../xmlreport.cpp \ ../../xmlreportv2.cpp HEADERS += testxmlreportv2.h \ ../../erroritem.h \ ../../report.h \ ../../xmlreport.cpp \ ../../xmlreportv2.h cppcheck-2.7/gui/threadhandler.cpp000066400000000000000000000166661417746362400172740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "threadhandler.h" #include "checkthread.h" #include "common.h" #include "resultsview.h" #include "settings.h" #include #include #include ThreadHandler::ThreadHandler(QObject *parent) : QObject(parent), mScanDuration(0), mRunningThreadCount(0), mAnalyseWholeProgram(false) { setThreadCount(1); } ThreadHandler::~ThreadHandler() { removeThreads(); } void ThreadHandler::clearFiles() { mLastFiles.clear(); mResults.clearFiles(); mAnalyseWholeProgram = false; mAddonsAndTools.clear(); mSuppressions.clear(); } void ThreadHandler::setFiles(const QStringList &files) { mResults.setFiles(files); mLastFiles = files; } void ThreadHandler::setProject(const ImportProject &prj) { mResults.setProject(prj); mLastFiles.clear(); } void ThreadHandler::setCheckFiles(bool all) { if (mRunningThreadCount == 0) { mResults.setFiles(getReCheckFiles(all)); } } void ThreadHandler::setCheckFiles(const QStringList& files) { if (mRunningThreadCount == 0) { mResults.setFiles(files); } } void ThreadHandler::check(const Settings &settings) { if (mResults.getFileCount() == 0 || mRunningThreadCount > 0 || settings.jobs == 0) { qDebug() << "Can't start checking if there's no files to check or if check is in progress."; emit done(); return; } setThreadCount(settings.jobs); mRunningThreadCount = mThreads.size(); if (mResults.getFileCount() < mRunningThreadCount) { mRunningThreadCount = mResults.getFileCount(); } QStringList addonsAndTools = mAddonsAndTools; for (const std::string& addon: settings.addons) { QString s = QString::fromStdString(addon); if (!addonsAndTools.contains(s)) addonsAndTools << s; } for (int i = 0; i < mRunningThreadCount; i++) { mThreads[i]->setAddonsAndTools(addonsAndTools); mThreads[i]->setSuppressions(mSuppressions); mThreads[i]->setClangIncludePaths(mClangIncludePaths); mThreads[i]->setDataDir(mDataDir); mThreads[i]->check(settings); } // Date and time when checking starts.. mCheckStartTime = QDateTime::currentDateTime(); mAnalyseWholeProgram = true; mTimer.start(); } bool ThreadHandler::isChecking() const { return mRunningThreadCount > 0; } void ThreadHandler::setThreadCount(const int count) { if (mRunningThreadCount > 0 || count == mThreads.size() || count <= 0) { return; } //Remove unused old threads removeThreads(); //Create new threads for (int i = mThreads.size(); i < count; i++) { mThreads << new CheckThread(mResults); connect(mThreads.last(), &CheckThread::done, this, &ThreadHandler::threadDone); connect(mThreads.last(), &CheckThread::fileChecked, &mResults, &ThreadResult::fileChecked); } } void ThreadHandler::removeThreads() { for (CheckThread* thread : mThreads) { thread->terminate(); disconnect(thread, &CheckThread::done, this, &ThreadHandler::threadDone); disconnect(thread, &CheckThread::fileChecked, &mResults, &ThreadResult::fileChecked); delete thread; } mThreads.clear(); mAnalyseWholeProgram = false; } void ThreadHandler::threadDone() { if (mRunningThreadCount == 1 && mAnalyseWholeProgram) { mThreads[0]->analyseWholeProgram(mLastFiles); mAnalyseWholeProgram = false; return; } mRunningThreadCount--; if (mRunningThreadCount == 0) { emit done(); mScanDuration = mTimer.elapsed(); // Set date/time used by the recheck if (!mCheckStartTime.isNull()) { mLastCheckTime = mCheckStartTime; mCheckStartTime = QDateTime(); } } } void ThreadHandler::stop() { mCheckStartTime = QDateTime(); mAnalyseWholeProgram = false; for (CheckThread* thread : mThreads) { thread->stop(); } } void ThreadHandler::initialize(ResultsView *view) { connect(&mResults, &ThreadResult::progress, view, &ResultsView::progress); connect(&mResults, &ThreadResult::error, view, &ResultsView::error); connect(&mResults, &ThreadResult::log, this, &ThreadHandler::log); connect(&mResults, &ThreadResult::debugError, this, &ThreadHandler::debugError); connect(&mResults, &ThreadResult::bughuntingReportLine, this, &ThreadHandler::bughuntingReportLine); } void ThreadHandler::loadSettings(const QSettings &settings) { setThreadCount(settings.value(SETTINGS_CHECK_THREADS, 1).toInt()); } void ThreadHandler::saveSettings(QSettings &settings) const { settings.setValue(SETTINGS_CHECK_THREADS, mThreads.size()); } bool ThreadHandler::hasPreviousFiles() const { return !mLastFiles.isEmpty(); } int ThreadHandler::getPreviousFilesCount() const { return mLastFiles.size(); } int ThreadHandler::getPreviousScanDuration() const { return mScanDuration; } QStringList ThreadHandler::getReCheckFiles(bool all) const { if (mLastCheckTime.isNull() || all) return mLastFiles; std::set modified; std::set unmodified; QStringList files; for (int i = 0; i < mLastFiles.size(); ++i) { if (needsReCheck(mLastFiles[i], modified, unmodified)) files.push_back(mLastFiles[i]); } return files; } bool ThreadHandler::needsReCheck(const QString &filename, std::set &modified, std::set &unmodified) const { if (modified.find(filename) != modified.end()) return true; if (unmodified.find(filename) != unmodified.end()) return false; if (QFileInfo(filename).lastModified() > mLastCheckTime) { return true; } // Parse included files recursively QFile f(filename); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return false; // prevent recursion.. unmodified.insert(filename); QTextStream in(&f); while (!in.atEnd()) { QString line = in.readLine(); if (line.startsWith("#include \"")) { line.remove(0,10); int i = line.indexOf("\""); if (i > 0) { line.remove(i,line.length()); line = QFileInfo(filename).absolutePath() + "/" + line; if (needsReCheck(line, modified, unmodified)) { modified.insert(line); return true; } } } } return false; } QDateTime ThreadHandler::getCheckStartTime() const { return mCheckStartTime; } void ThreadHandler::setCheckStartTime(QDateTime checkStartTime) { mCheckStartTime = std::move(checkStartTime); } cppcheck-2.7/gui/threadhandler.h000066400000000000000000000141271417746362400167270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef THREADHANDLER_H #define THREADHANDLER_H #include "suppressions.h" #include "threadresult.h" #include #include #include #include #include class ResultsView; class CheckThread; class QSettings; class Settings; class ImportProject; /// @addtogroup GUI /// @{ /** * @brief This class handles creating threadresult and starting threads * */ class ThreadHandler : public QObject { Q_OBJECT public: explicit ThreadHandler(QObject *parent = nullptr); virtual ~ThreadHandler(); /** * @brief Set the number of threads to use * @param count The number of threads to use */ void setThreadCount(const int count); /** * @brief Initialize the threads (connect all signals to resultsview's slots) * * @param view View to show error results */ void initialize(ResultsView *view); /** * @brief Load settings * @param settings QSettings to load settings from */ void loadSettings(const QSettings &settings); /** * @brief Save settings * @param settings QSettings to save settings to */ void saveSettings(QSettings &settings) const; void setAddonsAndTools(const QStringList &addonsAndTools) { mAddonsAndTools = addonsAndTools; } void setSuppressions(const QList &s) { mSuppressions = s; } void setClangIncludePaths(const QStringList &s) { mClangIncludePaths = s; } void setDataDir(const QString &dataDir) { mDataDir = dataDir; } /** * @brief Clear all files from cppcheck * */ void clearFiles(); /** * @brief Set files to check * * @param files files to check */ void setFiles(const QStringList &files); /** * @brief Set project to check * * @param prj project to check */ void setProject(const ImportProject &prj); /** * @brief Start the threads to check the files * * @param settings Settings for checking */ void check(const Settings &settings); /** * @brief Set files to check * * @param all true if all files, false if modified files */ void setCheckFiles(bool all); /** * @brief Set selected files to check * * @param files list of files to be checked */ void setCheckFiles(const QStringList& files); /** * @brief Is checking running? * * @return true if check is running, false otherwise. */ bool isChecking() const; /** * @brief Have we checked files already? * * @return true check has been previously run and recheck can be done */ bool hasPreviousFiles() const; /** * @brief Return count of files we checked last time. * * @return count of files that were checked last time. */ int getPreviousFilesCount() const; /** * @brief Return the time elapsed while scanning the previous time. * * @return the time elapsed in milliseconds. */ int getPreviousScanDuration() const; /** * @brief Get files that should be rechecked because they have been * changed. */ QStringList getReCheckFiles(bool all) const; /** * @brief Get start time of last check * * @return start time of last check */ QDateTime getCheckStartTime() const; /** * @brief Set start time of check * * @param checkStartTime saved start time of the last check */ void setCheckStartTime(QDateTime checkStartTime); signals: /** * @brief Signal that all threads are done * */ void done(); void log(const QString &msg); void debugError(const ErrorItem &item); void bughuntingReportLine(QString line); public slots: /** * @brief Slot to stop all threads * */ void stop(); protected slots: /** * @brief Slot that a single thread is done * */ void threadDone(); protected: /** * @brief List of files checked last time (used when rechecking) * */ QStringList mLastFiles; /** @brief date and time when current checking started */ QDateTime mCheckStartTime; /** * @brief when was the files checked the last time (used when rechecking) */ QDateTime mLastCheckTime; /** * @brief Timer used for measuring scan duration * */ QElapsedTimer mTimer; /** * @brief The previous scan duration in milliseconds. * */ int mScanDuration; /** * @brief Function to delete all threads * */ void removeThreads(); /** * @brief Thread results are stored here * */ ThreadResult mResults; /** * @brief List of threads currently in use * */ QList mThreads; /** * @brief The amount of threads currently running * */ int mRunningThreadCount; bool mAnalyseWholeProgram; QStringList mAddonsAndTools; QList mSuppressions; QStringList mClangIncludePaths; QString mDataDir; private: /** * @brief Check if a file needs to be rechecked. Recursively checks * included headers. Used by GetReCheckFiles() */ bool needsReCheck(const QString &filename, std::set &modified, std::set &unmodified) const; }; /// @} #endif // THREADHANDLER_H cppcheck-2.7/gui/threadresult.cpp000066400000000000000000000072451417746362400171660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "threadresult.h" #include "common.h" #include "erroritem.h" #include "errorlogger.h" #include ThreadResult::ThreadResult() : QObject(), ErrorLogger(), mMaxProgress(0), mProgress(0), mFilesChecked(0), mTotalFiles(0) { //ctor } ThreadResult::~ThreadResult() { //dtor } void ThreadResult::reportOut(const std::string &outmsg, Color) { emit log(QString::fromStdString(outmsg)); } void ThreadResult::fileChecked(const QString &file) { QMutexLocker locker(&mutex); mProgress += QFile(file).size(); mFilesChecked++; if (mMaxProgress > 0) { const int value = static_cast(PROGRESS_MAX * mProgress / mMaxProgress); const QString description = tr("%1 of %2 files checked").arg(mFilesChecked).arg(mTotalFiles); emit progress(value, description); } } void ThreadResult::reportErr(const ErrorMessage &msg) { QMutexLocker locker(&mutex); const ErrorItem item(msg); if (msg.severity != Severity::debug) emit error(item); else emit debugError(item); } QString ThreadResult::getNextFile() { QMutexLocker locker(&mutex); if (mFiles.isEmpty()) { return QString(); } return mFiles.takeFirst(); } ImportProject::FileSettings ThreadResult::getNextFileSettings() { QMutexLocker locker(&mutex); if (mFileSettings.empty()) { return ImportProject::FileSettings(); } const ImportProject::FileSettings fs = mFileSettings.front(); mFileSettings.pop_front(); return fs; } void ThreadResult::setFiles(const QStringList &files) { QMutexLocker locker(&mutex); mFiles = files; mProgress = 0; mFilesChecked = 0; mTotalFiles = files.size(); // Determine the total size of all of the files to check, so that we can // show an accurate progress estimate quint64 sizeOfFiles = 0; foreach (const QString& file, files) { sizeOfFiles += QFile(file).size(); } mMaxProgress = sizeOfFiles; } void ThreadResult::setProject(const ImportProject &prj) { QMutexLocker locker(&mutex); mFiles.clear(); mFileSettings = prj.fileSettings; mProgress = 0; mFilesChecked = 0; mTotalFiles = prj.fileSettings.size(); // Determine the total size of all of the files to check, so that we can // show an accurate progress estimate quint64 sizeOfFiles = 0; foreach (const ImportProject::FileSettings& fs, prj.fileSettings) { sizeOfFiles += QFile(QString::fromStdString(fs.filename)).size(); } mMaxProgress = sizeOfFiles; } void ThreadResult::clearFiles() { QMutexLocker locker(&mutex); mFiles.clear(); mFileSettings.clear(); mFilesChecked = 0; mTotalFiles = 0; } int ThreadResult::getFileCount() const { QMutexLocker locker(&mutex); return mFiles.size() + mFileSettings.size(); } void ThreadResult::bughuntingReport(const std::string &str) { if (str.empty()) return; emit bughuntingReportLine(QString::fromStdString(str)); } cppcheck-2.7/gui/threadresult.h000066400000000000000000000066351417746362400166350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef THREADRESULT_H #define THREADRESULT_H #include "color.h" #include "errorlogger.h" #include "importproject.h" #include #include #include class ErrorItem; /// @addtogroup GUI /// @{ /** * @brief Threads use this class to obtain new files to process and to publish results * */ class ThreadResult : public QObject, public ErrorLogger { Q_OBJECT public: ThreadResult(); virtual ~ThreadResult(); /** * @brief Get next unprocessed file * @return File path */ QString getNextFile(); ImportProject::FileSettings getNextFileSettings(); /** * @brief Set list of files to check * @param files List of files to check */ void setFiles(const QStringList &files); void setProject(const ImportProject &prj); /** * @brief Clear files to check * */ void clearFiles(); /** * @brief Get the number of files to check * */ int getFileCount() const; /** * ErrorLogger methods */ void reportOut(const std::string &outmsg, Color c = Color::Reset) override; void reportErr(const ErrorMessage &msg) override; void bughuntingReport(const std::string &str) override; public slots: /** * @brief Slot threads use to signal this class that a specific file is checked * @param file File that is checked */ void fileChecked(const QString &file); signals: /** * @brief Progress signal * @param value Current progress * @param description Description of the current stage */ void progress(int value, const QString& description); /** * @brief Signal of a new error * * @param item Error data */ void error(const ErrorItem &item); /** * @brief Signal of a new log message * * @param logline Log line */ void log(const QString &logline); /** * @brief Signal of a debug error * * @param item Error data */ void debugError(const ErrorItem &item); /** @brief bug hunting report */ void bughuntingReportLine(QString line); protected: /** * @brief Mutex * */ mutable QMutex mutex; /** * @brief List of files to check * */ QStringList mFiles; std::list mFileSettings; /** * @brief Max progress * */ quint64 mMaxProgress; /** * @brief Current progress * */ quint64 mProgress; /** * @brief Current number of files checked * */ unsigned long mFilesChecked; /** * @brief Total number of files * */ unsigned long mTotalFiles; }; /// @} #endif // THREADRESULT_H cppcheck-2.7/gui/translationhandler.cpp000066400000000000000000000141111417746362400203420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "translationhandler.h" #include "common.h" #include #include #include #include #include #include // Provide own translations for standard buttons. This (garbage) code is needed to enforce them to appear in .ts files even after "lupdate gui.pro" static void unused() { Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "OK")) Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel")) Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Close")) Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Save")) } TranslationHandler::TranslationHandler(QObject *parent) : QObject(parent), mCurrentLanguage("en"), mTranslator(nullptr) { // Add our available languages // Keep this list sorted addTranslation("Chinese (Simplified)", "cppcheck_zh_CN"); addTranslation("Dutch", "cppcheck_nl"); addTranslation("English", "cppcheck_en"); addTranslation("Finnish", "cppcheck_fi"); addTranslation("French", "cppcheck_fr"); addTranslation("German", "cppcheck_de"); addTranslation("Italian", "cppcheck_it"); addTranslation("Japanese", "cppcheck_ja"); addTranslation("Korean", "cppcheck_ko"); addTranslation("Russian", "cppcheck_ru"); addTranslation("Serbian", "cppcheck_sr"); addTranslation("Spanish", "cppcheck_es"); addTranslation("Swedish", "cppcheck_sv"); } TranslationHandler::~TranslationHandler() {} bool TranslationHandler::setLanguage(const QString &code) { bool failure = false; QString error; //If English is the language. Code can be e.g. en_US if (code.indexOf("en") == 0) { //Just remove all extra translators if (mTranslator) { qApp->removeTranslator(mTranslator); delete mTranslator; mTranslator = nullptr; } mCurrentLanguage = code; return true; } //Make sure the translator is otherwise valid int index = getLanguageIndexByCode(code); if (index == -1) { error = QObject::tr("Unknown language specified!"); failure = true; } else { // Make sure there is a translator if (!mTranslator && !failure) mTranslator = new QTranslator(this); //Load the new language const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); QString datadir = getDataDir(); QString translationFile; if (QFile::exists(datadir + "/lang/" + mTranslations[index].mFilename + ".qm")) translationFile = datadir + "/lang/" + mTranslations[index].mFilename + ".qm"; else if (QFile::exists(datadir + "/" + mTranslations[index].mFilename + ".qm")) translationFile = datadir + "/" + mTranslations[index].mFilename + ".qm"; else translationFile = appPath + "/" + mTranslations[index].mFilename + ".qm"; if (!mTranslator->load(translationFile) && !failure) { //If it failed, lets check if the default file exists if (!QFile::exists(translationFile)) { error = QObject::tr("Language file %1 not found!"); error = error.arg(translationFile); failure = true; } //If file exists, there's something wrong with it error = QObject::tr("Failed to load translation for language %1 from file %2"); error = error.arg(mTranslations[index].mName); error = error.arg(translationFile); } } if (failure) { const QString msg(tr("Failed to change the user interface language:" "\n\n%1\n\n" "The user interface language has been reset to English. Open " "the Preferences-dialog to select any of the available " "languages.").arg(error)); QMessageBox msgBox(QMessageBox::Warning, tr("Cppcheck"), msg, QMessageBox::Ok); msgBox.exec(); return false; } qApp->installTranslator(mTranslator); mCurrentLanguage = code; return true; } QString TranslationHandler::getCurrentLanguage() const { return mCurrentLanguage; } QString TranslationHandler::suggestLanguage() const { //Get language from system locale's name ie sv_SE or zh_CN QString language = QLocale::system().name(); //qDebug()<<"Your language is"<. */ #ifndef TRANSLATIONHANDLER_H #define TRANSLATIONHANDLER_H #include #include class QTranslator; /// @addtogroup GUI /// @{ /** * @brief Information for one translation. * */ struct TranslationInfo { /** * @brief Readable name for the translation (e.g. "English"). * */ QString mName; /** * @brief Filename for the translation. * */ QString mFilename; /** * @brief ISO 639 language code for the translation (e.g. "en"). * */ QString mCode; }; /** * @brief A class handling the available translations. * * This class contains a list of available translations. The class also keeps * track which translation is the currently active translation. * */ class TranslationHandler : QObject { Q_OBJECT public: explicit TranslationHandler(QObject *parent = nullptr); virtual ~TranslationHandler(); /** * @brief Get a list of available translations. * @return List of available translations. * */ QList getTranslations() const { return mTranslations; } /** * @brief Set active translation. * @param code ISO 639 language code for new selected translation. * @return true if succeeds, false otherwise. * */ bool setLanguage(const QString &code); /** * @brief Get currently selected translation. * @return ISO 639 language code for current translation. * */ QString getCurrentLanguage() const; /** * @brief Get translation suggestion for the system. * This function checks the current system locale and determines which of * the available translations is best as current translation. If none of * the available translations is good then it returns English ("en"). * @return Suggested translation ISO 639 language code. * */ QString suggestLanguage() const; protected: /** * @brief Add new translation to list of available translations. * @param name Name of the translation ("English"). * @param filename Filename of the translation. * */ void addTranslation(const char *name, const char *filename); /** * @brief Find language in the list and return its index. * @param code ISO 639 language code. * @return Index at list, or -1 if not found. * */ int getLanguageIndexByCode(const QString &code) const; private: /** * @brief ISO 639 language code of the currently selected translation. * */ QString mCurrentLanguage; /** * @brief List of available translations. * */ QList mTranslations; /** * @brief Translator class instance. * */ QTranslator *mTranslator; }; /// @} #endif // TRANSLATIONHANDLER_H cppcheck-2.7/gui/txtreport.cpp000066400000000000000000000042051417746362400165240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "txtreport.h" #include "erroritem.h" #include TxtReport::TxtReport(const QString &filename) : Report(filename) {} TxtReport::~TxtReport() {} bool TxtReport::create() { if (Report::create()) { mTxtWriter.setDevice(Report::getFile()); return true; } return false; } void TxtReport::writeHeader() { // No header for txt report } void TxtReport::writeFooter() { // No footer for txt report } void TxtReport::writeError(const ErrorItem &error) { /* Error example from the core program in text [gui/test.cpp:23] -> [gui/test.cpp:14]: (error) Mismatching allocation and deallocation: k */ QString line; for (int i = 0; i < error.errorPath.size(); i++) { const QString file = QDir::toNativeSeparators(error.errorPath[i].file); line += QString("[%1:%2]").arg(file).arg(error.errorPath[i].line); if (i < error.errorPath.size() - 1) { line += " -> "; } if (i == error.errorPath.size() - 1) { line += ": "; } } QString temp = "(%1"; if (error.inconclusive) { temp += ", "; temp += tr("inconclusive"); } temp += ") "; line += temp.arg(GuiSeverity::toString(error.severity)); line += error.summary; #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) mTxtWriter << line << Qt::endl; #else mTxtWriter << line << endl; #endif } cppcheck-2.7/gui/txtreport.h000066400000000000000000000033561417746362400161770ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TXT_REPORT_H #define TXT_REPORT_H #include "report.h" #include #include /// @addtogroup GUI /// @{ /** * @brief Text file report. * This report mimics the output of the command line cppcheck. */ class TxtReport : public Report { Q_OBJECT public: explicit TxtReport(const QString &filename); virtual ~TxtReport(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create() override; /** * @brief Write report header. */ virtual void writeHeader() override; /** * @brief Write report footer. */ virtual void writeFooter() override; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) override; private: /** * @brief Text stream writer for writing the report in text format. */ QTextStream mTxtWriter; }; /// @} #endif // TXT_REPORT_H cppcheck-2.7/gui/variablecontractsdialog.cpp000066400000000000000000000040611417746362400213370ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "variablecontractsdialog.h" #include "ui_variablecontractsdialog.h" #include VariableContractsDialog::VariableContractsDialog(QWidget *parent, QString var) : QDialog(parent), mUI(new Ui::VariableContractsDialog) { mUI->setupUi(this); mVarName = var.indexOf(" ") < 0 ? var : var.mid(0, var.indexOf(" ")); this->setWindowTitle(mVarName); auto getMinMax = [](const QString& var, const QString& minmax) { if (var.indexOf(" " + minmax + ":") < 0) return QString(); int pos1 = var.indexOf(" " + minmax + ":") + 2 + minmax.length(); int pos2 = var.indexOf(" ", pos1); if (pos2 < 0) return var.mid(pos1); return var.mid(pos1, pos2-pos1); }; mUI->mMinValue->setText(getMinMax(var, "min")); mUI->mMaxValue->setText(getMinMax(var, "max")); mUI->mMinValue->setValidator(new QRegExpValidator(QRegExp("-?[0-9]*"))); mUI->mMaxValue->setValidator(new QRegExpValidator(QRegExp("-?[0-9]*"))); } VariableContractsDialog::~VariableContractsDialog() { delete mUI; } QString VariableContractsDialog::getVarname() const { return mVarName; } QString VariableContractsDialog::getMin() const { return mUI->mMinValue->text(); } QString VariableContractsDialog::getMax() const { return mUI->mMaxValue->text(); } cppcheck-2.7/gui/variablecontractsdialog.h000066400000000000000000000023651417746362400210110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VARIABLECONSTRAINTSDIALOG_H #define VARIABLECONSTRAINTSDIALOG_H #include namespace Ui { class VariableContractsDialog; } class VariableContractsDialog : public QDialog { Q_OBJECT public: explicit VariableContractsDialog(QWidget *parent, QString var); ~VariableContractsDialog(); QString getVarname() const; QString getMin() const; QString getMax() const; private: Ui::VariableContractsDialog *mUI; QString mVarName; }; #endif // VARIABLECONSTRAINTSDIALOG_H cppcheck-2.7/gui/variablecontractsdialog.ui000066400000000000000000000050551417746362400211760ustar00rootroot00000000000000 VariableContractsDialog 0 0 400 172 Dialog You can specify min and max value for the variable here Min Max Qt::Vertical 20 25 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() VariableContractsDialog accept() 248 254 157 274 buttonBox rejected() VariableContractsDialog reject() 316 260 286 274 cppcheck-2.7/gui/xmlreport.cpp000066400000000000000000000056071417746362400165140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "xmlreport.h" #include "report.h" #include #include static const char ResultElementName[] = "results"; static const char VersionAttribute[] = "version"; XmlReport::XmlReport(const QString &filename) : Report(filename) {} QString XmlReport::quoteMessage(const QString &message) { QString quotedMessage(message); quotedMessage.replace("&", "&"); quotedMessage.replace("\"", """); quotedMessage.replace("'", "'"); quotedMessage.replace("<", "<"); quotedMessage.replace(">", ">"); return quotedMessage; } QString XmlReport::unquoteMessage(const QString &message) { QString quotedMessage(message); quotedMessage.replace("&", "&"); quotedMessage.replace(""", "\""); quotedMessage.replace("'", "'"); quotedMessage.replace("<", "<"); quotedMessage.replace(">", ">"); return quotedMessage; } int XmlReport::determineVersion(const QString &filename) { QFile file; file.setFileName(filename); bool succeed = file.open(QIODevice::ReadOnly | QIODevice::Text); if (!succeed) return 0; QXmlStreamReader reader(&file); while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::StartElement: if (reader.name() == ResultElementName) { QXmlStreamAttributes attribs = reader.attributes(); if (attribs.hasAttribute(QString(VersionAttribute))) { int ver = attribs.value(QString(), VersionAttribute).toString().toInt(); return ver; } else return 1; } break; // Not handled case QXmlStreamReader::EndElement: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } return 0; } cppcheck-2.7/gui/xmlreport.h000066400000000000000000000034061417746362400161540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef XML_REPORT_H #define XML_REPORT_H #include "report.h" #include #include class ErrorItem; /// @addtogroup GUI /// @{ /** * @brief Base class for XML report classes. */ class XmlReport : public Report { public: explicit XmlReport(const QString &filename); /** * @brief Read contents of the report file. */ virtual QList read() = 0; /** * @brief Quote the message. * @param message Message to quote. * @return quoted message. */ static QString quoteMessage(const QString &message); /** * @brief Unquote the message. * @param message Message to quote. * @return quoted message. */ static QString unquoteMessage(const QString &message); /** * @brief Get the XML report format version from the file. * @param filename Filename of the report file. * @return XML report format version or 0 if error happened. */ static int determineVersion(const QString &filename); }; /// @} #endif // XML_REPORT_H cppcheck-2.7/gui/xmlreportv2.cpp000066400000000000000000000246041417746362400167620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "xmlreportv2.h" #include "cppcheck.h" #include "erroritem.h" #include "report.h" #include "xmlreport.h" #include #include #include static const QString ResultElementName = "results"; static const QString CppcheckElementName = "cppcheck"; static const QString ErrorElementName = "error"; static const QString ErrorsElementName = "errors"; static const QString LocationElementName = "location"; static const QString CWEAttribute = "cwe"; static const QString HashAttribute = "hash"; static const QString SinceDateAttribute = "sinceDate"; static const QString TagsAttribute = "tag"; static const QString FilenameAttribute = "file"; static const QString IncludedFromFilenameAttribute = "file0"; static const QString InconclusiveAttribute = "inconclusive"; static const QString InfoAttribute = "info"; static const QString LineAttribute = "line"; static const QString ColumnAttribute = "column"; static const QString IdAttribute = "id"; static const QString SeverityAttribute = "severity"; static const QString MsgAttribute = "msg"; static const QString VersionAttribute = "version"; static const QString VerboseAttribute = "verbose"; XmlReportV2::XmlReportV2(const QString &filename) : XmlReport(filename), mXmlReader(nullptr), mXmlWriter(nullptr) {} XmlReportV2::~XmlReportV2() { delete mXmlReader; delete mXmlWriter; } bool XmlReportV2::create() { if (Report::create()) { mXmlWriter = new QXmlStreamWriter(Report::getFile()); return true; } return false; } bool XmlReportV2::open() { if (Report::open()) { mXmlReader = new QXmlStreamReader(Report::getFile()); return true; } return false; } void XmlReportV2::writeHeader() { mXmlWriter->setAutoFormatting(true); mXmlWriter->writeStartDocument(); mXmlWriter->writeStartElement(ResultElementName); mXmlWriter->writeAttribute(VersionAttribute, QString::number(2)); mXmlWriter->writeStartElement(CppcheckElementName); mXmlWriter->writeAttribute(VersionAttribute, QString(CppCheck::version())); mXmlWriter->writeEndElement(); mXmlWriter->writeStartElement(ErrorsElementName); } void XmlReportV2::writeFooter() { mXmlWriter->writeEndElement(); // errors mXmlWriter->writeEndElement(); // results mXmlWriter->writeEndDocument(); } void XmlReportV2::writeError(const ErrorItem &error) { /* Error example from the core program in xml */ mXmlWriter->writeStartElement(ErrorElementName); mXmlWriter->writeAttribute(IdAttribute, error.errorId); // Don't localize severity so we can read these files mXmlWriter->writeAttribute(SeverityAttribute, GuiSeverity::toString(error.severity)); const QString summary = XmlReport::quoteMessage(error.summary); mXmlWriter->writeAttribute(MsgAttribute, summary); const QString message = XmlReport::quoteMessage(error.message); mXmlWriter->writeAttribute(VerboseAttribute, message); if (error.inconclusive) mXmlWriter->writeAttribute(InconclusiveAttribute, "true"); if (error.cwe > 0) mXmlWriter->writeAttribute(CWEAttribute, QString::number(error.cwe)); if (error.hash > 0) mXmlWriter->writeAttribute(HashAttribute, QString::number(error.hash)); if (!error.file0.isEmpty()) mXmlWriter->writeAttribute(IncludedFromFilenameAttribute, quoteMessage(error.file0)); if (!error.sinceDate.isEmpty()) mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate); if (!error.tags.isEmpty()) mXmlWriter->writeAttribute(TagsAttribute, error.tags); for (int i = error.errorPath.count() - 1; i >= 0; i--) { mXmlWriter->writeStartElement(LocationElementName); QString file = QDir::toNativeSeparators(error.errorPath[i].file); mXmlWriter->writeAttribute(FilenameAttribute, XmlReport::quoteMessage(file)); mXmlWriter->writeAttribute(LineAttribute, QString::number(error.errorPath[i].line)); if (error.errorPath[i].column > 0) mXmlWriter->writeAttribute(ColumnAttribute, QString::number(error.errorPath[i].column)); if (error.errorPath.count() > 1) mXmlWriter->writeAttribute(InfoAttribute, XmlReport::quoteMessage(error.errorPath[i].info)); mXmlWriter->writeEndElement(); } mXmlWriter->writeEndElement(); } QList XmlReportV2::read() { QList errors; bool insideResults = false; if (!mXmlReader) { qDebug() << "You must Open() the file before reading it!"; return errors; } while (!mXmlReader->atEnd()) { switch (mXmlReader->readNext()) { case QXmlStreamReader::StartElement: if (mXmlReader->name() == ResultElementName) insideResults = true; // Read error element from inside result element if (insideResults && mXmlReader->name() == ErrorElementName) { ErrorItem item = readError(mXmlReader); errors.append(item); } break; case QXmlStreamReader::EndElement: if (mXmlReader->name() == ResultElementName) insideResults = false; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } return errors; } ErrorItem XmlReportV2::readError(QXmlStreamReader *reader) { /* Error example from the core program in xml */ ErrorItem item; // Read error element from inside errors element if (mXmlReader->name() == ErrorElementName) { QXmlStreamAttributes attribs = reader->attributes(); item.errorId = attribs.value(QString(), IdAttribute).toString(); item.severity = GuiSeverity::fromString(attribs.value(QString(), SeverityAttribute).toString()); const QString summary = attribs.value(QString(), MsgAttribute).toString(); item.summary = XmlReport::unquoteMessage(summary); const QString message = attribs.value(QString(), VerboseAttribute).toString(); item.message = XmlReport::unquoteMessage(message); if (attribs.hasAttribute(QString(), InconclusiveAttribute)) item.inconclusive = true; if (attribs.hasAttribute(QString(), CWEAttribute)) item.cwe = attribs.value(QString(), CWEAttribute).toInt(); if (attribs.hasAttribute(QString(), HashAttribute)) item.hash = attribs.value(QString(), HashAttribute).toULongLong(); if (attribs.hasAttribute(QString(), IncludedFromFilenameAttribute)) item.file0 = attribs.value(QString(), IncludedFromFilenameAttribute).toString(); if (attribs.hasAttribute(QString(), SinceDateAttribute)) item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString(); if (attribs.hasAttribute(QString(), TagsAttribute)) item.tags = attribs.value(QString(), TagsAttribute).toString(); } bool errorRead = false; while (!errorRead && !mXmlReader->atEnd()) { switch (mXmlReader->readNext()) { case QXmlStreamReader::StartElement: // Read location element from inside error element if (mXmlReader->name() == LocationElementName) { QXmlStreamAttributes attribs = mXmlReader->attributes(); QString file0 = attribs.value(QString(), IncludedFromFilenameAttribute).toString(); if (!file0.isEmpty()) item.file0 = XmlReport::unquoteMessage(file0); QErrorPathItem loc; loc.file = XmlReport::unquoteMessage(attribs.value(QString(), FilenameAttribute).toString()); loc.line = attribs.value(QString(), LineAttribute).toString().toUInt(); if (attribs.hasAttribute(QString(), ColumnAttribute)) loc.column = attribs.value(QString(), ColumnAttribute).toString().toInt(); if (attribs.hasAttribute(QString(), InfoAttribute)) loc.info = XmlReport::unquoteMessage(attribs.value(QString(), InfoAttribute).toString()); item.errorPath.push_front(loc); } break; case QXmlStreamReader::EndElement: if (mXmlReader->name() == ErrorElementName) errorRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } if (item.errorPath.size() == 1 && item.errorPath[0].info.isEmpty()) item.errorPath[0].info = item.message; return item; } cppcheck-2.7/gui/xmlreportv2.h000066400000000000000000000045011417746362400164210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef XML_REPORTV2_H #define XML_REPORTV2_H #include "xmlreport.h" #include class QXmlStreamReader; class QXmlStreamWriter; /// @addtogroup GUI /// @{ /** * @brief XML file report version 2. * This report outputs XML-formatted report. The XML format must match command * line version's XML output. */ class XmlReportV2 : public XmlReport { public: explicit XmlReportV2(const QString &filename); virtual ~XmlReportV2(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create() override; /** * @brief Open existing report file. */ virtual bool open() override; /** * @brief Write report header. */ virtual void writeHeader() override; /** * @brief Write report footer. */ virtual void writeFooter() override; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) override; /** * @brief Read contents of the report file. */ virtual QList read() override; protected: /** * @brief Read and parse error item from XML stream. * @param reader XML stream reader to use. */ ErrorItem readError(QXmlStreamReader *reader); private: /** * @brief XML stream reader for reading the report in XML format. */ QXmlStreamReader *mXmlReader; /** * @brief XML stream writer for writing the report in XML format. */ QXmlStreamWriter *mXmlWriter; }; /// @} #endif // XML_REPORTV2_H cppcheck-2.7/htmlreport/000077500000000000000000000000001417746362400153605ustar00rootroot00000000000000cppcheck-2.7/htmlreport/README.txt000066400000000000000000000006611417746362400170610ustar00rootroot00000000000000cppcheck-htmlreport This is a little utility to generate a html report of a XML file produced by cppcheck. The utility is implemented in Python (2.7+) and requires the pygments module to generate syntax highlighted source code. If you are using a Debian based Linux system, the pygments package can be installed by following command: $ sudo apt-get install python-pygments For more information run './cppcheck-htmlreport --help' cppcheck-2.7/htmlreport/check.sh000077500000000000000000000055171417746362400170040ustar00rootroot00000000000000#!/bin/bash -ex # Command for checking HTML syntax with HTML Tidy, see http://www.html-tidy.org/ tidy_version=$(tidy --version) if [[ "$tidy_version" == *"5.6.0"* ]] ;then # newer tidy (5.6.0) command, if using this it is not necessary to ignore warnings: tidy_cmd='tidy -o /dev/null -eq --drop-empty-elements no' else # older tidy from 2009 (Ubuntu 16.04 Xenial comes with this old version): tidy_cmd='tidy -o /dev/null -eq' fi function validate_html { if [ ! -f "$1" ]; then echo "File $1 does not exist!" exit 1 fi if ! ${tidy_cmd} "$1"; then echo "HTML validation failed!" exit 1 fi } if [ -z "$PYTHON" ]; then PYTHON=python fi REPORT_DIR=$(mktemp -d -t htmlreport-XXXXXXXXXX) INDEX_HTML="$REPORT_DIR/index.html" STATS_HTML="$REPORT_DIR/stats.html" GUI_TEST_XML="$REPORT_DIR/gui_test.xml" ERRORLIST_XML="$REPORT_DIR/errorlist.xml" UNMATCHEDSUPPR_XML="$REPORT_DIR/unmatchedSuppr.xml" $PYTHON cppcheck-htmlreport --file ../gui/test/data/xmlfiles/xmlreport_v2.xml --title "xml2 test" --report-dir "$REPORT_DIR" --source-dir ../test/ echo -e "\n" # Check HTML syntax validate_html "$INDEX_HTML" validate_html "$STATS_HTML" ../cppcheck ../test/cfg --enable=all --inconclusive --xml-version=2 2> "$GUI_TEST_XML" xmllint --noout "$GUI_TEST_XML" $PYTHON cppcheck-htmlreport --file "$GUI_TEST_XML" --title "xml2 + inconclusive test" --report-dir "$REPORT_DIR" echo "" # Check HTML syntax validate_html "$INDEX_HTML" validate_html "$STATS_HTML" ../cppcheck ../test/cfg --enable=all --inconclusive --verbose --xml-version=2 2> "$GUI_TEST_XML" xmllint --noout "$GUI_TEST_XML" $PYTHON cppcheck-htmlreport --file "$GUI_TEST_XML" --title "xml2 + inconclusive + verbose test" --report-dir "$REPORT_DIR" echo -e "\n" # Check HTML syntax validate_html "$INDEX_HTML" validate_html "$STATS_HTML" ../cppcheck --errorlist --inconclusive --xml-version=2 > "$ERRORLIST_XML" xmllint --noout "$ERRORLIST_XML" $PYTHON cppcheck-htmlreport --file "$ERRORLIST_XML" --title "errorlist" --report-dir "$REPORT_DIR" # Check HTML syntax validate_html "$INDEX_HTML" validate_html "$STATS_HTML" ../cppcheck ../samples/memleak/good.c ../samples/resourceLeak/good.c --xml-version=2 --enable=information --suppressions-list=test_suppressions.txt --xml 2> "$UNMATCHEDSUPPR_XML" xmllint --noout "$UNMATCHEDSUPPR_XML" $PYTHON cppcheck-htmlreport --file "$UNMATCHEDSUPPR_XML" --title "unmatched Suppressions" --report-dir="$REPORT_DIR" grep "unmatchedSuppression<.*>information<.*>Unmatched suppression: variableScope*<" "$INDEX_HTML" grep ">unmatchedSuppressioninformation<.*>Unmatched suppression: uninitstring<" "$INDEX_HTML" grep "notexisting" "$INDEX_HTML" grep ">unmatchedSuppression<.*>information<.*>Unmatched suppression: \*<" "$INDEX_HTML" # Check HTML syntax validate_html "$INDEX_HTML" validate_html "$STATS_HTML" rm -rf "$REPORT_DIR" cppcheck-2.7/htmlreport/cppcheck-htmlreport000077500000000000000000001030661417746362400212720ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import unicode_literals from datetime import date import io import locale import operator import optparse import os import sys import subprocess from collections import Counter from pygments import highlight from pygments.lexers import guess_lexer, guess_lexer_for_filename from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module from pygments.util import ClassNotFound from xml.sax import parse as xml_parse from xml.sax import SAXParseException as XmlParseException from xml.sax.handler import ContentHandler as XmlContentHandler from xml.sax.saxutils import escape """ Turns a cppcheck xml file into a browsable html report along with syntax highlighted source code. """ STYLE_FILE = """ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; font-size: 13px; line-height: 1.5; height: 100%; margin: 0; } #wrapper { position: fixed; height: 100vh; width: 100vw; display: grid; grid-template-rows: fit-content(8rem) auto fit-content(8rem); grid-template-columns: fit-content(25%) 1fr; grid-template-areas: "header header" "menu content" "footer footer"; } h1 { margin: 0 0 8px -2px; font-size: 175%; } .header { padding: 0 0 5px 15px; grid-area: header; border-bottom: thin solid #aaa; } .footer { grid-area: footer; border-top: thin solid #aaa; font-size: 85%; } .footer > p { margin: 4px; } #menu, #menu_index { grid-area: menu; text-align: left; overflow: auto; padding: 0 23px 15px 15px; border-right: thin solid #aaa; min-width: 200px; } #menu > a { display: block; margin-left: 10px; font-size: 12px; } #content, #content_index { grid-area: content; padding: 0px 5px 15px 15px; overflow: auto; } label { white-space: nowrap; } label.checkBtn.disabled { color: #606060; background: #e0e0e0; font-style: italic; } label.checkBtn, input[type="text"] { border: 1px solid grey; border-radius: 4px; box-shadow: 1px 1px inset; padding: 1px 5px; } label.checkBtn { white-space: nowrap; background: #ccddff; } label.unchecked { background: #eff8ff; box-shadow: 1px 1px 1px; } label.checkBtn:hover, label.unchecked:hover{ box-shadow: 0 0 2px; } label.disabled:hover { box-shadow: 1px 1px inset; } label.checkBtn > input { display:none; } .summaryTable { width: 100%; } table.summaryTable td { padding: 0 5px 0 5px; } .statHeader, .severityHeader { font-weight: bold; } .warning { background-color: #ffffa7; } .error { background-color: #ffb7b7; } .error2 { background-color: #faa; display: inline-block; margin-left: 4px; } .inconclusive { background-color: #b6b6b4; } .inconclusive2 { background-color: #b6b6b4; display: inline-block; margin-left: 4px; } .verbose { display: inline-block; vertical-align: top; cursor: help; } .verbose .content { display: none; position: absolute; padding: 10px; margin: 4px; max-width: 40%; white-space: pre-wrap; border: 1px solid #000; background-color: #ffffcc; cursor: auto; } .highlight .hll { padding: 1px; } .highlighttable { background-color: #fff; position: relative; margin: -10px; } .linenos { border-right: thin solid #aaa; color: #d3d3d3; padding-right: 6px; } .id-filtered, .severity-filtered, .file-filtered, .tool-filtered, .text-filtered { visibility: collapse; } """ HTML_HEAD = """ Cppcheck - HTML report - %s
""" HTML_MENU = """
""" HTML_FOOTER = """
""" HTML_ERROR = "<--- %s\n" HTML_INCONCLUSIVE = "<--- %s\n" HTML_EXPANDABLE_ERROR = "
<--- %s [+]
%s
\n""" HTML_EXPANDABLE_INCONCLUSIVE = "
<--- %s [+]
%s
\n""" # escape() and unescape() takes care of &, < and >. html_escape_table = { '"': """, "'": "'" } html_unescape_table = {v: k for k, v in html_escape_table.items()} def html_escape(text): return escape(text, html_escape_table) def filter_button(enabled_filters, id, function): enabled = enabled_filters.get(id, False) return '\n '\ % (' disabled' if not enabled else '', function, id, 'checked' if enabled else 'disabled', id) def filter_bar(enabled): return ''.join([ '
\n' ,''.join([filter_button(enabled, severity, 'toggleSeverity') for severity in ['error', 'warning', 'portability', 'performance', 'style', 'information']]) ,'\n | ' ,''.join([filter_button(enabled, tool, 'toggleTool') for tool in ['cppcheck', 'clang-tidy']]) ,'\n | ' ,'\n ' ,'\n ' ,'\n
\n']) def git_blame(errors, path, file, blame_options): last_line= errors[-1]['line'] if last_line == 0: return {} first_line = next((error for error in errors if error['line'] > 0))['line'] full_path = os.path.join(path, file) path, filename = os.path.split(full_path) if path: os.chdir(path) cmd_args = ['git', 'blame', '-L %d,%d' % (first_line, last_line)] if '-w' in blame_options: cmd_args.append('-w') if '-M' in blame_options: cmd_args.append('-M') cmd_args = cmd_args + ['--porcelain', '--incremental', '--', filename] try: result = subprocess.check_output(cmd_args) result = result.decode(locale.getpreferredencoding()) except: return [] finally: os.chdir(cwd) if result.startswith('fatal'): return [] blame_data = [] line_from = 0 line_to = 0 blame_info = dict() for line_str in result.splitlines(): field, _, value = line_str.partition(' ') if len(field) == 40: line_nr, count = value.split(' ')[1:] line_from = int(line_nr) line_to = line_from + int(count) elif field.startswith('author'): blame_info[field] = value if field == 'author-time': author_date = date.fromtimestamp(int(blame_info['author-time'])) blame_info['author-time'] = author_date.strftime("%d/%m/%Y") elif field == 'filename': blame_data.append([line_from, line_to, blame_info.copy()]) return blame_data def blame_lookup(blame_data, line): return next((data for start, end, data in blame_data if line >= start and line < end), {}) def tr_str(td_th, line, id, cwe, severity, message, author, author_mail, date, add_author, tr_class=None, htmlfile=None, message_class=None): ret = '' if htmlfile: ret += '<%s>%d' % (td_th, htmlfile, line, line, td_th) for item in (id, cwe, severity): ret += '<%s>%s' % (td_th, item, td_th) else: for item in (line, id, cwe, severity): ret += '<%s>%s' % (td_th, item, td_th) if message_class: message_attribute = ' class="%s"' % message_class else: message_attribute = '' ret += '<%s%s>%s' % (td_th, message_attribute, html_escape(message), td_th) for field in add_author: if field == 'name': ret += '<%s>%s' % (td_th, html_escape(author), td_th) elif field == 'email': ret += '<%s>%s' % (td_th, html_escape(author_mail), td_th) elif field == 'date': ret += '<%s>%s' % (td_th, date, td_th) if tr_class: tr_attributes = ' class="%s"' % tr_class else: tr_attributes = '' return '%s' % (tr_attributes, ret) class AnnotateCodeFormatter(HtmlFormatter): errors = [] def wrap(self, source, outfile): line_no = 1 for i, t in HtmlFormatter.wrap(self, source, outfile): # If this is a source code line we want to add a span tag at the # end. if i == 1: for error in self.errors: if error['line'] == line_no: try: if error['inconclusive'] == 'true': # only print verbose msg if it really differs # from actual message if error.get('verbose') and (error['verbose'] != error['msg']): index = t.rfind('\n') t = t[:index] + HTML_EXPANDABLE_INCONCLUSIVE % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] else: t = t.replace('\n', HTML_INCONCLUSIVE % error['msg']) except KeyError: if error.get('verbose') and (error['verbose'] != error['msg']): index = t.rfind('\n') t = t[:index] + HTML_EXPANDABLE_ERROR % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] else: t = t.replace('\n', HTML_ERROR % error['msg']) line_no = line_no + 1 yield i, t class CppCheckHandler(XmlContentHandler): """Parses the cppcheck xml file and produces a list of all its errors.""" def __init__(self): XmlContentHandler.__init__(self) self.errors = [] self.version = '1' self.versionCppcheck = '' def startElement(self, name, attributes): if name == 'results': self.version = attributes.get('version', self.version) if self.version == '1': self.handleVersion1(name, attributes) else: self.handleVersion2(name, attributes) def handleVersion1(self, name, attributes): if name != 'error': return self.errors.append({ 'file': attributes.get('file', ''), 'line': int(attributes.get('line', 0)), 'locations': [{ 'file': attributes.get('file', ''), 'line': int(attributes.get('line', 0)), }], 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'] }) def handleVersion2(self, name, attributes): if name == 'cppcheck': self.versionCppcheck = attributes['version'] if name == 'error': error = { 'locations': [], 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], 'verbose': attributes.get('verbose') } if 'inconclusive' in attributes: error['inconclusive'] = attributes['inconclusive'] if 'cwe' in attributes: error['cwe'] = attributes['cwe'] self.errors.append(error) elif name == 'location': assert self.errors error = self.errors[-1] locations = error['locations'] file = attributes['file'] line = int(attributes['line']) if not locations: error['file'] = file error['line'] = line locations.append({ 'file': file, 'line': line, 'info': attributes.get('info') }) if __name__ == '__main__': # Configure all the options this little utility is using. parser = optparse.OptionParser() parser.add_option('--title', dest='title', help='The title of the project.', default='[project name]') parser.add_option('--file', dest='file', action="append", help='The cppcheck xml output file to read defects ' 'from. You can combine results from several ' 'xml reports i.e. "--file file1.xml --file file2.xml ..". ' 'Default is reading from stdin.') parser.add_option('--report-dir', dest='report_dir', help='The directory where the HTML report content is ' 'written.') parser.add_option('--source-dir', dest='source_dir', help='Base directory where source code files can be ' 'found.') parser.add_option('--add-author-information', dest='add_author_information', help='Blame information to include. ' 'Adds specified author information. ' 'Specify as comma-separated list of either "name", "email", "date" or "n","e","d". ' 'Default: "n,e,d"') parser.add_option('--source-encoding', dest='source_encoding', help='Encoding of source code.', default='utf-8') parser.add_option('--blame-options', dest='blame_options', help='[-w, -M] blame options which you can use to get author and author mail ' '-w --> not including white spaces and returns original author of the line ' '-M --> not including moving of lines and returns original author of the line') # Parse options and make sure that we have an output directory set. options, args = parser.parse_args() try: sys.argv[1] except IndexError: # no arguments give, print --help parser.print_help() quit() if not options.report_dir: parser.error('No report directory set.') # Get the directory where source code files are located. cwd = os.getcwd() source_dir = os.getcwd() if options.source_dir: source_dir = options.source_dir add_author_information = [] if options.add_author_information: fields = [x.strip() for x in options.add_author_information.split(',')] for x in fields: if x.lower() in ['n', 'name']: add_author_information.append('name') elif x.lower() in ['e', 'email']: add_author_information.append('email') elif x.lower() in ['d', 'date']: add_author_information.append('date') else: print('Unrecognized value "%s" for author information, using default (name, email, date)' % x) add_author_information = ['name', 'email', 'date'] break blame_options = '' if options.blame_options: blame_options = options.blame_options add_author_information = add_author_information or ['name', 'email', 'date'] # Parse the xml from all files defined in file argument # or from stdin. If no input is provided, stdin is used # Produce a simple list of errors. print('Parsing xml report.') try: contentHandler = CppCheckHandler() for fname in options.file or [sys.stdin]: xml_parse(fname, contentHandler) except (XmlParseException, ValueError) as msg: print('Failed to parse cppcheck xml file: %s' % msg) sys.exit(1) # We have a list of errors. But now we want to group them on # each source code file. Lets create a files dictionary that # will contain a list of all the errors in that file. For each # file we will also generate a HTML filename to use. files = {} file_no = 0 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 files[filename]['errors'].append(error) # Make sure that the report directory is created if it doesn't exist. print('Creating %s directory' % options.report_dir) if not os.path.exists(options.report_dir): os.makedirs(options.report_dir) # Generate a HTML file with syntax highlighted source code for each # file that contains one or more errors. print('Processing errors') decode_errors = [] for filename, data in sorted(files.items()): htmlfile = data['htmlfile'] errors = [] for error in data['errors']: for location in error['locations']: if filename == location['file']: newError = dict(error) del newError['locations'] newError['line'] = location['line'] if location.get('info'): newError['msg'] = location['info'] newError['severity'] = 'information' del newError['verbose'] errors.append(newError) lines = [] for error in errors: lines.append(error['line']) if filename == '': continue source_filename = os.path.join(source_dir, filename) try: with io.open(source_filename, 'r', encoding=options.source_encoding) as input_file: content = input_file.read() except IOError: if error['id'] == 'unmatchedSuppression': continue # file not found, bail out else: sys.stderr.write("ERROR: Source file '%s' not found.\n" % source_filename) continue except UnicodeDecodeError: sys.stderr.write("WARNING: Unicode decode error in '%s'.\n" % source_filename) decode_errors.append(source_filename[2:]) # "[2:]" gets rid of "./" at beginning continue htmlFormatter = AnnotateCodeFormatter(linenos=True, style='colorful', hl_lines=lines, lineanchors='line', encoding=options.source_encoding) htmlFormatter.errors = errors with io.open(os.path.join(options.report_dir, htmlfile), 'w', encoding='utf-8') as output_file: output_file.write(HTML_HEAD % (options.title, htmlFormatter.get_style_defs('.highlight'), options.title, ': ' + filename)) output_file.write(HTML_HEAD_END) output_file.write(HTML_MENU % (filename.split('/')[-1])) for error in sorted(errors, key=lambda k: k['line']): output_file.write(" %s %s" % (data['htmlfile'], error['line'], error['id'], error['line'])) output_file.write(HTML_MENU_END) try: lexer = guess_lexer_for_filename(source_filename, '', stripnl=False) except ClassNotFound: try: lexer = guess_lexer(content, stripnl=False) except ClassNotFound: sys.stderr.write("ERROR: Couldn't determine lexer for the file' " + source_filename + " '. Won't be able to syntax highlight this file.") output_file.write("\n Could not generate content because pygments failed to determine the code type.") output_file.write("\n Sorry about this.") continue if options.source_encoding: lexer.encoding = options.source_encoding output_file.write( highlight(content, lexer, htmlFormatter).decode( options.source_encoding)) output_file.write(HTML_FOOTER % contentHandler.versionCppcheck) print(' ' + filename) # Generate a master index.html file that will contain a list of # all the errors created. print('Creating index.html') with io.open(os.path.join(options.report_dir, 'index.html'), 'w') as output_file: stats_count = 0 stats = [] filter_enabled = {} for filename, data in sorted(files.items()): for error in data['errors']: stats.append(error['id']) # get the stats filter_enabled[error['severity']] = True filter_enabled['clang-tidy' if error['id'].startswith('clang-tidy-') else 'cppcheck'] = True stats_count += 1 counter = Counter(stats) stat_html = [] # the following lines sort the stat primary by value (occurrences), # but if two IDs occur equally often, then we sort them alphabetically by warning ID try: cnt_max = counter.most_common()[0][1] except IndexError: cnt_max = 0 try: cnt_min = counter.most_common()[-1][1] except IndexError: cnt_min = 0 stat_fmt = "\n {}{}" for occurrences in reversed(range(cnt_min, cnt_max + 1)): for _id in [k for k, v in sorted(counter.items()) if v == occurrences]: stat_html.append(stat_fmt.format(_id, _id, dict(counter.most_common())[_id], _id)) output_file.write(HTML_HEAD % (options.title, '', options.title, '')) output_file.write(filter_bar(filter_enabled)) output_file.write(HTML_HEAD_END) output_file.write(HTML_MENU.replace('id="menu"', 'id="menu_index"', 1).replace("Defects:", "Defect summary", 1) % ('')) output_file.write('\n ') output_file.write('\n ') output_file.write('\n ') output_file.write(''.join(stat_html)) output_file.write('\n ') output_file.write('\n
Show#Defect ID
' + str(stats_count) + 'total
') output_file.write('\n

Statistics

') output_file.write(HTML_MENU_END.replace("content", "content_index", 1)) output_file.write('\n ') output_file.write( '\n %s' % tr_str('th', 'Line', 'Id', 'CWE', 'Severity', 'Message', 'Author', 'Author mail', 'Date (DD/MM/YYYY)', add_author=add_author_information)) for filename, data in sorted(files.items()): file_error = filename in decode_errors or filename.endswith('*') is_file = filename != '' and not file_error row_content = filename if file_error else "%s" % (data['htmlfile'], filename) htmlfile = data.get('htmlfile') if is_file else None output_file.write("\n ") output_file.write("\n " % row_content) if filename in decode_errors: output_file.write("\n ") sorted_errors = sorted(data['errors'], key=lambda k: k['line']) blame_data = git_blame(sorted_errors, source_dir, filename, blame_options) if add_author_information else [] for error in sorted_errors: git_blame_dict = blame_lookup(blame_data, error['line']) message_class = None try: if error['inconclusive'] == 'true': message_class = 'inconclusive' error['severity'] += ", inconcl." except KeyError: pass try: if error['cwe']: cwe_url = "" + error['cwe'] + "" except KeyError: cwe_url = "" if error['severity'] in ['error', 'warning']: message_class = error['severity'] line = error["line"] if is_file else "" output_file.write( '\n %s' % tr_str('td', line, error["id"], cwe_url, error["severity"], error["msg"], git_blame_dict.get('author', 'Unknown'), git_blame_dict.get('author-mail', '---'), git_blame_dict.get('author-time', '---'), tr_class=error["id"] + ' sev_' + error["severity"] + ' issue', message_class=message_class, add_author=add_author_information, htmlfile=htmlfile)) output_file.write("\n ") output_file.write('\n
%s
Could not generated due to UnicodeDecodeError
') output_file.write(HTML_FOOTER % contentHandler.versionCppcheck) if decode_errors: sys.stderr.write("\nGenerating html failed for the following files: " + ' '.join(decode_errors)) sys.stderr.write("\nConsider changing source-encoding (for example: \"htmlreport ... --source-encoding=\"iso8859-1\"\"\n") print('Creating style.css file') os.chdir(cwd) # going back to the cwd to find style.css with io.open(os.path.join(options.report_dir, 'style.css'), 'w') as css_file: css_file.write(STYLE_FILE) print("Creating stats.html (statistics)\n") stats_countlist = {} for filename, data in sorted(files.items()): if filename == '': continue stats_tmplist = [] for error in sorted(data['errors'], key=lambda k: k['line']): stats_tmplist.append(error['severity']) stats_countlist[filename] = dict(Counter(stats_tmplist)) # get top ten for each severity SEVERITIES = "error", "warning", "portability", "performance", "style", "unusedFunction", "information", "missingInclude", "internal" with io.open(os.path.join(options.report_dir, 'stats.html'), 'w') as stats_file: stats_file.write(HTML_HEAD % (options.title, '', options.title, ': Statistics')) stats_file.write(HTML_HEAD_END) stats_file.write(HTML_MENU.replace('id="menu"', 'id="menu_index"', 1).replace("Defects:", "Back to summary", 1) % ('')) stats_file.write(HTML_MENU_END.replace("content", "content_index", 1)) for sev in SEVERITIES: _sum = 0 stats_templist = {} # if the we have an style warning but we are checking for # portability, we have to skip it to prevent KeyError try: for filename in stats_countlist: try: # also bail out if we have a file with no sev-results _sum += stats_countlist[filename][sev] stats_templist[filename] = int(stats_countlist[filename][sev]) # file : amount, except KeyError: continue # don't print "0 style" etc, if no style warnings were found if _sum == 0: continue except KeyError: continue stats_file.write("

Top 10 files for " + sev + " severity, total findings: " + str(_sum) + "
\n") # sort, so that the file with the most severities per type is first stats_list_sorted = sorted(stats_templist.items(), key=operator.itemgetter(1, 0), reverse=True) it = 0 LENGTH = 0 for i in stats_list_sorted: # printing loop # for aesthetics: if it's the first iteration of the loop, get # the max length of the number string if it == 0: LENGTH = len(str(i[1])) # <- length of longest number, now get the difference and try to make other numbers align to it stats_file.write(" " * 3 + str(i[1]) + " " * (1 + LENGTH - len(str(i[1]))) + " " + i[0] + "
\n") it += 1 if it == 10: # print only the top 10 break stats_file.write("

\n") stats_file.write(HTML_FOOTER % contentHandler.versionCppcheck) print("\nOpen '" + options.report_dir + "/index.html' to see the results.") cppcheck-2.7/htmlreport/example.cc000066400000000000000000000000711417746362400173200ustar00rootroot00000000000000#include "missing.h" int main() { int x; x++; } cppcheck-2.7/htmlreport/example.xml000066400000000000000000000007531417746362400175420ustar00rootroot00000000000000 Checking example.cc... Checking usage of global functions.. cppcheck-2.7/htmlreport/requirements.txt000066400000000000000000000000111417746362400206340ustar00rootroot00000000000000Pygments cppcheck-2.7/htmlreport/setup.py000077500000000000000000000010301417746362400170670ustar00rootroot00000000000000#!/usr/bin/env python from setuptools import setup with open('README.txt') as f: readme = f.read() setup( name="cppcheck", description='Python script to parse the XML (version 2) output of ' + 'cppcheck and generate an HTML report using Pygments for syntax ' + 'highlighting.', long_description=readme, author='Henrik Nilsson', url='https://github.com/danmar/cppcheck', license='GPL', scripts=[ "cppcheck-htmlreport", ], install_requires=['Pygments'] ) cppcheck-2.7/htmlreport/test_htmlreport.py000077500000000000000000000070131417746362400211750ustar00rootroot00000000000000#!/usr/bin/env python """Test cppcheck-htmlreport.""" import os import contextlib import shutil import subprocess import sys import tempfile if sys.version_info < (2, 7): # For TestCase.assertIn(). import unittest2 as unittest else: import unittest ROOT_DIR = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0] CPPCHECK_BIN = os.path.join(ROOT_DIR, 'cppcheck') HTML_REPORT_BIN = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'cppcheck-htmlreport') class TestHTMLReport(unittest.TestCase): def testReportError(self): for xml_version in ['2']: self.checkReportError(xml_version) def checkReportError(self, xml_version): with runCheck( os.path.join(ROOT_DIR, 'samples', 'memleak', 'bad.c'), xml_version=xml_version ) as (report, output_directory): self.assertIn('. */ #ifndef analyzerH #define analyzerH #include "config.h" #include "mathlib.h" #include #include #include class Token; template class ValuePtr; struct Analyzer { struct Action { Action() : mFlag(0) {} template ), REQUIRES("T must not be a bool", !std::is_same )> Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor {} enum { None = 0, Read = (1 << 0), Write = (1 << 1), Invalid = (1 << 2), Inconclusive = (1 << 3), Match = (1 << 4), Idempotent = (1 << 5), Incremental = (1 << 6), SymbolicMatch = (1 << 7), Internal = (1 << 8), }; void set(unsigned int f, bool state = true) { mFlag = state ? mFlag | f : mFlag & ~f; } bool get(unsigned int f) const { return ((mFlag & f) != 0); } bool isRead() const { return get(Read); } bool isWrite() const { return get(Write); } bool isInvalid() const { return get(Invalid); } bool isInconclusive() const { return get(Inconclusive); } bool isNone() const { return mFlag == None; } bool isModified() const { return isWrite() || isInvalid(); } bool isIdempotent() const { return get(Idempotent); } bool isIncremental() const { return get(Incremental); } bool isSymbolicMatch() const { return get(SymbolicMatch); } bool isInternal() const { return get(Internal); } bool matches() const { return get(Match); } Action& operator|=(Action a) { set(a.mFlag); return *this; } friend Action operator|(Action a, Action b) { a |= b; return a; } friend bool operator==(Action a, Action b) { return a.mFlag == b.mFlag; } friend bool operator!=(Action a, Action b) { return a.mFlag != b.mFlag; } private: unsigned int mFlag; }; enum class Terminate { None, Bail, Escape, Modified, Inconclusive, Conditional }; struct Result { Result(Action action = Action::None, Terminate terminate = Terminate::None) : action(action), terminate(terminate) {} Action action; Terminate terminate; void update(Result rhs) { if (terminate == Terminate::None) terminate = rhs.terminate; action |= rhs.action; } }; enum class Direction { Forward, Reverse }; struct Assume { enum Flags { None = 0, Quiet = (1 << 0), Absolute = (1 << 1), ContainerEmpty = (1 << 2), }; }; enum class Evaluate { Integral, ContainerEmpty }; /// Analyze a token virtual Action analyze(const Token* tok, Direction d) const = 0; /// Update the state of the value virtual void update(Token* tok, Action a, Direction d) = 0; /// Try to evaluate the value of a token(most likely a condition) virtual std::vector evaluate(Evaluate e, const Token* tok, const Token* ctx = nullptr) const = 0; std::vector evaluate(const Token* tok, const Token* ctx = nullptr) const { return evaluate(Evaluate::Integral, tok, ctx); } /// Lower any values to possible virtual bool lowerToPossible() = 0; /// Lower any values to inconclusive virtual bool lowerToInconclusive() = 0; /// If the analysis is unsure whether to update a scope, this will return true if the analysis should bifurcate the scope virtual bool updateScope(const Token* endBlock, bool modified) const = 0; /// Called when a scope will be forked virtual void forkScope(const Token* /*endBlock*/) {} /// If the value is conditional virtual bool isConditional() const = 0; /// If analysis should stop on the condition virtual bool stopOnCondition(const Token* condTok) const = 0; /// The condition that will be assumed during analysis virtual void assume(const Token* tok, bool state, unsigned int flags = 0) = 0; /// Return analyzer for expression at token virtual ValuePtr reanalyze(Token* tok, const std::string& msg = "") const = 0; virtual ~Analyzer() {} }; #endif cppcheck-2.7/lib/analyzerinfo.cpp000066400000000000000000000127471417746362400171460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "analyzerinfo.h" #include "errorlogger.h" #include "path.h" #include "utils.h" #include #include #include #include // IWYU pragma: keep AnalyzerInformation::~AnalyzerInformation() { close(); } static std::string getFilename(const std::string &fullpath) { std::string::size_type pos1 = fullpath.find_last_of("/\\"); pos1 = (pos1 == std::string::npos) ? 0U : (pos1 + 1U); std::string::size_type pos2 = fullpath.rfind('.'); if (pos2 < pos1) pos2 = std::string::npos; if (pos2 != std::string::npos) pos2 = pos2 - pos1; return fullpath.substr(pos1,pos2); } void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::string &userDefines, const std::list &fileSettings) { std::map fileCount; const std::string filesTxt(buildDir + "/files.txt"); std::ofstream fout(filesTxt); for (const std::string &f : sourcefiles) { const std::string afile = getFilename(f); fout << afile << ".a" << (++fileCount[afile]) << "::" << Path::simplifyPath(Path::fromNativeSeparators(f)) << '\n'; if (!userDefines.empty()) fout << afile << ".a" << (++fileCount[afile]) << ":" << userDefines << ":" << Path::simplifyPath(Path::fromNativeSeparators(f)) << '\n'; } for (const ImportProject::FileSettings &fs : fileSettings) { const std::string afile = getFilename(fs.filename); fout << afile << ".a" << (++fileCount[afile]) << ":" << fs.cfg << ":" << Path::simplifyPath(Path::fromNativeSeparators(fs.filename)) << std::endl; } } void AnalyzerInformation::close() { mAnalyzerInfoFile.clear(); if (mOutputStream.is_open()) { mOutputStream << "\n"; mOutputStream.close(); } } static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long checksum, std::list *errors) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(analyzerInfoFile.c_str()); if (error != tinyxml2::XML_SUCCESS) return false; const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); if (rootNode == nullptr) return false; const char *attr = rootNode->Attribute("checksum"); if (!attr || attr != std::to_string(checksum)) return false; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "error") == 0) errors->emplace_back(e); } return true; } std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg) { const std::string files(buildDir + "/files.txt"); std::ifstream fin(files); if (fin.is_open()) { std::string line; const std::string end(':' + cfg + ':' + sourcefile); while (std::getline(fin,line)) { if (line.size() <= end.size() + 2U) continue; if (!endsWith(line, end.c_str(), end.size())) continue; std::ostringstream ostr; ostr << buildDir << '/' << line.substr(0,line.find(':')); return ostr.str(); } } std::string filename = Path::fromNativeSeparators(buildDir); if (!endsWith(filename, '/')) filename += '/'; const std::string::size_type pos = sourcefile.rfind('/'); if (pos == std::string::npos) filename += sourcefile; else filename += sourcefile.substr(pos+1); filename += ".analyzerinfo"; return filename; } bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list *errors) { if (buildDir.empty() || sourcefile.empty()) return true; close(); mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg); if (skipAnalysis(mAnalyzerInfoFile, checksum, errors)) return false; mOutputStream.open(mAnalyzerInfoFile); if (mOutputStream.is_open()) { mOutputStream << "\n"; mOutputStream << "\n"; } else { mAnalyzerInfoFile.clear(); } return true; } void AnalyzerInformation::reportErr(const ErrorMessage &msg, bool /*verbose*/) { if (mOutputStream.is_open()) mOutputStream << msg.toXML() << '\n'; } void AnalyzerInformation::setFileInfo(const std::string &check, const std::string &fileInfo) { if (mOutputStream.is_open() && !fileInfo.empty()) mOutputStream << " \n" << fileInfo << " \n"; } cppcheck-2.7/lib/analyzerinfo.h000066400000000000000000000046241417746362400166060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef analyzerinfoH #define analyzerinfoH //--------------------------------------------------------------------------- #include "config.h" #include "importproject.h" #include #include #include class ErrorMessage; /// @addtogroup Core /// @{ /** * @brief Analyzer information * * Store various analysis information: * - checksum * - error messages * - whole program analysis data * * The information can be used for various purposes. It allows: * - 'make' - only analyze TUs that are changed and generate full report * - should be possible to add distributed analysis later * - multi-threaded whole program analysis */ class CPPCHECKLIB AnalyzerInformation { public: ~AnalyzerInformation(); static void writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::string &userDefines, const std::list &fileSettings); /** Close current TU.analyzerinfo file */ void close(); bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list *errors); void reportErr(const ErrorMessage &msg, bool verbose); void setFileInfo(const std::string &check, const std::string &fileInfo); static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg); private: std::ofstream mOutputStream; std::string mAnalyzerInfoFile; }; /// @} //--------------------------------------------------------------------------- #endif // analyzerinfoH cppcheck-2.7/lib/astutils.cpp000066400000000000000000003766021417746362400163200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "astutils.h" #include "config.h" #include "errortypes.h" #include "infer.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "valueflow.h" #include "valueptr.h" #include #include #include #include #include #include #include #include #include #include #include #include template )> void visitAstNodesGeneric(T *ast, std::function visitor) { if (!ast) return; std::stack> tokens; T *tok = ast; do { ChildrenToVisit c = visitor(tok); if (c == ChildrenToVisit::done) break; if (c == ChildrenToVisit::op2 || c == ChildrenToVisit::op1_and_op2) { T *t2 = tok->astOperand2(); if (t2) tokens.push(t2); } if (c == ChildrenToVisit::op1 || c == ChildrenToVisit::op1_and_op2) { T *t1 = tok->astOperand1(); if (t1) tokens.push(t1); } if (tokens.empty()) break; tok = tokens.top(); tokens.pop(); } while (true); } void visitAstNodes(const Token *ast, std::function visitor) { visitAstNodesGeneric(ast, std::move(visitor)); } void visitAstNodes(Token *ast, std::function visitor) { visitAstNodesGeneric(ast, std::move(visitor)); } const Token* findAstNode(const Token* ast, const std::function& pred) { const Token* result = nullptr; visitAstNodes(ast, [&](const Token* tok) { if (pred(tok)) { result = tok; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); return result; } const Token* findExpression(const nonneg int exprid, const Token* start, const Token* end, const std::function& pred) { if (!precedes(start, end)) return nullptr; if (exprid == 0) return nullptr; for (const Token* tok = start; tok != end; tok = tok->next()) { if (tok->exprId() != exprid) continue; if (pred(tok)) return tok; } return nullptr; } static int findArgumentPosRecursive(const Token* tok, const Token* tokToFind, bool &found, nonneg int depth=0) { ++depth; if (!tok || depth >= 100) return -1; if (tok->str() == ",") { int res = findArgumentPosRecursive(tok->astOperand1(), tokToFind, found, depth); if (res == -1) return -1; if (found) return res; int argn = res; res = findArgumentPosRecursive(tok->astOperand2(), tokToFind, found, depth); if (res == -1) return -1; return argn + res; } else { if (tokToFind == tok) found = true; return 1; } } static int findArgumentPos(const Token* tok, const Token* tokToFind){ bool found = false; int argn = findArgumentPosRecursive(tok, tokToFind, found, 0); if (found) return argn - 1; return -1; } static int getArgumentPos(const Token* ftok, const Token* tokToFind){ const Token* tok = ftok; if (Token::Match(tok, "%name% (|{")) tok = ftok->next(); if (!Token::Match(tok, "(|{|[")) return -1; const Token* startTok = tok->astOperand2(); if (!startTok && tok->next() != tok->link()) startTok = tok->astOperand1(); return findArgumentPos(startTok, tokToFind); } template )> static void astFlattenRecursive(T* tok, std::vector* result, const char* op, nonneg int depth = 0) { ++depth; if (!tok || depth >= 100) return; if (tok->str() == op) { astFlattenRecursive(tok->astOperand1(), result, op, depth); astFlattenRecursive(tok->astOperand2(), result, op, depth); } else { result->push_back(tok); } } std::vector astFlatten(const Token* tok, const char* op) { std::vector result; astFlattenRecursive(tok, &result, op); return result; } std::vector astFlatten(Token* tok, const char* op) { std::vector result; astFlattenRecursive(tok, &result, op); return result; } bool astHasToken(const Token* root, const Token * tok) { if (!root) return false; while (tok->astParent() && tok != root) tok = tok->astParent(); return root == tok; } bool astHasVar(const Token * tok, nonneg int varid) { if (!tok) return false; if (tok->varId() == varid) return true; return astHasVar(tok->astOperand1(), varid) || astHasVar(tok->astOperand2(), varid); } static bool astIsCharWithSign(const Token *tok, ValueType::Sign sign) { if (!tok) return false; const ValueType *valueType = tok->valueType(); if (!valueType) return false; return valueType->type == ValueType::Type::CHAR && valueType->pointer == 0U && valueType->sign == sign; } bool astIsSignedChar(const Token *tok) { return astIsCharWithSign(tok, ValueType::Sign::SIGNED); } bool astIsUnknownSignChar(const Token *tok) { return astIsCharWithSign(tok, ValueType::Sign::UNKNOWN_SIGN); } bool astIsGenericChar(const Token* tok) { return !astIsPointer(tok) && tok && tok->valueType() && (tok->valueType()->type == ValueType::Type::CHAR || tok->valueType()->type == ValueType::Type::WCHAR_T); } bool astIsPrimitive(const Token* tok) { const ValueType* vt = tok ? tok->valueType() : nullptr; if (!vt) return false; return vt->isPrimitive(); } bool astIsIntegral(const Token *tok, bool unknown) { const ValueType *vt = tok ? tok->valueType() : nullptr; if (!vt) return unknown; return vt->isIntegral() && vt->pointer == 0U; } bool astIsUnsigned(const Token* tok) { return tok && tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED; } bool astIsFloat(const Token *tok, bool unknown) { const ValueType *vt = tok ? tok->valueType() : nullptr; if (!vt) return unknown; return vt->type >= ValueType::Type::FLOAT && vt->pointer == 0U; } bool astIsBool(const Token *tok) { return tok && (tok->isBoolean() || (tok->valueType() && tok->valueType()->type == ValueType::Type::BOOL && !tok->valueType()->pointer)); } bool astIsPointer(const Token *tok) { return tok && tok->valueType() && tok->valueType()->pointer; } bool astIsSmartPointer(const Token* tok) { return tok && tok->valueType() && tok->valueType()->smartPointerTypeToken; } bool astIsUniqueSmartPointer(const Token* tok) { if (!astIsSmartPointer(tok)) return false; if (!tok->valueType()->smartPointer) return false; return tok->valueType()->smartPointer->unique; } bool astIsIterator(const Token *tok) { return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::ITERATOR; } bool astIsContainer(const Token* tok) { return getLibraryContainer(tok) != nullptr && !astIsIterator(tok); } bool astIsContainerView(const Token* tok) { const Library::Container* container = getLibraryContainer(tok); return container && !astIsIterator(tok) && container->view; } bool astIsContainerOwned(const Token* tok) { return astIsContainer(tok) && !astIsContainerView(tok); } std::string astCanonicalType(const Token *expr) { if (!expr) return ""; std::pair decl = Token::typeDecl(expr); if (decl.first && decl.second) { std::string ret; for (const Token *type = decl.first; Token::Match(type,"%name%|::") && type != decl.second; type = type->next()) { if (!Token::Match(type, "const|static")) ret += type->str(); } return ret; } return ""; } static bool match(const Token *tok, const std::string &rhs) { if (tok->str() == rhs) return true; if (!tok->varId() && tok->hasKnownIntValue() && MathLib::toString(tok->values().front().intvalue) == rhs) return true; return false; } const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok) { if (!tok) return nullptr; const Token *ret = nullptr; if (tok->isComparisonOp()) { if (tok->astOperand1() && match(tok->astOperand1(), rhs)) { // Invert comparator std::string s = tok->str(); if (s[0] == '>') s[0] = '<'; else if (s[0] == '<') s[0] = '>'; if (s == comp) { ret = tok->astOperand2(); } } else if (tok->str() == comp && tok->astOperand2() && match(tok->astOperand2(), rhs)) { ret = tok->astOperand1(); } } else if (comp == "!=" && rhs == std::string("0")) { if (tok->str() == "!") { ret = tok->astOperand1(); // handle (!(x==0)) as (x!=0) astIsVariableComparison(ret, "==", "0", &ret); } else ret = tok; } else if (comp == "==" && rhs == std::string("0")) { if (tok->str() == "!") { ret = tok->astOperand1(); // handle (!(x!=0)) as (x==0) astIsVariableComparison(ret, "!=", "0", &ret); } } while (ret && ret->str() == ".") ret = ret->astOperand2(); if (ret && ret->str() == "=" && ret->astOperand1() && ret->astOperand1()->varId()) ret = ret->astOperand1(); else if (ret && ret->varId() == 0U) ret = nullptr; if (vartok) *vartok = ret; return ret; } bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknown) { if (!tok) return false; if (Token::simpleMatch(tok, ".")) return (tok->originalName() != "->" && isTemporary(cpp, tok->astOperand1(), library)) || isTemporary(cpp, tok->astOperand2(), library); if (Token::Match(tok, ",|::")) return isTemporary(cpp, tok->astOperand2(), library); if (tok->isCast() || (cpp && isCPPCast(tok))) return isTemporary(cpp, tok->astOperand2(), library); if (Token::Match(tok, "?|.|[|++|--|%name%|%assign%")) return false; if (tok->isUnaryOp("*")) return false; if (Token::Match(tok, "&|<<|>>") && isLikelyStream(cpp, tok->astOperand1())) return false; if (Token::simpleMatch(tok, "(") && tok->astOperand1() && (tok->astOperand2() || Token::simpleMatch(tok->next(), ")"))) { if (tok->valueType()) { return tok->valueType()->reference == Reference::None; } const Token* ftok = nullptr; if (Token::simpleMatch(tok->previous(), ">") && tok->previous()->link()) ftok = tok->previous()->link()->previous(); else ftok = tok->previous(); if (!ftok) return false; if (const Function * f = ftok->function()) { return !Function::returnsReference(f, true); } else if (ftok->type()) { return true; } else if (library) { std::string returnType = library->returnValueType(ftok); return !returnType.empty() && returnType.back() != '&'; } else { return unknown; } } if (tok->isCast()) return false; // Currying a function is unknown in cppcheck if (Token::simpleMatch(tok, "(") && Token::simpleMatch(tok->astOperand1(), "(")) return unknown; if (Token::simpleMatch(tok, "{") && Token::simpleMatch(tok->astParent(), "return") && tok->astOperand1() && !tok->astOperand2()) return isTemporary(cpp, tok->astOperand1(), library); return true; } static bool isFunctionCall(const Token* tok) { if (Token::Match(tok, "%name% (")) return true; if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) return true; if (Token::Match(tok, "%name% ::")) return isFunctionCall(tok->tokAt(2)); return false; } static bool hasToken(const Token * startTok, const Token * stopTok, const Token * tok) { for (const Token * tok2 = startTok; tok2 != stopTok; tok2 = tok2->next()) { if (tok2 == tok) return true; } return false; } template )> static T* previousBeforeAstLeftmostLeafGeneric(T* tok) { if (!tok) return nullptr; T* leftmostLeaf = tok; while (leftmostLeaf->astOperand1()) leftmostLeaf = leftmostLeaf->astOperand1(); return leftmostLeaf->previous(); } const Token* previousBeforeAstLeftmostLeaf(const Token* tok) { return previousBeforeAstLeftmostLeafGeneric(tok); } Token* previousBeforeAstLeftmostLeaf(Token* tok) { return previousBeforeAstLeftmostLeafGeneric(tok); } template )> static T* nextAfterAstRightmostLeafGeneric(T* tok) { const Token * rightmostLeaf = tok; if (!rightmostLeaf || !rightmostLeaf->astOperand1()) return nullptr; do { if (const Token* lam = findLambdaEndToken(rightmostLeaf)) { rightmostLeaf = lam; break; } if (rightmostLeaf->astOperand2() && precedes(rightmostLeaf, rightmostLeaf->astOperand2())) rightmostLeaf = rightmostLeaf->astOperand2(); else if (rightmostLeaf->astOperand1() && precedes(rightmostLeaf, rightmostLeaf->astOperand1())) rightmostLeaf = rightmostLeaf->astOperand1(); else break; } while (rightmostLeaf->astOperand1() || rightmostLeaf->astOperand2()); while (Token::Match(rightmostLeaf->next(), "]|)") && !hasToken(rightmostLeaf->next()->link(), rightmostLeaf->next(), tok)) rightmostLeaf = rightmostLeaf->next(); if (Token::Match(rightmostLeaf, "{|(|[") && rightmostLeaf->link()) rightmostLeaf = rightmostLeaf->link(); return rightmostLeaf->next(); } const Token* nextAfterAstRightmostLeaf(const Token* tok) { return nextAfterAstRightmostLeafGeneric(tok); } Token* nextAfterAstRightmostLeaf(Token* tok) { return nextAfterAstRightmostLeafGeneric(tok); } const Token* astParentSkipParens(const Token* tok) { return astParentSkipParens(const_cast(tok)); } Token* astParentSkipParens(Token* tok) { if (!tok) return nullptr; Token * parent = tok->astParent(); if (!Token::simpleMatch(parent, "(")) return parent; if (parent->link() != nextAfterAstRightmostLeaf(tok)) return parent; if (Token::Match(parent->previous(), "%name% (") || (Token::simpleMatch(parent->previous(), "> (") && parent->previous()->link())) return parent; return astParentSkipParens(parent); } const Token* getParentMember(const Token * tok) { if (!tok) return tok; const Token * parent = tok->astParent(); if (!Token::simpleMatch(parent, ".")) return tok; if (astIsRHS(tok)) { if (Token::simpleMatch(parent->astOperand1(), ".")) return parent->astOperand1()->astOperand2(); return parent->astOperand1(); } const Token * gparent = parent->astParent(); if (!Token::simpleMatch(gparent, ".") || gparent->astOperand2() != parent) return tok; if (gparent->astOperand1()) return gparent->astOperand1(); return tok; } const Token* getParentLifetime(const Token* tok) { if (!tok) return tok; const Variable* var = tok->variable(); // TODO: Call getLifetimeVariable for deeper analysis if (!var) return tok; if (var->isLocal() || var->isArgument()) return tok; const Token* parent = getParentMember(tok); if (parent != tok) return getParentLifetime(parent); return tok; } bool astIsLHS(const Token* tok) { if (!tok) return false; const Token* parent = tok->astParent(); if (!parent) return false; if (!parent->astOperand1()) return false; if (!parent->astOperand2()) return false; return parent->astOperand1() == tok; } bool astIsRHS(const Token* tok) { if (!tok) return false; const Token* parent = tok->astParent(); if (!parent) return false; if (!parent->astOperand1()) return false; if (!parent->astOperand2()) return false; return parent->astOperand2() == tok; } template )> static T* getCondTokImpl(T* tok) { if (!tok) return nullptr; if (Token::simpleMatch(tok, "(")) return getCondTok(tok->previous()); if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") && tok->next()->astOperand2()->astOperand2()) return tok->next()->astOperand2()->astOperand2()->astOperand1(); if (Token::simpleMatch(tok->next()->astOperand2(), ";")) return tok->next()->astOperand2()->astOperand1(); return tok->next()->astOperand2(); } template )> static T* getCondTokFromEndImpl(T* endBlock) { if (!Token::simpleMatch(endBlock, "}")) return nullptr; T* startBlock = endBlock->link(); if (!Token::simpleMatch(startBlock, "{")) return nullptr; if (Token::simpleMatch(startBlock->previous(), ")")) { return getCondTok(startBlock->previous()->link()); } else if (Token::simpleMatch(startBlock->tokAt(-2), "} else {")) { return getCondTokFromEnd(startBlock->tokAt(-2)); } return nullptr; } template )> static T* getInitTokImpl(T* tok) { if (!tok) return nullptr; if (Token::Match(tok, "%name% (")) return getInitTokImpl(tok->next()); if (tok->str() != "(") return nullptr; if (!Token::simpleMatch(tok->astOperand2(), ";")) return nullptr; if (Token::simpleMatch(tok->astOperand2()->astOperand1(), ";")) return nullptr; return tok->astOperand2()->astOperand1(); } template )> static T* getStepTokImpl(T* tok) { if (!tok) return nullptr; if (Token::Match(tok, "%name% (")) return getStepTokImpl(tok->next()); if (tok->str() != "(") return nullptr; if (!Token::simpleMatch(tok->astOperand2(), ";")) return nullptr; if (!Token::simpleMatch(tok->astOperand2()->astOperand2(), ";")) return nullptr; return tok->astOperand2()->astOperand2()->astOperand2(); } Token* getCondTok(Token* tok) { return getCondTokImpl(tok); } const Token* getCondTok(const Token* tok) { return getCondTokImpl(tok); } Token* getCondTokFromEnd(Token* endBlock) { return getCondTokFromEndImpl(endBlock); } const Token* getCondTokFromEnd(const Token* endBlock) { return getCondTokFromEndImpl(endBlock); } Token* getInitTok(Token* tok) { return getInitTokImpl(tok); } const Token* getInitTok(const Token* tok) { return getInitTokImpl(tok); } Token* getStepTok(Token* tok) { return getStepTokImpl(tok); } const Token* getStepTok(const Token* tok) { return getStepTokImpl(tok); } const Token *findNextTokenFromBreak(const Token *breakToken) { const Scope *scope = breakToken->scope(); while (scope) { if (scope->isLoopScope() || scope->type == Scope::ScopeType::eSwitch) { if (scope->type == Scope::ScopeType::eDo && Token::simpleMatch(scope->bodyEnd, "} while (")) return scope->bodyEnd->linkAt(2)->next(); return scope->bodyEnd; } scope = scope->nestedIn; } return nullptr; } bool extractForLoopValues(const Token *forToken, nonneg int * const varid, bool * const knownInitValue, MathLib::bigint * const initValue, bool * const partialCond, MathLib::bigint * const stepValue, MathLib::bigint * const lastValue) { if (!Token::simpleMatch(forToken, "for (") || !Token::simpleMatch(forToken->next()->astOperand2(), ";")) return false; const Token *initExpr = forToken->next()->astOperand2()->astOperand1(); const Token *condExpr = forToken->next()->astOperand2()->astOperand2()->astOperand1(); const Token *incExpr = forToken->next()->astOperand2()->astOperand2()->astOperand2(); if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%")) return false; std::vector minInitValue = getMinValue(makeIntegralInferModel(), initExpr->astOperand2()->values()); *varid = initExpr->astOperand1()->varId(); *knownInitValue = initExpr->astOperand2()->hasKnownIntValue(); *initValue = minInitValue.empty() ? 0 : minInitValue.front(); *partialCond = Token::Match(condExpr, "%oror%|&&"); visitAstNodes(condExpr, [varid, &condExpr](const Token *tok) { if (Token::Match(tok, "%oror%|&&")) return ChildrenToVisit::op1_and_op2; if (Token::Match(tok, "<|<=") && tok->isBinaryOp() && tok->astOperand1()->varId() == *varid && tok->astOperand2()->hasKnownIntValue()) { if (Token::Match(condExpr, "%oror%|&&") || tok->astOperand2()->getKnownIntValue() < condExpr->astOperand2()->getKnownIntValue()) condExpr = tok; } return ChildrenToVisit::none; }); if (!Token::Match(condExpr, "<|<=") || !condExpr->isBinaryOp() || condExpr->astOperand1()->varId() != *varid || !condExpr->astOperand2()->hasKnownIntValue()) return false; if (!incExpr || !incExpr->isUnaryOp("++") || incExpr->astOperand1()->varId() != *varid) return false; *stepValue = 1; if (condExpr->str() == "<") *lastValue = condExpr->astOperand2()->getKnownIntValue() - 1; else *lastValue = condExpr->astOperand2()->getKnownIntValue(); return true; } static const Token * getVariableInitExpression(const Variable * var) { if (!var) return nullptr; const Token *varDeclEndToken = var->declEndToken(); if (!varDeclEndToken) return nullptr; if (Token::Match(varDeclEndToken, "; %varid% =", var->declarationId())) return varDeclEndToken->tokAt(2)->astOperand2(); return varDeclEndToken->astOperand2(); } static bool isInLoopCondition(const Token * tok) { return Token::Match(tok->astTop()->previous(), "for|while ("); } /// If tok2 comes after tok1 bool precedes(const Token * tok1, const Token * tok2) { if (tok1 == tok2) return false; if (!tok1) return false; if (!tok2) return true; return tok1->index() < tok2->index(); } /// If tok1 comes after tok2 bool succeeds(const Token* tok1, const Token* tok2) { if (tok1 == tok2) return false; if (!tok1) return false; if (!tok2) return true; return tok1->index() > tok2->index(); } bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive) { if (tok->varId() == varid) return false; for (const ValueFlow::Value &val : tok->values()) { if (!val.isLocalLifetimeValue()) continue; if (val.tokvalue->varId() == varid) { if (val.isInconclusive()) { if (inconclusive) *inconclusive = true; else continue; } return true; } } return false; } static bool isAliased(const Token *startTok, const Token *endTok, nonneg int varid) { if (!precedes(startTok, endTok)) return false; for (const Token *tok = startTok; tok != endTok; tok = tok->next()) { if (Token::Match(tok, "= & %varid% ;", varid)) return true; if (isAliasOf(tok, varid)) return true; } return false; } bool isAliased(const Variable *var) { if (!var) return false; if (!var->scope()) return false; const Token *start = var->declEndToken(); if (!start) return false; return isAliased(start, var->scope()->bodyEnd, var->declarationId()); } bool exprDependsOnThis(const Token* expr, bool onVar, nonneg int depth) { if (!expr) return false; if (expr->str() == "this") return true; if (depth >= 1000) // Abort recursion to avoid stack overflow return true; ++depth; // calling nonstatic method? if (Token::Match(expr->previous(), "!!:: %name% (") && expr->function() && expr->function()->nestedIn && expr->function()->nestedIn->isClassOrStruct()) { // is it a method of this? const Scope* fScope = expr->scope(); while (!fScope->functionOf && fScope->nestedIn) fScope = fScope->nestedIn; const Scope* classScope = fScope->functionOf; if (classScope && classScope->function) classScope = classScope->function->token->scope(); if (classScope && classScope->isClassOrStruct()) return contains(classScope->findAssociatedScopes(), expr->function()->nestedIn); return false; } else if (onVar && Token::Match(expr, "%var%") && expr->variable()) { const Variable* var = expr->variable(); return (var->isPrivate() || var->isPublic() || var->isProtected()); } if (Token::simpleMatch(expr, ".")) return exprDependsOnThis(expr->astOperand1(), onVar, depth); return exprDependsOnThis(expr->astOperand1(), onVar, depth) || exprDependsOnThis(expr->astOperand2(), onVar, depth); } static bool hasUnknownVars(const Token* startTok) { bool result = false; visitAstNodes(startTok, [&](const Token* tok) { if (tok->varId() > 0 && !tok->variable()) { result = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); return result; } static bool isStructuredBindingVariable(const Variable* var) { if (!var) return false; const Token* tok = var->nameToken(); while (Token::Match(tok->astParent(), "[|,")) tok = tok->astParent(); return Token::simpleMatch(tok, "["); } /// This takes a token that refers to a variable and it will return the token /// to the expression that the variable is assigned to. If its not valid to /// make such substitution then it will return the original token. static const Token * followVariableExpression(const Token * tok, bool cpp, const Token * end = nullptr) { if (!tok) return tok; // Skip following variables that is across multiple files if (end && end->fileIndex() != tok->fileIndex()) return tok; // Skip array access if (Token::Match(tok, "%var% [")) return tok; // Skip pointer indirection if (tok->astParent() && tok->isUnaryOp("*")) return tok; // Skip following variables if it is used in an assignment if (Token::Match(tok->next(), "%assign%")) return tok; const Variable * var = tok->variable(); const Token * varTok = getVariableInitExpression(var); if (!varTok) return tok; if (hasUnknownVars(varTok)) return tok; if (var->isVolatile()) return tok; if (!var->isLocal() && !var->isConst()) return tok; if (var->isStatic() && !var->isConst()) return tok; if (var->isArgument()) return tok; if (isStructuredBindingVariable(var)) return tok; const Token * lastTok = precedes(tok, end) ? end : tok; // If this is in a loop then check if variables are modified in the entire scope const Token * endToken = (isInLoopCondition(tok) || isInLoopCondition(varTok) || var->scope() != tok->scope()) ? var->scope()->bodyEnd : lastTok; if (!var->isConst() && (!precedes(varTok, endToken) || isVariableChanged(varTok, endToken, tok->varId(), false, nullptr, cpp))) return tok; if (precedes(varTok, endToken) && isAliased(varTok, endToken, tok->varId())) return tok; const Token* startToken = nextAfterAstRightmostLeaf(varTok); if (!startToken) startToken = varTok; if (varTok->exprId() == 0) { if (!varTok->isLiteral()) return tok; } else if (!precedes(startToken, endToken)) { return tok; } else if (isExpressionChanged(varTok, startToken, endToken, nullptr, cpp)) { return tok; } return varTok; } static void followVariableExpressionError(const Token *tok1, const Token *tok2, ErrorPath* errors) { if (!errors) return; if (!tok1) return; if (!tok2) return; ErrorPathItem item = std::make_pair(tok2, "'" + tok1->str() + "' is assigned value '" + tok2->expressionString() + "' here."); if (std::find(errors->begin(), errors->end(), item) != errors->end()) return; errors->push_back(item); } std::vector followAllReferences(const Token* tok, bool temporary, bool inconclusive, ErrorPath errors, int depth) { struct ReferenceTokenLess { bool operator()(const ReferenceToken& x, const ReferenceToken& y) const { return x.token < y.token; } }; if (!tok) return std::vector {}; if (depth < 0) return {{tok, std::move(errors)}}; const Variable *var = tok->variable(); if (var && var->declarationId() == tok->varId()) { if (var->nameToken() == tok || isStructuredBindingVariable(var)) { return {{tok, std::move(errors)}}; } else if (var->isReference() || var->isRValueReference()) { if (!var->declEndToken()) return {{tok, std::move(errors)}}; if (var->isArgument()) { errors.emplace_back(var->declEndToken(), "Passed to reference."); return {{tok, std::move(errors)}}; } else if (Token::simpleMatch(var->declEndToken(), "=")) { if (astHasToken(var->declEndToken(), tok)) return std::vector{}; errors.emplace_back(var->declEndToken(), "Assigned to reference."); const Token *vartok = var->declEndToken()->astOperand2(); if (vartok == tok || (!temporary && isTemporary(true, vartok, nullptr, true) && (var->isConst() || var->isRValueReference()))) return {{tok, std::move(errors)}}; if (vartok) return followAllReferences(vartok, temporary, inconclusive, std::move(errors), depth - 1); } else { return {{tok, std::move(errors)}}; } } } else if (Token::simpleMatch(tok, "?") && Token::simpleMatch(tok->astOperand2(), ":")) { std::set result; const Token* tok2 = tok->astOperand2(); std::vector refs; refs = followAllReferences(tok2->astOperand1(), temporary, inconclusive, errors, depth - 1); result.insert(refs.begin(), refs.end()); refs = followAllReferences(tok2->astOperand2(), temporary, inconclusive, errors, depth - 1); result.insert(refs.begin(), refs.end()); if (!inconclusive && result.size() != 1) return {{tok, std::move(errors)}}; if (!result.empty()) return std::vector(result.begin(), result.end()); } else if (Token::Match(tok->previous(), "%name% (")) { const Function *f = tok->previous()->function(); if (f) { if (!Function::returnsReference(f)) return {{tok, std::move(errors)}}; std::set result; std::vector returns = Function::findReturns(f); for (const Token* returnTok : returns) { if (returnTok == tok) continue; for (const ReferenceToken& rt : followAllReferences(returnTok, temporary, inconclusive, errors, depth - returns.size())) { const Variable* argvar = rt.token->variable(); if (!argvar) return {{tok, std::move(errors)}}; if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { int n = getArgumentPos(argvar, f); if (n < 0) return {{tok, std::move(errors)}}; std::vector args = getArguments(tok->previous()); if (n >= args.size()) return {{tok, std::move(errors)}}; const Token* argTok = args[n]; ErrorPath er = errors; er.emplace_back(returnTok, "Return reference."); er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); std::vector refs = followAllReferences(argTok, temporary, inconclusive, std::move(er), depth - returns.size()); result.insert(refs.begin(), refs.end()); if (!inconclusive && result.size() > 1) return {{tok, std::move(errors)}}; } } } if (!result.empty()) return std::vector(result.begin(), result.end()); } } return {{tok, std::move(errors)}}; } const Token* followReferences(const Token* tok, ErrorPath* errors) { if (!tok) return nullptr; std::vector refs = followAllReferences(tok, true, false); if (refs.size() == 1) { if (errors) *errors = refs.front().errors; return refs.front().token; } return nullptr; } static bool isSameLifetime(const Token * const tok1, const Token * const tok2) { ValueFlow::Value v1 = getLifetimeObjValue(tok1); ValueFlow::Value v2 = getLifetimeObjValue(tok2); if (!v1.isLifetimeValue() || !v2.isLifetimeValue()) return false; return v1.tokvalue == v2.tokvalue; } static bool compareKnownValue(const Token * const tok1, const Token * const tok2, std::function compare) { static const auto isKnownFn = std::mem_fn(&ValueFlow::Value::isKnown); const auto v1 = std::find_if(tok1->values().begin(), tok1->values().end(), isKnownFn); if (v1 == tok1->values().end()) { return false; } if (v1->isNonValue() || v1->isContainerSizeValue() || v1->isSymbolicValue()) return false; const auto v2 = std::find_if(tok2->values().begin(), tok2->values().end(), isKnownFn); if (v2 == tok2->values().end()) { return false; } if (v1->valueType != v2->valueType) { return false; } const bool sameLifetime = isSameLifetime(tok1, tok2); return compare(*v1, *v2, sameLifetime); } bool isEqualKnownValue(const Token * const tok1, const Token * const tok2) { return compareKnownValue(tok1, tok2, [&](const ValueFlow::Value& v1, const ValueFlow::Value& v2, bool sameLifetime) { bool r = v1.equalValue(v2); if (v1.isIteratorValue()) { r &= sameLifetime; } return r; }); } static inline bool isDifferentKnownValues(const Token * const tok1, const Token * const tok2) { return compareKnownValue(tok1, tok2, [&](const ValueFlow::Value& v1, const ValueFlow::Value& v2, bool sameLifetime) { bool r = v1.equalValue(v2); if (v1.isIteratorValue()) { r &= sameLifetime; } return !r; }); } static inline bool isSameConstantValue(bool macro, const Token * const tok1, const Token * const tok2) { if (tok1 == nullptr || tok2 == nullptr) return false; if (!tok1->isNumber() || !tok2->isNumber()) return false; if (macro && (tok1->isExpandedMacro() || tok2->isExpandedMacro() || tok1->isTemplateArg() || tok2->isTemplateArg())) return false; const ValueType * v1 = tok1->valueType(); const ValueType * v2 = tok2->valueType(); if (!v1 || !v2 || v1->sign != v2->sign || v1->type != v2->type || v1->pointer != v2->pointer) return false; return isEqualKnownValue(tok1, tok2); } static bool isForLoopCondition(const Token * const tok) { if (!tok) return false; const Token *const parent = tok->astParent(); return Token::simpleMatch(parent, ";") && parent->astOperand1() == tok && Token::simpleMatch(parent->astParent(), ";") && Token::simpleMatch(parent->astParent()->astParent(), "(") && parent->astParent()->astParent()->astOperand1()->str() == "for"; } static bool isZeroConstant(const Token *tok) { while (tok && tok->isCast()) tok = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); return Token::simpleMatch(tok, "0") && !tok->isExpandedMacro(); } /** * Is token used a boolean (cast to a bool, or used as a condition somewhere) * @param tok the token to check * @param checkingParent true if we are checking a parent. This is used to know * what we are checking. For instance in `if (i == 2)`, isUsedAsBool("==") is * true whereas isUsedAsBool("i") is false, but it might call * isUsedAsBool_internal("==") which must not return true */ static bool isUsedAsBool_internal(const Token * const tok, bool checkingParent) { if (!tok) return false; const Token::Type type = tok->tokType(); if (type == Token::eBitOp || type == Token::eIncDecOp || (type == Token::eArithmeticalOp && !tok->isUnaryOp("*"))) // those operators don't return a bool return false; if (type == Token::eComparisonOp) { if (!checkingParent) // this operator returns a bool return true; if (Token::Match(tok, "==|!=")) return isZeroConstant(tok->astOperand1()) || isZeroConstant(tok->astOperand2()); return false; } if (type == Token::eLogicalOp) return true; if (astIsBool(tok)) return true; const Token * const parent = tok->astParent(); if (!parent) return false; if (parent->str() == "(" && parent->astOperand2() == tok) { if (Token::Match(parent->astOperand1(), "if|while")) return true; if (!parent->isCast()) { // casts are handled via the recursive call, as astIsBool will be true // is it a call to a function ? int argnr; const Token *const func = getTokenArgumentFunction(tok, argnr); if (!func || !func->function()) return false; const Variable *var = func->function()->getArgumentVar(argnr); return var && (var->getTypeName() == "bool"); } } else if (isForLoopCondition(tok)) return true; else if (Token::simpleMatch(parent, "?") && astIsLHS(tok)) return true; return isUsedAsBool_internal(parent, true); } bool isUsedAsBool(const Token * const tok) { return isUsedAsBool_internal(tok, false); } static bool astIsBoolLike(const Token* tok) { return astIsBool(tok) || isUsedAsBool(tok); } bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors) { if (tok1 == nullptr && tok2 == nullptr) return true; if (tok1 == nullptr || tok2 == nullptr) return false; if (cpp) { if (tok1->str() == "." && tok1->astOperand1() && tok1->astOperand1()->str() == "this") tok1 = tok1->astOperand2(); if (tok2->str() == "." && tok2->astOperand1() && tok2->astOperand1()->str() == "this") tok2 = tok2->astOperand2(); } // Skip double not if (Token::simpleMatch(tok1, "!") && Token::simpleMatch(tok1->astOperand1(), "!") && !Token::simpleMatch(tok1->astParent(), "=")) { return isSameExpression(cpp, macro, tok1->astOperand1()->astOperand1(), tok2, library, pure, followVar, errors); } if (Token::simpleMatch(tok2, "!") && Token::simpleMatch(tok2->astOperand1(), "!") && !Token::simpleMatch(tok2->astParent(), "=")) { return isSameExpression(cpp, macro, tok1, tok2->astOperand1()->astOperand1(), library, pure, followVar, errors); } const bool tok_str_eq = tok1->str() == tok2->str(); if (!tok_str_eq && isDifferentKnownValues(tok1, tok2)) return false; if (isSameConstantValue(macro, tok1, tok2)) return true; // Follow variable if (followVar && !tok_str_eq && (Token::Match(tok1, "%var%") || Token::Match(tok2, "%var%"))) { const Token * varTok1 = followVariableExpression(tok1, cpp, tok2); if ((varTok1->str() == tok2->str()) || isSameConstantValue(macro, varTok1, tok2)) { followVariableExpressionError(tok1, varTok1, errors); return isSameExpression(cpp, macro, varTok1, tok2, library, true, followVar, errors); } const Token * varTok2 = followVariableExpression(tok2, cpp, tok1); if ((tok1->str() == varTok2->str()) || isSameConstantValue(macro, tok1, varTok2)) { followVariableExpressionError(tok2, varTok2, errors); return isSameExpression(cpp, macro, tok1, varTok2, library, true, followVar, errors); } if ((varTok1->str() == varTok2->str()) || isSameConstantValue(macro, varTok1, varTok2)) { followVariableExpressionError(tok1, varTok1, errors); followVariableExpressionError(tok2, varTok2, errors); return isSameExpression(cpp, macro, varTok1, varTok2, library, true, followVar, errors); } } // Follow references if (!tok_str_eq) { const Token* refTok1 = followReferences(tok1, errors); const Token* refTok2 = followReferences(tok2, errors); if (refTok1 != tok1 || refTok2 != tok2) return isSameExpression(cpp, macro, refTok1, refTok2, library, pure, followVar, errors); } if (tok1->varId() != tok2->varId() || !tok_str_eq || tok1->originalName() != tok2->originalName()) { if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) || (Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) { return isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, followVar, errors) && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, followVar, errors); } const Token* condTok = nullptr; const Token* exprTok = nullptr; if (Token::Match(tok1, "==|!=")) { condTok = tok1; exprTok = tok2; } else if (Token::Match(tok2, "==|!=")) { condTok = tok2; exprTok = tok1; } if (condTok && condTok->astOperand1() && condTok->astOperand2() && !Token::Match(exprTok, "%comp%")) { const Token* varTok1 = nullptr; const Token* varTok2 = exprTok; const ValueFlow::Value* value = nullptr; if (condTok->astOperand1()->hasKnownIntValue()) { value = &condTok->astOperand1()->values().front(); varTok1 = condTok->astOperand2(); } else if (condTok->astOperand2()->hasKnownIntValue()) { value = &condTok->astOperand2()->values().front(); varTok1 = condTok->astOperand1(); } if (Token::simpleMatch(exprTok, "!")) varTok2 = exprTok->astOperand1(); bool compare = false; if (value) { if (value->intvalue == 0 && Token::simpleMatch(exprTok, "!") && Token::simpleMatch(condTok, "==")) { compare = true; } else if (value->intvalue == 0 && !Token::simpleMatch(exprTok, "!") && Token::simpleMatch(condTok, "!=")) { compare = true; } else if (value->intvalue != 0 && Token::simpleMatch(exprTok, "!") && Token::simpleMatch(condTok, "!=")) { compare = true; } else if (value->intvalue != 0 && !Token::simpleMatch(exprTok, "!") && Token::simpleMatch(condTok, "==")) { compare = true; } } if (compare && astIsBoolLike(varTok1) && astIsBoolLike(varTok2)) return isSameExpression(cpp, macro, varTok1, varTok2, library, pure, followVar, errors); } return false; } auto flagsDiffer = [](const Token* tok1, const Token* tok2, bool macro) { if (macro && (tok1->isExpandedMacro() || tok2->isExpandedMacro() || tok1->isTemplateArg() || tok2->isTemplateArg())) return true; if (tok1->isComplex() != tok2->isComplex()) return true; if (tok1->isLong() != tok2->isLong()) return true; if (tok1->isUnsigned() != tok2->isUnsigned()) return true; if (tok1->isSigned() != tok2->isSigned()) return true; return false; }; if (flagsDiffer(tok1, tok2, macro)) return false; if (pure && tok1->isName() && tok1->next()->str() == "(" && tok1->str() != "sizeof" && !(tok1->variable() && tok1 == tok1->variable()->nameToken())) { if (!tok1->function()) { if (Token::simpleMatch(tok1->previous(), ".")) { const Token *lhs = tok1->previous(); while (Token::Match(lhs, "(|.|[")) lhs = lhs->astOperand1(); if (!lhs) return false; const bool lhsIsConst = (lhs->variable() && lhs->variable()->isConst()) || (lhs->valueType() && lhs->valueType()->constness > 0) || (Token::Match(lhs, "%var% . %name% (") && library.isFunctionConst(lhs->tokAt(2))); if (!lhsIsConst) return false; } else { const Token * ftok = tok1; if (Token::simpleMatch(tok1->previous(), "::")) ftok = tok1->previous(); if (!library.isFunctionConst(ftok) && !ftok->isAttributeConst() && !ftok->isAttributePure()) return false; } } else { if (tok1->function() && !tok1->function()->isConst() && !tok1->function()->isAttributeConst() && !tok1->function()->isAttributePure()) return false; } } // templates/casts if ((Token::Match(tok1, "%name% <") && tok1->next()->link()) || (Token::Match(tok2, "%name% <") && tok2->next()->link())) { // non-const template function that is not a dynamic_cast => return false if (pure && Token::simpleMatch(tok1->next()->link(), "> (") && !(tok1->function() && tok1->function()->isConst()) && tok1->str() != "dynamic_cast") return false; // some template/cast stuff.. check that the template arguments are same const Token *t1 = tok1->next(); const Token *t2 = tok2->next(); const Token *end1 = t1->link(); const Token *end2 = t2->link(); while (t1 && t2 && t1 != end1 && t2 != end2) { if (t1->str() != t2->str() || flagsDiffer(t1, t2, macro)) return false; t1 = t1->next(); t2 = t2->next(); } if (t1 != end1 || t2 != end2) return false; } if (tok1->tokType() == Token::eIncDecOp || tok1->isAssignmentOp()) return false; // bailout when we see ({..}) if (tok1->str() == "{") return false; // cast => assert that the casts are equal if (tok1->str() == "(" && tok1->previous() && !tok1->previous()->isName() && !(tok1->previous()->str() == ">" && tok1->previous()->link())) { const Token *t1 = tok1->next(); const Token *t2 = tok2->next(); while (t1 && t2 && t1->str() == t2->str() && !flagsDiffer(t1, t2, macro) && (t1->isName() || t1->str() == "*")) { t1 = t1->next(); t2 = t2->next(); } if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") return false; } bool noncommutativeEquals = isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand1(), library, pure, followVar, errors); noncommutativeEquals = noncommutativeEquals && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand2(), library, pure, followVar, errors); if (noncommutativeEquals) return true; // in c++, a+b might be different to b+a, depending on the type of a and b if (cpp && tok1->str() == "+" && tok1->isBinaryOp()) { const ValueType* vt1 = tok1->astOperand1()->valueType(); const ValueType* vt2 = tok1->astOperand2()->valueType(); if (!(vt1 && (vt1->type >= ValueType::VOID || vt1->pointer) && vt2 && (vt2->type >= ValueType::VOID || vt2->pointer))) return false; } const bool commutative = tok1->isBinaryOp() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); bool commutativeEquals = commutative && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, followVar, errors); commutativeEquals = commutativeEquals && isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, followVar, errors); return commutativeEquals; } static bool isZeroBoundCond(const Token * const cond) { if (cond == nullptr) return false; // Assume unsigned // TODO: Handle reverse conditions const bool isZero = cond->astOperand2()->getValue(0); if (cond->str() == "==" || cond->str() == ">=") return isZero; if (cond->str() == "<=") return true; if (cond->str() == "<") return !isZero; if (cond->str() == ">") return false; return false; } bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const Library& library, bool pure, bool followVar, ErrorPath* errors) { if (!cond1 || !cond2) return false; if (!isNot && cond1->str() == "&&" && cond2->str() == "&&") { for (const Token* tok1: { cond1->astOperand1(), cond1->astOperand2() }) { for (const Token* tok2: { cond2->astOperand1(), cond2->astOperand2() }) { if (isSameExpression(cpp, true, tok1, tok2, library, pure, followVar, errors)) { if (isOppositeCond(isNot, cpp, tok1->astSibling(), tok2->astSibling(), library, pure, followVar, errors)) return true; } } } } if (cond1->str() == "!") { if (cond2->str() == "!=") { if (cond2->astOperand1() && cond2->astOperand1()->str() == "0") return isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand2(), library, pure, followVar, errors); if (cond2->astOperand2() && cond2->astOperand2()->str() == "0") return isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors); } if (!isUsedAsBool(cond2)) return false; return isSameExpression(cpp, true, cond1->astOperand1(), cond2, library, pure, followVar, errors); } if (cond2->str() == "!") return isOppositeCond(isNot, cpp, cond2, cond1, library, pure, followVar, errors); if (!isNot) { if (cond1->str() == "==" && cond2->str() == "==") { if (isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors)) return isDifferentKnownValues(cond1->astOperand2(), cond2->astOperand2()); if (isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand2(), library, pure, followVar, errors)) return isDifferentKnownValues(cond1->astOperand1(), cond2->astOperand1()); } // TODO: Handle reverse conditions if (Library::isContainerYield(cond1, Library::Container::Yield::EMPTY, "empty") && Library::isContainerYield(cond2->astOperand1(), Library::Container::Yield::SIZE, "size") && isSameExpression(cpp, true, cond1->astOperand1()->astOperand1(), cond2->astOperand1()->astOperand1()->astOperand1(), library, pure, followVar, errors)) { return !isZeroBoundCond(cond2); } if (Library::isContainerYield(cond2, Library::Container::Yield::EMPTY, "empty") && Library::isContainerYield(cond1->astOperand1(), Library::Container::Yield::SIZE, "size") && isSameExpression(cpp, true, cond2->astOperand1()->astOperand1(), cond1->astOperand1()->astOperand1()->astOperand1(), library, pure, followVar, errors)) { return !isZeroBoundCond(cond1); } } if (!cond1->isComparisonOp() || !cond2->isComparisonOp()) return false; const std::string &comp1 = cond1->str(); // condition found .. get comparator std::string comp2; if (isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors) && isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand2(), library, pure, followVar, errors)) { comp2 = cond2->str(); } else if (isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand2(), library, pure, followVar, errors) && isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand1(), library, pure, followVar, errors)) { comp2 = cond2->str(); if (comp2[0] == '>') comp2[0] = '<'; else if (comp2[0] == '<') comp2[0] = '>'; } if (!isNot && comp2.empty()) { const Token *expr1 = nullptr, *value1 = nullptr, *expr2 = nullptr, *value2 = nullptr; std::string op1 = cond1->str(), op2 = cond2->str(); if (cond1->astOperand2()->hasKnownIntValue()) { expr1 = cond1->astOperand1(); value1 = cond1->astOperand2(); } else if (cond1->astOperand1()->hasKnownIntValue()) { expr1 = cond1->astOperand2(); value1 = cond1->astOperand1(); if (op1[0] == '>') op1[0] = '<'; else if (op1[0] == '<') op1[0] = '>'; } if (cond2->astOperand2()->hasKnownIntValue()) { expr2 = cond2->astOperand1(); value2 = cond2->astOperand2(); } else if (cond2->astOperand1()->hasKnownIntValue()) { expr2 = cond2->astOperand2(); value2 = cond2->astOperand1(); if (op2[0] == '>') op2[0] = '<'; else if (op2[0] == '<') op2[0] = '>'; } if (!expr1 || !value1 || !expr2 || !value2) { return false; } if (!isSameExpression(cpp, true, expr1, expr2, library, pure, followVar, errors)) return false; const ValueFlow::Value &rhsValue1 = value1->values().front(); const ValueFlow::Value &rhsValue2 = value2->values().front(); if (op1 == "<" || op1 == "<=") return (op2 == "==" || op2 == ">" || op2 == ">=") && (rhsValue1.intvalue < rhsValue2.intvalue); else if (op1 == ">=" || op1 == ">") return (op2 == "==" || op2 == "<" || op2 == "<=") && (rhsValue1.intvalue > rhsValue2.intvalue); return false; } // is condition opposite? return ((comp1 == "==" && comp2 == "!=") || (comp1 == "!=" && comp2 == "==") || (comp1 == "<" && comp2 == ">=") || (comp1 == "<=" && comp2 == ">") || (comp1 == ">" && comp2 == "<=") || (comp1 == ">=" && comp2 == "<") || (!isNot && ((comp1 == "<" && comp2 == ">") || (comp1 == ">" && comp2 == "<") || (comp1 == "==" && (comp2 == "!=" || comp2 == ">" || comp2 == "<")) || ((comp1 == "!=" || comp1 == ">" || comp1 == "<") && comp2 == "==") ))); } bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors) { if (!tok1 || !tok2) return false; if (isOppositeCond(true, cpp, tok1, tok2, library, pure, followVar, errors)) return true; if (tok1->isUnaryOp("-") && !(tok2->astParent() && tok2->astParent()->tokType() == Token::eBitOp)) return isSameExpression(cpp, true, tok1->astOperand1(), tok2, library, pure, followVar, errors); if (tok2->isUnaryOp("-") && !(tok2->astParent() && tok2->astParent()->tokType() == Token::eBitOp)) return isSameExpression(cpp, true, tok2->astOperand1(), tok1, library, pure, followVar, errors); return false; } static bool functionModifiesArguments(const Function* f) { return std::any_of(f->argumentList.begin(), f->argumentList.end(), [](const Variable& var) { if (var.isReference() || var.isPointer()) return !var.isConst(); return true; }); } bool isConstFunctionCall(const Token* ftok, const Library& library) { if (!Token::Match(ftok, "%name% (")) return false; if (const Function* f = ftok->function()) { if (f->isAttributePure() || f->isAttributeConst()) return true; // Any modified arguments if (functionModifiesArguments(f)) return false; if (Function::returnsVoid(f)) return false; // Member function call if (Token::simpleMatch(ftok->previous(), ".")) { if (f->isConst()) return true; // Check for const overloaded function that just return the const version if (!Function::returnsConst(f)) { std::vector fs = f->getOverloadedFunctions(); if (std::any_of(fs.begin(), fs.end(), [&](const Function* g) { if (f == g) return false; if (f->argumentList.size() != g->argumentList.size()) return false; if (functionModifiesArguments(g)) return false; if (g->isConst() && Function::returnsConst(g)) return true; return false; })) return true; } return false; } else if (f->argumentList.empty()) { return f->isConstexpr(); } } else if (const Library::Function* lf = library.getFunction(ftok)) { if (lf->ispure) return true; for (auto&& p : lf->argumentChecks) { const Library::ArgumentChecks& ac = p.second; if (ac.direction != Library::ArgumentChecks::Direction::DIR_IN) return false; } if (Token::simpleMatch(ftok->previous(), ".")) { if (!lf->isconst) return false; } else if (lf->argumentChecks.empty()) { return false; } } else if (Token::Match(ftok->previous(), ". %name% (") && ftok->previous()->originalName() != "->" && astIsSmartPointer(ftok->previous()->astOperand1())) { return Token::Match(ftok, "get|get_deleter ( )"); } else if (Token::Match(ftok->previous(), ". %name% (") && astIsContainer(ftok->previous()->astOperand1())) { const Library::Container* container = ftok->previous()->astOperand1()->valueType()->container; if (!container) return false; if (container->getYield(ftok->str()) != Library::Container::Yield::NO_YIELD) return true; if (container->getAction(ftok->str()) == Library::Container::Action::FIND) return true; return false; } else { bool memberFunction = Token::Match(ftok->previous(), ". %name% ("); bool constMember = !memberFunction; if (Token::Match(ftok->tokAt(-2), "%var% . %name% (")) { const Variable* var = ftok->tokAt(-2)->variable(); if (var) constMember = var->isConst(); } // TODO: Only check const on lvalues std::vector args = getArguments(ftok); if (memberFunction && args.empty()) return false; return constMember && std::all_of(args.begin(), args.end(), [](const Token* tok) { const Variable* var = tok->variable(); if (var) return var->isConst(); return false; }); } return true; } bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp) { if (!tok) return true; if (tok->variable() && tok->variable()->isVolatile()) return false; if (tok->isName() && tok->next()->str() == "(") { if (!isConstFunctionCall(tok, library)) return false; } if (tok->tokType() == Token::eIncDecOp) return false; if (tok->isAssignmentOp()) return false; if (isLikelyStreamRead(cpp, tok)) return false; // bailout when we see ({..}) if (tok->str() == "{") return false; return isConstExpression(tok->astOperand1(), library, pure, cpp) && isConstExpression(tok->astOperand2(), library, pure, cpp); } bool isWithoutSideEffects(bool cpp, const Token* tok) { if (!cpp) return true; while (tok && tok->astOperand2() && tok->astOperand2()->str() != "(") tok = tok->astOperand2(); if (tok && tok->varId()) { const Variable* var = tok->variable(); return var && (!var->isClass() || var->isPointer() || var->isStlType()); } return true; } bool isUniqueExpression(const Token* tok) { if (!tok) return true; if (tok->function()) { const Function * fun = tok->function(); const Scope * scope = fun->nestedIn; if (!scope) return true; const std::string returnType = fun->retType ? fun->retType->name() : fun->retDef->stringifyList(fun->tokenDef); for (const Function& f:scope->functionList) { if (f.type != Function::eFunction) continue; const std::string freturnType = f.retType ? f.retType->name() : f.retDef->stringifyList(f.returnDefEnd()); if (f.argumentList.size() == fun->argumentList.size() && returnType == freturnType && f.name() != fun->name()) { return false; } } } else if (tok->variable()) { const Variable * var = tok->variable(); const Scope * scope = var->scope(); if (!scope) return true; const Type * varType = var->type(); // Iterate over the variables in scope and the parameters of the function if possible const Function * fun = scope->function; const std::list* setOfVars[] = {&scope->varlist, fun ? &fun->argumentList : nullptr}; for (const std::list* vars:setOfVars) { if (!vars) continue; bool other = std::any_of(vars->cbegin(), vars->cend(), [=](const Variable &v) { if (varType) return v.type() && v.type()->name() == varType->name() && v.name() != var->name(); return v.isFloatingType() == var->isFloatingType() && v.isEnumType() == var->isEnumType() && v.isClass() == var->isClass() && v.isArray() == var->isArray() && v.isPointer() == var->isPointer() && v.name() != var->name(); }); if (other) return false; } } else if (!isUniqueExpression(tok->astOperand1())) { return false; } return isUniqueExpression(tok->astOperand2()); } static bool isEscaped(const Token* tok, bool functionsScope, const Library* library) { if (library && library->isnoreturn(tok)) return true; if (functionsScope) return Token::simpleMatch(tok, "throw"); else return Token::Match(tok, "return|throw"); } static bool isEscapedOrJump(const Token* tok, bool functionsScope, const Library* library) { if (library && library->isnoreturn(tok)) return true; if (functionsScope) return Token::simpleMatch(tok, "throw"); else return Token::Match(tok, "return|goto|throw|continue|break"); } bool isEscapeFunction(const Token* ftok, const Library* library) { if (!Token::Match(ftok, "%name% (")) return false; const Function* function = ftok->function(); if (function) { if (function->isEscapeFunction()) return true; if (function->isAttributeNoreturn()) return true; } else if (library) { if (library->isnoreturn(ftok)) return true; } return false; } static bool hasNoreturnFunction(const Token* tok, const Library* library, const Token** unknownFunc) { if (!tok) return false; const Token* ftok = tok->str() == "(" ? tok->previous() : nullptr; while (Token::simpleMatch(ftok, "(")) ftok = ftok->astOperand1(); if (ftok) { const Function * function = ftok->function(); if (function) { if (function->isEscapeFunction()) return true; if (function->isAttributeNoreturn()) return true; } else if (library && library->isnoreturn(ftok)) { return true; } else if (Token::Match(ftok, "exit|abort")) { return true; } if (unknownFunc && !function && library && library->functions.count(library->getFunctionName(ftok)) == 0) *unknownFunc = ftok; return false; } else if (tok->isConstOp()) { return hasNoreturnFunction(tok->astOperand1(), library, unknownFunc) || hasNoreturnFunction(tok->astOperand2(), library, unknownFunc); } return false; } bool isReturnScope(const Token* const endToken, const Library* library, const Token** unknownFunc, bool functionScope) { if (!endToken || endToken->str() != "}") return false; const Token *prev = endToken->previous(); while (prev && Token::simpleMatch(prev->previous(), "; ;")) prev = prev->previous(); if (prev && Token::simpleMatch(prev->previous(), "} ;")) prev = prev->previous(); if (Token::simpleMatch(prev, "}")) { if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {")) return isReturnScope(prev, library, unknownFunc, functionScope) && isReturnScope(prev->link()->tokAt(-2), library, unknownFunc, functionScope); // TODO: Check all cases if (!functionScope && Token::simpleMatch(prev->link()->previous(), ") {") && Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") && !Token::findsimplematch(prev->link(), "break", prev)) { return isReturnScope(prev, library, unknownFunc, functionScope); } if (isEscaped(prev->link()->astTop(), functionScope, library)) return true; if (Token::Match(prev->link()->previous(), "[;{}] {")) return isReturnScope(prev, library, unknownFunc, functionScope); } else if (Token::simpleMatch(prev, ";")) { if (prev->tokAt(-2) && hasNoreturnFunction(prev->tokAt(-2)->astTop(), library, unknownFunc)) return true; // Unknown symbol if (Token::Match(prev->tokAt(-2), ";|}|{ %name% ;") && prev->previous()->isIncompleteVar()) { if (unknownFunc) *unknownFunc = prev->previous(); return false; } if (Token::simpleMatch(prev->previous(), ") ;") && prev->previous()->link() && isEscaped(prev->previous()->link()->astTop(), functionScope, library)) return true; if (isEscaped(prev->previous()->astTop(), functionScope, library)) return true; // return/goto statement prev = prev->previous(); while (prev && !Token::Match(prev, ";|{|}") && !isEscapedOrJump(prev, functionScope, library)) prev = prev->previous(); return prev && prev->isName(); } return false; } bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int varid, const Settings *settings, bool *inconclusive) { if (!tok) return false; if (tok->varId() == varid) return isVariableChangedByFunctionCall(tok, indirect, settings, inconclusive); return isVariableChangedByFunctionCall(tok->astOperand1(), indirect, varid, settings, inconclusive) || isVariableChangedByFunctionCall(tok->astOperand2(), indirect, varid, settings, inconclusive); } bool isScopeBracket(const Token* tok) { if (!Token::Match(tok, "{|}")) return false; if (!tok->scope()) return false; if (tok->str() == "{") return tok->scope()->bodyStart == tok; if (tok->str() == "}") return tok->scope()->bodyEnd == tok; return false; } template )> T* getTokenArgumentFunctionImpl(T* tok, int& argn) { argn = -1; { T* parent = tok->astParent(); if (parent && parent->isUnaryOp("&")) parent = parent->astParent(); while (parent && parent->isCast()) parent = parent->astParent(); if (Token::Match(parent, "[+-]") && parent->valueType() && parent->valueType()->pointer) parent = parent->astParent(); // passing variable to subfunction? if (Token::Match(parent, "[(,{]")) ; else if (Token::simpleMatch(parent, ":")) { while (Token::Match(parent, "[?:]")) parent = parent->astParent(); while (Token::simpleMatch(parent, ",")) parent = parent->astParent(); if (!parent || parent->str() != "(") return nullptr; } else return nullptr; } T* argtok = tok; while (argtok && argtok->astParent() && (!Token::Match(argtok->astParent(), ",|(|{") || argtok->astParent()->isCast())) { argtok = argtok->astParent(); } if (!argtok) return nullptr; if (Token::simpleMatch(argtok, ",")) argtok = argtok->astOperand1(); if (Token::simpleMatch(argtok, "(") && argtok->astOperand2()) argtok = argtok->astOperand2(); tok = argtok; while (Token::Match(tok->astParent(), ",|(|{")) { tok = tok->astParent(); if (Token::Match(tok, "(|{")) break; } argn = getArgumentPos(tok, argtok); if (argn == -1) return nullptr; if (!Token::Match(tok, "{|(")) return nullptr; if (tok->astOperand2()) tok = tok->astOperand1(); while (tok && (tok->isUnaryOp("*") || tok->str() == "[")) tok = tok->astOperand1(); while (Token::simpleMatch(tok, ".")) tok = tok->astOperand2(); while (Token::simpleMatch(tok, "::")) { // If there is only a op1 and not op2, then this is a global scope if (!tok->astOperand2() && tok->astOperand1()) { tok = tok->astOperand1(); break; } tok = tok->astOperand2(); if (Token::simpleMatch(tok, "<") && tok->link()) tok = tok->astOperand1(); } if (tok && tok->link() && tok->str() == ">") tok = tok->link()->previous(); if (!Token::Match(tok, "%name%|(|{")) return nullptr; return tok; } const Token* getTokenArgumentFunction(const Token* tok, int& argn) { return getTokenArgumentFunctionImpl(tok, argn); } Token* getTokenArgumentFunction(Token* tok, int& argn) { return getTokenArgumentFunctionImpl(tok, argn); } std::vector getArgumentVars(const Token* tok, int argnr) { std::vector result; if (!tok) return result; if (tok->function()) { const Variable* argvar = tok->function()->getArgumentVar(argnr); if (argvar) return {argvar}; else return result; } if (Token::Match(tok->previous(), "%type% (|{") || Token::simpleMatch(tok, "{") || tok->variable()) { const bool constructor = Token::simpleMatch(tok, "{") || (tok->variable() && tok->variable()->nameToken() == tok); const Type* type = Token::typeOf(tok); if (!type) return result; const Scope* typeScope = type->classScope; if (!typeScope) return result; // Aggregate constructor if (Token::simpleMatch(tok, "{") && typeScope->numConstructors == 0 && argnr < typeScope->varlist.size()) { auto it = std::next(typeScope->varlist.begin(), argnr); return {&*it}; } const int argCount = numberOfArguments(tok); for (const Function &function : typeScope->functionList) { if (function.argCount() < argCount) continue; if (constructor && !function.isConstructor()) continue; if (!constructor && !Token::simpleMatch(function.token, "operator()")) continue; const Variable* argvar = function.getArgumentVar(argnr); if (argvar) result.push_back(argvar); } } return result; } static bool isCPPCastKeyword(const Token* tok) { if (!tok) return false; return endsWith(tok->str(), "_cast"); } static bool isTrivialConstructor(const Token* tok) { const Token* typeTok = nullptr; const Type* t = Token::typeOf(tok, &typeTok); if (t) return false; if (typeTok->valueType() && typeTok->valueType()->isPrimitive()) return true; return false; } static bool isArray(const Token* tok) { if (!tok) return false; if (tok->variable()) return tok->variable()->isArray(); if (Token::simpleMatch(tok, ".")) return isArray(tok->astOperand2()); return false; } bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive) { if (!tok) return false; if (Token::simpleMatch(tok, ",")) return false; const Token * const tok1 = tok; // address of variable const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&"); int argnr; tok = getTokenArgumentFunction(tok, argnr); if (!tok) return false; // not a function => variable not changed if (Token::simpleMatch(tok, "{") && isTrivialConstructor(tok)) return false; if (tok->isKeyword() && !isCPPCastKeyword(tok) && tok->str().compare(0,8,"operator") != 0) return false; // A functional cast won't modify the variable if (Token::Match(tok, "%type% (|{") && tok->tokType() == Token::eType && astIsPrimitive(tok->next())) return false; const Token * parenTok = tok->next(); if (Token::simpleMatch(parenTok, "<") && parenTok->link()) parenTok = parenTok->link()->next(); const bool possiblyPassedByReference = (parenTok->next() == tok1 || Token::Match(tok1->previous(), ", %name% [,)}]")); if (!tok->function() && !tok->variable() && Token::Match(tok, "%name%")) { // Check if direction (in, out, inout) is specified in the library configuration and use that if (!addressOf && settings) { const Library::ArgumentChecks::Direction argDirection = settings->library.getArgDirection(tok, 1 + argnr); if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) return false; else if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT || argDirection == Library::ArgumentChecks::Direction::DIR_INOUT) { // With out or inout the direction of the content is specified, not a pointer itself, so ignore pointers for now const ValueType * const valueType = tok1->valueType(); if ((valueType && valueType->pointer == indirect) || (indirect == 0 && isArray(tok1))) { return true; } } } // if the library says 0 is invalid // => it is assumed that parameter is an in parameter (TODO: this is a bad heuristic) if (!addressOf && settings && settings->library.isnullargbad(tok, 1+argnr)) return false; // possible pass-by-reference => inconclusive if (possiblyPassedByReference) { if (inconclusive != nullptr) *inconclusive = true; return false; } // Safe guess: Assume that parameter is changed by function call return true; } std::vector args = getArgumentVars(tok, argnr); bool conclusive = false; for (const Variable *arg:args) { if (!arg) continue; conclusive = true; if (addressOf || indirect > 0) { if (!arg->isConst() && arg->isPointer()) return true; // If const is applied to the pointer, then the value can still be modified if (Token::simpleMatch(arg->typeEndToken(), "* const")) return true; if (!arg->isPointer()) return true; } if (!arg->isConst() && arg->isReference()) return true; } if (!conclusive && inconclusive) { *inconclusive = true; } return false; } bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, bool cpp, int depth) { if (!tok) return false; if (indirect == 0 && isConstVarExpression(tok)) return false; const Token *tok2 = tok; int derefs = 0; while (Token::simpleMatch(tok2->astParent(), "*") || (Token::simpleMatch(tok2->astParent(), ".") && !Token::simpleMatch(tok2->astParent()->astParent(), "(")) || (tok2->astParent() && tok2->astParent()->isUnaryOp("&") && !tok2->astParent()->astOperand2() && Token::simpleMatch(tok2->astParent()->astParent(), ".") && tok2->astParent()->astParent()->originalName()=="->") || (Token::simpleMatch(tok2->astParent(), "[") && tok2 == tok2->astParent()->astOperand1())) { if (tok2->astParent() && (tok2->astParent()->isUnaryOp("*") || (astIsLHS(tok2) && tok2->astParent()->originalName() == "->"))) derefs++; if (derefs > indirect) break; if ((tok2->astParent() && tok2->astParent()->isUnaryOp("&") && Token::simpleMatch(tok2->astParent()->astParent(), ".") && tok2->astParent()->astParent()->originalName()=="->")) tok2 = tok2->astParent(); tok2 = tok2->astParent(); } while (Token::simpleMatch(tok2->astParent(), "?") || (Token::simpleMatch(tok2->astParent(), ":") && Token::simpleMatch(tok2->astParent()->astParent(), "?"))) tok2 = tok2->astParent(); if (Token::Match(tok2->astParent(), "++|--")) return true; if (tok2->astParent() && tok2->astParent()->isAssignmentOp()) { if (tok2 == tok2->astParent()->astOperand1()) return true; // Check if assigning to a non-const lvalue const Variable * var = getLHSVariable(tok2->astParent()); if (var && var->isReference() && !var->isConst() && var->nameToken() && var->nameToken()->next() == tok2->astParent()) { if (!var->isLocal() || isVariableChanged(var, settings, cpp, depth - 1)) return true; } } if (cpp && Token::Match(tok2->astParent(), ">>|&") && astIsRHS(tok2) && isLikelyStreamRead(cpp, tok2->astParent())) return true; if (isLikelyStream(cpp, tok2)) return true; // Member function call if (tok->variable() && Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) && tok2->astParent()->astOperand1() == tok2) { const Variable * var = tok->variable(); // Member function cannot change what `this` points to if (indirect == 0 && astIsPointer(tok)) return false; bool isConst = var && var->isConst(); if (!isConst) { const ValueType * valueType = var->valueType(); isConst = (valueType && valueType->pointer == 1 && valueType->constness == 1); } if (isConst) return false; const Token *ftok = tok->tokAt(2); if (settings) return !settings->library.isFunctionConst(ftok); const Function * fun = ftok->function(); if (!fun) return true; return !fun->isConst(); } const Token *ftok = tok2; while (ftok && (!Token::Match(ftok, "[({]") || ftok->isCast())) ftok = ftok->astParent(); if (ftok && Token::Match(ftok->link(), ")|} !!{")) { const Token * ptok = tok2; while (Token::Match(ptok->astParent(), ".|::|[")) ptok = ptok->astParent(); bool inconclusive = false; bool isChanged = isVariableChangedByFunctionCall(ptok, indirect, settings, &inconclusive); isChanged |= inconclusive; if (isChanged) return true; } const Token *parent = tok2->astParent(); while (Token::Match(parent, ".|::")) parent = parent->astParent(); if (parent && parent->tokType() == Token::eIncDecOp) return true; // structured binding, nonconst reference variable in lhs if (Token::Match(tok2->astParent(), ":|=") && tok2 == tok2->astParent()->astOperand2() && Token::simpleMatch(tok2->astParent()->previous(), "]")) { const Token *typeStart = tok2->astParent()->previous()->link()->previous(); if (Token::simpleMatch(typeStart, "&")) typeStart = typeStart->previous(); if (typeStart && Token::Match(typeStart->previous(), "[;{}(] auto &| [")) { for (const Token *vartok = typeStart->tokAt(2); vartok != tok2; vartok = vartok->next()) { if (vartok->varId()) { const Variable* refvar = vartok->variable(); if (!refvar || (!refvar->isConst() && refvar->isReference())) return true; } } } } if (Token::simpleMatch(tok2->astParent(), ":") && tok2->astParent()->astParent() && Token::simpleMatch(tok2->astParent()->astParent()->previous(), "for (")) { // TODO: Check if container is empty or not if (astIsLHS(tok2)) return true; const Token * varTok = tok2->astParent()->previous(); if (!varTok) return false; const Variable * loopVar = varTok->variable(); if (!loopVar) return false; if (!loopVar->isConst() && loopVar->isReference() && isVariableChanged(loopVar, settings, cpp, depth - 1)) return true; return false; } if (indirect > 0) { // check for `*(ptr + 1) = new_value` case parent = tok2->astParent(); while (parent && parent->isArithmeticalOp() && parent->isBinaryOp()) { parent = parent->astParent(); } if (Token::simpleMatch(parent, "*")) { if (parent->astParent() && parent->astParent()->isAssignmentOp() && (parent->astParent()->astOperand1() == parent)) { return true; } } } return false; } bool isVariableChanged(const Token *start, const Token *end, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth) { return findVariableChanged(start, end, 0, exprid, globalvar, settings, cpp, depth) != nullptr; } bool isVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth) { return findVariableChanged(start, end, indirect, exprid, globalvar, settings, cpp, depth) != nullptr; } const Token* findExpression(const Token* start, const nonneg int exprid) { Function * f = Scope::nestedInFunction(start->scope()); if (!f) return nullptr; const Scope* scope = f->functionScope; if (!scope) return nullptr; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->exprId() != exprid) continue; return tok; } return nullptr; } // Thread-unsafe memoization template()())> static std::function memoize(F f) { bool init = false; R result{}; return [=]() mutable -> R { if (init) return result; result = f(); init = true; return result; }; } template()()), const Token*> )> static bool isExpressionChangedAt(const F& getExprTok, const Token* tok, int indirect, const nonneg int exprid, bool globalvar, const Settings* settings, bool cpp, int depth) { if (depth < 0) return true; if (tok->exprId() != exprid) { if (globalvar && Token::Match(tok, "%name% (")) // TODO: Is global variable really changed by function call? return true; const bool pointer = astIsPointer(tok); bool aliased = false; // If we can't find the expression then assume it is an alias if (!getExprTok()) aliased = true; if (!aliased) { aliased = findAstNode(getExprTok(), [&](const Token* childTok) { for (const ValueFlow::Value& val : tok->values()) { if (val.isImpossible()) continue; if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) { if (findAstNode(val.tokvalue, [&](const Token* aliasTok) { return aliasTok->exprId() == childTok->exprId(); })) return true; } } return false; }); } if (!aliased) return false; if (isVariableChanged(tok, 1, settings, cpp, depth)) return true; // TODO: Try to traverse the lambda function if (Token::Match(tok, "%var% (")) return true; return false; } return (isVariableChanged(tok, indirect, settings, cpp, depth)); } bool isExpressionChangedAt(const Token* expr, const Token* tok, int indirect, bool globalvar, const Settings* settings, bool cpp, int depth) { return isExpressionChangedAt([&] { return expr; }, tok, indirect, expr->exprId(), globalvar, settings, cpp, depth); } Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth) { if (!precedes(start, end)) return nullptr; if (depth < 0) return start; auto getExprTok = memoize([&] { return findExpression(start, exprid); }); for (Token *tok = start; tok != end; tok = tok->next()) { if (isExpressionChangedAt(getExprTok, tok, indirect, exprid, globalvar, settings, cpp, depth)) return tok; } return nullptr; } const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth) { return findVariableChanged(const_cast(start), end, indirect, exprid, globalvar, settings, cpp, depth); } bool isVariableChanged(const Variable * var, const Settings *settings, bool cpp, int depth) { if (!var) return false; if (!var->scope()) return false; const Token * start = var->declEndToken(); if (!start) return false; if (Token::Match(start, "; %varid% =", var->declarationId())) start = start->tokAt(2); return isExpressionChanged(var->nameToken(), start->next(), var->scope()->bodyEnd, settings, cpp, depth); } bool isVariablesChanged(const Token* start, const Token* end, int indirect, std::vector vars, const Settings* settings, bool cpp) { std::set varids; std::transform(vars.begin(), vars.end(), std::inserter(varids, varids.begin()), [](const Variable* var) { return var->declarationId(); }); const bool globalvar = std::any_of(vars.begin(), vars.end(), [](const Variable* var) { return var->isGlobal(); }); for (const Token* tok = start; tok != end; tok = tok->next()) { if (tok->varId() == 0 || varids.count(tok->varId()) == 0) { if (globalvar && Token::Match(tok, "%name% (")) // TODO: Is global variable really changed by function call? return true; continue; } if (isVariableChanged(tok, indirect, settings, cpp)) return true; } return false; } bool isThisChanged(const Token* tok, int indirect, const Settings* settings, bool cpp) { if ((Token::Match(tok->previous(), "%name% (") && !Token::simpleMatch(tok->astOperand1(), ".")) || Token::Match(tok->tokAt(-3), "this . %name% (")) { if (tok->previous()->function()) { return (!tok->previous()->function()->isConst()); } else if (!tok->previous()->isKeyword()) { return true; } } if (isVariableChanged(tok, indirect, settings, cpp)) return true; return false; } bool isThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings, bool cpp) { if (!precedes(start, end)) return false; for (const Token* tok = start; tok != end; tok = tok->next()) { if (!exprDependsOnThis(tok)) continue; if (isThisChanged(tok, indirect, settings, cpp)) return true; } return false; } bool isExpressionChanged(const Token* expr, const Token* start, const Token* end, const Settings* settings, bool cpp, int depth) { if (depth < 0) return true; if (!precedes(start, end)) return false; const Token* result = findAstNode(expr, [&](const Token* tok) { if (exprDependsOnThis(tok) && isThisChanged(start, end, false, settings, cpp)) { return true; } bool global = false; if (tok->variable()) { if (tok->variable()->isConst()) return false; global = !tok->variable()->isLocal() && !tok->variable()->isArgument(); } if (tok->exprId() > 0) { for (const Token* tok2 = start; tok2 != end; tok2 = tok2->next()) { if (isExpressionChangedAt( tok, tok2, tok->valueType() ? tok->valueType()->pointer : 0, global, settings, cpp, depth)) return true; } } return false; }); return result; } int numberOfArguments(const Token *start) { int arguments=0; const Token* const openBracket = start->next(); if (openBracket && openBracket->str()=="(" && openBracket->next() && openBracket->next()->str()!=")") { const Token* argument=openBracket->next(); while (argument) { ++arguments; argument = argument->nextArgument(); } } return arguments; } std::vector getArguments(const Token *ftok) { const Token* tok = ftok; if (Token::Match(tok, "%name% (|{")) tok = ftok->next(); if (!Token::Match(tok, "(|{|[")) return std::vector {}; const Token *startTok = tok->astOperand2(); if (!startTok && tok->next() != tok->link()) startTok = tok->astOperand1(); return astFlatten(startTok, ","); } int getArgumentPos(const Variable* var, const Function* f) { auto arg_it = std::find_if(f->argumentList.begin(), f->argumentList.end(), [&](const Variable& v) { return v.nameToken() == var->nameToken(); }); if (arg_it == f->argumentList.end()) return -1; return std::distance(f->argumentList.begin(), arg_it); } bool isIteratorPair(std::vector args) { return args.size() == 2 && ((astIsIterator(args[0]) && astIsIterator(args[1])) || (astIsPointer(args[0]) && astIsPointer(args[1]))); } const Token *findLambdaStartToken(const Token *last) { if (!last || last->str() != "}") return nullptr; const Token* tok = last->link(); if (Token::simpleMatch(tok->astParent(), "(")) tok = tok->astParent(); if (Token::simpleMatch(tok->astParent(), "[")) return tok->astParent(); return nullptr; } template T* findLambdaEndTokenGeneric(T* first) { if (!first || first->str() != "[") return nullptr; if (!Token::Match(first->link(), "] (|{")) return nullptr; if (first->astOperand1() != first->link()->next()) return nullptr; const Token * tok = first; if (tok->astOperand1() && tok->astOperand1()->str() == "(") tok = tok->astOperand1(); if (tok->astOperand1() && tok->astOperand1()->str() == "{") return tok->astOperand1()->link(); return nullptr; } const Token* findLambdaEndToken(const Token* first) { return findLambdaEndTokenGeneric(first); } Token* findLambdaEndToken(Token* first) { return findLambdaEndTokenGeneric(first); } bool isLikelyStream(bool cpp, const Token *stream) { if (!cpp) return false; if (!stream) return false; if (!Token::Match(stream->astParent(), "&|<<|>>") || !stream->astParent()->isBinaryOp()) return false; if (stream->astParent()->astOperand1() != stream) return false; return !astIsIntegral(stream, false); } bool isLikelyStreamRead(bool cpp, const Token *op) { if (!cpp) return false; if (!Token::Match(op, "&|>>") || !op->isBinaryOp()) return false; if (!Token::Match(op->astOperand2(), "%name%|.|*|[") && op->str() != op->astOperand2()->str()) return false; const Token *parent = op; while (parent->astParent() && parent->astParent()->str() == op->str()) parent = parent->astParent(); if (parent->astParent() && !Token::Match(parent->astParent(), "%oror%|&&|(|,|.|!|;")) return false; if (op->str() == "&" && parent->astParent()) return false; if (!parent->astOperand1() || !parent->astOperand2()) return false; return (!parent->astOperand1()->valueType() || !parent->astOperand1()->valueType()->isIntegral()); } bool isCPPCast(const Token* tok) { return tok && Token::simpleMatch(tok->previous(), "> (") && tok->astOperand2() && tok->astOperand1() && isCPPCastKeyword(tok->astOperand1()); } bool isConstVarExpression(const Token *tok, const char* skipMatch) { if (!tok) return false; if (tok->str() == "?" && tok->astOperand2() && tok->astOperand2()->str() == ":") // ternary operator return isConstVarExpression(tok->astOperand2()->astOperand1()) && isConstVarExpression(tok->astOperand2()->astOperand2()); // left and right of ":" if (skipMatch && Token::Match(tok, skipMatch)) return false; if (Token::simpleMatch(tok->previous(), "sizeof (")) return true; if (Token::Match(tok->previous(), "%name% (")) { if (Token::simpleMatch(tok->astOperand1(), ".") && !isConstVarExpression(tok->astOperand1(), skipMatch)) return false; std::vector args = getArguments(tok); return std::all_of(args.begin(), args.end(), [&](const Token* t) { return isConstVarExpression(t, skipMatch); }); } if (isCPPCast(tok)) { return isConstVarExpression(tok->astOperand2(), skipMatch); } if (Token::Match(tok, "( %type%")) return isConstVarExpression(tok->astOperand1(), skipMatch); if (tok->str() == "::" && tok->hasKnownValue()) return isConstVarExpression(tok->astOperand2(), skipMatch); if (Token::Match(tok, "%cop%|[|.")) { if (tok->astOperand1() && !isConstVarExpression(tok->astOperand1(), skipMatch)) return false; if (tok->astOperand2() && !isConstVarExpression(tok->astOperand2(), skipMatch)) return false; return true; } if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL")) return true; if (tok->isEnumerator()) return true; if (tok->variable()) return tok->variable()->isConst() && tok->variable()->nameToken() && tok->variable()->nameToken()->hasKnownValue(); return false; } static void getLHSVariablesRecursive(std::vector& vars, const Token* tok) { if (!tok) return; if (vars.empty() && Token::Match(tok, "*|&|&&|[")) { getLHSVariablesRecursive(vars, tok->astOperand1()); if (!vars.empty() || Token::simpleMatch(tok, "[")) return; getLHSVariablesRecursive(vars, tok->astOperand2()); } else if (Token::Match(tok->previous(), "this . %var%")) { getLHSVariablesRecursive(vars, tok->next()); } else if (Token::simpleMatch(tok, ".")) { getLHSVariablesRecursive(vars, tok->astOperand1()); getLHSVariablesRecursive(vars, tok->astOperand2()); } else if (Token::simpleMatch(tok, "::")) { getLHSVariablesRecursive(vars, tok->astOperand2()); } else if (tok->variable()) { vars.push_back(tok->variable()); } } std::vector getLHSVariables(const Token* tok) { std::vector result; if (!Token::Match(tok, "%assign%|(|{")) return result; if (!tok->astOperand1()) return result; if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable()) return {tok->astOperand1()->variable()}; getLHSVariablesRecursive(result, tok->astOperand1()); return result; } static const Token* getLHSVariableRecursive(const Token* tok) { if (!tok) return nullptr; if (Token::Match(tok, "*|&|&&|[")) { const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); if ((vartok && vartok->variable()) || Token::simpleMatch(tok, "[")) return vartok; return getLHSVariableRecursive(tok->astOperand2()); } if (Token::Match(tok->previous(), "this . %var%")) return tok->next(); return tok; } const Variable *getLHSVariable(const Token *tok) { if (!Token::Match(tok, "%assign%")) return nullptr; if (!tok->astOperand1()) return nullptr; if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable()) return tok->astOperand1()->variable(); const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); if (!vartok) return nullptr; return vartok->variable(); } const Token* getLHSVariableToken(const Token* tok) { if (!Token::Match(tok, "%assign%")) return nullptr; if (!tok->astOperand1()) return nullptr; if (tok->astOperand1()->varId() > 0) return tok->astOperand1(); const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); if (!vartok) return tok->astOperand1(); return vartok; } const Token* findAllocFuncCallToken(const Token *expr, const Library &library) { if (!expr) return nullptr; if (Token::Match(expr, "[+-]")) { const Token *tok1 = findAllocFuncCallToken(expr->astOperand1(), library); return tok1 ? tok1 : findAllocFuncCallToken(expr->astOperand2(), library); } if (expr->isCast()) return findAllocFuncCallToken(expr->astOperand2() ? expr->astOperand2() : expr->astOperand1(), library); if (Token::Match(expr->previous(), "%name% (") && library.getAllocFuncInfo(expr->astOperand1())) return expr->astOperand1(); return (Token::simpleMatch(expr, "new") && expr->astOperand1()) ? expr : nullptr; } static bool nonLocal(const Variable* var, bool deref) { return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern(); } static bool hasGccCompoundStatement(const Token *tok) { if (!tok) return false; if (tok->str() == "{" && Token::simpleMatch(tok->previous(), "( {")) return true; return hasGccCompoundStatement(tok->astOperand1()) || hasGccCompoundStatement(tok->astOperand2()); } static bool hasFunctionCall(const Token *tok) { if (!tok) return false; if (Token::Match(tok, "%name% (")) // todo, const/pure function? return true; return hasFunctionCall(tok->astOperand1()) || hasFunctionCall(tok->astOperand2()); } static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local) { for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) // TODO: this is a quick bailout return false; if (tok->varId() == 0 || exprVarIds.find(tok->varId()) == exprVarIds.end()) continue; const Token *parent = tok; while (parent->astParent() && !parent->astParent()->isAssignmentOp() && parent->astParent()->tokType() != Token::Type::eIncDecOp) { if (parent->str() == "," || parent->isUnaryOp("&")) // TODO: This is a quick bailout return false; parent = parent->astParent(); } if (parent->astParent()) { if (parent->astParent()->tokType() == Token::Type::eIncDecOp) return false; else if (parent->astParent()->isAssignmentOp() && parent == parent->astParent()->astOperand1()) return false; } } return true; } bool isNullOperand(const Token *expr) { if (!expr) return false; if (Token::Match(expr, "static_cast|const_cast|dynamic_cast|reinterpret_cast <")) expr = expr->astParent(); else if (!expr->isCast()) return Token::Match(expr, "NULL|nullptr"); if (expr->valueType() && expr->valueType()->pointer == 0) return false; const Token *castOp = expr->astOperand2() ? expr->astOperand2() : expr->astOperand1(); return Token::Match(castOp, "NULL|nullptr") || (MathLib::isInt(castOp->str()) && MathLib::isNullValue(castOp->str())); } bool isGlobalData(const Token *expr, bool cpp) { // function call that returns reference => assume global data if (expr && expr->str() == "(" && expr->valueType() && expr->valueType()->reference != Reference::None) { if (expr->isBinaryOp()) return true; if (expr->astOperand1() && precedes(expr->astOperand1(), expr)) return true; } bool globalData = false; bool var = false; visitAstNodes(expr, [expr, cpp, &globalData, &var](const Token *tok) { if (tok->varId()) var = true; if (tok->varId() && !tok->variable()) { // Bailout, this is probably global globalData = true; return ChildrenToVisit::none; } if (tok->originalName() == "->") { // TODO check if pointer points at local data globalData = true; return ChildrenToVisit::none; } else if (Token::Match(tok, "[*[]") && tok->astOperand1() && tok->astOperand1()->variable()) { // TODO check if pointer points at local data const Variable *lhsvar = tok->astOperand1()->variable(); const ValueType *lhstype = tok->astOperand1()->valueType(); if (lhsvar->isPointer()) { globalData = true; return ChildrenToVisit::none; } else if (lhsvar->isArgument() && lhsvar->isArray()) { globalData = true; return ChildrenToVisit::none; } else if (lhsvar->isArgument() && (!lhstype || (lhstype->type <= ValueType::Type::VOID && !lhstype->container))) { globalData = true; return ChildrenToVisit::none; } } if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") { globalData = true; return ChildrenToVisit::none; } if (tok->variable()) { // TODO : Check references if (tok->variable()->isReference() && tok != tok->variable()->nameToken()) { globalData = true; return ChildrenToVisit::none; } if (tok->variable()->isExtern()) { globalData = true; return ChildrenToVisit::none; } if (tok->previous()->str() != "." && !tok->variable()->isLocal() && !tok->variable()->isArgument()) { globalData = true; return ChildrenToVisit::none; } if (tok->variable()->isArgument() && tok->variable()->isPointer() && tok != expr) { globalData = true; return ChildrenToVisit::none; } if (tok->variable()->isPointerArray()) { globalData = true; return ChildrenToVisit::none; } } // Unknown argument type => it might be some reference type.. if (cpp && tok->str() == "." && tok->astOperand1() && tok->astOperand1()->variable() && !tok->astOperand1()->valueType()) { globalData = true; return ChildrenToVisit::none; } if (Token::Match(tok, ".|[")) return ChildrenToVisit::op1; return ChildrenToVisit::op1_and_op2; }); return globalData || !var; } struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local, bool inInnerClass, int depth) { // Parse the given tokens if (++depth > 1000) return Result(Result::Type::BAILOUT); for (const Token* tok = startToken; precedes(tok, endToken); tok = tok->next()) { if (Token::simpleMatch(tok, "try {")) { // TODO: handle try return Result(Result::Type::BAILOUT); } if (Token::simpleMatch(tok, "break ;")) { return Result(Result::Type::BREAK, tok); } if (Token::simpleMatch(tok, "goto")) return Result(Result::Type::BAILOUT); if (!inInnerClass && tok->str() == "{" && tok->scope()->isClassOrStruct()) { // skip returns from local class definition FwdAnalysis::Result result = checkRecursive(expr, tok, tok->link(), exprVarIds, local, true, depth); if (result.type != Result::Type::NONE) return result; tok=tok->link(); } if (tok->str() == "continue") // TODO return Result(Result::Type::BAILOUT); if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { tok = lambdaEndToken; const Result lambdaResult = checkRecursive(expr, lambdaEndToken->link()->next(), lambdaEndToken, exprVarIds, local, inInnerClass, depth); if (lambdaResult.type == Result::Type::READ || lambdaResult.type == Result::Type::BAILOUT) return lambdaResult; } if (Token::Match(tok, "return|throw")) { // TODO: Handle these better // Is expr variable used in expression? const Token* opTok = tok->astOperand1(); if (!opTok) opTok = tok->next(); std::pair startEndTokens = opTok->findExpressionStartEndTokens(); FwdAnalysis::Result result = checkRecursive(expr, startEndTokens.first, startEndTokens.second->next(), exprVarIds, local, true, depth); if (result.type != Result::Type::NONE) return result; // #9167: if the return is inside an inner class, it does not tell us anything if (!inInnerClass) { if (!local && mWhat == What::Reassign) return Result(Result::Type::BAILOUT); return Result(Result::Type::RETURN); } } if (tok->str() == "}") { // Known value => possible value if (tok->scope() == expr->scope()) mValueFlowKnown = false; if (tok->scope()->isLoopScope()) { // check condition const Token *conditionStart = nullptr; const Token *conditionEnd = nullptr; if (Token::simpleMatch(tok->link()->previous(), ") {")) { conditionEnd = tok->link()->previous(); conditionStart = conditionEnd->link(); } else if (Token::simpleMatch(tok->link()->previous(), "do {") && Token::simpleMatch(tok, "} while (")) { conditionStart = tok->tokAt(2); conditionEnd = conditionStart->link(); } if (conditionStart && conditionEnd) { bool used = false; for (const Token *condTok = conditionStart; condTok != conditionEnd; condTok = condTok->next()) { if (exprVarIds.find(condTok->varId()) != exprVarIds.end()) { used = true; break; } } if (used) return Result(Result::Type::BAILOUT); } // check loop body again.. const struct FwdAnalysis::Result &result = checkRecursive(expr, tok->link(), tok, exprVarIds, local, inInnerClass, depth); if (result.type == Result::Type::BAILOUT || result.type == Result::Type::READ) return result; } } if (Token::simpleMatch(tok, "else {")) tok = tok->linkAt(1); if (Token::simpleMatch(tok, "asm (")) return Result(Result::Type::BAILOUT); if (mWhat == What::ValueFlow && (Token::Match(tok, "while|for (") || Token::simpleMatch(tok, "do {"))) { const Token *bodyStart = nullptr; const Token *conditionStart = nullptr; if (Token::simpleMatch(tok, "do {")) { bodyStart = tok->next(); if (Token::simpleMatch(bodyStart->link(), "} while (")) conditionStart = bodyStart->link()->tokAt(2); } else { conditionStart = tok->next(); if (Token::simpleMatch(conditionStart->link(), ") {")) bodyStart = conditionStart->link()->next(); } if (!bodyStart || !conditionStart) return Result(Result::Type::BAILOUT); // Is expr changed in condition? if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); // Is expr changed in loop body? if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); } if (mWhat == What::ValueFlow && Token::simpleMatch(tok, "if (") && Token::simpleMatch(tok->linkAt(1), ") {")) { const Token *bodyStart = tok->linkAt(1)->next(); const Token *conditionStart = tok->next(); const Token *condTok = conditionStart->astOperand2(); if (condTok->hasKnownIntValue()) { bool cond = condTok->values().front().intvalue; if (cond) { FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); if (result.type != Result::Type::NONE) return result; } else if (Token::simpleMatch(bodyStart->link(), "} else {")) { bodyStart = bodyStart->link()->tokAt(2); FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); if (result.type != Result::Type::NONE) return result; } } tok = bodyStart->link(); if (isReturnScope(tok, &mLibrary)) return Result(Result::Type::BAILOUT); if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); if (!tok) return Result(Result::Type::BAILOUT); // Is expr changed in condition? if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); // Is expr changed in condition body? if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); } if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { // TODO: this is a quick bailout return Result(Result::Type::BAILOUT); } if (mWhat == What::Reassign && Token::simpleMatch(tok, ";") && Token::simpleMatch(tok->astParent(), ";") && Token::simpleMatch(tok->astParent()->astParent(), "(") && Token::simpleMatch(tok->astParent()->astParent()->previous(), "for (") && !isUnchanged(tok, tok->astParent()->astParent()->link(), exprVarIds, local)) // TODO: This is a quick bailout to avoid FP #9420, there are false negatives (TODO_ASSERT_EQUALS) return Result(Result::Type::BAILOUT); if (expr->isName() && Token::Match(tok, "%name% (") && tok->str().find("<") != std::string::npos && tok->str().find(expr->str()) != std::string::npos) return Result(Result::Type::BAILOUT); if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { const Token *parent = tok; bool other = false; bool same = tok->astParent() && isSameExpression(mCpp, false, expr, tok, mLibrary, true, false, nullptr); while (!same && Token::Match(parent->astParent(), "*|.|::|[|(|%cop%")) { parent = parent->astParent(); if (parent->str() == "(" && !parent->isCast()) break; if (isSameExpression(mCpp, false, expr, parent, mLibrary, true, false, nullptr)) { same = true; if (mWhat == What::ValueFlow) { KnownAndToken v; v.known = mValueFlowKnown; v.token = parent; mValueFlow.push_back(v); } } if (Token::Match(parent, ". %var%") && parent->next()->varId() && exprVarIds.find(parent->next()->varId()) == exprVarIds.end() && isSameExpression(mCpp, false, expr->astOperand1(), parent->astOperand1(), mLibrary, true, false, nullptr)) { other = true; break; } } if (mWhat != What::ValueFlow && same && Token::simpleMatch(parent->astParent(), "[") && parent == parent->astParent()->astOperand2()) { return Result(Result::Type::READ); } if (other) continue; if (Token::simpleMatch(parent->astParent(), "=") && parent == parent->astParent()->astOperand1()) { if (!local && hasFunctionCall(parent->astParent()->astOperand2())) { // TODO: this is a quick bailout return Result(Result::Type::BAILOUT); } if (hasOperand(parent->astParent()->astOperand2(), expr)) { if (mWhat == What::Reassign) return Result(Result::Type::READ); continue; } // ({ .. }) if (hasGccCompoundStatement(parent->astParent()->astOperand2())) return Result(Result::Type::BAILOUT); const bool reassign = isSameExpression(mCpp, false, expr, parent, mLibrary, false, false, nullptr); if (reassign) return Result(Result::Type::WRITE, parent->astParent()); return Result(Result::Type::READ); } else if (mWhat == What::Reassign && parent->valueType() && parent->valueType()->pointer && Token::Match(parent->astParent(), "%assign%") && parent == parent->astParent()->astOperand1()) { return Result(Result::Type::READ); } else if (Token::Match(parent->astParent(), "%assign%") && !parent->astParent()->astParent() && parent == parent->astParent()->astOperand1()) { if (mWhat == What::Reassign) return Result(Result::Type::BAILOUT, parent->astParent()); if (mWhat == What::UnusedValue && (!parent->valueType() || parent->valueType()->reference != Reference::None)) return Result(Result::Type::BAILOUT, parent->astParent()); continue; } else if (mWhat == What::UnusedValue && parent->isUnaryOp("&") && Token::Match(parent->astParent(), "[,(]")) { // Pass variable to function the writes it const Token *ftok = parent->astParent(); while (Token::simpleMatch(ftok, ",")) ftok = ftok->astParent(); if (ftok && Token::Match(ftok->previous(), "%name% (")) { const std::vector args = getArguments(ftok); int argnr = 0; while (argnr < args.size() && args[argnr] != parent) argnr++; if (argnr < args.size()) { const Library::Function* functionInfo = mLibrary.getFunction(ftok->astOperand1()); if (functionInfo) { const auto it = functionInfo->argumentChecks.find(argnr + 1); if (it != functionInfo->argumentChecks.end() && it->second.direction == Library::ArgumentChecks::Direction::DIR_OUT) continue; } } } return Result(Result::Type::BAILOUT, parent->astParent()); } else { // TODO: this is a quick bailout return Result(Result::Type::BAILOUT, parent->astParent()); } } if (Token::Match(tok, ")|do {")) { if (tok->str() == ")" && Token::simpleMatch(tok->link()->previous(), "switch (")) // TODO: parse switch return Result(Result::Type::BAILOUT); const Result &result1 = checkRecursive(expr, tok->tokAt(2), tok->linkAt(1), exprVarIds, local, inInnerClass, depth); if (result1.type == Result::Type::READ || result1.type == Result::Type::BAILOUT) return result1; if (mWhat == What::ValueFlow && result1.type == Result::Type::WRITE) mValueFlowKnown = false; if (mWhat == What::Reassign && result1.type == Result::Type::BREAK) { const Token *scopeEndToken = findNextTokenFromBreak(result1.token); if (scopeEndToken) { const Result &result2 = checkRecursive(expr, scopeEndToken->next(), endToken, exprVarIds, local, inInnerClass, depth); if (result2.type == Result::Type::BAILOUT) return result2; } } if (Token::simpleMatch(tok->linkAt(1), "} else {")) { const Token *elseStart = tok->linkAt(1)->tokAt(2); const Result &result2 = checkRecursive(expr, elseStart, elseStart->link(), exprVarIds, local, inInnerClass, depth); if (mWhat == What::ValueFlow && result2.type == Result::Type::WRITE) mValueFlowKnown = false; if (result2.type == Result::Type::READ || result2.type == Result::Type::BAILOUT) return result2; if (result1.type == Result::Type::WRITE && result2.type == Result::Type::WRITE) return result1; tok = elseStart->link(); } else { tok = tok->linkAt(1); } } } return Result(Result::Type::NONE); } static bool hasVolatileCastOrVar(const Token *expr) { bool ret = false; visitAstNodes(expr, [&ret](const Token *tok) { if (Token::simpleMatch(tok, "( volatile")) ret = true; else if (tok->variable() && tok->variable()->isVolatile()) ret = true; return ret ? ChildrenToVisit::none : ChildrenToVisit::op1_and_op2; }); return ret; } bool FwdAnalysis::isGlobalData(const Token *expr) const { return ::isGlobalData(expr, mCpp); } std::set FwdAnalysis::getExprVarIds(const Token* expr, bool* localOut, bool* unknownVarIdOut) const { // all variable ids in expr. std::set exprVarIds; bool local = true; bool unknownVarId = false; visitAstNodes(expr, [&](const Token *tok) { if (tok->str() == "[" && mWhat == What::UnusedValue) return ChildrenToVisit::op1; if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") { // unknown variable unknownVarId = true; return ChildrenToVisit::none; } if (tok->varId() > 0) { exprVarIds.insert(tok->varId()); if (!Token::simpleMatch(tok->previous(), ".")) { const Variable *var = tok->variable(); if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && !isGlobalData(var->nameToken()->next()->astOperand2())) return ChildrenToVisit::none; const bool deref = tok->astParent() && (tok->astParent()->isUnaryOp("*") || (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); local &= !nonLocal(tok->variable(), deref); } } return ChildrenToVisit::op1_and_op2; }); if (localOut) *localOut = local; if (unknownVarIdOut) *unknownVarIdOut = unknownVarId; return exprVarIds; } FwdAnalysis::Result FwdAnalysis::check(const Token* expr, const Token* startToken, const Token* endToken) { // all variable ids in expr. bool local = true; bool unknownVarId = false; std::set exprVarIds = getExprVarIds(expr, &local, &unknownVarId); if (unknownVarId) return Result(FwdAnalysis::Result::Type::BAILOUT); if (mWhat == What::Reassign && isGlobalData(expr)) local = false; // In unused values checking we do not want to check assignments to // global data. if (mWhat == What::UnusedValue && isGlobalData(expr)) return Result(FwdAnalysis::Result::Type::BAILOUT); Result result = checkRecursive(expr, startToken, endToken, exprVarIds, local, false); // Break => continue checking in outer scope while (mWhat!=What::ValueFlow && result.type == FwdAnalysis::Result::Type::BREAK) { const Token *scopeEndToken = findNextTokenFromBreak(result.token); if (!scopeEndToken) break; result = checkRecursive(expr, scopeEndToken->next(), endToken, exprVarIds, local, false); } return result; } bool FwdAnalysis::hasOperand(const Token *tok, const Token *lhs) const { if (!tok) return false; if (isSameExpression(mCpp, false, tok, lhs, mLibrary, false, false, nullptr)) return true; return hasOperand(tok->astOperand1(), lhs) || hasOperand(tok->astOperand2(), lhs); } const Token *FwdAnalysis::reassign(const Token *expr, const Token *startToken, const Token *endToken) { if (hasVolatileCastOrVar(expr)) return nullptr; mWhat = What::Reassign; Result result = check(expr, startToken, endToken); return result.type == FwdAnalysis::Result::Type::WRITE ? result.token : nullptr; } bool FwdAnalysis::unusedValue(const Token *expr, const Token *startToken, const Token *endToken) { if (isEscapedAlias(expr)) return false; if (hasVolatileCastOrVar(expr)) return false; mWhat = What::UnusedValue; Result result = check(expr, startToken, endToken); return (result.type == FwdAnalysis::Result::Type::NONE || result.type == FwdAnalysis::Result::Type::RETURN) && !possiblyAliased(expr, startToken); } bool FwdAnalysis::possiblyAliased(const Token *expr, const Token *startToken) const { if (expr->isUnaryOp("*")) return true; const bool macro = false; const bool pure = false; const bool followVar = false; for (const Token *tok = startToken; tok; tok = tok->previous()) { if (tok->str() == "{" && tok->scope()->type == Scope::eFunction && !(tok->astParent() && tok->astParent()->str() == ",")) break; if (Token::Match(tok, "%name% (") && !Token::Match(tok, "if|while|for")) { // Is argument passed by reference? const std::vector args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { if (!Token::Match(args[argnr], "%name%|.|::")) continue; if (tok->function() && tok->function()->getArgumentVar(argnr) && !tok->function()->getArgumentVar(argnr)->isReference() && !tok->function()->isConst()) continue; for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { if (isSameExpression(mCpp, macro, subexpr, args[argnr], mLibrary, pure, followVar)) return true; } } continue; } const Token *addrOf = nullptr; if (Token::Match(tok, "& %name% =")) addrOf = tok->tokAt(2)->astOperand2(); else if (tok->isUnaryOp("&")) addrOf = tok->astOperand1(); else if (Token::simpleMatch(tok, "std :: ref (")) addrOf = tok->tokAt(3)->astOperand2(); else continue; for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { if (isSameExpression(mCpp, macro, subexpr, addrOf, mLibrary, pure, followVar)) return true; } } return false; } bool FwdAnalysis::isEscapedAlias(const Token* expr) { for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { for (const ValueFlow::Value &val : subexpr->values()) { if (!val.isLocalLifetimeValue()) continue; const Variable* var = val.tokvalue->variable(); if (!var) continue; if (!var->isLocal()) return true; if (var->isArgument()) return true; } } return false; } bool isSizeOfEtc(const Token *tok) { return Token::Match(tok, "sizeof|typeof|offsetof|decltype|__typeof__ ("); } cppcheck-2.7/lib/astutils.h000066400000000000000000000354571417746362400157650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef astutilsH #define astutilsH //--------------------------------------------------------------------------- #include #include #include #include #include "errortypes.h" #include "utils.h" class Function; class Library; class Settings; class Token; class Variable; enum class ChildrenToVisit { none, op1, op2, op1_and_op2, done // found what we looked for, don't visit any more children }; /** * Visit AST nodes recursively. The order is not "well defined" */ void visitAstNodes(const Token *ast, std::function visitor); void visitAstNodes(Token *ast, std::function visitor); const Token* findAstNode(const Token* ast, const std::function& pred); const Token* findExpression(const nonneg int exprid, const Token* start, const Token* end, const std::function& pred); const Token* findExpression(const Token* start, const nonneg int exprid); std::vector astFlatten(const Token* tok, const char* op); std::vector astFlatten(Token* tok, const char* op); bool astHasToken(const Token* root, const Token * tok); bool astHasVar(const Token * tok, nonneg int varid); bool astIsPrimitive(const Token* tok); /** Is expression a 'signed char' if no promotion is used */ bool astIsSignedChar(const Token *tok); /** Is expression a 'char' if no promotion is used? */ bool astIsUnknownSignChar(const Token *tok); /** Is expression a char according to valueType? */ bool astIsGenericChar(const Token* tok); /** Is expression of integral type? */ bool astIsIntegral(const Token *tok, bool unknown); bool astIsUnsigned(const Token* tok); /** Is expression of floating point type? */ bool astIsFloat(const Token *tok, bool unknown); /** Is expression of boolean type? */ bool astIsBool(const Token *tok); bool astIsPointer(const Token *tok); bool astIsSmartPointer(const Token* tok); bool astIsUniqueSmartPointer(const Token* tok); bool astIsIterator(const Token *tok); bool astIsContainer(const Token *tok); bool astIsContainerView(const Token* tok); bool astIsContainerOwned(const Token* tok); /** * Get canonical type of expression. const/static/etc are not included and neither *&. * For example: * Expression type Return * std::string std::string * int * int * static const int int * std::vector std::vector */ std::string astCanonicalType(const Token *expr); /** Is given syntax tree a variable comparison against value */ const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr); bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknown = false); const Token* previousBeforeAstLeftmostLeaf(const Token* tok); Token* previousBeforeAstLeftmostLeaf(Token* tok); const Token * nextAfterAstRightmostLeaf(const Token * tok); Token* nextAfterAstRightmostLeaf(Token* tok); Token* astParentSkipParens(Token* tok); const Token* astParentSkipParens(const Token* tok); const Token* getParentMember(const Token * tok); const Token* getParentLifetime(const Token* tok); bool astIsLHS(const Token* tok); bool astIsRHS(const Token* tok); Token* getCondTok(Token* tok); const Token* getCondTok(const Token* tok); Token* getInitTok(Token* tok); const Token* getInitTok(const Token* tok); Token* getStepTok(Token* tok); const Token* getStepTok(const Token* tok); Token* getCondTokFromEnd(Token* endBlock); const Token* getCondTokFromEnd(const Token* endBlock); /// For a "break" token, locate the next token to execute. The token will /// be either a "}" or a ";". const Token *findNextTokenFromBreak(const Token *breakToken); /** * Extract for loop values: loopvar varid, init value, step value, last value (inclusive) */ bool extractForLoopValues(const Token *forToken, nonneg int * const varid, bool * const knownInitValue, long long * const initValue, bool * const partialCond, long long * const stepValue, long long * const lastValue); bool precedes(const Token * tok1, const Token * tok2); bool succeeds(const Token* tok1, const Token* tok2); bool exprDependsOnThis(const Token* expr, bool onVar = true, nonneg int depth = 0); struct ReferenceToken { const Token* token; ErrorPath errors; }; std::vector followAllReferences(const Token* tok, bool temporary = true, bool inconclusive = true, ErrorPath errors = ErrorPath{}, int depth = 20); const Token* followReferences(const Token* tok, ErrorPath* errors = nullptr); bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isEqualKnownValue(const Token * const tok1, const Token * const tok2); /** * Is token used a boolean, that is to say cast to a bool, or used as a condition in a if/while/for */ bool isUsedAsBool(const Token * const tok); /** * Are two conditions opposite * @param isNot do you want to know if cond1 is !cond2 or if cond1 and cond2 are non-overlapping. true: cond1==!cond2 false: cond1==true => cond2==false * @param cpp c++ file * @param cond1 condition1 * @param cond2 condition2 * @param library files data * @param pure boolean */ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isConstFunctionCall(const Token* ftok, const Library& library); bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp); bool isWithoutSideEffects(bool cpp, const Token* tok); bool isUniqueExpression(const Token* tok); bool isEscapeFunction(const Token* ftok, const Library* library); /** Is scope a return scope (scope will unconditionally return) */ bool isReturnScope(const Token* const endToken, const Library* library = nullptr, const Token** unknownFunc = nullptr, bool functionScope = false); /// Return the token to the function and the argument number const Token * getTokenArgumentFunction(const Token * tok, int& argn); Token* getTokenArgumentFunction(Token* tok, int& argn); std::vector getArgumentVars(const Token* tok, int argnr); /** Is variable changed by function call? * In case the answer of the question is inconclusive, e.g. because the function declaration is not known * the return value is false and the output parameter inconclusive is set to true * * @param tok ast tree * @param varid Variable Id * @param settings program settings * @param inconclusive pointer to output variable which indicates that the answer of the question is inconclusive */ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int varid, const Settings *settings, bool *inconclusive); /** Is variable changed by function call? * In case the answer of the question is inconclusive, e.g. because the function declaration is not known * the return value is false and the output parameter inconclusive is set to true * * @param tok token of variable in function call * @param settings program settings * @param inconclusive pointer to output variable which indicates that the answer of the question is inconclusive */ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive); /** Is variable changed in block of code? */ bool isVariableChanged(const Token *start, const Token *end, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth = 20); bool isVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth = 20); bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, bool cpp, int depth = 20); bool isVariableChanged(const Variable * var, const Settings *settings, bool cpp, int depth = 20); bool isVariablesChanged(const Token* start, const Token* end, int indirect, std::vector vars, const Settings* settings, bool cpp); bool isThisChanged(const Token* tok, int indirect, const Settings* settings, bool cpp); bool isThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings, bool cpp); const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth = 20); Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth = 20); bool isExpressionChanged(const Token* expr, const Token* start, const Token* end, const Settings* settings, bool cpp, int depth = 20); bool isExpressionChangedAt(const Token* expr, const Token* tok, int indirect, bool globalvar, const Settings* settings, bool cpp, int depth = 20); /// If token is an alias if another variable bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr); bool isAliased(const Variable *var); /** Determines the number of arguments - if token is a function call or macro * @param start token which is supposed to be the function/macro name. * \return Number of arguments */ int numberOfArguments(const Token *start); /** * Get arguments (AST) */ std::vector getArguments(const Token *ftok); int getArgumentPos(const Variable* var, const Function* f); /** * Are the arguments a pair of iterators/pointers? */ bool isIteratorPair(std::vector args); const Token *findLambdaStartToken(const Token *last); /** * find lambda function end token * \param first The [ token * \return nullptr or the } */ const Token *findLambdaEndToken(const Token *first); Token* findLambdaEndToken(Token* first); bool isLikelyStream(bool cpp, const Token *stream); /** * do we see a likely write of rhs through overloaded operator * s >> x; * a & x; */ bool isLikelyStreamRead(bool cpp, const Token *op); bool isCPPCast(const Token* tok); bool isConstVarExpression(const Token *tok, const char * skipMatch = nullptr); const Variable *getLHSVariable(const Token *tok); const Token* getLHSVariableToken(const Token* tok); std::vector getLHSVariables(const Token* tok); /** Find a allocation function call in expression, so result of expression is allocated memory/resource. */ const Token* findAllocFuncCallToken(const Token *expr, const Library &library); bool isScopeBracket(const Token* tok); bool isNullOperand(const Token *expr); bool isGlobalData(const Token *expr, bool cpp); /** * Forward data flow analysis for checks * - unused value * - redundant assignment * - valueflow analysis */ class FwdAnalysis { public: FwdAnalysis(bool cpp, const Library &library) : mCpp(cpp), mLibrary(library), mWhat(What::Reassign), mValueFlowKnown(true) {} bool hasOperand(const Token *tok, const Token *lhs) const; /** * Check if "expr" is reassigned. The "expr" can be a tree (x.y[12]). * @param expr Symbolic expression to perform forward analysis for * @param startToken First token in forward analysis * @param endToken Last token in forward analysis * @return Token where expr is reassigned. If it's not reassigned then nullptr is returned. */ const Token *reassign(const Token *expr, const Token *startToken, const Token *endToken); /** * Check if "expr" is used. The "expr" can be a tree (x.y[12]). * @param expr Symbolic expression to perform forward analysis for * @param startToken First token in forward analysis * @param endToken Last token in forward analysis * @return true if expr is used. */ bool unusedValue(const Token *expr, const Token *startToken, const Token *endToken); struct KnownAndToken { bool known; const Token *token; }; /** Is there some possible alias for given expression */ bool possiblyAliased(const Token *expr, const Token *startToken) const; std::set getExprVarIds(const Token* expr, bool* localOut = nullptr, bool* unknownVarIdOut = nullptr) const; private: static bool isEscapedAlias(const Token* expr); /** Result of forward analysis */ struct Result { enum class Type { NONE, READ, WRITE, BREAK, RETURN, BAILOUT } type; explicit Result(Type type) : type(type), token(nullptr) {} Result(Type type, const Token *token) : type(type), token(token) {} const Token *token; }; struct Result check(const Token *expr, const Token *startToken, const Token *endToken); struct Result checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local, bool inInnerClass, int depth=0); // Is expression a l-value global data? bool isGlobalData(const Token *expr) const; const bool mCpp; const Library &mLibrary; enum class What { Reassign, UnusedValue, ValueFlow } mWhat; std::vector mValueFlow; bool mValueFlowKnown; }; bool isSizeOfEtc(const Token *tok); #endif // astutilsH cppcheck-2.7/lib/bughuntingchecks.cpp000066400000000000000000000730001417746362400177650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bughuntingchecks.h" #include "astutils.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include static const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Buffer static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer static float getKnownFloatValue(const Token *tok, float def) { for (const auto &value: tok->values()) { if (value.isKnown() && value.valueType == ValueFlow::Value::ValueType::FLOAT) return value.floatValue; } return def; } static bool isLessThan(ExprEngine::DataBase *dataBase, ExprEngine::ValuePtr lhs, ExprEngine::ValuePtr rhs) { return ExprEngine::BinOpResult("<", lhs, rhs).isTrue(dataBase); } static void arrayIndex(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!Token::simpleMatch(tok->astParent(), "[")) return; int nr = 0; const Token *buf = tok->astParent()->astOperand1(); while (Token::simpleMatch(buf, "[")) { ++nr; buf = buf->astOperand1(); } if (!buf || !buf->variable() || !buf->variable()->isArray() || buf == buf->variable()->nameToken()) // TODO return; const Token *index = tok->astParent()->astOperand2(); if (tok != index) // TODO return; if (buf->variable()->dimensions().size() > nr && buf->variable()->dimensions()[nr].known) { const MathLib::bigint bufSize = buf->variable()->dimensions()[nr].num; if (value.isGreaterThan(dataBase, bufSize - 1)) { const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue); dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingArrayIndexOutOfBounds", "Array index out of bounds, cannot determine that " + index->expressionString() + " is less than " + std::to_string(bufSize), CWE_BUFFER_OVERRUN, false, bailout); } } bool isUnsigned = tok->valueType() && tok->valueType()->sign == ::ValueType::Sign::UNSIGNED; if (!isUnsigned && value.isLessThan(dataBase, 0)) { const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue); dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingArrayIndexNegative", "Array index out of bounds, cannot determine that " + index->expressionString() + " is not negative", CWE_BUFFER_UNDERRUN, false, bailout); } } static void bufferOverflow(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (value.type != ExprEngine::ValueType::FunctionCallArgumentValues) return; if (!Token::simpleMatch(tok, "(") || !Token::Match(tok->previous(), "%name% (")) return; const Library::Function *func = dataBase->settings->library.getFunction(tok->previous()); if (!func) return; const ExprEngine::FunctionCallArgumentValues *functionCallArguments = dynamic_cast(&value); if (!functionCallArguments) return; const std::vector arguments = getArguments(tok); if (functionCallArguments->argValues.size() != arguments.size()) // TODO investigate what to do return; int overflowArgument = 0; bool bailout = false; for (auto argNrChecks: func->argumentChecks) { const int argnr = argNrChecks.first; const Library::ArgumentChecks &checks = argNrChecks.second; if (argnr <= 0 || argnr > arguments.size() || checks.minsizes.empty()) continue; ExprEngine::ValuePtr argValue = functionCallArguments->argValues[argnr - 1]; if (!argValue || argValue->type == ExprEngine::ValueType::BailoutValue) { overflowArgument = argnr; bailout = true; break; } std::shared_ptr arrayValue = std::dynamic_pointer_cast(argValue); if (!arrayValue || arrayValue->size.size() != 1) { // TODO: implement this properly. overflowArgument = argnr; bailout = true; break; } for (const Library::ArgumentChecks::MinSize &minsize: checks.minsizes) { if (minsize.type == Library::ArgumentChecks::MinSize::Type::ARGVALUE && minsize.arg > 0 && minsize.arg <= arguments.size()) { ExprEngine::ValuePtr otherValue = functionCallArguments->argValues[minsize.arg - 1]; if (!otherValue || otherValue->type == ExprEngine::ValueType::BailoutValue) { overflowArgument = argnr; bailout = true; break; } if (isLessThan(dataBase, arrayValue->size[0], otherValue)) { overflowArgument = argnr; break; } } else if (minsize.type == Library::ArgumentChecks::MinSize::Type::STRLEN && minsize.arg > 0 && minsize.arg <= arguments.size()) { if (func->formatstr) { // TODO: implement this properly. check if minsize refers to a format string and check max length of that.. overflowArgument = argnr; bailout = true; break; } if (Token::Match(arguments[minsize.arg - 1], "%str%")) { const Token * const str = arguments[minsize.arg - 1]; if (arrayValue->size[0]->isLessThan(dataBase, Token::getStrLength(str))) { overflowArgument = argnr; break; } } else { ExprEngine::ValuePtr otherValue = functionCallArguments->argValues[minsize.arg - 1]; if (!otherValue || otherValue->type == ExprEngine::ValueType::BailoutValue) { overflowArgument = argnr; bailout = true; break; } std::shared_ptr arrayValue2 = std::dynamic_pointer_cast(otherValue); if (!arrayValue2 || arrayValue2->size.size() != 1) { overflowArgument = argnr; bailout = true; break; } if (isLessThan(dataBase, arrayValue->size[0], arrayValue2->size[0])) { overflowArgument = argnr; break; } } } } if (overflowArgument > 0) break; } if (overflowArgument == 0) return; dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingBufferOverflow", "Buffer read/write, when calling '" + tok->previous()->str() + "' it cannot be determined that " + std::to_string(overflowArgument) + getOrdinalText(overflowArgument) + " argument is not overflowed", CWE(120), false, bailout); } static void divByZero(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!tok->astParent() || !std::strchr("/%", tok->astParent()->str()[0])) return; if (tok->hasKnownIntValue() && tok->getKnownIntValue() != 0) return; if (tok->isImpossibleIntValue(0)) return; if (value.isUninit(dataBase) && value.type != ExprEngine::ValueType::BailoutValue) return; float f = getKnownFloatValue(tok, 0.0f); if (f > 0.0f || f < 0.0f) return; if (value.type == ExprEngine::ValueType::BailoutValue) { if (Token::simpleMatch(tok->previous(), "sizeof (")) return; } if (tok->astParent()->astOperand2() == tok && value.isEqual(dataBase, 0)) { const char * const id = (tok->valueType() && tok->valueType()->isFloat()) ? "bughuntingDivByZeroFloat" : "bughuntingDivByZero"; const bool bailout = (value.type == ExprEngine::ValueType::BailoutValue); dataBase->reportError(dataBase->settings->clang ? tok : tok->astParent(), Severity::SeverityType::error, id, "There is division, cannot determine that there can't be a division by zero.", CWE(369), false, bailout); } } #ifdef BUG_HUNTING_INTEGEROVERFLOW static void integerOverflow(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!tok->isArithmeticalOp() || !tok->valueType() || !tok->valueType()->isIntegral() || tok->valueType()->pointer > 0) return; const ExprEngine::BinOpResult *b = dynamic_cast(&value); if (!b) return; int bits = getIntBitsFromValueType(tok->valueType(), *dataBase->settings); if (bits == 0 || bits >= 60) return; std::string errorMessage; if (tok->valueType()->sign == ::ValueType::Sign::SIGNED) { MathLib::bigint v = 1LL << (bits - 1); if (b->isGreaterThan(dataBase, v-1)) errorMessage = "greater than " + std::to_string(v - 1); if (b->isLessThan(dataBase, -v)) { if (!errorMessage.empty()) errorMessage += " or "; errorMessage += "less than " + std::to_string(-v); } } else { MathLib::bigint maxValue = (1LL << bits) - 1; if (b->isGreaterThan(dataBase, maxValue)) errorMessage = "greater than " + std::to_string(maxValue); if (b->isLessThan(dataBase, 0)) { if (!errorMessage.empty()) errorMessage += " or "; errorMessage += "less than 0"; } } if (errorMessage.empty()) return; errorMessage = "There is integer arithmetic, cannot determine that there can't be overflow (if result is " + errorMessage + ")."; if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED) errorMessage += " Note that unsigned integer overflow is defined and will wrap around."; dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingIntegerOverflow", errorMessage, false, value.type == ExprEngine::ValueType::BailoutValue); } #endif /** check if variable is unconditionally assigned */ static bool isVariableAssigned(const Variable *var, const Token *tok, const Token *scopeStart=nullptr) { const Token * const start = scopeStart && precedes(var->nameToken(), scopeStart) ? scopeStart : var->nameToken(); for (const Token *prev = tok->previous(); prev; prev = prev->previous()) { if (!precedes(start, prev)) break; if (prev->str() == "}") { if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {")) { const Token *elseEnd = prev; const Token *elseStart = prev->link(); const Token *ifEnd = elseStart->tokAt(-2); const Token *ifStart = ifEnd->link(); if (isVariableAssigned(var, ifEnd, ifStart) && isVariableAssigned(var, elseEnd, elseStart)) { return true; } } prev = prev->link(); } if (scopeStart && Token::Match(prev, "return|throw|continue|break")) return true; if (Token::Match(prev, "%varid% =", var->declarationId())) { bool usedInRhs = false; visitAstNodes(prev->next()->astOperand2(), [&usedInRhs, var](const Token *tok) { if (tok->varId() == var->declarationId()) { usedInRhs = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (!usedInRhs) return true; } } return false; } static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!tok->astParent()) return; std::string uninitStructMember; if (const auto* structValue = dynamic_cast(&value)) { uninitStructMember = structValue->getUninitStructMember(dataBase); // uninitialized struct member => is there data copy of struct.. if (!uninitStructMember.empty()) { if (!Token::Match(tok->astParent(), "[=,(]")) return; } } bool uninitData = false; if (!value.isUninit(dataBase) && uninitStructMember.empty()) { if (Token::Match(tok->astParent(), "[(,]")) { if (const auto* arrayValue = dynamic_cast(&value)) { uninitData = arrayValue->data.size() >= 1 && arrayValue->data[0].value->isUninit(dataBase); } } if (!uninitData) return; } // container is not uninitialized if (tok->valueType() && tok->valueType()->pointer==0 && tok->valueType()->container) return; // container element is not uninitialized if (tok->str() == "[" && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->pointer==0 && tok->astOperand1()->valueType()->container) { if (tok->astOperand1()->valueType()->container->stdStringLike) return; bool pointerType = false; for (const Token *typeTok = tok->astOperand1()->valueType()->containerTypeToken; Token::Match(typeTok, "%name%|*|::|<"); typeTok = typeTok->next()) { if (typeTok->str() == "<" && typeTok->link()) typeTok = typeTok->link(); if (typeTok->str() == "*") pointerType = true; } if (!pointerType) return; } // variable that is not uninitialized.. if (tok->variable() && !tok->variable()->isPointer() && !tok->variable()->isReference()) { // smart pointer is not uninitialized if (tok->variable()->isSmartPointer()) return; // struct if (tok->variable()->type() && tok->variable()->type()->needInitialization == Type::NeedInitialization::False) return; // template variable is not uninitialized if (Token::findmatch(tok->variable()->typeStartToken(), "%name% <", tok->variable()->typeEndToken())) return; } // lhs in assignment if (tok->astParent()->str() == "=" && tok == tok->astParent()->astOperand1()) return; // Avoid FP when there is bailout.. if (value.type == ExprEngine::ValueType::BailoutValue) { if (tok->hasKnownValue()) return; if (!tok->variable()) // FIXME return; // lhs for scope operator if (Token::Match(tok, "%name% ::")) return; if (tok->astParent()->str() == "::" && tok == tok->astParent()->astOperand1()) return; // Object allocated on the stack if (Token::Match(tok, "%var% .") && tok->next()->originalName() != "->") return; // Assume that stream object is initialized if (Token::Match(tok->previous(), "[;{}] %var% <<|>>") && !tok->next()->astParent()) return; // Containers are not uninitialized std::vector tokens{tok, tok->astOperand1(), tok->astOperand2()}; if (Token::Match(tok->previous(), ". %name%")) tokens.push_back(tok->previous()->astOperand1()); for (const Token *t: tokens) { if (t && t->valueType() && t->valueType()->pointer == 0 && t->valueType()->container) return; } const Variable *var = tok->variable(); if (var && var->nameToken() == tok) return; if (var && !var->isLocal()) return; // FIXME if (var && !var->isPointer()) { if (!var->isLocal() || var->isStatic()) return; } if (var && (Token::Match(var->nameToken(), "%name% [=:({)]") || var->isInit())) return; if (var && var->nameToken() == tok) return; // Are there unconditional assignment? if (var && Token::Match(var->nameToken(), "%varid% ;| %varid%| =", tok->varId())) return; // Arrays are allocated on the stack if (var && Token::Match(tok, "%var% [") && var->isArray()) return; if (tok->variable() && isVariableAssigned(tok->variable(), tok)) return; } // Uninitialized function argument bool inconclusive = false; if (Token::Match(tok->astParent(), "[,(]")) { const Token *parent = tok->astParent(); int count = 0; if (Token::simpleMatch(parent, ",")) { if (tok == parent->astOperand2()) count = 1; parent = parent->astParent(); while (Token::simpleMatch(parent, ",")) { count++; parent = parent->astParent(); } } if (Token::simpleMatch(parent, "(") && parent->astOperand1() != tok) { if (parent->astOperand1()->function()) { const Variable *argvar = parent->astOperand1()->function()->getArgumentVar(count); if (argvar && argvar->isReference() && !argvar->isConst()) return; if (uninitData && argvar && !argvar->isConst()) { if (parent->astOperand1()->function()->hasBody()) return; inconclusive = true; } if (!uninitStructMember.empty() && dataBase->isC() && argvar && !argvar->isConst()) { if (parent->astOperand1()->function()->hasBody()) return; inconclusive = true; } } else if (uninitData) { if (dataBase->settings->library.getFunction(parent->astOperand1())) return; if (parent->astOperand1()->isKeyword()) return; } } else if (uninitData) return; } if (inconclusive && !dataBase->settings->certainty.isEnabled(Certainty::inconclusive)) return; // Avoid FP for array declaration const Token *parent = tok->astParent(); while (parent && parent->str() == "[") parent = parent->astParent(); if (!parent) return; const std::string inconclusiveMessage(inconclusive ? ". It is inconclusive if there would be a problem in the function call." : ""); if (!uninitStructMember.empty()) { const std::string symbol = tok->expressionString() + "." + uninitStructMember; dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingUninitStructMember", "$symbol:" + symbol + "\nCannot determine that '$symbol' is initialized" + inconclusiveMessage, CWE_USE_OF_UNINITIALIZED_VARIABLE, inconclusive, value.type == ExprEngine::ValueType::BailoutValue); return; } std::string uninitexpr = tok->expressionString(); if (uninitData) uninitexpr += "[0]"; const std::string symbol = (tok->varId() > 0) ? ("$symbol:" + tok->str() + "\n") : std::string(); std::string constMessage; std::string errorId = "bughuntingUninit"; { const Token *vartok = tok; while (Token::Match(vartok, ".|[")) vartok = vartok->astOperand1(); const Variable *var = vartok ? vartok->variable() : nullptr; if (var && var->isArgument()) { errorId += "NonConstArg"; constMessage = " (you can use 'const' to say data must be initialized)"; } } dataBase->reportError(tok, Severity::SeverityType::error, errorId.c_str(), symbol + "Cannot determine that '" + uninitexpr + "' is initialized" + constMessage + inconclusiveMessage, CWE_USE_OF_UNINITIALIZED_VARIABLE, inconclusive, value.type == ExprEngine::ValueType::BailoutValue); } static void checkFunctionCall(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!Token::Match(tok->astParent(), "[(,]")) return; const Token *parent = tok->astParent(); while (Token::simpleMatch(parent, ",")) parent = parent->astParent(); if (!parent || parent->str() != "(") return; int num = 0; for (const Token *argTok: getArguments(parent->astOperand1())) { --num; if (argTok == tok) { num = -num; break; } } if (num <= 0) return; if (parent->astOperand1()->function()) { const Variable *arg = parent->astOperand1()->function()->getArgumentVar(num - 1); if (arg && arg->nameToken()) { std::string bad; MathLib::bigint low; if (arg->nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low)) { if (!(tok->hasKnownIntValue() && tok->getKnownIntValue() >= low) && value.isLessThan(dataBase, low)) bad = "__cppcheck_low__(" + std::to_string(low) + ")"; } MathLib::bigint high; if (arg->nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high)) { if (!(tok->hasKnownIntValue() && tok->getKnownIntValue() <= high) && value.isGreaterThan(dataBase, high)) bad = "__cppcheck_high__(" + std::to_string(high) + ")"; } if (!bad.empty()) { dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingInvalidArgValue", "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument value meets the attribute " + bad, CWE(0), false); return; } } } // Check invalid function argument values.. for (const Library::InvalidArgValue &invalidArgValue : Library::getInvalidArgValues(dataBase->settings->library.validarg(parent->astOperand1(), num))) { bool err = false; std::string bad; switch (invalidArgValue.type) { case Library::InvalidArgValue::Type::eq: if (!tok->hasKnownIntValue() || tok->getKnownIntValue() == MathLib::toLongNumber(invalidArgValue.op1)) err = value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op1)); bad = "equals " + invalidArgValue.op1; break; case Library::InvalidArgValue::Type::le: if (!tok->hasKnownIntValue() || tok->getKnownIntValue() <= MathLib::toLongNumber(invalidArgValue.op1)) err = value.isLessThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1) + 1); bad = "less equal " + invalidArgValue.op1; break; case Library::InvalidArgValue::Type::lt: if (!tok->hasKnownIntValue() || tok->getKnownIntValue() < MathLib::toLongNumber(invalidArgValue.op1)) err = value.isLessThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1)); bad = "less than " + invalidArgValue.op1; break; case Library::InvalidArgValue::Type::ge: if (!tok->hasKnownIntValue() || tok->getKnownIntValue() >= MathLib::toLongNumber(invalidArgValue.op1)) err = value.isGreaterThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1) - 1); bad = "greater equal " + invalidArgValue.op1; break; case Library::InvalidArgValue::Type::gt: if (!tok->hasKnownIntValue() || tok->getKnownIntValue() > MathLib::toLongNumber(invalidArgValue.op1)) err = value.isGreaterThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1)); bad = "greater than " + invalidArgValue.op1; break; case Library::InvalidArgValue::Type::range: // TODO err = value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op1)); err |= value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op2)); bad = "range " + invalidArgValue.op1 + "-" + invalidArgValue.op2; break; } if (err) { dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingInvalidArgValue", "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument value is valid. Bad value: " + bad, CWE(0), false); break; } } // Uninitialized function argument.. if (dataBase->settings->library.isuninitargbad(parent->astOperand1(), num) && dataBase->settings->library.isnullargbad(parent->astOperand1(), num) && value.type == ExprEngine::ValueType::ArrayValue) { const ExprEngine::ArrayValue &arrayValue = static_cast(value); auto index0 = std::make_shared("0", 0, 0); for (const auto &v: arrayValue.read(index0)) { if (v.second->isUninit(dataBase)) { dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingUninitArg", "There is function call, cannot determine that " + std::to_string(num) + getOrdinalText(num) + " argument is initialized.", CWE_USE_OF_UNINITIALIZED_VARIABLE, false); break; } } } } static void checkAssignment(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!Token::simpleMatch(tok->astParent(), "=")) return; const Token *lhs = tok->astParent()->astOperand1(); while (Token::simpleMatch(lhs, ".")) lhs = lhs->astOperand2(); if (!lhs || !lhs->variable() || !lhs->variable()->nameToken()) return; const Token *vartok = lhs->variable()->nameToken(); bool executable = false; std::string fullName = lhs->variable()->name(); for (const Scope *s = lhs->variable()->nameToken()->scope(); s->type != Scope::ScopeType::eGlobal; s = s->nestedIn) { if (s->isExecutable()) { executable = true; break; } fullName = s->className + "::" + fullName; } auto getMinMaxValue = [=](TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *val) { if (vartok->getCppcheckAttribute(type, val)) return true; if (!executable) { const auto it = dataBase->settings->variableContracts.find(fullName); if (it != dataBase->settings->variableContracts.end()) { const std::string *str; if (type == TokenImpl::CppcheckAttributes::Type::LOW) str = &it->second.minValue; else if (type == TokenImpl::CppcheckAttributes::Type::HIGH) str = &it->second.maxValue; else return false; *val = MathLib::toLongNumber(*str); return !str->empty(); } } return false; }; MathLib::bigint low; if (getMinMaxValue(TokenImpl::CppcheckAttributes::Type::LOW, &low)) { if (value.isLessThan(dataBase, low)) dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingAssign", "There is assignment, cannot determine that value is greater or equal with " + std::to_string(low), CWE_INCORRECT_CALCULATION, false); } MathLib::bigint high; if (getMinMaxValue(TokenImpl::CppcheckAttributes::Type::HIGH, &high)) { if (value.isGreaterThan(dataBase, high)) dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingAssign", "There is assignment, cannot determine that value is lower or equal with " + std::to_string(high), CWE_INCORRECT_CALCULATION, false); } } void addBughuntingChecks(std::vector *callbacks) { callbacks->push_back(arrayIndex); callbacks->push_back(bufferOverflow); callbacks->push_back(divByZero); callbacks->push_back(checkFunctionCall); callbacks->push_back(checkAssignment); #ifdef BUG_HUNTING_INTEGEROVERFLOW callbacks->push_back(integerOverflow); #endif callbacks->push_back(uninit); } cppcheck-2.7/lib/bughuntingchecks.h000066400000000000000000000022371417746362400174360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef bughuntingchecksH #define bughuntingchecksH //--------------------------------------------------------------------------- #include "exprengine.h" #include void addBughuntingChecks(std::vector *callbacks); //--------------------------------------------------------------------------- #endif // bughuntingchecksH cppcheck-2.7/lib/calculate.h000066400000000000000000000064611417746362400160430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef calculateH #define calculateH #include "mathlib.h" #include "errortypes.h" #include template bool isEqual(T x, T y) { return x == y; } inline bool isEqual(double x, double y) { const double diff = (x > y) ? x - y : y - x; return !((diff / 2) < diff); } inline bool isEqual(float x, float y) { return isEqual(double{x}, double{y}); } template bool isZero(T x) { return isEqual(x, T(0)); } template R calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr) { auto wrap = [](T z) { return R{z}; }; const MathLib::bigint maxBitsShift = sizeof(MathLib::bigint) * 8; // For portability we cannot shift signed integers by 63 bits const MathLib::bigint maxBitsSignedShift = maxBitsShift - 1; switch (MathLib::encodeMultiChar(s)) { case '+': return wrap(x + y); case '-': return wrap(x - y); case '*': return wrap(x * y); case '/': if (isZero(y)) { if (error) *error = true; return R{}; } return wrap(x / y); case '%': if (isZero(y)) { if (error) *error = true; return R{}; } return wrap(MathLib::bigint(x) % MathLib::bigint(y)); case '&': return wrap(MathLib::bigint(x) & MathLib::bigint(y)); case '|': return wrap(MathLib::bigint(x) | MathLib::bigint(y)); case '^': return wrap(MathLib::bigint(x) ^ MathLib::bigint(y)); case '>': return wrap(x > y); case '<': return wrap(x < y); case '<<': if (y >= maxBitsSignedShift || y < 0 || x < 0) { if (error) *error = true; return R{}; } return wrap(MathLib::bigint(x) << MathLib::bigint(y)); case '>>': if (y >= maxBitsSignedShift || y < 0 || x < 0) { if (error) *error = true; return R{}; } return wrap(MathLib::bigint(x) >> MathLib::bigint(y)); case '&&': return wrap(!isZero(x) && !isZero(y)); case '||': return wrap(!isZero(x) || !isZero(y)); case '==': return wrap(isEqual(x, y)); case '!=': return wrap(!isEqual(x, y)); case '>=': return wrap(x >= y); case '<=': return wrap(x <= y); } throw InternalError(nullptr, "Unknown operator: " + s); } template T calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr) { return calculate(s, x, y, error); } #endif cppcheck-2.7/lib/check.cpp000066400000000000000000000077601417746362400155210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "check.h" #include "errorlogger.h" #include "settings.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include //--------------------------------------------------------------------------- Check::Check(const std::string &aname) : mTokenizer(nullptr), mSettings(nullptr), mErrorLogger(nullptr), mName(aname) { for (std::list::iterator i = instances().begin(); i != instances().end(); ++i) { if ((*i)->name() > aname) { instances().insert(i, this); return; } } instances().push_back(this); } void Check::reportError(const ErrorMessage &errmsg) { std::cout << errmsg.toXML() << std::endl; } void Check::reportError(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty) { const ErrorMessage errmsg(callstack, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty, mSettings ? mSettings->bugHunting : false); if (mErrorLogger) mErrorLogger->reportErr(errmsg); else reportError(errmsg); } void Check::reportError(const ErrorPath &errorPath, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty) { const ErrorMessage errmsg(errorPath, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty, mSettings ? mSettings->bugHunting : false); if (mErrorLogger) mErrorLogger->reportErr(errmsg); else reportError(errmsg); } bool Check::wrongData(const Token *tok, const char *str) { if (mSettings->daca) reportError(tok, Severity::debug, "DacaWrongData", "Wrong data detected by condition " + std::string(str)); return true; } std::list &Check::instances() { #ifdef __SVR4 // Under Solaris, destructors are called in wrong order which causes a segmentation fault. // This fix ensures pointer remains valid and reachable until program terminates. static std::list *_instances= new std::list; return *_instances; #else static std::list _instances; return _instances; #endif } std::string Check::getMessageId(const ValueFlow::Value &value, const char id[]) { if (value.condition != nullptr) return id + std::string("Cond"); if (value.safe) return std::string("safe") + (char)std::toupper(id[0]) + (id + 1); return id; } ErrorPath Check::getErrorPath(const Token* errtok, const ValueFlow::Value* value, const std::string& bug) const { ErrorPath errorPath; if (!value) { errorPath.emplace_back(errtok, bug); } else if (mSettings->verbose || mSettings->xml || !mSettings->templateLocation.empty()) { errorPath = value->errorPath; errorPath.emplace_back(errtok, bug); } else { if (value->condition) errorPath.emplace_back(value->condition, "condition '" + value->condition->expressionString() + "'"); //else if (!value->isKnown() || value->defaultArg) // errorPath = value->callstack; errorPath.emplace_back(errtok, bug); } return errorPath; } cppcheck-2.7/lib/check.h000066400000000000000000000134401417746362400151560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkH #define checkH //--------------------------------------------------------------------------- #include "config.h" #include "errortypes.h" #include #include namespace tinyxml2 { class XMLElement; } namespace CTU { class FileInfo; } namespace ValueFlow { class Value; } class Settings; class Token; class ErrorLogger; class ErrorMessage; class Tokenizer; /** Use WRONG_DATA in checkers to mark conditions that check that data is correct */ #define WRONG_DATA(COND, TOK) ((COND) && wrongData((TOK), #COND)) /// @addtogroup Core /// @{ /** * @brief Interface class that cppcheck uses to communicate with the checks. * All checking classes must inherit from this class */ class CPPCHECKLIB Check { public: /** This constructor is used when registering the CheckClass */ explicit Check(const std::string &aname); /** This constructor is used when running checks. */ Check(const std::string &aname, const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger), mName(aname) {} virtual ~Check() { if (!mTokenizer) instances().remove(this); } /** List of registered check classes. This is used by Cppcheck to run checks and generate documentation */ static std::list &instances(); /** run checks, the token list is not simplified */ virtual void runChecks(const Tokenizer *, const Settings *, ErrorLogger *) = 0; /** get error messages */ virtual void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const = 0; /** class name, used to generate documentation */ const std::string& name() const { return mName; } /** get information about this class, used to generate documentation */ virtual std::string classInfo() const = 0; /** * Write given error to errorlogger or to out stream in xml format. * This is for for printout out the error list with --errorlist * @param errmsg Error message to write */ static void reportError(const ErrorMessage &errmsg); /** Base class used for whole-program analysis */ class CPPCHECKLIB FileInfo { public: FileInfo() {} virtual ~FileInfo() {} virtual std::string toString() const { return std::string(); } }; virtual FileInfo * getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { (void)tokenizer; (void)settings; return nullptr; } virtual FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { (void)xmlElement; return nullptr; } // Return true if an error is reported. virtual bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& /*settings*/, ErrorLogger & /*errorLogger*/) { (void)ctu; (void)fileInfo; //(void)settings; //(void)errorLogger; return false; } static std::string getMessageId(const ValueFlow::Value &value, const char id[]); protected: const Tokenizer * const mTokenizer; const Settings * const mSettings; ErrorLogger * const mErrorLogger; /** report an error */ void reportError(const Token *tok, const Severity::SeverityType severity, const std::string &id, const std::string &msg) { reportError(tok, severity, id, msg, CWE(0U), Certainty::normal); } /** report an error */ void reportError(const Token *tok, const Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty) { const std::list callstack(1, tok); reportError(callstack, severity, id, msg, cwe, certainty); } /** report an error */ void reportError(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg) { reportError(callstack, severity, id, msg, CWE(0U), Certainty::normal); } /** report an error */ void reportError(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty); void reportError(const ErrorPath &errorPath, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty); ErrorPath getErrorPath(const Token* errtok, const ValueFlow::Value* value, const std::string& bug) const; /** * Use WRONG_DATA in checkers when you check for wrong data. That * will call this method */ bool wrongData(const Token *tok, const char *str); /** disabled assignment operator and copy constructor */ void operator=(const Check &) = delete; Check(const Check &) = delete; private: const std::string mName; }; /// @} //--------------------------------------------------------------------------- #endif // checkH cppcheck-2.7/lib/check64bit.cpp000066400000000000000000000162731417746362400163710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // 64-bit portability //--------------------------------------------------------------------------- #include "check64bit.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include //--------------------------------------------------------------------------- // CWE ids used static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior // Register this check class (by creating a static instance of it) namespace { Check64BitPortability instance; } void Check64BitPortability::pointerassignment() { if (!mSettings->severity.isEnabled(Severity::portability)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Check return values for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body continue; bool retPointer = false; if (scope->function->token->strAt(-1) == "*") // Function returns a pointer retPointer = true; else if (Token::Match(scope->function->token->previous(), "int|long|DWORD")) // Function returns an integer ; else continue; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // skip nested functions if (tok->str() == "{") { if (tok->scope()->type == Scope::ScopeType::eFunction || tok->scope()->type == Scope::ScopeType::eLambda) tok = tok->link(); } if (tok->str() != "return") continue; if (!tok->astOperand1() || tok->astOperand1()->isNumber()) continue; const ValueType * const returnType = tok->astOperand1()->valueType(); if (!returnType) continue; if (retPointer && !returnType->typeScope && returnType->pointer == 0U) returnIntegerError(tok); if (!retPointer && returnType->pointer >= 1U) returnPointerError(tok); } } // Check assignments for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() != "=") continue; const ValueType *lhstype = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *rhstype = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; if (!lhstype || !rhstype) continue; // Assign integer to pointer.. if (lhstype->pointer >= 1U && !tok->astOperand2()->isNumber() && rhstype->pointer == 0U && rhstype->originalTypeName.empty() && rhstype->type == ValueType::Type::INT) assignmentIntegerToAddressError(tok); // Assign pointer to integer.. if (rhstype->pointer >= 1U && lhstype->pointer == 0U && lhstype->originalTypeName.empty() && lhstype->isIntegral() && lhstype->type >= ValueType::Type::CHAR && lhstype->type <= ValueType::Type::INT) assignmentAddressToIntegerError(tok); } } } void Check64BitPortability::assignmentAddressToIntegerError(const Token *tok) { reportError(tok, Severity::portability, "AssignmentAddressToInteger", "Assigning a pointer to an integer is not portable.\n" "Assigning a pointer to an integer (int/long/etc) is not portable across different platforms and " "compilers. For example in 32-bit Windows and linux they are same width, but in 64-bit Windows and linux " "they are of different width. In worst case you end up assigning 64-bit address to 32-bit integer. The safe " "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, Certainty::normal); } void Check64BitPortability::assignmentIntegerToAddressError(const Token *tok) { reportError(tok, Severity::portability, "AssignmentIntegerToAddress", "Assigning an integer to a pointer is not portable.\n" "Assigning an integer (int/long/etc) to a pointer is not portable across different platforms and " "compilers. For example in 32-bit Windows and linux they are same width, but in 64-bit Windows and linux " "they are of different width. In worst case you end up assigning 64-bit integer to 32-bit pointer. The safe " "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, Certainty::normal); } void Check64BitPortability::returnPointerError(const Token *tok) { reportError(tok, Severity::portability, "CastAddressToIntegerAtReturn", "Returning an address value in a function with integer return type is not portable.\n" "Returning an address value in a function with integer (int/long/etc) return type is not portable across " "different platforms and compilers. For example in 32-bit Windows and Linux they are same width, but in " "64-bit Windows and Linux they are of different width. In worst case you end up casting 64-bit address down " "to 32-bit integer. The safe way is to always return an integer.", CWE758, Certainty::normal); } void Check64BitPortability::returnIntegerError(const Token *tok) { reportError(tok, Severity::portability, "CastIntegerToAddressAtReturn", "Returning an integer in a function with pointer return type is not portable.\n" "Returning an integer (int/long/etc) in a function with pointer return type is not portable across different " "platforms and compilers. For example in 32-bit Windows and Linux they are same width, but in 64-bit Windows " "and Linux they are of different width. In worst case you end up casting 64-bit integer down to 32-bit pointer. " "The safe way is to always return a pointer.", CWE758, Certainty::normal); } cppcheck-2.7/lib/check64bit.h000066400000000000000000000056441417746362400160360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef check64bitH #define check64bitH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief Check for 64-bit portability issues */ class CPPCHECKLIB Check64BitPortability : public Check { public: /** This constructor is used when registering the Check64BitPortability */ Check64BitPortability() : Check(myName()) {} /** This constructor is used when running checks. */ Check64BitPortability(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { Check64BitPortability check64BitPortability(tokenizer, settings, errorLogger); check64BitPortability.pointerassignment(); } /** Check for pointer assignment */ void pointerassignment(); private: void assignmentAddressToIntegerError(const Token *tok); void assignmentIntegerToAddressError(const Token *tok); void returnIntegerError(const Token *tok); void returnPointerError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { Check64BitPortability c(nullptr, settings, errorLogger); c.assignmentAddressToIntegerError(nullptr); c.assignmentIntegerToAddressError(nullptr); c.returnIntegerError(nullptr); c.returnPointerError(nullptr); } static std::string myName() { return "64-bit portability"; } std::string classInfo() const OVERRIDE { return "Check if there is 64-bit portability issues:\n" "- assign address to/from int/long\n" "- casting address from/to integer when returning from function\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // check64bitH cppcheck-2.7/lib/checkassert.cpp000066400000000000000000000137511417746362400167400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // You should not write statements with side effects in assert() //--------------------------------------------------------------------------- #include "checkassert.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" //--------------------------------------------------------------------------- // CWE ids used static const struct CWE CWE398(398U); // Indicator of Poor Code Quality // Register this check class (by creating a static instance of it) namespace { CheckAssert instance; } void CheckAssert::assertWithSideEffects() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token* tok = mTokenizer->list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "assert (")) continue; const Token *endTok = tok->next()->link(); for (const Token* tmp = tok->next(); tmp != endTok; tmp = tmp->next()) { if (Token::simpleMatch(tmp, "sizeof (")) tmp = tmp->linkAt(1); checkVariableAssignment(tmp, tok->scope()); if (tmp->tokType() != Token::eFunction) continue; const Function* f = tmp->function(); if (f->nestedIn->isClassOrStruct() && !f->isStatic() && !f->isConst()) { sideEffectInAssertError(tmp, f->name()); // Non-const member function called continue; } const Scope* scope = f->functionScope; if (!scope) continue; for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (!tok2->isAssignmentOp() && tok2->tokType() != Token::eIncDecOp) continue; const Variable* var = tok2->previous()->variable(); if (!var || var->isLocal() || (var->isArgument() && !var->isReference() && !var->isPointer())) continue; // See ticket #4937. Assigning function arguments not passed by reference is ok. if (var->isArgument() && var->isPointer() && tok2->strAt(-2) != "*") continue; // Pointers need to be dereferenced, otherwise there is no error bool noReturnInScope = true; for (const Token *rt = scope->bodyStart; rt != scope->bodyEnd; rt = rt->next()) { if (rt->str() != "return") continue; // find all return statements if (inSameScope(rt, tok2)) { noReturnInScope = false; break; } } if (noReturnInScope) continue; sideEffectInAssertError(tmp, f->name()); break; } } tok = endTok; } } //--------------------------------------------------------------------------- void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& functionName) { reportError(tok, Severity::warning, "assertWithSideEffect", "$symbol:" + functionName + "\n" "Assert statement calls a function which may have desired side effects: '$symbol'.\n" "Non-pure function: '$symbol' is called inside assert statement. " "Assert statements are removed from release builds so the code inside " "assert statement is not executed. If the code is needed also in release " "builds, this is a bug.", CWE398, Certainty::normal); } void CheckAssert::assignmentInAssertError(const Token *tok, const std::string& varname) { reportError(tok, Severity::warning, "assignmentInAssert", "$symbol:" + varname + "\n" "Assert statement modifies '$symbol'.\n" "Variable '$symbol' is modified inside assert statement. " "Assert statements are removed from release builds so the code inside " "assert statement is not executed. If the code is needed also in release " "builds, this is a bug.", CWE398, Certainty::normal); } // checks if side effects happen on the variable prior to tmp void CheckAssert::checkVariableAssignment(const Token* assignTok, const Scope *assertionScope) { if (!assignTok->isAssignmentOp() && assignTok->tokType() != Token::eIncDecOp) return; const Variable* var = assignTok->astOperand1()->variable(); if (!var) return; // Variable declared in inner scope in assert => don't warn if (assertionScope != var->scope()) { const Scope *s = var->scope(); while (s && s != assertionScope) s = s->nestedIn; if (s == assertionScope) return; } // assignment if (assignTok->isAssignmentOp() || assignTok->tokType() == Token::eIncDecOp) { if (var->isConst()) { return; } assignmentInAssertError(assignTok, var->name()); } // TODO: function calls on var } bool CheckAssert::inSameScope(const Token* returnTok, const Token* assignTok) { // TODO: even if a return is in the same scope, the assignment might not affect it. return returnTok->scope() == assignTok->scope(); } cppcheck-2.7/lib/checkassert.h000066400000000000000000000052641417746362400164050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkassertH #define checkassertH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Scope; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief Checking for side effects in assert statements */ class CPPCHECKLIB CheckAssert : public Check { public: CheckAssert() : Check(myName()) {} CheckAssert(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** run checks, the token list is not simplified */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckAssert checkAssert(tokenizer, settings, errorLogger); checkAssert.assertWithSideEffects(); } void assertWithSideEffects(); protected: void checkVariableAssignment(const Token* assignTok, const Scope *assertionScope); static bool inSameScope(const Token* returnTok, const Token* assignTok); private: void sideEffectInAssertError(const Token *tok, const std::string& functionName); void assignmentInAssertError(const Token *tok, const std::string &varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckAssert c(nullptr, settings, errorLogger); c.sideEffectInAssertError(nullptr, "function"); c.assignmentInAssertError(nullptr, "var"); } static std::string myName() { return "Assert"; } std::string classInfo() const OVERRIDE { return "Warn if there are side effects in assert statements (since this cause different behaviour in debug/release builds).\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkassertH cppcheck-2.7/lib/checkautovariables.cpp000066400000000000000000001047061417746362400203010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Auto variables checks //--------------------------------------------------------------------------- #include "checkautovariables.h" #include "astutils.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class into cppcheck by creating a static instance of it.. namespace { CheckAutoVariables instance; } static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE562(562U); // Return of Stack Variable Address static const CWE CWE590(590U); // Free of Memory not on the Heap static bool isPtrArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isPointer()); } static bool isArrayArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isArray()); } static bool isArrayVar(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArray() && !var->isArgument()); } static bool isRefPtrArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isReference() && var->isPointer()); } static bool isNonReferenceArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && !var->isReference() && (var->isPointer() || (var->valueType() && var->valueType()->type >= ValueType::Type::CONTAINER) || var->type())); } static bool isAutoVar(const Token *tok) { const Variable *var = tok->variable(); if (!var || !var->isLocal() || var->isStatic()) return false; if (var->isReference()) { // address of reference variable can be taken if the address // of the variable it points at is not a auto-var // TODO: check what the reference variable references. return false; } if (Token::Match(tok, "%name% .|::")) { do { tok = tok->tokAt(2); } while (Token::Match(tok, "%name% .|::")); if (Token::Match(tok, "%name% (")) return false; } return true; } static bool isAutoVarArray(const Token *tok) { if (!tok) return false; // &x[..] if (tok->isUnaryOp("&") && Token::simpleMatch(tok->astOperand1(), "[")) return isAutoVarArray(tok->astOperand1()->astOperand1()); // x+y if (tok->str() == "+") return isAutoVarArray(tok->astOperand1()) || isAutoVarArray(tok->astOperand2()); // x-intexpr if (tok->str() == "-") return isAutoVarArray(tok->astOperand1()) && tok->astOperand2() && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->isIntegral(); const Variable *var = tok->variable(); if (!var) return false; // Variable if (var->isLocal() && !var->isStatic() && var->isArray() && !var->isPointer()) return true; // ValueFlow if (var->isPointer() && !var->isArgument()) { for (std::list::const_iterator it = tok->values().begin(); it != tok->values().end(); ++it) { const ValueFlow::Value &val = *it; if (val.isTokValue() && isAutoVarArray(val.tokvalue)) return true; } } return false; } // Verification that we really take the address of a local variable static bool checkRvalueExpression(const Token * const vartok) { const Variable * const var = vartok->variable(); if (var == nullptr) return false; if (Token::Match(vartok->previous(), "& %name% [") && var->isPointer()) return false; const Token * const next = vartok->next(); // &a.b[0] if (Token::Match(vartok, "%name% . %var% [") && !var->isPointer()) { const Variable *var2 = next->next()->variable(); return var2 && !var2->isPointer(); } return ((next->str() != "." || (!var->isPointer() && (!var->isClass() || var->type()))) && next->strAt(2) != "."); } static bool isAddressOfLocalVariable(const Token *expr) { if (!expr) return false; if (Token::Match(expr, "+|-")) return isAddressOfLocalVariable(expr->astOperand1()) || isAddressOfLocalVariable(expr->astOperand2()); if (expr->isCast()) return isAddressOfLocalVariable(expr->astOperand2() ? expr->astOperand2() : expr->astOperand1()); if (expr->isUnaryOp("&")) { const Token *op = expr->astOperand1(); bool deref = false; while (Token::Match(op, ".|[")) { if (op->originalName() == "->") return false; if (op->str() == "[") deref = true; op = op->astOperand1(); } return op && isAutoVar(op) && (!deref || !op->variable()->isPointer()); } return false; } static bool variableIsUsedInScope(const Token* start, nonneg int varId, const Scope *scope) { if (!start) // Ticket #5024 return false; for (const Token *tok = start; tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId() == varId) return true; const Scope::ScopeType scopeType = tok->scope()->type; if (scopeType == Scope::eFor || scopeType == Scope::eDo || scopeType == Scope::eWhile) // In case of loops, better checking would be necessary return true; if (Token::simpleMatch(tok, "asm (")) return true; } return false; } void CheckAutoVariables::assignFunctionArg() { const bool printStyle = mSettings->severity.isEnabled(Severity::style); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); if (!printStyle && !printWarning) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { // TODO: What happens if this is removed? if (tok->astParent()) continue; if (!(tok->isAssignmentOp() || Token::Match(tok, "++|--")) || !Token::Match(tok->astOperand1(), "%var%")) continue; const Token* const vartok = tok->astOperand1(); if (isNonReferenceArg(vartok) && !Token::Match(vartok->next(), "= %varid% ;", vartok->varId()) && !variableIsUsedInScope(Token::findsimplematch(vartok->next(), ";"), vartok->varId(), scope) && !Token::findsimplematch(vartok, "goto", scope->bodyEnd)) { if (vartok->variable()->isPointer() && printWarning) errorUselessAssignmentPtrArg(vartok); else if (printStyle) errorUselessAssignmentArg(vartok); } } } } void CheckAutoVariables::autoVariables() { const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { // Skip lambda.. if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { tok = lambdaEndToken; continue; } // Critical assignment if (Token::Match(tok, "[;{}] %var% = & %var%") && isRefPtrArg(tok->next()) && isAutoVar(tok->tokAt(4))) { if (checkRvalueExpression(tok->tokAt(4))) checkAutoVariableAssignment(tok->next(), false); } else if (Token::Match(tok, "[;{}] * %var% =") && isPtrArg(tok->tokAt(2)) && isAddressOfLocalVariable(tok->tokAt(3)->astOperand2())) { checkAutoVariableAssignment(tok->next(), false); } else if (Token::Match(tok, "[;{}] %var% . %var% =") && isPtrArg(tok->next()) && isAddressOfLocalVariable(tok->tokAt(4)->astOperand2())) { checkAutoVariableAssignment(tok->next(), false); } else if (Token::Match(tok, "[;{}] %var% . %var% = %var% ;")) { // TODO: check if the parameter is only changed temporarily (#2969) if (printInconclusive && isPtrArg(tok->next())) { if (isAutoVarArray(tok->tokAt(5))) checkAutoVariableAssignment(tok->next(), true); } tok = tok->tokAt(5); } else if (Token::Match(tok, "[;{}] * %var% = %var% ;")) { const Variable * var1 = tok->tokAt(2)->variable(); if (var1 && var1->isArgument() && Token::Match(var1->nameToken()->tokAt(-3), "%type% * *")) { if (isAutoVarArray(tok->tokAt(4))) checkAutoVariableAssignment(tok->next(), false); } tok = tok->tokAt(4); } else if (Token::Match(tok, "[;{}] %var% [") && Token::simpleMatch(tok->linkAt(2), "] =") && (isPtrArg(tok->next()) || isArrayArg(tok->next())) && isAddressOfLocalVariable(tok->linkAt(2)->next()->astOperand2())) { errorAutoVariableAssignment(tok->next(), false); } // Invalid pointer deallocation else if ((Token::Match(tok, "%name% ( %var%|%str% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) || (mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| %var%|%str% !!["))) { tok = Token::findmatch(tok->next(), "%var%|%str%"); if (isArrayVar(tok) || tok->tokType() == Token::eString) errorInvalidDeallocation(tok, nullptr); else if (tok->variable() && tok->variable()->isPointer()) { for (const ValueFlow::Value &v : tok->values()) { if (!(v.isTokValue())) continue; if (isArrayVar(v.tokvalue) || ((v.tokvalue->tokType() == Token::eString) && !v.isImpossible())) { errorInvalidDeallocation(tok, &v); break; } } } } else if ((Token::Match(tok, "%name% ( & %var% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) || (mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); if (isAutoVar(tok)) errorInvalidDeallocation(tok, nullptr); } } } } bool CheckAutoVariables::checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken) { if (!startToken) startToken = Token::findsimplematch(expr, "=")->next(); for (const Token *tok = startToken; tok; tok = tok->next()) { if (tok->str() == "}" && tok->scope()->type == Scope::ScopeType::eFunction) errorAutoVariableAssignment(expr, inconclusive); if (Token::Match(tok, "return|throw|break|continue")) { errorAutoVariableAssignment(expr, inconclusive); return true; } if (Token::simpleMatch(tok, "=")) { const Token *lhs = tok; while (Token::Match(lhs->previous(), "%name%|.|*")) lhs = lhs->previous(); const Token *e = expr; while (e->str() != "=" && lhs->str() == e->str()) { e = e->next(); lhs = lhs->next(); } if (lhs->str() == "=") return false; } if (Token::simpleMatch(tok, "if (")) { const Token *ifStart = tok->linkAt(1)->next(); return checkAutoVariableAssignment(expr, inconclusive, ifStart) || checkAutoVariableAssignment(expr, inconclusive, ifStart->link()->next()); } if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); } return false; } //--------------------------------------------------------------------------- void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok) { reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of an auto-variable returned.", CWE562, Certainty::normal); } void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok, const ValueFlow::Value *value) { reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of auto-variable '" + value->tokvalue->astOperand1()->expressionString() + "' returned", CWE562, Certainty::normal); } void CheckAutoVariables::errorReturnPointerToLocalArray(const Token *tok) { reportError(tok, Severity::error, "returnLocalVariable", "Pointer to local array variable returned.", CWE562, Certainty::normal); } void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok, bool inconclusive) { if (!inconclusive) { reportError(tok, Severity::error, "autoVariables", "Address of local auto-variable assigned to a function parameter.\n" "Dangerous assignment - the function parameter is assigned the address of a local " "auto-variable. Local auto-variables are reserved from the stack which " "is freed when the function ends. So the pointer to a local variable " "is invalid after the function ends.", CWE562, Certainty::normal); } else { reportError(tok, Severity::error, "autoVariables", "Address of local auto-variable assigned to a function parameter.\n" "Function parameter is assigned the address of a local auto-variable. " "Local auto-variables are reserved from the stack which is freed when " "the function ends. The address is invalid after the function ends and it " "might 'leak' from the function through the parameter.", CWE562, Certainty::inconclusive); } } void CheckAutoVariables::errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "returnAddressOfFunctionParameter", "$symbol:" + varname + "\n" "Address of function parameter '$symbol' returned.\n" "Address of the function parameter '$symbol' becomes invalid after the function exits because " "function parameters are stored on the stack which is freed when the function exits. Thus the returned " "value is invalid.", CWE562, Certainty::normal); } void CheckAutoVariables::errorUselessAssignmentArg(const Token *tok) { reportError(tok, Severity::style, "uselessAssignmentArg", "Assignment of function parameter has no effect outside the function.", CWE398, Certainty::normal); } void CheckAutoVariables::errorUselessAssignmentPtrArg(const Token *tok) { reportError(tok, Severity::warning, "uselessAssignmentPtrArg", "Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- static bool isInScope(const Token * tok, const Scope * scope) { if (!tok) return false; if (!scope) return false; const Variable * var = tok->variable(); if (var && (var->isGlobal() || var->isStatic() || var->isExtern())) return false; if (tok->scope() && tok->scope()->isNestedIn(scope)) return true; if (!var) return false; if (var->isArgument() && !var->isReference()) { const Scope * tokScope = tok->scope(); if (!tokScope) return false; for (const Scope * argScope:tokScope->nestedList) { if (argScope && argScope->isNestedIn(scope)) return true; } } return false; } static bool isDeadScope(const Token * tok, const Scope * scope) { if (!tok) return false; if (!scope) return false; const Variable * var = tok->variable(); if (var && (!var->isLocal() || var->isStatic() || var->isExtern())) return false; if (tok->scope() && tok->scope()->bodyEnd != scope->bodyEnd && precedes(tok->scope()->bodyEnd, scope->bodyEnd)) return true; return false; } static int getPointerDepth(const Token *tok) { if (!tok) return 0; if (tok->valueType()) return tok->valueType()->pointer; int n = 0; std::pair decl = Token::typeDecl(tok); for (const Token* tok2 = decl.first; tok2 != decl.second; tok2 = tok2->next()) if (Token::simpleMatch(tok, "*")) n++; return n; } static bool isDeadTemporary(bool cpp, const Token* tok, const Token* expr, const Library* library) { if (!isTemporary(cpp, tok, library)) return false; if (expr) { if (!precedes(nextAfterAstRightmostLeaf(tok->astTop()), nextAfterAstRightmostLeaf(expr->astTop()))) return false; const Token* parent = tok->astParent(); // Is in a for loop if (astIsRHS(tok) && Token::simpleMatch(parent, ":") && Token::simpleMatch(parent->astParent(), "(") && Token::simpleMatch(parent->astParent()->previous(), "for (")) { const Token* braces = parent->astParent()->link()->next(); if (precedes(braces, expr) && precedes(expr, braces->link())) return false; } } return true; } static bool isEscapedReference(const Variable* var) { if (!var) return false; if (!var->isReference()) return false; if (!var->declEndToken()) return false; if (!Token::simpleMatch(var->declEndToken(), "=")) return false; const Token* vartok = var->declEndToken()->astOperand2(); return !isTemporary(true, vartok, nullptr, false); } static bool isDanglingSubFunction(const Token* tokvalue, const Token* tok) { if (!tokvalue) return false; const Variable* var = tokvalue->variable(); if (!var->isLocal()) return false; Function* f = Scope::nestedInFunction(tok->scope()); if (!f) return false; const Token* parent = tokvalue->astParent(); while (parent && !Token::Match(parent->previous(), "%name% (")) { parent = parent->astParent(); } if (!Token::simpleMatch(parent, "(")) return false; return exprDependsOnThis(parent); } static const Variable* getParentVar(const Token* tok) { if (!tok) return nullptr; if (Token::simpleMatch(tok, ".")) return getParentVar(tok->astOperand1()); return tok->variable(); } static bool isAssignedToNonLocal(const Token* tok) { if (!Token::simpleMatch(tok->astParent(), "=")) return false; if (!astIsRHS(tok)) return false; const Variable* var = getParentVar(tok->astParent()->astOperand1()); if (!var) return false; return !var->isLocal() || var->isStatic(); } static std::vector getParentMembers(const Token* tok) { if (!tok) return {}; if (!Token::simpleMatch(tok->astParent(), ".")) return {tok}; const Token* parent = tok; while (Token::simpleMatch(parent->astParent(), ".")) parent = parent->astParent(); std::vector result; for (const Token* tok2 : astFlatten(parent, ".")) { if (Token::simpleMatch(tok2, "(") && Token::simpleMatch(tok2->astOperand1(), ".")) { std::vector sub = getParentMembers(tok2->astOperand1()); result.insert(result.end(), sub.begin(), sub.end()); } result.push_back(tok2); } return result; } static const Token* getParentLifetime(bool cpp, const Token* tok, const Library* library) { std::vector members = getParentMembers(tok); if (members.size() < 2) return tok; // Find the first local variable or temporary auto it = std::find_if(members.rbegin(), members.rend(), [&](const Token* tok2) { const Variable* var = tok2->variable(); if (var) { return var->isLocal() || var->isArgument(); } else { return isTemporary(cpp, tok2, library); } }); if (it == members.rend()) return tok; // If any of the submembers are borrowed types then stop if (std::any_of(it.base() - 1, members.end() - 1, [&](const Token* tok2) { if (astIsPointer(tok2) || astIsContainerView(tok2) || astIsIterator(tok2)) return true; if (!astIsUniqueSmartPointer(tok2)) { if (astIsSmartPointer(tok2)) return true; const Token* dotTok = tok2->next(); if (!Token::simpleMatch(dotTok, ".")) { const Token* endTok = nextAfterAstRightmostLeaf(tok2); if (!endTok) dotTok = tok2->next(); else if (Token::simpleMatch(endTok, ".")) dotTok = endTok; else if (Token::simpleMatch(endTok->next(), ".")) dotTok = endTok->next(); } // If we are dereferencing the member variable then treat it as borrowed if (Token::simpleMatch(dotTok, ".") && dotTok->originalName() == "->") return true; } const Variable* var = tok2->variable(); return var && var->isReference(); })) return nullptr; return *it; } void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end) { const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); if (!start) return; const Scope * scope = start->scope(); if (!scope) return; // If the scope is not set correctly then skip checking it if (scope->bodyStart != start) return; bool returnRef = Function::returnsReference(scope->function); for (const Token *tok = start; tok && tok != end; tok = tok->next()) { // Return reference from function if (returnRef && Token::simpleMatch(tok->astParent(), "return")) { for (const LifetimeToken& lt : getLifetimeTokens(tok, true)) { if (!printInconclusive && lt.inconclusive) continue; const Variable* var = lt.token->variable(); if (var && !var->isGlobal() && !var->isStatic() && !var->isReference() && !var->isRValueReference() && isInScope(var->nameToken(), tok->scope())) { errorReturnReference(tok, lt.errorPath, lt.inconclusive); break; } else if (isDeadTemporary(mTokenizer->isCPP(), lt.token, nullptr, &mSettings->library)) { errorReturnTempReference(tok, lt.errorPath, lt.inconclusive); break; } } // Assign reference to non-local variable } else if (Token::Match(tok->previous(), "&|&& %var% =") && tok->astParent() == tok->next() && tok->variable() && tok->variable()->nameToken() == tok && tok->variable()->declarationId() == tok->varId() && tok->variable()->isStatic() && !tok->variable()->isArgument()) { ErrorPath errorPath; const Variable *var = getLifetimeVariable(tok, errorPath); if (var && isInScope(var->nameToken(), tok->scope())) { errorDanglingReference(tok, var, errorPath); continue; } // Reference to temporary } else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) { for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(tok))) { if (!printInconclusive && lt.inconclusive) continue; const Token * tokvalue = lt.token; if (isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) { errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive); break; } } } const bool escape = Token::Match(tok->astParent(), "return|throw"); std::unordered_set exprs; for (const ValueFlow::Value& val:tok->values()) { if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue()) continue; if (!printInconclusive && val.isInconclusive()) continue; const Token* parent = getParentLifetime(mTokenizer->isCPP(), val.tokvalue, &mSettings->library); if (!exprs.insert(parent).second) continue; for (const LifetimeToken& lt : getLifetimeTokens(parent, escape || isAssignedToNonLocal(tok))) { const Token * tokvalue = lt.token; if (val.isLocalLifetimeValue()) { if (escape) { if (getPointerDepth(tok) < getPointerDepth(tokvalue)) continue; if (!isLifetimeBorrowed(tok, mSettings)) continue; if (tokvalue->exprId() == tok->exprId() && !(tok->variable() && tok->variable()->isArray()) && !astIsContainerView(tok->astParent())) continue; if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) && isInScope(tokvalue->variable()->nameToken(), scope)) || isDeadTemporary(mTokenizer->isCPP(), tokvalue, nullptr, &mSettings->library)) { errorReturnDanglingLifetime(tok, &val); break; } } else if (tokvalue->variable() && isDeadScope(tokvalue->variable()->nameToken(), tok->scope())) { errorInvalidLifetime(tok, &val); break; } else if (!tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) { errorDanglingTemporaryLifetime(tok, &val, tokvalue); break; } } if (tokvalue->variable() && (isInScope(tokvalue->variable()->nameToken(), tok->scope()) || (val.isSubFunctionLifetimeValue() && isDanglingSubFunction(tokvalue, tok)))) { const Variable * var = nullptr; const Token * tok2 = tok; if (Token::simpleMatch(tok->astParent(), "=")) { if (astIsRHS(tok)) { var = getParentVar(tok->astParent()->astOperand1()); tok2 = tok->astParent()->astOperand1(); } } else if (tok->variable() && tok->variable()->declarationId() == tok->varId()) { var = tok->variable(); } if (!isLifetimeBorrowed(tok, mSettings)) continue; const Token* nextTok = nextAfterAstRightmostLeaf(tok->astTop()); if (!nextTok) nextTok = tok->next(); if (var && !var->isLocal() && !var->isArgument() && !isVariableChanged(nextTok, tok->scope()->bodyEnd, var->declarationId(), var->isGlobal(), mSettings, mTokenizer->isCPP())) { errorDanglngLifetime(tok2, &val); break; } } } } const Token *lambdaEndToken = findLambdaEndToken(tok); if (lambdaEndToken) { checkVarLifetimeScope(lambdaEndToken->link(), lambdaEndToken); tok = lambdaEndToken; } if (tok->str() == "{" && tok->scope()) { // Check functions in local classes if (tok->scope()->type == Scope::eClass || tok->scope()->type == Scope::eStruct || tok->scope()->type == Scope::eUnion) { for (const Function& f:tok->scope()->functionList) { if (f.functionScope) checkVarLifetimeScope(f.functionScope->bodyStart, f.functionScope->bodyEnd); } tok = tok->link(); } } } } void CheckAutoVariables::checkVarLifetime() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (!scope->function) continue; checkVarLifetimeScope(scope->bodyStart, scope->bodyEnd); } } void CheckAutoVariables::errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value *val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string msg = "Returning " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "returnDanglingLifetime", msg + " that will be invalid when returning.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckAutoVariables::errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "invalidLifetime", msg + " that is out of scope.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckAutoVariables::errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tempTok, "Temporary created here."); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "danglingTemporaryLifetime", msg + " that is a temporary.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string tokName = tok ? tok->expressionString() : "x"; std::string msg = "Non-local variable '" + tokName + "' will use " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckAutoVariables::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "danglingTempReference", "Using reference to dangling temporary.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "returnReference", "Reference to local variable returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckAutoVariables::errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath) { std::string tokName = tok ? tok->str() : "x"; std::string varName = var ? var->name() : "y"; std::string msg = "Non-local reference variable '" + tokName + "' to local variable '" + varName + "'"; errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "danglingReference", msg, CWE562, Certainty::normal); } void CheckAutoVariables::errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "returnTempReference", "Reference to temporary returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckAutoVariables::errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val) { const Variable *var = val ? val->tokvalue->variable() : (tok ? tok->variable() : nullptr); std::string type = "an auto-variable"; if (tok && tok->tokType() == Token::eString) type = "a string literal"; else if (val && val->tokvalue->tokType() == Token::eString) type = "a pointer pointing to a string literal"; else if (var) { if (var->isGlobal()) type = "a global variable"; else if (var->isStatic()) type = "a static variable"; } if (val) type += " (" + val->tokvalue->str() + ")"; reportError(getErrorPath(tok, val, "Deallocating memory that was not dynamically allocated"), Severity::error, "autovarInvalidDeallocation", "Deallocation of " + type + " results in undefined behaviour.\n" "The deallocation of " + type + " results in undefined behaviour. You should only free memory " "that has been allocated dynamically.", CWE590, Certainty::normal); } cppcheck-2.7/lib/checkautovariables.h000066400000000000000000000130641417746362400177420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkautovariablesH #define checkautovariablesH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errortypes.h" #include class Settings; class Token; class Tokenizer; class ErrorLogger; class Variable; namespace ValueFlow { class Value; } /// @addtogroup Checks /** @brief Various small checks for automatic variables */ /// @{ class CPPCHECKLIB CheckAutoVariables : public Check { public: /** This constructor is used when registering the CheckClass */ CheckAutoVariables() : Check(myName()) {} /** This constructor is used when running checks. */ CheckAutoVariables(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); checkAutoVariables.assignFunctionArg(); checkAutoVariables.checkVarLifetime(); checkAutoVariables.autoVariables(); } /** assign function argument */ void assignFunctionArg(); /** Check auto variables */ void autoVariables(); /** * Check variable assignment.. value must be changed later or there will be a error reported * @return true if error is reported */ bool checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken = nullptr); void checkVarLifetime(); void checkVarLifetimeScope(const Token * start, const Token * end); private: void errorReturnAddressToAutoVariable(const Token *tok); void errorReturnAddressToAutoVariable(const Token *tok, const ValueFlow::Value *value); void errorReturnPointerToLocalArray(const Token *tok); void errorAutoVariableAssignment(const Token *tok, bool inconclusive); void errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value* val); void errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val); void errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val); void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok); void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath); void errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val); void errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname); void errorUselessAssignmentArg(const Token *tok); void errorUselessAssignmentPtrArg(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { ErrorPath errorPath; CheckAutoVariables c(nullptr,settings,errorLogger); c.errorAutoVariableAssignment(nullptr, false); c.errorReturnAddressToAutoVariable(nullptr); c.errorReturnPointerToLocalArray(nullptr); c.errorReturnReference(nullptr, errorPath, false); c.errorDanglingReference(nullptr, nullptr, errorPath); c.errorReturnTempReference(nullptr, errorPath, false); c.errorDanglingTempReference(nullptr, errorPath, false); c.errorInvalidDeallocation(nullptr, nullptr); c.errorReturnAddressOfFunctionParameter(nullptr, "parameter"); c.errorUselessAssignmentArg(nullptr); c.errorUselessAssignmentPtrArg(nullptr); c.errorReturnDanglingLifetime(nullptr, nullptr); c.errorInvalidLifetime(nullptr, nullptr); c.errorDanglngLifetime(nullptr, nullptr); c.errorDanglingTemporaryLifetime(nullptr, nullptr, nullptr); } static std::string myName() { return "Auto Variables"; } std::string classInfo() const OVERRIDE { return "A pointer to a variable is only valid as long as the variable is in scope.\n" "Check:\n" "- returning a pointer to auto or temporary variable\n" "- assigning address of an variable to an effective parameter of a function\n" "- returning reference to local/temporary variable\n" "- returning address of function parameter\n" "- suspicious assignment of pointer argument\n" "- useless assignment of function argument\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkautovariablesH cppcheck-2.7/lib/checkbool.cpp000066400000000000000000000474241417746362400163760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkbool.h" #include "astutils.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckBool instance; } static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE571(571U); // Expression is Always True static const CWE CWE587(587U); // Assignment of a Fixed Address to a Pointer static const CWE CWE704(704U); // Incorrect Type Conversion or Cast static bool isBool(const Variable* var) { return (var && Token::Match(var->typeEndToken(), "bool|_Bool")); } //--------------------------------------------------------------------------- void CheckBool::checkIncrementBoolean() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (astIsBool(tok) && tok->astParent() && tok->astParent()->str() == "++") { incrementBooleanError(tok); } } } } void CheckBool::incrementBooleanError(const Token *tok) { reportError( tok, Severity::style, "incrementboolean", "Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n" "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true. You should assign it the value 'true' instead.", CWE398, Certainty::normal ); } static bool isConvertedToBool(const Token* tok) { if (!tok->astParent()) return false; return astIsBool(tok->astParent()) || Token::Match(tok->astParent()->previous(), "if|while ("); } //--------------------------------------------------------------------------- // if (bool & bool) -> if (bool && bool) // if (bool | bool) -> if (bool || bool) //--------------------------------------------------------------------------- void CheckBool::checkBitwiseOnBoolean() { if (!mSettings->severity.isEnabled(Severity::style)) return; // danmar: this is inconclusive because I don't like that there are // warnings for calculations. Example: set_flag(a & b); if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->isBinaryOp() && (tok->str() == "&" || tok->str() == "|")) { if (!(astIsBool(tok->astOperand1()) || astIsBool(tok->astOperand2()))) continue; if (tok->str() == "|" && !isConvertedToBool(tok) && !(astIsBool(tok->astOperand1()) && astIsBool(tok->astOperand2()))) continue; if (!isConstExpression(tok->astOperand1(), mSettings->library, true, mTokenizer->isCPP())) continue; if (!isConstExpression(tok->astOperand2(), mSettings->library, true, mTokenizer->isCPP())) continue; if (tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2()) continue; const std::string expression = astIsBool(tok->astOperand1()) ? tok->astOperand1()->expressionString() : tok->astOperand2()->expressionString(); bitwiseOnBooleanError(tok, expression, tok->str() == "&" ? "&&" : "||"); } } } } void CheckBool::bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op) { reportError(tok, Severity::style, "bitwiseOnBoolean", "Boolean expression '" + expression + "' is used in bitwise operation. Did you mean '" + op + "'?", CWE398, Certainty::inconclusive); } //--------------------------------------------------------------------------- // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolWithInt() { if (!mSettings->severity.isEnabled(Severity::warning) || !mTokenizer->isCPP()) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->isBinaryOp()) continue; const Token* const left = tok->astOperand1(); const Token* const right = tok->astOperand2(); if (left->isBoolean() && right->varId()) { // Comparing boolean constant with variable if (tok->str() != "==" && tok->str() != "!=") { comparisonOfBoolWithInvalidComparator(right, left->str()); } } else if (left->varId() && right->isBoolean()) { // Comparing variable with boolean constant if (tok->str() != "==" && tok->str() != "!=") { comparisonOfBoolWithInvalidComparator(right, left->str()); } } } } } void CheckBool::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression) { reportError(tok, Severity::warning, "comparisonOfBoolWithInvalidComparator", "Comparison of a boolean value using relational operator (<, >, <= or >=).\n" "The result of the expression '" + expression + "' is of type 'bool'. " "Comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results."); } //------------------------------------------------------------------------------- // Comparing functions which are returning value of type bool //------------------------------------------------------------------------------- static bool tokenIsFunctionReturningBool(const Token* tok) { const Function* func = tok->function(); if (func && Token::Match(tok, "%name% (")) { if (func->tokenDef && Token::Match(func->tokenDef->previous(), "bool|_Bool")) { return true; } } return false; } void CheckBool::checkComparisonOfFuncReturningBool() { if (!mSettings->severity.isEnabled(Severity::style)) return; if (!mTokenizer->isCPP()) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") continue; const Token *firstToken = tok->previous(); if (tok->strAt(-1) == ")") { firstToken = firstToken->link()->previous(); } const Token *secondToken = tok->next(); while (secondToken->str() == "!") { secondToken = secondToken->next(); } const bool firstIsFunctionReturningBool = tokenIsFunctionReturningBool(firstToken); const bool secondIsFunctionReturningBool = tokenIsFunctionReturningBool(secondToken); if (firstIsFunctionReturningBool && secondIsFunctionReturningBool) { comparisonOfTwoFuncsReturningBoolError(firstToken->next(), firstToken->str(), secondToken->str()); } else if (firstIsFunctionReturningBool) { comparisonOfFuncReturningBoolError(firstToken->next(), firstToken->str()); } else if (secondIsFunctionReturningBool) { comparisonOfFuncReturningBoolError(secondToken->previous(), secondToken->str()); } } } } void CheckBool::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression) { reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError", "Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" "The return type of function '" + expression + "' is 'bool' " "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results.", CWE398, Certainty::normal); } void CheckBool::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2) { reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError", "Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" "The return type of function '" + expression1 + "' and function '" + expression2 + "' is 'bool' " "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results.", CWE398, Certainty::normal); } //------------------------------------------------------------------------------- // Comparison of bool with bool //------------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolWithBool() { // FIXME: This checking is "experimental" because of the false positives // when self checking lib/tokenize.cpp (#2617) if (!mSettings->certainty.isEnabled(Certainty::experimental)) return; if (!mSettings->severity.isEnabled(Severity::style)) return; if (!mTokenizer->isCPP()) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") continue; bool firstTokenBool = false; const Token *firstToken = tok->previous(); if (firstToken->varId()) { if (isBool(firstToken->variable())) { firstTokenBool = true; } } if (!firstTokenBool) continue; bool secondTokenBool = false; const Token *secondToken = tok->next(); if (secondToken->varId()) { if (isBool(secondToken->variable())) { secondTokenBool = true; } } if (secondTokenBool) { comparisonOfBoolWithBoolError(firstToken->next(), secondToken->str()); } } } } void CheckBool::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression) { reportError(tok, Severity::style, "comparisonOfBoolWithBoolError", "Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n" "The variable '" + expression + "' is of type 'bool' " "and comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results.", CWE398, Certainty::normal); } //----------------------------------------------------------------------------- void CheckBool::checkAssignBoolToPointer() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "=" && astIsPointer(tok->astOperand1()) && astIsBool(tok->astOperand2())) { assignBoolToPointerError(tok); } } } } void CheckBool::assignBoolToPointerError(const Token *tok) { reportError(tok, Severity::error, "assignBoolToPointer", "Boolean value assigned to pointer.", CWE587, Certainty::normal); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolExpressionWithInt() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token* numTok = nullptr; const Token* boolExpr = nullptr; bool numInRhs; if (astIsBool(tok->astOperand1())) { boolExpr = tok->astOperand1(); numTok = tok->astOperand2(); numInRhs = true; } else if (astIsBool(tok->astOperand2())) { boolExpr = tok->astOperand2(); numTok = tok->astOperand1(); numInRhs = false; } else { continue; } if (!numTok || !boolExpr) continue; if (boolExpr->isOp() && numTok->isName() && Token::Match(tok, "==|!=")) // there is weird code such as: ((agetValueLE(0, mSettings); if (minval && minval->intvalue == 0 && (numInRhs ? Token::Match(tok, ">|==|!=") : Token::Match(tok, "<|==|!="))) minval = nullptr; const ValueFlow::Value *maxval = numTok->getValueGE(1, mSettings); if (maxval && maxval->intvalue == 1 && (numInRhs ? Token::Match(tok, "<|==|!=") : Token::Match(tok, ">|==|!="))) maxval = nullptr; if (minval || maxval) { bool not0or1 = (minval && minval->intvalue < 0) || (maxval && maxval->intvalue > 1); comparisonOfBoolExpressionWithIntError(tok, not0or1); } } } } void CheckBool::comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1) { if (not0or1) reportError(tok, Severity::warning, "compareBoolExpressionWithInt", "Comparison of a boolean expression with an integer other than 0 or 1.", CWE398, Certainty::normal); else reportError(tok, Severity::warning, "compareBoolExpressionWithInt", "Comparison of a boolean expression with an integer.", CWE398, Certainty::normal); } void CheckBool::pointerArithBool() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf && !scope.isLoopScope()) continue; const Token* tok = scope.classDef->next()->astOperand2(); if (scope.type == Scope::eFor) { tok = Token::findsimplematch(scope.classDef->tokAt(2), ";"); if (tok) tok = tok->astOperand2(); if (tok) tok = tok->astOperand1(); } else if (scope.type == Scope::eDo) tok = (scope.bodyEnd->tokAt(2)) ? scope.bodyEnd->tokAt(2)->astOperand2() : nullptr; pointerArithBoolCond(tok); } } void CheckBool::pointerArithBoolCond(const Token *tok) { if (!tok) return; if (Token::Match(tok, "&&|%oror%")) { pointerArithBoolCond(tok->astOperand1()); pointerArithBoolCond(tok->astOperand2()); return; } if (tok->str() != "+" && tok->str() != "-") return; if (tok->isBinaryOp() && tok->astOperand1()->isName() && tok->astOperand1()->variable() && tok->astOperand1()->variable()->isPointer() && tok->astOperand2()->isNumber()) pointerArithBoolError(tok); } void CheckBool::pointerArithBoolError(const Token *tok) { reportError(tok, Severity::error, "pointerArithBool", "Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n" "Converting pointer arithmetic result to bool. The boolean result is always true unless there is pointer arithmetic overflow, and overflow is undefined behaviour. Probably a dereference is forgotten.", CWE571, Certainty::normal); } void CheckBool::checkAssignBoolToFloat() { if (!mTokenizer->isCPP()) return; if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "=" && astIsFloat(tok->astOperand1(), false) && astIsBool(tok->astOperand2())) { assignBoolToFloatError(tok); } } } } void CheckBool::assignBoolToFloatError(const Token *tok) { reportError(tok, Severity::style, "assignBoolToFloat", "Boolean value assigned to floating point variable.", CWE704, Certainty::normal); } void CheckBool::returnValueOfFunctionReturningBool() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (!(scope->function && Token::Match(scope->function->retDef, "bool|_Bool"))) continue; for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { // Skip lambdas const Token* tok2 = findLambdaEndToken(tok); if (tok2) tok = tok2; else if (tok->scope() && tok->scope()->isClassOrStruct()) tok = tok->scope()->bodyEnd; else if (Token::simpleMatch(tok, "return") && tok->astOperand1() && (tok->astOperand1()->getValueGE(2, mSettings) || tok->astOperand1()->getValueLE(-1, mSettings)) && !(tok->astOperand1()->astOperand1() && Token::Match(tok->astOperand1(), "&|%or%"))) returnValueBoolError(tok); } } } void CheckBool::returnValueBoolError(const Token *tok) { reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool"); } cppcheck-2.7/lib/checkbool.h000066400000000000000000000140421417746362400160310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkboolH #define checkboolH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief checks dealing with suspicious usage of boolean type (not for evaluating conditions) */ class CPPCHECKLIB CheckBool : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckBool() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckBool(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckBool checkBool(tokenizer, settings, errorLogger); // Checks checkBool.checkComparisonOfBoolExpressionWithInt(); checkBool.checkComparisonOfBoolWithInt(); checkBool.checkAssignBoolToFloat(); checkBool.pointerArithBool(); checkBool.returnValueOfFunctionReturningBool(); checkBool.checkComparisonOfFuncReturningBool(); checkBool.checkComparisonOfBoolWithBool(); checkBool.checkIncrementBoolean(); checkBool.checkAssignBoolToPointer(); checkBool.checkBitwiseOnBoolean(); } /** @brief %Check for comparison of function returning bool*/ void checkComparisonOfFuncReturningBool(); /** @brief %Check for comparison of variable of type bool*/ void checkComparisonOfBoolWithBool(); /** @brief %Check for using postfix increment on bool */ void checkIncrementBoolean(); /** @brief %Check for suspicious comparison of a bool and a non-zero (and non-one) value (e.g. "if (!x==4)") */ void checkComparisonOfBoolWithInt(); /** @brief assigning bool to pointer */ void checkAssignBoolToPointer(); /** @brief assigning bool to float */ void checkAssignBoolToFloat(); /** @brief %Check for using bool in bitwise expression */ void checkBitwiseOnBoolean(); /** @brief %Check for comparing a bool expression with an integer other than 0 or 1 */ void checkComparisonOfBoolExpressionWithInt(); /** @brief %Check for 'if (p+1)' etc. either somebody forgot to dereference, or else somebody uses pointer overflow */ void pointerArithBool(); void pointerArithBoolCond(const Token *tok); /** @brief %Check if a function returning bool returns an integer other than 0 or 1 */ void returnValueOfFunctionReturningBool(); private: // Error messages.. void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression); void comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2); void comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression); void incrementBooleanError(const Token *tok); void comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression); void assignBoolToPointerError(const Token *tok); void assignBoolToFloatError(const Token *tok); void bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op); void comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1); void pointerArithBoolError(const Token *tok); void returnValueBoolError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckBool c(nullptr, settings, errorLogger); c.assignBoolToPointerError(nullptr); c.assignBoolToFloatError(nullptr); c.comparisonOfFuncReturningBoolError(nullptr, "func_name"); c.comparisonOfTwoFuncsReturningBoolError(nullptr, "func_name1", "func_name2"); c.comparisonOfBoolWithBoolError(nullptr, "var_name"); c.incrementBooleanError(nullptr); c.bitwiseOnBooleanError(nullptr, "expression", "&&"); c.comparisonOfBoolExpressionWithIntError(nullptr, true); c.pointerArithBoolError(nullptr); c.comparisonOfBoolWithInvalidComparator(nullptr, "expression"); c.returnValueBoolError(nullptr); } static std::string myName() { return "Boolean"; } std::string classInfo() const OVERRIDE { return "Boolean type checks\n" "- using increment on boolean\n" "- comparison of a boolean expression with an integer other than 0 or 1\n" "- comparison of a function returning boolean value using relational operator\n" "- comparison of a boolean value with boolean value using relational operator\n" "- using bool in bitwise expression\n" "- pointer addition in condition (either dereference is forgot or pointer overflow is required to make the condition false)\n" "- Assigning bool value to pointer or float\n" "- Returning an integer other than 0 or 1 from a function with boolean return value\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkboolH cppcheck-2.7/lib/checkboost.cpp000066400000000000000000000047151417746362400165650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkboost.h" #include "errortypes.h" #include "symboldatabase.h" #include "token.h" #include // Register this check class (by creating a static instance of it) namespace { CheckBoost instance; } static const CWE CWE664(664); void CheckBoost::checkBoostForeachModification() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "BOOST_FOREACH (")) continue; const Token *containerTok = tok->next()->link()->previous(); if (!Token::Match(containerTok, "%var% ) {")) continue; const Token *tok2 = containerTok->tokAt(2); const Token *end = tok2->link(); for (; tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "%varid% . insert|erase|push_back|push_front|pop_front|pop_back|clear|swap|resize|assign|merge|remove|remove_if|reverse|sort|splice|unique|pop|push", containerTok->varId())) { const Token* nextStatement = Token::findsimplematch(tok2->linkAt(3), ";", end); if (!Token::Match(nextStatement, "; break|return|throw")) boostForeachError(tok2); break; } } } } } void CheckBoost::boostForeachError(const Token *tok) { reportError(tok, Severity::error, "boostForeachError", "BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.", CWE664, Certainty::normal ); } cppcheck-2.7/lib/checkboost.h000066400000000000000000000050501417746362400162230ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkboostH #define checkboostH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "tokenize.h" #include class ErrorLogger; class Settings; class Token; /// @addtogroup Checks /// @{ /** @brief %Check Boost usage */ class CPPCHECKLIB CheckBoost : public Check { public: /** This constructor is used when registering the CheckClass */ CheckBoost() : Check(myName()) {} /** This constructor is used when running checks. */ CheckBoost(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (!tokenizer->isCPP()) return; CheckBoost checkBoost(tokenizer, settings, errorLogger); checkBoost.checkBoostForeachModification(); } /** @brief %Check for container modification while using the BOOST_FOREACH macro */ void checkBoostForeachModification(); private: void boostForeachError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckBoost c(nullptr, settings, errorLogger); c.boostForeachError(nullptr); } static std::string myName() { return "Boost usage"; } std::string classInfo() const OVERRIDE { return "Check for invalid usage of Boost:\n" "- container modification during BOOST_FOREACH\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkboostH cppcheck-2.7/lib/checkbufferoverrun.cpp000066400000000000000000001326411417746362400203310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Buffer overrun.. //--------------------------------------------------------------------------- #include "checkbufferoverrun.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include // std::accumulate #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckBufferOverrun instance; } //--------------------------------------------------------------------------- // CWE ids used: static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size static const CWE CWE170(170U); // Improper Null Termination static const CWE CWE_ARGUMENT_SIZE(398U); // Indicator of Poor Code Quality static const CWE CWE_ARRAY_INDEX_THEN_CHECK(398U); // Indicator of Poor Code Quality static const CWE CWE682(682U); // Incorrect Calculation static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE_POINTER_ARITHMETIC_OVERFLOW(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Buffer static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer //--------------------------------------------------------------------------- static const ValueFlow::Value *getBufferSizeValue(const Token *tok) { const std::list &tokenValues = tok->values(); const auto it = std::find_if(tokenValues.begin(), tokenValues.end(), std::mem_fn(&ValueFlow::Value::isBufferSizeValue)); return it == tokenValues.end() ? nullptr : &*it; } static int getMinFormatStringOutputLength(const std::vector ¶meters, nonneg int formatStringArgNr) { if (formatStringArgNr <= 0 || formatStringArgNr > parameters.size()) return 0; if (parameters[formatStringArgNr - 1]->tokType() != Token::eString) return 0; const std::string &formatString = parameters[formatStringArgNr - 1]->str(); bool percentCharFound = false; int outputStringSize = 0; bool handleNextParameter = false; std::string digits_string; bool i_d_x_f_found = false; int parameterLength = 0; int inputArgNr = formatStringArgNr; for (int i = 1; i + 1 < formatString.length(); ++i) { if (formatString[i] == '\\') { if (i < formatString.length() - 1 && formatString[i + 1] == '0') break; ++outputStringSize; ++i; continue; } if (percentCharFound) { switch (formatString[i]) { case 'f': case 'x': case 'X': case 'i': i_d_x_f_found = true; handleNextParameter = true; parameterLength = 1; // TODO break; case 'c': case 'e': case 'E': case 'g': case 'o': case 'u': case 'p': case 'n': handleNextParameter = true; parameterLength = 1; // TODO break; case 'd': i_d_x_f_found = true; parameterLength = 1; if (inputArgNr < parameters.size() && parameters[inputArgNr]->hasKnownIntValue()) parameterLength = MathLib::toString(parameters[inputArgNr]->getKnownIntValue()).length(); handleNextParameter = true; break; case 's': parameterLength = 0; if (inputArgNr < parameters.size() && parameters[inputArgNr]->tokType() == Token::eString) parameterLength = Token::getStrLength(parameters[inputArgNr]); handleNextParameter = true; break; } } if (formatString[i] == '%') percentCharFound = !percentCharFound; else if (percentCharFound) { digits_string.append(1, formatString[i]); } if (!percentCharFound) outputStringSize++; if (handleNextParameter) { int tempDigits = std::abs(std::atoi(digits_string.c_str())); if (i_d_x_f_found) tempDigits = std::max(tempDigits, 1); if (digits_string.find('.') != std::string::npos) { const std::string endStr = digits_string.substr(digits_string.find('.') + 1); const int maxLen = std::max(std::abs(std::atoi(endStr.c_str())), 1); if (formatString[i] == 's') { // For strings, the length after the dot "%.2s" will limit // the length of the string. if (parameterLength > maxLen) parameterLength = maxLen; } else { // For integers, the length after the dot "%.2d" can // increase required length if (tempDigits < maxLen) tempDigits = maxLen; } } if (tempDigits < parameterLength) outputStringSize += parameterLength; else outputStringSize += tempDigits; parameterLength = 0; digits_string.clear(); i_d_x_f_found = false; percentCharFound = false; handleNextParameter = false; ++inputArgNr; } } return outputStringSize; } //--------------------------------------------------------------------------- static bool getDimensionsEtc(const Token * const arrayToken, const Settings *settings, std::vector * const dimensions, ErrorPath * const errorPath, bool * const mightBeLarger, MathLib::bigint* path) { const Token *array = arrayToken; while (Token::Match(array, ".|::")) array = array->astOperand2(); if (array->variable() && array->variable()->isArray() && !array->variable()->dimensions().empty()) { *dimensions = array->variable()->dimensions(); if (dimensions->size() >= 1 && ((*dimensions)[0].num <= 1 || !(*dimensions)[0].tok)) { visitAstNodes(arrayToken, [&](const Token *child) { if (child->originalName() == "->") { *mightBeLarger = true; return ChildrenToVisit::none; } return ChildrenToVisit::op1_and_op2; }); } } else if (const Token *stringLiteral = array->getValueTokenMinStrSize(settings)) { Dimension dim; dim.tok = nullptr; dim.num = Token::getStrArraySize(stringLiteral); dim.known = array->hasKnownValue(); dimensions->emplace_back(dim); } else if (array->valueType() && array->valueType()->pointer >= 1 && (array->valueType()->isIntegral() || array->valueType()->isFloat())) { const ValueFlow::Value *value = getBufferSizeValue(array); if (!value) return false; if (path) *path = value->path; *errorPath = value->errorPath; Dimension dim; dim.known = value->isKnown(); dim.tok = nullptr; const int typeSize = array->valueType()->typeSize(*settings, array->valueType()->pointer > 1); if (typeSize == 0) return false; dim.num = value->intvalue / typeSize; dimensions->emplace_back(dim); } return !dimensions->empty(); } static ValueFlow::Value makeSizeValue(MathLib::bigint size, MathLib::bigint path) { ValueFlow::Value v(size); v.path = path; return v; } static std::vector getOverrunIndexValues(const Token* tok, const Token* arrayToken, const std::vector& dimensions, const std::vector& indexTokens, MathLib::bigint path) { const Token *array = arrayToken; while (Token::Match(array, ".|::")) array = array->astOperand2(); bool isArrayIndex = tok->str() == "["; if (isArrayIndex) { const Token* parent = tok; while (Token::simpleMatch(parent, "[")) parent = parent->astParent(); if (!parent || parent->isUnaryOp("&")) isArrayIndex = false; } bool overflow = false; std::vector indexValues; for (int i = 0; i < dimensions.size() && i < indexTokens.size(); ++i) { MathLib::bigint size = dimensions[i].num; if (!isArrayIndex) size++; const bool zeroArray = array->variable() && array->variable()->isArray() && dimensions[i].num == 0; std::vector values = !zeroArray ? ValueFlow::isOutOfBounds(makeSizeValue(size, path), indexTokens[i]) : std::vector{}; if (values.empty()) { if (indexTokens[i]->hasKnownIntValue()) indexValues.push_back(indexTokens[i]->values().front()); else indexValues.push_back(ValueFlow::Value::unknown()); continue; } overflow = true; indexValues.push_back(values.front()); } if (overflow) return indexValues; return {}; } void CheckBufferOverrun::arrayIndex() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "[") continue; const Token *array = tok->astOperand1(); while (Token::Match(array, ".|::")) array = array->astOperand2(); if (!array || ((!array->variable() || array->variable()->nameToken() == array) && array->tokType() != Token::eString)) continue; if (!array->scope()->isExecutable()) { // LHS in non-executable scope => This is just a definition const Token *parent = tok; while (parent && !Token::simpleMatch(parent->astParent(), "=")) parent = parent->astParent(); if (!parent || parent == parent->astParent()->astOperand1()) continue; } if (astIsContainer(array)) continue; std::vector indexTokens; for (const Token *tok2 = tok; tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) { if (!tok2->astOperand2()) { indexTokens.clear(); break; } indexTokens.emplace_back(tok2->astOperand2()); } if (indexTokens.empty()) continue; std::vector dimensions; ErrorPath errorPath; bool mightBeLarger = false; MathLib::bigint path = 0; if (!getDimensionsEtc(tok->astOperand1(), mSettings, &dimensions, &errorPath, &mightBeLarger, &path)) continue; // Positive index if (!mightBeLarger) { // TODO check arrays with dim 1 also const std::vector& indexValues = getOverrunIndexValues(tok, tok->astOperand1(), dimensions, indexTokens, path); if (!indexValues.empty()) arrayIndexError(tok, dimensions, indexValues); } // Negative index bool neg = false; std::vector negativeIndexes; for (const Token * indexToken : indexTokens) { const ValueFlow::Value *negativeValue = indexToken->getValueLE(-1, mSettings); if (negativeValue) { negativeIndexes.emplace_back(*negativeValue); neg = true; } else { negativeIndexes.emplace_back(ValueFlow::Value::unknown()); } } if (neg) { negativeIndexError(tok, dimensions, negativeIndexes); } } } static std::string stringifyIndexes(const std::string& array, const std::vector& indexValues) { if (indexValues.size() == 1) return MathLib::toString(indexValues[0].intvalue); std::ostringstream ret; ret << array; for (const ValueFlow::Value& index : indexValues) { ret << "["; if (index.isNonValue()) ret << "*"; else ret << index.intvalue; ret << "]"; } return ret.str(); } static std::string arrayIndexMessage(const Token* tok, const std::vector& dimensions, const std::vector& indexValues, const Token* condition) { auto add_dim = [](const std::string &s, const Dimension &dim) { return s + "[" + MathLib::toString(dim.num) + "]"; }; const std::string array = std::accumulate(dimensions.begin(), dimensions.end(), tok->astOperand1()->expressionString(), add_dim); std::ostringstream errmsg; if (condition) errmsg << ValueFlow::eitherTheConditionIsRedundant(condition) << " or the array '" + array + "' is accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds."; else errmsg << "Array '" << array << "' accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds."; return errmsg.str(); } void CheckBufferOverrun::arrayIndexError(const Token* tok, const std::vector& dimensions, const std::vector& indexes) { if (!tok) { reportError(tok, Severity::error, "arrayIndexOutOfBounds", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, Certainty::normal); reportError(tok, Severity::warning, "arrayIndexOutOfBoundsCond", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, Certainty::normal); return; } const Token *condition = nullptr; const ValueFlow::Value *index = nullptr; for (const ValueFlow::Value& indexValue : indexes) { if (!indexValue.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning)) return; if (indexValue.condition) condition = indexValue.condition; if (!index || !indexValue.errorPath.empty()) index = &indexValue; } reportError(getErrorPath(tok, index, "Array index out of bounds"), index->errorSeverity() ? Severity::error : Severity::warning, index->condition ? "arrayIndexOutOfBoundsCond" : "arrayIndexOutOfBounds", arrayIndexMessage(tok, dimensions, indexes, condition), CWE_BUFFER_OVERRUN, index->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } void CheckBufferOverrun::negativeIndexError(const Token* tok, const std::vector& dimensions, const std::vector& indexes) { if (!tok) { reportError(tok, Severity::error, "negativeIndex", "Negative array index", CWE_BUFFER_UNDERRUN, Certainty::normal); return; } const Token *condition = nullptr; const ValueFlow::Value *negativeValue = nullptr; for (const ValueFlow::Value& indexValue : indexes) { if (!indexValue.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning)) return; if (indexValue.condition) condition = indexValue.condition; if (!negativeValue || !indexValue.errorPath.empty()) negativeValue = &indexValue; } reportError(getErrorPath(tok, negativeValue, "Negative array index"), negativeValue->errorSeverity() ? Severity::error : Severity::warning, "negativeIndex", arrayIndexMessage(tok, dimensions, indexes, condition), CWE_BUFFER_UNDERRUN, negativeValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- void CheckBufferOverrun::pointerArithmetic() { if (!mSettings->severity.isEnabled(Severity::portability)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "+|-")) continue; if (!tok->valueType() || tok->valueType()->pointer == 0) continue; if (!tok->isBinaryOp()) continue; if (!tok->astOperand1()->valueType() || !tok->astOperand2()->valueType()) continue; const Token *arrayToken, *indexToken; if (tok->astOperand1()->valueType()->pointer > 0) { arrayToken = tok->astOperand1(); indexToken = tok->astOperand2(); } else { arrayToken = tok->astOperand2(); indexToken = tok->astOperand1(); } if (!indexToken || !indexToken->valueType() || indexToken->valueType()->pointer > 0 || !indexToken->valueType()->isIntegral()) continue; std::vector dimensions; ErrorPath errorPath; bool mightBeLarger = false; MathLib::bigint path = 0; if (!getDimensionsEtc(arrayToken, mSettings, &dimensions, &errorPath, &mightBeLarger, &path)) continue; if (tok->str() == "+") { // Positive index if (!mightBeLarger) { // TODO check arrays with dim 1 also const std::vector indexTokens{indexToken}; const std::vector& indexValues = getOverrunIndexValues(tok, arrayToken, dimensions, indexTokens, path); if (!indexValues.empty()) pointerArithmeticError(tok, indexToken, &indexValues.front()); } if (const ValueFlow::Value *neg = indexToken->getValueLE(-1, mSettings)) pointerArithmeticError(tok, indexToken, neg); } else if (tok->str() == "-") { if (arrayToken->variable() && arrayToken->variable()->isArgument()) continue; const Token *array = arrayToken; while (Token::Match(array, ".|::")) array = array->astOperand2(); if (array->variable() && array->variable()->isArray()) { const ValueFlow::Value *v = indexToken->getValueGE(1, mSettings); if (v) pointerArithmeticError(tok, indexToken, v); } } } } void CheckBufferOverrun::pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue) { if (!tok) { reportError(tok, Severity::portability, "pointerOutOfBounds", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, Certainty::normal); reportError(tok, Severity::portability, "pointerOutOfBoundsCond", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, Certainty::normal); return; } std::string errmsg; if (indexValue->condition) errmsg = "Undefined behaviour, when '" + indexToken->expressionString() + "' is " + MathLib::toString(indexValue->intvalue) + " the pointer arithmetic '" + tok->expressionString() + "' is out of bounds."; else errmsg = "Undefined behaviour, pointer arithmetic '" + tok->expressionString() + "' is out of bounds."; reportError(getErrorPath(tok, indexValue, "Pointer arithmetic overflow"), Severity::portability, indexValue->condition ? "pointerOutOfBoundsCond" : "pointerOutOfBounds", errmsg, CWE_POINTER_ARITHMETIC_OVERFLOW, indexValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const { if (!bufTok->valueType()) return ValueFlow::Value(-1); const Variable *var = bufTok->variable(); if (!var || var->dimensions().empty()) { const ValueFlow::Value *value = getBufferSizeValue(bufTok); if (value) return *value; } if (!var) return ValueFlow::Value(-1); MathLib::bigint dim = std::accumulate(var->dimensions().begin(), var->dimensions().end(), 1LL, [](MathLib::bigint i1, const Dimension &dim) { return i1 * dim.num; }); ValueFlow::Value v; v.setKnown(); v.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; if (var->isPointerArray()) v.intvalue = dim * mSettings->sizeof_pointer; else if (var->isPointer()) return ValueFlow::Value(-1); else { const MathLib::bigint typeSize = bufTok->valueType()->typeSize(*mSettings); v.intvalue = dim * typeSize; } return v; } //--------------------------------------------------------------------------- static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector &args, const MathLib::bigint bufferSize, const Settings *settings) { const Token * const arg = (minsize.arg > 0 && minsize.arg - 1 < args.size()) ? args[minsize.arg - 1] : nullptr; const Token * const arg2 = (minsize.arg2 > 0 && minsize.arg2 - 1 < args.size()) ? args[minsize.arg2 - 1] : nullptr; switch (minsize.type) { case Library::ArgumentChecks::MinSize::Type::STRLEN: if (settings->library.isargformatstr(ftok, minsize.arg)) { return getMinFormatStringOutputLength(args, minsize.arg) < bufferSize; } else if (arg) { const Token *strtoken = arg->getValueTokenMaxStrLength(); if (strtoken) return Token::getStrLength(strtoken) < bufferSize; } break; case Library::ArgumentChecks::MinSize::Type::ARGVALUE: if (arg && arg->hasKnownIntValue()) return arg->getKnownIntValue() <= bufferSize; break; case Library::ArgumentChecks::MinSize::Type::SIZEOF: // TODO break; case Library::ArgumentChecks::MinSize::Type::MUL: if (arg && arg2 && arg->hasKnownIntValue() && arg2->hasKnownIntValue()) return (arg->getKnownIntValue() * arg2->getKnownIntValue()) <= bufferSize; break; case Library::ArgumentChecks::MinSize::Type::VALUE: return minsize.value <= bufferSize; case Library::ArgumentChecks::MinSize::Type::NONE: break; } return true; } void CheckBufferOverrun::bufferOverflow() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (") || Token::simpleMatch(tok, ") {")) continue; if (!mSettings->library.hasminsize(tok)) continue; const std::vector args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { if (!args[argnr]->valueType() || args[argnr]->valueType()->pointer == 0) continue; const std::vector *minsizes = mSettings->library.argminsizes(tok, argnr + 1); if (!minsizes || minsizes->empty()) continue; // Get buffer size.. const Token *argtok = args[argnr]; while (argtok && argtok->isCast()) argtok = argtok->astOperand2() ? argtok->astOperand2() : argtok->astOperand1(); while (Token::Match(argtok, ".|::")) argtok = argtok->astOperand2(); if (!argtok || !argtok->variable()) continue; if (argtok->valueType() && argtok->valueType()->pointer == 0) continue; // TODO: strcpy(buf+10, "hello"); const ValueFlow::Value bufferSize = getBufferSize(argtok); if (bufferSize.intvalue <= 0) continue; // buffer size == 1 => do not warn for dynamic memory if (bufferSize.intvalue == 1) { const Token *tok2 = argtok; while (Token::simpleMatch(tok2->astParent(), ".")) tok2 = tok2->astParent(); while (Token::Match(tok2, "[|.")) tok2 = tok2->astOperand1(); const Variable *var = tok2 ? tok2->variable() : nullptr; if (var) { if (var->isPointer()) continue; if (var->isArgument() && var->isReference()) continue; } } const bool error = std::none_of(minsizes->begin(), minsizes->end(), [=](const Library::ArgumentChecks::MinSize &minsize) { return checkBufferSize(tok, minsize, args, bufferSize.intvalue, mSettings); }); if (error) bufferOverflowError(args[argnr], &bufferSize, (bufferSize.intvalue == 1) ? Certainty::inconclusive : Certainty::normal); } } } } void CheckBufferOverrun::bufferOverflowError(const Token *tok, const ValueFlow::Value *value, const Certainty::CertaintyLevel &certainty) { reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? tok->expressionString() : "buf"), CWE_BUFFER_OVERRUN, certainty); } //--------------------------------------------------------------------------- void CheckBufferOverrun::arrayIndexThenCheck() { if (!mSettings->severity.isEnabled(Severity::portability)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * const scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { tok = tok->linkAt(1); continue; } if (Token::Match(tok, "%name% [ %var% ]")) { tok = tok->next(); const int indexID = tok->next()->varId(); const std::string& indexName(tok->strAt(1)); // Iterate AST upwards const Token* tok2 = tok; const Token* tok3 = tok2; while (tok2->astParent() && tok2->tokType() != Token::eLogicalOp && tok2->str() != "?") { tok3 = tok2; tok2 = tok2->astParent(); } // Ensure that we ended at a logical operator and that we came from its left side if (tok2->tokType() != Token::eLogicalOp || tok2->astOperand1() != tok3) continue; // check if array index is ok // statement can be closed in parentheses, so "(| " is using if (Token::Match(tok2, "&& (| %varid% <|<=", indexID)) arrayIndexThenCheckError(tok, indexName); else if (Token::Match(tok2, "&& (| %any% >|>= %varid% !!+", indexID)) arrayIndexThenCheckError(tok, indexName); } } } } void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName) { reportError(tok, Severity::style, "arrayIndexThenCheck", "$symbol:" + indexName + "\n" "Array index '$symbol' is used before limits check.\n" "Defensive programming: The variable '$symbol' is used as an array index before it " "is checked that is within limits. This can mean that the array might be accessed out of bounds. " "Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will " "not be accessed if the index is out of limits.", CWE_ARRAY_INDEX_THEN_CHECK, Certainty::normal); } //--------------------------------------------------------------------------- void CheckBufferOverrun::stringNotZeroTerminated() { // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3 if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * const scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "strncpy (")) continue; const std::vector args = getArguments(tok); if (args.size() != 3) continue; const Token *sizeToken = args[2]; if (!sizeToken->hasKnownIntValue()) continue; const ValueFlow::Value &bufferSize = getBufferSize(args[0]); if (bufferSize.intvalue < 0 || sizeToken->getKnownIntValue() < bufferSize.intvalue) continue; const Token *srcValue = args[1]->getValueTokenMaxStrLength(); if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue()) continue; // Is the buffer zero terminated after the call? bool isZeroTerminated = false; for (const Token *tok2 = tok->next()->link(); tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (!Token::simpleMatch(tok2, "] =")) continue; const Token *rhs = tok2->next()->astOperand2(); if (!rhs || !rhs->hasKnownIntValue() || rhs->getKnownIntValue() != 0) continue; if (isSameExpression(mTokenizer->isCPP(), false, args[0], tok2->link()->astOperand1(), mSettings->library, false, false)) isZeroTerminated = true; } if (isZeroTerminated) continue; // TODO: Locate unsafe string usage.. terminateStrncpyError(tok, args[0]->expressionString()); } } } void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname) { const std::string shortMessage = "The buffer '$symbol' may not be null-terminated after the call to strncpy()."; reportError(tok, Severity::warning, "terminateStrncpy", "$symbol:" + varname + '\n' + shortMessage + '\n' + shortMessage + ' ' + "If the source string's size fits or exceeds the given size, strncpy() does not add a " "zero at the end of the buffer. This causes bugs later in the code if the code " "assumes buffer is null-terminated.", CWE170, Certainty::inconclusive); } //--------------------------------------------------------------------------- void CheckBufferOverrun::argumentSize() { // Check '%type% x[10]' arguments if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * const scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!tok->function() || !Token::Match(tok, "%name% (")) continue; // If argument is '%type% a[num]' then check bounds against num const Function *callfunc = tok->function(); const std::vector callargs = getArguments(tok); for (nonneg int paramIndex = 0; paramIndex < callargs.size() && paramIndex < callfunc->argCount(); ++paramIndex) { const Variable* const argument = callfunc->getArgumentVar(paramIndex); if (!argument || !argument->nameToken() || !argument->isArray()) continue; if (!argument->valueType() || !callargs[paramIndex]->valueType()) continue; if (argument->valueType()->type != callargs[paramIndex]->valueType()->type) continue; const Token * calldata = callargs[paramIndex]; while (Token::Match(calldata, "::|.")) calldata = calldata->astOperand2(); if (!calldata->variable() || !calldata->variable()->isArray()) continue; if (calldata->variable()->dimensions().size() != argument->dimensions().size()) continue; bool err = false; for (int d = 0; d < argument->dimensions().size(); ++d) { const auto& dim1 = calldata->variable()->dimensions()[d]; const auto& dim2 = argument->dimensions()[d]; if (!dim1.known || !dim2.known) break; if (dim1.num < dim2.num) err = true; } if (err) argumentSizeError(tok, tok->str(), paramIndex, callargs[paramIndex]->expressionString(), calldata->variable(), argument); } } } } void CheckBufferOverrun::argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg) { const std::string strParamNum = std::to_string(paramIndex + 1) + getOrdinalText(paramIndex + 1); ErrorPath errorPath; errorPath.emplace_back(tok, "Function '" + functionName + "' is called"); if (functionArg) errorPath.emplace_back(functionArg->nameToken(), "Declaration of " + strParamNum + " function argument."); if (paramVar) errorPath.emplace_back(paramVar->nameToken(), "Passing buffer '" + paramVar->name() + "' to function that is declared here"); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::warning, "argumentSize", "$symbol:" + functionName + '\n' + "Buffer '" + paramExpression + "' is too small, the function '" + functionName + "' expects a bigger buffer in " + strParamNum + " argument", CWE_ARGUMENT_SIZE, Certainty::normal); } //--------------------------------------------------------------------------- // CTU.. //--------------------------------------------------------------------------- std::string CheckBufferOverrun::MyFileInfo::toString() const { std::string xml; if (!unsafeArrayIndex.empty()) xml = " \n" + CTU::toString(unsafeArrayIndex) + " \n"; if (!unsafePointerArith.empty()) xml += " \n" + CTU::toString(unsafePointerArith) + " \n"; return xml; } bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Check *check, const Token *argtok, MathLib::bigint *offset, int type) { const CheckBufferOverrun *c = dynamic_cast(check); if (!c) return false; if (!argtok->valueType() || argtok->valueType()->typeSize(*c->mSettings) == 0) return false; const Token *indexTok = nullptr; if (type == 1 && Token::Match(argtok, "%name% [") && argtok->astParent() == argtok->next() && !Token::simpleMatch(argtok->linkAt(1), "] [")) indexTok = argtok->next()->astOperand2(); else if (type == 2 && Token::simpleMatch(argtok->astParent(), "+")) indexTok = (argtok == argtok->astParent()->astOperand1()) ? argtok->astParent()->astOperand2() : argtok->astParent()->astOperand1(); if (!indexTok) return false; if (!indexTok->hasKnownIntValue()) return false; if (!offset) return false; *offset = indexTok->getKnownIntValue() * argtok->valueType()->typeSize(*c->mSettings); return true; } bool CheckBufferOverrun::isCtuUnsafeArrayIndex(const Check *check, const Token *argtok, MathLib::bigint *offset) { return CheckBufferOverrun::isCtuUnsafeBufferUsage(check, argtok, offset, 1); } bool CheckBufferOverrun::isCtuUnsafePointerArith(const Check *check, const Token *argtok, MathLib::bigint *offset) { return CheckBufferOverrun::isCtuUnsafeBufferUsage(check, argtok, offset, 2); } /** @brief Parse current TU and extract file info */ Check::FileInfo *CheckBufferOverrun::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { CheckBufferOverrun checkBufferOverrun(tokenizer, settings, nullptr); MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeArrayIndex = CTU::getUnsafeUsage(tokenizer, settings, &checkBufferOverrun, isCtuUnsafeArrayIndex); fileInfo->unsafePointerArith = CTU::getUnsafeUsage(tokenizer, settings, &checkBufferOverrun, isCtuUnsafePointerArith); if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) { delete fileInfo; return nullptr; } return fileInfo; } Check::FileInfo * CheckBufferOverrun::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { const std::string arrayIndex("array-index"); const std::string pointerArith("pointer-arith"); MyFileInfo *fileInfo = new MyFileInfo; for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (e->Name() == arrayIndex) fileInfo->unsafeArrayIndex = CTU::loadUnsafeUsageListFromXml(e); else if (e->Name() == pointerArith) fileInfo->unsafePointerArith = CTU::loadUnsafeUsageListFromXml(e); } if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) { delete fileInfo; return nullptr; } return fileInfo; } /** @brief Analyse all file infos for all TU */ bool CheckBufferOverrun::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { if (!ctu) return false; bool foundErrors = false; (void)settings; // This argument is unused const std::map> callsMap = ctu->getCallsMap(); for (Check::FileInfo *fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); if (!fi) continue; for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeArrayIndex) foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 1, errorLogger); for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafePointerArith) foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 2, errorLogger); } return foundErrors; } bool CheckBufferOverrun::analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger) { const CTU::FileInfo::FunctionCall *functionCall = nullptr; const std::list &locationList = CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::bufferOverflow, unsafeUsage, callsMap, "Using argument ARG", &functionCall, false); if (locationList.empty()) return false; const char *errorId = nullptr; std::string errmsg; CWE cwe(0); if (type == 1) { errorId = "ctuArrayIndex"; if (unsafeUsage.value > 0) errmsg = "Array index out of bounds; '" + unsafeUsage.myArgumentName + "' buffer size is " + MathLib::toString(functionCall->callArgValue) + " and it is accessed at offset " + MathLib::toString(unsafeUsage.value) + "."; else errmsg = "Array index out of bounds; buffer '" + unsafeUsage.myArgumentName + "' is accessed at offset " + MathLib::toString(unsafeUsage.value) + "."; cwe = (unsafeUsage.value > 0) ? CWE_BUFFER_OVERRUN : CWE_BUFFER_UNDERRUN; } else { errorId = "ctuPointerArith"; errmsg = "Pointer arithmetic overflow; '" + unsafeUsage.myArgumentName + "' buffer size is " + MathLib::toString(functionCall->callArgValue); cwe = CWE_POINTER_ARITHMETIC_OVERFLOW; } const ErrorMessage errorMessage(locationList, emptyString, Severity::error, errmsg, errorId, cwe, Certainty::normal); errorLogger.reportErr(errorMessage); return true; } void CheckBufferOverrun::objectIndex() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "[")) continue; const Token *obj = tok->astOperand1(); const Token *idx = tok->astOperand2(); if (!idx || !obj) continue; if (idx->hasKnownIntValue()) { if (idx->getKnownIntValue() == 0) continue; } if (idx->hasKnownIntValue() && idx->getKnownIntValue() == 0) continue; std::vector values = getLifetimeObjValues(obj, false, -1); for (const ValueFlow::Value& v:values) { if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) continue; const Variable *var = v.tokvalue->variable(); if (!var) continue; if (var->isReference()) continue; if (var->isRValueReference()) continue; if (var->isArray()) continue; if (var->isPointer()) { if (!var->valueType()) continue; if (!obj->valueType()) continue; if (var->valueType()->pointer > obj->valueType()->pointer) continue; } if (obj->valueType() && var->valueType() && (obj->isCast() || (mTokenizer->isCPP() && isCPPCast(obj)) || obj->valueType()->pointer)) { // allow cast to a different type const auto varSize = var->valueType()->typeSize(*mSettings); if (varSize == 0) continue; if (obj->valueType()->type != var->valueType()->type) { if (ValueFlow::isOutOfBounds(makeSizeValue(varSize, v.path), idx).empty()) continue; } } if (v.path != 0) { std::vector idxValues; std::copy_if(idx->values().begin(), idx->values().end(), std::back_inserter(idxValues), [&](const ValueFlow::Value& vidx) { if (!vidx.isIntValue()) return false; return vidx.path == v.path || vidx.path == 0; }); if (std::any_of(idxValues.begin(), idxValues.end(), [&](const ValueFlow::Value& vidx) { if (vidx.isImpossible()) return (vidx.intvalue == 0); else return (vidx.intvalue != 0); })) { objectIndexError(tok, &v, idx->hasKnownIntValue()); } } else { objectIndexError(tok, &v, idx->hasKnownIntValue()); } } } } } void CheckBufferOverrun::objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known) { ErrorPath errorPath; std::string name; if (v) { name = v->tokvalue->variable()->name(); errorPath = v->errorPath; } errorPath.emplace_back(tok, ""); std::string verb = known ? "is" : "might be"; reportError(errorPath, known ? Severity::error : Severity::warning, "objectIndex", "The address of local variable '" + name + "' " + verb + " accessed at non-zero index.", CWE758, Certainty::normal); } cppcheck-2.7/lib/checkbufferoverrun.h000066400000000000000000000152111417746362400177670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkbufferoverrunH #define checkbufferoverrunH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "ctu.h" #include "errortypes.h" #include "mathlib.h" #include "symboldatabase.h" #include "utils.h" #include "valueflow.h" #include #include #include #include namespace tinyxml2 { class XMLElement; } class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief buffer overruns and array index out of bounds * * Buffer overrun and array index out of bounds are pretty much the same. * But I generally use 'array index' if the code contains []. And the given * index is out of bounds. * I generally use 'buffer overrun' if you for example call a strcpy or * other function and pass a buffer and reads or writes too much data. */ class CPPCHECKLIB CheckBufferOverrun : public Check { public: /** This constructor is used when registering the CheckClass */ CheckBufferOverrun() : Check(myName()) {} /** This constructor is used when running checks. */ CheckBufferOverrun(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger); checkBufferOverrun.arrayIndex(); checkBufferOverrun.pointerArithmetic(); checkBufferOverrun.bufferOverflow(); checkBufferOverrun.arrayIndexThenCheck(); checkBufferOverrun.stringNotZeroTerminated(); checkBufferOverrun.objectIndex(); checkBufferOverrun.argumentSize(); } void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckBufferOverrun c(nullptr, settings, errorLogger); c.arrayIndexError(nullptr, std::vector(), std::vector()); c.pointerArithmeticError(nullptr, nullptr, nullptr); c.negativeIndexError(nullptr, std::vector(), std::vector()); c.arrayIndexThenCheckError(nullptr, "i"); c.bufferOverflowError(nullptr, nullptr, Certainty::normal); c.objectIndexError(nullptr, nullptr, true); c.argumentSizeError(nullptr, "function", 1, "buffer", nullptr, nullptr); } /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; private: void arrayIndex(); void arrayIndexError(const Token* tok, const std::vector& dimensions, const std::vector& indexes); void negativeIndexError(const Token* tok, const std::vector& dimensions, const std::vector& indexes); void pointerArithmetic(); void pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue); void bufferOverflow(); void bufferOverflowError(const Token *tok, const ValueFlow::Value *value, const Certainty::CertaintyLevel& certainty); void arrayIndexThenCheck(); void arrayIndexThenCheckError(const Token *tok, const std::string &indexName); void stringNotZeroTerminated(); void terminateStrncpyError(const Token *tok, const std::string &varname); void argumentSize(); void argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg); void objectIndex(); void objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known); ValueFlow::Value getBufferSize(const Token *bufTok) const; // CTU /** data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: /** unsafe array index usage */ std::list unsafeArrayIndex; /** unsafe pointer arithmetics */ std::list unsafePointerArith; /** Convert MyFileInfo data into xml string */ std::string toString() const OVERRIDE; }; static bool isCtuUnsafeBufferUsage(const Check *check, const Token *argtok, MathLib::bigint *offset, int type); static bool isCtuUnsafeArrayIndex(const Check *check, const Token *argtok, MathLib::bigint *offset); static bool isCtuUnsafePointerArith(const Check *check, const Token *argtok, MathLib::bigint *offset); Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const OVERRIDE; static bool analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger); static std::string myName() { return "Bounds checking"; } std::string classInfo() const OVERRIDE { return "Out of bounds checking:\n" "- Array index out of bounds\n" "- Pointer arithmetic overflow\n" "- Buffer overflow\n" "- Dangerous usage of strncat()\n" "- Using array index before checking it\n" "- Partial string write that leads to buffer that is not zero terminated.\n" "- Check for large enough arrays being passed to functions\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkbufferoverrunH cppcheck-2.7/lib/checkclass.cpp000066400000000000000000004104641417746362400165460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkclass.h" #include "astutils.h" #include "library.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "errorlogger.h" #include "errortypes.h" #include "mathlib.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include #include #include #include #include namespace CTU { class FileInfo; } //--------------------------------------------------------------------------- // Register CheckClass.. namespace { CheckClass instance; } static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE404(404U); // Improper Resource Shutdown or Release static const CWE CWE665(665U); // Improper Initialization static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE762(762U); // Mismatched Memory Management Routines static const CWE CWE_ONE_DEFINITION_RULE(758U); static const char * getFunctionTypeName(Function::Type type) { switch (type) { case Function::eConstructor: return "constructor"; case Function::eCopyConstructor: return "copy constructor"; case Function::eMoveConstructor: return "move constructor"; case Function::eDestructor: return "destructor"; case Function::eFunction: return "function"; case Function::eOperatorEqual: return "operator="; case Function::eLambda: return "lambda"; } return ""; } static bool isVariableCopyNeeded(const Variable &var) { return var.isPointer() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True) || (var.valueType() && var.valueType()->type >= ValueType::Type::CHAR); } static bool isVcl(const Settings *settings) { for (const std::string &library: settings->libraries) { if (library == "vcl") return true; } return false; } static bool isVclTypeInit(const Type *type) { if (!type) return false; for (const Type::BaseInfo &baseInfo: type->derivedFrom) { if (!baseInfo.type) return true; if (isVclTypeInit(baseInfo.type)) return true; } return false; } //--------------------------------------------------------------------------- CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr) {} //--------------------------------------------------------------------------- // ClassCheck: Check that all class constructors are ok. //--------------------------------------------------------------------------- void CheckClass::constructors() { const bool printStyle = mSettings->severity.isEnabled(Severity::style); const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); if (!printStyle && !printWarnings) return; const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { if (isVcl(mSettings) && isVclTypeInit(scope->definedType)) continue; const bool unusedTemplate = Token::simpleMatch(scope->classDef->previous(), ">"); bool usedInUnion = false; for (const Scope &unionScope : mSymbolDatabase->scopeList) { if (unionScope.type != Scope::eUnion) continue; for (const Variable &var : unionScope.varlist) { if (var.type() && var.type()->classScope == scope) { usedInUnion = true; break; } } } // There are no constructors. if (scope->numConstructors == 0 && printStyle && !usedInUnion) { // If there is a private variable, there should be a constructor.. for (const Variable &var : scope->varlist) { if (var.isPrivate() && !var.isStatic() && !var.isInit() && (!var.isClass() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True))) { noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct"); break; } } } if (!printWarnings) continue; // #3196 => bailout if there are nested unions // TODO: handle union variables better { bool bailout = false; for (const Scope * const nestedScope : scope->nestedList) { if (nestedScope->type == Scope::eUnion) { bailout = true; break; } } if (bailout) continue; } std::vector usageList = createUsageList(scope); for (const Function &func : scope->functionList) { if (!func.hasBody() || !(func.isConstructor() || func.type == Function::eOperatorEqual)) continue; // Bail: If initializer list is not recognized as a variable or type then skip since parsing is incomplete if (unusedTemplate && func.type == Function::eConstructor) { const Token *initList = func.constructorMemberInitialization(); if (Token::Match(initList, ": %name% (") && initList->next()->tokType() == Token::eName) break; } // Mark all variables not used clearAllVar(usageList); std::list callstack; initializeVarList(func, callstack, scope, usageList); // Check if any variables are uninitialized for (Usage &usage : usageList) { const Variable& var = *usage.var; // check for C++11 initializer if (var.hasDefault()) { usage.init = true; continue; } if (usage.assign || usage.init || var.isStatic()) continue; if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty()) continue; if (var.isConst() && func.isOperator()) // We can't set const members in assignment operator continue; // Check if this is a class constructor if (!var.isPointer() && !var.isPointerArray() && var.isClass() && func.type == Function::eConstructor) { // Unknown type so assume it is initialized if (!var.type()) continue; // Known type that doesn't need initialization or // known type that has member variables of an unknown type else if (var.type()->needInitialization != Type::NeedInitialization::True) continue; } // Check if type can't be copied if (!var.isPointer() && !var.isPointerArray() && var.typeScope()) { if (func.type == Function::eMoveConstructor) { if (canNotMove(var.typeScope())) continue; } else { if (canNotCopy(var.typeScope())) continue; } } // Is there missing member copy in copy/move constructor or assignment operator? bool missingCopy = false; // Don't warn about unknown types in copy constructors since we // don't know if they can be copied or not.. if ((func.type == Function::eCopyConstructor || func.type == Function::eMoveConstructor || func.type == Function::eOperatorEqual) && !isVariableCopyNeeded(var)) { if (!printInconclusive) continue; missingCopy = true; } // It's non-static and it's not initialized => error if (func.type == Function::eOperatorEqual) { const Token *operStart = func.arg; bool classNameUsed = false; for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next()) { if (operTok->str() == scope->className) { classNameUsed = true; break; } } if (classNameUsed) operatorEqVarError(func.token, scope->className, var.name(), missingCopy); } else if (func.access != AccessControl::Private || mSettings->standards.cpp >= Standards::CPP11) { // If constructor is not in scope then we maybe using a constructor from a different template specialization if (!precedes(scope->bodyStart, func.tokenDef)) continue; const Scope *varType = var.typeScope(); if (!varType || varType->type != Scope::eUnion) { const bool derived = scope != var.scope(); if (func.type == Function::eConstructor && func.nestedIn && (func.nestedIn->numConstructors - func.nestedIn->numCopyOrMoveConstructors) > 1 && func.argCount() == 0 && func.functionScope && func.arg && func.arg->link()->next() == func.functionScope->bodyStart && func.functionScope->bodyStart->link() == func.functionScope->bodyStart->next()) { // don't warn about user defined default constructor when there are other constructors if (printInconclusive) uninitVarError(func.token, func.access == AccessControl::Private, var.scope()->className, var.name(), derived, true); } else if (missingCopy) missingMemberCopyError(func.token, var.scope()->className, var.name()); else uninitVarError(func.token, func.access == AccessControl::Private, var.scope()->className, var.name(), derived, false); } } } } } } void CheckClass::checkExplicitConstructors() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // Do not perform check, if the class/struct has not any constructors if (scope->numConstructors == 0) continue; // Is class abstract? Maybe this test is over-simplification, but it will suffice for simple cases, // and it will avoid false positives. bool isAbstractClass = false; for (const Function &func : scope->functionList) { if (func.isPure()) { isAbstractClass = true; break; } } // Abstract classes can't be instantiated. But if there is C++11 // "misuse" by derived classes then these constructors must be explicit. if (isAbstractClass && mSettings->standards.cpp >= Standards::CPP11) continue; for (const Function &func : scope->functionList) { // We are looking for constructors, which are meeting following criteria: // 1) Constructor is declared with a single parameter // 2) Constructor is not declared as explicit // 3) It is not a copy/move constructor of non-abstract class // 4) Constructor is not marked as delete (programmer can mark the default constructor as deleted, which is ok) if (!func.isConstructor() || func.isDelete() || (!func.hasBody() && func.access == AccessControl::Private)) continue; if (!func.isExplicit() && func.minArgCount() == 1 && func.type != Function::eCopyConstructor && func.type != Function::eMoveConstructor) { noExplicitConstructorError(func.tokenDef, scope->className, scope->type == Scope::eStruct); } } } } static bool hasNonCopyableBase(const Scope *scope, bool *unknown) { // check if there is base class that is not copyable for (const Type::BaseInfo &baseInfo : scope->definedType->derivedFrom) { if (!baseInfo.type || !baseInfo.type->classScope) { *unknown = true; continue; } if (hasNonCopyableBase(baseInfo.type->classScope, unknown)) return true; for (const Function &func : baseInfo.type->classScope->functionList) { if (func.type != Function::eCopyConstructor) continue; if (func.access == AccessControl::Private || func.isDelete()) { *unknown = false; return true; } } } return false; } void CheckClass::copyconstructors() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { std::map allocatedVars; for (const Function &func : scope->functionList) { if (func.type != Function::eConstructor || !func.functionScope) continue; const Token* tok = func.token->linkAt(1); for (const Token* const end = func.functionScope->bodyStart; tok != end; tok = tok->next()) { if (Token::Match(tok, "%var% ( new") || (Token::Match(tok, "%var% ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); if (var && var->isPointer() && var->scope() == scope) allocatedVars[tok->varId()] = tok; } } for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) { if (Token::Match(tok, "%var% = new") || (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); if (var && var->isPointer() && var->scope() == scope && !var->isStatic()) allocatedVars[tok->varId()] = tok; } } } if (!allocatedVars.empty()) { const Function *funcCopyCtor = nullptr; const Function *funcOperatorEq = nullptr; const Function *funcDestructor = nullptr; for (const Function &func : scope->functionList) { if (func.type == Function::eCopyConstructor) funcCopyCtor = &func; else if (func.type == Function::eOperatorEqual) funcOperatorEq = &func; else if (func.type == Function::eDestructor) funcDestructor = &func; } if (!funcCopyCtor || funcCopyCtor->isDefault()) { bool unknown = false; if (!hasNonCopyableBase(scope, &unknown) && !unknown) noCopyConstructorError(scope, funcCopyCtor, allocatedVars.begin()->second, unknown); } if (!funcOperatorEq || funcOperatorEq->isDefault()) { bool unknown = false; if (!hasNonCopyableBase(scope, &unknown) && !unknown) noOperatorEqError(scope, funcOperatorEq, allocatedVars.begin()->second, unknown); } if (!funcDestructor || funcDestructor->isDefault()) { const Token * mustDealloc = nullptr; for (std::map::const_iterator it = allocatedVars.begin(); it != allocatedVars.end(); ++it) { if (!Token::Match(it->second, "%var% [(=] new %type%")) { mustDealloc = it->second; break; } if (it->second->valueType() && it->second->valueType()->isIntegral()) { mustDealloc = it->second; break; } const Variable *var = it->second->variable(); if (var && var->typeScope() && var->typeScope()->functionList.empty() && var->type()->derivedFrom.empty()) { mustDealloc = it->second; break; } } if (mustDealloc) noDestructorError(scope, funcDestructor, mustDealloc); } } std::set copiedVars; const Token* copyCtor = nullptr; for (const Function &func : scope->functionList) { if (func.type != Function::eCopyConstructor) continue; copyCtor = func.tokenDef; if (!func.functionScope) { allocatedVars.clear(); break; } const Token* tok = func.tokenDef->linkAt(1)->next(); if (tok->str()==":") { tok=tok->next(); while (Token::Match(tok, "%name% (")) { if (allocatedVars.find(tok->varId()) != allocatedVars.end()) { if (tok->varId() && Token::Match(tok->tokAt(2), "%name% . %name% )")) copiedVars.insert(tok); else if (!Token::Match(tok->tokAt(2), "%any% )")) allocatedVars.erase(tok->varId()); // Assume memory is allocated } tok = tok->linkAt(1)->tokAt(2); } } for (tok = func.functionScope->bodyStart; tok != func.functionScope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) { allocatedVars.erase(tok->varId()); } else if (Token::Match(tok, "%var% = %name% . %name% ;") && allocatedVars.find(tok->varId()) != allocatedVars.end()) { copiedVars.insert(tok); } } break; } if (copyCtor && !copiedVars.empty()) { for (const Token *cv : copiedVars) copyConstructorShallowCopyError(cv, cv->str()); // throw error if count mismatch /* FIXME: This doesn't work. See #4154 for (std::map::const_iterator i = allocatedVars.begin(); i != allocatedVars.end(); ++i) { copyConstructorMallocError(copyCtor, i->second, i->second->str()); } */ } } } /* This doesn't work. See #4154 void CheckClass::copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& varname) { std::list callstack; callstack.push_back(cctor); callstack.push_back(alloc); reportError(callstack, Severity::warning, "copyCtorNoAllocation", "Copy constructor does not allocate memory for member '" + varname + "' although memory has been allocated in other constructors."); } */ void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname) { reportError(tok, Severity::warning, "copyCtorPointerCopying", "$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, Certainty::normal); } static std::string noMemberErrorMessage(const Scope *scope, const char function[], bool isdefault) { const std::string &classname = scope ? scope->className : "class"; const std::string type = (scope && scope->type == Scope::eStruct) ? "Struct" : "Class"; const bool isDestructor = (function[0] == 'd'); std::string errmsg = "$symbol:" + classname + '\n'; if (isdefault) { errmsg += type + " '$symbol' has dynamic memory/resource allocation(s). The " + function + " is explicitly defaulted but the default " + function + " does not work well."; if (isDestructor) errmsg += " It is recommended to define the " + std::string(function) + '.'; else errmsg += " It is recommended to define or delete the " + std::string(function) + '.'; } else { errmsg += type + " '$symbol' does not have a " + function + " which is recommended since it has dynamic memory/resource allocation(s)."; } return errmsg; } void CheckClass::noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) { reportError(alloc, Severity::warning, "noCopyConstructor", noMemberErrorMessage(scope, "copy constructor", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckClass::noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) { reportError(alloc, Severity::warning, "noOperatorEq", noMemberErrorMessage(scope, "operator=", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckClass::noDestructorError(const Scope *scope, bool isdefault, const Token *alloc) { reportError(alloc, Severity::warning, "noDestructor", noMemberErrorMessage(scope, "destructor", isdefault), CWE398, Certainty::normal); } bool CheckClass::canNotCopy(const Scope *scope) { bool constructor = false; bool publicAssign = false; bool publicCopy = false; for (const Function &func : scope->functionList) { if (func.isConstructor()) constructor = true; if (func.access != AccessControl::Public) continue; if (func.type == Function::eCopyConstructor) { publicCopy = true; break; } else if (func.type == Function::eOperatorEqual) { publicAssign = true; break; } } return constructor && !(publicAssign || publicCopy); } bool CheckClass::canNotMove(const Scope *scope) { bool constructor = false; bool publicAssign = false; bool publicCopy = false; bool publicMove = false; for (const Function &func : scope->functionList) { if (func.isConstructor()) constructor = true; if (func.access != AccessControl::Public) continue; if (func.type == Function::eCopyConstructor) { publicCopy = true; break; } else if (func.type == Function::eMoveConstructor) { publicMove = true; break; } else if (func.type == Function::eOperatorEqual) { publicAssign = true; break; } } return constructor && !(publicAssign || publicCopy || publicMove); } static void getAllVariableMembers(const Scope *scope, std::vector& varList) { for (const Variable& var: scope->varlist) varList.push_back(&var); if (scope->definedType) { for (const Type::BaseInfo& baseInfo: scope->definedType->derivedFrom) { if (scope->definedType == baseInfo.type) continue; const Scope *baseClass = baseInfo.type ? baseInfo.type->classScope : nullptr; if (baseClass && baseClass->isClassOrStruct() && baseClass->numConstructors == 0) getAllVariableMembers(baseClass, varList); } } } std::vector CheckClass::createUsageList(const Scope *scope) { std::vector ret; std::vector varlist; getAllVariableMembers(scope, varlist); ret.reserve(varlist.size()); for (const Variable *var: varlist) ret.emplace_back(var); return ret; } void CheckClass::assignVar(std::vector &usageList, nonneg int varid) { for (Usage& usage: usageList) { if (usage.var->declarationId() == varid) { usage.assign = true; return; } } } void CheckClass::initVar(std::vector &usageList, nonneg int varid) { for (Usage& usage: usageList) { if (usage.var->declarationId() == varid) { usage.init = true; return; } } } void CheckClass::assignAllVar(std::vector &usageList) { for (Usage & i : usageList) i.assign = true; } void CheckClass::clearAllVar(std::vector &usageList) { for (Usage & i : usageList) { i.assign = false; i.init = false; } } bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope) { // Iterate through each base class... for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { const Type *derivedFrom = i.type; // Check if base class exists in database if (derivedFrom && derivedFrom->classScope) { const std::list& functionList = derivedFrom->classScope->functionList; for (const Function &func : functionList) { if (func.tokenDef->str() == tok->str()) return true; } } // Base class not found so assume it is in it. else return true; } return false; } void CheckClass::initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage) { if (!func.functionScope) throw InternalError(nullptr, "Internal Error: Invalid syntax"); // #5702 bool initList = func.isConstructor(); const Token *ftok = func.arg->link()->next(); int level = 0; for (; ftok && ftok != func.functionScope->bodyEnd; ftok = ftok->next()) { // Class constructor.. initializing variables like this // clKalle::clKalle() : var(value) { } if (initList) { if (level == 0 && Token::Match(ftok, "%name% {|(") && Token::Match(ftok->linkAt(1), "}|) ,|{")) { if (ftok->str() != func.name()) { initVar(usage, ftok->varId()); } else { // c++11 delegate constructor const Function *member = ftok->function(); // member function not found => assume it initializes all members if (!member) { assignAllVar(usage); return; } // recursive call // assume that all variables are initialized if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { /** @todo false negative: just bail */ assignAllVar(usage); return; } // member function has implementation if (member->hasBody()) { // initialize variable use list using member function callstack.push_back(member); initializeVarList(*member, callstack, scope, usage); callstack.pop_back(); } // there is a called member function, but it has no implementation, so we assume it initializes everything else { assignAllVar(usage); } } } else if (level != 0 && Token::Match(ftok, "%name% =")) // assignment in the initializer: var(value = x) assignVar(usage, ftok->varId()); // Level handling if (ftok->link() && Token::Match(ftok, "(|<")) level++; else if (ftok->str() == "{") { if (level != 0 || (Token::Match(ftok->previous(), "%name%|>") && Token::Match(ftok->link(), "} ,|{"))) level++; else initList = false; } else if (ftok->link() && Token::Match(ftok, ")|>|}")) level--; } if (initList) continue; // Variable getting value from stream? if (Token::Match(ftok, ">>|& %name%") && isLikelyStreamRead(true, ftok)) { assignVar(usage, ftok->next()->varId()); } // If assignment comes after an && or || this is really inconclusive because of short circuiting if (Token::Match(ftok, "%oror%|&&")) continue; if (Token::simpleMatch(ftok, "( !")) ftok = ftok->next(); // Using the operator= function to initialize all variables.. if (Token::Match(ftok->next(), "return| (| * this )| =")) { assignAllVar(usage); break; } // Using swap to assign all variables.. if (func.type == Function::eOperatorEqual && Token::Match(ftok, "[;{}] %name% (") && Token::Match(ftok->linkAt(2), ") . %name% ( *| this ) ;")) { assignAllVar(usage); break; } // Calling member variable function? if (Token::Match(ftok->next(), "%var% . %name% (")) { for (const Variable &var : scope->varlist) { if (var.declarationId() == ftok->next()->varId()) { /** @todo false negative: we assume function changes variable state */ assignVar(usage, ftok->next()->varId()); break; } } ftok = ftok->tokAt(2); } if (!Token::Match(ftok->next(), "::| %name%") && !Token::Match(ftok->next(), "*| this . %name%") && !Token::Match(ftok->next(), "* %name% =") && !Token::Match(ftok->next(), "( * this ) . %name%")) continue; // Goto the first token in this statement.. ftok = ftok->next(); // skip "return" if (ftok->str() == "return") ftok = ftok->next(); // Skip "( * this )" if (Token::simpleMatch(ftok, "( * this ) .")) { ftok = ftok->tokAt(5); } // Skip "this->" if (Token::simpleMatch(ftok, "this .")) ftok = ftok->tokAt(2); // Skip "classname :: " if (Token::Match(ftok, ":: %name%")) ftok = ftok->next(); while (Token::Match(ftok, "%name% ::")) ftok = ftok->tokAt(2); // Clearing all variables.. if (Token::Match(ftok, "::| memset ( this ,")) { assignAllVar(usage); return; } // Ticket #7068 else if (Token::Match(ftok, "::| memset ( &| this . %name%")) { if (ftok->str() == "::") ftok = ftok->next(); int offsetToMember = 4; if (ftok->strAt(2) == "&") ++offsetToMember; assignVar(usage, ftok->tokAt(offsetToMember)->varId()); ftok = ftok->linkAt(1); continue; } // Clearing array.. else if (Token::Match(ftok, "::| memset ( %name% ,")) { if (ftok->str() == "::") ftok = ftok->next(); assignVar(usage, ftok->tokAt(2)->varId()); ftok = ftok->linkAt(1); continue; } // Calling member function? else if (Token::simpleMatch(ftok, "operator= (") && ftok->previous()->str() != "::") { if (ftok->function() && ftok->function()->nestedIn == scope) { const Function *member = ftok->function(); // recursive call // assume that all variables are initialized if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { /** @todo false negative: just bail */ assignAllVar(usage); return; } // member function has implementation if (member->hasBody()) { // initialize variable use list using member function callstack.push_back(member); initializeVarList(*member, callstack, scope, usage); callstack.pop_back(); } // there is a called member function, but it has no implementation, so we assume it initializes everything else { assignAllVar(usage); } } // using default operator =, assume everything initialized else { assignAllVar(usage); } } else if (Token::Match(ftok, "::| %name% (") && !Token::Match(ftok, "if|while|for")) { if (ftok->str() == "::") ftok = ftok->next(); // Passing "this" => assume that everything is initialized for (const Token *tok2 = ftok->next()->link(); tok2 && tok2 != ftok; tok2 = tok2->previous()) { if (tok2->str() == "this") { assignAllVar(usage); return; } } // check if member function if (ftok->function() && ftok->function()->nestedIn == scope && !ftok->function()->isConstructor()) { const Function *member = ftok->function(); // recursive call // assume that all variables are initialized if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { assignAllVar(usage); return; } // member function has implementation if (member->hasBody()) { // initialize variable use list using member function callstack.push_back(member); initializeVarList(*member, callstack, scope, usage); callstack.pop_back(); // Assume that variables that are passed to it are initialized.. for (const Token *tok2 = ftok; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}]")) break; if (Token::Match(tok2, "[(,] &| %name% [,)]")) { tok2 = tok2->next(); if (tok2->str() == "&") tok2 = tok2->next(); assignVar(usage, tok2->varId()); } } } // there is a called member function, but it has no implementation, so we assume it initializes everything else { assignAllVar(usage); } } // not member function else { // could be a base class virtual function, so we assume it initializes everything if (!func.isConstructor() && isBaseClassFunc(ftok, scope)) { /** @todo False Negative: we should look at the base class functions to see if they * call any derived class virtual functions that change the derived class state */ assignAllVar(usage); } // has friends, so we assume it initializes everything if (!scope->definedType->friendList.empty()) assignAllVar(usage); // the function is external and it's neither friend nor inherited virtual function. // assume all variables that are passed to it are initialized.. else { for (const Token *tok = ftok->tokAt(2); tok && tok != ftok->next()->link(); tok = tok->next()) { if (tok->isName()) { assignVar(usage, tok->varId()); } } } } } // Assignment of member variable? else if (Token::Match(ftok, "%name% =")) { assignVar(usage, ftok->varId()); bool bailout = ftok->variable() && ftok->variable()->isReference(); const Token* tok2 = ftok->tokAt(2); if (tok2->str() == "&") { tok2 = tok2->next(); bailout = true; } if (tok2->variable() && (bailout || tok2->variable()->isArray()) && tok2->strAt(1) != "[") assignVar(usage, tok2->varId()); } // Assignment of array item of member variable? else if (Token::Match(ftok, "%name% [|.")) { const Token *tok2 = ftok; while (tok2) { if (tok2->strAt(1) == "[") tok2 = tok2->next()->link(); else if (Token::Match(tok2->next(), ". %name%")) tok2 = tok2->tokAt(2); else break; } if (tok2 && tok2->strAt(1) == "=") assignVar(usage, ftok->varId()); } // Assignment of array item of member variable? else if (Token::Match(ftok, "* %name% =")) { assignVar(usage, ftok->next()->varId()); } else if (Token::Match(ftok, "* this . %name% =")) { assignVar(usage, ftok->tokAt(3)->varId()); } // The functions 'clear' and 'Clear' are supposed to initialize variable. if (Token::Match(ftok, "%name% . clear|Clear (")) { assignVar(usage, ftok->varId()); } } } void CheckClass::noConstructorError(const Token *tok, const std::string &classname, bool isStruct) { // For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning" reportError(tok, Severity::style, "noConstructor", "$symbol:" + classname + "\n" + "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not declare a constructor although it has private member variables which likely require initialization.\n" "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not declare a constructor " "although it has private member variables. Member variables of builtin types are left " "uninitialized when the class is instantiated. That may cause bugs or undefined behavior.", CWE398, Certainty::normal); } void CheckClass::noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct) { const std::string message(std::string(isStruct ? "Struct" : "Class") + " '$symbol' has a constructor with 1 argument that is not explicit."); const std::string verbose(message + " Such constructors should in general be explicit for type safety reasons. Using the explicit keyword in the constructor means some mistakes when using the class can be avoided."); reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal); } void CheckClass::uninitVarError(const Token *tok, bool isprivate, const std::string &classname, const std::string &varname, bool derived, bool inconclusive) { std::string message; message = "Member variable '$symbol' is not initialized in the constructor."; if (derived) message += " Maybe it should be initialized directly in the class " + classname + "?"; std::string id = std::string("uninit") + (derived ? "Derived" : "") + "MemberVar" + (isprivate ? "Private" : ""); reportError(tok, Severity::warning, id, "$symbol:" + classname + "::" + varname + "\n" + message, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckClass::missingMemberCopyError(const Token *tok, const std::string& classname, const std::string& varname) { const std::string message = "$symbol:" + classname + "::" + varname + "\n" + "Member variable '$symbol' is not assigned in the copy constructor. Should it be copied?"; const char id[] = "missingMemberCopy"; reportError(tok, Severity::warning, id, message, CWE398, Certainty::inconclusive); } void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive) { reportError(tok, Severity::warning, "operatorEqVarError", "$symbol:" + classname + "::" + varname + "\nMember variable '$symbol' is not assigned a value in '" + classname + "::operator='.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- // ClassCheck: Use initialization list instead of assignment //--------------------------------------------------------------------------- void CheckClass::initializationListUsage() { if (!mSettings->severity.isEnabled(Severity::performance)) return; for (const Scope *scope : mSymbolDatabase->functionScopes) { // Check every constructor if (!scope->function || !scope->function->isConstructor()) continue; // Do not warn when a delegate constructor is called if (const Token *initList = scope->function->constructorMemberInitialization()) { if (Token::Match(initList, ": %name% {|(") && initList->strAt(1) == scope->className) continue; } const Scope* owner = scope->functionOf; for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%name% (")) // Assignments might depend on this function call or if/for/while/switch statement from now on. break; if (Token::Match(tok, "try|do {")) break; if (!Token::Match(tok, "%var% =") || tok->strAt(-1) == "*" || tok->strAt(-1) == ".") continue; const Variable* var = tok->variable(); if (!var || var->scope() != owner || var->isStatic()) continue; if (var->isPointer() || var->isReference() || var->isEnumType()) continue; if (!WRONG_DATA(!var->valueType(), tok) && var->valueType()->type > ValueType::Type::ITERATOR) continue; // bailout: multi line lambda in rhs => do not warn if (findLambdaEndToken(tok->tokAt(2)) && tok->tokAt(2)->findExpressionStartEndTokens().second->linenr() > tok->tokAt(2)->linenr()) continue; // Access local var member in rhs => do not warn bool localmember = false; visitAstNodes(tok->next()->astOperand2(), [&](const Token *rhs) { if (rhs->str() == "." && rhs->astOperand1() && rhs->astOperand1()->variable() && rhs->astOperand1()->variable()->isLocal()) localmember = true; return ChildrenToVisit::op1_and_op2; }); if (localmember) continue; bool allowed = true; visitAstNodes(tok->next()->astOperand2(), [&](const Token *tok2) { const Variable* var2 = tok2->variable(); if (var2) { if (var2->scope() == owner && tok2->strAt(-1)!=".") { // Is there a dependency between two member variables? allowed = false; return ChildrenToVisit::done; } else if (var2->isArray() && var2->isLocal()) { // Can't initialize with a local array allowed = false; return ChildrenToVisit::done; } } else if (tok2->str() == "this") { // 'this' instance is not completely constructed in initialization list allowed = false; return ChildrenToVisit::done; } else if (Token::Match(tok2, "%name% (") && tok2->strAt(-1) != "." && isMemberFunc(owner, tok2)) { // Member function called? allowed = false; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (!allowed) continue; suggestInitializationList(tok, tok->str()); } } } void CheckClass::suggestInitializationList(const Token* tok, const std::string& varname) { reportError(tok, Severity::performance, "useInitializationList", "$symbol:" + varname + "\nVariable '$symbol' is assigned in constructor body. Consider performing initialization in initialization list.\n" "When an object of a class is created, the constructors of all member variables are called consecutively " "in the order the variables are declared, even if you don't explicitly write them to the initialization list. You " "could avoid assigning '$symbol' a value by passing the value to the constructor in the initialization list.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // ClassCheck: Unused private functions //--------------------------------------------------------------------------- static bool checkFunctionUsage(const Function *privfunc, const Scope* scope) { if (!scope) return true; // Assume it is used, if scope is not seen for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->functionScope) { if (Token::Match(func->tokenDef, "%name% (")) { for (const Token *ftok = func->tokenDef->tokAt(2); ftok && ftok->str() != ")"; ftok = ftok->next()) { if (Token::Match(ftok, "= %name% [(,)]") && ftok->strAt(1) == privfunc->name()) return true; if (ftok->str() == "(") ftok = ftok->link(); } } for (const Token *ftok = func->functionScope->classDef->linkAt(1); ftok != func->functionScope->bodyEnd; ftok = ftok->next()) { if (ftok->function() == privfunc) return true; if (ftok->varId() == 0U && ftok->str() == privfunc->name()) // TODO: This condition should be redundant return true; } } else if ((func->type != Function::eCopyConstructor && func->type != Function::eOperatorEqual) || func->access != AccessControl::Private) // Assume it is used, if a function implementation isn't seen, but empty private copy constructors and assignment operators are OK return true; } const std::map::const_iterator end = scope->definedTypesMap.end(); for (std::map::const_iterator iter = scope->definedTypesMap.begin(); iter != end; ++iter) { const Type *type = (*iter).second; if (type->enclosingScope == scope && checkFunctionUsage(privfunc, type->classScope)) return true; } for (const Variable &var : scope->varlist) { if (var.isStatic()) { const Token* tok = Token::findmatch(scope->bodyEnd, "%varid% =|(|{", var.declarationId()); if (tok) tok = tok->tokAt(2); while (tok && tok->str() != ";") { if (tok->function() == privfunc) return true; tok = tok->next(); } } } return false; // Unused in this scope } void CheckClass::privateFunctions() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // do not check borland classes with properties.. if (Token::findsimplematch(scope->bodyStart, "; __property ;", scope->bodyEnd)) continue; std::list privateFuncs; for (const Function &func : scope->functionList) { // Get private functions.. if (func.type == Function::eFunction && func.access == AccessControl::Private && !func.isOperator()) // TODO: There are smarter ways to check private operator usage privateFuncs.push_back(&func); } // Bailout for overridden virtual functions of base classes if (!scope->definedType->derivedFrom.empty()) { // Check virtual functions for (std::list::iterator it = privateFuncs.begin(); it != privateFuncs.end();) { if ((*it)->isImplicitlyVirtual(true)) // Give true as default value to be returned if we don't see all base classes privateFuncs.erase(it++); else ++it; } } while (!privateFuncs.empty()) { // Check that all private functions are used bool used = checkFunctionUsage(privateFuncs.front(), scope); // Usage in this class // Check in friend classes const std::vector& friendList = scope->definedType->friendList; for (int i = 0; i < friendList.size() && !used; i++) { if (friendList[i].type) used = checkFunctionUsage(privateFuncs.front(), friendList[i].type->classScope); else used = true; // Assume, it is used if we do not see friend class } if (!used) unusedPrivateFunctionError(privateFuncs.front()->tokenDef, scope->className, privateFuncs.front()->name()); privateFuncs.pop_front(); } } } void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) { reportError(tok, Severity::style, "unusedPrivateFunction", "$symbol:" + classname + "::" + funcname + "\nUnused private function: '$symbol'", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // ClassCheck: Check that memset is not used on classes //--------------------------------------------------------------------------- static const Scope* findFunctionOf(const Scope* scope) { while (scope) { if (scope->type == Scope::eFunction) return scope->functionOf; scope = scope->nestedIn; } return nullptr; } void CheckClass::checkMemset() { const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); for (const Scope *scope : mSymbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "memset|memcpy|memmove (")) { const Token* arg1 = tok->tokAt(2); const Token* arg3 = arg1->nextArgument(); if (arg3) arg3 = arg3->nextArgument(); if (!arg3) // weird, shouldn't happen: memset etc should have // 3 arguments. continue; const Token *typeTok = nullptr; const Scope *type = nullptr; if (Token::Match(arg3, "sizeof ( %type% ) )")) typeTok = arg3->tokAt(2); else if (Token::Match(arg3, "sizeof ( %type% :: %type% ) )")) typeTok = arg3->tokAt(4); else if (Token::Match(arg3, "sizeof ( struct %type% ) )")) typeTok = arg3->tokAt(3); else if (Token::simpleMatch(arg3, "sizeof ( * this ) )") || Token::simpleMatch(arg1, "this ,")) { type = findFunctionOf(arg3->scope()); } else if (Token::Match(arg1, "&|*|%var%")) { int numIndirToVariableType = 0; // Offset to the actual type in terms of dereference/addressof for (;; arg1 = arg1->next()) { if (arg1->str() == "&") ++numIndirToVariableType; else if (arg1->str() == "*") --numIndirToVariableType; else break; } const Variable * const var = arg1->variable(); if (var && arg1->strAt(1) == ",") { if (var->isArrayOrPointer()) { const Token *endTok = var->typeEndToken(); while (Token::simpleMatch(endTok, "*")) { ++numIndirToVariableType; endTok = endTok->previous(); } } if (var->isArray()) numIndirToVariableType += int(var->dimensions().size()); if (numIndirToVariableType == 1) type = var->typeScope(); } } // No type defined => The tokens didn't match if (!typeTok && !type) continue; if (typeTok && typeTok->str() == "(") typeTok = typeTok->next(); if (!type && typeTok->type()) type = typeTok->type()->classScope; if (type) { const std::set parsedTypes; checkMemsetType(scope, tok, type, false, parsedTypes); } } else if (tok->variable() && tok->variable()->typeScope() && Token::Match(tok, "%var% = calloc|malloc|realloc|g_malloc|g_try_malloc|g_realloc|g_try_realloc (")) { const std::set parsedTypes; checkMemsetType(scope, tok->tokAt(2), tok->variable()->typeScope(), true, parsedTypes); if (printWarnings && tok->variable()->typeScope()->numConstructors > 0) mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->typeScope()->classDef); } } } } void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes) { // If type has been checked there is no need to check it again if (parsedTypes.find(type) != parsedTypes.end()) return; parsedTypes.insert(type); const bool printPortability = mSettings->severity.isEnabled(Severity::portability); // recursively check all parent classes for (const Type::BaseInfo & i : type->definedType->derivedFrom) { const Type* derivedFrom = i.type; if (derivedFrom && derivedFrom->classScope) checkMemsetType(start, tok, derivedFrom->classScope, allocation, parsedTypes); } // Warn if type is a class that contains any virtual functions for (const Function &func : type->functionList) { if (func.hasVirtualSpecifier()) { if (allocation) mallocOnClassError(tok, tok->str(), type->classDef, "virtual function"); else memsetError(tok, tok->str(), "virtual function", type->classDef->str()); } } // Warn if type is a class or struct that contains any std::* variables for (const Variable &var : type->varlist) { if (var.isReference() && !var.isStatic()) { memsetErrorReference(tok, tok->str(), type->classDef->str()); continue; } // don't warn if variable static or const, pointer or array of pointers if (!var.isStatic() && !var.isConst() && !var.isPointer() && (!var.isArray() || var.typeEndToken()->str() != "*")) { const Token *tok1 = var.typeStartToken(); const Scope *typeScope = var.typeScope(); std::string typeName; if (Token::Match(tok1, "%type% ::")) { const Token *typeTok = tok1; while (Token::Match(typeTok, "%type% ::")) { typeName += typeTok->str() + "::"; typeTok = typeTok->tokAt(2); } typeName += typeTok->str(); } // check for std:: type if (var.isStlType() && typeName != "std::array" && !mSettings->library.podtype(typeName)) { if (allocation) mallocOnClassError(tok, tok->str(), type->classDef, "'" + typeName + "'"); else memsetError(tok, tok->str(), "'" + typeName + "'", type->classDef->str()); } // check for known type else if (typeScope && typeScope != type) checkMemsetType(start, tok, typeScope, allocation, parsedTypes); // check for float else if (printPortability && var.isFloatingType() && tok->str() == "memset") memsetErrorFloat(tok, type->classDef->str()); } } } void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok) { std::list toks = { tok, classTok }; reportError(toks, Severity::warning, "mallocOnClassWarning", "$symbol:" + memfunc +"\n" "Memory for class instance allocated with $symbol(), but class provides constructors.\n" "Memory for class instance allocated with $symbol(), but class provides constructors. This is unsafe, " "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE762, Certainty::normal); } void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname) { std::list toks = { tok, classTok }; reportError(toks, Severity::error, "mallocOnClassError", "$symbol:" + memfunc +"\n" "$symbol:" + classname +"\n" "Memory for class instance allocated with " + memfunc + "(), but class contains a " + classname + ".\n" "Memory for class instance allocated with " + memfunc + "(), but class a " + classname + ". This is unsafe, " "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE665, Certainty::normal); } void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type) { reportError(tok, Severity::error, "memsetClass", "$symbol:" + memfunc +"\n" "$symbol:" + classname +"\n" "Using '" + memfunc + "' on " + type + " that contains a " + classname + ".\n" "Using '" + memfunc + "' on " + type + " that contains a " + classname + " is unsafe, because constructor, destructor " "and copy operator calls are omitted. These are necessary for this non-POD type to ensure that a valid object " "is created.", CWE762, Certainty::normal); } void CheckClass::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type) { reportError(tok, Severity::error, "memsetClassReference", "$symbol:" + memfunc +"\n" "Using '" + memfunc + "' on " + type + " that contains a reference.", CWE665, Certainty::normal); } void CheckClass::memsetErrorFloat(const Token *tok, const std::string &type) { reportError(tok, Severity::portability, "memsetClassFloat", "Using memset() on " + type + " which contains a floating point number.\n" "Using memset() on " + type + " which contains a floating point number." " This is not portable because memset() sets each byte of a block of memory to a specific value and" " the actual representation of a floating-point value is implementation defined." " Note: In case of an IEEE754-1985 compatible implementation setting all bits to zero results in the value 0.0.", CWE758, Certainty::normal); } //--------------------------------------------------------------------------- // ClassCheck: "C& operator=(const C&) { ... return *this; }" // operator= should return a reference to *this //--------------------------------------------------------------------------- void CheckClass::operatorEqRetRefThis() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eOperatorEqual && func->hasBody()) { // make sure return signature is correct if (func->retType == func->nestedIn->definedType && func->tokenDef->strAt(-1) == "&") { checkReturnPtrThis(scope, &(*func), func->functionScope->bodyStart, func->functionScope->bodyEnd); } } } } } void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last) { std::set analyzedFunctions; checkReturnPtrThis(scope, func, tok, last, analyzedFunctions); } void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions) { bool foundReturn = false; const Token* const startTok = tok; for (; tok && tok != last; tok = tok->next()) { // check for return of reference to this if (tok->str() != "return") continue; foundReturn = true; const Token *retExpr = tok->astOperand1(); if (retExpr && retExpr->str() == "=") retExpr = retExpr->astOperand1(); if (retExpr && retExpr->isUnaryOp("*") && Token::simpleMatch(retExpr->astOperand1(), "this")) continue; std::string cast("( " + scope->className + " & )"); if (Token::simpleMatch(tok->next(), cast.c_str(), cast.size())) tok = tok->tokAt(4); // check if a function is called if (tok->strAt(2) == "(" && tok->linkAt(2)->next()->str() == ";") { // check if it is a member function for (std::list::const_iterator it = scope->functionList.begin(); it != scope->functionList.end(); ++it) { // check for a regular function with the same name and a body if (it->type == Function::eFunction && it->hasBody() && it->token->str() == tok->next()->str()) { // check for the proper return type if (it->tokenDef->previous()->str() == "&" && it->tokenDef->strAt(-2) == scope->className) { // make sure it's not a const function if (!it->isConst()) { /** @todo make sure argument types match */ // avoid endless recursions if (analyzedFunctions.find(&*it) == analyzedFunctions.end()) { analyzedFunctions.insert(&*it); checkReturnPtrThis(scope, &*it, it->arg->link()->next(), it->arg->link()->next()->link(), analyzedFunctions); } // just bail for now else return; } } } } } // check if *this is returned else if (!(Token::simpleMatch(tok->next(), "operator= (") || Token::simpleMatch(tok->next(), "this . operator= (") || (Token::Match(tok->next(), "%type% :: operator= (") && tok->next()->str() == scope->className))) operatorEqRetRefThisError(func->token); } if (foundReturn) { return; } if (startTok->next() == last) { const std::string tmp("( const " + scope->className + " &"); if (Token::simpleMatch(func->argDef, tmp.c_str(), tmp.size())) { // Typical wrong way to suppress default assignment operator by declaring it and leaving empty operatorEqMissingReturnStatementError(func->token, func->access == AccessControl::Public); } else { operatorEqMissingReturnStatementError(func->token, true); } return; } if (mSettings->library.isScopeNoReturn(last, nullptr)) { // Typical wrong way to prohibit default assignment operator // by always throwing an exception or calling a noreturn function operatorEqShouldBeLeftUnimplementedError(func->token); return; } operatorEqMissingReturnStatementError(func->token, func->access == AccessControl::Public); } void CheckClass::operatorEqRetRefThisError(const Token *tok) { reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance.", CWE398, Certainty::normal); } void CheckClass::operatorEqShouldBeLeftUnimplementedError(const Token *tok) { reportError(tok, Severity::style, "operatorEqShouldBeLeftUnimplemented", "'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.", CWE398, Certainty::normal); } void CheckClass::operatorEqMissingReturnStatementError(const Token *tok, bool error) { if (error) { reportError(tok, Severity::error, "operatorEqMissingReturnStatement", "No 'return' statement in non-void function causes undefined behavior.", CWE398, Certainty::normal); } else { operatorEqRetRefThisError(tok); } } //--------------------------------------------------------------------------- // ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }" // operator= should check for assignment to self // // For simple classes, an assignment to self check is only a potential optimization. // // For classes that allocate dynamic memory, assignment to self can be a real error // if it is deallocated and allocated again without being checked for. // // This check is not valid for classes with multiple inheritance because a // class can have multiple addresses so there is no trivial way to check for // assignment to self. //--------------------------------------------------------------------------- void CheckClass::operatorEqToSelf() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // skip classes with multiple inheritance if (scope->definedType->derivedFrom.size() > 1) continue; for (const Function &func : scope->functionList) { if (func.type == Function::eOperatorEqual && func.hasBody()) { // make sure that the operator takes an object of the same type as *this, otherwise we can't detect self-assignment checks if (func.argumentList.empty()) continue; const Token* typeTok = func.argumentList.front().typeEndToken(); while (typeTok->str() == "const" || typeTok->str() == "&" || typeTok->str() == "*") typeTok = typeTok->previous(); if (typeTok->str() != scope->className) continue; // make sure return signature is correct if (Token::Match(func.retDef, "%type% &") && func.retDef->str() == scope->className) { // find the parameter name const Token *rhs = func.argumentList.begin()->nameToken(); const Token* out_ifStatementScopeStart = nullptr; if (!hasAssignSelf(&func, rhs, &out_ifStatementScopeStart)) { if (hasAllocation(&func, scope)) operatorEqToSelfError(func.token); } else if (out_ifStatementScopeStart != nullptr) { if (hasAllocationInIfScope(&func, scope, out_ifStatementScopeStart)) operatorEqToSelfError(func.token); } } } } } } bool CheckClass::hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const { const Token *end; if (ifStatementScopeStart->str() == "{") end = ifStatementScopeStart->link(); else end = func->functionScope->bodyEnd; return hasAllocation(func, scope, ifStatementScopeStart, end); } bool CheckClass::hasAllocation(const Function *func, const Scope* scope) const { return hasAllocation(func, scope, func->functionScope->bodyStart, func->functionScope->bodyEnd); } bool CheckClass::hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const { if (!end) end = func->functionScope->bodyEnd; for (const Token *tok = start; tok && (tok != end); tok = tok->next()) { if (Token::Match(tok, "%var% = malloc|realloc|calloc|new") && isMemberVar(scope, tok)) return true; // check for deallocating memory const Token *var; if (Token::Match(tok, "free ( %var%")) var = tok->tokAt(2); else if (Token::Match(tok, "delete [ ] %var%")) var = tok->tokAt(3); else if (Token::Match(tok, "delete %var%")) var = tok->next(); else continue; // Check for assignment to the deleted pointer (only if its a member of the class) if (isMemberVar(scope, var)) { for (const Token *tok1 = var->next(); tok1 && (tok1 != end); tok1 = tok1->next()) { if (Token::Match(tok1, "%varid% =", var->varId())) return true; } } } return false; } static bool isTrueKeyword(const Token* tok) { return tok->hasKnownIntValue() && tok->getKnownIntValue() == 1; } static bool isFalseKeyword(const Token* tok) { return tok->hasKnownIntValue() && tok->getKnownIntValue() == 0; } /* * Checks if self-assignment test is inverse * For example 'if (this == &rhs)' */ CheckClass::Bool CheckClass::isInverted(const Token *tok, const Token *rhs) { bool res = true; for (const Token *itr = tok; itr && itr->str()!="("; itr=itr->astParent()) { if (Token::simpleMatch(itr, "!=") && (isTrueKeyword(itr->astOperand1()) || isTrueKeyword(itr->astOperand2()))) { res = !res; } else if (Token::simpleMatch(itr, "!=") && ((Token::simpleMatch(itr->astOperand1(), "this") && Token::simpleMatch(itr->astOperand2(), "&") && Token::simpleMatch(itr->astOperand2()->next(), rhs->str().c_str(), rhs->str().size())) || (Token::simpleMatch(itr->astOperand2(), "this") && Token::simpleMatch(itr->astOperand1(), "&") && Token::simpleMatch(itr->astOperand1()->next(), rhs->str().c_str(), rhs->str().size())))) { res = !res; } else if (Token::simpleMatch(itr, "!=") && (isFalseKeyword(itr->astOperand1()) || isFalseKeyword(itr->astOperand2()))) { //Do nothing } else if (Token::simpleMatch(itr, "!")) { res = !res; } else if (Token::simpleMatch(itr, "==") && (isFalseKeyword(itr->astOperand1()) || isFalseKeyword(itr->astOperand2()))) { res = !res; } else if (Token::simpleMatch(itr, "==") && (isTrueKeyword(itr->astOperand1()) || isTrueKeyword(itr->astOperand2()))) { //Do nothing } else if (Token::simpleMatch(itr, "==") && ((Token::simpleMatch(itr->astOperand1(), "this") && Token::simpleMatch(itr->astOperand2(), "&") && Token::simpleMatch(itr->astOperand2()->next(), rhs->str().c_str(), rhs->str().size())) || (Token::simpleMatch(itr->astOperand2(), "this") && Token::simpleMatch(itr->astOperand1(), "&") && Token::simpleMatch(itr->astOperand1()->next(), rhs->str().c_str(), rhs->str().size())))) { //Do nothing } else { return Bool::BAILOUT; } } if (res) return Bool::TRUE; return Bool::FALSE; } const Token * CheckClass::getIfStmtBodyStart(const Token *tok, const Token *rhs) { const Token *top = tok->astTop(); if (Token::simpleMatch(top->link(), ") {")) { switch (isInverted(tok->astParent(), rhs)) { case Bool::BAILOUT: return nullptr; case Bool::TRUE: return top->link()->next(); case Bool::FALSE: return top->link()->next()->link(); } } return nullptr; } bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs, const Token **out_ifStatementScopeStart) { if (!rhs) return false; const Token *last = func->functionScope->bodyEnd; for (const Token *tok = func->functionScope->bodyStart; tok && tok != last; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; bool ret = false; visitAstNodes(tok->next()->astOperand2(), [&](const Token *tok2) { if (!Token::Match(tok2, "==|!=")) return ChildrenToVisit::op1_and_op2; if (Token::simpleMatch(tok2->astOperand1(), "this")) tok2 = tok2->astOperand2(); else if (Token::simpleMatch(tok2->astOperand2(), "this")) tok2 = tok2->astOperand1(); else return ChildrenToVisit::op1_and_op2; if (tok2 && tok2->isUnaryOp("&") && tok2->astOperand1()->str() == rhs->str()) ret = true; if (ret) { *out_ifStatementScopeStart = getIfStmtBodyStart(tok2, rhs); } return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (ret) return ret; } return false; } void CheckClass::operatorEqToSelfError(const Token *tok) { reportError(tok, Severity::warning, "operatorEqToSelf", "'operator=' should check for assignment to self to avoid problems with dynamic memory.\n" "'operator=' should check for assignment to self to ensure that each block of dynamically " "allocated memory is owned and managed by only one instance of the class.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // A destructor in a base class should be virtual //--------------------------------------------------------------------------- void CheckClass::virtualDestructor() { // This error should only be given if: // * base class doesn't have virtual destructor // * derived class has non-empty destructor (only c++03, in c++11 it's UB see paragraph 3 in [expr.delete]) // * base class is deleted // unless inconclusive in which case: // * A class with any virtual functions should have a destructor that is either public and virtual or protected const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); std::list inconclusiveErrors; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // Skip base classes (unless inconclusive) if (scope->definedType->derivedFrom.empty()) { if (printInconclusive) { const Function *destructor = scope->getDestructor(); if (destructor && !destructor->hasVirtualSpecifier() && destructor->access == AccessControl::Public) { for (const Function &func : scope->functionList) { if (func.hasVirtualSpecifier()) { inconclusiveErrors.push_back(destructor); break; } } } } continue; } // Check if destructor is empty and non-empty .. if (mSettings->standards.cpp <= Standards::CPP03) { // Find the destructor const Function *destructor = scope->getDestructor(); // Check for destructor with implementation if (!destructor || !destructor->hasBody()) continue; // Empty destructor if (destructor->token->linkAt(3) == destructor->token->tokAt(4)) continue; } const Token *derived = scope->classDef; const Token *derivedClass = derived->next(); // Iterate through each base class... for (const Type::BaseInfo & j : scope->definedType->derivedFrom) { // Check if base class is public and exists in database if (j.access != AccessControl::Private && j.type) { const Type *derivedFrom = j.type; const Scope *derivedFromScope = derivedFrom->classScope; if (!derivedFromScope) continue; // Check for this pattern: // 1. Base class pointer is given the address of derived class instance // 2. Base class pointer is deleted // // If this pattern is not seen then bailout the checking of these base/derived classes { // pointer variables of type 'Base *' std::set baseClassPointers; for (const Variable* var : mSymbolDatabase->variableList()) { if (var && var->isPointer() && var->type() == derivedFrom) baseClassPointers.insert(var->declarationId()); } // pointer variables of type 'Base *' that should not be deleted std::set dontDelete; // No deletion of derived class instance through base class pointer found => the code is ok bool ok = true; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %var% =") && baseClassPointers.find(tok->next()->varId()) != baseClassPointers.end()) { // new derived class.. const std::string tmp("new " + derivedClass->str()); if (Token::simpleMatch(tok->tokAt(3), tmp.c_str(), tmp.size())) { dontDelete.insert(tok->next()->varId()); } } // Delete base class pointer that might point at derived class else if (Token::Match(tok, "delete %var% ;") && dontDelete.find(tok->next()->varId()) != dontDelete.end()) { ok = false; break; } } // No base class pointer that points at a derived class is deleted if (ok) continue; } // Find the destructor declaration for the base class. const Function *baseDestructor = derivedFromScope->getDestructor(); // Check that there is a destructor.. if (!baseDestructor) { if (derivedFrom->derivedFrom.empty()) { virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false); } } else if (!baseDestructor->hasVirtualSpecifier()) { // TODO: This is just a temporary fix, better solution is needed. // Skip situations where base class has base classes of its own, because // some of the base classes might have virtual destructor. // Proper solution is to check all of the base classes. If base class is not // found or if one of the base classes has virtual destructor, error should not // be printed. See TODO test case "virtualDestructorInherited" if (derivedFrom->derivedFrom.empty()) { // Make sure that the destructor is public (protected or private // would not compile if inheritance is used in a way that would // cause the bug we are trying to find here.) if (baseDestructor->access == AccessControl::Public) { virtualDestructorError(baseDestructor->token, derivedFrom->name(), derivedClass->str(), false); // check for duplicate error and remove it if found const std::list::iterator found = find(inconclusiveErrors.begin(), inconclusiveErrors.end(), baseDestructor); if (found != inconclusiveErrors.end()) inconclusiveErrors.erase(found); } } } } } } for (const Function *func : inconclusiveErrors) virtualDestructorError(func->tokenDef, func->name(), emptyString, true); } void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive) { if (inconclusive) { if (mSettings->severity.isEnabled(Severity::warning)) reportError(tok, Severity::warning, "virtualDestructor", "$symbol:" + Base + "\nClass '$symbol' which has virtual members does not have a virtual destructor.", CWE404, Certainty::inconclusive); } else { reportError(tok, Severity::error, "virtualDestructor", "$symbol:" + Base +"\n" "$symbol:" + Derived +"\n" "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n" "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. " "If you destroy instances of the derived class by deleting a pointer that points to the base class, only " "the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class " "could leak. This can be avoided by adding a virtual destructor to the base class.", CWE404, Certainty::normal); } } //--------------------------------------------------------------------------- // warn for "this-x". The indented code may be "this->x" //--------------------------------------------------------------------------- void CheckClass::thisSubtraction() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const Token *tok = mTokenizer->tokens(); for (;;) { tok = Token::findmatch(tok, "this - %name%"); if (!tok) break; if (tok->strAt(-1) != "*") thisSubtractionError(tok); tok = tok->next(); } } void CheckClass::thisSubtractionError(const Token *tok) { reportError(tok, Severity::warning, "thisSubtraction", "Suspicious pointer subtraction. Did you intend to write '->'?", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // can member function be const? //--------------------------------------------------------------------------- void CheckClass::checkConst() { // This is an inconclusive check. False positives: #3322. if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) return; if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { for (const Function &func : scope->functionList) { // does the function have a body? if (func.type != Function::eFunction || !func.hasBody()) continue; // don't warn for friend/static/virtual functions if (func.isFriend() || func.isStatic() || func.hasVirtualSpecifier()) continue; // don't warn when returning non-const pointer/reference { bool isPointerOrReference = false; for (const Token *typeToken = func.retDef; typeToken; typeToken = typeToken->next()) { if (Token::Match(typeToken, "(|{|;")) break; if (!isPointerOrReference && typeToken->str() == "const") break; if (Token::Match(typeToken, "*|&")) { isPointerOrReference = true; break; } } if (isPointerOrReference) continue; } if (func.isOperator()) { // Operator without return type: conversion operator const std::string& opName = func.tokenDef->str(); if (opName.compare(8, 5, "const") != 0 && (endsWith(opName,'&') || endsWith(opName,'*'))) continue; } else if (mSettings->library.isSmartPointer(func.retDef)) { // Don't warn if a std::shared_ptr etc is returned continue; } else { // don't warn for unknown types.. // LPVOID, HDC, etc if (func.retDef->str().size() > 2 && !func.retDef->type() && func.retDef->isUpperCaseName()) continue; } // check if base class function is virtual if (!scope->definedType->derivedFrom.empty() && func.isImplicitlyVirtual(true)) continue; bool memberAccessed = false; // if nothing non-const was found. write error.. if (!checkConstFunc(scope, &func, memberAccessed)) continue; if (func.isConst() && (memberAccessed || func.isOperator())) continue; std::string classname = scope->className; const Scope *nest = scope->nestedIn; while (nest && nest->type != Scope::eGlobal) { classname = std::string(nest->className + "::" + classname); nest = nest->nestedIn; } // get function name std::string functionName = (func.tokenDef->isName() ? "" : "operator") + func.tokenDef->str(); if (func.tokenDef->str() == "(") functionName += ")"; else if (func.tokenDef->str() == "[") functionName += "]"; if (func.isInline()) checkConstError(func.token, classname, functionName, !memberAccessed && !func.isOperator()); else // not inline checkConstError2(func.token, func.tokenDef, classname, functionName, !memberAccessed && !func.isOperator()); } } } bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const { bool again = false; // try to find the member variable do { again = false; if (tok->str() == "this") { return true; } else if (Token::simpleMatch(tok->tokAt(-3), "( * this )")) { return true; } else if (Token::Match(tok->tokAt(-2), "%name% . %name%")) { tok = tok->tokAt(-2); again = true; } else if (Token::Match(tok->tokAt(-2), "] . %name%")) { tok = tok->linkAt(-2)->previous(); again = true; } else if (tok->str() == "]") { tok = tok->link()->previous(); again = true; } } while (again); for (const Variable &var : scope->varlist) { if (var.name() == tok->str()) { if (tok->varId() == 0) mSymbolDatabase->debugMessage(tok, "varid0", "CheckClass::isMemberVar found used member variable \'" + tok->str() + "\' with varid 0"); return !var.isStatic(); } } // not found in this class if (!scope->definedType->derivedFrom.empty()) { // check each base class for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { // find the base class const Type *derivedFrom = i.type; // find the function in the base class if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) { if (isMemberVar(derivedFrom->classScope, tok)) return true; } } } return false; } bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) const { if (!tok->function()) { for (const Function &func : scope->functionList) { if (func.name() == tok->str()) { const Token* tok2 = tok->tokAt(2); int argsPassed = tok2->str() == ")" ? 0 : 1; for (;;) { tok2 = tok2->nextArgument(); if (tok2) argsPassed++; else break; } if (argsPassed == func.argCount() || (func.isVariadic() && argsPassed >= (func.argCount() - 1)) || (argsPassed < func.argCount() && argsPassed >= func.minArgCount())) return true; } } } else if (tok->function()->nestedIn == scope) return !tok->function()->isStatic(); // not found in this class if (!scope->definedType->derivedFrom.empty()) { // check each base class for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { // find the base class const Type *derivedFrom = i.type; // find the function in the base class if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) { if (isMemberFunc(derivedFrom->classScope, tok)) return true; } } } return false; } bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) const { if (!tok->function()) return false; else if (tok->function()->nestedIn == scope) return tok->function()->isConst(); // not found in this class if (!scope->definedType->derivedFrom.empty()) { // check each base class for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { // find the base class const Type *derivedFrom = i.type; // find the function in the base class if (derivedFrom && derivedFrom->classScope) { if (isConstMemberFunc(derivedFrom->classScope, tok)) return true; } } } return false; } // The container contains the STL types whose operator[] is not a const. static const std::set stl_containers_not_const = { "map", "unordered_map" }; bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool& memberAccessed) const { if (mTokenizer->hasIfdef(func->functionScope->bodyStart, func->functionScope->bodyEnd)) return false; // if the function doesn't have any assignment nor function call, // it can be a const function.. for (const Token *tok1 = func->functionScope->bodyStart; tok1 && tok1 != func->functionScope->bodyEnd; tok1 = tok1->next()) { if (tok1->isName() && isMemberVar(scope, tok1)) { memberAccessed = true; const Variable* v = tok1->variable(); if (v && v->isMutable()) continue; if (tok1->str() == "this" && tok1->previous()->isAssignmentOp()) return false; // non const pointer cast if (tok1->valueType() && tok1->valueType()->pointer > 0 && tok1->astParent() && tok1->astParent()->isCast() && !Token::simpleMatch(tok1->astParent(), "( const")) return false; const Token* lhs = tok1->previous(); if (lhs->str() == "(" && tok1->astParent() && tok1->astParent()->astParent()) lhs = tok1->astParent()->astParent(); if (lhs->str() == "&") { lhs = lhs->previous(); if (lhs->isAssignmentOp() && lhs->previous()->variable()) { if (lhs->previous()->variable()->typeStartToken()->strAt(-1) != "const" && lhs->previous()->variable()->isPointer()) return false; } } else if (lhs->str() == ":" && lhs->astParent() && lhs->astParent()->str() == "(") { // range-based for-loop (C++11) // TODO: We could additionally check what is done with the elements to avoid false negatives. Here we just rely on "const" keyword being used. if (lhs->astParent()->strAt(1) != "const") return false; } else { if (lhs->isAssignmentOp()) { const Variable* lhsVar = lhs->previous()->variable(); if (lhsVar && !lhsVar->isConst() && lhsVar->isReference() && lhs == lhsVar->nameToken()->next()) return false; } } const Token* jumpBackToken = nullptr; const Token *lastVarTok = tok1; const Token *end = tok1; for (;;) { if (Token::Match(end->next(), ". %name%")) { end = end->tokAt(2); if (end->varId()) lastVarTok = end; } else if (end->strAt(1) == "[") { if (end->varId()) { const Variable *var = end->variable(); if (var && var->isStlType(stl_containers_not_const)) return false; const Token* assignTok = end->next()->astParent(); if (var && assignTok && assignTok->isAssignmentOp() && assignTok->astOperand1() && assignTok->astOperand1()->variable()) { const Variable* assignVar = assignTok->astOperand1()->variable(); if (assignVar->isPointer() && !assignVar->isConst() && var->typeScope()) { const auto& funcMap = var->typeScope()->functionMap; // if there is no operator that is const and returns a non-const pointer, func cannot be const if (std::none_of(funcMap.begin(), funcMap.end(), [](const std::pair& fm) { return fm.second->isConst() && fm.first == "operator[]" && !Function::returnsConst(fm.second); })) return false; } } } if (!jumpBackToken) jumpBackToken = end->next(); // Check inside the [] brackets end = end->linkAt(1); } else if (end->strAt(1) == ")") end = end->next(); else break; } if (end->strAt(1) == "(") { const Variable *var = lastVarTok->variable(); if (!var) return false; if (var->isStlType() // assume all std::*::size() and std::*::empty() are const && (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) ; else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end)) return false; } // Assignment else if (end->next()->isAssignmentOp()) return false; // Streaming else if (end->strAt(1) == "<<" && tok1->strAt(-1) != "<<") return false; else if (isLikelyStreamRead(true, tok1->previous())) return false; // ++/-- else if (end->next()->tokType() == Token::eIncDecOp || tok1->previous()->tokType() == Token::eIncDecOp) return false; const Token* start = tok1; while (tok1->strAt(-1) == ")") tok1 = tok1->linkAt(-1); if (start->strAt(-1) == "delete") return false; tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression if (tok1 == end && Token::Match(end->previous(), ". %name% ( !!)")) tok1 = tok1->previous(); // check function call } // streaming: << else if (Token::simpleMatch(tok1->previous(), ") <<") && isMemberVar(scope, tok1->tokAt(-2))) { const Variable* var = tok1->tokAt(-2)->variable(); if (!var || !var->isMutable()) return false; } // streaming: >> *this else if (Token::simpleMatch(tok1, ">> * this") && isLikelyStreamRead(true, tok1)) { return false; } // function/constructor call, return init list else if ((Token::Match(tok1, "%name% (|{") || Token::simpleMatch(tok1->astParent(), "return {")) && !tok1->isStandardType() && !Token::Match(tok1, "return|if|string|switch|while|catch|for")) { if (isMemberFunc(scope, tok1) && tok1->strAt(-1) != ".") { if (!isConstMemberFunc(scope, tok1)) return false; memberAccessed = true; } // Member variable given as parameter const Token *lpar = tok1->next(); if (Token::simpleMatch(lpar, "( ) (")) lpar = lpar->tokAt(2); for (const Token* tok2 = lpar->next(); tok2 && tok2 != tok1->next()->link(); tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if ((tok2->isName() && isMemberVar(scope, tok2)) || (tok2->isUnaryOp("&") && (tok2 = tok2->astOperand1()))) { const Variable* var = tok2->variable(); if (!var || !var->isMutable()) return false; // TODO: Only bailout if function takes argument as non-const reference } } } else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) { return false; } } return true; } void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic) { checkConstError2(tok, nullptr, classname, funcname, suggestStatic); } void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic) { std::list toks; toks.push_back(tok1); if (tok2) toks.push_back(tok2); if (!suggestStatic) reportError(toks, Severity::style, "functionConst", "$symbol:" + classname + "::" + funcname +"\n" "Technically the member function '$symbol' can be const.\n" "The member function '$symbol' can be made a const " "function. Making this function 'const' should not cause compiler errors. " "Even though the function can be made const function technically it may not make " "sense conceptually. Think about your design and the task of the function first - is " "it a function that must not change object internal state?", CWE398, Certainty::inconclusive); else reportError(toks, Severity::performance, "functionStatic", "$symbol:" + classname + "::" + funcname +"\n" "Technically the member function '$symbol' can be static (but you may consider moving to unnamed namespace).\n" "The member function '$symbol' can be made a static " "function. Making a function static can bring a performance benefit since no 'this' instance is " "passed to the function. This change should not cause compiler errors but it does not " "necessarily make sense conceptually. Think about your design and the task of the function first - " "is it a function that must not access members of class instances? And maybe it is more appropriate " "to move this function to a unnamed namespace.", CWE398, Certainty::inconclusive); } //--------------------------------------------------------------------------- // ClassCheck: Check that initializer list is in declared order. //--------------------------------------------------------------------------- namespace { // avoid one-definition-rule violation struct VarInfo { VarInfo(const Variable *_var, const Token *_tok) : var(_var), tok(_tok) {} const Variable *var; const Token *tok; }; } void CheckClass::initializerListOrder() { if (!mSettings->severity.isEnabled(Severity::style)) return; // This check is not inconclusive. However it only determines if the initialization // order is incorrect. It does not determine if being out of order causes // a real error. Out of order is not necessarily an error but you can never // have an error if the list is in order so this enforces defensive programming. if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // iterate through all member functions looking for constructors for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->isConstructor() && func->hasBody()) { // check for initializer list const Token *tok = func->arg->link()->next(); if (tok->str() == ":") { std::vector vars; tok = tok->next(); // find all variable initializations in list while (tok && tok != func->functionScope->bodyStart) { if (Token::Match(tok, "%name% (|{")) { const Variable *var = scope->getVariable(tok->str()); if (var) vars.emplace_back(var, tok); if (Token::Match(tok->tokAt(2), "%name% =")) { var = scope->getVariable(tok->strAt(2)); if (var) vars.emplace_back(var, tok->tokAt(2)); } tok = tok->next()->link()->next(); } else tok = tok->next(); } // need at least 2 members to have out of order initialization for (int j = 1; j < vars.size(); j++) { // check for out of order initialization if (vars[j].var->index() < vars[j - 1].var->index()) initializerListError(vars[j].tok,vars[j].var->nameToken(), scope->className, vars[j].var->name()); } } } } } } void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname) { std::list toks = { tok1, tok2 }; reportError(toks, Severity::style, "initializerList", "$symbol:" + classname + "::" + varname +"\n" "Member variable '$symbol' is in the wrong place in the initializer list.\n" "Member variable '$symbol' is in the wrong place in the initializer list. " "Members are initialized in the order they are declared, not in the " "order they are in the initializer list. Keeping the initializer list " "in the same order that the members were declared prevents order dependent " "initialization errors.", CWE398, Certainty::inconclusive); } //--------------------------------------------------------------------------- // Check for self initialization in initialization list //--------------------------------------------------------------------------- void CheckClass::checkSelfInitialization() { for (const Scope *scope : mSymbolDatabase->functionScopes) { const Function* function = scope->function; if (!function || !function->isConstructor()) continue; const Token* tok = function->arg->link()->next(); if (tok->str() != ":") continue; for (; tok != scope->bodyStart; tok = tok->next()) { if (Token::Match(tok, "[:,] %var% (|{ %var% )|}") && tok->next()->varId() == tok->tokAt(3)->varId()) { selfInitializationError(tok, tok->strAt(1)); } } } } void CheckClass::selfInitializationError(const Token* tok, const std::string& varname) { reportError(tok, Severity::error, "selfInitialization", "$symbol:" + varname + "\nMember variable '$symbol' is initialized by itself.", CWE665, Certainty::normal); } //--------------------------------------------------------------------------- // Check for virtual function calls in constructor/destructor //--------------------------------------------------------------------------- void CheckClass::checkVirtualFunctionCallInConstructor() { if (!mSettings->severity.isEnabled(Severity::warning)) return; std::map> virtualFunctionCallsMap; for (const Scope *scope : mSymbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody() || !(scope->function->isConstructor() || scope->function->isDestructor())) continue; const std::list & virtualFunctionCalls = getVirtualFunctionCalls(*scope->function, virtualFunctionCallsMap); for (const Token *callToken : virtualFunctionCalls) { std::list callstack(1, callToken); getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack); if (callstack.empty()) continue; if (!(callstack.back()->function()->hasVirtualSpecifier() || callstack.back()->function()->hasOverrideSpecifier())) continue; if (callstack.back()->function()->isPure()) pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); else if (!callstack.back()->function()->hasFinalSpecifier()) virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); } } } const std::list & CheckClass::getVirtualFunctionCalls(const Function & function, std::map> & virtualFunctionCallsMap) { const std::map>::const_iterator found = virtualFunctionCallsMap.find(&function); if (found != virtualFunctionCallsMap.end()) return found->second; virtualFunctionCallsMap[&function] = std::list(); std::list & virtualFunctionCalls = virtualFunctionCallsMap.find(&function)->second; if (!function.hasBody()) return virtualFunctionCalls; for (const Token *tok = function.arg->link(); tok != function.functionScope->bodyEnd; tok = tok->next()) { if (function.type != Function::eConstructor && function.type != Function::eCopyConstructor && function.type != Function::eMoveConstructor && function.type != Function::eDestructor) { if ((Token::simpleMatch(tok, ") {") && tok->link() && Token::Match(tok->link()->previous(), "if|switch")) || Token::simpleMatch(tok, "else {")) { // Assume pure virtual function call is prevented by "if|else|switch" condition tok = tok->linkAt(1); continue; } } if (tok->scope()->type == Scope::eLambda) tok = tok->scope()->bodyEnd->next(); const Function * callFunction = tok->function(); if (!callFunction || function.nestedIn != callFunction->nestedIn || (tok->previous() && tok->previous()->str() == ".")) continue; if (tok->previous() && tok->previous()->str() == "(") { const Token * prev = tok->previous(); if (prev->previous() && (mSettings->library.ignorefunction(tok->str()) || mSettings->library.ignorefunction(prev->previous()->str()))) continue; } if (callFunction->isImplicitlyVirtual()) { if (!callFunction->isPure() && Token::simpleMatch(tok->previous(), "::")) continue; virtualFunctionCalls.push_back(tok); continue; } const std::list & virtualFunctionCallsOfTok = getVirtualFunctionCalls(*callFunction, virtualFunctionCallsMap); if (!virtualFunctionCallsOfTok.empty()) virtualFunctionCalls.push_back(tok); } return virtualFunctionCalls; } void CheckClass::getFirstVirtualFunctionCallStack( std::map> & virtualFunctionCallsMap, const Token * callToken, std::list & pureFuncStack) { const Function *callFunction = callToken->function(); if (callFunction->isImplicitlyVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) { pureFuncStack.push_back(callFunction->tokenDef); return; } std::map>::const_iterator found = virtualFunctionCallsMap.find(callFunction); if (found == virtualFunctionCallsMap.end() || found->second.empty()) { pureFuncStack.clear(); return; } const Token * firstCall = *found->second.begin(); pureFuncStack.push_back(firstCall); getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, firstCall, pureFuncStack); } void CheckClass::virtualFunctionCallInConstructorError( const Function * scopeFunction, const std::list & tokStack, const std::string &funcname) { const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor"; ErrorPath errorPath; int lineNumber = 1; for (const Token *tok : tokStack) errorPath.emplace_back(tok, "Calling " + tok->str()); if (!errorPath.empty()) { lineNumber = errorPath.front().first->linenr(); errorPath.back().second = funcname + " is a virtual function"; } std::string constructorName; if (scopeFunction) { const Token *endToken = scopeFunction->argDef->link()->next(); if (scopeFunction->type == Function::Type::eDestructor) constructorName = "~"; for (const Token *tok = scopeFunction->tokenDef; tok != endToken; tok = tok->next()) { if (!constructorName.empty() && Token::Match(tok->previous(), "%name%|%num% %name%|%num%")) constructorName += ' '; constructorName += tok->str(); if (tok->str() == ")") break; } } reportError(errorPath, Severity::style, "virtualCallInConstructor", "Virtual function '" + funcname + "' is called from " + scopeFunctionTypeName + " '" + constructorName + "' at line " + MathLib::toString(lineNumber) + ". Dynamic binding is not used.", CWE(0U), Certainty::normal); } void CheckClass::pureVirtualFunctionCallInConstructorError( const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname) { const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor"; ErrorPath errorPath; for (const Token *tok : tokStack) errorPath.emplace_back(tok, "Calling " + tok->str()); if (!errorPath.empty()) errorPath.back().second = purefuncname + " is a pure virtual function without body"; reportError(errorPath, Severity::warning, "pureVirtualCall", "$symbol:" + purefuncname +"\n" "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ".\n" "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), Certainty::normal); } //--------------------------------------------------------------------------- // Check for members hiding inherited members with the same name //--------------------------------------------------------------------------- void CheckClass::checkDuplInheritedMembers() { if (!mSettings->severity.isEnabled(Severity::warning)) return; // Iterate over all classes for (const Type &classIt : mSymbolDatabase->typeList) { // Iterate over the parent classes checkDuplInheritedMembersRecursive(&classIt, &classIt); } } void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase) { for (const Type::BaseInfo &parentClassIt : typeBase->derivedFrom) { // Check if there is info about the 'Base' class if (!parentClassIt.type || !parentClassIt.type->classScope) continue; // Don't crash on recursive templates if (parentClassIt.type == typeBase) continue; // Check if they have a member variable in common for (const Variable &classVarIt : typeCurrent->classScope->varlist) { for (const Variable &parentClassVarIt : parentClassIt.type->classScope->varlist) { if (classVarIt.name() == parentClassVarIt.name() && !parentClassVarIt.isPrivate()) { // Check if the class and its parent have a common variable duplInheritedMembersError(classVarIt.nameToken(), parentClassVarIt.nameToken(), typeCurrent->name(), parentClassIt.type->name(), classVarIt.name(), typeCurrent->classScope->type == Scope::eStruct, parentClassIt.type->classScope->type == Scope::eStruct); } } } if (typeCurrent != parentClassIt.type) checkDuplInheritedMembersRecursive(typeCurrent, parentClassIt.type); } } void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &variableName, bool derivedIsStruct, bool baseIsStruct) { ErrorPath errorPath; errorPath.emplace_back(tok2, "Parent variable '" + baseName + "::" + variableName + "'"); errorPath.emplace_back(tok1, "Derived variable '" + derivedName + "::" + variableName + "'"); const std::string symbols = "$symbol:" + derivedName + "\n$symbol:" + variableName + "\n$symbol:" + baseName; const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedName + "' defines member variable with name '" + variableName + "' also defined in its parent " + std::string(baseIsStruct ? "struct" : "class") + " '" + baseName + "'."; reportError(errorPath, Severity::warning, "duplInheritedMember", symbols + '\n' + message, CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // Check that copy constructor and operator defined together //--------------------------------------------------------------------------- enum class CtorType { NO, WITHOUT_BODY, WITH_BODY }; void CheckClass::checkCopyCtorAndEqOperator() { // This is disabled because of #8388 // The message must be clarified. How is the behaviour different? return; // cppcheck-suppress unreachableCode - remove when code is enabled again if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { bool hasNonStaticVars = false; for (std::list::const_iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { if (!var->isStatic()) { hasNonStaticVars = true; break; } } if (!hasNonStaticVars) continue; CtorType copyCtors = CtorType::NO; bool moveCtor = false; CtorType assignmentOperators = CtorType::NO; for (const Function &func : scope->functionList) { if (copyCtors == CtorType::NO && func.type == Function::eCopyConstructor) { copyCtors = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY; } if (assignmentOperators == CtorType::NO && func.type == Function::eOperatorEqual) { const Variable * variable = func.getArgumentVar(0); if (variable && variable->type() && variable->type()->classScope == scope) { assignmentOperators = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY; } } if (func.type == Function::eMoveConstructor) { moveCtor = true; break; } } if (moveCtor) continue; // No method defined if (copyCtors != CtorType::WITH_BODY && assignmentOperators != CtorType::WITH_BODY) continue; // both methods are defined if (copyCtors != CtorType::NO && assignmentOperators != CtorType::NO) continue; copyCtorAndEqOperatorError(scope->classDef, scope->className, scope->type == Scope::eStruct, copyCtors == CtorType::WITH_BODY); } } void CheckClass::copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor) { const std::string message = "$symbol:" + classname + "\n" "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' has '" + getFunctionTypeName(hasCopyCtor ? Function::eCopyConstructor : Function::eOperatorEqual) + "' but lack of '" + getFunctionTypeName(hasCopyCtor ? Function::eOperatorEqual : Function::eCopyConstructor) + "'."; reportError(tok, Severity::warning, "copyCtorAndEqOperator", message); } void CheckClass::checkOverride() { if (!mSettings->severity.isEnabled(Severity::style)) return; if (mSettings->standards.cpp < Standards::CPP11) return; for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { if (!classScope->definedType || classScope->definedType->derivedFrom.empty()) continue; for (const Function &func : classScope->functionList) { if (func.hasOverrideSpecifier() || func.hasFinalSpecifier()) continue; const Function *baseFunc = func.getOverriddenFunction(); if (baseFunc) overrideError(baseFunc, &func); } } } void CheckClass::overrideError(const Function *funcInBase, const Function *funcInDerived) { const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : ""; const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function"; ErrorPath errorPath; if (funcInBase && funcInDerived) { errorPath.push_back(ErrorPathItem(funcInBase->tokenDef, "Virtual " + funcType + " in base class")); errorPath.push_back(ErrorPathItem(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class")); } reportError(errorPath, Severity::style, "missingOverride", "$symbol:" + functionName + "\n" "The " + funcType + " '$symbol' overrides a " + funcType + " in a base class but is not marked with a 'override' specifier.", CWE(0U) /* Unknown CWE! */, Certainty::normal); } void CheckClass::checkThisUseAfterFree() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { for (const Variable &var : classScope->varlist) { // Find possible "self pointer".. pointer/smartpointer member variable of "self" type. if (var.valueType() && var.valueType()->smartPointerType != classScope->definedType && var.valueType()->typeScope != classScope) { const ValueType valueType = ValueType::parseDecl(var.typeStartToken(), mSettings); if (valueType.smartPointerType != classScope->definedType) continue; } // If variable is not static, check that "this" is assigned if (!var.isStatic()) { bool hasAssign = false; for (const Function &func : classScope->functionList) { if (func.type != Function::Type::eFunction || !func.hasBody()) continue; for (const Token *tok = func.functionScope->bodyStart; tok != func.functionScope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%varid% = this|shared_from_this", var.declarationId())) { hasAssign = true; break; } } if (hasAssign) break; } if (!hasAssign) continue; } // Check usage of self pointer.. for (const Function &func : classScope->functionList) { if (func.type != Function::Type::eFunction || !func.hasBody()) continue; const Token * freeToken = nullptr; std::set callstack; checkThisUseAfterFreeRecursive(classScope, &func, &var, callstack, &freeToken); } } } } bool CheckClass::checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token **freeToken) { if (!func || !func->functionScope) return false; // avoid recursion if (callstack.count(func)) return false; callstack.insert(func); const Token * const bodyStart = func->functionScope->bodyStart; const Token * const bodyEnd = func->functionScope->bodyEnd; for (const Token *tok = bodyStart; tok != bodyEnd; tok = tok->next()) { const bool isDestroyed = *freeToken != nullptr && !func->isStatic(); if (Token::Match(tok, "delete %var% ;") && selfPointer == tok->next()->variable()) { *freeToken = tok; tok = tok->tokAt(2); } else if (Token::Match(tok, "%var% . reset ( )") && selfPointer == tok->variable()) *freeToken = tok; else if (Token::Match(tok->previous(), "!!. %name% (") && tok->function() && tok->function()->nestedIn == classScope) { if (isDestroyed) { thisUseAfterFree(selfPointer->nameToken(), *freeToken, tok); return true; } if (checkThisUseAfterFreeRecursive(classScope, tok->function(), selfPointer, callstack, freeToken)) return true; } else if (isDestroyed && Token::Match(tok->previous(), "!!. %name%") && tok->variable() && tok->variable()->scope() == classScope && !tok->variable()->isStatic() && !tok->variable()->isArgument()) { thisUseAfterFree(selfPointer->nameToken(), *freeToken, tok); return true; } else if (*freeToken && Token::Match(tok, "return|throw")) { // TODO return tok->str() == "throw"; } else if (tok->str() == "{" && tok->scope()->type == Scope::ScopeType::eLambda) { tok = tok->link(); } } return false; } void CheckClass::thisUseAfterFree(const Token *self, const Token *free, const Token *use) { std::string selfPointer = self ? self->str() : "ptr"; const ErrorPath errorPath = { ErrorPathItem(self, "Assuming '" + selfPointer + "' is used as 'this'"), ErrorPathItem(free, "Delete '" + selfPointer + "', invalidating 'this'"), ErrorPathItem(use, "Call method when 'this' is invalid") }; const std::string usestr = use ? use->str() : "x"; const std::string usemsg = use && use->function() ? ("Calling method '" + usestr + "()'") : ("Using member '" + usestr + "'"); reportError(errorPath, Severity::warning, "thisUseAfterFree", "$symbol:" + selfPointer + "\n" + usemsg + " when 'this' might be invalid", CWE(0), Certainty::normal); } void CheckClass::checkUnsafeClassRefMember() { if (!mSettings->safeChecks.classes || !mSettings->severity.isEnabled(Severity::warning)) return; for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { for (const Function &func : classScope->functionList) { if (!func.hasBody() || !func.isConstructor()) continue; const Token *initList = func.constructorMemberInitialization(); while (Token::Match(initList, "[:,] %name% (")) { if (Token::Match(initList->tokAt(2), "( %var% )")) { const Variable * const memberVar = initList->next()->variable(); const Variable * const argVar = initList->tokAt(3)->variable(); if (memberVar && argVar && memberVar->isConst() && memberVar->isReference() && argVar->isArgument() && argVar->isConst() && argVar->isReference()) unsafeClassRefMemberError(initList->next(), classScope->className + "::" + memberVar->name()); } initList = initList->linkAt(2)->next(); } } } } void CheckClass::unsafeClassRefMemberError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "unsafeClassRefMember", "$symbol:" + varname + "\n" "Unsafe class: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n" "Unsafe class checking: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues. If you pass a local variable or temporary value in this constructor argument, be extra careful. If the argument is always some global object that is never destroyed then this is safe usage. However it would be defensive to make the member '$symbol' a non-reference variable or a smart pointer.", CWE(0), Certainty::normal); } Check::FileInfo *CheckClass::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { if (!tokenizer->isCPP()) return nullptr; (void)settings; // One definition rule std::vector classDefinitions; for (const Scope * classScope : tokenizer->getSymbolDatabase()->classAndStructScopes) { if (classScope->isAnonymous()) continue; // the full definition must be compared bool fullDefinition = std::all_of(classScope->functionList.begin(), classScope->functionList.end(), [](const Function& f) { return f.hasBody(); }); if (!fullDefinition) continue; std::string name; const Scope *scope = classScope; while (scope->isClassOrStruct() && !classScope->className.empty()) { if (Token::Match(scope->classDef, "struct|class %name% :: %name%")) { // TODO handle such classnames name.clear(); break; } name = scope->className + "::" + name; scope = scope->nestedIn; } if (name.empty()) continue; name.erase(name.size() - 2); if (scope->type != Scope::ScopeType::eGlobal) continue; MyFileInfo::NameLoc nameLoc; nameLoc.className = name; nameLoc.fileName = tokenizer->list.file(classScope->classDef); nameLoc.lineNumber = classScope->classDef->linenr(); nameLoc.column = classScope->classDef->column(); // Calculate hash from the full class/struct definition std::string def; for (const Token *tok = classScope->classDef; tok != classScope->bodyEnd; tok = tok->next()) def += tok->str(); for (const Function &f: classScope->functionList) { if (f.functionScope && f.functionScope->nestedIn != classScope) { for (const Token *tok = f.functionScope->bodyStart; tok != f.functionScope->bodyEnd; tok = tok->next()) def += tok->str(); } } nameLoc.hash = std::hash {}(def); classDefinitions.push_back(nameLoc); } if (classDefinitions.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->classDefinitions.swap(classDefinitions); return fileInfo; } std::string CheckClass::MyFileInfo::toString() const { std::string ret; for (const MyFileInfo::NameLoc &nameLoc: classDefinitions) { ret += "\n"; } return ret; } Check::FileInfo * CheckClass::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { MyFileInfo *fileInfo = new MyFileInfo; for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "class") != 0) continue; const char *name = e->Attribute("name"); const char *file = e->Attribute("file"); const char *line = e->Attribute("line"); const char *col = e->Attribute("col"); const char *hash = e->Attribute("hash"); if (name && file && line && col && hash) { MyFileInfo::NameLoc nameLoc; nameLoc.className = name; nameLoc.fileName = file; nameLoc.lineNumber = std::atoi(line); nameLoc.column = std::atoi(col); nameLoc.hash = MathLib::toULongNumber(hash); fileInfo->classDefinitions.push_back(nameLoc); } } if (fileInfo->classDefinitions.empty()) { delete fileInfo; fileInfo = nullptr; } return fileInfo; } bool CheckClass::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { bool foundErrors = false; (void)ctu; // This argument is unused (void)settings; // This argument is unused std::unordered_map all; for (Check::FileInfo *fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); if (!fi) continue; for (const MyFileInfo::NameLoc &nameLoc : fi->classDefinitions) { auto it = all.find(nameLoc.className); if (it == all.end()) { all[nameLoc.className] = nameLoc; continue; } if (it->second.hash == nameLoc.hash) continue; // Same location, sometimes the hash is different wrongly (possibly because of different token simplifications). if (it->second.isSameLocation(nameLoc)) continue; std::list locationList; locationList.emplace_back(nameLoc.fileName, nameLoc.lineNumber, nameLoc.column); locationList.emplace_back(it->second.fileName, it->second.lineNumber, it->second.column); const ErrorMessage errmsg(locationList, emptyString, Severity::error, "$symbol:" + nameLoc.className + "\nThe one definition rule is violated, different classes/structs have the same name '$symbol'", "ctuOneDefinitionRuleViolation", CWE_ONE_DEFINITION_RULE, Certainty::normal); errorLogger.reportErr(errmsg); foundErrors = true; } } return foundErrors; } cppcheck-2.7/lib/checkclass.h000066400000000000000000000463411417746362400162120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkclassH #define checkclassH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "symboldatabase.h" #include "tokenize.h" #include "utils.h" #include #include #include #include #include #include class ErrorLogger; class Settings; class Token; namespace CTU { class FileInfo; } namespace tinyxml2 { class XMLElement; } /// @addtogroup Checks /// @{ /** @brief %Check classes. Uninitialized member variables, non-conforming operators, missing virtual destructor, etc */ class CPPCHECKLIB CheckClass : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckClass() : Check(myName()), mSymbolDatabase(nullptr) {} /** @brief This constructor is used when running checks. */ CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger); /** @brief Run checks on the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (tokenizer->isC()) return; CheckClass checkClass(tokenizer, settings, errorLogger); // can't be a simplified check .. the 'sizeof' is used. checkClass.checkMemset(); checkClass.constructors(); checkClass.privateFunctions(); checkClass.operatorEqRetRefThis(); checkClass.thisSubtraction(); checkClass.operatorEqToSelf(); checkClass.initializerListOrder(); checkClass.initializationListUsage(); checkClass.checkSelfInitialization(); checkClass.virtualDestructor(); checkClass.checkConst(); checkClass.copyconstructors(); checkClass.checkVirtualFunctionCallInConstructor(); checkClass.checkDuplInheritedMembers(); checkClass.checkExplicitConstructors(); checkClass.checkCopyCtorAndEqOperator(); checkClass.checkOverride(); checkClass.checkThisUseAfterFree(); checkClass.checkUnsafeClassRefMember(); } /** @brief %Check that all class constructors are ok */ void constructors(); /** @brief %Check that constructors with single parameter are explicit, * if they has to be.*/ void checkExplicitConstructors(); /** @brief %Check that all private functions are called */ void privateFunctions(); /** * @brief %Check that the memsets are valid. * The 'memset' function can do dangerous things if used wrong. If it * is used on STL containers for instance it will clear all its data * and then the STL container may leak memory or worse have an invalid state. * It can also overwrite the virtual table. * Important: The checking doesn't work on simplified tokens list. */ void checkMemset(); void checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes); /** @brief 'operator=' should return reference to *this */ void operatorEqRetRefThis(); // Warning upon no "return *this;" /** @brief 'operator=' should check for assignment to self */ void operatorEqToSelf(); // Warning upon no check for assignment to self /** @brief The destructor in a base class should be virtual */ void virtualDestructor(); /** @brief warn for "this-x". The indented code may be "this->x" */ void thisSubtraction(); /** @brief can member function be const? */ void checkConst(); /** @brief Check initializer list order */ void initializerListOrder(); /** @brief Suggest using initialization list */ void initializationListUsage(); /** @brief Check for initialization of a member with itself */ void checkSelfInitialization(); void copyconstructors(); /** @brief call of virtual function in constructor/destructor */ void checkVirtualFunctionCallInConstructor(); /** @brief Check duplicated inherited members */ void checkDuplInheritedMembers(); /** @brief Check that copy constructor and operator defined together */ void checkCopyCtorAndEqOperator(); /** @brief Check that the override keyword is used when overriding virtual functions */ void checkOverride(); /** @brief When "self pointer" is destroyed, 'this' might become invalid. */ void checkThisUseAfterFree(); /** @brief Unsafe class check - const reference member */ void checkUnsafeClassRefMember(); /* multifile checking; one definition rule violations */ class MyFileInfo : public Check::FileInfo { public: struct NameLoc { std::string className; std::string fileName; int lineNumber; int column; std::size_t hash; bool operator==(const NameLoc& other) const { return isSameLocation(other) && hash == other.hash; } bool isSameLocation(const NameLoc& other) const { return fileName == other.fileName && lineNumber == other.lineNumber && column == other.column; } }; std::vector classDefinitions; /** Convert MyFileInfo data into xml string */ std::string toString() const OVERRIDE; }; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; private: const SymbolDatabase *mSymbolDatabase; // Reporting errors.. void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct); //void copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& var_name); void copyConstructorShallowCopyError(const Token *tok, const std::string& varname); void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc); void uninitVarError(const Token *tok, bool isprivate, const std::string &classname, const std::string &varname, bool derived, bool inconclusive); void missingMemberCopyError(const Token *tok, const std::string& classname, const std::string& varname); void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive); void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname); void memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type); void memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type); void memsetErrorFloat(const Token *tok, const std::string &type); void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname); void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok); void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive); void thisSubtractionError(const Token *tok); void operatorEqRetRefThisError(const Token *tok); void operatorEqShouldBeLeftUnimplementedError(const Token *tok); void operatorEqMissingReturnStatementError(const Token *tok, bool error); void operatorEqToSelfError(const Token *tok); void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname); void suggestInitializationList(const Token *tok, const std::string& varname); void selfInitializationError(const Token* tok, const std::string& varname); void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &funcname); void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &variableName, bool derivedIsStruct, bool baseIsStruct); void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor); void overrideError(const Function *funcInBase, const Function *funcInDerived); void thisUseAfterFree(const Token *self, const Token *free, const Token *use); void unsafeClassRefMemberError(const Token *tok, const std::string &varname); void checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckClass c(nullptr, settings, errorLogger); c.noConstructorError(nullptr, "classname", false); c.noExplicitConstructorError(nullptr, "classname", false); //c.copyConstructorMallocError(nullptr, 0, "var"); c.copyConstructorShallowCopyError(nullptr, "var"); c.noCopyConstructorError(nullptr, false, nullptr, false); c.noOperatorEqError(nullptr, false, nullptr, false); c.noDestructorError(nullptr, false, nullptr); c.uninitVarError(nullptr, false, "classname", "varname", false, false); c.uninitVarError(nullptr, true, "classname", "varnamepriv", false, false); c.uninitVarError(nullptr, false, "classname", "varname", true, false); c.uninitVarError(nullptr, true, "classname", "varnamepriv", true, false); c.missingMemberCopyError(nullptr, "classname", "varnamepriv"); c.operatorEqVarError(nullptr, "classname", emptyString, false); c.unusedPrivateFunctionError(nullptr, "classname", "funcname"); c.memsetError(nullptr, "memfunc", "classname", "class"); c.memsetErrorReference(nullptr, "memfunc", "class"); c.memsetErrorFloat(nullptr, "class"); c.mallocOnClassWarning(nullptr, "malloc", nullptr); c.mallocOnClassError(nullptr, "malloc", nullptr, "std::string"); c.virtualDestructorError(nullptr, "Base", "Derived", false); c.thisSubtractionError(nullptr); c.operatorEqRetRefThisError(nullptr); c.operatorEqMissingReturnStatementError(nullptr, true); c.operatorEqShouldBeLeftUnimplementedError(nullptr); c.operatorEqToSelfError(nullptr); c.checkConstError(nullptr, "class", "function", false); c.checkConstError(nullptr, "class", "function", true); c.initializerListError(nullptr, nullptr, "class", "variable"); c.suggestInitializationList(nullptr, "variable"); c.selfInitializationError(nullptr, "var"); c.duplInheritedMembersError(nullptr, nullptr, "class", "class", "variable", false, false); c.copyCtorAndEqOperatorError(nullptr, "class", false, false); c.pureVirtualFunctionCallInConstructorError(nullptr, std::list(), "f"); c.virtualFunctionCallInConstructorError(nullptr, std::list(), "f"); c.overrideError(nullptr, nullptr); c.thisUseAfterFree(nullptr, nullptr, nullptr); c.unsafeClassRefMemberError(nullptr, "UnsafeClass::var"); } static std::string myName() { return "Class"; } std::string classInfo() const OVERRIDE { return "Check the code for each class.\n" "- Missing constructors and copy constructors\n" //"- Missing allocation of memory in copy constructor\n" "- Constructors which should be explicit\n" "- Are all variables initialized by the constructors?\n" "- Are all variables assigned by 'operator='?\n" "- Warn if memset, memcpy etc are used on a class\n" "- Warn if memory for classes is allocated with malloc()\n" "- If it's a base class, check that the destructor is virtual\n" "- Are there unused private functions?\n" "- 'operator=' should check for assignment to self\n" "- Constness for member functions\n" "- Order of initializations\n" "- Suggest usage of initialization list\n" "- Initialization of a member with itself\n" "- Suspicious subtraction from 'this'\n" "- Call of pure virtual function in constructor/destructor\n" "- Duplicated inherited data members\n" // disabled for now "- If 'copy constructor' defined, 'operator=' also should be defined and vice versa\n" "- Check that arbitrary usage of public interface does not result in division by zero\n" "- Delete \"self pointer\" and then access 'this'\n" "- Check that the 'override' keyword is used when overriding virtual functions\n" "- Check that the 'one definition rule' is not violated\n"; } // operatorEqRetRefThis helper functions void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last); void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions); // operatorEqToSelf helper functions bool hasAllocation(const Function *func, const Scope* scope) const; bool hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const; bool hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const; static bool hasAssignSelf(const Function *func, const Token *rhs, const Token **out_ifStatementScopeStart); enum class Bool { TRUE, FALSE, BAILOUT }; static Bool isInverted(const Token *tok, const Token *rhs); static const Token * getIfStmtBodyStart(const Token *tok, const Token *rhs); // checkConst helper functions bool isMemberVar(const Scope *scope, const Token *tok) const; bool isMemberFunc(const Scope *scope, const Token *tok) const; bool isConstMemberFunc(const Scope *scope, const Token *tok) const; bool checkConstFunc(const Scope *scope, const Function *func, bool& memberAccessed) const; // constructors helper function /** @brief Information about a member variable. Used when checking for uninitialized variables */ struct Usage { explicit Usage(const Variable *var) : var(var), assign(false), init(false) {} /** Variable that this usage is for */ const Variable *var; /** @brief has this variable been assigned? */ bool assign; /** @brief has this variable been initialized? */ bool init; }; static bool isBaseClassFunc(const Token *tok, const Scope *scope); /** * @brief Create usage list that contains all scope members and also members * of base classes without constructors. * @param scope current class scope */ static std::vector createUsageList(const Scope *scope); /** * @brief assign a variable in the varlist * @param usageList reference to usage vector * @param varid id of variable to mark assigned */ static void assignVar(std::vector &usageList, nonneg int varid); /** * @brief initialize a variable in the varlist * @param usageList reference to usage vector * @param varid id of variable to mark initialized */ static void initVar(std::vector &usageList, nonneg int varid); /** * @brief set all variables in list assigned * @param usageList reference to usage vector */ static void assignAllVar(std::vector &usageList); /** * @brief set all variables in list not assigned and not initialized * @param usageList reference to usage vector */ static void clearAllVar(std::vector &usageList); /** * @brief parse a scope for a constructor or member function and set the "init" flags in the provided varlist * @param func reference to the function that should be checked * @param callstack the function doesn't look into recursive function calls. * @param scope pointer to variable Scope * @param usage reference to usage vector */ void initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage); /** * @brief gives a list of tokens where virtual functions are called directly or indirectly * @param function function to be checked * @param virtualFunctionCallsMap map of results for already checked functions * @return list of tokens where pure virtual functions are called */ const std::list & getVirtualFunctionCalls( const Function & function, std::map> & virtualFunctionCallsMap); /** * @brief looks for the first virtual function call stack * @param virtualFunctionCallsMap map of results obtained from getVirtualFunctionCalls * @param callToken token where pure virtual function is called directly or indirectly * @param[in,out] pureFuncStack list to append the stack */ void getFirstVirtualFunctionCallStack( std::map> & virtualFunctionCallsMap, const Token *callToken, std::list & pureFuncStack); static bool canNotCopy(const Scope *scope); static bool canNotMove(const Scope *scope); /** * @brief Helper for checkThisUseAfterFree */ bool checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token **freeToken); }; /// @} //--------------------------------------------------------------------------- #endif // checkclassH cppcheck-2.7/lib/checkcondition.cpp000066400000000000000000002401451417746362400174240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Check for condition mismatches //--------------------------------------------------------------------------- #include "checkcondition.h" #include "astutils.h" #include "library.h" #include "platform.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include "checkother.h" // comparisonNonZeroExpressionLessThanZero and testIfNonZeroExpressionIsPositive #include #include #include #include #include #include #include #include // CWE ids used static const struct CWE uncheckedErrorConditionCWE(391U); static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE570(570U); // Expression is Always False static const struct CWE CWE571(571U); // Expression is Always True //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckCondition instance; } bool CheckCondition::diag(const Token* tok, bool insert) { if (!tok) return false; const Token* parent = tok->astParent(); bool hasParent = false; while (Token::Match(parent, "&&|%oror%")) { if (mCondDiags.count(parent) != 0) { hasParent = true; break; } parent = parent->astParent(); } if (mCondDiags.count(tok) == 0 && !hasParent) { if (insert) mCondDiags.insert(tok); return false; } return true; } bool CheckCondition::isAliased(const std::set &vars) const { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "= & %var% ;") && vars.find(tok->tokAt(2)->varId()) != vars.end()) return true; } return false; } void CheckCondition::assignIf() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "=") continue; if (Token::Match(tok->tokAt(-2), "[;{}] %var% =")) { const Variable *var = tok->previous()->variable(); if (var == nullptr) continue; char bitop = '\0'; MathLib::bigint num = 0; if (Token::Match(tok->next(), "%num% [&|]")) { bitop = tok->strAt(2).at(0); num = MathLib::toLongNumber(tok->next()->str()); } else { const Token *endToken = Token::findsimplematch(tok, ";"); // Casting address if (endToken && Token::Match(endToken->tokAt(-4), "* ) & %any% ;")) endToken = nullptr; if (endToken && Token::Match(endToken->tokAt(-2), "[&|] %num% ;")) { bitop = endToken->strAt(-2).at(0); num = MathLib::toLongNumber(endToken->previous()->str()); } } if (bitop == '\0') continue; if (num < 0 && bitop == '|') continue; assignIfParseScope(tok, tok->tokAt(4), var->declarationId(), var->isLocal(), bitop, num); } } } static bool isParameterChanged(const Token *partok) { bool addressOf = Token::Match(partok, "[(,] &"); int argumentNumber = 0; const Token *ftok; for (ftok = partok; ftok && ftok->str() != "("; ftok = ftok->previous()) { if (ftok->str() == ")") ftok = ftok->link(); else if (argumentNumber == 0U && ftok->str() == "&") addressOf = true; else if (ftok->str() == ",") argumentNumber++; } ftok = ftok ? ftok->previous() : nullptr; if (!(ftok && ftok->function())) return true; const Variable *par = ftok->function()->getArgumentVar(argumentNumber); if (!par) return true; if (par->isConst()) return false; if (addressOf || par->isReference() || par->isPointer()) return true; return false; } /** parse scopes recursively */ bool CheckCondition::assignIfParseScope(const Token * const assignTok, const Token * const startTok, const nonneg int varid, const bool islocal, const char bitop, const MathLib::bigint num) { bool ret = false; for (const Token *tok2 = startTok; tok2; tok2 = tok2->next()) { if ((bitop == '&') && Token::Match(tok2->tokAt(2), "%varid% %cop% %num% ;", varid) && tok2->strAt(3) == std::string(1U, bitop)) { const MathLib::bigint num2 = MathLib::toLongNumber(tok2->strAt(4)); if (0 == (num & num2)) mismatchingBitAndError(assignTok, num, tok2, num2); } if (Token::Match(tok2, "%varid% =", varid)) { return true; } if (bitop == '&' && Token::Match(tok2, "%varid% &= %num% ;", varid)) { const MathLib::bigint num2 = MathLib::toLongNumber(tok2->strAt(2)); if (0 == (num & num2)) mismatchingBitAndError(assignTok, num, tok2, num2); } if (Token::Match(tok2, "++|-- %varid%", varid) || Token::Match(tok2, "%varid% ++|--", varid)) return true; if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid) && isParameterChanged(tok2)) return true; if (tok2->str() == "}") return false; if (Token::Match(tok2, "break|continue|return")) ret = true; if (ret && tok2->str() == ";") return false; if (!islocal && Token::Match(tok2, "%name% (") && !Token::simpleMatch(tok2->next()->link(), ") {")) return true; if (Token::Match(tok2, "if|while (")) { if (!islocal && tok2->str() == "while") continue; if (tok2->str() == "while") { // is variable changed in loop? const Token *bodyStart = tok2->linkAt(1)->next(); const Token *bodyEnd = bodyStart ? bodyStart->link() : nullptr; if (!bodyEnd || bodyEnd->str() != "}" || isVariableChanged(bodyStart, bodyEnd, varid, !islocal, mSettings, mTokenizer->isCPP())) continue; } // parse condition const Token * const end = tok2->next()->link(); for (; tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid)) { return true; } if (Token::Match(tok2,"&&|%oror%|( %varid% ==|!= %num% &&|%oror%|)", varid)) { const Token *vartok = tok2->next(); const MathLib::bigint num2 = MathLib::toLongNumber(vartok->strAt(2)); if ((num & num2) != ((bitop=='&') ? num2 : num)) { const std::string& op(vartok->strAt(1)); const bool alwaysTrue = op == "!="; const std::string condition(vartok->str() + op + vartok->strAt(2)); assignIfError(assignTok, tok2, condition, alwaysTrue); } } if (Token::Match(tok2, "%varid% %op%", varid) && tok2->next()->isAssignmentOp()) { return true; } } const bool ret1 = assignIfParseScope(assignTok, end->tokAt(2), varid, islocal, bitop, num); bool ret2 = false; if (Token::simpleMatch(end->next()->link(), "} else {")) ret2 = assignIfParseScope(assignTok, end->next()->link()->tokAt(3), varid, islocal, bitop, num); if (ret1 || ret2) return true; } } return false; } void CheckCondition::assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result) { if (tok2 && diag(tok2->tokAt(2))) return; std::list locations = { tok1, tok2 }; reportError(locations, Severity::style, "assignIfError", "Mismatching assignment and comparison, comparison '" + condition + "' is always " + std::string(result ? "true" : "false") + ".", CWE398, Certainty::normal); } void CheckCondition::mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2) { std::list locations = { tok1, tok2 }; std::ostringstream msg; msg << "Mismatching bitmasks. Result is always 0 (" << "X = Y & 0x" << std::hex << num1 << "; Z = X & 0x" << std::hex << num2 << "; => Z=0)."; reportError(locations, Severity::style, "mismatchingBitAnd", msg.str(), CWE398, Certainty::normal); } static void getnumchildren(const Token *tok, std::list &numchildren) { if (tok->astOperand1() && tok->astOperand1()->isNumber()) numchildren.push_back(MathLib::toLongNumber(tok->astOperand1()->str())); else if (tok->astOperand1() && tok->str() == tok->astOperand1()->str()) getnumchildren(tok->astOperand1(), numchildren); if (tok->astOperand2() && tok->astOperand2()->isNumber()) numchildren.push_back(MathLib::toLongNumber(tok->astOperand2()->str())); else if (tok->astOperand2() && tok->str() == tok->astOperand2()->str()) getnumchildren(tok->astOperand2(), numchildren); } /* Return whether tok is in the body for a function returning a boolean. */ static bool inBooleanFunction(const Token *tok) { const Scope *scope = tok ? tok->scope() : nullptr; while (scope && scope->isLocal()) scope = scope->nestedIn; if (scope && scope->type == Scope::eFunction) { const Function *func = scope->function; if (func) { const Token *ret = func->retDef; while (Token::Match(ret, "static|const")) ret = ret->next(); return Token::Match(ret, "bool|_Bool"); } } return false; } void CheckCondition::checkBadBitmaskCheck() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "|" && tok->astOperand1() && tok->astOperand2() && tok->astParent()) { const Token* parent = tok->astParent(); const bool isBoolean = Token::Match(parent, "&&|%oror%") || (parent->str() == "?" && parent->astOperand1() == tok) || (parent->str() == "=" && parent->astOperand2() == tok && parent->astOperand1() && parent->astOperand1()->variable() && Token::Match(parent->astOperand1()->variable()->typeStartToken(), "bool|_Bool")) || (parent->str() == "(" && Token::Match(parent->astOperand1(), "if|while")) || (parent->str() == "return" && parent->astOperand1() == tok && inBooleanFunction(tok)); const bool isTrue = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue != 0) || (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue != 0); if (isBoolean && isTrue) badBitmaskCheckError(tok); } } } void CheckCondition::badBitmaskCheckError(const Token *tok) { reportError(tok, Severity::warning, "badBitmaskCheck", "Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?", CWE571, Certainty::normal); } void CheckCondition::comparison() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token *expr1 = tok->astOperand1(); const Token *expr2 = tok->astOperand2(); if (!expr1 || !expr2) continue; if (expr1->isNumber()) std::swap(expr1,expr2); if (!expr2->isNumber()) continue; const MathLib::bigint num2 = MathLib::toLongNumber(expr2->str()); if (num2 < 0) continue; if (!Token::Match(expr1,"[&|]")) continue; std::list numbers; getnumchildren(expr1, numbers); for (const MathLib::bigint num1 : numbers) { if (num1 < 0) continue; if (Token::Match(tok, "==|!=")) { if ((expr1->str() == "&" && (num1 & num2) != num2) || (expr1->str() == "|" && (num1 | num2) != num2)) { const std::string& op(tok->str()); comparisonError(expr1, expr1->str(), num1, op, num2, op=="==" ? false : true); } } else if (expr1->str() == "&") { const bool or_equal = Token::Match(tok, ">=|<="); const std::string& op(tok->str()); if ((Token::Match(tok, ">=|<")) && (num1 < num2)) { comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? false : true); } else if ((Token::Match(tok, "<=|>")) && (num1 <= num2)) { comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? true : false); } } else if (expr1->str() == "|") { if ((expr1->astOperand1()->valueType()) && (expr1->astOperand1()->valueType()->sign == ValueType::Sign::UNSIGNED)) { const bool or_equal = Token::Match(tok, ">=|<="); const std::string& op(tok->str()); if ((Token::Match(tok, ">=|<")) && (num1 >= num2)) { //"(a | 0x07) >= 7U" is always true for unsigned a //"(a | 0x07) < 7U" is always false for unsigned a comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? true : false); } else if ((Token::Match(tok, "<=|>")) && (num1 > num2)) { //"(a | 0x08) <= 7U" is always false for unsigned a //"(a | 0x07) > 6U" is always true for unsigned a comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? false : true); } } } } } } void CheckCondition::comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result) { std::ostringstream expression; expression << std::hex << "(X " << bitop << " 0x" << value1 << ") " << op << " 0x" << value2; const std::string errmsg("Expression '" + expression.str() + "' is always " + (result?"true":"false") + ".\n" "The expression '" + expression.str() + "' is always " + (result?"true":"false") + ". Check carefully constants and operators used, these errors might be hard to " "spot sometimes. In case of complex expression it might help to split it to " "separate expressions."); reportError(tok, Severity::style, "comparisonError", errmsg, CWE398, Certainty::normal); } bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const { if (!cond1 || !cond2) return false; // same expressions if (isSameExpression(mTokenizer->isCPP(), true, cond1, cond2, mSettings->library, pure, false)) return true; // bitwise overlap for example 'x&7' and 'x==1' if (cond1->str() == "&" && cond1->astOperand1() && cond2->astOperand2()) { const Token *expr1 = cond1->astOperand1(); const Token *num1 = cond1->astOperand2(); if (!num1) // unary operator& return false; if (!num1->isNumber()) std::swap(expr1,num1); if (!num1->isNumber() || MathLib::isNegative(num1->str())) return false; if (!Token::Match(cond2, "&|==") || !cond2->astOperand1() || !cond2->astOperand2()) return false; const Token *expr2 = cond2->astOperand1(); const Token *num2 = cond2->astOperand2(); if (!num2->isNumber()) std::swap(expr2,num2); if (!num2->isNumber() || MathLib::isNegative(num2->str())) return false; if (!isSameExpression(mTokenizer->isCPP(), true, expr1, expr2, mSettings->library, pure, false)) return false; const MathLib::bigint value1 = MathLib::toLongNumber(num1->str()); const MathLib::bigint value2 = MathLib::toLongNumber(num2->str()); if (cond2->str() == "&") return ((value1 & value2) == value2); return ((value1 & value2) > 0); } return false; } void CheckCondition::duplicateCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; const Token* tok2 = scope.classDef->next(); if (!tok2) continue; const Token* cond1 = tok2->astOperand2(); if (!cond1) continue; if (cond1->hasKnownIntValue()) continue; tok2 = tok2->link(); if (!Token::simpleMatch(tok2, ") {")) continue; tok2 = tok2->linkAt(1); if (!Token::simpleMatch(tok2, "} if (")) continue; const Token *cond2 = tok2->tokAt(2)->astOperand2(); if (!cond2) continue; ErrorPath errorPath; if (!isExpressionChanged(cond1, scope.classDef->next(), cond2, mSettings, mTokenizer->isCPP()) && isSameExpression(mTokenizer->isCPP(), true, cond1, cond2, mSettings->library, true, true, &errorPath)) duplicateConditionError(cond1, cond2, errorPath); } } void CheckCondition::duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; errorPath.emplace_back(tok1, "First condition"); errorPath.emplace_back(tok2, "Second condition"); std::string msg = "The if condition is the same as the previous if condition"; reportError(errorPath, Severity::style, "duplicateCondition", msg, CWE398, Certainty::normal); } void CheckCondition::multiCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; const Token * const cond1 = scope.classDef->next()->astOperand2(); if (!cond1) continue; const Token * tok2 = scope.classDef->next(); // Check each 'else if' for (;;) { tok2 = tok2->link(); if (!Token::simpleMatch(tok2, ") {")) break; tok2 = tok2->linkAt(1); if (!Token::simpleMatch(tok2, "} else { if (")) break; tok2 = tok2->tokAt(4); if (tok2->astOperand2()) { ErrorPath errorPath; if (isOverlappingCond(cond1, tok2->astOperand2(), true)) overlappingElseIfConditionError(tok2->astOperand2(), cond1->linenr()); else if (isOppositeCond(true, mTokenizer->isCPP(), cond1, tok2->astOperand2(), mSettings->library, true, true, &errorPath)) oppositeElseIfConditionError(cond1, tok2->astOperand2(), errorPath); } } } } void CheckCondition::overlappingElseIfConditionError(const Token *tok, nonneg int line1) { if (diag(tok)) return; std::ostringstream errmsg; errmsg << "Expression is always false because 'else if' condition matches previous condition at line " << line1 << "."; reportError(tok, Severity::style, "multiCondition", errmsg.str(), CWE398, Certainty::normal); } void CheckCondition::oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath) { if (diag(ifCond) & diag(elseIfCond)) return; std::ostringstream errmsg; errmsg << "Expression is always true because 'else if' condition is opposite to previous condition at line " << ifCond->linenr() << "."; errorPath.emplace_back(ifCond, "first condition"); errorPath.emplace_back(elseIfCond, "else if condition is opposite to first condition"); reportError(errorPath, Severity::style, "multiCondition", errmsg.str(), CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // - Opposite inner conditions => always false // - (TODO) Same/Overlapping inner condition => always true // - same condition after early exit => always false //--------------------------------------------------------------------------- static bool isNonConstFunctionCall(const Token *ftok, const Library &library) { if (library.isFunctionConst(ftok)) return false; const Token *obj = ftok->next()->astOperand1(); while (obj && obj->str() == ".") obj = obj->astOperand1(); if (!obj) return true; else if (obj->variable() && obj->variable()->isConst()) return false; else if (ftok->function() && ftok->function()->isConst()) return false; return true; } void CheckCondition::multiCondition2() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { const Token *condTok = nullptr; if (scope.type == Scope::eIf || scope.type == Scope::eWhile) condTok = scope.classDef->next()->astOperand2(); else if (scope.type == Scope::eFor) { condTok = scope.classDef->next()->astOperand2(); if (!condTok || condTok->str() != ";") continue; condTok = condTok->astOperand2(); if (!condTok || condTok->str() != ";") continue; condTok = condTok->astOperand1(); } if (!condTok) continue; const Token * const cond1 = condTok; if (!Token::simpleMatch(scope.classDef->linkAt(1), ") {")) continue; bool functionCall = false; bool nonConstFunctionCall = false; bool nonlocal = false; // nonlocal variable used in condition std::set vars; // variables used in condition visitAstNodes(condTok, [&](const Token *cond) { if (Token::Match(cond, "%name% (")) { functionCall = true; nonConstFunctionCall = isNonConstFunctionCall(cond, mSettings->library); if (nonConstFunctionCall) return ChildrenToVisit::done; } if (cond->varId()) { vars.insert(cond->varId()); const Variable *var = cond->variable(); if (!nonlocal && var) { if (!(var->isLocal() || var->isArgument())) nonlocal = true; else if ((var->isPointer() || var->isReference()) && !Token::Match(cond->astParent(), "%oror%|&&|!")) // TODO: if var is pointer check what it points at nonlocal = true; } } else if (!nonlocal && cond->isName()) { // varid is 0. this is possibly a nonlocal variable.. nonlocal = Token::Match(cond->astParent(), "%cop%|(|[") || Token::Match(cond, "%name% .") || (mTokenizer->isCPP() && cond->str() == "this"); } else { return ChildrenToVisit::op1_and_op2; } return ChildrenToVisit::none; }); if (nonConstFunctionCall) continue; std::vector varsInCond; visitAstNodes(condTok, [&varsInCond](const Token *cond) { if (cond->variable()) { const Variable *var = cond->variable(); if (std::find(varsInCond.begin(), varsInCond.end(), var) == varsInCond.end()) varsInCond.push_back(var); } return ChildrenToVisit::op1_and_op2; }); // parse until second condition is reached.. enum MULTICONDITIONTYPE { INNER, AFTER }; const Token *tok; // Parse inner condition first and then early return condition std::vector types = {MULTICONDITIONTYPE::INNER}; if (Token::Match(scope.bodyStart, "{ return|throw|continue|break")) types.push_back(MULTICONDITIONTYPE::AFTER); for (MULTICONDITIONTYPE type:types) { if (type == MULTICONDITIONTYPE::AFTER) { tok = scope.bodyEnd->next(); } else { tok = scope.bodyStart; } const Token * const endToken = tok->scope()->bodyEnd; for (; tok && tok != endToken; tok = tok->next()) { if (isExpressionChangedAt(cond1, tok, 0, false, mSettings, mTokenizer->isCPP())) break; if (Token::Match(tok, "if|return")) { const Token * condStartToken = tok->str() == "if" ? tok->next() : tok; const Token * condEndToken = tok->str() == "if" ? condStartToken->link() : Token::findsimplematch(condStartToken, ";"); // Does condition modify tracked variables? if (isExpressionChanged(cond1, condStartToken, condEndToken, mSettings, mTokenizer->isCPP())) break; // Condition.. const Token *cond2 = tok->str() == "if" ? condStartToken->astOperand2() : condStartToken->astOperand1(); const bool isReturnVar = (tok->str() == "return" && !Token::Match(cond2, "%cop%")); ErrorPath errorPath; if (type == MULTICONDITIONTYPE::INNER) { visitAstNodes(cond1, [&](const Token* firstCondition) { if (!firstCondition) return ChildrenToVisit::none; if (firstCondition->str() == "&&") { if (!isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true)) return ChildrenToVisit::op1_and_op2; } if (!firstCondition->hasKnownIntValue()) { if (!isReturnVar && isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) { if (!isAliased(vars)) oppositeInnerConditionError(firstCondition, cond2, errorPath); } else if (!isReturnVar && isSameExpression(mTokenizer->isCPP(), true, firstCondition, cond2, mSettings->library, true, true, &errorPath)) { identicalInnerConditionError(firstCondition, cond2, errorPath); } } return ChildrenToVisit::none; }); } else { visitAstNodes(cond2, [&](const Token *secondCondition) { if (secondCondition->str() == "||" || secondCondition->str() == "&&") return ChildrenToVisit::op1_and_op2; if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) && isSameExpression(mTokenizer->isCPP(), true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) { if (!isAliased(vars) && !mTokenizer->hasIfdef(cond1, secondCondition)) { identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath); return ChildrenToVisit::done; } } return ChildrenToVisit::none; }); } } if (Token::Match(tok, "%name% (") && isVariablesChanged(tok, tok->linkAt(1), 0, varsInCond, mSettings, mTokenizer->isCPP())) { break; } if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables break; if (Token::Match(tok, "case|break|continue|return|throw") && tok->scope() == endToken->scope()) break; if (Token::Match(tok, "[;{}] %name% :")) break; // bailout if loop is seen. // TODO: handle loops better. if (Token::Match(tok, "for|while|do")) { const Token *tok1 = tok->next(); const Token *tok2; if (Token::simpleMatch(tok, "do {")) { if (!Token::simpleMatch(tok->linkAt(1), "} while (")) break; tok2 = tok->linkAt(1)->linkAt(2); } else if (Token::Match(tok, "if|while (")) { tok2 = tok->linkAt(1); if (Token::simpleMatch(tok2, ") {")) tok2 = tok2->linkAt(1); if (!tok2) break; } else { // Incomplete code break; } bool changed = false; for (int varid : vars) { if (isVariableChanged(tok1, tok2, varid, nonlocal, mSettings, mTokenizer->isCPP())) { changed = true; break; } } if (changed) break; } if ((tok->varId() && vars.find(tok->varId()) != vars.end()) || (!tok->varId() && nonlocal) || (functionCall && tok->variable() && !tok->variable()->isLocal())) { if (Token::Match(tok, "%name% %assign%|++|--")) break; if (Token::Match(tok->astParent(), "*|.|[")) { const Token *parent = tok; while (Token::Match(parent->astParent(), ".|[") || (parent->astParent() && parent->astParent()->isUnaryOp("*"))) parent = parent->astParent(); if (Token::Match(parent->astParent(), "%assign%|++|--")) break; } if (mTokenizer->isCPP() && Token::Match(tok, "%name% <<") && (!tok->valueType() || !tok->valueType()->isIntegral())) break; if (isLikelyStreamRead(mTokenizer->isCPP(), tok->next()) || isLikelyStreamRead(mTokenizer->isCPP(), tok->previous())) break; if (Token::Match(tok, "%name% [")) { const Token *tok2 = tok->linkAt(1); while (Token::simpleMatch(tok2, "] [")) tok2 = tok2->linkAt(1); if (Token::Match(tok2, "] %assign%|++|--")) break; } if (Token::Match(tok->previous(), "++|--|& %name%")) break; if (tok->variable() && !tok->variable()->isConst() && Token::Match(tok, "%name% . %name% (")) { const Function* function = tok->tokAt(2)->function(); if (!function || !function->isConst()) break; } if (Token::Match(tok->previous(), "[(,] %name% [,)]") && isParameterChanged(tok)) break; } } } } } static std::string innerSmtString(const Token * tok) { if (!tok) return "if"; if (!tok->astTop()) return "if"; const Token * top = tok->astTop(); if (top->str() == "(" && top->astOperand1()) return top->astOperand1()->str(); return top->str(); } void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; const std::string s1(tok1 ? tok1->expressionString() : "x"); const std::string s2(tok2 ? tok2->expressionString() : "!x"); const std::string innerSmt = innerSmtString(tok2); errorPath.emplace_back(ErrorPathItem(tok1, "outer condition: " + s1)); errorPath.emplace_back(ErrorPathItem(tok2, "opposite inner condition: " + s2)); const std::string msg("Opposite inner '" + innerSmt + "' condition leads to a dead code block.\n" "Opposite inner '" + innerSmt + "' condition leads to a dead code block (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); reportError(errorPath, Severity::warning, "oppositeInnerCondition", msg, CWE398, Certainty::normal); } void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; const std::string s1(tok1 ? tok1->expressionString() : "x"); const std::string s2(tok2 ? tok2->expressionString() : "x"); const std::string innerSmt = innerSmtString(tok2); errorPath.emplace_back(ErrorPathItem(tok1, "outer condition: " + s1)); errorPath.emplace_back(ErrorPathItem(tok2, "identical inner condition: " + s2)); const std::string msg("Identical inner '" + innerSmt + "' condition is always true.\n" "Identical inner '" + innerSmt + "' condition is always true (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); reportError(errorPath, Severity::warning, "identicalInnerCondition", msg, CWE398, Certainty::normal); } void CheckCondition::identicalConditionAfterEarlyExitError(const Token *cond1, const Token* cond2, ErrorPath errorPath) { if (diag(cond1) & diag(cond2)) return; const bool isReturnValue = cond2 && Token::simpleMatch(cond2->astParent(), "return"); const std::string cond(cond1 ? cond1->expressionString() : "x"); const std::string value = (cond2 && cond2->valueType() && cond2->valueType()->type == ValueType::Type::BOOL) ? "false" : "0"; errorPath.emplace_back(ErrorPathItem(cond1, "If condition '" + cond + "' is true, the function will return/exit")); errorPath.emplace_back(ErrorPathItem(cond2, (isReturnValue ? "Returning identical expression '" : "Testing identical condition '") + cond + "'")); reportError(errorPath, Severity::warning, "identicalConditionAfterEarlyExit", isReturnValue ? ("Identical condition and return expression '" + cond + "', return value is always " + value) : ("Identical condition '" + cond + "', second condition is always false"), CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // if ((x != 1) || (x != 3)) // expression always true // if ((x == 1) && (x == 3)) // expression always false // if ((x < 1) && (x > 3)) // expression always false // if ((x > 3) || (x < 10)) // expression always true // if ((x > 5) && (x != 1)) // second comparison always true // // Check for suspect logic for an expression consisting of 2 comparison // expressions with a shared variable and constants and a logical operator // between them. // // Suggest a different logical operator when the logical operator between // the comparisons is probably wrong. // // Inform that second comparison is always true when first comparison is true. //--------------------------------------------------------------------------- static std::string invertOperatorForOperandSwap(std::string s) { if (s[0] == '<') s[0] = '>'; else if (s[0] == '>') s[0] = '<'; return s; } template static bool checkIntRelation(const std::string &op, const T value1, const T value2) { return (op == "==" && value1 == value2) || (op == "!=" && value1 != value2) || (op == ">" && value1 > value2) || (op == ">=" && value1 >= value2) || (op == "<" && value1 < value2) || (op == "<=" && value1 <= value2); } static bool checkFloatRelation(const std::string &op, const double value1, const double value2) { return (op == ">" && value1 > value2) || (op == ">=" && value1 >= value2) || (op == "<" && value1 < value2) || (op == "<=" && value1 <= value2); } template T getvalue3(const T value1, const T value2) { const T min = std::min(value1, value2); if (min== std::numeric_limits::max()) return min; else return min+1; // see #5895 } template<> double getvalue3(const double value1, const double value2) { return (value1 + value2) / 2.0f; } template static inline T getvalue(const int test, const T value1, const T value2) { // test: // 1 => return value that is less than both value1 and value2 // 2 => return value1 // 3 => return value that is between value1 and value2 // 4 => return value2 // 5 => return value that is larger than both value1 and value2 switch (test) { case 1: return std::numeric_limits::lowest(); case 2: return value1; case 3: return getvalue3(value1, value2); case 4: return value2; case 5: return std::numeric_limits::max(); } return 0; } static bool parseComparison(const Token *comp, bool *not1, std::string *op, std::string *value, const Token **expr, bool* inconclusive) { *not1 = false; while (comp && comp->str() == "!") { *not1 = !(*not1); comp = comp->astOperand1(); } if (!comp) return false; const Token* op1 = comp->astOperand1(); const Token* op2 = comp->astOperand2(); if (!comp->isComparisonOp() || !op1 || !op2) { *op = "!="; *value = "0"; *expr = comp; } else if (op1->isLiteral()) { if (op1->isExpandedMacro()) return false; *op = invertOperatorForOperandSwap(comp->str()); if (op1->enumerator() && op1->enumerator()->value_known) *value = MathLib::toString(op1->enumerator()->value); else *value = op1->str(); *expr = op2; } else if (comp->astOperand2()->isLiteral()) { if (op2->isExpandedMacro()) return false; *op = comp->str(); if (op2->enumerator() && op2->enumerator()->value_known) *value = MathLib::toString(op2->enumerator()->value); else *value = op2->str(); *expr = op1; } else { *op = "!="; *value = "0"; *expr = comp; } *inconclusive = *inconclusive || ((*value)[0] == '\'' && !(*op == "!=" || *op == "==")); // Only float and int values are currently handled if (!MathLib::isInt(*value) && !MathLib::isFloat(*value) && (*value)[0] != '\'') return false; return true; } static std::string conditionString(bool not1, const Token *expr1, const std::string &op, const std::string &value1) { if (expr1->astParent()->isComparisonOp()) return std::string(not1 ? "!(" : "") + (expr1->isName() ? expr1->str() : std::string("EXPR")) + " " + op + " " + value1 + (not1 ? ")" : ""); return std::string(not1 ? "!" : "") + (expr1->isName() ? expr1->str() : std::string("EXPR")); } static std::string conditionString(const Token * tok) { if (!tok) return ""; if (tok->isComparisonOp()) { bool inconclusive = false; bool not_; std::string op, value; const Token *expr; if (parseComparison(tok, ¬_, &op, &value, &expr, &inconclusive) && expr->isName()) { return conditionString(not_, expr, op, value); } } if (Token::Match(tok, "%cop%|&&|%oror%")) { if (tok->astOperand2()) return conditionString(tok->astOperand1()) + " " + tok->str() + " " + conditionString(tok->astOperand2()); return tok->str() + "(" + conditionString(tok->astOperand1()) + ")"; } return tok->expressionString(); } void CheckCondition::checkIncorrectLogicOperator() { const bool printStyle = mSettings->severity.isEnabled(Severity::style); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); if (!printWarning && !printStyle) return; const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2()) continue; // 'A && (!A || B)' is equivalent to 'A && B' // 'A || (!A && B)' is equivalent to 'A || B' // 'A && (A || B)' is equivalent to 'A' // 'A || (A && B)' is equivalent to 'A' if (printStyle && ((tok->str() == "||" && tok->astOperand2()->str() == "&&") || (tok->str() == "&&" && tok->astOperand2()->str() == "||"))) { const Token* tok2 = tok->astOperand2()->astOperand1(); if (isOppositeCond(true, mTokenizer->isCPP(), tok->astOperand1(), tok2, mSettings->library, true, false)) { std::string expr1(tok->astOperand1()->expressionString()); std::string expr2(tok->astOperand2()->astOperand1()->expressionString()); std::string expr3(tok->astOperand2()->astOperand2()->expressionString()); // make copy for later because the original string might get overwritten const std::string expr1VerboseMsg = expr1; const std::string expr2VerboseMsg = expr2; const std::string expr3VerboseMsg = expr3; if (expr1.length() + expr2.length() + expr3.length() > 50U) { if (expr1[0] == '!' && expr2[0] != '!') { expr1 = "!A"; expr2 = "A"; } else { expr1 = "A"; expr2 = "!A"; } expr3 = "B"; } const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")"; const std::string cond2 = expr1 + " " + tok->str() + " " + expr3; const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg; const std::string cond2VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr3VerboseMsg; // for the --verbose message, transform the actual condition and print it const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n" "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'."; redundantConditionError(tok, msg, false); continue; } else if (isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok2, mSettings->library, true, true)) { std::string expr1(tok->astOperand1()->expressionString()); std::string expr2(tok->astOperand2()->astOperand1()->expressionString()); std::string expr3(tok->astOperand2()->astOperand2()->expressionString()); // make copy for later because the original string might get overwritten const std::string expr1VerboseMsg = expr1; const std::string expr2VerboseMsg = expr2; const std::string expr3VerboseMsg = expr3; if (expr1.length() + expr2.length() + expr3.length() > 50U) { expr1 = "A"; expr2 = "A"; expr3 = "B"; } const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")"; const std::string cond2 = expr1; const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg; const std::string& cond2VerboseMsg = expr1VerboseMsg; // for the --verbose message, transform the actual condition and print it const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n" "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'."; redundantConditionError(tok, msg, false); continue; } } // Comparison #1 (LHS) const Token *comp1 = tok->astOperand1(); if (comp1->str() == tok->str()) comp1 = comp1->astOperand2(); // Comparison #2 (RHS) const Token *comp2 = tok->astOperand2(); bool inconclusive = false; bool parseable = true; // Parse LHS bool not1; std::string op1, value1; const Token *expr1 = nullptr; parseable &= (parseComparison(comp1, ¬1, &op1, &value1, &expr1, &inconclusive)); // Parse RHS bool not2; std::string op2, value2; const Token *expr2 = nullptr; parseable &= (parseComparison(comp2, ¬2, &op2, &value2, &expr2, &inconclusive)); if (inconclusive && !printInconclusive) continue; const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2); ErrorPath errorPath; // Opposite comparisons around || or && => always true or always false if (!isfloat && isOppositeCond(tok->str() == "||", mTokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath)) { const bool alwaysTrue(tok->str() == "||"); incorrectLogicOperatorError(tok, conditionString(tok), alwaysTrue, inconclusive, errorPath); continue; } if (!parseable) continue; if (isSameExpression(mTokenizer->isCPP(), true, comp1, comp2, mSettings->library, true, true)) continue; // same expressions => only report that there are same expressions if (!isSameExpression(mTokenizer->isCPP(), true, expr1, expr2, mSettings->library, true, true)) continue; // don't check floating point equality comparisons. that is bad // and deserves different warnings. if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!=")) continue; const double d1 = (isfloat) ? MathLib::toDoubleNumber(value1) : 0; const double d2 = (isfloat) ? MathLib::toDoubleNumber(value2) : 0; const MathLib::bigint i1 = (isfloat) ? 0 : MathLib::toLongNumber(value1); const MathLib::bigint i2 = (isfloat) ? 0 : MathLib::toLongNumber(value2); const bool useUnsignedInt = (std::numeric_limits::max()==i1) || (std::numeric_limits::max()==i2); const MathLib::biguint u1 = (useUnsignedInt) ? MathLib::toLongNumber(value1) : 0; const MathLib::biguint u2 = (useUnsignedInt) ? MathLib::toLongNumber(value2) : 0; // evaluate if expression is always true/false bool alwaysTrue = true, alwaysFalse = true; bool firstTrue = true, secondTrue = true; for (int test = 1; test <= 5; ++test) { // test: // 1 => testvalue is less than both value1 and value2 // 2 => testvalue is value1 // 3 => testvalue is between value1 and value2 // 4 => testvalue value2 // 5 => testvalue is larger than both value1 and value2 bool result1, result2; if (isfloat) { const double testvalue = getvalue(test, d1, d2); result1 = checkFloatRelation(op1, testvalue, d1); result2 = checkFloatRelation(op2, testvalue, d2); } else if (useUnsignedInt) { const MathLib::biguint testvalue = getvalue(test, u1, u2); result1 = checkIntRelation(op1, testvalue, u1); result2 = checkIntRelation(op2, testvalue, u2); } else { const MathLib::bigint testvalue = getvalue(test, i1, i2); result1 = checkIntRelation(op1, testvalue, i1); result2 = checkIntRelation(op2, testvalue, i2); } if (not1) result1 = !result1; if (not2) result2 = !result2; if (tok->str() == "&&") { alwaysTrue &= (result1 && result2); alwaysFalse &= !(result1 && result2); } else { alwaysTrue &= (result1 || result2); alwaysFalse &= !(result1 || result2); } firstTrue &= !(!result1 && result2); secondTrue &= !(result1 && !result2); } const std::string cond1str = conditionString(not1, expr1, op1, value1); const std::string cond2str = conditionString(not2, expr2, op2, value2); if (printWarning && (alwaysTrue || alwaysFalse)) { const std::string text = cond1str + " " + tok->str() + " " + cond2str; incorrectLogicOperatorError(tok, text, alwaysTrue, inconclusive, errorPath); } else if (printStyle && secondTrue) { const std::string text = "If '" + cond1str + "', the comparison '" + cond2str + "' is always true."; redundantConditionError(tok, text, inconclusive); } else if (printStyle && firstTrue) { //const std::string text = "The comparison " + cond1str + " is always " + // (firstTrue ? "true" : "false") + " when " + // cond2str + "."; const std::string text = "If '" + cond2str + "', the comparison '" + cond1str + "' is always true."; redundantConditionError(tok, text, inconclusive); } } } } void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors) { if (diag(tok)) return; errors.emplace_back(tok, ""); if (always) reportError(errors, Severity::warning, "incorrectLogicOperator", "Logical disjunction always evaluates to true: " + condition + ".\n" "Logical disjunction always evaluates to true: " + condition + ". " "Are these conditions necessary? Did you intend to use && instead? Are the numbers correct? Are you comparing the correct variables?", CWE571, inconclusive ? Certainty::inconclusive : Certainty::normal); else reportError(errors, Severity::warning, "incorrectLogicOperator", "Logical conjunction always evaluates to false: " + condition + ".\n" "Logical conjunction always evaluates to false: " + condition + ". " "Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables?", CWE570, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckCondition::redundantConditionError(const Token *tok, const std::string &text, bool inconclusive) { if (diag(tok)) return; reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } //----------------------------------------------------------------------------- // Detect "(var % val1) > val2" where val2 is >= val1. //----------------------------------------------------------------------------- void CheckCondition::checkModuloAlwaysTrueFalse() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token *num, *modulo; if (Token::simpleMatch(tok->astOperand1(), "%") && Token::Match(tok->astOperand2(), "%num%")) { modulo = tok->astOperand1(); num = tok->astOperand2(); } else if (Token::Match(tok->astOperand1(), "%num%") && Token::simpleMatch(tok->astOperand2(), "%")) { num = tok->astOperand1(); modulo = tok->astOperand2(); } else { continue; } if (Token::Match(modulo->astOperand2(), "%num%") && MathLib::isLessEqual(modulo->astOperand2()->str(), num->str())) moduloAlwaysTrueFalseError(tok, modulo->astOperand2()->str()); } } } void CheckCondition::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal) { reportError(tok, Severity::warning, "moduloAlwaysTrueFalse", "Comparison of modulo result is predetermined, because it is always less than " + maxVal + ".", CWE398, Certainty::normal); } static int countPar(const Token *tok1, const Token *tok2) { int par = 0; for (const Token *tok = tok1; tok && tok != tok2; tok = tok->next()) { if (tok->str() == "(") ++par; else if (tok->str() == ")") --par; else if (tok->str() == ";") return -1; } return par; } //--------------------------------------------------------------------------- // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))' //--------------------------------------------------------------------------- void CheckCondition::clarifyCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; const bool isC = mTokenizer->isC(); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "( %name% [=&|^]")) { for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); else if (tok2->isComparisonOp()) { // This might be a template if (!isC && tok2->link()) break; if (Token::simpleMatch(tok2->astParent(), "?")) break; clarifyConditionError(tok, tok->strAt(2) == "=", false); break; } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".") break; } } else if (tok->tokType() == Token::eBitOp && !tok->isUnaryOp("&")) { if (tok->astOperand2() && tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2()) continue; // using boolean result in bitwise operation ! x [&|^] const ValueType* vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType* vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; if (vt1 && vt1->type == ValueType::BOOL && !Token::Match(tok->astOperand1(), "%name%|(|[|::|.") && countPar(tok->astOperand1(), tok) == 0) clarifyConditionError(tok, false, true); else if (vt2 && vt2->type == ValueType::BOOL && !Token::Match(tok->astOperand2(), "%name%|(|[|::|.") && countPar(tok, tok->astOperand2()) == 0) clarifyConditionError(tok, false, true); } } } } void CheckCondition::clarifyConditionError(const Token *tok, bool assign, bool boolop) { std::string errmsg; if (assign) errmsg = "Suspicious condition (assignment + comparison); Clarify expression with parentheses."; else if (boolop) errmsg = "Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" "Suspicious expression. Boolean result is used in bitwise operation. The operator '!' " "and the comparison operators have higher precedence than bitwise operators. " "It is recommended that the expression is clarified with parentheses."; else errmsg = "Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "Suspicious condition. Comparison operators have higher precedence than bitwise operators. " "Please clarify the condition with parentheses."; reportError(tok, Severity::style, "clarifyCondition", errmsg, CWE398, Certainty::normal); } void CheckCondition::alwaysTrueFalse() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "<") && tok->link()) // don't write false positives when templates are used continue; if (!tok->hasKnownIntValue()) continue; if (Token::Match(tok->previous(), "%name% (") && tok->previous()->function()) { const Function* f = tok->previous()->function(); if (f->functionScope && Token::Match(f->functionScope->bodyStart, "{ return true|false ;")) continue; } { // is this a condition.. const Token *parent = tok->astParent(); while (Token::Match(parent, "%oror%|&&")) parent = parent->astParent(); if (!parent) continue; const Token *condition = nullptr; if (parent->str() == "?" && precedes(tok, parent)) condition = parent->astOperand1(); else if (Token::Match(parent->previous(), "if|while (")) condition = parent->astOperand2(); else if (Token::simpleMatch(parent, "return")) condition = parent->astOperand1(); else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() && Token::simpleMatch(parent->astParent()->astParent()->previous(), "for (")) condition = parent->astOperand1(); else continue; (void)condition; } // Skip already diagnosed values if (diag(tok, false)) continue; if (Token::Match(tok, "%num%|%bool%|%char%")) continue; if (Token::Match(tok, "! %num%|%bool%|%char%")) continue; if (Token::Match(tok, "%oror%|&&|:")) continue; if (Token::Match(tok, "%comp%") && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true)) continue; if (isConstVarExpression(tok, "[|(|&|+|-|*|/|%|^|>>|<<")) continue; // there are specific warnings about nonzero expressions (pointer/unsigned) // do not write alwaysTrueFalse for these comparisons. { const ValueFlow::Value *zeroValue = nullptr; const Token *nonZeroExpr = nullptr; if (CheckOther::comparisonNonZeroExpressionLessThanZero(tok, &zeroValue, &nonZeroExpr) || CheckOther::testIfNonZeroExpressionIsPositive(tok, &zeroValue, &nonZeroExpr)) continue; } const bool constIfWhileExpression = tok->astParent() && Token::Match(tok->astTop()->astOperand1(), "if|while") && !tok->astTop()->astOperand1()->isConstexpr() && (Token::Match(tok->astParent(), "%oror%|&&") || Token::Match(tok->astParent()->astOperand1(), "if|while")); const bool constValExpr = tok->isNumber() && Token::Match(tok->astParent(),"%oror%|&&|?"); // just one number in boolean expression const bool compExpr = Token::Match(tok, "%comp%|!"); // a compare expression const bool ternaryExpression = Token::simpleMatch(tok->astParent(), "?"); const bool returnExpression = Token::simpleMatch(tok->astTop(), "return") && (tok->isComparisonOp() || Token::Match(tok, "&&|%oror%")); if (!(constIfWhileExpression || constValExpr || compExpr || ternaryExpression || returnExpression)) continue; // Don't warn when there are expanded macros.. bool isExpandedMacro = false; visitAstNodes(tok, [&](const Token * tok2) { if (!tok2) return ChildrenToVisit::none; if (tok2->isExpandedMacro()) { isExpandedMacro = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (isExpandedMacro) continue; for (const Token *parent = tok; parent; parent = parent->astParent()) { if (parent->isExpandedMacro()) { isExpandedMacro = true; break; } } if (isExpandedMacro) continue; // don't warn when condition checks sizeof result bool hasSizeof = false; visitAstNodes(tok, [&](const Token * tok2) { if (!tok2) return ChildrenToVisit::none; if (tok2->isNumber()) return ChildrenToVisit::none; if (Token::simpleMatch(tok2->previous(), "sizeof (")) { hasSizeof = true; return ChildrenToVisit::none; } if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) { return ChildrenToVisit::op1_and_op2; } return ChildrenToVisit::none; }); if (hasSizeof) continue; alwaysTrueFalseError(tok, &tok->values().front()); } } } void CheckCondition::alwaysTrueFalseError(const Token *tok, const ValueFlow::Value *value) { const bool alwaysTrue = value && (value->intvalue != 0); const std::string expr = tok ? tok->expressionString() : std::string("x"); const std::string errmsg = "Condition '" + expr + "' is always " + (alwaysTrue ? "true" : "false"); const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::style, "knownConditionTrueFalse", errmsg, (alwaysTrue ? CWE571 : CWE570), Certainty::normal); } void CheckCondition::checkInvalidTestForOverflow() { // Interesting blogs: // https://www.airs.com/blog/archives/120 // https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html // https://research.checkpoint.com/2020/optout-compiler-undefined-behavior-optimizations/ // x + c < x -> false // x + c <= x -> false // x + c > x -> true // x + c >= x -> true // x + y < x -> y < 0 if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "<|<=|>=|>") || !tok->isBinaryOp()) continue; const Token *lhsTokens[2] = {tok->astOperand1(), tok->astOperand2()}; for (const Token *lhs: lhsTokens) { std::string cmp = tok->str(); if (lhs == tok->astOperand2()) cmp[0] = (cmp[0] == '<') ? '>' : '<'; if (!Token::Match(lhs, "[+-]") || !lhs->isBinaryOp()) continue; const bool isSignedInteger = lhs->valueType() && lhs->valueType()->isIntegral() && lhs->valueType()->sign == ValueType::Sign::SIGNED; const bool isPointer = lhs->valueType() && lhs->valueType()->pointer > 0; if (!isSignedInteger && !isPointer) continue; const Token *exprTokens[2] = {lhs->astOperand1(), lhs->astOperand2()}; for (const Token *expr: exprTokens) { if (lhs->str() == "-" && expr == lhs->astOperand2()) continue; // TODO? if (expr->hasKnownIntValue()) continue; if (!isSameExpression(mTokenizer->isCPP(), true, expr, lhs->astSibling(), mSettings->library, true, false)) continue; const Token * const other = expr->astSibling(); // x [+-] c cmp x if ((other->isNumber() && other->getKnownIntValue() > 0) || (!other->isNumber() && other->valueType() && other->valueType()->isIntegral() && other->valueType()->sign == ValueType::Sign::UNSIGNED)) { bool result; if (lhs->str() == "+") result = (cmp == ">" || cmp == ">="); else result = (cmp == "<" || cmp == "<="); invalidTestForOverflow(tok, lhs->valueType(), result ? "true" : "false"); continue; } // x + y cmp x if (lhs->str() == "+" && other->varId() > 0) { const std::string result = other->str() + cmp + "0"; invalidTestForOverflow(tok, lhs->valueType(), result); continue; } // x - y cmp x if (lhs->str() == "-" && other->varId() > 0) { std::string cmp2 = cmp; cmp2[0] = (cmp[0] == '<') ? '>' : '<'; const std::string result = other->str() + cmp2 + "0"; invalidTestForOverflow(tok, lhs->valueType(), result); continue; } } } } } void CheckCondition::invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace) { const std::string expr = (tok ? tok->expressionString() : std::string("x + c < x")); const std::string overflow = (valueType && valueType->pointer) ? "pointer overflow" : "signed integer overflow"; std::string errmsg = "Invalid test for overflow '" + expr + "'; " + overflow + " is undefined behavior."; if (replace == "false" || replace == "true") errmsg += " Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " + replace + "."; else errmsg += " Some mainstream compilers removes handling of overflows when optimising the code and change the code to '" + replace + "'."; reportError(tok, Severity::warning, "invalidTestForOverflow", errmsg, uncheckedErrorConditionCWE, Certainty::normal); } void CheckCondition::checkPointerAdditionResultNotNull() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) continue; // Macros might have pointless safety checks if (tok->isExpandedMacro()) continue; const Token *calcToken, *exprToken; if (tok->astOperand1()->str() == "+") { calcToken = tok->astOperand1(); exprToken = tok->astOperand2(); } else if (tok->astOperand2()->str() == "+") { calcToken = tok->astOperand2(); exprToken = tok->astOperand1(); } else continue; // pointer comparison against NULL (ptr+12==0) if (calcToken->hasKnownIntValue()) continue; if (!calcToken->valueType() || calcToken->valueType()->pointer==0) continue; if (!exprToken->hasKnownIntValue() || !exprToken->getValue(0)) continue; pointerAdditionResultNotNullError(tok, calcToken); } } } void CheckCondition::pointerAdditionResultNotNullError(const Token *tok, const Token *calc) { const std::string s = calc ? calc->expressionString() : "ptr+1"; reportError(tok, Severity::warning, "pointerAdditionResultNotNull", "Comparison is wrong. Result of '" + s + "' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour."); } void CheckCondition::checkDuplicateConditionalAssign() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; if (!Token::simpleMatch(tok->next()->link(), ") {")) continue; const Token *blockTok = tok->next()->link()->next(); const Token *condTok = tok->next()->astOperand2(); if (!Token::Match(condTok, "==|!=")) continue; if (condTok->str() == "!=" && Token::simpleMatch(blockTok->link(), "} else {")) continue; if (!blockTok->next()) continue; const Token *assignTok = blockTok->next()->astTop(); if (!Token::simpleMatch(assignTok, "=")) continue; if (nextAfterAstRightmostLeaf(assignTok) != blockTok->link()->previous()) continue; if (!isSameExpression( mTokenizer->isCPP(), true, condTok->astOperand1(), assignTok->astOperand1(), mSettings->library, true, true)) continue; if (!isSameExpression( mTokenizer->isCPP(), true, condTok->astOperand2(), assignTok->astOperand2(), mSettings->library, true, true)) continue; duplicateConditionalAssignError(condTok, assignTok); } } } void CheckCondition::duplicateConditionalAssignError(const Token *condTok, const Token* assignTok) { ErrorPath errors; std::string msg = "Duplicate expression for the condition and assignment."; if (condTok && assignTok) { if (condTok->str() == "==") { msg = "Assignment '" + assignTok->expressionString() + "' is redundant with condition '" + condTok->expressionString() + "'."; errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "'"); errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "' is redundant"); } else { msg = "The statement 'if (" + condTok->expressionString() + ") " + assignTok->expressionString() + "' is logically equivalent to '" + assignTok->expressionString() + "'."; errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "'"); errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "' is redundant"); } } reportError( errors, Severity::style, "duplicateConditionalAssign", msg, CWE398, Certainty::normal); } void CheckCondition::checkAssignmentInCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() != "=") continue; if (!tok->astParent()) continue; // Is this assignment of container/iterator? if (!tok->valueType()) continue; if (tok->valueType()->pointer > 0) continue; if (tok->valueType()->type != ValueType::Type::CONTAINER && tok->valueType()->type != ValueType::Type::ITERATOR) continue; // warn if this is a conditional expression.. if (Token::Match(tok->astParent()->previous(), "if|while (")) assignmentInCondition(tok); else if (Token::Match(tok->astParent(), "%oror%|&&")) assignmentInCondition(tok); else if (Token::simpleMatch(tok->astParent(), "?") && tok == tok->astParent()->astOperand1()) assignmentInCondition(tok); } } } void CheckCondition::assignmentInCondition(const Token *eq) { std::string expr = eq ? eq->expressionString() : "x=y"; reportError( eq, Severity::style, "assignmentInCondition", "Suspicious assignment in condition. Condition '" + expr + "' is always true.", CWE571, Certainty::normal); } void CheckCondition::checkCompareValueOutOfTypeRange() { if (!mSettings->severity.isEnabled(Severity::style)) return; if (mSettings->platformType == cppcheck::Platform::PlatformType::Native || mSettings->platformType == cppcheck::Platform::PlatformType::Unspecified) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->isBinaryOp()) continue; for (int i = 0; i < 2; ++i) { const Token * const valueTok = (i == 0) ? tok->astOperand1() : tok->astOperand2(); const Token * const typeTok = valueTok->astSibling(); if (!valueTok->hasKnownIntValue() || !typeTok->valueType() || typeTok->valueType()->pointer) continue; if (valueTok->getKnownIntValue() < 0 && valueTok->valueType() && valueTok->valueType()->sign != ValueType::Sign::SIGNED) continue; int bits = 0; switch (typeTok->valueType()->type) { case ValueType::Type::BOOL: bits = 1; break; case ValueType::Type::CHAR: bits = mSettings->char_bit; break; case ValueType::Type::SHORT: bits = mSettings->short_bit; break; case ValueType::Type::INT: bits = mSettings->int_bit; break; case ValueType::Type::LONG: bits = mSettings->long_bit; break; case ValueType::Type::LONGLONG: bits = mSettings->long_long_bit; break; default: break; } if (bits == 0 || bits >= 64) continue; const auto typeMinValue = (typeTok->valueType()->sign == ValueType::Sign::UNSIGNED) ? 0 : (-(1LL << (bits-1))); const auto unsignedTypeMaxValue = (1LL << bits) - 1LL; long long typeMaxValue; if (typeTok->valueType()->sign != ValueType::Sign::SIGNED) typeMaxValue = unsignedTypeMaxValue; else if (bits >= mSettings->int_bit && (!valueTok->valueType() || valueTok->valueType()->sign != ValueType::Sign::SIGNED)) typeMaxValue = unsignedTypeMaxValue; else typeMaxValue = unsignedTypeMaxValue / 2; bool result; if (tok->str() == "==") result = false; else if (tok->str() == "!=") result = true; else if (tok->str()[0] == '>' && i == 0) // num > var result = (valueTok->getKnownIntValue() > 0); else if (tok->str()[0] == '>' && i == 1) // var > num result = (valueTok->getKnownIntValue() < 0); else if (tok->str()[0] == '<' && i == 0) // num < var result = (valueTok->getKnownIntValue() < 0); else if (tok->str()[0] == '<' && i == 1) // var < num result = (valueTok->getKnownIntValue() > 0); if (valueTok->getKnownIntValue() < typeMinValue) { compareValueOutOfTypeRangeError(valueTok, typeTok->valueType()->str(), valueTok->getKnownIntValue(), result); } else if (valueTok->getKnownIntValue() > typeMaxValue) compareValueOutOfTypeRangeError(valueTok, typeTok->valueType()->str(), valueTok->getKnownIntValue(), result); } } } } void CheckCondition::compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result) { reportError( comparison, Severity::style, "compareValueOutOfTypeRangeError", "Comparing expression of type '" + type + "' against value " + std::to_string(value) + ". Condition is always " + (result ? "true" : "false") + ".", CWE398, Certainty::normal); } cppcheck-2.7/lib/checkcondition.h000066400000000000000000000225241417746362400170700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkconditionH #define checkconditionH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "mathlib.h" #include "errortypes.h" #include "utils.h" #include #include class Settings; class Token; class Tokenizer; class ErrorLogger; class ValueType; namespace ValueFlow { class Value; } /// @addtogroup Checks /// @{ /** * @brief Check for condition mismatches */ class CPPCHECKLIB CheckCondition : public Check { public: /** This constructor is used when registering the CheckAssignIf */ CheckCondition() : Check(myName()) {} /** This constructor is used when running checks. */ CheckCondition(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckCondition checkCondition(tokenizer, settings, errorLogger); checkCondition.multiCondition(); checkCondition.clarifyCondition(); // not simplified because ifAssign checkCondition.multiCondition2(); checkCondition.checkIncorrectLogicOperator(); checkCondition.checkInvalidTestForOverflow(); checkCondition.duplicateCondition(); checkCondition.checkPointerAdditionResultNotNull(); checkCondition.checkDuplicateConditionalAssign(); checkCondition.assignIf(); checkCondition.alwaysTrueFalse(); checkCondition.checkBadBitmaskCheck(); checkCondition.comparison(); checkCondition.checkModuloAlwaysTrueFalse(); checkCondition.checkAssignmentInCondition(); checkCondition.checkCompareValueOutOfTypeRange(); } /** mismatching assignment / comparison */ void assignIf(); /** parse scopes recursively */ bool assignIfParseScope(const Token * const assignTok, const Token * const startTok, const nonneg int varid, const bool islocal, const char bitop, const MathLib::bigint num); /** check bitmask using | instead of & */ void checkBadBitmaskCheck(); /** mismatching lhs and rhs in comparison */ void comparison(); void duplicateCondition(); /** match 'if' and 'else if' conditions */ void multiCondition(); /** * multiconditions #2 * - Opposite inner conditions => always false * - (TODO) Same/Overlapping inner condition => always true * - same condition after early exit => always false **/ void multiCondition2(); /** @brief %Check for testing for mutual exclusion over ||*/ void checkIncorrectLogicOperator(); /** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */ void checkModuloAlwaysTrueFalse(); /** @brief Suspicious condition (assignment+comparison) */ void clarifyCondition(); /** @brief Condition is always true/false */ void alwaysTrueFalse(); /** @brief %Check for invalid test for overflow 'x+100 < x' */ void checkInvalidTestForOverflow(); /** @brief Check if pointer addition result is NULL '(ptr + 1) == NULL' */ void checkPointerAdditionResultNotNull(); void checkDuplicateConditionalAssign(); /** @brief Assignment in condition */ void checkAssignmentInCondition(); private: // The conditions that have been diagnosed std::set mCondDiags; bool diag(const Token* tok, bool insert=true); bool isAliased(const std::set &vars) const; bool isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const; void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result); void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2); void badBitmaskCheckError(const Token *tok); void comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result); void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath); void overlappingElseIfConditionError(const Token *tok, nonneg int line1); void oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath); void oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); void identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath); void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors); void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive); void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal); void clarifyConditionError(const Token *tok, bool assign, bool boolop); void alwaysTrueFalseError(const Token *tok, const ValueFlow::Value *value); void invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace); void pointerAdditionResultNotNullError(const Token *tok, const Token *calc); void duplicateConditionalAssignError(const Token *condTok, const Token* assignTok); void assignmentInCondition(const Token *eq); void checkCompareValueOutOfTypeRange(); void compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckCondition c(nullptr, settings, errorLogger); ErrorPath errorPath; c.assignIfError(nullptr, nullptr, emptyString, false); c.badBitmaskCheckError(nullptr); c.comparisonError(nullptr, "&", 6, "==", 1, false); c.duplicateConditionError(nullptr, nullptr, errorPath); c.overlappingElseIfConditionError(nullptr, 1); c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1); c.oppositeInnerConditionError(nullptr, nullptr, errorPath); c.identicalInnerConditionError(nullptr, nullptr, errorPath); c.identicalConditionAfterEarlyExitError(nullptr, nullptr, errorPath); c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false, errorPath); c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false); c.moduloAlwaysTrueFalseError(nullptr, "1"); c.clarifyConditionError(nullptr, true, false); c.alwaysTrueFalseError(nullptr, nullptr); c.invalidTestForOverflow(nullptr, nullptr, "false"); c.pointerAdditionResultNotNullError(nullptr, nullptr); c.duplicateConditionalAssignError(nullptr, nullptr); c.assignmentInCondition(nullptr); c.compareValueOutOfTypeRangeError(nullptr, "unsigned char", 256, true); } static std::string myName() { return "Condition"; } std::string classInfo() const OVERRIDE { return "Match conditions with assignments and other conditions:\n" "- Mismatching assignment and comparison => comparison is always true/false\n" "- Mismatching lhs and rhs in comparison => comparison is always true/false\n" "- Detect usage of | where & should be used\n" "- Duplicate condition and assignment\n" "- Detect matching 'if' and 'else if' conditions\n" "- Mismatching bitand (a &= 0xf0; a &= 1; => a = 0)\n" "- Opposite inner condition is always false\n" "- Identical condition after early exit is always false\n" "- Condition that is always true/false\n" "- Mutual exclusion over || always evaluating to true\n" "- Comparisons of modulo results that are always true/false.\n" "- Known variable values => condition is always true/false\n" "- Invalid test for overflow. Some mainstream compilers remove such overflow tests when optimising code.\n" "- Suspicious assignment of container/iterator in condition => condition is always true.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkconditionH cppcheck-2.7/lib/checkexceptionsafety.cpp000066400000000000000000000405601417746362400206470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkexceptionsafety.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include #include #include #include //--------------------------------------------------------------------------- // Register CheckExceptionSafety.. namespace { CheckExceptionSafety instance; } //--------------------------------------------------------------------------- void CheckExceptionSafety::destructors() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Perform check.. for (const Scope * scope : symbolDatabase->functionScopes) { const Function * function = scope->function; if (!function) continue; // only looking for destructors if (function->type == Function::eDestructor) { // Inspect this destructor. for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // Skip try blocks if (Token::simpleMatch(tok, "try {")) { tok = tok->next()->link(); } // Skip uncaught exceptions else if (Token::simpleMatch(tok, "if ( ! std :: uncaught_exception ( ) ) {")) { tok = tok->next()->link(); // end of if ( ... ) tok = tok->next()->link(); // end of { ... } } // throw found within a destructor else if (tok->str() == "throw") { destructorsError(tok, scope->className); break; } } } } } void CheckExceptionSafety::destructorsError(const Token * const tok, const std::string &className) { reportError(tok, Severity::warning, "exceptThrowInDestructor", "Class " + className + " is not safe, destructor throws exception\n" "The class " + className + " is not safe because its destructor " "throws an exception. If " + className + " is used and an exception " "is thrown that is caught in an outer scope the program will terminate.", CWE398, Certainty::normal); } void CheckExceptionSafety::deallocThrow() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Deallocate a global/member pointer and then throw exception // the pointer will be a dead pointer for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // only looking for delete now if (tok->str() != "delete") continue; // Check if this is something similar with: "delete p;" tok = tok->next(); if (Token::simpleMatch(tok, "[ ]")) tok = tok->tokAt(2); if (!tok || tok == scope->bodyEnd) break; if (!Token::Match(tok, "%var% ;")) continue; // we only look for global variables const Variable *var = tok->variable(); if (!var || !(var->isGlobal() || var->isStatic())) continue; const unsigned int varid(tok->varId()); // Token where throw occurs const Token *throwToken = nullptr; // is there a throw after the deallocation? const Token* const end2 = tok->scope()->bodyEnd; for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next()) { // Throw after delete -> Dead pointer if (tok2->str() == "throw") { if (printInconclusive) { // For inconclusive checking, throw directly. deallocThrowError(tok2, tok->str()); break; } throwToken = tok2; } // Variable is assigned -> Bail out else if (Token::Match(tok2, "%varid% =", varid)) { if (throwToken) // For non-inconclusive checking, wait until we find an assignment to it. Otherwise we assume it is safe to leave a dead pointer. deallocThrowError(throwToken, tok2->str()); break; } // Variable passed to function. Assume it becomes assigned -> Bail out else if (Token::Match(tok2, "[,(] &| %varid% [,)]", varid)) // TODO: No bailout if passed by value or as const reference break; } } } } void CheckExceptionSafety::deallocThrowError(const Token * const tok, const std::string &varname) { reportError(tok, Severity::warning, "exceptDeallocThrow", "Exception thrown in invalid state, '" + varname + "' points at deallocated memory.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // catch(const exception & err) // { // throw err; // <- should be just "throw;" // } //--------------------------------------------------------------------------- void CheckExceptionSafety::checkRethrowCopy() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eCatch) continue; const unsigned int varid = scope.bodyStart->tokAt(-2)->varId(); if (varid) { for (const Token* tok = scope.bodyStart->next(); tok && tok != scope.bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "catch (") && tok->next()->link() && tok->next()->link()->next()) { // Don't check inner catch - it is handled in another iteration of outer loop. tok = tok->next()->link()->next()->link(); if (!tok) break; } else if (Token::Match(tok, "%varid% .", varid)) { const Token *parent = tok->astParent(); while (Token::simpleMatch(parent->astParent(), ".")) parent = parent->astParent(); if (Token::Match(parent->astParent(), "%assign%|++|--|(") && parent == parent->astParent()->astOperand1()) break; } else if (Token::Match(tok, "throw %varid% ;", varid)) rethrowCopyError(tok, tok->strAt(1)); } } } } void CheckExceptionSafety::rethrowCopyError(const Token * const tok, const std::string &varname) { reportError(tok, Severity::style, "exceptRethrowCopy", "Throwing a copy of the caught exception instead of rethrowing the original exception.\n" "Rethrowing an exception with 'throw " + varname + ";' creates an unnecessary copy of '" + varname + "'. " "To rethrow the caught exception without unnecessary copying or slicing, use a bare 'throw;'.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // try {} catch (std::exception err) {} <- Should be "std::exception& err" //--------------------------------------------------------------------------- void CheckExceptionSafety::checkCatchExceptionByValue() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eCatch) continue; // Find a pass-by-value declaration in the catch(), excluding basic types // e.g. catch (std::exception err) const Variable *var = scope.bodyStart->tokAt(-2)->variable(); if (var && var->isClass() && !var->isPointer() && !var->isReference()) catchExceptionByValueError(scope.classDef); } } void CheckExceptionSafety::catchExceptionByValueError(const Token *tok) { reportError(tok, Severity::style, "catchExceptionByValue", "Exception should be caught by reference.\n" "The exception is caught by value. It could be caught " "as a (const) reference which is usually recommended in C++.", CWE398, Certainty::normal); } static const Token * functionThrowsRecursive(const Function * function, std::set & recursive) { // check for recursion and bail if found if (!recursive.insert(function).second) return nullptr; if (!function->functionScope) return nullptr; for (const Token *tok = function->functionScope->bodyStart->next(); tok != function->functionScope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "try {")) tok = tok->linkAt(1); // skip till start of catch clauses if (tok->str() == "throw") { return tok; } else if (tok->function()) { const Function * called = tok->function(); // check if called function has an exception specification if (called->isThrow() && called->throwArg) { return tok; } else if (called->isNoExcept() && called->noexceptArg && called->noexceptArg->str() != "true") { return tok; } else if (functionThrowsRecursive(called, recursive)) { return tok; } } } return nullptr; } static const Token * functionThrows(const Function * function) { std::set recursive; return functionThrowsRecursive(function, recursive); } //-------------------------------------------------------------------------- // void func() noexcept { throw x; } // void func() throw() { throw x; } // void func() __attribute__((nothrow)); void func() { throw x; } //-------------------------------------------------------------------------- void CheckExceptionSafety::nothrowThrows() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const Function* function = scope->function; if (!function) continue; // check noexcept and noexcept(true) functions if (function->isNoExcept() && (!function->noexceptArg || function->noexceptArg->str() == "true")) { const Token *throws = functionThrows(function); if (throws) noexceptThrowError(throws); } // check throw() functions else if (function->isThrow() && !function->throwArg) { const Token *throws = functionThrows(function); if (throws) noexceptThrowError(throws); } // check __attribute__((nothrow)) or __declspec(nothrow) functions else if (function->isAttributeNothrow()) { const Token *throws = functionThrows(function); if (throws) noexceptThrowError(throws); } } } void CheckExceptionSafety::noexceptThrowError(const Token * const tok) { reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal); } //-------------------------------------------------------------------------- // void func() { functionWithExceptionSpecification(); } //-------------------------------------------------------------------------- void CheckExceptionSafety::unhandledExceptionSpecification() { if (!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // only check functions without exception epecification if (scope->function && !scope->function->isThrow() && scope->className != "main" && scope->className != "wmain" && scope->className != "_tmain" && scope->className != "WinMain") { for (const Token *tok = scope->function->functionScope->bodyStart->next(); tok != scope->function->functionScope->bodyEnd; tok = tok->next()) { if (tok->str() == "try") { break; } else if (tok->function()) { const Function * called = tok->function(); // check if called function has an exception specification if (called->isThrow() && called->throwArg) { unhandledExceptionSpecificationError(tok, called->tokenDef, scope->function->name()); break; } } } } } } void CheckExceptionSafety::unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) { const std::string str1(tok1 ? tok1->str() : "foo"); const std::list locationList = { tok1, tok2 }; reportError(locationList, Severity::style, "unhandledExceptionSpecification", "Unhandled exception specification when calling function " + str1 + "().\n" "Unhandled exception specification when calling function " + str1 + "(). " "Either use a try/catch around the function call, or add a exception specification for " + funcname + "() also.", CWE703, Certainty::inconclusive); } //-------------------------------------------------------------------------- // 7.6.18.4 If no exception is presently being handled, evaluating a throw-expression with no operand calls std​::​​terminate(). //-------------------------------------------------------------------------- void CheckExceptionSafety::rethrowNoCurrentException() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const Function* function = scope->function; if (!function) continue; // Rethrow can be used in 'exception dispatcher' idiom which is FP in such case // https://isocpp.org/wiki/faq/exceptions#throw-without-an-object // We check the beginning of the function with idiom pattern if (Token::simpleMatch(function->functionScope->bodyStart->next(), "try { throw ; } catch (")) continue; for (const Token *tok = function->functionScope->bodyStart->next(); tok != function->functionScope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "catch (")) { tok = tok->linkAt(1); // skip catch argument if (Token::simpleMatch(tok, ") {")) tok = tok->linkAt(1); // skip catch scope else break; } if (Token::simpleMatch(tok, "throw ;")) { rethrowNoCurrentExceptionError(tok); } } } } void CheckExceptionSafety::rethrowNoCurrentExceptionError(const Token *tok) { reportError(tok, Severity::error, "rethrowNoCurrentException", "Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow." " If there is no current exception this calls std::terminate()." " More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object", CWE480, Certainty::normal); } cppcheck-2.7/lib/checkexceptionsafety.h000066400000000000000000000130631417746362400203120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkexceptionsafetyH #define checkexceptionsafetyH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errortypes.h" #include "tokenize.h" #include class Settings; class ErrorLogger; class Token; // CWE ID used: static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE703(703U); // Improper Check or Handling of Exceptional Conditions static const struct CWE CWE480(480U); // Use of Incorrect Operator /// @addtogroup Checks /// @{ /** * @brief %Check exception safety (exceptions shouldn't cause leaks nor corrupt data) * * The problem with these checks is that Cppcheck can't determine what the valid * values are for variables. But in some cases (dead pointers) it can be determined * that certain variable values are corrupt. */ class CPPCHECKLIB CheckExceptionSafety : public Check { public: /** This constructor is used when registering the CheckClass */ CheckExceptionSafety() : Check(myName()) {} /** This constructor is used when running checks. */ CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (tokenizer->isC()) return; CheckExceptionSafety checkExceptionSafety(tokenizer, settings, errorLogger); checkExceptionSafety.destructors(); checkExceptionSafety.deallocThrow(); checkExceptionSafety.checkRethrowCopy(); checkExceptionSafety.checkCatchExceptionByValue(); checkExceptionSafety.nothrowThrows(); checkExceptionSafety.unhandledExceptionSpecification(); checkExceptionSafety.rethrowNoCurrentException(); } /** Don't throw exceptions in destructors */ void destructors(); /** deallocating memory and then throw (dead pointer) */ void deallocThrow(); /** Don't rethrow a copy of the caught exception; use a bare throw instead */ void checkRethrowCopy(); /** @brief %Check for exceptions that are caught by value instead of by reference */ void checkCatchExceptionByValue(); /** @brief %Check for functions that throw that shouldn't */ void nothrowThrows(); /** @brief %Check for unhandled exception specification */ void unhandledExceptionSpecification(); /** @brief %Check for rethrow not from catch scope */ void rethrowNoCurrentException(); private: /** Don't throw exceptions in destructors */ void destructorsError(const Token * const tok, const std::string &className); void deallocThrowError(const Token * const tok, const std::string &varname); void rethrowCopyError(const Token * const tok, const std::string &varname); void catchExceptionByValueError(const Token *tok); void noexceptThrowError(const Token * const tok); /** Missing exception specification */ void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname); /** Rethrow without currently handled exception */ void rethrowNoCurrentExceptionError(const Token *tok); /** Generate all possible errors (for --errorlist) */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckExceptionSafety c(nullptr, settings, errorLogger); c.destructorsError(nullptr, "Class"); c.deallocThrowError(nullptr, "p"); c.rethrowCopyError(nullptr, "varname"); c.catchExceptionByValueError(nullptr); c.noexceptThrowError(nullptr); c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname"); c.rethrowNoCurrentExceptionError(nullptr); } /** Short description of class (for --doc) */ static std::string myName() { return "Exception Safety"; } /** wiki formatted description of the class (for --doc) */ std::string classInfo() const OVERRIDE { return "Checking exception safety\n" "- Throwing exceptions in destructors\n" "- Throwing exception during invalid state\n" "- Throwing a copy of a caught exception instead of rethrowing the original exception\n" "- Exception caught by value instead of by reference\n" "- Throwing exception in noexcept, nothrow(), __attribute__((nothrow)) or __declspec(nothrow) function\n" "- Unhandled exception specification when calling function foo()\n" "- Rethrow without currently handled exception\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkexceptionsafetyH cppcheck-2.7/lib/checkfunctions.cpp000066400000000000000000000761671417746362400174610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Check functions //--------------------------------------------------------------------------- #include "checkfunctions.h" #include "astutils.h" #include "mathlib.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckFunctions instance; } static const CWE CWE252(252U); // Unchecked Return Value static const CWE CWE477(477U); // Use of Obsolete Functions static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const CWE CWE686(686U); // Function Call With Incorrect Argument Type static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument void CheckFunctions::checkProhibitedFunctions() { const bool checkAlloca = mSettings->severity.isEnabled(Severity::warning) && ((mSettings->standards.c >= Standards::C99 && mTokenizer->isC()) || mSettings->standards.cpp >= Standards::CPP11); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (") && tok->varId() == 0) continue; // alloca() is special as it depends on the code being C or C++, so it is not in Library if (checkAlloca && Token::simpleMatch(tok, "alloca (") && (!tok->function() || tok->function()->nestedIn->type == Scope::eGlobal)) { if (mTokenizer->isC()) { if (mSettings->standards.c > Standards::C89) reportError(tok, Severity::warning, "allocaCalled", "$symbol:alloca\n" "Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n" "The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or " "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); } else reportError(tok, Severity::warning, "allocaCalled", "$symbol:alloca\n" "Obsolete function 'alloca' called.\n" "The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or " "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); } else { if (tok->function() && tok->function()->hasBody()) continue; const Library::WarnInfo* wi = mSettings->library.getWarnInfo(tok); if (wi) { if (mSettings->severity.isEnabled(wi->severity) && mSettings->standards.c >= wi->standards.c && mSettings->standards.cpp >= wi->standards.cpp) { const std::string daca = mSettings->daca ? "prohibited" : ""; reportError(tok, wi->severity, daca + tok->str() + "Called", wi->message, CWE477, Certainty::normal); } } } } } } //--------------------------------------------------------------------------- // Check and //--------------------------------------------------------------------------- void CheckFunctions::invalidFunctionUsage() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% ( !!)")) continue; const Token * const functionToken = tok; const std::vector arguments = getArguments(tok); for (int argnr = 1; argnr <= arguments.size(); ++argnr) { const Token * const argtok = arguments[argnr-1]; // check ... const ValueFlow::Value *invalidValue = argtok->getInvalidValue(functionToken,argnr,mSettings); if (invalidValue) { invalidFunctionArgError(argtok, functionToken->next()->astOperand1()->expressionString(), argnr, invalidValue, mSettings->library.validarg(functionToken, argnr)); } if (astIsBool(argtok)) { // check if (mSettings->library.isboolargbad(functionToken, argnr)) invalidFunctionArgBoolError(argtok, functionToken->str(), argnr); // Are the values 0 and 1 valid? else if (!mSettings->library.isIntArgValid(functionToken, argnr, 0)) invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1)) invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); } if (mSettings->library.isargstrz(functionToken, argnr)) { if (Token::Match(argtok, "& %var% !![") && argtok->next() && argtok->next()->valueType()) { const ValueType * valueType = argtok->next()->valueType(); const Variable * variable = argtok->next()->variable(); if (valueType->type == ValueType::Type::CHAR && !variable->isArray() && !variable->isGlobal() && (!argtok->next()->hasKnownValue() || argtok->next()->getValue(0) == nullptr)) { invalidFunctionArgStrError(argtok, functionToken->str(), argnr); } } } } } } } void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; if (invalidValue && invalidValue->condition) errmsg << ValueFlow::eitherTheConditionIsRedundant(invalidValue->condition) << " or $symbol() argument nr " << argnr << " can have invalid value."; else errmsg << "Invalid $symbol() argument nr " << argnr << '.'; if (invalidValue) errmsg << " The value is " << std::setprecision(10) << (invalidValue->isIntValue() ? invalidValue->intvalue : invalidValue->floatValue) << " but the valid values are '" << validstr << "'."; else errmsg << " The value is 0 or 1 (boolean) but the valid values are '" << validstr << "'."; if (invalidValue) reportError(getErrorPath(tok, invalidValue, "Invalid argument"), invalidValue->errorSeverity() && invalidValue->isKnown() ? Severity::error : Severity::warning, "invalidFunctionArg", errmsg.str(), CWE628, invalidValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); else reportError(tok, Severity::error, "invalidFunctionArg", errmsg.str(), CWE628, Certainty::normal); } void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; errmsg << "Invalid $symbol() argument nr " << argnr << ". A non-boolean value is required."; reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, Certainty::normal); } void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; errmsg << "Invalid $symbol() argument nr " << argnr << ". A nul-terminated string is required."; reportError(tok, Severity::error, "invalidFunctionArgStr", errmsg.str(), CWE628, Certainty::normal); } //--------------------------------------------------------------------------- // Check for ignored return values. //--------------------------------------------------------------------------- void CheckFunctions::checkIgnoredReturnValue() { if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // skip c++11 initialization, ({...}) if (Token::Match(tok, "%var%|(|,|return {")) tok = tok->linkAt(1); else if (Token::Match(tok, "[(<]") && tok->link()) tok = tok->link(); if (tok->varId() || !Token::Match(tok, "%name% (") || tok->isKeyword()) continue; const Token *parent = tok->next()->astParent(); while (Token::Match(parent, "%cop%")) { if (Token::Match(parent, "<<|>>") && !parent->astParent()) break; parent = parent->astParent(); } if (parent) continue; if (!tok->scope()->isExecutable()) { tok = tok->scope()->bodyEnd; continue; } if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && !WRONG_DATA(!tok->next()->astOperand1(), tok)) { const Library::UseRetValType retvalTy = mSettings->library.getUseRetValType(tok); if (mSettings->severity.isEnabled(Severity::warning) && ((retvalTy == Library::UseRetValType::DEFAULT) || (tok->function() && tok->function()->isAttributeNodiscard()))) ignoredReturnValueError(tok, tok->next()->astOperand1()->expressionString()); else if (mSettings->severity.isEnabled(Severity::style) && retvalTy == Library::UseRetValType::ERROR_CODE) ignoredReturnErrorCode(tok, tok->next()->astOperand1()->expressionString()); } } } } void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function) { reportError(tok, Severity::warning, "ignoredReturnValue", "$symbol:" + function + "\nReturn value of function $symbol() is not used.", CWE252, Certainty::normal); } void CheckFunctions::ignoredReturnErrorCode(const Token* tok, const std::string& function) { reportError(tok, Severity::style, "ignoredReturnErrorCode", "$symbol:" + function + "\nError code from the return value of function $symbol() is not used.", CWE252, Certainty::normal); } //--------------------------------------------------------------------------- // Check for ignored return values. //--------------------------------------------------------------------------- static const Token *checkMissingReturnScope(const Token *tok, const Library &library); void CheckFunctions::checkMissingReturn() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { const Function *function = scope->function; if (!function || !function->hasBody()) continue; if (function->name() == "main" && !(mSettings->standards.c < Standards::C99 && mTokenizer->isC())) continue; if (function->type != Function::Type::eFunction && function->type != Function::Type::eOperatorEqual) continue; if (Token::Match(function->retDef, "%name% (") && function->retDef->isUpperCaseName()) continue; if (Function::returnsVoid(function, true)) continue; const Token *errorToken = checkMissingReturnScope(scope->bodyEnd, mSettings->library); if (errorToken) missingReturnError(errorToken); } } static bool isForwardJump(const Token *gotoToken) { if (!Token::Match(gotoToken, "goto %name% ;")) return false; for (const Token *prev = gotoToken; gotoToken; gotoToken = gotoToken->previous()) { if (Token::Match(prev, "%name% :") && prev->str() == gotoToken->next()->str()) return true; if (prev->str() == "{" && prev->scope()->type == Scope::eFunction) return false; } return false; } static const Token *checkMissingReturnScope(const Token *tok, const Library &library) { const Token *lastStatement = nullptr; while ((tok = tok->previous()) != nullptr) { if (tok->str() == ")") tok = tok->link(); if (tok->str() == "{") return lastStatement ? lastStatement : tok->next(); if (tok->str() == "}") { for (const Token *prev = tok->link()->previous(); prev && prev->scope() == tok->scope() && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { if (prev->isKeyword() && Token::Match(prev, "return|throw")) return nullptr; if (prev->str() == "goto" && !isForwardJump(prev)) return nullptr; } if (tok->scope()->type == Scope::ScopeType::eSwitch) { // find reachable break / !default bool hasDefault = false; bool reachable = false; for (const Token *switchToken = tok->link()->next(); switchToken != tok; switchToken = switchToken->next()) { if (reachable && Token::simpleMatch(switchToken, "break ;")) { if (Token::simpleMatch(switchToken->previous(), "}") && !checkMissingReturnScope(switchToken->previous(), library)) reachable = false; else return switchToken; } if (switchToken->isKeyword() && Token::Match(switchToken, "return|throw")) reachable = false; if (Token::Match(switchToken, "%name% (") && library.isnoreturn(switchToken)) reachable = false; if (Token::Match(switchToken, "case|default")) reachable = true; if (Token::simpleMatch(switchToken, "default :")) hasDefault = true; else if (switchToken->str() == "{" && (switchToken->scope()->isLoopScope() || switchToken->scope()->type == Scope::ScopeType::eSwitch)) switchToken = switchToken->link(); } if (!hasDefault) return tok->link(); } else if (tok->scope()->type == Scope::ScopeType::eIf) { const Token *condition = tok->scope()->classDef->next()->astOperand2(); if (condition && condition->hasKnownIntValue() && condition->getKnownIntValue() == 1) return checkMissingReturnScope(tok, library); return tok; } else if (tok->scope()->type == Scope::ScopeType::eElse) { const Token *errorToken = checkMissingReturnScope(tok, library); if (errorToken) return errorToken; tok = tok->link(); if (Token::simpleMatch(tok->tokAt(-2), "} else {")) return checkMissingReturnScope(tok->tokAt(-2), library); return tok; } // FIXME return nullptr; } if (tok->isKeyword() && Token::Match(tok, "return|throw")) return nullptr; if (tok->str() == "goto" && !isForwardJump(tok)) return nullptr; if (Token::Match(tok, "%name% (") && !library.isnotnoreturn(tok)) { const Token *start = tok; while (Token::Match(start->tokAt(-2), "%name% :: %name%")) start = start->tokAt(-2); if (Token::Match(start->previous(), "[;{}] %name% ::|(")) return nullptr; } if (Token::Match(tok, "[;{}] %name% :")) return tok; if (Token::Match(tok, "; !!}") && !lastStatement) lastStatement = tok->next(); } return nullptr; } void CheckFunctions::missingReturnError(const Token* tok) { reportError(tok, Severity::error, "missingReturn", "Found a exit path from function with non-void return type that has missing return statement", CWE758, Certainty::normal); } //--------------------------------------------------------------------------- // Detect passing wrong values to functions like atan(0, x); //--------------------------------------------------------------------------- void CheckFunctions::checkMathFunctions() { const bool styleC99 = mSettings->severity.isEnabled(Severity::style) && mSettings->standards.c != Standards::C89 && mSettings->standards.cpp != Standards::CPP03; const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId()) continue; if (printWarnings && Token::Match(tok, "%name% ( !!)")) { if (tok->strAt(-1) != "." && Token::Match(tok, "log|logf|logl|log10|log10f|log10l|log2|log2f|log2l ( %num% )")) { const std::string& number = tok->strAt(2); if ((MathLib::isInt(number) && MathLib::toLongNumber(number) <= 0) || (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= 0.)) mathfunctionCallWarning(tok); } else if (Token::Match(tok, "log1p|log1pf|log1pl ( %num% )")) { const std::string& number = tok->strAt(2); if ((MathLib::isInt(number) && MathLib::toLongNumber(number) <= -1) || (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= -1.)) mathfunctionCallWarning(tok); } // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined else if (Token::Match(tok, "atan2|atan2f|atan2l ( %num% , %num% )")) { if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4))) mathfunctionCallWarning(tok, 2); } // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). else if (Token::Match(tok, "fmod|fmodf|fmodl (")) { const Token* nextArg = tok->tokAt(2)->nextArgument(); if (nextArg && MathLib::isNullValue(nextArg->str())) mathfunctionCallWarning(tok, 2); } // pow ( x , y) If x is zero, and y is negative --> division by zero else if (Token::Match(tok, "pow|powf|powl ( %num% , %num% )")) { if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4))) mathfunctionCallWarning(tok, 2); } } if (styleC99) { if (Token::Match(tok, "%num% - erf (") && Tokenizer::isOneNumber(tok->str()) && tok->next()->astOperand2() == tok->tokAt(3)) { mathfunctionCallWarning(tok, "1 - erf(x)", "erfc(x)"); } else if (Token::simpleMatch(tok, "exp (") && Token::Match(tok->linkAt(1), ") - %num%") && Tokenizer::isOneNumber(tok->linkAt(1)->strAt(2)) && tok->linkAt(1)->next()->astOperand1() == tok->next()) { mathfunctionCallWarning(tok, "exp(x) - 1", "expm1(x)"); } else if (Token::simpleMatch(tok, "log (") && tok->next()->astOperand2()) { const Token* plus = tok->next()->astOperand2(); if (plus->str() == "+" && ((plus->astOperand1() && Tokenizer::isOneNumber(plus->astOperand1()->str())) || (plus->astOperand2() && Tokenizer::isOneNumber(plus->astOperand2()->str())))) mathfunctionCallWarning(tok, "log(1 + x)", "log1p(x)"); } } } } } void CheckFunctions::mathfunctionCallWarning(const Token *tok, const nonneg int numParam) { if (tok) { if (numParam == 1) reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing value " + tok->strAt(2) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal); else if (numParam == 2) reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal); } else reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.", CWE758, Certainty::normal); } void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp) { reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.", CWE758, Certainty::normal); } //--------------------------------------------------------------------------- // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted //--------------------------------------------------------------------------- void CheckFunctions::memsetZeroBytes() { // FIXME: // Replace this with library configuration. // For instance: // // // if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "memset|wmemset (") && (numberOfArguments(tok)==3)) { const std::vector &arguments = getArguments(tok); if (WRONG_DATA(arguments.size() != 3U, tok)) continue; const Token* lastParamTok = arguments[2]; if (MathLib::isNullValue(lastParamTok->str())) memsetZeroBytesError(tok); } } } } void CheckFunctions::memsetZeroBytesError(const Token *tok) { const std::string summary("memset() called to fill 0 bytes."); const std::string verbose(summary + " The second and third arguments might be inverted." " The function memset ( void * ptr, int value, size_t num ) sets the" " first num bytes of the block of memory pointed by ptr to the specified value."); reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose, CWE687, Certainty::normal); } void CheckFunctions::memsetInvalid2ndParam() { // FIXME: // Replace this with library configuration. // For instance: // // // // const bool printPortability = mSettings->severity.isEnabled(Severity::portability); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); if (!printWarning && !printPortability) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { if (!Token::simpleMatch(tok, "memset (")) continue; const std::vector args = getArguments(tok); if (args.size() != 3) continue; // Second parameter is zero literal, i.e. 0.0f const Token * const secondParamTok = args[1]; if (Token::Match(secondParamTok, "%num% ,") && MathLib::isNullValue(secondParamTok->str())) continue; // Check if second parameter is a float variable or a float literal != 0.0f if (printPortability && astIsFloat(secondParamTok,false)) { memsetFloatError(secondParamTok, secondParamTok->expressionString()); } if (printWarning && secondParamTok->isNumber()) { // Check if the second parameter is a literal and is out of range const long long int value = MathLib::toLongNumber(secondParamTok->str()); const long long sCharMin = mSettings->signedCharMin(); const long long uCharMax = mSettings->unsignedCharMax(); if (value < sCharMin || value > uCharMax) memsetValueOutOfRangeError(secondParamTok, secondParamTok->str()); } } } } void CheckFunctions::memsetFloatError(const Token *tok, const std::string &var_value) { const std::string message("The 2nd memset() argument '" + var_value + "' is a float, its representation is implementation defined."); const std::string verbose(message + " memset() is used to set each byte of a block of memory to a specific value and" " the actual representation of a floating-point value is implementation defined."); reportError(tok, Severity::portability, "memsetFloat", message + "\n" + verbose, CWE688, Certainty::normal); } void CheckFunctions::memsetValueOutOfRangeError(const Token *tok, const std::string &value) { const std::string message("The 2nd memset() argument '" + value + "' doesn't fit into an 'unsigned char'."); const std::string verbose(message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value."); reportError(tok, Severity::warning, "memsetValueOutOfRange", message + "\n" + verbose, CWE686, Certainty::normal); } //--------------------------------------------------------------------------- // --check-library => warn for unconfigured functions //--------------------------------------------------------------------------- void CheckFunctions::checkLibraryMatchFunctions() { if (!mSettings->checkLibrary || !mSettings->severity.isEnabled(Severity::information)) return; bool insideNew = false; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->scope() || !tok->scope()->isExecutable()) continue; if (tok->str() == "new") insideNew = true; else if (tok->str() == ";") insideNew = false; else if (insideNew) continue; if (!Token::Match(tok, "%name% (") || Token::Match(tok, "asm|sizeof|catch")) continue; if (tok->varId() != 0 || tok->type() || tok->isStandardType() || tok->isControlFlowKeyword()) continue; if (tok->linkAt(1)->strAt(1) == "(") continue; if (tok->function()) continue; if (!mSettings->library.isNotLibraryFunction(tok)) continue; const std::string &functionName = mSettings->library.getFunctionName(tok); if (functionName.empty() || mSettings->library.functions.find(functionName) != mSettings->library.functions.end()) continue; reportError(tok, Severity::information, "checkLibraryFunction", "--check-library: There is no matching configuration for function " + functionName + "()"); } } // Check for problems to compiler apply (Named) Return Value Optimization for local variable // Technically we have different guarantees between standard versions // details: https://en.cppreference.com/w/cpp/language/copy_elision void CheckFunctions::returnLocalStdMove() { if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11) return; if (!mSettings->severity.isEnabled(Severity::performance)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { // Expect return by-value if (Function::returnsReference(scope->function, true)) continue; const auto rets = Function::findReturns(scope->function); for (const Token* ret : rets) { if (!Token::simpleMatch(ret->tokAt(-3), "std :: move (")) continue; const Token* retval = ret->astOperand2(); // NRVO if (retval->variable() && retval->variable()->isLocal() && !retval->variable()->isVolatile()) copyElisionError(retval); // RVO if (Token::Match(retval, "(|{") && !retval->isCast()) copyElisionError(retval); } } } void CheckFunctions::copyElisionError(const Token *tok) { reportError(tok, Severity::performance, "returnStdMoveLocal", "Using std::move for returning object by-value from function will affect copy elision optimization." " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local"); } cppcheck-2.7/lib/checkfunctions.h000066400000000000000000000145561417746362400171200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkfunctionsH #define checkfunctionsH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "utils.h" #include #include #include class Token; class Tokenizer; class ErrorLogger; namespace ValueFlow { class Value; } // namespace ValueFlow /// @addtogroup Checks /// @{ /** * @brief Check for bad function usage */ class CPPCHECKLIB CheckFunctions : public Check { public: /** This constructor is used when registering the CheckFunctions */ CheckFunctions() : Check(myName()) {} /** This constructor is used when running checks. */ CheckFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckFunctions checkFunctions(tokenizer, settings, errorLogger); checkFunctions.checkIgnoredReturnValue(); checkFunctions.checkMissingReturn(); // Missing "return" in exit path // --check-library : functions with nonmatching configuration checkFunctions.checkLibraryMatchFunctions(); checkFunctions.checkProhibitedFunctions(); checkFunctions.invalidFunctionUsage(); checkFunctions.checkMathFunctions(); checkFunctions.memsetZeroBytes(); checkFunctions.memsetInvalid2ndParam(); checkFunctions.returnLocalStdMove(); } /** Check for functions that should not be used */ void checkProhibitedFunctions(); /** * @brief Invalid function usage (invalid input value / overlapping data) * * %Check that given function parameters are valid according to the standard * - wrong radix given for strtol/strtoul * - overlapping data when using sprintf/snprintf * - wrong input value according to library */ void invalidFunctionUsage(); /** @brief %Check for ignored return values. */ void checkIgnoredReturnValue(); /** @brief %Check for parameters given to math function that do not make sense*/ void checkMathFunctions(); /** @brief %Check for filling zero bytes with memset() */ void memsetZeroBytes(); /** @brief %Check for invalid 2nd parameter of memset() */ void memsetInvalid2ndParam(); /** @brief %Check for copy elision by RVO|NRVO */ void returnLocalStdMove(); /** @brief --check-library: warn for unconfigured function calls */ void checkLibraryMatchFunctions(); private: /** @brief %Check for missing "return" */ void checkMissingReturn(); void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr); void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr); void ignoredReturnValueError(const Token* tok, const std::string& function); void ignoredReturnErrorCode(const Token* tok, const std::string& function); void mathfunctionCallWarning(const Token *tok, const nonneg int numParam = 1); void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp); void memsetZeroBytesError(const Token *tok); void memsetFloatError(const Token *tok, const std::string &var_value); void memsetValueOutOfRangeError(const Token *tok, const std::string &value); void missingReturnError(const Token *tok); void copyElisionError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckFunctions c(nullptr, settings, errorLogger); for (std::map::const_iterator i = settings->library.functionwarn.cbegin(); i != settings->library.functionwarn.cend(); ++i) { c.reportError(nullptr, Severity::style, i->first+"Called", i->second.message); } c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4"); c.invalidFunctionArgBoolError(nullptr, "func_name", 1); c.invalidFunctionArgStrError(nullptr, "func_name", 1); c.ignoredReturnValueError(nullptr, "malloc"); c.mathfunctionCallWarning(nullptr); c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)"); c.memsetZeroBytesError(nullptr); c.memsetFloatError(nullptr, "varname"); c.memsetValueOutOfRangeError(nullptr, "varname"); c.missingReturnError(nullptr); c.copyElisionError(nullptr); } static std::string myName() { return "Check function usage"; } std::string classInfo() const OVERRIDE { return "Check function usage:\n" "- missing 'return' in non-void function\n" "- return value of certain functions not used\n" "- invalid input values for functions\n" "- Warn if a function is called whose usage is discouraged\n" "- memset() third argument is zero\n" "- memset() with a value out of range as the 2nd parameter\n" "- memset() with a float as the 2nd parameter\n" "- copy elision optimization for returning value affected by std::move\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkfunctionsH cppcheck-2.7/lib/checkinternal.cpp000066400000000000000000000412331417746362400172470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef CHECK_INTERNAL #include "checkinternal.h" #include "astutils.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include // Register this check class (by creating a static instance of it). // Disabled in release builds namespace { CheckInternal instance; } void CheckInternal::checkTokenMatchPatterns() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); if (pattern.empty()) { simplePatternError(tok, pattern, funcname); continue; } if (pattern.find("||") != std::string::npos || pattern.find(" | ") != std::string::npos || pattern[0] == '|' || (pattern[pattern.length() - 1] == '|' && pattern[pattern.length() - 2] == ' ')) orInComplexPattern(tok, pattern, funcname); // Check for signs of complex patterns if (pattern.find_first_of("[|") != std::string::npos) continue; else if (pattern.find("!!") != std::string::npos) continue; bool complex = false; size_t index = pattern.find('%'); while (index != std::string::npos) { if (pattern.length() <= index + 2) { complex = true; break; } if (pattern[index + 1] == 'o' && pattern[index + 2] == 'r') // %or% or %oror% index = pattern.find('%', index + 1); else { complex = true; break; } index = pattern.find('%', index + 1); } if (!complex) simplePatternError(tok, pattern, funcname); } } } void CheckInternal::checkRedundantTokCheck() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "&& Token :: simpleMatch|Match|findsimplematch|findmatch (")) { // in code like // if (tok->previous() && Token::match(tok->previous(), "bla")) {} // the first tok->previous() check is redundant const Token *astOp1 = tok->astOperand1(); const Token *astOp2 = getArguments(tok->tokAt(3))[0]; if (Token::simpleMatch(astOp1, "&&")) { astOp1 = astOp1->astOperand2(); } if (astOp1->expressionString() == astOp2->expressionString()) { checkRedundantTokCheckError(astOp2); } // if (!tok || !Token::match(tok, "foo")) } else if (Token::Match(tok, "%oror% ! Token :: simpleMatch|Match|findsimplematch|findmatch (")) { const Token *negTok = tok->next()->astParent()->astOperand1(); if (Token::simpleMatch(negTok, "||")) { negTok = negTok->astOperand2(); } // the first tok condition is negated if (Token::simpleMatch(negTok, "!")) { const Token *astOp1 = negTok->astOperand1(); const Token *astOp2 = getArguments(tok->tokAt(4))[0]; if (astOp1->expressionString() == astOp2->expressionString()) { checkRedundantTokCheckError(astOp2); } } } } } void CheckInternal::checkRedundantTokCheckError(const Token* tok) { reportError(tok, Severity::style, "redundantTokCheck", "Unnecessary check of \"" + (tok? tok->expressionString(): emptyString) + "\", match-function already checks if it is null."); } void CheckInternal::checkTokenSimpleMatchPatterns() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: simpleMatch (") && !Token::simpleMatch(tok, "Token :: findsimplematch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); if (pattern.empty()) { complexPatternError(tok, pattern, funcname); continue; } // Check for [xyz] usage - but exclude standalone square brackets unsigned int char_count = 0; for (char c : pattern) { if (c == ' ') { char_count = 0; } else if (c == ']') { if (char_count > 0) { complexPatternError(tok, pattern, funcname); continue; } } else { ++char_count; } } // Check | usage: Count characters before the symbol char_count = 0; for (char c : pattern) { if (c == ' ') { char_count = 0; } else if (c == '|') { if (char_count > 0) { complexPatternError(tok, pattern, funcname); continue; } } else { ++char_count; } } // Check for real errors if (pattern.length() > 1) { for (size_t j = 0; j < pattern.length() - 1; j++) { if (pattern[j] == '%' && pattern[j + 1] != ' ') complexPatternError(tok, pattern, funcname); else if (pattern[j] == '!' && pattern[j + 1] == '!') complexPatternError(tok, pattern, funcname); } } } } } namespace { const std::set knownPatterns = { "%any%" , "%assign%" , "%bool%" , "%char%" , "%comp%" , "%num%" , "%op%" , "%cop%" , "%or%" , "%oror%" , "%str%" , "%type%" , "%name%" , "%var%" , "%varid%" }; } void CheckInternal::checkMissingPercentCharacter() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); std::set::const_iterator knownPattern, knownPatternsEnd = knownPatterns.end(); for (knownPattern = knownPatterns.begin(); knownPattern != knownPatternsEnd; ++knownPattern) { const std::string brokenPattern = (*knownPattern).substr(0, (*knownPattern).size() - 1); std::string::size_type pos = 0; while ((pos = pattern.find(brokenPattern, pos)) != std::string::npos) { // Check if it's the full pattern if (pattern.find(*knownPattern, pos) != pos) { // Known whitelist of substrings if ((brokenPattern == "%var" && pattern.find("%varid%", pos) == pos) || (brokenPattern == "%or" && pattern.find("%oror%", pos) == pos)) { ++pos; continue; } missingPercentCharacterError(tok, pattern, funcname); } ++pos; } } } } } void CheckInternal::checkUnknownPattern() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) continue; // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); bool inBrackets = false; for (std::string::size_type j = 0; j < pattern.length() - 1; j++) { if (pattern[j] == '[' && (j == 0 || pattern[j - 1] == ' ')) inBrackets = true; else if (pattern[j] == ']') inBrackets = false; else if (pattern[j] == '%' && pattern[j + 1] != ' ' && pattern[j + 1] != '|' && !inBrackets) { const std::string::size_type end = pattern.find('%', j + 1); if (end != std::string::npos) { const std::string s = pattern.substr(j, end - j + 1); if (knownPatterns.find(s) == knownPatterns.end()) unknownPatternError(tok, s); } } } } } } void CheckInternal::checkRedundantNextPrevious() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() != ".") continue; tok = tok->next(); if (Token::Match(tok, "previous ( ) . next|tokAt|strAt|linkAt (") || Token::Match(tok, "next ( ) . previous|tokAt|strAt|linkAt (") || (Token::simpleMatch(tok, "tokAt (") && Token::Match(tok->linkAt(1), ") . previous|next|tokAt|strAt|linkAt|str|link ("))) { const std::string& func1 = tok->str(); const std::string& func2 = tok->linkAt(1)->strAt(2); if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->linkAt(1)->strAt(4) != ")") continue; redundantNextPreviousError(tok, func1, func2); } else if (Token::Match(tok, "next|previous ( ) . next|previous ( ) . next|previous|linkAt|strAt|link|str (")) { const std::string& func1 = tok->str(); const std::string& func2 = tok->strAt(8); if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->strAt(10) != ")") continue; redundantNextPreviousError(tok, func1, func2); } } } } void CheckInternal::checkExtraWhitespace() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "Token :: simpleMatch|findsimplematch|Match|findmatch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); if (!pattern.empty() && (pattern[0] == ' ' || *pattern.rbegin() == ' ')) extraWhitespaceError(tok, pattern, funcname); // two whitespaces or more if (pattern.find(" ") != std::string::npos) extraWhitespaceError(tok, pattern, funcname); } } } void CheckInternal::checkStlUsage() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, ". emplace (")) reportError(tok, Severity::error, "internalStlUsage", "The 'emplace' function shall be avoided for now. It is not available e.g. in Slackware 14.0. 'emplace_back' is fine."); //if (Token::simpleMatch(tok, ". back ( )") && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->container && Token::simpleMatch(tok->astOperand1()->valueType()->container, "std :: string")) // reportError(tok, Severity::error, "internalStlUsage", "The 'std::string::back()' function shall be avoided for now."); } } } void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "multiComparePatternError", "Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%name%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "simplePatternError", "Found simple pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } void CheckInternal::complexPatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "complexPatternError", "Found complex pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } void CheckInternal::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname) { reportError(tok, Severity::error, "missingPercentCharacter", "Missing percent end character in Token::" + funcname + "() pattern: \"" + pattern + "\"" ); } void CheckInternal::unknownPatternError(const Token* tok, const std::string& pattern) { reportError(tok, Severity::error, "unknownPattern", "Unknown pattern used: \"" + pattern + "\""); } void CheckInternal::redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2) { reportError(tok, Severity::style, "redundantNextPrevious", "Call to 'Token::" + func1 + "()' followed by 'Token::" + func2 + "()' can be simplified."); } void CheckInternal::orInComplexPattern(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "orInComplexPattern", "Token::" + funcname + "() pattern \"" + pattern + "\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\"."); } void CheckInternal::extraWhitespaceError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "extraWhitespaceError", "Found extra whitespace inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } #endif // #ifdef CHECK_INTERNAL cppcheck-2.7/lib/checkinternal.h000066400000000000000000000121721417746362400167140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkinternalH #define checkinternalH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "settings.h" #include /// @addtogroup Checks /// @{ /** @brief %Check Internal cppcheck API usage */ class CPPCHECKLIB CheckInternal : public Check { public: /** This constructor is used when registering the CheckClass */ CheckInternal() : Check(myName()) {} /** This constructor is used when running checks. */ CheckInternal(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (!settings->checks.isEnabled(Checks::internalCheck)) return; CheckInternal checkInternal(tokenizer, settings, errorLogger); checkInternal.checkTokenMatchPatterns(); checkInternal.checkTokenSimpleMatchPatterns(); checkInternal.checkMissingPercentCharacter(); checkInternal.checkUnknownPattern(); checkInternal.checkRedundantNextPrevious(); checkInternal.checkExtraWhitespace(); checkInternal.checkRedundantTokCheck(); checkInternal.checkStlUsage(); } /** @brief %Check if a simple pattern is used inside Token::Match or Token::findmatch */ void checkTokenMatchPatterns(); /** @brief %Check if a complex pattern is used inside Token::simpleMatch or Token::findsimplematch */ void checkTokenSimpleMatchPatterns(); /** @brief %Check for missing % end character in Token::Match pattern */ void checkMissingPercentCharacter(); /** @brief %Check for unknown (invalid) complex patterns like "%typ%" */ void checkUnknownPattern(); /** @brief %Check for inefficient usage of Token::next(), Token::previous() and Token::tokAt() */ void checkRedundantNextPrevious(); /** @brief %Check if there is whitespace at the beginning or at the end of a pattern */ void checkExtraWhitespace(); /** @brief %Check if there is a redundant check for none-nullness of parameter before Match functions, such as (tok && Token::Match(tok, "foo")) */ void checkRedundantTokCheck(); /** @brief Try to avoid some new functions that are not fully supported in Linux */ void checkStlUsage(); private: void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); void unknownPatternError(const Token* tok, const std::string& pattern); void redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2); void orInComplexPattern(const Token *tok, const std::string &pattern, const std::string &funcname); void extraWhitespaceError(const Token *tok, const std::string &pattern, const std::string &funcname); void checkRedundantTokCheckError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckInternal c(nullptr, settings, errorLogger); c.multiComparePatternError(nullptr, ";|%type%", "Match"); c.simplePatternError(nullptr, "class {", "Match"); c.complexPatternError(nullptr, "%type% ( )", "Match"); c.missingPercentCharacterError(nullptr, "%num", "Match"); c.unknownPatternError(nullptr, "%typ"); c.redundantNextPreviousError(nullptr, "previous", "next"); c.orInComplexPattern(nullptr, "||", "Match"); c.extraWhitespaceError(nullptr, "%str% ", "Match"); c.checkRedundantTokCheckError(nullptr); } static std::string myName() { return "cppcheck internal API usage"; } std::string classInfo() const OVERRIDE { // Don't include these checks on the WIKI where people can read what // checks there are. These checks are not intended for users. return ""; } }; /// @} //--------------------------------------------------------------------------- #endif // checkinternalH cppcheck-2.7/lib/checkio.cpp000066400000000000000000003327671417746362400160610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkio.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register CheckIO.. namespace { CheckIO instance; } // CVE ID used: static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime static const CWE CWE685(685U); // Function Call With Incorrect Number of Arguments static const CWE CWE686(686U); // Function Call With Incorrect Argument Type static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value static const CWE CWE704(704U); // Incorrect Type Conversion or Cast static const CWE CWE910(910U); // Use of Expired File Descriptor //--------------------------------------------------------------------------- // std::cout << std::cout; //--------------------------------------------------------------------------- void CheckIO::checkCoutCerrMisusage() { if (mTokenizer->isC()) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "std :: cout|cerr !!.") && tok->next()->astParent() && tok->next()->astParent()->astOperand1() == tok->next()) { const Token* tok2 = tok->next(); while (tok2->astParent() && tok2->astParent()->str() == "<<") { tok2 = tok2->astParent(); if (tok2->astOperand2() && Token::Match(tok2->astOperand2()->previous(), "std :: cout|cerr")) coutCerrMisusageError(tok, tok2->astOperand2()->strAt(1)); } } } } } void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName) { reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // fflush(stdin) <- fflush only applies to output streams in ANSI C // fread(); fwrite(); <- consecutive read/write statements require repositioning in between // fopen("","r"); fwrite(); <- write to read-only file (or vice versa) // fclose(); fread(); <- Use closed file //--------------------------------------------------------------------------- enum class OpenMode { CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN_OM }; static OpenMode getMode(const std::string& str) { if (str.find('+', 1) != std::string::npos) return OpenMode::RW_MODE; else if (str.find('w') != std::string::npos || str.find('a') != std::string::npos) return OpenMode::WRITE_MODE; else if (str.find('r') != std::string::npos) return OpenMode::READ_MODE; return OpenMode::UNKNOWN_OM; } struct Filepointer { OpenMode mode; nonneg int mode_indent; enum class Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation; nonneg int op_indent; enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX }; AppendMode append_mode; std::string filename; explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM) : mode(mode_), mode_indent(0), lastOperation(Operation::NONE), op_indent(0), append_mode(AppendMode::UNKNOWN_AM) {} }; namespace { const std::unordered_set whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" }; } void CheckIO::checkFileUsage() { const bool windows = mSettings->isWindowsPlatform(); const bool printPortability = mSettings->severity.isEnabled(Severity::portability); const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); std::map filepointers; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->declarationId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *")) continue; if (var->isLocal()) { if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor" filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM))); else filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::CLOSED))); } else { filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM))); // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode } } for (const Scope * scope : symbolDatabase->functionScopes) { int indent = 0; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "{") indent++; else if (tok->str() == "}") { indent--; for (std::pair& filepointer : filepointers) { if (indent < filepointer.second.mode_indent) { filepointer.second.mode_indent = 0; filepointer.second.mode = OpenMode::UNKNOWN_OM; } if (indent < filepointer.second.op_indent) { filepointer.second.op_indent = 0; filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; } } } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break" || mSettings->library.isnoreturn(tok)) { // Reset upon return, continue or break for (std::pair& filepointer : filepointers) { filepointer.second.mode_indent = 0; filepointer.second.mode = OpenMode::UNKNOWN_OM; filepointer.second.op_indent = 0; filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; } } else if (Token::Match(tok, "%var% =") && (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile" && (windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) { std::map::iterator i = filepointers.find(tok->varId()); if (i != filepointers.end()) { i->second.mode = OpenMode::UNKNOWN_OM; i->second.lastOperation = Filepointer::Operation::UNKNOWN_OP; } } else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { std::string mode; const Token* fileTok = nullptr; const Token* fileNameTok = nullptr; Filepointer::Operation operation = Filepointer::Operation::NONE; if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" || (windows && (tok->str() == "_wfopen" || tok->str() == "_wfreopen"))) && tok->strAt(-1) == "=") { if (tok->str() != "tmpfile") { const Token* modeTok = tok->tokAt(2)->nextArgument(); if (modeTok && modeTok->tokType() == Token::eString) mode = modeTok->strValue(); } else mode = "wb+"; fileTok = tok->tokAt(-2); operation = Filepointer::Operation::OPEN; if (Token::Match(tok, "fopen ( %str% ,")) fileNameTok = tok->tokAt(2); } else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name%")) { const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument(); if (modeTok && modeTok->tokType() == Token::eString) mode = modeTok->strValue(); fileTok = tok->tokAt(3); operation = Filepointer::Operation::OPEN; } else if ((tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") || (windows && tok->str() == "_fseeki64")) { fileTok = tok->tokAt(2); if (printPortability && fileTok && tok->str() == "fflush") { if (fileTok->str() == "stdin") fflushOnInputStreamError(tok, fileTok->str()); else { const Filepointer& f = filepointers[fileTok->varId()]; if (f.mode == OpenMode::READ_MODE) fflushOnInputStreamError(tok, fileTok->str()); } } operation = Filepointer::Operation::POSITIONING; } else if (tok->str() == "fgetc" || tok->str() == "fgetwc" || tok->str() == "fgets" || tok->str() == "fgetws" || tok->str() == "fread" || tok->str() == "fscanf" || tok->str() == "fwscanf" || tok->str() == "getc" || (windows && (tok->str() == "fscanf_s" || tok->str() == "fwscanf_s"))) { if (tok->str().find("scanf") != std::string::npos) fileTok = tok->tokAt(2); else fileTok = tok->linkAt(1)->previous(); operation = Filepointer::Operation::READ; } else if (tok->str() == "fputc" || tok->str() == "fputwc" || tok->str() == "fputs" || tok->str() == "fputws" || tok->str() == "fwrite" || tok->str() == "fprintf" || tok->str() == "fwprintf" || tok->str() == "putcc" || (windows && (tok->str() == "fprintf_s" || tok->str() == "fwprintf_s"))) { if (tok->str().find("printf") != std::string::npos) fileTok = tok->tokAt(2); else fileTok = tok->linkAt(1)->previous(); operation = Filepointer::Operation::WRITE; } else if (tok->str() == "fclose") { fileTok = tok->tokAt(2); operation = Filepointer::Operation::CLOSE; } else if (whitelist.find(tok->str()) != whitelist.end()) { fileTok = tok->tokAt(2); if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok) fileTok = fileTok->nextArgument(); operation = Filepointer::Operation::UNIMPORTANT; } else if (!Token::Match(tok, "if|for|while|catch|switch") && !mSettings->library.isFunctionConst(tok->str(), true)) { const Token* const end2 = tok->linkAt(1); if (scope->functionOf && scope->functionOf->isClassOrStruct() && !scope->function->isStatic() && ((tok->strAt(-1) != "::" && tok->strAt(-1) != ".") || tok->strAt(-2) == "this")) { if (!tok->function() || (tok->function()->nestedIn && tok->function()->nestedIn->isClassOrStruct())) { for (std::pair& filepointer : filepointers) { const Variable* var = symbolDatabase->getVariableFromVarId(filepointer.first); if (!var || !(var->isLocal() || var->isGlobal() || var->isStatic())) { filepointer.second.mode = OpenMode::UNKNOWN_OM; filepointer.second.mode_indent = 0; filepointer.second.op_indent = indent; filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; } } continue; } } for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) { fileTok = tok2; operation = Filepointer::Operation::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now break; } } } while (Token::Match(fileTok, "%name% .")) fileTok = fileTok->tokAt(2); if (!fileTok || !fileTok->varId() || fileTok->strAt(1) == "[") continue; if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(OpenMode::UNKNOWN_OM))); } Filepointer& f = filepointers[fileTok->varId()]; switch (operation) { case Filepointer::Operation::OPEN: if (fileNameTok) { for (std::map::const_iterator it = filepointers.cbegin(); it != filepointers.cend(); ++it) { const Filepointer &fptr = it->second; if (fptr.filename == fileNameTok->str() && (fptr.mode == OpenMode::RW_MODE || fptr.mode == OpenMode::WRITE_MODE)) incompatibleFileOpenError(tok, fileNameTok->str()); } f.filename = fileNameTok->str(); } f.mode = getMode(mode); if (mode.find('a') != std::string::npos) { if (f.mode == OpenMode::RW_MODE) f.append_mode = Filepointer::AppendMode::APPEND_EX; else f.append_mode = Filepointer::AppendMode::APPEND; } else f.append_mode = Filepointer::AppendMode::UNKNOWN_AM; f.mode_indent = indent; break; case Filepointer::Operation::POSITIONING: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); else if (f.append_mode == Filepointer::AppendMode::APPEND && tok->str() != "fflush" && printWarnings) seekOnAppendedFileError(tok); break; case Filepointer::Operation::READ: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); else if (f.mode == OpenMode::WRITE_MODE) readWriteOnlyFileError(tok); else if (f.lastOperation == Filepointer::Operation::WRITE) ioWithoutPositioningError(tok); break; case Filepointer::Operation::WRITE: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); else if (f.mode == OpenMode::READ_MODE) writeReadOnlyFileError(tok); else if (f.lastOperation == Filepointer::Operation::READ) ioWithoutPositioningError(tok); break; case Filepointer::Operation::CLOSE: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); else f.mode = OpenMode::CLOSED; f.mode_indent = indent; break; case Filepointer::Operation::UNIMPORTANT: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); break; case Filepointer::Operation::UNKNOWN_OP: f.mode = OpenMode::UNKNOWN_OM; f.mode_indent = 0; break; default: break; } if (operation != Filepointer::Operation::NONE && operation != Filepointer::Operation::UNIMPORTANT) { f.op_indent = indent; f.lastOperation = operation; } } } for (std::pair& filepointer : filepointers) { filepointer.second.op_indent = 0; filepointer.second.mode = OpenMode::UNKNOWN_OM; filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; } } } void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname) { reportError(tok, Severity::portability, "fflushOnInputStream", "fflush() called on input stream '" + varname + "' may result in undefined behaviour on non-linux systems.", CWE398, Certainty::normal); } void CheckIO::ioWithoutPositioningError(const Token *tok) { reportError(tok, Severity::error, "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.", CWE664, Certainty::normal); } void CheckIO::readWriteOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "readWriteOnlyFile", "Read operation on a file that was opened only for writing.", CWE664, Certainty::normal); } void CheckIO::writeReadOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "writeReadOnlyFile", "Write operation on a file that was opened only for reading.", CWE664, Certainty::normal); } void CheckIO::useClosedFileError(const Token *tok) { reportError(tok, Severity::error, "useClosedFile", "Used file that is not opened.", CWE910, Certainty::normal); } void CheckIO::seekOnAppendedFileError(const Token *tok) { reportError(tok, Severity::warning, "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal); } void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename) { reportError(tok, Severity::warning, "incompatibleFileOpen", "The file '" + filename + "' is opened for read and write access at the same time on different streams", CWE664, Certainty::normal); } //--------------------------------------------------------------------------- // scanf without field width limits can crash with huge input data //--------------------------------------------------------------------------- void CheckIO::invalidScanf() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const Token *formatToken = nullptr; if (Token::Match(tok, "scanf|vscanf ( %str% ,")) formatToken = tok->tokAt(2); else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) { const Token* nextArg = tok->tokAt(2)->nextArgument(); if (nextArg && nextArg->tokType() == Token::eString) formatToken = nextArg; else continue; } else continue; bool format = false; // scan the string backwards, so we do not need to keep states const std::string &formatstr(formatToken->str()); for (std::size_t i = 1; i < formatstr.length(); i++) { if (formatstr[i] == '%') format = !format; else if (!format) continue; else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') { format = false; } else if (std::isalpha((unsigned char)formatstr[i]) || formatstr[i] == '[') { if (formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's')) // #3490 - field width limits are only necessary for string input invalidScanfError(tok); format = false; } } } } } void CheckIO::invalidScanfError(const Token *tok) { const std::string fname = (tok ? tok->str() : std::string("scanf")); reportError(tok, Severity::warning, "invalidscanf", fname + "() without field width limits can crash with huge input data.\n" + fname + "() without field width limits can crash with huge input data. Add a field width " "specifier to fix this problem.\n" "\n" "Sample program that can crash:\n" "\n" "#include \n" "int main()\n" "{\n" " char c[5];\n" " scanf(\"%s\", c);\n" " return 0;\n" "}\n" "\n" "Typing in 5 or more characters may make the program crash. The correct usage " "here is 'scanf(\"%4s\", c);', as the maximum field width does not include the " "terminating null byte.\n" "Source: http://linux.die.net/man/3/scanf\n" "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c", CWE119, Certainty::normal); } //--------------------------------------------------------------------------- // printf("%u", "xyz"); // Wrong argument type // printf("%u%s", 1); // Too few arguments // printf("", 1); // Too much arguments //--------------------------------------------------------------------------- static bool findFormat(nonneg int arg, const Token *firstArg, const Token **formatStringTok, const Token **formatArgTok) { const Token* argTok = firstArg; for (int i = 0; i < arg && argTok; ++i) argTok = argTok->nextArgument(); if (Token::Match(argTok, "%str% [,)]")) { *formatArgTok = argTok->nextArgument(); *formatStringTok = argTok; return true; } else if (Token::Match(argTok, "%var% [,)]") && argTok->variable() && Token::Match(argTok->variable()->typeStartToken(), "char|wchar_t") && (argTok->variable()->isPointer() || (argTok->variable()->dimensions().size() == 1 && argTok->variable()->dimensionKnown(0) && argTok->variable()->dimension(0) != 0))) { *formatArgTok = argTok->nextArgument(); if (!argTok->values().empty()) { std::list::const_iterator value = std::find_if( argTok->values().begin(), argTok->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue)); if (value != argTok->values().end() && value->isTokValue() && value->tokvalue && value->tokvalue->tokType() == Token::eString) { *formatStringTok = value->tokvalue; } } return true; } return false; } // Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename static inline bool typesMatch(const std::string& iToTest, const std::string& iTypename, const std::string& iOptionalPrefix = "std::") { return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename); } void CheckIO::checkWrongPrintfScanfArguments() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); const bool isWindows = mSettings->isWindowsPlatform(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isName()) continue; const Token* argListTok = nullptr; // Points to first va_list argument const Token* formatStringTok = nullptr; // Points to format string token bool scan = false; bool scanf_s = false; int formatStringArgNo = -1; if (tok->strAt(1) == "(" && mSettings->library.formatstr_function(tok)) { formatStringArgNo = mSettings->library.formatstr_argno(tok); scan = mSettings->library.formatstr_scan(tok); scanf_s = mSettings->library.formatstr_secure(tok); } if (formatStringArgNo >= 0) { // formatstring found in library. Find format string and first argument belonging to format string. if (!findFormat(formatStringArgNo, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else if (Token::simpleMatch(tok, "swprintf (")) { if (Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) { // Find third parameter and format string if (!findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else { // Find fourth parameter and format string if (!findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } } else if (isWindows && Token::Match(tok, "sprintf_s|swprintf_s (")) { // template int sprintf_s(char (&buffer)[size], const char *format, ...); if (findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } // int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...); else if (findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } } else if (isWindows && Token::Match(tok, "_snprintf_s|_snwprintf_s (")) { // template int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...); if (findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } // int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...); else if (findFormat(3, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } } else { continue; } if (!formatStringTok) continue; checkFormatString(tok, formatStringTok, argListTok, scan, scanf_s); } } } void CheckIO::checkFormatString(const Token * const tok, const Token * const formatStringTok, const Token * argListTok, const bool scan, const bool scanf_s) { const bool isWindows = mSettings->isWindowsPlatform(); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); const std::string &formatString = formatStringTok->str(); // Count format string parameters.. int numFormat = 0; int numSecure = 0; bool percent = false; const Token* argListTok2 = argListTok; std::set parameterPositionsUsed; for (std::string::const_iterator i = formatString.begin(); i != formatString.end(); ++i) { if (*i == '%') { percent = !percent; } else if (percent && *i == '[') { while (i != formatString.end()) { if (*i == ']') { numFormat++; if (argListTok) argListTok = argListTok->nextArgument(); percent = false; break; } ++i; } if (scanf_s) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } if (i == formatString.end()) break; } else if (percent) { percent = false; bool _continue = false; bool skip = false; std::string width; int parameterPosition = 0; bool hasParameterPosition = false; while (i != formatString.end() && *i != '[' && !std::isalpha((unsigned char)*i)) { if (*i == '*') { skip = true; if (scan) _continue = true; else { numFormat++; if (argListTok) argListTok = argListTok->nextArgument(); } } else if (std::isdigit(*i)) { width += *i; } else if (*i == '$') { parameterPosition = std::atoi(width.c_str()); hasParameterPosition = true; width.clear(); } ++i; } auto bracketBeg = formatString.end(); if (i != formatString.end() && *i == '[') { bracketBeg = i; while (i != formatString.end()) { if (*i == ']') break; ++i; } if (scanf_s && !skip) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } } if (i == formatString.end()) break; if (_continue) continue; if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. ++numFormat; // Handle parameter positions (POSIX extension) - Ticket #4900 if (hasParameterPosition) { if (parameterPositionsUsed.find(parameterPosition) == parameterPositionsUsed.end()) parameterPositionsUsed.insert(parameterPosition); else // Parameter already referenced, hence don't consider it a new format --numFormat; } // Perform type checks ArgumentInfo argInfo(argListTok, mSettings, mTokenizer->isCPP()); if ((argInfo.typeToken && !argInfo.isLibraryType(mSettings)) || *i == ']') { if (scan) { std::string specifier; bool done = false; while (!done) { switch (*i) { case 's': case ']': // charset specifier += (*i == 's' || bracketBeg == formatString.end()) ? std::string{ 's' } : std::string{ bracketBeg, i + 1 }; if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) { if (!width.empty()) { const int numWidth = std::atoi(width.c_str()); if (numWidth != (argInfo.variableInfo->dimension(0) - 1)) invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, specifier); } } if (argListTok && argListTok->tokType() != Token::eString && argInfo.typeToken && argInfo.isKnownType() && argInfo.isArrayOrPointer() && (!Token::Match(argInfo.typeToken, "char|wchar_t") || argInfo.typeToken->strAt(-1) == "const")) { if (!(argInfo.isArrayOrPointer() && argInfo.element && !argInfo.typeToken->isStandardType())) invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo); } if (scanf_s && argInfo.typeToken) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } done = true; break; case 'c': if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) { if (!width.empty()) { const int numWidth = std::atoi(width.c_str()); if (numWidth > argInfo.variableInfo->dimension(0)) invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, std::string(1, *i)); } } if (scanf_s) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } done = true; break; case 'x': case 'X': case 'u': case 'o': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "char|short|int|long")) { if (argInfo.typeToken->isStandardType() || !argInfo.element) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (!argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const") { invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (argInfo.typeToken->str() != "char") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (argInfo.typeToken->str() != "short") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'j': if (!typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; default: if (argInfo.typeToken->str() != "int") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; } } } done = true; break; case 'n': case 'd': case 'i': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "char|short|int|long")) { if (argInfo.typeToken->isStandardType() || !argInfo.element) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const") { invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (argInfo.typeToken->str() != "char") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (argInfo.typeToken->str() != "short") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "intmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "intmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'j': if (!typesMatch(argInfo.typeToken->originalName(), "intmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'z': if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") || (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T")))) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; default: if (argInfo.typeToken->str() != "int") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; } } } done = true; break; case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "float|double")) { if (argInfo.typeToken->isStandardType()) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else if (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const") { invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'l': if (argInfo.typeToken->str() != "double" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); break; case 'L': if (argInfo.typeToken->str() != "double" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); break; default: if (argInfo.typeToken->str() != "float") invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'I': if ((i+1 != formatString.end() && *(i+1) == '6' && i+2 != formatString.end() && *(i+2) == '4') || (i+1 != formatString.end() && *(i+1) == '3' && i+2 != formatString.end() && *(i+2) == '2')) { specifier += *i++; specifier += *i++; if ((i+1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } } else { if ((i+1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } } break; case 'h': case 'l': if (i+1 != formatString.end() && *(i+1) == *i) specifier += *i++; FALLTHROUGH; case 'j': case 'q': case 't': case 'z': case 'L': // Expect an alphabetical character after these specifiers if ((i + 1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } break; default: done = true; break; } } } else if (printWarning) { std::string specifier; bool done = false; while (!done) { if (i == formatString.end()) { done = true; break; } switch (*i) { case 's': if (argListTok->tokType() != Token::eString && argInfo.isKnownType() && !argInfo.isArrayOrPointer()) { if (!Token::Match(argInfo.typeToken, "char|wchar_t")) { if (!argInfo.element) invalidPrintfArgTypeError_s(tok, numFormat, &argInfo); } } done = true; break; case 'n': if ((argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->tokType() == Token::eString) invalidPrintfArgTypeError_n(tok, numFormat, &argInfo); done = true; break; case 'c': case 'x': case 'X': case 'o': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'j': if (argInfo.typeToken->originalName() != "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!(typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "WPARAM" || argInfo.typeToken->originalName() == "UINT_PTR" || argInfo.typeToken->originalName() == "LONG_PTR" || argInfo.typeToken->originalName() == "LPARAM" || argInfo.typeToken->originalName() == "LRESULT")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; default: if (!Token::Match(argInfo.typeToken, "bool|char|short|wchar_t|int")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'd': case 'i': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) { invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (!(argInfo.typeToken->str() == "char" && !argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (!(argInfo.typeToken->str() == "short" && !argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'j': if (argInfo.typeToken->originalName() != "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'z': if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") || (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T")))) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; default: if (!Token::Match(argInfo.typeToken, "bool|char|short|int")) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'u': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) { invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (!argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "bool|_Bool")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'j': if (argInfo.typeToken->originalName() != "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; default: if (!Token::Match(argInfo.typeToken, "bool|char|short|int")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'p': if (argInfo.typeToken->tokType() == Token::eString) ; // string literals are passed as pointers to literal start, okay else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) invalidPrintfArgTypeError_p(tok, numFormat, &argInfo); done = true; break; case 'e': case 'E': case 'f': case 'g': case 'G': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "float|double")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) || (specifier[0] != 'L' && argInfo.typeToken->isLong())) invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } done = true; break; case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) case 'l': { // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int) // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character if ((i + 1) != formatString.end() && *(i + 1) == *i) { if ((i + 2) != formatString.end() && !isalpha(*(i + 2))) { std::string modifier; modifier += *i; modifier += *(i + 1); invalidLengthModifierError(tok, numFormat, modifier); done = true; } else { specifier = *i++; specifier += *i++; } } else { if ((i + 1) != formatString.end() && !isalpha(*(i + 1))) { std::string modifier; modifier += *i; invalidLengthModifierError(tok, numFormat, modifier); done = true; } else { specifier = *i++; } } } break; case 'I': // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64 if ((*(i+1) == '3' && *(i+2) == '2') || (*(i+1) == '6' && *(i+2) == '4')) { specifier += *i++; specifier += *i++; } FALLTHROUGH; case 'j': // intmax_t or uintmax_t case 'z': // size_t case 't': // ptrdiff_t case 'L': // long double // Expect an alphabetical character after these specifiers if ((i + 1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } break; default: done = true; break; } } } } if (argListTok) argListTok = argListTok->nextArgument(); // Find next argument } } } // Count printf/scanf parameters.. int numFunction = 0; while (argListTok2) { numFunction++; argListTok2 = argListTok2->nextArgument(); // Find next argument } if (printWarning) { // Check that all parameter positions reference an actual parameter for (int i : parameterPositionsUsed) { if ((i == 0) || (i > numFormat)) wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), i, numFormat); } } // Mismatching number of parameters => warning if ((numFormat + numSecure) != numFunction) wrongPrintfScanfArgumentsError(tok, tok->originalName().empty() ? tok->str() : tok->originalName(), numFormat + numSecure, numFunction); } // We currently only support string literals, variables, and functions. /// @todo add non-string literals, and generic expressions CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings *settings, bool _isCPP) : variableInfo(nullptr) , typeToken(nullptr) , functionInfo(nullptr) , tempToken(nullptr) , element(false) , _template(false) , address(false) , isCPP(_isCPP) { if (!arg) return; // Use AST type info // TODO: This is a bailout so that old code is used in simple cases. Remove the old code and always use the AST type. if (!Token::Match(arg, "%str% ,|)") && !(Token::Match(arg,"%var%") && arg->variable() && arg->variable()->isArray())) { const Token *top = arg; while (top->str() == "(" && !top->isCast()) top = top->next(); while (top->astParent() && top->astParent()->str() != "," && top->astParent() != arg->previous()) top = top->astParent(); const ValueType *valuetype = top->argumentType(); if (valuetype && valuetype->type >= ValueType::Type::BOOL) { typeToken = tempToken = new Token(); if (valuetype->pointer && valuetype->constness & 1) { tempToken->str("const"); tempToken->insertToken("a"); tempToken = tempToken->next(); } if (valuetype->type == ValueType::BOOL) tempToken->str("bool"); else if (valuetype->type == ValueType::CHAR) tempToken->str("char"); else if (valuetype->type == ValueType::SHORT) tempToken->str("short"); else if (valuetype->type == ValueType::WCHAR_T) tempToken->str("wchar_t"); else if (valuetype->type == ValueType::INT) tempToken->str("int"); else if (valuetype->type == ValueType::LONG) tempToken->str("long"); else if (valuetype->type == ValueType::LONGLONG) { tempToken->str("long"); tempToken->isLong(true); } else if (valuetype->type == ValueType::FLOAT) tempToken->str("float"); else if (valuetype->type == ValueType::DOUBLE) tempToken->str("double"); else if (valuetype->type == ValueType::LONGDOUBLE) { tempToken->str("double"); tempToken->isLong(true); } if (valuetype->isIntegral()) { if (valuetype->sign == ValueType::Sign::UNSIGNED) tempToken->isUnsigned(true); else if (valuetype->sign == ValueType::Sign::SIGNED) tempToken->isSigned(true); } if (!valuetype->originalTypeName.empty()) tempToken->originalName(valuetype->originalTypeName); for (int p = 0; p < valuetype->pointer; p++) tempToken->insertToken("*"); tempToken = const_cast(typeToken); if (top->isBinaryOp() && valuetype->pointer == 1 && (valuetype->type == ValueType::CHAR || valuetype->type == ValueType::WCHAR_T)) tempToken->tokType(Token::eString); return; } } if (arg->tokType() == Token::eString) { typeToken = arg; return; } else if (arg->str() == "&" || arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction || Token::Match(arg, "%type% ::") || (Token::Match(arg, "static_cast|reinterpret_cast|const_cast <") && Token::simpleMatch(arg->linkAt(1), "> (") && Token::Match(arg->linkAt(1)->linkAt(1), ") ,|)"))) { if (Token::Match(arg, "static_cast|reinterpret_cast|const_cast")) { typeToken = arg->tokAt(2); while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); return; } if (arg->str() == "&") { address = true; arg = arg->next(); } while (Token::Match(arg, "%type% ::")) arg = arg->tokAt(2); if (!arg || !(arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction)) return; const Token *varTok = nullptr; const Token *tok1 = arg->next(); for (; tok1; tok1 = tok1->next()) { if (tok1->str() == "," || tok1->str() == ")") { if (tok1->previous()->str() == "]") { varTok = tok1->linkAt(-1)->previous(); if (varTok->str() == ")" && varTok->link()->previous()->tokType() == Token::eFunction) { const Function * function = varTok->link()->previous()->function(); if (function && function->retType && function->retType->isEnumType()) { if (function->retType->classScope->enumType) typeToken = function->retType->classScope->enumType; else { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); tempToken->str("int"); typeToken = tempToken; } } else if (function && function->retDef) { typeToken = function->retDef; while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); functionInfo = function; element = true; } return; } } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->tokType() == Token::eFunction) { const Function * function = tok1->linkAt(-1)->previous()->function(); if (function && function->retType && function->retType->isEnumType()) { if (function->retType->classScope->enumType) typeToken = function->retType->classScope->enumType; else { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); tempToken->str("int"); typeToken = tempToken; } } else if (function && function->retDef) { typeToken = function->retDef; while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); functionInfo = function; element = false; } return; } else varTok = tok1->previous(); break; } else if (tok1->str() == "(" || tok1->str() == "{" || tok1->str() == "[") tok1 = tok1->link(); else if (tok1->link() && tok1->str() == "<") tok1 = tok1->link(); // check for some common well known functions else if (isCPP && ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous())) || (Token::Match(tok1->previous(), "] . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous()->link()->previous())))) { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); if (tok1->next()->str() == "size") { // size_t is platform dependent if (settings->sizeof_size_t == 8) { tempToken->str("long"); if (settings->sizeof_long != 8) tempToken->isLong(true); } else if (settings->sizeof_size_t == 4) { if (settings->sizeof_long == 4) { tempToken->str("long"); } else { tempToken->str("int"); } } tempToken->originalName("size_t"); tempToken->isUnsigned(true); } else if (tok1->next()->str() == "empty") { tempToken->str("bool"); } else if (tok1->next()->str() == "c_str") { tempToken->str("const"); tempToken->insertToken("*"); if (typeToken->strAt(2) == "string") tempToken->insertToken("char"); else tempToken->insertToken("wchar_t"); } typeToken = tempToken; return; } // check for std::vector::at() and std::string::at() else if (Token::Match(tok1->previous(), "%var% . at (") && Token::Match(tok1->linkAt(2), ") [,)]")) { varTok = tok1->previous(); variableInfo = varTok->variable(); if (!variableInfo || !isStdVectorOrString()) { variableInfo = nullptr; typeToken = nullptr; } return; } else if (!(tok1->str() == "." || tok1->tokType() == Token::eVariable || tok1->tokType() == Token::eFunction)) return; } if (varTok) { variableInfo = varTok->variable(); element = tok1->previous()->str() == "]"; // look for std::vector operator [] and use template type as return type if (variableInfo) { if (element && isStdVectorOrString()) { // isStdVectorOrString sets type token if true element = false; // not really an array element } else if (variableInfo->isEnumType()) { if (variableInfo->type() && variableInfo->type()->classScope && variableInfo->type()->classScope->enumType) typeToken = variableInfo->type()->classScope->enumType; else { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); tempToken->str("int"); typeToken = tempToken; } } else typeToken = variableInfo->typeStartToken(); } return; } } } CheckIO::ArgumentInfo::~ArgumentInfo() { if (tempToken) { while (tempToken->next()) tempToken->deleteNext(); delete tempToken; } } namespace { const std::set stl_vector = { "array", "vector" }; const std::set stl_string = { "string", "u16string", "u32string", "wstring" }; } bool CheckIO::ArgumentInfo::isStdVectorOrString() { if (!isCPP) return false; if (variableInfo->isStlType(stl_vector)) { typeToken = variableInfo->typeStartToken()->tokAt(4); _template = true; return true; } else if (variableInfo->isStlType(stl_string)) { tempToken = new Token(); tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex()); tempToken->linenr(variableInfo->typeStartToken()->linenr()); if (variableInfo->typeStartToken()->strAt(2) == "string") tempToken->str("char"); else tempToken->str("wchar_t"); typeToken = tempToken; return true; } else if (variableInfo->type() && !variableInfo->type()->derivedFrom.empty()) { const std::vector& derivedFrom = variableInfo->type()->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Token* nameTok = i.nameTok; if (Token::Match(nameTok, "std :: vector|array <")) { typeToken = nameTok->tokAt(4); _template = true; return true; } else if (Token::Match(nameTok, "std :: string|wstring")) { tempToken = new Token(); tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex()); tempToken->linenr(variableInfo->typeStartToken()->linenr()); if (nameTok->strAt(2) == "string") tempToken->str("char"); else tempToken->str("wchar_t"); typeToken = tempToken; return true; } } } else if (variableInfo->type()) { const Scope * classScope = variableInfo->type()->classScope; if (classScope) { for (const Function &func : classScope->functionList) { if (func.name() == "operator[]") { if (Token::Match(func.retDef, "%type% &")) { typeToken = func.retDef; return true; } } } } } return false; } static const std::set stl_container = { "array", "bitset", "deque", "forward_list", "hash_map", "hash_multimap", "hash_set", "list", "map", "multimap", "multiset", "priority_queue", "queue", "set", "stack", "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector" }; bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok) { if (!isCPP) return false; if (tok && tok->variable()) { const Variable* variable = tok->variable(); if (variable->isStlType(stl_container)) { typeToken = variable->typeStartToken()->tokAt(4); return true; } else if (variable->isStlType(stl_string)) { typeToken = variable->typeStartToken(); return true; } else if (variable->type() && !variable->type()->derivedFrom.empty()) { for (const Type::BaseInfo &baseInfo : variable->type()->derivedFrom) { const Token* nameTok = baseInfo.nameTok; if (Token::Match(nameTok, "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <")) { typeToken = nameTok->tokAt(4); return true; } else if (Token::Match(nameTok, "std :: string|wstring")) { typeToken = nameTok; return true; } } } } return false; } bool CheckIO::ArgumentInfo::isArrayOrPointer() const { if (address) return true; else if (variableInfo && !_template) { return variableInfo->isArrayOrPointer(); } else { const Token *tok = typeToken; while (Token::Match(tok, "const|struct")) tok = tok->next(); if (tok && tok->strAt(1) == "*") return true; } return false; } bool CheckIO::ArgumentInfo::isComplexType() const { if (variableInfo->type()) return (true); const Token* varTypeTok = typeToken; if (varTypeTok->str() == "std") varTypeTok = varTypeTok->tokAt(2); return ((variableInfo->isStlStringType() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer()); } bool CheckIO::ArgumentInfo::isKnownType() const { if (variableInfo) return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType()); else if (functionInfo) return (typeToken->isStandardType() || functionInfo->retType || Token::Match(typeToken, "std :: string|wstring")); return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring"); } bool CheckIO::ArgumentInfo::isLibraryType(const Settings *settings) const { return typeToken && typeToken->isStandardType() && settings->library.podtype(typeToken->str()); } void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, const std::string &functionName, nonneg int numFormat, nonneg int numFunction) { const Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning; if (severity != Severity::error && !mSettings->severity.isEnabled(Severity::warning)) return; std::ostringstream errmsg; errmsg << functionName << " format string requires " << numFormat << " parameter" << (numFormat != 1 ? "s" : "") << " but " << (numFormat > numFunction ? "only " : "") << numFunction << (numFunction != 1 ? " are" : " is") << " given."; reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str(), CWE685, Certainty::normal); } void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, nonneg int index, nonneg int numFunction) { if (!mSettings->severity.isEnabled(Severity::warning)) return; std::ostringstream errmsg; errmsg << functionName << ": "; if (index == 0) { errmsg << "parameter positions start at 1, not 0"; } else { errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given"; } reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, Certainty::normal); } void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a \'"; if (specifier[0] == 's') errmsg << "char"; else if (specifier[0] == 'S') errmsg << "wchar_t"; errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidScanfArgType_s", errmsg.str(), CWE686, Certainty::normal); } void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; if (specifier[0] == 'h') { if (specifier[1] == 'h') errmsg << (isUnsigned ? "unsigned " : "") << "char"; else errmsg << (isUnsigned ? "unsigned " : "") << "short"; } else if (specifier[0] == 'l') { if (specifier[1] == 'l') errmsg << (isUnsigned ? "unsigned " : "") << "long long"; else errmsg << (isUnsigned ? "unsigned " : "") << "long"; } else if (specifier.find("I32") != std::string::npos) { errmsg << (isUnsigned ? "unsigned " : "") << "__int32"; } else if (specifier.find("I64") != std::string::npos) { errmsg << (isUnsigned ? "unsigned " : "") << "__int64"; } else if (specifier[0] == 'I') { errmsg << (isUnsigned ? "size_t" : "ptrdiff_t"); } else if (specifier[0] == 'j') { if (isUnsigned) errmsg << "uintmax_t"; else errmsg << "intmax_t"; } else if (specifier[0] == 'z') { if (specifier[1] == 'd' || specifier[1] == 'i') errmsg << "ssize_t"; else errmsg << "size_t"; } else if (specifier[0] == 't') { errmsg << (isUnsigned ? "unsigned " : "") << "ptrdiff_t"; } else if (specifier[0] == 'L') { errmsg << (isUnsigned ? "unsigned " : "") << "long long"; } else { errmsg << (isUnsigned ? "unsigned " : "") << "int"; } errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidScanfArgType_int", errmsg.str(), CWE686, Certainty::normal); } void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; if (specifier[0] == 'l' && specifier[1] != 'l') errmsg << "double"; else if (specifier[0] == 'L') errmsg << "long double"; else errmsg << "float"; errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidScanfArgType_float", errmsg.str(), CWE686, Certainty::normal); } void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_s", errmsg.str(), CWE686, Certainty::normal); } void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_n", errmsg.str(), CWE686, Certainty::normal); } void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_p", errmsg.str(), CWE686, Certainty::normal); } static void printfFormatType(std::ostream& os, const std::string& specifier, bool isUnsigned) { os << "\'"; if (specifier[0] == 'l') { if (specifier[1] == 'l') os << (isUnsigned ? "unsigned " : "") << "long long"; else os << (isUnsigned ? "unsigned " : "") << "long"; } else if (specifier[0] == 'h') { if (specifier[1] == 'h') os << (isUnsigned ? "unsigned " : "") << "char"; else os << (isUnsigned ? "unsigned " : "") << "short"; } else if (specifier.find("I32") != std::string::npos) { os << (isUnsigned ? "unsigned " : "") << "__int32"; } else if (specifier.find("I64") != std::string::npos) { os << (isUnsigned ? "unsigned " : "") << "__int64"; } else if (specifier[0] == 'I') { os << (isUnsigned ? "size_t" : "ptrdiff_t"); } else if (specifier[0] == 'j') { if (isUnsigned) os << "uintmax_t"; else os << "intmax_t"; } else if (specifier[0] == 'z') { if (specifier[1] == 'd' || specifier[1] == 'i') os << "ssize_t"; else os << "size_t"; } else if (specifier[0] == 't') { os << (isUnsigned ? "unsigned " : "") << "ptrdiff_t"; } else if (specifier[0] == 'L') { os << (isUnsigned ? "unsigned " : "") << "long long"; } else { os << (isUnsigned ? "unsigned " : "") << "int"; } os << "\'"; } void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires "; printfFormatType(errmsg, specifier, true); errmsg << " but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_uint", errmsg.str(), CWE686, Certainty::normal); } void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires "; printfFormatType(errmsg, specifier, false); errmsg << " but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_sint", errmsg.str(), CWE686, Certainty::normal); } void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; if (specifier[0] == 'L') errmsg << "long "; errmsg << "double\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_float", errmsg.str(), CWE686, Certainty::normal); } Severity::SeverityType CheckIO::getSeverity(const CheckIO::ArgumentInfo *argInfo) { return (argInfo && argInfo->typeToken && !argInfo->typeToken->originalName().empty()) ? Severity::portability : Severity::warning; } void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) { if (argInfo) { os << "\'"; const Token *type = argInfo->typeToken; if (type->tokType() == Token::eString) { if (type->isLong()) os << "const wchar_t *"; else os << "const char *"; } else { if (type->originalName().empty()) { if (type->strAt(-1) == "const") os << "const "; while (Token::Match(type, "const|struct")) { os << type->str() << " "; type = type->next(); } while (Token::Match(type, "%any% ::")) { os << type->str() << "::"; type = type->tokAt(2); } os << type->stringify(false, true, false); if (type->strAt(1) == "*" && !argInfo->element) os << " *"; else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray()) os << " *"; else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray()) os << " *"; if (argInfo->address) os << " *"; } else { if (type->isUnsigned()) { if (type->originalName() == "__int64" || type->originalName() == "__int32" || type->originalName() == "ptrdiff_t") os << "unsigned "; } os << type->originalName(); if (type->strAt(1) == "*" || argInfo->address) os << " *"; os << " {aka " << type->stringify(false, true, false); if (type->strAt(1) == "*" || argInfo->address) os << " *"; os << "}"; } } os << "\'"; } else os << "Unknown"; } void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier) { if (!mSettings->severity.isEnabled(Severity::warning)) return; std::ostringstream errmsg; errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier."; reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str(), CWE704, Certainty::normal); } void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier) { MathLib::bigint arrlen = 0; std::string varname; if (var) { arrlen = var->dimension(0); varname = var->name(); } std::ostringstream errmsg; if (arrlen > width) { if (tok != nullptr && (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning))) return; errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer" << " '" << varname << "[" << arrlen << "]'."; reportError(tok, Severity::warning, "invalidScanfFormatWidth_smaller", errmsg.str(), CWE(0U), Certainty::inconclusive); } else { errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '" << varname << "[" << arrlen << "]', use %" << (specifier == "c" ? arrlen : (arrlen - 1)) << specifier << " to prevent overflowing it."; reportError(tok, Severity::error, "invalidScanfFormatWidth", errmsg.str(), CWE687, Certainty::normal); } } cppcheck-2.7/lib/checkio.h000066400000000000000000000207211417746362400155060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkioH #define checkioH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errortypes.h" #include "utils.h" #include #include class Function; class Settings; class Token; class Tokenizer; class Variable; class ErrorLogger; /// @addtogroup Checks /// @{ /** @brief %Check input output operations. */ class CPPCHECKLIB CheckIO : public Check { public: /** @brief This constructor is used when registering CheckIO */ CheckIO() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckIO(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks on the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckIO checkIO(tokenizer, settings, errorLogger); checkIO.checkWrongPrintfScanfArguments(); checkIO.checkCoutCerrMisusage(); checkIO.checkFileUsage(); checkIO.invalidScanf(); } /** @brief %Check for missusage of std::cout */ void checkCoutCerrMisusage(); /** @brief %Check usage of files*/ void checkFileUsage(); /** @brief scanf can crash if width specifiers are not used */ void invalidScanf(); /** @brief %Checks type and number of arguments given to functions like printf or scanf*/ void checkWrongPrintfScanfArguments(); private: class ArgumentInfo { public: ArgumentInfo(const Token *arg, const Settings *settings, bool _isCPP); ~ArgumentInfo(); bool isArrayOrPointer() const; bool isComplexType() const; bool isKnownType() const; bool isStdVectorOrString(); bool isStdContainer(const Token *tok); bool isLibraryType(const Settings *settings) const; const Variable *variableInfo; const Token *typeToken; const Function *functionInfo; Token *tempToken; bool element; bool _template; bool address; bool isCPP; private: ArgumentInfo(const ArgumentInfo &); // not implemented ArgumentInfo operator = (const ArgumentInfo &); // not implemented }; void checkFormatString(const Token * const tok, const Token * const formatStringTok, const Token * argListTok, const bool scan, const bool scanf_s); // Reporting errors.. void coutCerrMisusageError(const Token* tok, const std::string& streamName); void fflushOnInputStreamError(const Token *tok, const std::string &varname); void ioWithoutPositioningError(const Token *tok); void readWriteOnlyFileError(const Token *tok); void writeReadOnlyFileError(const Token *tok); void useClosedFileError(const Token *tok); void seekOnAppendedFileError(const Token *tok); void incompatibleFileOpenError(const Token *tok, const std::string &filename); void invalidScanfError(const Token *tok); void wrongPrintfScanfArgumentsError(const Token* tok, const std::string &functionName, nonneg int numFormat, nonneg int numFunction); void wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, nonneg int index, nonneg int numFunction); void invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned); void invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier); void invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier); static void argumentType(std::ostream & os, const ArgumentInfo * argInfo); static Severity::SeverityType getSeverity(const ArgumentInfo *argInfo); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckIO c(nullptr, settings, errorLogger); c.coutCerrMisusageError(nullptr, "cout"); c.fflushOnInputStreamError(nullptr, "stdin"); c.ioWithoutPositioningError(nullptr); c.readWriteOnlyFileError(nullptr); c.writeReadOnlyFileError(nullptr); c.useClosedFileError(nullptr); c.seekOnAppendedFileError(nullptr); c.incompatibleFileOpenError(nullptr, "tmp"); c.invalidScanfError(nullptr); c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2); c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr); c.invalidScanfArgTypeError_int(nullptr, 1, "d", nullptr, false); c.invalidScanfArgTypeError_float(nullptr, 1, "f", nullptr); c.invalidPrintfArgTypeError_s(nullptr, 1, nullptr); c.invalidPrintfArgTypeError_n(nullptr, 1, nullptr); c.invalidPrintfArgTypeError_p(nullptr, 1, nullptr); c.invalidPrintfArgTypeError_uint(nullptr, 1, "u", nullptr); c.invalidPrintfArgTypeError_sint(nullptr, 1, "i", nullptr); c.invalidPrintfArgTypeError_float(nullptr, 1, "f", nullptr); c.invalidLengthModifierError(nullptr, 1, "I"); c.invalidScanfFormatWidthError(nullptr, 10, 5, nullptr, "s"); c.invalidScanfFormatWidthError(nullptr, 99, -1, nullptr, "s"); c.wrongPrintfScanfPosixParameterPositionError(nullptr, "printf", 2, 1); } static std::string myName() { return "IO using format string"; } std::string classInfo() const OVERRIDE { return "Check format string input/output operations.\n" "- Bad usage of the function 'sprintf' (overlapping data)\n" "- Missing or wrong width specifiers in 'scanf' format string\n" "- Use a file that has been closed\n" "- File input/output without positioning results in undefined behaviour\n" "- Read to a file that has only been opened for writing (or vice versa)\n" "- Repositioning operation on a file opened in append mode\n" "- The same file can't be open for read and write at the same time on different streams\n" "- Using fflush() on an input stream\n" "- Invalid usage of output stream. For example: 'std::cout << std::cout;'\n" "- Wrong number of arguments given to 'printf' or 'scanf;'\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkioH cppcheck-2.7/lib/checkleakautovar.cpp000066400000000000000000001271051417746362400177540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Leaks when using auto variables //--------------------------------------------------------------------------- #include "checkleakautovar.h" #include "astutils.h" #include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak #include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef #include "mathlib.h" #include "settings.h" #include "errortypes.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckLeakAutoVar instance; } static const CWE CWE672(672U); static const CWE CWE415(415U); // Hardcoded allocation types (not from library) static const int NEW_ARRAY = -2; static const int NEW = -1; static const std::vector> alloc_failed_conds {{"==", "0"}, {"<", "0"}, {"==", "-1"}, {"<=", "-1"}}; static const std::vector> alloc_success_conds {{"!=", "0"}, {">", "0"}, {"!=", "-1"}, {">=", "0"}}; /** * @brief Is variable type some class with automatic deallocation? * @param var variable token * @return true unless it can be seen there is no automatic deallocation */ static bool isAutoDealloc(const Variable *var) { if (var->valueType() && var->valueType()->type != ValueType::Type::RECORD && var->valueType()->type != ValueType::Type::UNKNOWN_TYPE) return false; // return false if the type is a simple record type without side effects // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check base class for side effects */ /** @todo false negative: check constructors for side effects */ if (var->typeScope() && var->typeScope()->numConstructors == 0 && (var->typeScope()->varlist.empty() || var->type()->needInitialization == Type::NeedInitialization::True) && var->type()->derivedFrom.empty()) return false; return true; } static bool isVarTokComparison(const Token * tok, const Token ** vartok, const std::vector>& ops) { for (const auto & op : ops) { if (astIsVariableComparison(tok, op.first, op.second, vartok)) return true; } return false; } //--------------------------------------------------------------------------- void VarInfo::print() { std::cout << "size=" << alloctype.size() << std::endl; for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) { std::string strusage; const std::map::const_iterator use = possibleUsage.find(it->first); if (use != possibleUsage.end()) strusage = use->second; std::string status; switch (it->second.status) { case OWNED: status = "owned"; break; case DEALLOC: status = "dealloc"; break; case ALLOC: status = "alloc"; break; case NOALLOC: status = "noalloc"; break; case REALLOC: status = "realloc"; break; default: status = "?"; break; } std::cout << "status=" << status << " " << "alloctype='" << it->second.type << "' " << "possibleUsage='" << strusage << "' " << "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " " << "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " " << "reallocedFrom=" << it->second.reallocedFromType << std::endl; } } void VarInfo::possibleUsageAll(const std::string &functionName) { possibleUsage.clear(); for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) possibleUsage[it->first] = functionName; } void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type) { const CheckMemoryLeak checkmemleak(mTokenizer, mErrorLogger, mSettings); if (Library::isresource(type)) checkmemleak.resourceLeakError(tok, varname); else checkmemleak.memleakError(tok, varname); } void CheckLeakAutoVar::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) { const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); const std::list callstack = { allocTok, deallocTok }; c.mismatchAllocDealloc(callstack, varname); } void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname) { const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); c.deallocuseError(tok, varname); } void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname) { const std::list locations = { deallocTok, tok }; reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, Certainty::normal); } void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName) { if (mSettings->checkLibrary && mSettings->severity.isEnabled(Severity::information)) { reportError(tok, Severity::information, "checkLibraryUseIgnore", "--check-library: Function " + functionName + "() should have / configuration"); } } void CheckLeakAutoVar::doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type) { const std::list locations = { prevFreeTok, tok }; if (Library::isresource(type)) reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, Certainty::normal); else reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, Certainty::normal); } void CheckLeakAutoVar::check() { if (mSettings->clang) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Local variables that are known to be non-zero. const std::set notzero; // Check function scopes for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->hasInlineOrLambdaFunction()) continue; // Empty variable info VarInfo varInfo; checkScope(scope->bodyStart, &varInfo, notzero, 0); } } static bool isVarUsedInTree(const Token *tok, nonneg int varid) { if (!tok) return false; if (tok->varId() == varid) return true; if (tok->str() == "(" && Token::simpleMatch(tok->astOperand1(), "sizeof")) return false; return isVarUsedInTree(tok->astOperand1(), varid) || isVarUsedInTree(tok->astOperand2(), varid); } static bool isPointerReleased(const Token *startToken, const Token *endToken, nonneg int varid) { for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { if (tok->varId() != varid) continue; if (Token::Match(tok, "%var% . release ( )")) return true; if (Token::Match(tok, "%var% =")) return false; } return false; } static bool isLocalVarNoAutoDealloc(const Token *varTok, const bool isCpp) { // not a local variable nor argument? const Variable *var = varTok->variable(); if (!var) return true; if (!var->isArgument() && (!var->isLocal() || var->isStatic())) return false; // Don't check reference variables if (var->isReference()) return false; // non-pod variable if (isCpp) { // Possibly automatically deallocated memory if (isAutoDealloc(var) && Token::Match(varTok, "%var% = new")) return false; if (!var->isPointer() && !var->typeStartToken()->isStandardType()) return false; } return true; } /** checks if nameToken is a name of a function in a function call: * func(arg) * or * func(arg) * @param nameToken Function name token * @return opening parenthesis token or NULL if not a function call */ static const Token * isFunctionCall(const Token * nameToken) { if (nameToken->isName()) { nameToken = nameToken->next(); // check if function is a template if (nameToken && nameToken->link() && nameToken->str() == "<") { // skip template arguments nameToken = nameToken->link()->next(); } // check for '(' if (nameToken && nameToken->link() && nameToken->str() == "(") { // returning opening parenthesis pointer return nameToken; } } return nullptr; } void CheckLeakAutoVar::checkScope(const Token * const startToken, VarInfo *varInfo, std::set notzero, nonneg int recursiveCount) { #if ASAN static const nonneg int recursiveLimit = 300; #else static const nonneg int recursiveLimit = 1000; #endif if (++recursiveCount > recursiveLimit) // maximum number of "else if ()" throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached.", InternalError::LIMIT); std::map &alloctype = varInfo->alloctype; std::map &possibleUsage = varInfo->possibleUsage; const std::set conditionalAlloc(varInfo->conditionalAlloc); // Parse all tokens const Token * const endToken = startToken->link(); for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { if (!tok->scope()->isExecutable()) { tok = tok->scope()->bodyEnd; if (!tok) // Ticket #6666 (crash upon invalid code) break; } // check each token { const Token * nextTok = checkTokenInsideExpression(tok, varInfo); if (nextTok) { tok = nextTok; continue; } } // look for end of statement if (!Token::Match(tok, "[;{},]") || Token::Match(tok->next(), "[;{},]")) continue; tok = tok->next(); if (!tok || tok == endToken) break; if (Token::Match(tok, "const %type%")) tok = tok->tokAt(2); // parse statement, skip to last member const Token *varTok = tok; while (Token::Match(varTok, "%name% ::|. %name% !!(")) varTok = varTok->tokAt(2); const Token *ftok = tok; if (ftok->str() == "::") ftok = ftok->next(); while (Token::Match(ftok, "%name% :: %name%")) ftok = ftok->tokAt(2); // assignment.. if (Token::Match(varTok, "%var% =")) { const Token* const tokAssignOp = varTok->next(); // taking address of another variable.. if (Token::Match(tokAssignOp, "= %var% [+;]")) { if (varTok->tokAt(2)->varId() != varTok->varId()) { // If variable points at allocated memory => error leakIfAllocated(varTok, *varInfo); // no multivariable checking currently => bail out for rhs variables for (const Token *tok2 = varTok; tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { break; } if (tok2->varId()) { varInfo->erase(tok2->varId()); } } } } // right ast part (after `=` operator) const Token* tokRightAstOperand = tokAssignOp->astOperand2(); while (tokRightAstOperand && tokRightAstOperand->isCast()) tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1(); // is variable used in rhs? if (isVarUsedInTree(tokRightAstOperand, varTok->varId())) continue; // Variable has already been allocated => error if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end()) leakIfAllocated(varTok, *varInfo); varInfo->erase(varTok->varId()); if (!isLocalVarNoAutoDealloc(varTok, mTokenizer->isCPP())) continue; // allocation? const Token *const fTok = tokRightAstOperand ? tokRightAstOperand->previous() : nullptr; if (Token::Match(fTok, "%type% (")) { const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(fTok); if (f && f->arg == -1) { VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; varAlloc.type = f->groupId; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = fTok; } changeAllocStatusIfRealloc(alloctype, fTok, varTok); } else if (mTokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) { const Token* tok2 = varTok->tokAt(2)->astOperand1(); const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; varAlloc.type = arrayNew ? NEW_ARRAY : NEW; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = varTok->tokAt(2); } // Assigning non-zero value variable. It might be used to // track the execution for a later if condition. if (Token::Match(varTok->tokAt(2), "%num% ;") && MathLib::toLongNumber(varTok->strAt(2)) != 0) notzero.insert(varTok->varId()); else if (Token::Match(varTok->tokAt(2), "- %type% ;") && varTok->tokAt(3)->isUpperCaseName()) notzero.insert(varTok->varId()); else notzero.erase(varTok->varId()); } // if/else else if (Token::simpleMatch(tok, "if (")) { // Parse function calls inside the condition const Token * closingParenthesis = tok->linkAt(1); for (const Token *innerTok = tok->tokAt(2); innerTok && innerTok != closingParenthesis; innerTok = innerTok->next()) { // TODO: replace with checkTokenInsideExpression() if (!isLocalVarNoAutoDealloc(innerTok, mTokenizer->isCPP())) continue; if (Token::Match(innerTok, "%var% =") && innerTok->astParent() == innerTok->next()) { // allocation? // right ast part (after `=` operator) const Token* tokRightAstOperand = innerTok->next()->astOperand2(); while (tokRightAstOperand && tokRightAstOperand->isCast()) tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1(); if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) { const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(tokRightAstOperand->previous()); if (f && f->arg == -1) { VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; varAlloc.type = f->groupId; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = tokRightAstOperand->previous(); } else { // Fixme: warn about leak alloctype.erase(innerTok->varId()); } changeAllocStatusIfRealloc(alloctype, innerTok->tokAt(2), varTok); } else if (mTokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) { const Token* tok2 = innerTok->tokAt(2)->astOperand1(); const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; varAlloc.type = arrayNew ? NEW_ARRAY : NEW; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = innerTok->tokAt(2); } } // check for function call const Token * const openingPar = isFunctionCall(innerTok); if (openingPar) { // innerTok is a function name const VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC); functionCall(innerTok, openingPar, varInfo, allocation, nullptr); innerTok = openingPar->link(); } } if (Token::simpleMatch(closingParenthesis, ") {")) { VarInfo varInfo1(*varInfo); // VarInfo for if code VarInfo varInfo2(*varInfo); // VarInfo for else code // Skip expressions before commas const Token * astOperand2AfterCommas = tok->next()->astOperand2(); while (Token::simpleMatch(astOperand2AfterCommas, ",")) astOperand2AfterCommas = astOperand2AfterCommas->astOperand2(); // Recursively scan variable comparisons in condition visitAstNodes(astOperand2AfterCommas, [&](const Token *tok3) { if (!tok3) return ChildrenToVisit::none; if (tok3->str() == "&&" || tok3->str() == "||") { // FIXME: handle && ! || better return ChildrenToVisit::op1_and_op2; } if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) { return ChildrenToVisit::op2; } else if (tok3->str() == "(" && Token::Match(tok3->previous(), "%name%")) { const std::vector params = getArguments(tok3->previous()); for (const Token *par : params) { if (!par->isComparisonOp()) continue; const Token *vartok = nullptr; if (isVarTokComparison(par, &vartok, alloc_success_conds) || (isVarTokComparison(par, &vartok, alloc_failed_conds))) { varInfo1.erase(vartok->varId()); varInfo2.erase(vartok->varId()); } } return ChildrenToVisit::none; } const Token *vartok = nullptr; if (isVarTokComparison(tok3, &vartok, alloc_success_conds)) { varInfo2.reallocToAlloc(vartok->varId()); varInfo2.erase(vartok->varId()); if (astIsVariableComparison(tok3, "!=", "0", &vartok) && (notzero.find(vartok->varId()) != notzero.end())) varInfo2.clear(); } else if (isVarTokComparison(tok3, &vartok, alloc_failed_conds)) { varInfo1.reallocToAlloc(vartok->varId()); varInfo1.erase(vartok->varId()); } return ChildrenToVisit::none; }); checkScope(closingParenthesis->next(), &varInfo1, notzero, recursiveCount); closingParenthesis = closingParenthesis->linkAt(1); if (Token::simpleMatch(closingParenthesis, "} else {")) { checkScope(closingParenthesis->tokAt(2), &varInfo2, notzero, recursiveCount); tok = closingParenthesis->linkAt(2)->previous(); } else { tok = closingParenthesis->previous(); } VarInfo old; old.swap(*varInfo); std::map::const_iterator it; for (it = old.alloctype.begin(); it != old.alloctype.end(); ++it) { const int varId = it->first; if (old.conditionalAlloc.find(varId) == old.conditionalAlloc.end()) continue; if (varInfo1.alloctype.find(varId) == varInfo1.alloctype.end() || varInfo2.alloctype.find(varId) == varInfo2.alloctype.end()) { varInfo1.erase(varId); varInfo2.erase(varId); } } // Conditional allocation in varInfo1 for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) { if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() && old.alloctype.find(it->first) == old.alloctype.end()) { varInfo->conditionalAlloc.insert(it->first); } } // Conditional allocation in varInfo2 for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) { if (varInfo1.alloctype.find(it->first) == varInfo1.alloctype.end() && old.alloctype.find(it->first) == old.alloctype.end()) { varInfo->conditionalAlloc.insert(it->first); } } // Conditional allocation/deallocation for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) { if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) { varInfo->conditionalAlloc.erase(it->first); varInfo2.erase(it->first); } } for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) { if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) { varInfo->conditionalAlloc.erase(it->first); varInfo1.erase(it->first); } } alloctype.insert(varInfo1.alloctype.begin(), varInfo1.alloctype.end()); alloctype.insert(varInfo2.alloctype.begin(), varInfo2.alloctype.end()); possibleUsage.insert(varInfo1.possibleUsage.begin(), varInfo1.possibleUsage.end()); possibleUsage.insert(varInfo2.possibleUsage.begin(), varInfo2.possibleUsage.end()); } } // unknown control.. (TODO: handle loops) else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::simpleMatch(tok, "do {")) { varInfo->clear(); break; } // return else if (tok->str() == "return") { ret(tok, *varInfo); varInfo->clear(); } // throw else if (mTokenizer->isCPP() && tok->str() == "throw") { bool tryFound = false; const Scope* scope = tok->scope(); while (scope && scope->isExecutable()) { if (scope->type == Scope::eTry) tryFound = true; scope = scope->nestedIn; } // If the execution leaves the function then treat it as return if (!tryFound) ret(tok, *varInfo); varInfo->clear(); } // delete else if (mTokenizer->isCPP() && tok->str() == "delete") { const Token * delTok = tok; const bool arrayDelete = Token::simpleMatch(tok->next(), "[ ]"); if (arrayDelete) tok = tok->tokAt(3); else tok = tok->next(); if (tok->str() == "(") tok = tok->next(); while (Token::Match(tok, "%name% ::|.")) tok = tok->tokAt(2); const bool isnull = tok->hasKnownIntValue() && tok->values().front().intvalue == 0; if (!isnull && tok->varId() && tok->strAt(1) != "[") { const VarInfo::AllocInfo allocation(arrayDelete ? NEW_ARRAY : NEW, VarInfo::DEALLOC, delTok); changeAllocStatus(varInfo, allocation, tok, tok); } } // Function call.. else if (isFunctionCall(ftok)) { const Token * openingPar = isFunctionCall(ftok); const Library::AllocFunc* af = mSettings->library.getDeallocFuncInfo(ftok); VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC, ftok); if (allocation.type == 0) allocation.status = VarInfo::NOALLOC; functionCall(ftok, openingPar, varInfo, allocation, af); tok = ftok->next()->link(); // Handle scopes that might be noreturn if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) { const std::string &functionName(tok->link()->previous()->str()); bool unknown = false; if (mTokenizer->isScopeNoReturn(tok->tokAt(2), &unknown)) { if (!unknown) varInfo->clear(); else if (!mSettings->library.isLeakIgnore(functionName) && !mSettings->library.isUse(functionName)) varInfo->possibleUsageAll(functionName); } } continue; } // goto => weird execution path else if (tok->str() == "goto") { varInfo->clear(); } // continue/break else if (Token::Match(tok, "continue|break ;")) { varInfo->clear(); } // Check smart pointer else if (Token::Match(ftok, "%name% <") && mSettings->library.isSmartPointer(tok)) { const Token * typeEndTok = ftok->linkAt(1); if (!Token::Match(typeEndTok, "> %var% {|( %var% ,|)|}")) continue; tok = typeEndTok->linkAt(2); const int varid = typeEndTok->next()->varId(); if (isPointerReleased(typeEndTok->tokAt(2), endToken, varid)) continue; bool arrayDelete = false; if (Token::findsimplematch(ftok->next(), "[ ]", typeEndTok)) arrayDelete = true; // Check deleter const Token * deleterToken = nullptr; const Token * endDeleterToken = nullptr; const Library::AllocFunc* af = nullptr; if (Token::Match(ftok, "unique_ptr < %type% ,")) { deleterToken = ftok->tokAt(4); endDeleterToken = typeEndTok; } else if (Token::Match(typeEndTok, "> %var% {|( %var% ,")) { deleterToken = typeEndTok->tokAt(5); endDeleterToken = typeEndTok->linkAt(2); } if (deleterToken) { // Skip the decaying plus in expressions like +[](T*){} if (deleterToken->str() == "+") { deleterToken = deleterToken->next(); } // Check if its a pointer to a function const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken); if (dtok) { af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1)); } else { const Token * tscopeStart = nullptr; const Token * tscopeEnd = nullptr; // If the deleter is a lambda, check if it calls the dealloc function if (deleterToken->str() == "[" && Token::simpleMatch(deleterToken->link(), "] (") && // TODO: Check for mutable keyword Token::simpleMatch(deleterToken->link()->linkAt(1), ") {")) { tscopeStart = deleterToken->link()->linkAt(1)->tokAt(1); tscopeEnd = tscopeStart->link(); // If the deleter is a class, check if class calls the dealloc function } else if ((dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken)) && dtok->type()) { const Scope * tscope = dtok->type()->classScope; if (tscope) { tscopeStart = tscope->bodyStart; tscopeEnd = tscope->bodyEnd; } } if (tscopeStart && tscopeEnd) { for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) { af = mSettings->library.getDeallocFuncInfo(tok2); if (af) break; } } } } const Token * vtok = typeEndTok->tokAt(3); const VarInfo::AllocInfo allocation(af ? af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, ftok); changeAllocStatus(varInfo, allocation, vtok, vtok); } } ret(endToken, *varInfo, true); } const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const tok, VarInfo *varInfo) { // Deallocation and then dereferencing pointer.. if (tok->varId() > 0) { // TODO : Write a separate checker for this that uses valueFlowForward. const std::map::const_iterator var = varInfo->alloctype.find(tok->varId()); if (var != varInfo->alloctype.end()) { bool unknown = false; if (var->second.status == VarInfo::DEALLOC && CheckNullPointer::isPointerDeRef(tok, unknown, mSettings) && !unknown) { deallocUseError(tok, tok->str()); } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) { varInfo->erase(tok->varId()); } else { // check if tok is assigned into another variable const Token *rhs = tok; while (rhs->astParent()) { if (rhs->astParent()->str() == "=") break; rhs = rhs->astParent(); } while (rhs->isCast()) { rhs = rhs->astOperand1(); } if (rhs->varId() == tok->varId()) { // simple assignment varInfo->erase(tok->varId()); } else if (rhs->str() == "(" && mSettings->library.returnValue(rhs->astOperand1()) != emptyString) { // #9298, assignment through return value of a function const std::string &returnValue = mSettings->library.returnValue(rhs->astOperand1()); if (returnValue.compare(0, 3, "arg") == 0) { int argn; const Token *func = getTokenArgumentFunction(tok, argn); if (func) { const std::string arg = "arg" + std::to_string(argn + 1); if (returnValue == arg) { varInfo->erase(tok->varId()); } } } } } } else if (Token::Match(tok->previous(), "& %name% = %var% ;")) { varInfo->referenced.insert(tok->tokAt(2)->varId()); } } // check for function call const Token * const openingPar = isFunctionCall(tok); if (openingPar) { const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(tok); VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC, tok); if (alloc.type == 0) alloc.status = VarInfo::NOALLOC; functionCall(tok, openingPar, varInfo, alloc, nullptr); const std::string &returnValue = mSettings->library.returnValue(tok); if (returnValue.compare(0, 3, "arg") == 0) // the function returns one of its argument, we need to process a potential assignment return openingPar; return openingPar->link(); } return nullptr; } void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) { const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(fTok); if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) { const Token* argTok = getArguments(fTok).at(f->reallocArg - 1); if (alloctype.find(argTok->varId()) != alloctype.end()) { VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()]; if (argAlloc.type != 0 && argAlloc.type != f->groupId) mismatchError(fTok, argAlloc.allocTok, argTok->str()); argAlloc.status = VarInfo::REALLOC; argAlloc.allocTok = fTok; } VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()]; retAlloc.type = f->groupId; retAlloc.status = VarInfo::ALLOC; retAlloc.allocTok = fTok; retAlloc.reallocedFromType = argTok->varId(); } } void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg) { std::map &alloctype = varInfo->alloctype; const std::map::iterator var = alloctype.find(arg->varId()); if (var != alloctype.end()) { if (allocation.status == VarInfo::NOALLOC) { // possible usage varInfo->possibleUsage[arg->varId()] = tok->str(); if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&") varInfo->erase(arg->varId()); } else if (var->second.managed()) { doubleFreeError(tok, var->second.allocTok, arg->str(), allocation.type); var->second.status = allocation.status; } else if (var->second.type != allocation.type && var->second.type != 0) { // mismatching allocation and deallocation mismatchError(tok, var->second.allocTok, arg->str()); varInfo->erase(arg->varId()); } else { // deallocation var->second.status = allocation.status; var->second.type = allocation.type; var->second.allocTok = allocation.allocTok; } } else if (allocation.status != VarInfo::NOALLOC && allocation.status != VarInfo::OWNED) { alloctype[arg->varId()].status = VarInfo::DEALLOC; alloctype[arg->varId()].allocTok = tok; } } void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af) { // Ignore function call? if (mSettings->library.isLeakIgnore(tokName->str())) return; if (mSettings->library.getReallocFuncInfo(tokName)) return; const Token * const tokFirstArg = tokOpeningPar->next(); if (!tokFirstArg || tokFirstArg->str() == ")") { // no arguments return; } int argNr = 1; for (const Token *funcArg = tokFirstArg; funcArg; funcArg = funcArg->nextArgument()) { const Token* arg = funcArg; if (mTokenizer->isCPP()) { int tokAdvance = 0; if (arg->str() == "new") tokAdvance = 1; else if (Token::simpleMatch(arg, "* new")) tokAdvance = 2; if (tokAdvance > 0) { arg = arg->tokAt(tokAdvance); if (Token::simpleMatch(arg, "( std :: nothrow )")) arg = arg->tokAt(5); } } // Skip casts while (arg && arg->isCast()) arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1(); const Token * const argTypeStartTok = arg; while (Token::Match(arg, "%name% .|:: %name%")) arg = arg->tokAt(2); if (Token::Match(arg, "%var% [-,)] !!.") || Token::Match(arg, "& %var%")) { // goto variable if (arg->str() == "&") arg = arg->next(); const bool isnull = arg->hasKnownIntValue() && arg->values().front().intvalue == 0; // Is variable allocated? if (!isnull && (!af || af->arg == argNr)) changeAllocStatus(varInfo, allocation, tokName, arg); } // Check smart pointer else if (Token::Match(arg, "%name% < %type%") && mSettings->library.isSmartPointer(argTypeStartTok)) { const Token * typeEndTok = arg->linkAt(1); const Token * allocTok = nullptr; if (!Token::Match(typeEndTok, "> {|( %var% ,|)|}")) continue; bool arrayDelete = false; if (Token::findsimplematch(arg->next(), "[ ]", typeEndTok)) arrayDelete = true; // Check deleter const Token * deleterToken = nullptr; const Token * endDeleterToken = nullptr; const Library::AllocFunc* sp_af = nullptr; if (Token::Match(arg, "unique_ptr < %type% ,")) { deleterToken = arg->tokAt(4); endDeleterToken = typeEndTok; } else if (Token::Match(typeEndTok, "> {|( %var% ,")) { deleterToken = typeEndTok->tokAt(4); endDeleterToken = typeEndTok->linkAt(1); } if (deleterToken) { // Check if its a pointer to a function const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken); if (dtok) { sp_af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1)); } else { // If the deleter is a class, check if class calls the dealloc function dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken); if (dtok && dtok->type()) { const Scope * tscope = dtok->type()->classScope; for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) { sp_af = mSettings->library.getDeallocFuncInfo(tok2); if (sp_af) { allocTok = tok2; break; } } } } } const Token * vtok = typeEndTok->tokAt(2); const VarInfo::AllocInfo sp_allocation(sp_af ? sp_af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, allocTok); changeAllocStatus(varInfo, sp_allocation, vtok, vtok); } else { checkTokenInsideExpression(arg, varInfo); } // TODO: check each token in argument expression (could contain multiple variables) argNr++; } } void CheckLeakAutoVar::leakIfAllocated(const Token *vartok, const VarInfo &varInfo) { const std::map &alloctype = varInfo.alloctype; const std::map &possibleUsage = varInfo.possibleUsage; const std::map::const_iterator var = alloctype.find(vartok->varId()); if (var != alloctype.end() && var->second.status == VarInfo::ALLOC) { const std::map::const_iterator use = possibleUsage.find(vartok->varId()); if (use == possibleUsage.end()) { leakError(vartok, vartok->str(), var->second.type); } else { configurationInfo(vartok, use->second); } } } void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope) { const std::map &alloctype = varInfo.alloctype; const std::map &possibleUsage = varInfo.possibleUsage; std::vector toRemove; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) { // don't warn if variable is conditionally allocated, unless it leaves the scope if (!isEndOfScope && !it->second.managed() && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end()) continue; // don't warn if there is a reference of the variable if (varInfo.referenced.find(it->first) != varInfo.referenced.end()) continue; const int varid = it->first; const Variable *var = symbolDatabase->getVariableFromVarId(varid); if (var) { // don't warn if we leave an inner scope if (isEndOfScope && var->scope() && tok != var->scope()->bodyEnd) continue; bool used = false; for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == ";") break; if (!Token::Match(tok2, "return|(|{|,")) continue; const Token* tok3 = tok2->next(); while (tok3 && tok3->isCast() && tok3->valueType() && (tok3->valueType()->pointer || (tok3->valueType()->typeSize(*mSettings) == 0) || (tok3->valueType()->typeSize(*mSettings) >= mSettings->sizeof_pointer))) tok3 = tok3->astOperand2() ? tok3->astOperand2() : tok3->astOperand1(); if (Token::Match(tok3, "%varid%", varid)) tok2 = tok3->next(); else if (Token::Match(tok3, "& %varid% . %name%", varid)) tok2 = tok3->tokAt(4); else continue; if (Token::Match(tok2, "[});,]")) { used = true; break; } } // return deallocated pointer if (used && it->second.status == VarInfo::DEALLOC) deallocReturnError(tok, it->second.allocTok, var->name()); else if (!used && !it->second.managed()) { const std::map::const_iterator use = possibleUsage.find(varid); if (use == possibleUsage.end()) { leakError(tok, var->name(), it->second.type); } else { configurationInfo(tok, use->second); } } toRemove.push_back(varid); } } for (int varId : toRemove) varInfo.erase(varId); } cppcheck-2.7/lib/checkleakautovar.h000066400000000000000000000147001417746362400174150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkleakautovarH #define checkleakautovarH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "library.h" #include "utils.h" #include #include #include #include class ErrorLogger; class Settings; class Token; class Tokenizer; class CPPCHECKLIB VarInfo { public: enum AllocStatus { REALLOC = -3, OWNED = -2, DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; struct AllocInfo { AllocStatus status; /** Allocation type. If it is a positive value then it corresponds to * a Library allocation id. A negative value is a builtin * checkleakautovar allocation type. */ int type; int reallocedFromType = -1; const Token * allocTok; AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC, const Token* allocTok_ = nullptr) : status(status_), type(type_), allocTok(allocTok_) {} bool managed() const { return status < 0; } }; std::map alloctype; std::map possibleUsage; std::set conditionalAlloc; std::set referenced; void clear() { alloctype.clear(); possibleUsage.clear(); conditionalAlloc.clear(); referenced.clear(); } void erase(nonneg int varid) { alloctype.erase(varid); possibleUsage.erase(varid); conditionalAlloc.erase(varid); referenced.erase(varid); } void swap(VarInfo &other) { alloctype.swap(other.alloctype); possibleUsage.swap(other.possibleUsage); conditionalAlloc.swap(other.conditionalAlloc); referenced.swap(other.referenced); } void reallocToAlloc(nonneg int varid) { const AllocInfo& alloc = alloctype[varid]; if (alloc.reallocedFromType >= 0) { const std::map::iterator it = alloctype.find(alloc.reallocedFromType); if (it != alloctype.end() && it->second.status == REALLOC) { it->second.status = ALLOC; } } } /** set possible usage for all variables */ void possibleUsageAll(const std::string &functionName); void print(); }; /// @addtogroup Checks /// @{ /** * @brief Check for leaks */ class CPPCHECKLIB CheckLeakAutoVar : public Check { public: /** This constructor is used when registering the CheckLeakAutoVar */ CheckLeakAutoVar() : Check(myName()) {} /** This constructor is used when running checks. */ CheckLeakAutoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckLeakAutoVar checkLeakAutoVar(tokenizer, settings, errorLogger); checkLeakAutoVar.check(); } private: /** check for leaks in all scopes */ void check(); /** check for leaks in a function scope */ void checkScope(const Token * const startToken, VarInfo *varInfo, std::set notzero, nonneg int recursiveCount); /** Check token inside expression. * @param tok token inside expression. * @param varInfo Variable info * @return next token to process (if no other checks needed for this token). NULL if other checks could be performed. */ const Token * checkTokenInsideExpression(const Token * const tok, VarInfo *varInfo); /** parse function call */ void functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af); /** parse changes in allocation status */ void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg); /** update allocation status if reallocation function */ void changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok); /** return. either "return" or end of variable scope is seen */ void ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope = false); /** if variable is allocated then there is a leak */ void leakIfAllocated(const Token *vartok, const VarInfo &varInfo); void leakError(const Token* tok, const std::string &varname, int type); void mismatchError(const Token* deallocTok, const Token* allocTok, const std::string &varname); void deallocUseError(const Token *tok, const std::string &varname); void deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname); void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type); /** message: user configuration is needed to complete analysis */ void configurationInfo(const Token* tok, const std::string &functionName); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckLeakAutoVar c(nullptr, settings, errorLogger); c.deallocReturnError(nullptr, nullptr, "p"); c.configurationInfo(nullptr, "f"); // user configuration is needed to complete analysis c.doubleFreeError(nullptr, nullptr, "varname", 0); } static std::string myName() { return "Leaks (auto variables)"; } std::string classInfo() const OVERRIDE { return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkleakautovarH cppcheck-2.7/lib/checkmemoryleak.cpp000066400000000000000000001320261417746362400176010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkmemoryleak.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckMemoryLeakInFunction instance1; CheckMemoryLeakInClass instance2; CheckMemoryLeakStructMember instance3; CheckMemoryLeakNoVar instance4; } // CWE ID used: static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE401(401U); // Improper Release of Memory Before Removing Last Reference ('Memory Leak') static const CWE CWE771(771U); // Missing Reference to Active Allocated Resource static const CWE CWE772(772U); // Missing Release of Resource after Effective Lifetime /** List of functions that can be ignored when searching for memory leaks. * These functions don't take the address of the given pointer * This list contains function names with const parameters e.g.: atof(const char *) * TODO: This list should be replaced by in .cfg files. */ static const std::unordered_set call_func_white_list = { "_open", "_wopen", "access", "adjtime", "asctime_r", "asprintf", "chdir", "chmod", "chown" , "creat", "ctime_r", "execl", "execle", "execlp", "execv", "execve", "fchmod", "fcntl" , "fdatasync", "fclose", "flock", "fmemopen", "fnmatch", "fopen", "fopencookie", "for", "free" , "freopen", "fseeko", "fstat", "fsync", "ftello", "ftruncate", "getgrnam", "gethostbyaddr", "gethostbyname" , "getnetbyname", "getopt", "getopt_long", "getprotobyname", "getpwnam", "getservbyname", "getservbyport" , "glob", "gmtime", "gmtime_r", "if", "index", "inet_addr", "inet_aton", "inet_network", "initgroups" , "ioctl", "link", "localtime_r", "lockf", "lseek", "lstat", "mkdir", "mkfifo", "mknod", "mkstemp" , "obstack_printf", "obstack_vprintf", "open", "opendir", "parse_printf_format", "pathconf" , "perror", "popen", "posix_fadvise", "posix_fallocate", "pread", "psignal", "pwrite", "read", "readahead" , "readdir", "readdir_r", "readlink", "readv", "realloc", "regcomp", "return", "rewinddir", "rindex" , "rmdir", "scandir", "seekdir", "setbuffer", "sethostname", "setlinebuf", "sizeof", "strdup" , "stat", "stpcpy", "strcasecmp", "stricmp", "strncasecmp", "switch" , "symlink", "sync_file_range", "telldir", "tempnam", "time", "typeid", "unlink" , "utime", "utimes", "vasprintf", "while", "wordexp", "write", "writev" }; //--------------------------------------------------------------------------- CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack) const { // What we may have... // * var = (char *)malloc(10); // * var = new char[10]; // * var = strdup("hello"); // * var = strndup("hello", 3); if (tok2 && tok2->str() == "(") { tok2 = tok2->link(); tok2 = tok2 ? tok2->next() : nullptr; } if (!tok2) return No; if (tok2->str() == "::") tok2 = tok2->next(); if (!tok2->isName()) return No; if (!Token::Match(tok2, "%name% ::|. %type%")) { // Using realloc.. AllocType reallocType = getReallocationType(tok2, varid); if (reallocType != No) return reallocType; if (mTokenizer_->isCPP() && tok2->str() == "new") { if (tok2->strAt(1) == "(" && !Token::Match(tok2->next(),"( std| ::| nothrow )")) return No; if (tok2->astOperand1() && (tok2->astOperand1()->str() == "[" || (tok2->astOperand1()->astOperand1() && tok2->astOperand1()->astOperand1()->str() == "["))) return NewArray; const Token *typeTok = tok2->next(); while (Token::Match(typeTok, "%name% :: %name%")) typeTok = typeTok->tokAt(2); const Scope* classScope = nullptr; if (typeTok->type() && typeTok->type()->isClassType()) { classScope = typeTok->type()->classScope; } else if (typeTok->function() && typeTok->function()->isConstructor()) { classScope = typeTok->function()->nestedIn; } if (classScope && classScope->numConstructors > 0) return No; return New; } if (mSettings_->posix()) { if (Token::Match(tok2, "open|openat|creat|mkstemp|mkostemp|socket (")) { // simple sanity check of function parameters.. // TODO: Make such check for all these functions const int num = numberOfArguments(tok2); if (tok2->str() == "open" && num != 2 && num != 3) return No; // is there a user function with this name? if (tok2->function()) return No; return Fd; } if (Token::simpleMatch(tok2, "popen (")) return Pipe; } // Does tok2 point on a Library allocation function? const int alloctype = mSettings_->library.getAllocId(tok2, -1); if (alloctype > 0) { if (alloctype == mSettings_->library.deallocId("free")) return Malloc; if (alloctype == mSettings_->library.deallocId("fclose")) return File; return Library::ismemory(alloctype) ? OtherMem : OtherRes; } } while (Token::Match(tok2,"%name% ::|. %type%")) tok2 = tok2->tokAt(2); // User function const Function* func = tok2->function(); if (func == nullptr) return No; // Prevent recursion if (callstack && std::find(callstack->begin(), callstack->end(), func) != callstack->end()) return No; std::list cs; if (!callstack) callstack = &cs; callstack->push_back(func); return functionReturnType(func, callstack); } CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok2, nonneg int varid) const { // What we may have... // * var = (char *)realloc(..; if (tok2 && tok2->str() == "(") { tok2 = tok2->link(); tok2 = tok2 ? tok2->next() : nullptr; } if (!tok2) return No; if (!Token::Match(tok2, "%name% (")) return No; const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok2); if (!(f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok2))) return No; const auto args = getArguments(tok2); if (args.size() < (f->reallocArg)) return No; const Token* arg = args.at(f->reallocArg - 1); while (arg && arg->isCast()) arg = arg->astOperand1(); while (arg && arg->isUnaryOp("*")) arg = arg->astOperand1(); if (varid > 0 && !Token::Match(arg, "%varid% [,)]", varid)) return No; const int realloctype = mSettings_->library.getReallocId(tok2, -1); if (realloctype > 0) { if (realloctype == mSettings_->library.deallocId("free")) return Malloc; if (realloctype == mSettings_->library.deallocId("fclose")) return File; return Library::ismemory(realloctype) ? OtherMem : OtherRes; } return No; } CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, nonneg int varid) const { if (mTokenizer_->isCPP() && tok->str() == "delete" && tok->astOperand1()) { const Token* vartok = tok->astOperand1(); if (Token::Match(vartok, ".|::")) vartok = vartok->astOperand2(); if (vartok && vartok->varId() == varid) { if (tok->strAt(1) == "[") return NewArray; return New; } } if (tok->str() == "::") tok = tok->next(); if (Token::Match(tok, "%name% (")) { if (Token::simpleMatch(tok, "fcloseall ( )")) return File; int argNr = 1; for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->nextArgument()) { const Token* vartok = tok2; while (Token::Match(vartok, "%name% .|::")) vartok = vartok->tokAt(2); if (Token::Match(vartok, "%varid% )|,|-", varid)) { if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )")) return Malloc; if (mSettings_->posix()) { if (tok->str() == "close") return Fd; if (tok->str() == "pclose") return Pipe; } // Does tok point on a Library deallocation function? const int dealloctype = mSettings_->library.getDeallocId(tok, argNr); if (dealloctype > 0) { if (dealloctype == mSettings_->library.deallocId("free")) return Malloc; if (dealloctype == mSettings_->library.deallocId("fclose")) return File; return Library::ismemory(dealloctype) ? OtherMem : OtherRes; } } argNr++; } } return No; } bool CheckMemoryLeak::isReopenStandardStream(const Token *tok) const { if (getReallocationType(tok, 0) == File) { const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok); if (f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok)) { const Token* arg = getArguments(tok).at(f->reallocArg - 1); if (Token::Match(arg, "stdin|stdout|stderr")) return true; } } return false; } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- void CheckMemoryLeak::memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const { if (alloctype == CheckMemoryLeak::File || alloctype == CheckMemoryLeak::Pipe || alloctype == CheckMemoryLeak::Fd || alloctype == CheckMemoryLeak::OtherRes) resourceLeakError(tok, varname); else memleakError(tok, varname); } //--------------------------------------------------------------------------- void CheckMemoryLeak::reportErr(const Token *tok, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const { std::list callstack; if (tok) callstack.push_back(tok); reportErr(callstack, severity, id, msg, cwe); } void CheckMemoryLeak::reportErr(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const { const ErrorMessage errmsg(callstack, mTokenizer_ ? &mTokenizer_->list : nullptr, severity, id, msg, cwe, Certainty::normal, mSettings_->bugHunting); if (mErrorLogger_) mErrorLogger_->reportErr(errmsg); else Check::reportError(errmsg); } void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "memleak", "$symbol:" + varname + "\nMemory leak: $symbol", CWE(401U)); } void CheckMemoryLeak::memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const { reportErr(tok, Severity::error, "memleakOnRealloc", "$symbol:" + varname + "\nCommon " + reallocfunction + " mistake: \'$symbol\' nulled but not freed upon failure", CWE(401U)); } void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname) const { std::string errmsg("Resource leak"); if (!varname.empty()) errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; reportErr(tok, Severity::error, "resourceLeak", errmsg, CWE(775U)); } void CheckMemoryLeak::deallocDeallocError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocDealloc", "$symbol:" + varname + "\nDeallocating a deallocated pointer: $symbol", CWE(415U)); } void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocuse", "$symbol:" + varname + "\nDereferencing '$symbol' after it is deallocated / released", CWE(416U)); } void CheckMemoryLeak::mismatchSizeError(const Token *tok, const std::string &sz) const { reportErr(tok, Severity::error, "mismatchSize", "The allocated size " + sz + " is not a multiple of the underlying type's size.", CWE(131U)); } void CheckMemoryLeak::mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const { reportErr(callstack, Severity::error, "mismatchAllocDealloc", "$symbol:" + varname + "\nMismatching allocation and deallocation: $symbol", CWE(762U)); } CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* func, std::list *callstack) const { if (!func || !func->hasBody() || !func->functionScope) return No; // Get return pointer.. int varid = 0; for (const Token *tok2 = func->functionScope->bodyStart; tok2 != func->functionScope->bodyEnd; tok2 = tok2->next()) { if (const Token *endOfLambda = findLambdaEndToken(tok2)) tok2 = endOfLambda; if (tok2->str() == "{" && !tok2->scope()->isExecutable()) tok2 = tok2->link(); if (tok2->str() == "return") { const AllocType allocType = getAllocationType(tok2->next(), 0, callstack); if (allocType != No) return allocType; if (tok2->scope() != func->functionScope || !tok2->astOperand1()) return No; const Token* tok = tok2->astOperand1(); if (Token::Match(tok, ".|::")) tok = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); if (tok) varid = tok->varId(); break; } } // Not returning pointer value.. if (varid == 0) return No; // If variable is not local then alloctype shall be "No" // Todo: there can be false negatives about mismatching allocation/deallocation. // => Generate "alloc ; use ;" if variable is not local? const Variable *var = mTokenizer_->getSymbolDatabase()->getVariableFromVarId(varid); if (!var || !var->isLocal() || var->isStatic()) return No; // Check if return pointer is allocated.. AllocType allocType = No; for (const Token* tok = func->functionScope->bodyStart; tok != func->functionScope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%varid% =", varid)) { allocType = getAllocationType(tok->tokAt(2), varid, callstack); } if (Token::Match(tok, "= %varid% ;", varid)) { return No; } if (!mTokenizer_->isC() && Token::Match(tok, "[(,] %varid% [,)]", varid)) { return No; } if (Token::Match(tok, "[(,] & %varid% [.,)]", varid)) { return No; } if (Token::Match(tok, "[;{}] %varid% .", varid)) { return No; } if (allocType == No && tok->str() == "return") return No; } return allocType; } static bool notvar(const Token *tok, nonneg int varid) { if (!tok) return false; if (Token::Match(tok, "&&|;")) return notvar(tok->astOperand1(),varid) || notvar(tok->astOperand2(),varid); if (tok->str() == "(" && Token::Match(tok->astOperand1(), "UNLIKELY|LIKELY")) return notvar(tok->astOperand2(), varid); const Token *vartok = astIsVariableComparison(tok, "==", "0"); return vartok && (vartok->varId() == varid); } static bool ifvar(const Token *tok, nonneg int varid, const std::string &comp, const std::string &rhs) { if (!Token::simpleMatch(tok, "if (")) return false; const Token *condition = tok->next()->astOperand2(); if (condition && condition->str() == "(" && Token::Match(condition->astOperand1(), "UNLIKELY|LIKELY")) condition = condition->astOperand2(); if (!condition || condition->str() == "&&") return false; const Token *vartok = astIsVariableComparison(condition, comp, rhs); return (vartok && vartok->varId() == varid); } bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname, const Settings *settings, bool cpp) { return ((call_func_white_list.find(funcname)!=call_func_white_list.end()) || settings->library.isLeakIgnore(funcname) || (cpp && funcname == "delete")); } //--------------------------------------------------------------------------- // Check for memory leaks due to improper realloc() usage. // Below, "a" may be set to null without being freed if realloc() cannot // allocate the requested memory: // a = malloc(10); a = realloc(a, 100); //--------------------------------------------------------------------------- static bool isNoArgument(const SymbolDatabase* symbolDatabase, nonneg int varid) { const Variable* var = symbolDatabase->getVariableFromVarId(varid); return var && !var->isArgument(); } void CheckMemoryLeakInFunction::checkReallocUsage() { // only check functions const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // Search for the "var = realloc(var, 100" pattern within this function for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId() > 0 && Token::Match(tok, "%name% =")) { // Get the parenthesis in "realloc(" const Token* parTok = tok->next()->astOperand2(); // Skip casts while (parTok && parTok->isCast()) parTok = parTok->astOperand1(); if (!parTok) continue; const Token *const reallocTok = parTok->astOperand1(); if (!reallocTok) continue; const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(reallocTok); if (!(f && f->arg == -1 && mSettings->library.isnotnoreturn(reallocTok))) continue; const AllocType allocType = getReallocationType(reallocTok, tok->varId()); if (!((allocType == Malloc || allocType == OtherMem))) continue; const Token* arg = getArguments(reallocTok).at(f->reallocArg - 1); while (arg && arg->isCast()) arg = arg->astOperand1(); const Token* tok2 = tok; while (arg && arg->isUnaryOp("*") && tok2 && tok2->astParent() && tok2->astParent()->isUnaryOp("*")) { arg = arg->astOperand1(); tok2 = tok2->astParent(); } if (!arg || !tok2) continue; if (!((tok->varId() == arg->varId()) && isNoArgument(symbolDatabase, tok->varId()))) continue; // Check that another copy of the pointer wasn't saved earlier in the function if (Token::findmatch(scope->bodyStart, "%name% = %varid% ;", tok, tok->varId()) || Token::findmatch(scope->bodyStart, "[{};] %varid% = *| %var% .| %var%| [;=]", tok, tok->varId())) continue; // Check if the argument is known to be null, which means it is not a memory leak if (arg->hasKnownIntValue() && arg->getKnownIntValue() == 0) { continue; } const Token* tokEndRealloc = reallocTok->linkAt(1); // Check that the allocation isn't followed immediately by an 'if (!var) { error(); }' that might handle failure if (Token::simpleMatch(tokEndRealloc->next(), "; if (") && notvar(tokEndRealloc->tokAt(3)->astOperand2(), tok->varId())) { const Token* tokEndBrace = tokEndRealloc->linkAt(3)->linkAt(1); if (tokEndBrace && mTokenizer->isScopeNoReturn(tokEndBrace)) continue; } memleakUponReallocFailureError(tok, reallocTok->str(), tok->str()); } } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Checks for memory leaks in classes.. //--------------------------------------------------------------------------- void CheckMemoryLeakInClass::check() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // only check classes and structures for (const Scope * scope : symbolDatabase->classAndStructScopes) { for (const Variable &var : scope->varlist) { if (!var.isStatic() && var.isPointer()) { // allocation but no deallocation of private variables in public function.. const Token *tok = var.typeStartToken(); // Either it is of standard type or a non-derived type if (tok->isStandardType() || (var.type() && var.type()->derivedFrom.empty())) { if (var.isPrivate()) checkPublicFunctions(scope, var.nameToken()); variable(scope, var.nameToken()); } } } } } void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarname) { const std::string& varname = tokVarname->str(); const int varid = tokVarname->varId(); const std::string& classname = scope->className; // Check if member variable has been allocated and deallocated.. CheckMemoryLeak::AllocType memberAlloc = CheckMemoryLeak::No; CheckMemoryLeak::AllocType memberDealloc = CheckMemoryLeak::No; bool allocInConstructor = false; bool deallocInDestructor = false; // Inspect member functions for (const Function &func : scope->functionList) { const bool constructor = func.isConstructor(); const bool destructor = func.isDestructor(); if (!func.hasBody()) { if (destructor) { // implementation for destructor is not seen => assume it deallocates all variables properly deallocInDestructor = true; memberDealloc = CheckMemoryLeak::Many; } continue; } if (!func.functionScope) // defaulted destructor continue; bool body = false; const Token *end = func.functionScope->bodyEnd; for (const Token *tok = func.arg->link(); tok != end; tok = tok->next()) { if (tok == func.functionScope->bodyStart) body = true; else { if (!body) { if (!Token::Match(tok, ":|, %varid% (", varid)) continue; } // Allocate.. if (!body || Token::Match(tok, "%varid% =", varid)) { // var1 = var2 = ... // bail out if (tok->strAt(-1) == "=") return; // Foo::var1 = .. // bail out when not same class if (tok->strAt(-1) == "::" && tok->strAt(-2) != scope->className) return; AllocType alloc = getAllocationType(tok->tokAt(body ? 2 : 3), 0); if (alloc != CheckMemoryLeak::No) { if (constructor) allocInConstructor = true; if (memberAlloc != No && memberAlloc != alloc) alloc = CheckMemoryLeak::Many; if (alloc != CheckMemoryLeak::Many && memberDealloc != CheckMemoryLeak::No && memberDealloc != CheckMemoryLeak::Many && memberDealloc != alloc) { std::list callstack; callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } memberAlloc = alloc; } } if (!body) continue; // Deallocate.. AllocType dealloc = getDeallocationType(tok, varid); // some usage in the destructor => assume it's related // to deallocation if (destructor && tok->str() == varname) dealloc = CheckMemoryLeak::Many; if (dealloc != CheckMemoryLeak::No) { if (destructor) deallocInDestructor = true; // several types of allocation/deallocation? if (memberDealloc != CheckMemoryLeak::No && memberDealloc != dealloc) dealloc = CheckMemoryLeak::Many; if (dealloc != CheckMemoryLeak::Many && memberAlloc != CheckMemoryLeak::No && memberAlloc != Many && memberAlloc != dealloc) { std::list callstack; callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } memberDealloc = dealloc; } // Function call .. possible deallocation else if (Token::Match(tok->previous(), "[{};] %name% (")) { if (!CheckMemoryLeakInFunction::test_white_list(tok->str(), mSettings, mTokenizer->isCPP())) { return; } } } } } if (allocInConstructor && !deallocInDestructor) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); } else if (memberAlloc != CheckMemoryLeak::No && memberDealloc == CheckMemoryLeak::No) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); } } void CheckMemoryLeakInClass::unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname) { if (!mSettings->severity.isEnabled(Severity::style)) return; reportError(tok, Severity::style, "unsafeClassCanLeak", "$symbol:" + classname + "\n" "$symbol:" + varname + "\n" "Class '" + classname + "' is unsafe, '" + varname + "' can leak by wrong usage.\n" "The class '" + classname + "' is unsafe, wrong usage can cause memory/resource leaks for '" + varname + "'. This can for instance be fixed by adding proper cleanup in the destructor.", CWE398, Certainty::normal); } void CheckMemoryLeakInClass::checkPublicFunctions(const Scope *scope, const Token *classtok) { // Check that public functions deallocate the pointers that they allocate. // There is no checking how these functions are used and therefore it // isn't established if there is real leaks or not. if (!mSettings->severity.isEnabled(Severity::warning)) return; const int varid = classtok->varId(); // Parse public functions.. // If they allocate member variables, they should also deallocate for (const Function &func : scope->functionList) { if ((func.type == Function::eFunction || func.type == Function::eOperatorEqual) && func.access == AccessControl::Public && func.hasBody()) { const Token *tok2 = func.functionScope->bodyStart->next(); if (Token::Match(tok2, "%varid% =", varid)) { const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(2), varid); if (alloc != CheckMemoryLeak::No) publicAllocationError(tok2, tok2->str()); } else if (Token::Match(tok2, "%type% :: %varid% =", varid) && tok2->str() == scope->className) { const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(4), varid); if (alloc != CheckMemoryLeak::No) publicAllocationError(tok2, tok2->strAt(2)); } } } } void CheckMemoryLeakInClass::publicAllocationError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "publicAllocationError", "$symbol:" + varname + "\nPossible leak in public function. The pointer '$symbol' is not deallocated before it is allocated.", CWE398, Certainty::normal); } void CheckMemoryLeakStructMember::check() { if (mSettings->clang) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->isLocal() || var->isStatic() || var->isReference()) continue; if (var->typeEndToken()->isStandardType()) continue; if (var->scope()->hasInlineOrLambdaFunction()) continue; checkStructVariable(var); } } bool CheckMemoryLeakStructMember::isMalloc(const Variable *variable) { const int declarationId(variable->declarationId()); bool alloc = false; for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { if (Token::Match(tok2, "= %varid% [;=]", declarationId)) { return false; } else if (Token::Match(tok2, "%varid% = malloc|kmalloc (", declarationId)) { alloc = true; } } return alloc; } void CheckMemoryLeakStructMember::checkStructVariable(const Variable * const variable) { // Is struct variable a pointer? if (variable->isPointer()) { // Check that variable is allocated with malloc if (!isMalloc(variable)) return; } else if (!mTokenizer->isC() && (!variable->typeScope() || variable->typeScope()->getDestructor())) { // For non-C code a destructor might cleanup members return; } // Check struct.. int indentlevel2 = 0; auto deallocInFunction = [this](const Token* tok, int structid) -> bool { // Calling non-function / function that doesn't deallocate? if (CheckMemoryLeakInFunction::test_white_list(tok->str(), mSettings, mTokenizer->isCPP())) return false; // Check if the struct is used.. bool deallocated = false; const Token* const end = tok->linkAt(1); for (const Token* tok2 = tok; tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "[(,] &| %varid% [,)]", structid)) { /** @todo check if the function deallocates the memory */ deallocated = true; break; } if (Token::Match(tok2, "[(,] &| %varid% . %name% [,)]", structid)) { /** @todo check if the function deallocates the memory */ deallocated = true; break; } }; return deallocated; }; for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel2; else if (tok2->str() == "}") { if (indentlevel2 == 0) break; --indentlevel2; } // Unknown usage of struct /** @todo Check how the struct is used. Only bail out if necessary */ else if (Token::Match(tok2, "[(,] %varid% [,)]", variable->declarationId())) break; // Struct member is allocated => check if it is also properly deallocated.. else if (Token::Match(tok2->previous(), "[;{}] %varid% . %var% =", variable->declarationId())) { if (getAllocationType(tok2->tokAt(4), tok2->tokAt(2)->varId()) == AllocType::No) continue; const int structid(variable->declarationId()); const int structmemberid(tok2->tokAt(2)->varId()); // This struct member is allocated.. check that it is deallocated int indentlevel3 = indentlevel2; for (const Token *tok3 = tok2; tok3; tok3 = tok3->next()) { if (tok3->str() == "{") ++indentlevel3; else if (tok3->str() == "}") { if (indentlevel3 == 0) { memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); break; } --indentlevel3; } // Deallocating the struct member.. else if (getDeallocationType(tok3, structmemberid) != AllocType::No) { // If the deallocation happens at the base level, don't check this member anymore if (indentlevel3 == 0) break; // deallocating and then returning from function in a conditional block => // skip ahead out of the block bool ret = false; while (tok3) { if (tok3->str() == "return") ret = true; else if (tok3->str() == "{" || tok3->str() == "}") break; tok3 = tok3->next(); } if (!ret || !tok3 || tok3->str() != "}") break; --indentlevel3; continue; } // Deallocating the struct.. else if (Token::Match(tok3, "free|kfree ( %varid% )", structid)) { if (indentlevel2 == 0) memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); break; } // failed allocation => skip code.. else if (Token::simpleMatch(tok3, "if (") && notvar(tok3->next()->astOperand2(), structmemberid)) { // Goto the ")" tok3 = tok3->next()->link(); // make sure we have ") {".. it should be if (!Token::simpleMatch(tok3, ") {")) break; // Goto the "}" tok3 = tok3->next()->link(); } // succeeded allocation else if (ifvar(tok3, structmemberid, "!=", "0")) { // goto the ")" tok3 = tok3->next()->link(); // check if the variable is deallocated or returned.. int indentlevel4 = 0; for (const Token *tok4 = tok3; tok4; tok4 = tok4->next()) { if (tok4->str() == "{") ++indentlevel4; else if (tok4->str() == "}") { --indentlevel4; if (indentlevel4 == 0) break; } else if (Token::Match(tok4, "free|kfree ( %var% . %varid% )", structmemberid)) { break; } } // was there a proper deallocation? if (indentlevel4 > 0) break; } // Returning from function.. else if (tok3->str() == "return") { // Returning from function without deallocating struct member? if (!Token::Match(tok3, "return %varid% ;", structid) && !Token::Match(tok3, "return & %varid%", structid) && !(Token::Match(tok3, "return %varid% . %var%", structid) && tok3->tokAt(3)->varId() == structmemberid) && !(Token::Match(tok3, "return %name% (") && tok3->astOperand1() && deallocInFunction(tok3->astOperand1(), structid))) { memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); } break; } // struct assignment.. else if (Token::Match(tok3, "= %varid% ;", structid)) { break; } else if (Token::Match(tok3, "= %var% . %varid% ;", structmemberid)) { break; } // goto isn't handled well.. bail out even though there might be leaks else if (tok3->str() == "goto") break; // using struct in a function call.. else if (Token::Match(tok3, "%name% (")) { if (deallocInFunction(tok3, structid)) break; } } } } } void CheckMemoryLeakNoVar::check() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // only check functions for (const Scope * scope : symbolDatabase->functionScopes) { // Checks if a call to an allocation function like malloc() is made and its return value is not assigned. checkForUnusedReturnValue(scope); // Checks to see if a function is called with memory allocated for an argument that // could be leaked if a function called for another argument throws. checkForUnsafeArgAlloc(scope); // Check for leaks where a the return value of an allocation function like malloc() is an input argument, // for example f(malloc(1)), where f is known to not release the input argument. checkForUnreleasedInputArgument(scope); } } //--------------------------------------------------------------------------- // Checks if an input argument to a function is the return value of an allocation function // like malloc(), and the function does not release it. //--------------------------------------------------------------------------- void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) { // parse the executable scope until tok is reached... for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { // allocating memory in parameter for function call.. if (!Token::Match(tok, "%name% (")) continue; // check if the output of the function is assigned const Token* tok2 = tok->next()->astParent(); while (tok2 && tok2->isCast()) tok2 = tok2->astParent(); if (Token::Match(tok2, "%assign%|return")) continue; const std::string& functionName = tok->str(); if ((mTokenizer->isCPP() && functionName == "delete") || functionName == "free" || functionName == "fclose" || functionName == "realloc" || functionName == "return") continue; if (!CheckMemoryLeakInFunction::test_white_list(functionName, mSettings, mTokenizer->isCPP())) continue; const std::vector args = getArguments(tok); for (const Token* arg : args) { if (arg->isOp()) continue; while (arg->astOperand1()) arg = arg->astOperand1(); if (getAllocationType(arg, 0) == No) continue; if (isReopenStandardStream(arg)) continue; functionCallLeak(arg, arg->str(), functionName); } } } //--------------------------------------------------------------------------- // Checks if a call to an allocation function like malloc() is made and its return value is not assigned. //--------------------------------------------------------------------------- void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; if (tok->varId()) continue; const AllocType allocType = getAllocationType(tok, 0); if (allocType == No) continue; if (tok != tok->next()->astOperand1()) continue; if (isReopenStandardStream(tok)) continue; // get ast parent, skip casts const Token *parent = tok->next()->astParent(); while (parent && parent->str() == "(" && !parent->astOperand2()) parent = parent->astParent(); if (!parent) { // Check if we are in a C++11 constructor const Token * closingBrace = Token::findmatch(tok, "}|;"); if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%")) continue; returnValueNotUsedError(tok, tok->str()); } else if (Token::Match(parent, "%comp%|!")) { returnValueNotUsedError(tok, tok->str()); } } } //--------------------------------------------------------------------------- // Check if an exception could cause a leak in an argument constructed with // shared_ptr/unique_ptr. For example, in the following code, it is possible // that if g() throws an exception, the memory allocated by "new int(42)" // could be leaked. See stackoverflow.com/questions/19034538/ // why-is-there-memory-leak-while-using-shared-ptr-as-a-function-parameter // // void x() { // f(shared_ptr(new int(42)), g()); // } //--------------------------------------------------------------------------- void CheckMemoryLeakNoVar::checkForUnsafeArgAlloc(const Scope *scope) { // This test only applies to C++ source if (!mTokenizer->isCPP() || !mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) return; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%name% (")) { const Token *endParamToken = tok->next()->link(); const Token* pointerType = nullptr; const Token* functionCalled = nullptr; // Scan through the arguments to the function call for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != endParamToken; tok2 = tok2->nextArgument()) { const Function *func = tok2->function(); const bool isNothrow = func && (func->isAttributeNothrow() || func->isThrow()); if (Token::Match(tok2, "shared_ptr|unique_ptr <") && Token::Match(tok2->next()->link(), "> ( new %name%")) { pointerType = tok2; } else if (!isNothrow) { if (Token::Match(tok2, "%name% (")) functionCalled = tok2; else if (tok2->isName() && Token::simpleMatch(tok2->next()->link(), "> (")) functionCalled = tok2; } } if (pointerType && functionCalled) { std::string functionName = functionCalled->str(); if (functionCalled->strAt(1) == "<") { functionName += '<'; for (const Token* tok2 = functionCalled->tokAt(2); tok2 != functionCalled->next()->link(); tok2 = tok2->next()) functionName += tok2->str(); functionName += '>'; } std::string objectTypeName; for (const Token* tok2 = pointerType->tokAt(2); tok2 != pointerType->next()->link(); tok2 = tok2->next()) objectTypeName += tok2->str(); unsafeArgAllocError(tok, functionName, pointerType->str(), objectTypeName); } } } } void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall) { reportError(loc, Severity::error, "leakNoVarFunctionCall", "Allocation with " + alloc + ", " + functionCall + " doesn't release it.", CWE772, Certainty::normal); } void CheckMemoryLeakNoVar::returnValueNotUsedError(const Token *tok, const std::string &alloc) { reportError(tok, Severity::error, "leakReturnValNotUsed", "$symbol:" + alloc + "\nReturn value of allocation function '$symbol' is not stored.", CWE771, Certainty::normal); } void CheckMemoryLeakNoVar::unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string& objType) { const std::string factoryFunc = ptrType == "shared_ptr" ? "make_shared" : "make_unique"; reportError(tok, Severity::warning, "leakUnsafeArgAlloc", "$symbol:" + funcName + "\n" "Unsafe allocation. If $symbol() throws, memory could be leaked. Use " + factoryFunc + "<" + objType + ">() instead.", CWE401, Certainty::inconclusive); // Inconclusive because funcName may never throw } cppcheck-2.7/lib/checkmemoryleak.h000066400000000000000000000323331417746362400172460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkmemoryleakH #define checkmemoryleakH //--------------------------------------------------------------------------- /** * @file * * %Check for memory leaks * * The checking is split up into three specialized classes. * - CheckMemoryLeakInFunction can detect when a function variable is allocated but not deallocated properly. * - CheckMemoryLeakInClass can detect when a class variable is allocated but not deallocated properly. * - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members */ #include "check.h" #include "config.h" #include "errortypes.h" #include "tokenize.h" #include "utils.h" #include #include class Function; class Scope; class Settings; class Token; class Variable; class ErrorLogger; /// @addtogroup Core /// @{ /** @brief Base class for memory leaks checking */ class CPPCHECKLIB CheckMemoryLeak { private: /** For access to the tokens */ const Tokenizer * const mTokenizer_; /** ErrorLogger used to report errors */ ErrorLogger * const mErrorLogger_; /** Enabled standards */ const Settings * const mSettings_; /** * Report error. Similar with the function Check::reportError * @param tok the token where the error occurs * @param severity the severity of the bug * @param id type of message * @param msg text * @param cwe cwe number */ void reportErr(const Token *tok, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const; /** * Report error. Similar with the function Check::reportError * @param callstack callstack of error * @param severity the severity of the bug * @param id type of message * @param msg text * @param cwe cwe number */ void reportErr(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const; public: CheckMemoryLeak() = delete; CheckMemoryLeak(const CheckMemoryLeak &) = delete; void operator=(const CheckMemoryLeak &) = delete; CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Settings *s) : mTokenizer_(t), mErrorLogger_(e), mSettings_(s) {} /** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */ enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many }; void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const; /** * @brief Get type of deallocation at given position * @param tok position * @param varid variable id * @return type of deallocation */ AllocType getDeallocationType(const Token *tok, nonneg int varid) const; /** * @brief Get type of allocation at given position */ AllocType getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack = nullptr) const; /** * @brief Get type of reallocation at given position */ AllocType getReallocationType(const Token *tok2, nonneg int varid) const; /** * Check if token reopens a standard stream * @param tok token to check */ bool isReopenStandardStream(const Token *tok) const; /** * Report that there is a memory leak (new/malloc/etc) * @param tok token where memory is leaked * @param varname name of variable */ void memleakError(const Token *tok, const std::string &varname) const; /** * Report that there is a resource leak (fopen/popen/etc) * @param tok token where resource is leaked * @param varname name of variable */ void resourceLeakError(const Token *tok, const std::string &varname) const; /** * @brief Report error: deallocating a deallocated pointer * @param tok token where error occurs * @param varname name of variable */ void deallocDeallocError(const Token *tok, const std::string &varname) const; void deallocuseError(const Token *tok, const std::string &varname) const; void mismatchSizeError(const Token *tok, const std::string &sz) const; void mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const; void memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const; /** What type of allocated memory does the given function return? */ AllocType functionReturnType(const Function* func, std::list *callstack = nullptr) const; }; /// @} /// @addtogroup Checks /// @{ /** * @brief %CheckMemoryLeakInFunction detects when a function variable is allocated but not deallocated properly. * * The checking is done by looking at each function variable separately. By repeating these 4 steps over and over: * -# locate a function variable * -# create a simple token list that describes the usage of the function variable. * -# simplify the token list. * -# finally, check if the simplified token list contain any leaks. */ class CPPCHECKLIB CheckMemoryLeakInFunction : private Check, public CheckMemoryLeak { public: /** @brief This constructor is used when registering this class */ CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} /** @brief This constructor is used when running checks */ CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckMemoryLeakInFunction checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.checkReallocUsage(); } /** @brief Unit testing : testing the white list */ static bool test_white_list(const std::string &funcname, const Settings *settings, bool cpp); /** * Checking for a memory leak caused by improper realloc usage. */ void checkReallocUsage(); private: /** Report all possible errors (for the --errorlist) */ void getErrorMessages(ErrorLogger *e, const Settings *settings) const OVERRIDE { CheckMemoryLeakInFunction c(nullptr, settings, e); c.memleakError(nullptr, "varname"); c.resourceLeakError(nullptr, "varname"); c.deallocDeallocError(nullptr, "varname"); c.deallocuseError(nullptr, "varname"); c.mismatchSizeError(nullptr, "sz"); const std::list callstack; c.mismatchAllocDealloc(callstack, "varname"); c.memleakUponReallocFailureError(nullptr, "realloc", "varname"); } /** * Get name of class (--doc) * @return name of class */ static std::string myName() { return "Memory leaks (function variables)"; } /** * Get class information (--doc) * @return Wiki formatted information about this class */ std::string classInfo() const OVERRIDE { return "Is there any allocated memory when a function goes out of scope\n"; } }; /** * @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor */ class CPPCHECKLIB CheckMemoryLeakInClass : private Check, private CheckMemoryLeak { public: CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} void runChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) OVERRIDE { if (!tokenizr->isCPP()) return; CheckMemoryLeakInClass checkMemoryLeak(tokenizr, settings, errLog); checkMemoryLeak.check(); } void check(); private: void variable(const Scope *scope, const Token *tokVarname); /** Public functions: possible double-allocation */ void checkPublicFunctions(const Scope *scope, const Token *classtok); void publicAllocationError(const Token *tok, const std::string &varname); void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname); void getErrorMessages(ErrorLogger *e, const Settings *settings) const OVERRIDE { CheckMemoryLeakInClass c(nullptr, settings, e); c.publicAllocationError(nullptr, "varname"); c.unsafeClassError(nullptr, "class", "class::varname"); } static std::string myName() { return "Memory leaks (class variables)"; } std::string classInfo() const OVERRIDE { return "If the constructor allocate memory then the destructor must deallocate it.\n"; } }; /** @brief detect simple memory leaks for struct members */ class CPPCHECKLIB CheckMemoryLeakStructMember : private Check, private CheckMemoryLeak { public: CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} CheckMemoryLeakStructMember(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckMemoryLeakStructMember checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.check(); } void check(); private: /** Is local variable allocated with malloc? */ static bool isMalloc(const Variable *variable); void checkStructVariable(const Variable * const variable); void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const OVERRIDE {} static std::string myName() { return "Memory leaks (struct members)"; } std::string classInfo() const OVERRIDE { return "Don't forget to deallocate struct members\n"; } }; /** @brief detect simple memory leaks (address not taken) */ class CPPCHECKLIB CheckMemoryLeakNoVar : private Check, private CheckMemoryLeak { public: CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} CheckMemoryLeakNoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckMemoryLeakNoVar checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.check(); } void check(); private: /** * @brief %Check if an input argument to a function is the return value of an allocation function * like malloc(), and the function does not release it. * @param scope The scope of the function to check. */ void checkForUnreleasedInputArgument(const Scope *scope); /** * @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned. * @param scope The scope of the function to check. */ void checkForUnusedReturnValue(const Scope *scope); /** * @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr. * @param scope The scope of the function to check. */ void checkForUnsafeArgAlloc(const Scope *scope); void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall); void returnValueNotUsedError(const Token* tok, const std::string &alloc); void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType); void getErrorMessages(ErrorLogger *e, const Settings *settings) const OVERRIDE { CheckMemoryLeakNoVar c(nullptr, settings, e); c.functionCallLeak(nullptr, "funcName", "funcName"); c.returnValueNotUsedError(nullptr, "funcName"); c.unsafeArgAllocError(nullptr, "funcName", "shared_ptr", "int"); } static std::string myName() { return "Memory leaks (address not taken)"; } std::string classInfo() const OVERRIDE { return "Not taking the address to allocated memory\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkmemoryleakH cppcheck-2.7/lib/checknullpointer.cpp000066400000000000000000000603551417746362400200140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checknullpointer.h" #include "astutils.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckNullPointer instance; } //--------------------------------------------------------------------------- static bool checkNullpointerFunctionCallPlausibility(const Function* func, unsigned int arg) { return !func || (func->argCount() >= arg && func->getArgumentVar(arg - 1) && func->getArgumentVar(arg - 1)->isPointer()); } /** * @brief parse a function call and extract information about variable usage * @param tok first token * @param var variables that the function read / write. * @param library --library files data */ void CheckNullPointer::parseFunctionCall(const Token &tok, std::list &var, const Library *library) { if (Token::Match(&tok, "%name% ( )") || !tok.tokAt(2)) return; const std::vector args = getArguments(&tok); if (library || tok.function() != nullptr) { for (int argnr = 1; argnr <= args.size(); ++argnr) { const Token *param = args[argnr - 1]; if (library && library->isnullargbad(&tok, argnr) && checkNullpointerFunctionCallPlausibility(tok.function(), argnr)) var.push_back(param); else if (tok.function()) { const Variable* argVar = tok.function()->getArgumentVar(argnr-1); if (argVar && argVar->isStlStringType() && !argVar->isArrayOrPointer()) var.push_back(param); } } } if (library && library->formatstr_function(&tok)) { const int formatStringArgNr = library->formatstr_argno(&tok); if (formatStringArgNr < 0 || formatStringArgNr >= args.size()) return; // 1st parameter.. if (Token::Match(&tok, "snprintf|vsnprintf|fnprintf|vfnprintf") && args.size() > 1 && !(args[1] && args[1]->hasKnownIntValue() && args[1]->getKnownIntValue() == 0)) // Only if length (second parameter) is not zero var.push_back(args[0]); if (args[formatStringArgNr]->tokType() != Token::eString) return; const std::string &formatString = args[formatStringArgNr]->strValue(); int argnr = formatStringArgNr + 1; const bool scan = library->formatstr_scan(&tok); bool percent = false; for (std::string::const_iterator i = formatString.begin(); i != formatString.end(); ++i) { if (*i == '%') { percent = !percent; } else if (percent) { percent = false; bool _continue = false; while (!std::isalpha((unsigned char)*i)) { if (*i == '*') { if (scan) _continue = true; else argnr++; } ++i; if (i == formatString.end()) return; } if (_continue) continue; if (argnr < args.size() && (*i == 'n' || *i == 's' || scan)) var.push_back(args[argnr]); if (*i != 'm') // %m is a non-standard glibc extension that requires no parameter argnr++; } } } } namespace { const std::set stl_stream = { "fstream", "ifstream", "iostream", "istream", "istringstream", "ofstream", "ostream", "ostringstream", "stringstream", "wistringstream", "wostringstream", "wstringstream" }; } /** * Is there a pointer dereference? Everything that should result in * a nullpointer dereference error message will result in a true * return value. If it's unknown if the pointer is dereferenced false * is returned. * @param tok token for the pointer * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) * @return true => there is a dereference */ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown) const { return isPointerDeRef(tok, unknown, mSettings); } bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings) { unknown = false; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %name% [,)]") && settings) { const Token *ftok = tok->previous(); while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); ftok = ftok->previous(); } if (ftok && ftok->previous()) { std::list varlist; parseFunctionCall(*ftok->previous(), varlist, &settings->library); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { return true; } } } if (tok->str() == "(" && !tok->scope()->isExecutable()) return false; const Token* parent = tok->astParent(); if (!parent) return false; const bool addressOf = parent->astParent() && parent->astParent()->str() == "&"; if (parent->str() == "." && astIsRHS(tok)) return isPointerDeRef(parent, unknown, settings); const bool firstOperand = parent->astOperand1() == tok; parent = astParentSkipParens(tok); if (!parent) return false; // Dereferencing pointer.. if (parent->isUnaryOp("*") && !addressOf) return true; // array access if (firstOperand && parent->str() == "[" && !addressOf) return true; // address of member variable / array element const Token *parent2 = parent; while (Token::Match(parent2, "[|.")) parent2 = parent2->astParent(); if (parent2 != parent && parent2 && parent2->isUnaryOp("&")) return false; // read/write member variable if (firstOperand && parent->originalName() == "->" && !addressOf) return true; // If its a function pointer then check if its called if (tok->variable() && tok->variable()->isPointer() && Token::Match(tok->variable()->nameToken(), "%name% ) (") && Token::Match(tok, "%name% (")) return true; if (Token::Match(tok, "%var% = %var% .") && tok->varId() == tok->tokAt(2)->varId()) return true; // std::string dereferences nullpointers if (Token::Match(parent->tokAt(-3), "std :: string|wstring (") && tok->strAt(1) == ")") return true; if (Token::Match(parent->previous(), "%name% (") && tok->strAt(1) == ")") { const Variable* var = tok->tokAt(-2)->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) return true; } // streams dereference nullpointers if (Token::Match(parent, "<<|>>") && !firstOperand) { const Variable* var = tok->variable(); if (var && var->isPointer() && Token::Match(var->typeStartToken(), "char|wchar_t")) { // Only outputting or reading to char* can cause problems const Token* tok2 = parent; // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::Match(tok2, "std :: cout|cin|cerr")) return true; if (tok2 && tok2->varId() != 0) { const Variable* var2 = tok2->variable(); if (var2 && var2->isStlType(stl_stream)) return true; } } } const Variable *ovar = nullptr; if (Token::Match(parent, "+|==|!=") || (parent->str() == "=" && !firstOperand)) { if (parent->astOperand1() == tok && parent->astOperand2()) ovar = parent->astOperand2()->variable(); else if (parent->astOperand1() && parent->astOperand2() == tok) ovar = parent->astOperand1()->variable(); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType()) return true; // assume that it's not a dereference (no false positives) return false; } static bool isNullablePointer(const Token* tok, const Settings* settings) { if (!tok) return false; if (Token::simpleMatch(tok, "new") && tok->varId() == 0) return false; if (astIsPointer(tok)) return true; if (astIsSmartPointer(tok)) return true; if (Token::simpleMatch(tok, ".")) return isNullablePointer(tok->astOperand2(), settings); if (const Variable* var = tok->variable()) { return (var->isPointer() || var->isSmartPointer()); } return false; } void CheckNullPointer::nullPointerByDeRefAndChec() { const bool printInconclusive = (mSettings->certainty.isEnabled(Certainty::inconclusive)); for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) { tok = tok->next()->link(); continue; } if (Token::Match(tok, "%num%|%char%|%str%")) continue; if (!isNullablePointer(tok, mSettings)) continue; // Can pointer be NULL? const ValueFlow::Value *value = tok->getValue(0); if (!value) continue; if (!printInconclusive && value->isInconclusive()) continue; // Pointer dereference. bool unknown = false; if (!isPointerDeRef(tok,unknown)) { if (unknown) nullPointerError(tok, tok->expressionString(), value, true); continue; } nullPointerError(tok, tok->expressionString(), value, value->isInconclusive()); } } void CheckNullPointer::nullPointer() { nullPointerByDeRefAndChec(); } namespace { const std::set stl_istream = { "fstream", "ifstream", "iostream", "istream", "istringstream", "stringstream", "wistringstream", "wstringstream" }; } /** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body continue; const Token *tok = scope->bodyStart; if (scope->function->isConstructor()) tok = scope->function->token; // Check initialization list for (; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp()) { nullPointerError(tok); } } else if (Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (Token::Match(tok->previous(), "!!. %name% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::Match(tok->tokAt(2), "0|NULL|nullptr )") && tok->varId()) { // constructor call const Variable *var = tok->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) nullPointerError(tok); } else { // function call std::list var; parseFunctionCall(*tok, var, &mSettings->library); // is one of the var items a NULL pointer? for (const Token *vartok : var) { if (vartok->hasKnownIntValue() && vartok->getKnownIntValue() == 0) nullPointerError(vartok); } } } else if (Token::Match(tok, "std :: string|wstring ( 0|NULL|nullptr )")) nullPointerError(tok); else if (Token::Match(tok->previous(), "::|. %name% (")) { const std::vector &args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { const Token *argtok = args[argnr]; if (!argtok->hasKnownIntValue()) continue; if (argtok->values().front().intvalue != 0) continue; if (mSettings->library.isnullargbad(tok, argnr+1)) nullPointerError(argtok); } } else if (Token::Match(tok->previous(), ">> 0|NULL|nullptr")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:|(")) break; } if (tok2 && tok2->previous() && tok2->previous()->str()=="(") continue; if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable *var = tok2->variable(); if (var && var->isStlType(stl_istream)) nullPointerError(tok); } } const Variable *ovar = nullptr; const Token *tokNull = nullptr; if (Token::Match(tok, "0|NULL|nullptr ==|!=|>|>=|<|<= %var%")) { if (!Token::Match(tok->tokAt(3),".|[")) { ovar = tok->tokAt(2)->variable(); tokNull = tok; } } else if (Token::Match(tok, "%var% ==|!=|>|>=|<|<= 0|NULL|nullptr") || Token::Match(tok, "%var% =|+ 0|NULL|nullptr )|]|,|;|+")) { ovar = tok->variable(); tokNull = tok->tokAt(2); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType() && tokNull && tokNull->originalName() != "'\\0'") nullPointerError(tokNull); } } } void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) { const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible null pointer dereference: $symbol."); const std::string errmsgdefarg("$symbol:" + varname + "\nPossible null pointer dereference if the default parameter value is used: $symbol"); if (!tok) { reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); reportError(tok, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); reportError(tok, Severity::warning, "nullPointerRedundantCheck", errmsgcond, CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); return; } if (!value) { reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE_NULL_POINTER_DEREFERENCE, inconclusive ? Certainty::inconclusive : Certainty::normal); return; } if (!mSettings->isEnabled(value, inconclusive)) return; const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer dereference"); if (value->condition) { reportError(errorPath, Severity::warning, "nullPointerRedundantCheck", errmsgcond, CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } else if (value->defaultArg) { reportError(errorPath, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } else { std::string errmsg; errmsg = std::string(value->isKnown() ? "Null" : "Possible null") + " pointer dereference"; if (!varname.empty()) errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; reportError(errorPath, value->isKnown() ? Severity::error : Severity::warning, "nullPointer", errmsg, CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } } void CheckNullPointer::arithmetic() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "-|+|+=|-=|++|--")) continue; const Token *pointerOperand; const Token *numericOperand; if (tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->pointer != 0) { pointerOperand = tok->astOperand1(); numericOperand = tok->astOperand2(); } else if (tok->astOperand2() && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->pointer != 0) { pointerOperand = tok->astOperand2(); numericOperand = tok->astOperand1(); } else continue; if (numericOperand && numericOperand->valueType() && !numericOperand->valueType()->isIntegral()) continue; const ValueFlow::Value* value = pointerOperand->getValue(0); if (!value) continue; if (!mSettings->certainty.isEnabled(Certainty::inconclusive) && value->isInconclusive()) continue; if (value->condition && !mSettings->severity.isEnabled(Severity::warning)) continue; if (value->condition) redundantConditionWarning(tok, value, value->condition, value->isInconclusive()); else pointerArithmeticError(tok, value, value->isInconclusive()); } } } static std::string arithmeticTypeString(const Token *tok) { if (tok && tok->str()[0] == '-') return "subtraction"; else if (tok && tok->str()[0] == '+') return "addition"; else return "arithmetic"; } void CheckNullPointer::pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) { std::string arithmetic = arithmeticTypeString(tok); std::string errmsg; if (tok && tok->str()[0] == '-') { errmsg = "Overflow in pointer arithmetic, NULL pointer is subtracted."; } else { errmsg = "Pointer " + arithmetic + " with NULL pointer."; } const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer " + arithmetic); reportError(errorPath, Severity::error, "nullPointerArithmetic", errmsg, CWE_INCORRECT_CALCULATION, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckNullPointer::redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive) { std::string arithmetic = arithmeticTypeString(tok); std::string errmsg; if (tok && tok->str()[0] == '-') { errmsg = ValueFlow::eitherTheConditionIsRedundant(condition) + " or there is overflow in pointer " + arithmetic + "."; } else { errmsg = ValueFlow::eitherTheConditionIsRedundant(condition) + " or there is pointer arithmetic with NULL pointer."; } const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer " + arithmetic); reportError(errorPath, Severity::warning, "nullPointerArithmeticRedundantCheck", errmsg, CWE_INCORRECT_CALCULATION, inconclusive ? Certainty::inconclusive : Certainty::normal); } std::string CheckNullPointer::MyFileInfo::toString() const { return CTU::toString(unsafeUsage); } static bool isUnsafeUsage(const Check *check, const Token *vartok, MathLib::bigint *value) { (void)value; const CheckNullPointer *checkNullPointer = dynamic_cast(check); bool unknown = false; return checkNullPointer && checkNullPointer->isPointerDeRef(vartok, unknown); } Check::FileInfo *CheckNullPointer::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { CheckNullPointer check(tokenizer, settings, nullptr); const std::list &unsafeUsage = CTU::getUnsafeUsage(tokenizer, settings, &check, ::isUnsafeUsage); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } Check::FileInfo * CheckNullPointer::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { const std::list &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } bool CheckNullPointer::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { if (!ctu) return false; bool foundErrors = false; (void)settings; // This argument is unused const std::map> callsMap = ctu->getCallsMap(); for (Check::FileInfo *fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); if (!fi) continue; for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) { for (int warning = 0; warning <= 1; warning++) { if (warning == 1 && !settings.severity.isEnabled(Severity::warning)) break; const std::list &locationList = CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::null, unsafeUsage, callsMap, "Dereferencing argument ARG that is null", nullptr, warning); if (locationList.empty()) continue; const ErrorMessage errmsg(locationList, emptyString, warning ? Severity::warning : Severity::error, "Null pointer dereference: " + unsafeUsage.myArgumentName, "ctunullpointer", CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); errorLogger.reportErr(errmsg); foundErrors = true; break; } } } return foundErrors; } cppcheck-2.7/lib/checknullpointer.h000066400000000000000000000131721417746362400174540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checknullpointerH #define checknullpointerH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "ctu.h" #include "valueflow.h" #include #include class ErrorLogger; class Library; class Settings; class Token; class Tokenizer; namespace tinyxml2 { class XMLElement; } /// @addtogroup Checks /// @{ /** @brief check for null pointer dereferencing */ class CPPCHECKLIB CheckNullPointer : public Check { public: /** @brief This constructor is used when registering the CheckNullPointer */ CheckNullPointer() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckNullPointer(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckNullPointer checkNullPointer(tokenizer, settings, errorLogger); checkNullPointer.nullPointer(); checkNullPointer.arithmetic(); checkNullPointer.nullConstantDereference(); } /** * @brief parse a function call and extract information about variable usage * @param tok first token * @param var variables that the function read / write. * @param library --library files data */ static void parseFunctionCall(const Token &tok, std::list &var, const Library *library); /** * Is there a pointer dereference? Everything that should result in * a nullpointer dereference error message will result in a true * return value. If it's unknown if the pointer is dereferenced false * is returned. * @param tok token for the pointer * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) * @return true => there is a dereference */ bool isPointerDeRef(const Token *tok, bool &unknown) const; static bool isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings); /** @brief possible null pointer dereference */ void nullPointer(); /** @brief dereferencing null constant (after Tokenizer::simplifyKnownVariables) */ void nullConstantDereference(); void nullPointerError(const Token *tok) { ValueFlow::Value v(0); v.setKnown(); nullPointerError(tok, "", &v, false); } void nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value* value, bool inconclusive); /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: /** function arguments that are dereferenced without checking if they are null */ std::list unsafeUsage; /** Convert MyFileInfo data into xml string */ std::string toString() const OVERRIDE; }; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; private: /** Get error messages. Used by --errorlist */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckNullPointer c(nullptr, settings, errorLogger); c.nullPointerError(nullptr, "pointer", nullptr, false); c.pointerArithmeticError(nullptr, nullptr, false); c.redundantConditionWarning(nullptr, nullptr, nullptr, false); } /** Name of check */ static std::string myName() { return "Null pointer"; } /** class info in WIKI format. Used by --doc */ std::string classInfo() const OVERRIDE { return "Null pointers\n" "- null pointer dereferencing\n" "- undefined null pointer arithmetic\n"; } /** * @brief Does one part of the check for nullPointer(). * Dereferencing a pointer and then checking if it's NULL.. */ void nullPointerByDeRefAndChec(); /** undefined null pointer arithmetic */ void arithmetic(); void pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); void redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive); }; /// @} //--------------------------------------------------------------------------- #endif // checknullpointerH cppcheck-2.7/lib/checkother.cpp000066400000000000000000005163131417746362400165620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkother.h" #include "astutils.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include "valueflow.h" #include "checkuninitvar.h" // CheckUninitVar::isVariableUsage #include // find_if() #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckOther instance; } static const struct CWE CWE128(128U); // Wrap-around Error static const struct CWE CWE131(131U); // Incorrect Calculation of Buffer Size static const struct CWE CWE197(197U); // Numeric Truncation Error static const struct CWE CWE362(362U); // Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') static const struct CWE CWE369(369U); // Divide By Zero static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE475(475U); // Undefined Behavior for Input to API static const struct CWE CWE482(482U); // Comparing instead of Assigning static const struct CWE CWE561(561U); // Dead Code static const struct CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') static const struct CWE CWE570(570U); // Expression is Always False static const struct CWE CWE571(571U); // Expression is Always True static const struct CWE CWE672(672U); // Operation on a Resource after Expiration or Release static const struct CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const struct CWE CWE683(683U); // Function Call With Incorrect Order of Arguments static const struct CWE CWE686(686U); // Function Call With Incorrect Argument Type static const struct CWE CWE704(704U); // Incorrect Type Conversion or Cast static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const struct CWE CWE768(768U); // Incorrect Short Circuit Evaluation static const struct CWE CWE783(783U); // Operator Precedence Logic Error //---------------------------------------------------------------------------------- // The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. // If this return value is stored in a character variable and then compared // to EOF, which is an integer, the comparison maybe be false. // // Reference: // - Ticket #160 // - http://www.cplusplus.com/reference/cstdio/fgetc/ // - http://www.cplusplus.com/reference/cstdio/getc/ // - http://www.cplusplus.com/reference/cstdio/getchar/ // - http://www.cplusplus.com/reference/cstdio/ungetc/ ... //---------------------------------------------------------------------------------- void CheckOther::checkCastIntToCharAndBack() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { std::map vars; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // Quick check to see if any of the matches below have any chances if (!Token::Match(tok, "%var%|EOF %comp%|=")) continue; if (Token::Match(tok, "%var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { vars[tok->varId()] = tok->strAt(2); } } else if (Token::Match(tok, "EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { tok = tok->tokAt(3); const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { checkCastIntToCharAndBackError(tok, tok->strAt(2)); } } else if (mTokenizer->isCPP() && (Token::Match(tok, "EOF %comp% ( %var% = std :: cin . get (") || Token::Match(tok, "EOF %comp% ( %var% = cin . get ("))) { tok = tok->tokAt(3); const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { checkCastIntToCharAndBackError(tok, "cin.get"); } } else if (mTokenizer->isCPP() && (Token::Match(tok, "%var% = std :: cin . get (") || Token::Match(tok, "%var% = cin . get ("))) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { vars[tok->varId()] = "cin.get"; } } else if (Token::Match(tok, "%var% %comp% EOF")) { if (vars.find(tok->varId()) != vars.end()) { checkCastIntToCharAndBackError(tok, vars[tok->varId()]); } } else if (Token::Match(tok, "EOF %comp% %var%")) { tok = tok->tokAt(2); if (vars.find(tok->varId()) != vars.end()) { checkCastIntToCharAndBackError(tok, vars[tok->varId()]); } } } } } void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) { reportError( tok, Severity::warning, "checkCastIntToCharAndBack", "$symbol:" + strFunctionName + "\n" "Storing $symbol() return value in char variable and then comparing with EOF.\n" "When saving $symbol() return value in char variable there is loss of precision. " " When $symbol() returns EOF this value is truncated. Comparing the char " "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = $symbol());\" " "loops forever on some compilers/platforms and on other compilers/platforms it will stop " "when the file contains a matching character.", CWE197, Certainty::normal ); } //--------------------------------------------------------------------------- // Clarify calculation precedence for ternary operators. //--------------------------------------------------------------------------- void CheckOther::clarifyCalculation() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // ? operator where lhs is arithmetical expression if (tok->str() != "?" || !tok->astOperand1() || !tok->astOperand1()->isCalculation()) continue; if (!tok->astOperand1()->isArithmeticalOp() && tok->astOperand1()->tokType() != Token::eBitOp) continue; // non-pointer calculation in lhs and pointer in rhs => no clarification is needed if (tok->astOperand1()->isBinaryOp() && Token::Match(tok->astOperand1(), "%or%|&|%|*|/") && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->pointer > 0) continue; // bit operation in lhs and char literals in rhs => probably no mistake if (tok->astOperand1()->tokType() == Token::eBitOp && Token::Match(tok->astOperand2()->astOperand1(), "%char%") && Token::Match(tok->astOperand2()->astOperand2(), "%char%")) continue; // 2nd operand in lhs has known integer value => probably no mistake if (tok->astOperand1()->isBinaryOp() && tok->astOperand1()->astOperand2()->hasKnownIntValue()) { const Token *op = tok->astOperand1()->astOperand2(); if (op->isNumber()) continue; if (op->valueType() && op->valueType()->isEnum()) continue; } // Is code clarified by parentheses already? const Token *tok2 = tok->astOperand1(); for (; tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")") break; else if (tok2->str() == "?") { clarifyCalculationError(tok, tok->astOperand1()->str()); break; } } } } } void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) { // suspicious calculation const std::string calc("'a" + op + "b?c:d'"); // recommended calculation #1 const std::string s1("'(a" + op + "b)?c:d'"); // recommended calculation #2 const std::string s2("'a" + op + "(b?c:d)'"); reportError(tok, Severity::style, "clarifyCalculation", "Clarify calculation precedence for '" + op + "' and '?'.\n" "Suspicious calculation. Please use parentheses to clarify the code. " "The code '" + calc + "' should be written as either '" + s1 + "' or '" + s2 + "'.", CWE783, Certainty::normal); } //--------------------------------------------------------------------------- // Clarify (meaningless) statements like *foo++; with parentheses. //--------------------------------------------------------------------------- void CheckOther::clarifyStatement() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "* %name%") && tok->astOperand1()) { const Token *tok2 = tok->previous(); while (tok2 && tok2->str() == "*") tok2 = tok2->previous(); if (tok2 && !tok2->astParent() && Token::Match(tok2, "[{};]")) { tok2 = tok->astOperand1(); if (Token::Match(tok2, "++|-- [;,]")) clarifyStatementError(tok2); } } } } } void CheckOther::clarifyStatementError(const Token *tok) { reportError(tok, Severity::warning, "clarifyStatement", "In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n" "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. " "Thus, the dereference is meaningless. Did you intend to write '(*A)++;'?", CWE783, Certainty::normal); } //--------------------------------------------------------------------------- // Check for suspicious occurrences of 'if(); {}'. //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousSemicolon() { if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Look for "if(); {}", "for(); {}" or "while(); {}" for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type == Scope::eIf || scope.type == Scope::eElse || scope.type == Scope::eWhile || scope.type == Scope::eFor) { // Ensure the semicolon is at the same line number as the if/for/while statement // and the {..} block follows it without an extra empty line. if (Token::simpleMatch(scope.bodyStart, "{ ; } {") && scope.bodyStart->previous()->linenr() == scope.bodyStart->tokAt(2)->linenr() && scope.bodyStart->linenr()+1 >= scope.bodyStart->tokAt(3)->linenr() && !scope.bodyStart->tokAt(3)->isExpandedMacro()) { suspiciousSemicolonError(scope.classDef); } } } } void CheckOther::suspiciousSemicolonError(const Token* tok) { reportError(tok, Severity::warning, "suspiciousSemicolon", "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // For C++ code, warn if C-style casts are used on pointer types //--------------------------------------------------------------------------- void CheckOther::warningOldStylePointerCast() { // Only valid on C++ code if (!mSettings->severity.isEnabled(Severity::style) || !mTokenizer->isCPP()) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const Token* tok; if (scope->function && scope->function->isConstructor()) tok = scope->classDef; else tok = scope->bodyStart; for (; tok && tok != scope->bodyEnd; tok = tok->next()) { // Old style pointer casting.. if (!Token::Match(tok, "( const|volatile| const|volatile| %type% * const| ) (| %name%|%num%|%bool%|%char%|%str%")) continue; // skip first "const" in "const Type* const" while (Token::Match(tok->next(), "const|volatile")) tok = tok->next(); const Token* typeTok = tok->next(); // skip second "const" in "const Type* const" if (tok->strAt(3) == "const") tok = tok->next(); const Token *p = tok->tokAt(4); if (p->hasKnownIntValue() && p->values().front().intvalue==0) // Casting nullpointers is safe continue; // Is "type" a class? if (typeTok->type()) cstyleCastError(tok); } } } void CheckOther::cstyleCastError(const Token *tok) { reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting\n" "C-style pointer casting detected. C++ offers four different kinds of casts as replacements: " "static_cast, const_cast, dynamic_cast and reinterpret_cast. A C-style cast could evaluate to " "any of those automatically, thus it is considered safer if the programmer explicitly states " "which kind of cast is expected. See also: https://www.securecoding.cert.org/confluence/display/cplusplus/EXP05-CPP.+Do+not+use+C-style+casts.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation //--------------------------------------------------------------------------- void CheckOther::invalidPointerCast() { if (!mSettings->severity.isEnabled(Severity::portability)) return; const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const Token* toTok = nullptr; const Token* fromTok = nullptr; // Find cast if (Token::Match(tok, "( const|volatile| const|volatile| %type% %type%| const| * )")) { toTok = tok; fromTok = tok->astOperand1(); } else if (Token::simpleMatch(tok, "reinterpret_cast <") && tok->linkAt(1)) { toTok = tok->linkAt(1)->next(); fromTok = toTok->astOperand2(); } if (!fromTok) continue; const ValueType* fromType = fromTok->valueType(); const ValueType* toType = toTok->valueType(); if (!fromType || !toType || !fromType->pointer || !toType->pointer) continue; if (fromType->type != toType->type && fromType->type >= ValueType::Type::BOOL && toType->type >= ValueType::Type::BOOL && (toType->type != ValueType::Type::CHAR || printInconclusive)) { if (toType->isIntegral() && fromType->isIntegral()) continue; invalidPointerCastError(tok, fromType->str(), toType->str(), toType->type == ValueType::Type::CHAR, toType->isIntegral()); } } } } void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt) { if (toIsInt) { // If we cast something to int*, this can be useful to play with its binary data representation reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + " to " + to + " is not portable due to different binary data representations on different platforms.", CWE704, inconclusive ? Certainty::inconclusive : Certainty::normal); } else reportError(tok, Severity::portability, "invalidPointerCast", "Casting between " + from + " and " + to + " which have an incompatible binary data representation.", CWE704, Certainty::normal); } //--------------------------------------------------------------------------- // This check detects errors on POSIX systems, when a pipe command called // with a wrong dimensioned file descriptor array. The pipe command requires // exactly an integer array of dimension two as parameter. // // References: // - http://linux.die.net/man/2/pipe // - ticket #3521 //--------------------------------------------------------------------------- void CheckOther::checkPipeParameterSize() { if (!mSettings->posix()) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "pipe ( %var% )") || Token::Match(tok, "pipe2 ( %var% ,")) { const Token * const varTok = tok->tokAt(2); const Variable *var = varTok->variable(); MathLib::bigint dim; if (var && var->isArray() && !var->isArgument() && ((dim=var->dimension(0U)) < 2)) { const std::string strDim = MathLib::toString(dim); checkPipeParameterSizeError(varTok,varTok->str(), strDim); } } } } } void CheckOther::checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim) { reportError(tok, Severity::error, "wrongPipeParameterSize", "$symbol:" + strVarName + "\n" "Buffer '$symbol' must have size of 2 integers if used as parameter of pipe().\n" "The pipe()/pipe2() system command takes an argument, which is an array of exactly two integers.\n" "The variable '$symbol' is an array of size " + strDim + ", which does not match.", CWE686, Certainty::safe); } //--------------------------------------------------------------------------- // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- void CheckOther::checkRedundantAssignment() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { if (!scope->bodyStart) continue; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "] (")) // todo: handle lambdas break; if (Token::simpleMatch(tok, "try {")) // todo: check try blocks tok = tok->linkAt(1); if ((tok->isAssignmentOp() || Token::Match(tok, "++|--")) && tok->astOperand1()) { if (tok->astParent()) continue; // Do not warn about redundant initialization when rhs is trivial // TODO : do not simplify the variable declarations bool isInitialization = false; if (Token::Match(tok->tokAt(-2), "; %var% =") && tok->tokAt(-2)->isSplittedVarDeclEq()) { isInitialization = true; bool trivial = true; visitAstNodes(tok->astOperand2(), [&](const Token *rhs) { if (Token::simpleMatch(rhs, "{ 0 }")) return ChildrenToVisit::none; if (Token::Match(rhs, "%str%|%num%|%name%") && !rhs->varId()) return ChildrenToVisit::none; if (Token::Match(rhs, ":: %name%") && rhs->hasKnownIntValue()) return ChildrenToVisit::none; if (rhs->isCast()) return ChildrenToVisit::op2; trivial = false; return ChildrenToVisit::done; }); if (trivial) continue; } const Token* rhs = tok->astOperand2(); // Do not warn about assignment with 0 / NULL if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs)) continue; if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) // todo: check references continue; if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) // todo: check static variables continue; bool inconclusive = false; if (mTokenizer->isCPP() && tok->astOperand1()->valueType()) { // If there is a custom assignment operator => this is inconclusive if (tok->astOperand1()->valueType()->typeScope) { const std::string op = "operator" + tok->str(); for (const Function& f : tok->astOperand1()->valueType()->typeScope->functionList) { if (f.name() == op) { inconclusive = true; break; } } } // assigning a smart pointer has side effects if (tok->astOperand1()->valueType()->type == ValueType::SMART_POINTER) break; } if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive)) continue; FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library); if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) continue; // Is there a redundant assignment? const Token *start; if (tok->isAssignmentOp()) start = tok->next(); else start = tok->findExpressionStartEndTokens().second->next(); // Get next assignment.. const Token *nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); if (!nextAssign) continue; // there is redundant assignment. Is there a case between the assignments? bool hasCase = false; for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) { if (tok2->str() == "break" || tok2->str() == "return") break; if (tok2->str() == "case") { hasCase = true; break; } } // warn if (hasCase) redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString()); else if (isInitialization) redundantInitializationError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); else redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); } } } } void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) { const std::list callstack = { tok1, tok2 }; reportError(callstack, Severity::performance, "redundantCopy", "$symbol:" + var + "\n" "Buffer '$symbol' is being written before its old content has been used.", CWE563, Certainty::normal); } void CheckOther::redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { const std::list callstack = { tok1, tok2 }; reportError(callstack, Severity::style, "redundantCopyInSwitch", "$symbol:" + var + "\n" "Buffer '$symbol' is being written before its old content has been used. 'break;' missing?", CWE563, Certainty::normal); } void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is assigned"), ErrorPathItem(tok2, var + " is overwritten") }; if (inconclusive) reportError(errorPath, Severity::style, "redundantAssignment", "$symbol:" + var + "\n" "Variable '$symbol' is reassigned a value before the old one has been used if variable is no semaphore variable.\n" "Variable '$symbol' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", CWE563, Certainty::inconclusive); else reportError(errorPath, Severity::style, "redundantAssignment", "$symbol:" + var + "\n" "Variable '$symbol' is reassigned a value before the old one has been used.", CWE563, Certainty::normal); } void CheckOther::redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is initialized"), ErrorPathItem(tok2, var + " is overwritten") }; reportError(errorPath, Severity::style, "redundantInitialization", "$symbol:" + var + "\nRedundant initialization for '$symbol'. The initialized value is overwritten before it is read.", CWE563, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { const ErrorPath errorPath = { ErrorPathItem(tok1, "$symbol is assigned"), ErrorPathItem(tok2, "$symbol is overwritten") }; reportError(errorPath, Severity::style, "redundantAssignInSwitch", "$symbol:" + var + "\n" "Variable '$symbol' is reassigned a value before the old one has been used. 'break;' missing?", CWE563, Certainty::normal); } //--------------------------------------------------------------------------- // switch (x) // { // case 2: // y = a; // <- this assignment is redundant // case 3: // y = b; // <- case 2 falls through and sets y twice // } //--------------------------------------------------------------------------- static inline bool isFunctionOrBreakPattern(const Token *tok) { if (Token::Match(tok, "%name% (") || Token::Match(tok, "break|continue|return|exit|goto|throw")) return true; return false; } void CheckOther::checkRedundantAssignmentInSwitch() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Find the beginning of a switch. E.g.: // switch (var) { ... for (const Scope &switchScope : symbolDatabase->scopeList) { if (switchScope.type != Scope::eSwitch || !switchScope.bodyStart) continue; // Check the contents of the switch statement std::map varsWithBitsSet; std::map bitOperations; for (const Token *tok2 = switchScope.bodyStart->next(); tok2 != switchScope.bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") { // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.: // case 3: b = 1; // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) { const Token* endOfConditional = tok2->link(); for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) { if (tok3->varId() != 0) { varsWithBitsSet.erase(tok3->varId()); bitOperations.erase(tok3->varId()); } else if (isFunctionOrBreakPattern(tok3)) { varsWithBitsSet.clear(); bitOperations.clear(); } } tok2 = endOfConditional; } } // Variable assignment. Report an error if it's assigned to twice before a break. E.g.: // case 3: b = 1; // <== redundant // case 4: b = 2; if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;")) { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } // Bitwise operation. Report an error if it's performed twice before a break. E.g.: // case 3: b |= 1; // <== redundant // case 4: b |= 1; else if (Token::Match(tok2->previous(), ";|{|}|: %var% %assign% %num% ;") && (tok2->strAt(1) == "|=" || tok2->strAt(1) == "&=") && Token::Match(tok2->next()->astOperand2(), "%num%")) { const std::string bitOp = tok2->strAt(1)[0] + tok2->strAt(2); const std::map::const_iterator i2 = varsWithBitsSet.find(tok2->varId()); // This variable has not had a bit operation performed on it yet, so just make a note of it if (i2 == varsWithBitsSet.end()) { varsWithBitsSet[tok2->varId()] = tok2; bitOperations[tok2->varId()] = bitOp; } // The same bit operation has been performed on the same variable twice, so report an error else if (bitOperations[tok2->varId()] == bitOp) redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); // A different bit operation was performed on the variable, so clear it else { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } } // Bitwise operation. Report an error if it's performed twice before a break. E.g.: // case 3: b = b | 1; // <== redundant // case 4: b = b | 1; else if (Token::Match(tok2->previous(), ";|{|}|: %var% = %name% %or%|& %num% ;") && tok2->varId() == tok2->tokAt(2)->varId()) { const std::string bitOp = tok2->strAt(3) + tok2->strAt(4); const std::map::const_iterator i2 = varsWithBitsSet.find(tok2->varId()); // This variable has not had a bit operation performed on it yet, so just make a note of it if (i2 == varsWithBitsSet.end()) { varsWithBitsSet[tok2->varId()] = tok2; bitOperations[tok2->varId()] = bitOp; } // The same bit operation has been performed on the same variable twice, so report an error else if (bitOperations[tok2->varId()] == bitOp) redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); // A different bit operation was performed on the variable, so clear it else { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } } // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: // case 3: b = 1; // case 4: b++; else if (tok2->varId() != 0 && tok2->strAt(1) != "|" && tok2->strAt(1) != "&") { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } // Reset our record of assignments if there is a break or function call. E.g.: // case 3: b = 1; break; if (isFunctionOrBreakPattern(tok2)) { varsWithBitsSet.clear(); bitOperations.clear(); } } } } void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "redundantBitwiseOperationInSwitch", "$symbol:" + varname + "\n" "Redundant bitwise operation on '$symbol' in 'switch' statement. 'break;' missing?"); } //--------------------------------------------------------------------------- // Check for statements like case A||B: in switch() //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousCaseInSwitch() { if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope & scope : symbolDatabase->scopeList) { if (scope.type != Scope::eSwitch) continue; for (const Token* tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() == "case") { const Token* finding = nullptr; for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ":") break; if (Token::Match(tok2, "[;}{]")) break; if (tok2->str() == "?") finding = nullptr; else if (Token::Match(tok2, "&&|%oror%")) finding = tok2; } if (finding) suspiciousCaseInSwitchError(finding, finding->str()); } } } } void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) { reportError(tok, Severity::warning, "suspiciousCase", "Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n" "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, Certainty::inconclusive); } //--------------------------------------------------------------------------- // Find consecutive return, break, continue, goto or throw statements. e.g.: // break; break; // Detect dead code, that follows such a statement. e.g.: // return(0); foo(); //--------------------------------------------------------------------------- void CheckOther::checkUnreachableCode() { if (!mSettings->severity.isEnabled(Severity::style)) return; const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { const Token* secondBreak = nullptr; const Token* labelName = nullptr; if (tok->link() && Token::Match(tok, "(|[|<")) tok = tok->link(); else if (Token::Match(tok, "break|continue ;")) secondBreak = tok->tokAt(2); else if (Token::Match(tok, "[;{}:] return|throw") && tok->next()->isKeyword()) { if (Token::simpleMatch(tok->astParent(), "?")) continue; tok = tok->next(); // tok should point to return or throw for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "{") tok2 = tok2->link(); if (tok2->str() == ";") { secondBreak = tok2->next(); break; } } } else if (Token::Match(tok, "goto %any% ;")) { secondBreak = tok->tokAt(3); labelName = tok->next(); } else if (Token::Match(tok, "%name% (") && mSettings->library.isnoreturn(tok) && !Token::Match(tok->next()->astParent(), "?|:")) { if ((!tok->function() || (tok->function()->token != tok && tok->function()->tokenDef != tok)) && tok->linkAt(1)->strAt(1) != "{") secondBreak = tok->linkAt(1)->tokAt(2); if (Token::simpleMatch(secondBreak, "return")) { // clarification for tools that function returns continue; } } // Statements follow directly, no line between them. (#3383) // TODO: Try to find a better way to avoid false positives due to preprocessor configurations. const bool inconclusive = secondBreak && (secondBreak->linenr() - 1 > secondBreak->previous()->linenr()); if (secondBreak && (printInconclusive || !inconclusive)) { if (Token::Match(secondBreak, "continue|goto|throw|return") && secondBreak->isKeyword()) { duplicateBreakError(secondBreak, inconclusive); tok = Token::findmatch(secondBreak, "[}:]"); } else if (secondBreak->str() == "break") { // break inside switch as second break statement should not issue a warning if (tok->str() == "break") // If the previous was a break, too: Issue warning duplicateBreakError(secondBreak, inconclusive); else { if (tok->scope()->type != Scope::eSwitch) // Check, if the enclosing scope is a switch duplicateBreakError(secondBreak, inconclusive); } tok = Token::findmatch(secondBreak, "[}:]"); } else if (!Token::Match(secondBreak, "return|}|case|default") && secondBreak->strAt(1) != ":") { // TODO: No bailout for unconditional scopes // If the goto label is followed by a loop construct in which the label is defined it's quite likely // that the goto jump was intended to skip some code on the first loop iteration. bool labelInFollowingLoop = false; if (labelName && Token::Match(secondBreak, "while|do|for")) { const Token *scope2 = Token::findsimplematch(secondBreak, "{"); if (scope2) { for (const Token *tokIter = scope2; tokIter != scope2->link() && tokIter; tokIter = tokIter->next()) { if (Token::Match(tokIter, "[;{}] %any% :") && labelName->str() == tokIter->strAt(1)) { labelInFollowingLoop = true; break; } } } } // hide FP for statements that just hide compiler warnings about unused function arguments bool silencedCompilerWarningOnly = false; const Token *silencedWarning = secondBreak; for (;;) { if (Token::Match(silencedWarning, "( void ) %name% ;")) { silencedWarning = silencedWarning->tokAt(5); continue; } else if (silencedWarning && silencedWarning == scope->bodyEnd) silencedCompilerWarningOnly = true; break; } if (silencedWarning) secondBreak = silencedWarning; if (!labelInFollowingLoop && !silencedCompilerWarningOnly) unreachableCodeError(secondBreak, inconclusive); tok = Token::findmatch(secondBreak, "[}:]"); } else tok = secondBreak; if (!tok) break; tok = tok->previous(); // Will be advanced again by for loop } } } } void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "duplicateBreak", "Consecutive return, break, continue, goto or throw statements are unnecessary.\n" "Consecutive return, break, continue, goto or throw statements are unnecessary. " "The second statement can never be executed, and so should be removed.", CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckOther::unreachableCodeError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "unreachableCode", "Statements following return, break, continue, goto or throw will never be executed.", CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- // Check scope of variables.. //--------------------------------------------------------------------------- void CheckOther::checkVariableScope() { if (mSettings->clang) return; if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // In C it is common practice to declare local variables at the // start of functions. if (mSettings->daca && mTokenizer->isC()) return; for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->isLocal() || (!var->isPointer() && !var->isReference() && !var->typeStartToken()->isStandardType())) continue; if (var->isConst()) continue; if (mTokenizer->hasIfdef(var->nameToken(), var->scope()->bodyEnd)) continue; // reference of range for loop variable.. if (Token::Match(var->nameToken()->previous(), "& %var% = %var% .")) { const Token *otherVarToken = var->nameToken()->tokAt(2); const Variable *otherVar = otherVarToken->variable(); if (otherVar && Token::Match(otherVar->nameToken(), "%var% :") && otherVar->nameToken()->next()->astParent() && Token::simpleMatch(otherVar->nameToken()->next()->astParent()->previous(), "for (")) continue; } bool forHead = false; // Don't check variables declared in header of a for loop for (const Token* tok = var->typeStartToken(); tok; tok = tok->previous()) { if (tok->str() == "(") { forHead = true; break; } else if (Token::Match(tok, "[;{}]")) break; } if (forHead) continue; const Token* tok = var->nameToken()->next(); if (Token::Match(tok, "; %varid% = %any% ;", var->declarationId())) { tok = tok->tokAt(3); if (!tok->isNumber() && tok->tokType() != Token::eString && tok->tokType() != Token::eChar && !tok->isBoolean()) continue; } // bailout if initialized with function call that has possible side effects if (Token::Match(tok, "[(=]") && Token::simpleMatch(tok->astOperand2(), "(")) continue; bool reduce = true; bool used = false; // Don't warn about unused variables for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) { if (tok->str() == "{" && tok->scope() != tok->previous()->scope() && !tok->isExpandedMacro() && tok->scope()->type != Scope::eLambda) { if (used) { bool used2 = false; if (!checkInnerScope(tok, var, used2) || used2) { reduce = false; break; } } else if (!checkInnerScope(tok, var, used)) { reduce = false; break; } tok = tok->link(); // parse else if blocks.. } else if (Token::simpleMatch(tok, "else { if (") && Token::simpleMatch(tok->linkAt(3), ") {")) { const Token *endif = tok->linkAt(3)->linkAt(1); bool elseif = false; if (Token::simpleMatch(endif, "} }")) elseif = true; else if (Token::simpleMatch(endif, "} else {") && Token::simpleMatch(endif->linkAt(2),"} }")) elseif = true; if (elseif && Token::findmatch(tok->next(), "%varid%", tok->linkAt(1), var->declarationId())) { reduce = false; break; } } else if (tok->varId() == var->declarationId() || tok->str() == "goto") { reduce = false; break; } } if (reduce && used) variableScopeError(var->nameToken(), var->name()); } } bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) { const Scope* scope = tok->next()->scope(); bool loopVariable = scope->isLoopScope(); bool noContinue = true; const Token* forHeadEnd = nullptr; const Token* end = tok->link(); if (scope->type == Scope::eUnconditional && (tok->strAt(-1) == ")" || tok->previous()->isName())) // Might be an unknown macro like BOOST_FOREACH loopVariable = true; if (scope->type == Scope::eDo) { end = end->linkAt(2); } else if (loopVariable && tok->strAt(-1) == ")") { tok = tok->linkAt(-1); // Jump to opening ( of for/while statement } else if (scope->type == Scope::eSwitch) { for (const Scope* innerScope : scope->nestedList) { if (used) { bool used2 = false; if (!checkInnerScope(innerScope->bodyStart, var, used2) || used2) { return false; } } else if (!checkInnerScope(innerScope->bodyStart, var, used)) { return false; } } } bool bFirstAssignment=false; for (; tok && tok != end; tok = tok->next()) { if (tok->str() == "goto") return false; if (tok->str() == "continue") noContinue = false; if (Token::simpleMatch(tok, "for (")) forHeadEnd = tok->linkAt(1); if (tok == forHeadEnd) forHeadEnd = nullptr; if (loopVariable && noContinue && tok->scope() == scope && !forHeadEnd && scope->type != Scope::eSwitch && Token::Match(tok, "%varid% =", var->declarationId())) { // Assigned in outer scope. loopVariable = false; int indent = 0; for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { // Ensure that variable isn't used on right side of =, too if (tok2->str() == "(") indent++; else if (tok2->str() == ")") { if (indent == 0) break; indent--; } else if (tok2->str() == ";") break; else if (tok2->varId() == var->declarationId()) { loopVariable = true; break; } } } if (loopVariable && Token::Match(tok, "%varid% !!=", var->declarationId())) // Variable used in loop return false; if (Token::Match(tok, "& %varid%", var->declarationId())) // Taking address of variable return false; if (Token::Match(tok, "%varid% =", var->declarationId())) bFirstAssignment = true; if (!bFirstAssignment && Token::Match(tok, "* %varid%", var->declarationId())) // dereferencing means access to previous content return false; if (Token::Match(tok, "= %varid%", var->declarationId()) && (var->isArray() || var->isPointer())) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope return false; if (tok->varId() == var->declarationId()) { used = true; if (scope->type == Scope::eSwitch && scope == tok->scope()) return false; // Used in outer switch scope - unsafe or impossible to reduce scope } } return true; } void CheckOther::variableScopeError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "variableScope", "$symbol:" + varname + "\n" "The scope of the variable '$symbol' can be reduced.\n" "The scope of the variable '$symbol' can be reduced. Warning: Be careful " "when fixing this message, especially when there are inner loops. Here is an " "example where cppcheck will write that the scope for 'i' can be reduced:\n" "void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {\n" " // it's safe to move 'int i = 0;' here\n" " for (int n = 0; n < 10; ++n) {\n" " // it is possible but not safe to move 'int i = 0;' here\n" " do_something(&i);\n" " }\n" " }\n" "}\n" "When you see this message it is always safe to reduce the variable scope 1 level.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // Comma in return statement: return a+1, b++;. (experimental) //--------------------------------------------------------------------------- void CheckOther::checkCommaSeparatedReturn() { // This is experimental for now. See #5076 if (!mSettings->certainty.isEnabled(Certainty::experimental)) return; if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "return") { tok = tok->next(); while (tok && tok->str() != ";") { if (tok->link() && Token::Match(tok, "[([{<]")) tok = tok->link(); if (!tok->isExpandedMacro() && tok->str() == "," && tok->linenr() != tok->next()->linenr()) commaSeparatedReturnError(tok); tok = tok->next(); } // bailout: missing semicolon (invalid code / bad tokenizer) if (!tok) break; } } } void CheckOther::commaSeparatedReturnError(const Token *tok) { reportError(tok, Severity::style, "commaSeparatedReturn", "Comma is used in return statement. The comma can easily be misread as a ';'.\n" "Comma is used in return statement. When comma is used in a return statement it can " "easily be misread as a semicolon. For example in the code below the value " "of 'b' is returned if the condition is true, but it is easy to think that 'a+1' is " "returned:\n" " if (x)\n" " return a + 1,\n" " b++;\n" "However it can be useful to use comma in macros. Cppcheck does not warn when such a " "macro is then used in a return statement, it is less likely such code is misunderstood.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // Check for function parameters that should be passed by const reference //--------------------------------------------------------------------------- static int estimateSize(const Type* type, const Settings* settings, const SymbolDatabase* symbolDatabase, int recursionDepth = 0) { if (recursionDepth > 20) return 0; int cumulatedSize = 0; const bool isUnion = type->classScope->type == Scope::ScopeType::eUnion; const auto accumulateSize = [](int& cumulatedSize, int size, bool isUnion) -> void { if (isUnion) cumulatedSize = std::max(cumulatedSize, size); else cumulatedSize += size; }; for (const Variable&var : type->classScope->varlist) { int size = 0; if (var.isStatic()) continue; if (var.isPointer() || var.isReference()) size = settings->sizeof_pointer; else if (var.type() && var.type()->classScope) size = estimateSize(var.type(), settings, symbolDatabase, recursionDepth+1); else if (var.valueType()->type == ValueType::Type::CONTAINER) size = 3 * settings->sizeof_pointer; // Just guess else size = symbolDatabase->sizeOfType(var.typeStartToken()); if (var.isArray()) size *= std::accumulate(var.dimensions().begin(), var.dimensions().end(), 1, [](int v, const Dimension& d) { return v *= d.num; }); accumulateSize(cumulatedSize, size, isUnion); } for (const Type::BaseInfo &baseInfo : type->derivedFrom) { if (baseInfo.type && baseInfo.type->classScope) cumulatedSize += estimateSize(baseInfo.type, settings, symbolDatabase, recursionDepth+1); } return cumulatedSize; } static bool canBeConst(const Variable *var, const Settings* settings) { { // check initializer list. If variable is moved from it can't be const. const Function* func_scope = var->scope()->function; if (func_scope->type == Function::Type::eConstructor) { //could be initialized in initializer list if (func_scope->arg->link()->next()->str() == ":") { for (const Token* tok2 = func_scope->arg->link()->next()->next(); tok2 != var->scope()->bodyStart; tok2 = tok2->next()) { if (tok2->varId() != var->declarationId()) continue; const Token* parent = tok2->astParent(); if (parent && Token::simpleMatch(parent->previous(), "move (")) return false; } } } } for (const Token* tok2 = var->scope()->bodyStart; tok2 != var->scope()->bodyEnd; tok2 = tok2->next()) { if (tok2->varId() != var->declarationId()) continue; const Token* parent = tok2->astParent(); if (!parent) continue; if (parent->str() == "<<" || isLikelyStreamRead(true, parent)) { if (parent->str() == "<<" && parent->astOperand1() == tok2) return false; if (parent->str() == ">>" && parent->astOperand2() == tok2) return false; } else if (parent->str() == "," || parent->str() == "(") { // function argument const Token* tok3 = tok2->previous(); int argNr = 0; while (tok3 && tok3->str() != "(") { if (tok3->link() && Token::Match(tok3, ")|]|}|>")) tok3 = tok3->link(); else if (tok3->link()) break; else if (tok3->str() == ";") break; else if (tok3->str() == ",") argNr++; tok3 = tok3->previous(); } if (!tok3 || tok3->str() != "(") return false; const Token* functionTok = tok3->astOperand1(); if (!functionTok) return false; const Function* tokFunction = functionTok->function(); if (!tokFunction && functionTok->str() == "." && (functionTok = functionTok->astOperand2())) tokFunction = functionTok->function(); if (tokFunction) { const Variable* argVar = tokFunction->getArgumentVar(argNr); if (!argVar || (!argVar->isConst() && argVar->isReference())) return false; } else if (!settings->library.isFunctionConst(functionTok)) return false; } else if (parent->isUnaryOp("&")) { // TODO: check how pointer is used return false; } else if (parent->isConstOp() || (parent->astOperand2() && settings->library.isFunctionConst(parent->astOperand2()))) continue; else if (parent->isAssignmentOp()) { if (parent->astOperand1() == tok2) return false; const Variable* assignedVar = parent->astOperand1() ? parent->astOperand1()->variable() : nullptr; if (assignedVar && !assignedVar->isConst() && assignedVar->isReference() && assignedVar->nameToken() == parent->astOperand1()) return false; } else if (Token::Match(tok2, "%var% . %name% (")) { const Function* func = tok2->tokAt(2)->function(); if (func && (func->isConst() || func->isStatic())) continue; else return false; } else return false; } return true; } void CheckOther::checkPassByReference() { if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC()) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->isArgument() || !var->isClass() || var->isPointer() || var->isArray() || var->isReference() || var->isEnumType()) continue; if (var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...") continue; // references could not be used as va_start parameters (#5824) if ((var->declEndToken() && var->declEndToken()->isExternC()) || (var->scope() && var->scope()->function && var->scope()->function->tokenDef && var->scope()->function->tokenDef->isExternC())) continue; // references cannot be used in functions in extern "C" blocks bool inconclusive = false; if (var->valueType() && var->valueType()->type == ValueType::Type::CONTAINER) {} else if (var->type() && !var->type()->isEnumType()) { // Check if type is a struct or class. // Ensure that it is a large object. if (!var->type()->classScope) inconclusive = true; else if (estimateSize(var->type(), mSettings, symbolDatabase) <= 2 * mSettings->sizeof_pointer) continue; } else continue; if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive)) continue; const bool isConst = var->isConst(); if (isConst) { passedByValueError(var->nameToken(), var->name(), inconclusive); continue; } // Check if variable could be const if (!var->scope() || var->scope()->function->hasVirtualSpecifier()) continue; if (canBeConst(var, mSettings)) { passedByValueError(var->nameToken(), var->name(), inconclusive); } } } void CheckOther::passedByValueError(const Token *tok, const std::string &parname, bool inconclusive) { reportError(tok, Severity::performance, "passedByValue", "$symbol:" + parname + "\n" "Function parameter '$symbol' should be passed by const reference.\n" "Parameter '$symbol' is passed by value. It could be passed " "as a const reference which is usually faster and recommended in C++.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } static bool isUnusedVariable(const Variable *var) { if (!var) return false; if (!var->scope()) return false; const Token *start = var->declEndToken(); if (!start) return false; if (Token::Match(start, "; %varid% =", var->declarationId())) start = start->tokAt(2); return !Token::findmatch(start->next(), "%varid%", var->scope()->bodyEnd, var->declarationId()); } static bool isVariableMutableInInitializer(const Token* start, const Token * end, nonneg int varid) { if (!start) return false; if (!end) return false; for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->varId() != varid) continue; if (tok->astParent()) { const Token * memberTok = tok->astParent()->previous(); if (Token::Match(memberTok, "%var% (") && memberTok->variable()) { const Variable * memberVar = memberTok->variable(); if (memberVar->isClass()) //TODO: check if the called constructor could live with a const variable // pending that, assume the worst (that it can't) return true; if (!memberVar->isReference()) continue; if (memberVar->isConst()) continue; } return true; } else { return true; } } return false; } void CheckOther::checkConstVariable() { if (!mSettings->severity.isEnabled(Severity::style) || mTokenizer->isC()) return; const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable *var : symbolDatabase->variableList()) { if (!var) continue; if (!var->isReference()) continue; if (var->isRValueReference()) continue; if (var->isPointer()) continue; if (var->isConst()) continue; if (!var->scope()) continue; const Scope *scope = var->scope(); if (!scope->function) continue; const Function *function = scope->function; if (var->isArgument()) { if (function->isImplicitlyVirtual() || function->templateDef) continue; if (isUnusedVariable(var)) continue; if (function->isConstructor() && isVariableMutableInInitializer(function->constructorMemberInitialization(), scope->bodyStart, var->declarationId())) continue; } if (var->isGlobal()) continue; if (var->isStatic()) continue; if (var->isArray()) continue; if (var->isEnumType()) continue; if (var->isVolatile()) continue; if (isAliased(var)) continue; if (isVariableChanged(var, mSettings, mTokenizer->isCPP())) continue; if (Function::returnsReference(function) && !Function::returnsConst(function)) { std::vector returns = Function::findReturns(function); if (std::any_of(returns.begin(), returns.end(), [&](const Token* retTok) { if (retTok->varId() == var->declarationId()) return true; while (retTok && retTok->isCast()) retTok = retTok->astOperand2(); while (Token::simpleMatch(retTok, ".")) retTok = retTok->astOperand2(); return hasLifetimeToken(getParentLifetime(retTok), var->nameToken()); })) continue; } // Skip if address is taken if (Token::findmatch(var->nameToken(), "& %varid%", scope->bodyEnd, var->declarationId())) continue; // Skip if another non-const variable is initialized with this variable { //Is it the right side of an initialization of a non-const reference bool usedInAssignment = false; for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) { if (Token::Match(tok, "& %var% = %varid%", var->declarationId())) { const Variable* refvar = tok->next()->variable(); if (refvar && !refvar->isConst() && refvar->nameToken() == tok->next()) { usedInAssignment = true; break; } } } if (usedInAssignment) continue; } // Skip if we ever cast this variable to a pointer/reference to a non-const type { bool castToNonConst = false; for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) { if (tok->isCast()) { if (!tok->valueType()) { castToNonConst = true; // safe guess break; } bool isConst = 0 != (tok->valueType()->constness & (1 << tok->valueType()->pointer)); if (!isConst) { castToNonConst = true; break; } } } if (castToNonConst) continue; } // Do not warn if struct data is changed { bool changeStructData = false; for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) { if (tok->variable() == var && Token::Match(tok, "%var% .")) { const Token *parent = tok; while (Token::simpleMatch(parent->astParent(), ".") && parent == parent->astParent()->astOperand1()) parent = parent->astParent(); if (parent->valueType() && parent->valueType()->pointer > 0 && parent->valueType()->constness == 0 && isVariableChanged(parent, 1, mSettings, mTokenizer->isCPP())) { changeStructData = true; break; } } } if (changeStructData) continue; } // Calling non-const method using non-const reference if (var->isReference()) { bool callNonConstMethod = false; for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) { if (tok->variable() == var && Token::Match(tok, "%var% . * ( & %name% ::")) { const Token *ftok = tok->linkAt(3)->previous(); if (!ftok->function() || !ftok->function()->isConst()) callNonConstMethod = true; break; } } if (callNonConstMethod) continue; } constVariableError(var, function); } } void CheckOther::checkConstPointer() { if (!mSettings->severity.isEnabled(Severity::style)) return; std::set pointers; std::set nonConstPointers; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->variable()) continue; if (!tok->variable()->isLocal() && !tok->variable()->isArgument()) continue; if (tok == tok->variable()->nameToken()) continue; if (!tok->valueType()) continue; if (tok->valueType()->pointer == 0 || tok->valueType()->constness > 0) continue; if (nonConstPointers.find(tok->variable()) != nonConstPointers.end()) continue; pointers.insert(tok->variable()); const Token *parent = tok->astParent(); bool deref = false; if (parent && parent->isUnaryOp("*")) deref = true; else if (Token::simpleMatch(parent, "[") && parent->astOperand1() == tok) deref = true; if (deref) { if (Token::Match(parent->astParent(), "%cop%") && !parent->astParent()->isUnaryOp("&") && !parent->astParent()->isUnaryOp("*")) continue; if (Token::simpleMatch(parent->astParent(), "return")) continue; else if (Token::Match(parent->astParent(), "%assign%") && parent == parent->astParent()->astOperand2()) { bool takingRef = false; const Token *lhs = parent->astParent()->astOperand1(); if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs->variable()->nameToken() == lhs) takingRef = true; if (!takingRef) continue; } else if (Token::simpleMatch(parent->astParent(), "[") && parent->astParent()->astOperand2() == parent) continue; } else { if (Token::Match(parent, "%oror%|%comp%|&&|?|!|-")) continue; else if (Token::simpleMatch(parent, "(") && Token::Match(parent->astOperand1(), "if|while")) continue; } nonConstPointers.insert(tok->variable()); } for (const Variable *p: pointers) { if (p->isArgument()) { if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(true) || p->scope()->function->hasVirtualSpecifier()) continue; } if (nonConstPointers.find(p) == nonConstPointers.end()) { const Token *start = (p->isArgument()) ? p->scope()->bodyStart : p->nameToken()->next(); const int indirect = p->isArray() ? p->dimensions().size() : 1; if (isVariableChanged(start, p->scope()->bodyEnd, indirect, p->declarationId(), false, mSettings, mTokenizer->isCPP())) continue; constVariableError(p, nullptr); } } } void CheckOther::constVariableError(const Variable *var, const Function *function) { const std::string vartype((var && var->isArgument()) ? "Parameter" : "Variable"); const std::string varname(var ? var->name() : std::string("x")); ErrorPath errorPath; std::string id = "const" + vartype; std::string message = "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared with const"; errorPath.push_back(ErrorPathItem(var ? var->nameToken() : nullptr, message)); if (var && var->isArgument() && function && function->functionPointerUsage) { errorPath.push_front(ErrorPathItem(function->functionPointerUsage, "You might need to cast the function pointer here")); id += "Callback"; message += ". However it seems that '" + function->name() + "' is a callback function, if '$symbol' is declared with const you might also need to cast function pointer(s)."; } reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- void CheckOther::checkCharVariable() { const bool warning = mSettings->severity.isEnabled(Severity::warning); const bool portability = mSettings->severity.isEnabled(Severity::portability); if (!warning && !portability) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%var% [")) { if (!tok->variable()) continue; if (!tok->variable()->isArray() && !tok->variable()->isPointer()) continue; const Token *index = tok->next()->astOperand2(); if (warning && tok->variable()->isArray() && astIsSignedChar(index) && index->getValueGE(0x80, mSettings)) signedCharArrayIndexError(tok); if (portability && astIsUnknownSignChar(index) && index->getValueGE(0x80, mSettings)) unknownSignCharArrayIndexError(tok); } else if (warning && Token::Match(tok, "[&|^]") && tok->isBinaryOp()) { bool warn = false; if (astIsSignedChar(tok->astOperand1())) { const ValueFlow::Value *v1 = tok->astOperand1()->getValueLE(-1, mSettings); const ValueFlow::Value *v2 = tok->astOperand2()->getMaxValue(false); if (!v1) v1 = tok->astOperand1()->getValueGE(0x80, mSettings); if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) warn = true; } else if (astIsSignedChar(tok->astOperand2())) { const ValueFlow::Value *v1 = tok->astOperand2()->getValueLE(-1, mSettings); const ValueFlow::Value *v2 = tok->astOperand1()->getMaxValue(false); if (!v1) v1 = tok->astOperand2()->getValueGE(0x80, mSettings); if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) warn = true; } // is the result stored in a short|int|long? if (warn && Token::simpleMatch(tok->astParent(), "=")) { const Token *lhs = tok->astParent()->astOperand1(); if (lhs && lhs->valueType() && lhs->valueType()->type >= ValueType::Type::SHORT) charBitOpError(tok); // This is an error.. } } } } } void CheckOther::signedCharArrayIndexError(const Token *tok) { reportError(tok, Severity::warning, "signedCharArrayIndex", "Signed 'char' type used as array index.\n" "Signed 'char' type used as array index. If the value " "can be greater than 127 there will be a buffer underflow " "because of sign extension.", CWE128, Certainty::normal); } void CheckOther::unknownSignCharArrayIndexError(const Token *tok) { reportError(tok, Severity::portability, "unknownSignCharArrayIndex", "'char' type used as array index.\n" "'char' type used as array index. Values greater than 127 will be " "treated depending on whether 'char' is signed or unsigned on target platform.", CWE758, Certainty::normal); } void CheckOther::charBitOpError(const Token *tok) { reportError(tok, Severity::warning, "charBitOp", "When using 'char' variables in bit operations, sign extension can generate unexpected results.\n" "When using 'char' variables in bit operations, sign extension can generate unexpected results. For example:\n" " char c = 0x80;\n" " int i = 0 | c;\n" " if (i & 0x8000)\n" " printf(\"not expected\");\n" "The \"not expected\" will be printed on the screen.", CWE398, Certainty::normal); } //--------------------------------------------------------------------------- // Incomplete statement.. //--------------------------------------------------------------------------- static bool isType(const Token * tok, bool unknown) { if (Token::Match(tok, "%type%")) return true; if (Token::simpleMatch(tok, "::")) return isType(tok->astOperand2(), unknown); if (Token::simpleMatch(tok, "<") && tok->link()) return true; if (unknown && Token::Match(tok, "%name% !!(")) return true; return false; } static bool isVarDeclOp(const Token* tok) { if (!tok) return false; const Token * vartok = tok->astOperand2(); if (vartok && vartok->variable() && vartok->variable()->nameToken() == vartok) return true; const Token * typetok = tok->astOperand1(); return isType(typetok, Token::Match(vartok, "%var%")); } static bool isConstStatement(const Token *tok) { if (!tok) return false; if (tok->isExpandedMacro()) return false; if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL")) return true; if (Token::Match(tok, "%var%")) return true; if (Token::Match(tok, "*|&|&&") && (Token::Match(tok->previous(), "::|.|const|volatile|restrict") || isVarDeclOp(tok))) return false; if (Token::Match(tok, "<<|>>") && !astIsIntegral(tok, false)) return false; if (Token::Match(tok, "!|~|%cop%") && (tok->astOperand1() || tok->astOperand2())) return true; if (Token::simpleMatch(tok->previous(), "sizeof (")) return true; if (isCPPCast(tok)) return isConstStatement(tok->astOperand2()); if (Token::Match(tok, "( %type%")) return isConstStatement(tok->astOperand1()); if (Token::simpleMatch(tok, ",")) return isConstStatement(tok->astOperand2()); return false; } static bool isVoidStmt(const Token *tok) { if (Token::simpleMatch(tok, "( void")) return true; const Token *tok2 = tok; while (tok2->astOperand1()) tok2 = tok2->astOperand1(); if (Token::simpleMatch(tok2->previous(), ")") && Token::simpleMatch(tok2->previous()->link(), "( void")) return true; if (Token::simpleMatch(tok2, "( void")) return true; return Token::Match(tok2->previous(), "delete|throw|return"); } static bool isConstTop(const Token *tok) { if (!tok) return false; if (!tok->astParent()) return true; if (Token::simpleMatch(tok->astParent(), ";") && Token::Match(tok->astTop()->previous(), "for|if (") && Token::simpleMatch(tok->astTop()->astOperand2(), ";")) { if (Token::simpleMatch(tok->astParent()->astParent(), ";")) return tok->astParent()->astOperand2() == tok; else return tok->astParent()->astOperand1() == tok; } return false; } void CheckOther::checkIncompleteStatement() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { const Scope *scope = tok->scope(); if (scope && !scope->isExecutable()) continue; if (!isConstTop(tok)) continue; if (tok->str() == "," && Token::simpleMatch(tok->astTop()->previous(), "for (")) continue; // Do not warn for statement when both lhs and rhs has side effects: // dostuff() || x=213; if (Token::Match(tok, "%oror%|&&")) { bool warn = false; visitAstNodes(tok, [&warn](const Token *child) { if (Token::Match(child, "%oror%|&&")) return ChildrenToVisit::op1_and_op2; if (child->isAssignmentOp()) return ChildrenToVisit::none; if (child->tokType() == Token::Type::eIncDecOp) return ChildrenToVisit::none; if (Token::Match(child->previous(), "%name% (")) return ChildrenToVisit::none; warn = true; return ChildrenToVisit::done; }); if (!warn) continue; } const Token *rtok = nextAfterAstRightmostLeaf(tok); if (!Token::simpleMatch(tok->astParent(), ";") && !Token::simpleMatch(rtok, ";") && !Token::Match(tok->previous(), ";|}|{ %any% ;")) continue; // Skip statement expressions if (Token::simpleMatch(rtok, "; } )")) continue; if (!isConstStatement(tok)) continue; if (isVoidStmt(tok)) continue; if (mTokenizer->isCPP() && tok->str() == "&" && !(tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isIntegral())) // Possible archive continue; bool inconclusive = Token::Match(tok, "%cop%"); if (mSettings->certainty.isEnabled(Certainty::inconclusive) || !inconclusive) constStatementError(tok, tok->isNumber() ? "numeric" : "string", inconclusive); } } void CheckOther::constStatementError(const Token *tok, const std::string &type, bool inconclusive) { const Token *valueTok = tok; while (valueTok && valueTok->isCast()) valueTok = valueTok->astOperand2() ? valueTok->astOperand2() : valueTok->astOperand1(); std::string msg; if (Token::simpleMatch(tok, "==")) msg = "Found suspicious equality comparison. Did you intend to assign a value instead?"; else if (Token::Match(tok, ",|!|~|%cop%")) msg = "Found suspicious operator '" + tok->str() + "'"; else if (Token::Match(tok, "%var%")) msg = "Unused variable value '" + tok->str() + "'"; else if (Token::Match(valueTok, "%str%|%num%")) msg = "Redundant code: Found a statement that begins with " + std::string(valueTok->isNumber() ? "numeric" : "string") + " constant."; else if (!tok) msg = "Redundant code: Found a statement that begins with " + type + " constant."; else return; // Strange! reportError(tok, Severity::warning, "constStatement", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- // Detect division by zero. //--------------------------------------------------------------------------- void CheckOther::checkZeroDivision() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand2() || !tok->astOperand1()) continue; if (tok->str() != "%" && tok->str() != "/" && tok->str() != "%=" && tok->str() != "/=") continue; if (!tok->valueType() || !tok->valueType()->isIntegral()) continue; // Value flow.. const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL); if (value && mSettings->isEnabled(value, false)) zerodivError(tok, value); } } void CheckOther::zerodivError(const Token *tok, const ValueFlow::Value *value) { if (!tok && !value) { reportError(tok, Severity::error, "zerodiv", "Division by zero.", CWE369, Certainty::normal); reportError(tok, Severity::error, "zerodivcond", ValueFlow::eitherTheConditionIsRedundant(nullptr) + " or there is division by zero.", CWE369, Certainty::normal); return; } const ErrorPath errorPath = getErrorPath(tok, value, "Division by zero"); std::ostringstream errmsg; if (value->condition) { const int line = tok ? tok->linenr() : 0; errmsg << ValueFlow::eitherTheConditionIsRedundant(value->condition) << " or there is division by zero at line " << line << "."; } else errmsg << "Division by zero."; reportError(errorPath, value->errorSeverity() ? Severity::error : Severity::warning, value->condition ? "zerodivcond" : "zerodiv", errmsg.str(), CWE369, value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- // Check for NaN (not-a-number) in an arithmetic expression, e.g. // double d = 1.0 / 0.0 + 100.0; //--------------------------------------------------------------------------- void CheckOther::checkNanInArithmeticExpression() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "/") continue; if (!Token::Match(tok->astParent(), "[+-]")) continue; if (Token::simpleMatch(tok->astOperand2(), "0.0")) nanInArithmeticExpressionError(tok); } } void CheckOther::nanInArithmeticExpressionError(const Token *tok) { reportError(tok, Severity::style, "nanInArithmeticExpression", "Using NaN/Inf in a computation.\n" "Using NaN/Inf in a computation. " "Although nothing bad really happens, it is suspicious.", CWE369, Certainty::normal); } //--------------------------------------------------------------------------- // Creating instance of classes which are destroyed immediately //--------------------------------------------------------------------------- void CheckOther::checkMisusedScopedObject() { // Skip this check for .c files if (mTokenizer->isC()) return; if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if ((tok->next()->type() || (tok->next()->function() && tok->next()->function()->isConstructor())) // TODO: The rhs of || should be removed; It is a workaround for a symboldatabase bug && Token::Match(tok, "[;{}] %name% (") && Token::Match(tok->linkAt(2), ") ; !!}") && (!tok->next()->function() || // is not a function on this scope tok->next()->function()->isConstructor())) { // or is function in this scope and it's a ctor tok = tok->next(); misusedScopeObjectError(tok, tok->str()); tok = tok->next(); } } } } void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname) { reportError(tok, Severity::style, "unusedScopedObject", "$symbol:" + varname + "\n" "Instance of '$symbol' object is destroyed immediately.", CWE563, Certainty::normal); } static const Token * getSingleExpressionInBlock(const Token * tok) { if (!tok) return nullptr; const Token * top = tok->astTop(); if (!top) return nullptr; const Token * nextExpression = nextAfterAstRightmostLeaf(top); if (!Token::simpleMatch(nextExpression, "; }")) return nullptr; return top; } //----------------------------------------------------------------------------- // check for duplicate code in if and else branches // if (a) { b = true; } else { b = true; } //----------------------------------------------------------------------------- void CheckOther::checkDuplicateBranch() { // This is inconclusive since in practice most warnings are noise: // * There can be unfixed low-priority todos. The code is fine as it // is but it could be possible to enhance it. Writing a warning // here is noise since the code is fine (see cppcheck, abiword, ..) // * There can be overspecified code so some conditions can't be true // and their conditional code is a duplicate of the condition that // is always true just in case it would be false. See for instance // abiword. if (!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope & scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; // check all the code in the function for if (..) else if (Token::simpleMatch(scope.bodyEnd, "} else {")) { // Make sure there are no macros (different macros might be expanded // to the same code) bool macro = false; for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd->linkAt(2); tok = tok->next()) { if (tok->isExpandedMacro()) { macro = true; break; } } if (macro) continue; // save if branch code const std::string branch1 = scope.bodyStart->next()->stringifyList(scope.bodyEnd); if (branch1.empty()) continue; // save else branch code const std::string branch2 = scope.bodyEnd->tokAt(3)->stringifyList(scope.bodyEnd->linkAt(2)); ErrorPath errorPath; // check for duplicates if (branch1 == branch2) { duplicateBranchError(scope.classDef, scope.bodyEnd->next(), errorPath); continue; } // check for duplicates using isSameExpression const Token * branchTop1 = getSingleExpressionInBlock(scope.bodyStart->next()); const Token * branchTop2 = getSingleExpressionInBlock(scope.bodyEnd->tokAt(3)); if (!branchTop1 || !branchTop2) continue; if (branchTop1->str() != branchTop2->str()) continue; if (isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand1(), branchTop2->astOperand1(), mSettings->library, true, true, &errorPath) && isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand2(), branchTop2->astOperand2(), mSettings->library, true, true, &errorPath)) duplicateBranchError(scope.classDef, scope.bodyEnd->next(), errorPath); } } } void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors) { errors.emplace_back(tok2, ""); errors.emplace_back(tok1, ""); reportError(errors, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" "Finding the same code in an 'if' and related 'else' branch is suspicious and " "might indicate a cut and paste or logic error. Please examine this code " "carefully to determine if it is correct.", CWE398, Certainty::inconclusive); } //----------------------------------------------------------------------------- // Check for a free() of an invalid address // char* p = malloc(100); // free(p + 10); //----------------------------------------------------------------------------- void CheckOther::checkInvalidFree() { std::map inconclusive; std::map allocation; const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // Keep track of which variables were assigned addresses to newly-allocated memory if (Token::Match(tok, "%var% = malloc|g_malloc|new")) { allocation.insert(std::make_pair(tok->varId(), tok->strAt(2))); inconclusive.insert(std::make_pair(tok->varId(), false)); } // If a previously-allocated pointer is incremented or decremented, any subsequent // free involving pointer arithmetic may or may not be invalid, so we should only // report an inconclusive result. else if (Token::Match(tok, "%var% = %name% +|-") && tok->varId() == tok->tokAt(2)->varId() && allocation.find(tok->varId()) != allocation.end()) { if (printInconclusive) inconclusive[tok->varId()] = true; else { allocation.erase(tok->varId()); inconclusive.erase(tok->varId()); } } // If a previously-allocated pointer is assigned a completely new value, // we can't know if any subsequent free() on that pointer is valid or not. else if (Token::Match(tok, "%var% =")) { allocation.erase(tok->varId()); inconclusive.erase(tok->varId()); } // If a variable that was previously assigned a newly-allocated memory location is // added or subtracted from when used to free the memory, report an error. else if (Token::Match(tok, "free|g_free|delete ( %any% +|-") || Token::Match(tok, "delete [ ] ( %any% +|-") || Token::Match(tok, "delete %any% +|- %any%")) { const int varIndex = tok->strAt(1) == "(" ? 2 : tok->strAt(3) == "(" ? 4 : 1; const int var1 = tok->tokAt(varIndex)->varId(); const int var2 = tok->tokAt(varIndex + 2)->varId(); const std::map::const_iterator alloc1 = inconclusive.find(var1); const std::map::const_iterator alloc2 = inconclusive.find(var2); if (alloc1 != inconclusive.end()) { invalidFreeError(tok, allocation[var1], alloc1->second); } else if (alloc2 != inconclusive.end()) { invalidFreeError(tok, allocation[var2], alloc2->second); } } // If the previously-allocated variable is passed in to another function // as a parameter, it might be modified, so we shouldn't report an error // if it is later used to free memory else if (Token::Match(tok, "%name% (") && !mSettings->library.isFunctionConst(tok->str(), true)) { const Token* tok2 = Token::findmatch(tok->next(), "%var%", tok->linkAt(1)); while (tok2 != nullptr) { allocation.erase(tok->varId()); inconclusive.erase(tok2->varId()); tok2 = Token::findmatch(tok2->next(), "%var%", tok->linkAt(1)); } } } } } void CheckOther::invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive) { std::string alloc = allocation; if (alloc != "new") alloc += "()"; std::string deallocated = (alloc == "new") ? "deleted" : "freed"; reportError(tok, Severity::error, "invalidFree", "Mismatching address is " + deallocated + ". The address you get from " + alloc + " must be " + deallocated + " without offset.", CWE(0U), inconclusive ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- // check for the same expression on both sides of an operator // (x == x), (x && x), (x || x) // (x.y == x.y), (x.y && x.y), (x.y || x.y) //--------------------------------------------------------------------------- namespace { bool notconst(const Function* func) { return !func->isConst(); } void getConstFunctions(const SymbolDatabase *symbolDatabase, std::list &constFunctions) { for (const Scope &scope : symbolDatabase->scopeList) { // only add const functions that do not have a non-const overloaded version // since it is pretty much impossible to tell which is being called. using StringFunctionMap = std::map>; StringFunctionMap functionsByName; for (const Function &func : scope.functionList) { functionsByName[func.tokenDef->str()].push_back(&func); } for (std::pair>& it : functionsByName) { const std::list::const_iterator nc = std::find_if(it.second.begin(), it.second.end(), notconst); if (nc == it.second.end()) { // ok to add all of them constFunctions.splice(constFunctions.end(), it.second); } } } } } void CheckOther::checkDuplicateExpression() { const bool styleEnabled = mSettings->severity.isEnabled(Severity::style); const bool warningEnabled = mSettings->severity.isEnabled(Severity::warning); if (!styleEnabled && !warningEnabled) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); std::list constFunctions; getConstFunctions(symbolDatabase, constFunctions); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "=" && Token::Match(tok->astOperand1(), "%var%")) { const Token * endStatement = Token::findsimplematch(tok, ";"); if (Token::Match(endStatement, "; %type% %var% ;")) { endStatement = endStatement->tokAt(4); } if (Token::Match(endStatement, "%var% %assign%")) { const Token * nextAssign = endStatement->tokAt(1); const Token * var1 = tok->astOperand1(); const Token * var2 = nextAssign->astOperand1(); if (var1 && var2 && Token::Match(var1->previous(), ";|{|} %var%") && Token::Match(var2->previous(), ";|{|} %var%") && var2->valueType() && var1->valueType() && var2->valueType()->originalTypeName == var1->valueType()->originalTypeName && var2->valueType()->pointer == var1->valueType()->pointer && var2->valueType()->constness == var1->valueType()->constness && var2->varId() != var1->varId() && ( tok->astOperand2()->isArithmeticalOp() || tok->astOperand2()->str() == "." || Token::Match(tok->astOperand2()->previous(), "%name% (") ) && tok->next()->tokType() != Token::eType && isSameExpression(mTokenizer->isCPP(), true, tok->next(), nextAssign->next(), mSettings->library, true, false) && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), nextAssign->astOperand2(), mSettings->library, true, false) && tok->astOperand2()->expressionString() == nextAssign->astOperand2()->expressionString()) { bool differentDomain = false; const Scope * varScope = var1->scope() ? var1->scope() : scope; for (const Token *assignTok = Token::findsimplematch(var2, ";"); assignTok && assignTok != varScope->bodyEnd; assignTok = assignTok->next()) { if (!Token::Match(assignTok, "%assign%|%comp%")) continue; if (!assignTok->astOperand1()) continue; if (!assignTok->astOperand2()) continue; if (assignTok->astOperand1()->varId() != var1->varId() && assignTok->astOperand1()->varId() != var2->varId() && !isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), assignTok->astOperand1(), mSettings->library, true, true)) continue; if (assignTok->astOperand2()->varId() != var1->varId() && assignTok->astOperand2()->varId() != var2->varId() && !isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), assignTok->astOperand2(), mSettings->library, true, true)) continue; differentDomain = true; break; } if (!differentDomain && !isUniqueExpression(tok->astOperand2())) duplicateAssignExpressionError(var1, var2, false); else if (mSettings->certainty.isEnabled(Certainty::inconclusive)) duplicateAssignExpressionError(var1, var2, true); } } } ErrorPath errorPath; if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>|+=|*=|<<=|>>=")) { if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true)) continue; const bool pointerDereference = (tok->astOperand1() && tok->astOperand1()->isUnaryOp("*")) || (tok->astOperand2() && tok->astOperand2()->isUnaryOp("*")); const bool followVar = (!isConstVarExpression(tok) || Token::Match(tok, "%comp%|%oror%|&&")) && !pointerDereference; if (isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, followVar, &errorPath)) { if (isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) { const bool assignment = tok->str() == "="; if (assignment && warningEnabled) selfAssignmentError(tok, tok->astOperand1()->expressionString()); else if (styleEnabled) { if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11 && tok->str() == "==") { const Token* parent = tok->astParent(); while (parent && parent->astParent()) { parent = parent->astParent(); } if (parent && parent->previous() && parent->previous()->str() == "static_assert") { continue; } } duplicateExpressionError(tok->astOperand1(), tok->astOperand2(), tok, errorPath); } } } else if (tok->str() == "=" && Token::simpleMatch(tok->astOperand2(), "=") && isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok->astOperand2()->astOperand1(), mSettings->library, true, false)) { if (warningEnabled && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) { selfAssignmentError(tok, tok->astOperand1()->expressionString()); } } else if (styleEnabled && isOppositeExpression(mTokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), mSettings->library, false, true, &errorPath) && !Token::Match(tok, "=|-|-=|/|/=") && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) { oppositeExpressionError(tok, errorPath); } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), tok->astOperand1()->astOperand2(), mSettings->library, true, followVar, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2())) duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath); else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library, true, mTokenizer->isCPP())) { const Token *ast1 = tok->astOperand1(); while (ast1 && tok->str() == ast1->str()) { if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1()) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2())) // Probably the message should be changed to 'duplicate expressions X in condition or something like that'. duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok, errorPath); ast1 = ast1->astOperand1(); } } } } else if (styleEnabled && tok->astOperand1() && tok->astOperand2() && tok->str() == ":" && tok->astParent() && tok->astParent()->str() == "?") { if (!isVariableChanged(tok->astParent(), /*indirect*/ 0, mSettings, mTokenizer->isCPP()) && !tok->astOperand1()->values().empty() && !tok->astOperand2()->values().empty() && isEqualKnownValue(tok->astOperand1(), tok->astOperand2())) duplicateValueTernaryError(tok); else if (isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, false, true, &errorPath)) duplicateExpressionTernaryError(tok, errorPath); } } } } void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors) { errors.emplace_back(opTok, ""); const std::string& op = opTok ? opTok->str() : "&&"; reportError(errors, Severity::style, "oppositeExpression", "Opposite expression on both sides of \'" + op + "\'.\n" "Finding the opposite expression on both sides of an operator is suspicious and might " "indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct.", CWE398, Certainty::normal); } void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors) { errors.emplace_back(opTok, ""); const std::string& expr1 = tok1 ? tok1->expressionString() : "x"; const std::string& expr2 = tok2 ? tok2->expressionString() : "x"; const std::string& op = opTok ? opTok->str() : "&&"; std::string msg = "Same expression on both sides of \'" + op + "\'"; const char *id = "duplicateExpression"; if (expr1 != expr2 && (!opTok || !opTok->isArithmeticalOp())) { id = "knownConditionTrueFalse"; std::string exprMsg = "The comparison \'" + expr1 + " " + op + " " + expr2 + "\' is always "; if (Token::Match(opTok, "==|>=|<=")) msg = exprMsg + "true"; else if (Token::Match(opTok, "!=|>|<")) msg = exprMsg + "false"; if (!Token::Match(tok1, "%num%|NULL|nullptr") && !Token::Match(tok2, "%num%|NULL|nullptr")) msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value"; } reportError(errors, Severity::style, id, msg + ".\n" "Finding the same expression on both sides of an operator is suspicious and might " "indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct.", CWE398, Certainty::normal); } void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive) { const std::list toks = { tok2, tok1 }; const std::string& var1 = tok1 ? tok1->str() : "x"; const std::string& var2 = tok2 ? tok2->str() : "x"; reportError(toks, Severity::style, "duplicateAssignExpression", "Same expression used in consecutive assignments of '" + var1 + "' and '" + var2 + "'.\n" "Finding variables '" + var1 + "' and '" + var2 + "' that are assigned the same expression " "is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors) { errors.emplace_back(tok, ""); reportError(errors, Severity::style, "duplicateExpressionTernary", "Same expression in both branches of ternary operator.\n" "Finding the same expression in both branches of ternary operator is suspicious as " "the same code is executed regardless of the condition.", CWE398, Certainty::normal); } void CheckOther::duplicateValueTernaryError(const Token *tok) { reportError(tok, Severity::style, "duplicateValueTernary", "Same value in both branches of ternary operator.\n" "Finding the same value in both branches of ternary operator is suspicious as " "the same code is executed regardless of the condition.", CWE398, Certainty::normal); } void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", "$symbol:" + varname + "\n" "Redundant assignment of '$symbol' to itself.", CWE398, Certainty::normal); } //----------------------------------------------------------------------------- // Check is a comparison of two variables leads to condition, which is // always true or false. // For instance: int a = 1; if(isless(a,a)){...} // In this case isless(a,a) always evaluates to false. // // Reference: // - http://www.cplusplus.com/reference/cmath/ //----------------------------------------------------------------------------- void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->isName() && Token::Match(tok, "isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% )")) { const int varidLeft = tok->tokAt(2)->varId();// get the left varid const int varidRight = tok->tokAt(4)->varId();// get the right varid // compare varids: if they are not zero but equal // --> the comparison function is called with the same variables if (varidLeft == varidRight) { const std::string& functionName = tok->str(); // store function name const std::string& varNameLeft = tok->strAt(2); // get the left variable name if (functionName == "isgreater" || functionName == "isless" || functionName == "islessgreater") { // e.g.: isgreater(x,x) --> (x)>(x) --> false checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, false); } else { // functionName == "isgreaterequal" || functionName == "islessequal" // e.g.: isgreaterequal(x,x) --> (x)>=(x) --> true checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, true); } } } } } } void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result) { const std::string strResult = result ? "true" : "false"; const struct CWE cweResult = result ? CWE571 : CWE570; reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse", "$symbol:" + functionName + "\n" "Comparison of two identical variables with $symbol(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n" "The function $symbol is designed to compare two variables. Calling this function with one variable (" + varName + ") " "for both parameters leads to a statement which is always " + strResult + ".", cweResult, Certainty::normal); } //--------------------------------------------------------------------------- // Check testing sign of unsigned variables and pointers. //--------------------------------------------------------------------------- void CheckOther::checkSignOfUnsignedVariable() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // check all the code in the function for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const ValueFlow::Value *zeroValue = nullptr; const Token *nonZeroExpr = nullptr; if (comparisonNonZeroExpressionLessThanZero(tok, &zeroValue, &nonZeroExpr)) { const ValueType* vt = nonZeroExpr->valueType(); if (vt->pointer) pointerLessThanZeroError(tok, zeroValue); else unsignedLessThanZeroError(tok, zeroValue, nonZeroExpr->expressionString()); } else if (testIfNonZeroExpressionIsPositive(tok, &zeroValue, &nonZeroExpr)) { const ValueType* vt = nonZeroExpr->valueType(); if (vt->pointer) pointerPositiveError(tok, zeroValue); else unsignedPositiveError(tok, zeroValue, nonZeroExpr->expressionString()); } } } } bool CheckOther::comparisonNonZeroExpressionLessThanZero(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr) { if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) return false; const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0); const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0); if (Token::Match(tok, "<|<=") && v2 && v2->isKnown()) { *zeroValue = v2; *nonZeroExpr = tok->astOperand1(); } else if (Token::Match(tok, ">|>=") && v1 && v1->isKnown()) { *zeroValue = v1; *nonZeroExpr = tok->astOperand2(); } else { return false; } const ValueType* vt = (*nonZeroExpr)->valueType(); return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED); } bool CheckOther::testIfNonZeroExpressionIsPositive(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr) { if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) return false; const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0); const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0); if (Token::simpleMatch(tok, ">=") && v2 && v2->isKnown()) { *zeroValue = v2; *nonZeroExpr = tok->astOperand1(); } else if (Token::simpleMatch(tok, "<=") && v1 && v1->isKnown()) { *zeroValue = v1; *nonZeroExpr = tok->astOperand2(); } else { return false; } const ValueType* vt = (*nonZeroExpr)->valueType(); return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED); } void CheckOther::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) { reportError(getErrorPath(tok, v, "Unsigned less than zero"), Severity::style, "unsignedLessThanZero", "$symbol:" + varname + "\n" "Checking if unsigned expression '$symbol' is less than zero.\n" "The unsigned expression '$symbol' will never be negative so it " "is either pointless or an error to check if it is.", CWE570, Certainty::normal); } void CheckOther::pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v) { reportError(getErrorPath(tok, v, "Pointer less than zero"), Severity::style, "pointerLessThanZero", "A pointer can not be negative so it is either pointless or an error to check if it is.", CWE570, Certainty::normal); } void CheckOther::unsignedPositiveError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) { reportError(getErrorPath(tok, v, "Unsigned positive"), Severity::style, "unsignedPositive", "$symbol:" + varname + "\n" "Unsigned expression '$symbol' can't be negative so it is unnecessary to test it.", CWE570, Certainty::normal); } void CheckOther::pointerPositiveError(const Token *tok, const ValueFlow::Value * v) { reportError(getErrorPath(tok, v, "Pointer positive"), Severity::style, "pointerPositive", "A pointer can not be negative so it is either pointless or an error to check if it is not.", CWE570, Certainty::normal); } /* check if a constructor in given class scope takes a reference */ static bool constructorTakesReference(const Scope * const classScope) { for (const Function &constructor : classScope->functionList) { if (constructor.isConstructor()) { for (int argnr = 0U; argnr < constructor.argCount(); argnr++) { const Variable * const argVar = constructor.getArgumentVar(argnr); if (argVar && argVar->isReference()) { return true; } } } } return false; } //--------------------------------------------------------------------------- // This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &". // In most scenarios, "const A & a = getA()" will be more efficient. //--------------------------------------------------------------------------- void CheckOther::checkRedundantCopy() { if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC() || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || var->isReference() || !var->isConst() || var->isPointer() || (!var->type() && !var->isStlType())) // bailout if var is of standard type, if it is a pointer or non-const continue; const Token* startTok = var->nameToken(); if (startTok->strAt(1) == "=") // %type% %name% = ... ; ; else if (startTok->strAt(1) == "(" && var->isClass() && var->typeScope()) { // Object is instantiated. Warn if constructor takes arguments by value. if (constructorTakesReference(var->typeScope())) continue; } else continue; const Token* tok = startTok->next()->astOperand2(); if (!tok) continue; if (!Token::Match(tok->previous(), "%name% (")) continue; if (!Token::Match(tok->link(), ") )| ;")) // bailout for usage like "const A a = getA()+3" continue; const Function* func = tok->previous()->function(); if (func && func->tokenDef->strAt(-1) == "&") { redundantCopyError(startTok, startTok->str()); } } } void CheckOther::redundantCopyError(const Token *tok,const std::string& varname) { reportError(tok, Severity::performance, "redundantCopyLocalConst", "$symbol:" + varname + "\n" "Use const reference for '$symbol' to avoid unnecessary data copying.\n" "The const variable '$symbol' is assigned a copy of the data. You can avoid " "the unnecessary data copying by converting '$symbol' to const reference.", CWE398, Certainty::inconclusive); // since #5618 that check became inconclusive } //--------------------------------------------------------------------------- // Checking for shift by negative values //--------------------------------------------------------------------------- static bool isNegative(const Token *tok, const Settings *settings) { return tok->valueType() && tok->valueType()->sign == ValueType::SIGNED && tok->getValueLE(-1LL, settings); } void CheckOther::checkNegativeBitwiseShift() { const bool portability = mSettings->severity.isEnabled(Severity::portability); for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand1() || !tok->astOperand2()) continue; if (!Token::Match(tok, "<<|>>|<<=|>>=")) continue; // don't warn if lhs is a class. this is an overloaded operator then if (mTokenizer->isCPP()) { const ValueType * lhsType = tok->astOperand1()->valueType(); if (!lhsType || !lhsType->isIntegral()) continue; } // bailout if operation is protected by ?: bool ternary = false; for (const Token *parent = tok; parent; parent = parent->astParent()) { if (Token::Match(parent, "?|:")) { ternary = true; break; } } if (ternary) continue; // Get negative rhs value. preferably a value which doesn't have 'condition'. if (portability && isNegative(tok->astOperand1(), mSettings)) negativeBitwiseShiftError(tok, 1); else if (isNegative(tok->astOperand2(), mSettings)) negativeBitwiseShiftError(tok, 2); } } void CheckOther::negativeBitwiseShiftError(const Token *tok, int op) { if (op == 1) // LHS - this is used by intention in various software, if it // is used often in a project and works as expected then this is // a portability issue reportError(tok, Severity::portability, "shiftNegativeLHS", "Shifting a negative value is technically undefined behaviour", CWE758, Certainty::normal); else // RHS reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value is undefined behaviour", CWE758, Certainty::normal); } //--------------------------------------------------------------------------- // Check for incompletely filled buffers. //--------------------------------------------------------------------------- void CheckOther::checkIncompleteArrayFill() { if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) return; const bool printWarning = mSettings->severity.isEnabled(Severity::warning); const bool printPortability = mSettings->severity.isEnabled(Severity::portability); if (!printPortability && !printWarning) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "memset|memcpy|memmove ( %var% ,") && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )")) { const Variable *var = tok->tokAt(2)->variable(); if (!var || !var->isArray() || var->dimensions().empty() || !var->dimension(0)) continue; if (MathLib::toLongNumber(tok->linkAt(1)->strAt(-1)) == var->dimension(0)) { int size = mTokenizer->sizeOfType(var->typeStartToken()); if (size == 0 && var->valueType()->pointer) size = mSettings->sizeof_pointer; else if (size == 0 && var->type()) size = estimateSize(var->type(), mSettings, symbolDatabase); if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { if (printWarning) incompleteArrayFillError(tok, var->name(), tok->str(), false); } else if (var->valueType()->type == ValueType::Type::BOOL && printPortability) // sizeof(bool) is not 1 on all platforms incompleteArrayFillError(tok, var->name(), tok->str(), true); } } } } } void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) { if (boolean) reportError(tok, Severity::portability, "incompleteArrayFill", "$symbol:" + buffer + "\n" "$symbol:" + function + "\n" "Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive); else reportError(tok, Severity::warning, "incompleteArrayFill", "$symbol:" + buffer + "\n" "$symbol:" + function + "\n" "Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive); } //--------------------------------------------------------------------------- // Detect NULL being passed to variadic function. //--------------------------------------------------------------------------- void CheckOther::checkVarFuncNullUB() { if (!mSettings->severity.isEnabled(Severity::portability)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { // Is NULL passed to a function? if (Token::Match(tok,"[(,] NULL [,)]")) { // Locate function name in this function call. const Token *ftok = tok; int argnr = 1; while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); else if (ftok->str() == ",") ++argnr; ftok = ftok->previous(); } ftok = ftok ? ftok->previous() : nullptr; if (ftok && ftok->isName()) { // If this is a variadic function then report error const Function *f = ftok->function(); if (f && f->argCount() <= argnr) { const Token *tok2 = f->argDef; tok2 = tok2 ? tok2->link() : nullptr; // goto ')' if (tok2 && Token::simpleMatch(tok2->tokAt(-1), "...")) varFuncNullUBError(tok); } } } } } } void CheckOther::varFuncNullUBError(const Token *tok) { reportError(tok, Severity::portability, "varFuncNullUB", "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" "The C99 standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined.\n" "The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL.\n" "In practice on common architectures, this will cause real crashes if sizeof(int) != sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int.\n" "To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \"ERROR\", the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \"ERROR\" output go away.\n" "#include \n" "#include \n" "\n" "void f(char *s, ...) {\n" " va_list ap;\n" " va_start(ap,s);\n" " for (;;) {\n" " char *p = va_arg(ap,char*);\n" " printf(\"%018p, %s\\n\", p, (long)p & 255 ? p : \"\");\n" " if(!p) break;\n" " }\n" " va_end(ap);\n" "}\n" "\n" "void g() {\n" " char *s2 = \"x\";\n" " char *s3 = \"ERROR\";\n" "\n" " // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64\n" " f(\"first\", s2, s2, s2, s2, s2, 0, s3, (char*)0);\n" "}\n" "\n" "void h() {\n" " int i;\n" " volatile unsigned char a[1000];\n" " for (i = 0; iseverity.isEnabled(Severity::style)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->isExpandedMacro() && tok->str() == "(") tok = tok->link(); if (!tok->isUnaryOp("&") || !tok->astOperand1()->isUnaryOp("*")) continue; // variable const Token *varTok = tok->astOperand1()->astOperand1(); if (!varTok || varTok->isExpandedMacro()) continue; const Variable *var = varTok->variable(); if (!var || !var->isPointer()) continue; redundantPointerOpError(tok, var->name(), false); } } void CheckOther::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive) { reportError(tok, Severity::style, "redundantPointerOp", "$symbol:" + varname + "\n" "Redundant pointer operation on '$symbol' - it's already a pointer.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckOther::checkInterlockedDecrement() { if (!mSettings->isWindowsPlatform()) { return; } for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->isName() && Token::Match(tok, "InterlockedDecrement ( & %name% ) ; if ( %name%|!|0")) { const Token* interlockedVarTok = tok->tokAt(3); const Token* checkStartTok = interlockedVarTok->tokAt(5); if ((Token::Match(checkStartTok, "0 %comp% %name% )") && checkStartTok->strAt(2) == interlockedVarTok->str()) || (Token::Match(checkStartTok, "! %name% )") && checkStartTok->strAt(1) == interlockedVarTok->str()) || (Token::Match(checkStartTok, "%name% )") && checkStartTok->str() == interlockedVarTok->str()) || (Token::Match(checkStartTok, "%name% %comp% 0 )") && checkStartTok->str() == interlockedVarTok->str())) { raceAfterInterlockedDecrementError(checkStartTok); } } else if (Token::Match(tok, "if ( ::| InterlockedDecrement ( & %name%")) { const Token* condEnd = tok->next()->link(); const Token* funcTok = tok->tokAt(2); const Token* firstAccessTok = funcTok->str() == "::" ? funcTok->tokAt(4) : funcTok->tokAt(3); if (condEnd && condEnd->next() && condEnd->next()->link()) { const Token* ifEndTok = condEnd->next()->link(); if (Token::Match(ifEndTok, "} return %name%")) { const Token* secondAccessTok = ifEndTok->tokAt(2); if (secondAccessTok->str() == firstAccessTok->str()) { raceAfterInterlockedDecrementError(secondAccessTok); } } else if (Token::Match(ifEndTok, "} else { return %name%")) { const Token* secondAccessTok = ifEndTok->tokAt(4); if (secondAccessTok->str() == firstAccessTok->str()) { raceAfterInterlockedDecrementError(secondAccessTok); } } } } } } void CheckOther::raceAfterInterlockedDecrementError(const Token* tok) { reportError(tok, Severity::error, "raceAfterInterlockedDecrement", "Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.", CWE362, Certainty::normal); } void CheckOther::checkUnusedLabel() { if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const bool hasIfdef = mTokenizer->hasIfdef(scope->bodyStart, scope->bodyEnd); for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!tok->scope()->isExecutable()) tok = tok->scope()->bodyEnd; if (Token::Match(tok, "{|}|; %name% :") && tok->strAt(1) != "default") { const std::string tmp("goto " + tok->strAt(1)); if (!Token::findsimplematch(scope->bodyStart->next(), tmp.c_str(), tmp.size(), scope->bodyEnd->previous())) unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch, hasIfdef); } } } } void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef) { if (tok && !mSettings->severity.isEnabled(inSwitch ? Severity::warning : Severity::style)) return; std::string id = "unusedLabel"; if (inSwitch) id += "Switch"; if (hasIfdef) id += "Configuration"; std::string msg = "$symbol:" + (tok ? tok->str() : emptyString) + "\nLabel '$symbol' is not used."; if (hasIfdef) msg += " There is #if in function body so the label might be used in code that is removed by the preprocessor."; if (inSwitch) msg += " Should this be a 'case' of the enclosing switch()?"; reportError(tok, inSwitch ? Severity::warning : Severity::style, id, msg, CWE398, Certainty::normal); } void CheckOther::checkEvaluationOrder() { // This checker is not written according to C++11 sequencing rules if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * functionScope : symbolDatabase->functionScopes) { for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "++|--") && !tok->isAssignmentOp()) continue; if (!tok->astOperand1()) continue; for (const Token *tok2 = tok;; tok2 = tok2->astParent()) { // If ast parent is a sequence point then break const Token * const parent = tok2->astParent(); if (!parent) break; if (Token::Match(parent, "%oror%|&&|?|:|;")) break; if (parent->str() == ",") { const Token *par = parent; while (Token::simpleMatch(par,",")) par = par->astParent(); // not function or in a while clause => break if (!(par && par->str() == "(" && par->astOperand2() && par->strAt(-1) != "while")) break; // control flow (if|while|etc) => break if (Token::simpleMatch(par->link(),") {")) break; // sequence point in function argument: dostuff((1,2),3) => break par = par->next(); while (par && (par->previous() != parent)) par = par->nextArgument(); if (!par) break; } if (parent->str() == "(" && parent->astOperand2()) break; // self assignment.. if (tok2 == tok && tok->str() == "=" && parent->str() == "=" && isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) { if (mSettings->severity.isEnabled(Severity::warning) && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) selfAssignmentError(parent, tok->astOperand1()->expressionString()); break; } // Is expression used? bool foundError = false; visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { if (tok3->str() == "&" && !tok3->astOperand2()) return ChildrenToVisit::none; // don't handle address-of for now if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) return ChildrenToVisit::none; // don't care about sizeof usage if (isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok3, mSettings->library, true, false)) foundError = true; return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (foundError) { unknownEvaluationOrder(parent); break; } } } } } void CheckOther::unknownEvaluationOrder(const Token* tok) { reportError(tok, Severity::error, "unknownEvaluationOrder", "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, Certainty::normal); } void CheckOther::checkAccessOfMovedVariable() { if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11 || !mSettings->severity.isEnabled(Severity::warning)) return; CheckUninitVar checkUninitVar(mTokenizer, mSettings, mErrorLogger); const bool reportInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const Token * scopeStart = scope->bodyStart; if (scope->function) { const Token * memberInitializationStart = scope->function->constructorMemberInitialization(); if (memberInitializationStart) scopeStart = memberInitializationStart; } for (const Token* tok = scopeStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const ValueFlow::Value * movedValue = tok->getMovedValue(); if (!movedValue || movedValue->moveKind == ValueFlow::Value::MoveKind::NonMovedVariable) continue; if (movedValue->isInconclusive() && !reportInconclusive) continue; bool inconclusive = false; bool accessOfMoved = false; if (tok->strAt(1) == ".") { if (tok->next()->originalName() == "->") accessOfMoved = true; else inconclusive = true; } else { const bool variableChanged = isVariableChangedByFunctionCall(tok, 0, mSettings, &inconclusive); accessOfMoved = !variableChanged && checkUninitVar.isVariableUsage(tok, false, CheckUninitVar::NO_ALLOC); if (inconclusive) { accessOfMoved = !isMovedParameterAllowedForInconclusiveFunction(tok); if (accessOfMoved) inconclusive = false; } } if (accessOfMoved || (inconclusive && reportInconclusive)) accessMovedError(tok, tok->str(), movedValue, inconclusive || movedValue->isInconclusive()); } } } bool CheckOther::isMovedParameterAllowedForInconclusiveFunction(const Token * tok) { if (Token::simpleMatch(tok->tokAt(-4), "std :: move (")) return false; const Token * tokAtM2 = tok->tokAt(-2); if (Token::simpleMatch(tokAtM2, "> (") && tokAtM2->link()) { const Token * leftAngle = tokAtM2->link(); if (Token::simpleMatch(leftAngle->tokAt(-3), "std :: forward <")) return false; } return true; } void CheckOther::accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) { if (!tok) { reportError(tok, Severity::warning, "accessMoved", "Access of moved variable 'v'.", CWE672, Certainty::normal); reportError(tok, Severity::warning, "accessForwarded", "Access of forwarded variable 'v'.", CWE672, Certainty::normal); return; } const char * errorId = nullptr; std::string kindString; switch (value->moveKind) { case ValueFlow::Value::MoveKind::MovedVariable: errorId = "accessMoved"; kindString = "moved"; break; case ValueFlow::Value::MoveKind::ForwardedVariable: errorId = "accessForwarded"; kindString = "forwarded"; break; default: return; } const std::string errmsg("$symbol:" + varname + "\nAccess of " + kindString + " variable '$symbol'."); const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::warning, errorId, errmsg, CWE672, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckOther::checkFuncArgNamesDifferent() { const bool style = mSettings->severity.isEnabled(Severity::style); const bool inconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const bool warning = mSettings->severity.isEnabled(Severity::warning); if (!(warning || (style && inconclusive))) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // check every function for (const Scope *scope : symbolDatabase->functionScopes) { const Function * function = scope->function; // only check functions with arguments if (!function || function->argCount() == 0) continue; // only check functions with separate declarations and definitions if (function->argDef == function->arg) continue; // get the function argument name tokens std::vector declarations(function->argCount()); std::vector definitions(function->argCount()); const Token * decl = function->argDef->next(); for (int j = 0; j < function->argCount(); ++j) { declarations[j] = nullptr; definitions[j] = nullptr; // get the definition const Variable * variable = function->getArgumentVar(j); if (variable) { definitions[j] = variable->nameToken(); } // get the declaration (search for first token with varId) while (decl && !Token::Match(decl, ",|)|;")) { // skip everything after the assignment because // it could also have a varId or be the first // token with a varId if there is no name token if (decl->str() == "=") { decl = decl->nextArgument(); break; } // skip over template if (decl->link()) decl = decl->link(); else if (decl->varId()) declarations[j] = decl; decl = decl->next(); } if (Token::simpleMatch(decl, ",")) decl = decl->next(); } // check for different argument order if (warning) { bool order_different = false; for (int j = 0; j < function->argCount(); ++j) { if (!declarations[j] || !definitions[j] || declarations[j]->str() == definitions[j]->str()) continue; for (int k = 0; k < function->argCount(); ++k) { if (j != k && definitions[k] && declarations[j]->str() == definitions[k]->str()) { order_different = true; break; } } } if (order_different) { funcArgOrderDifferent(function->name(), function->argDef->next(), function->arg->next(), declarations, definitions); continue; } } // check for different argument names if (style && inconclusive) { for (int j = 0; j < function->argCount(); ++j) { if (declarations[j] && definitions[j] && declarations[j]->str() != definitions[j]->str()) funcArgNamesDifferent(function->name(), j, declarations[j], definitions[j]); } } } } void CheckOther::funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition) { std::list tokens = { declaration,definition }; reportError(tokens, Severity::style, "funcArgNamesDifferent", "$symbol:" + functionName + "\n" "Function '$symbol' argument " + MathLib::toString(index + 1) + " names different: declaration '" + (declaration ? declaration->str() : std::string("A")) + "' definition '" + (definition ? definition->str() : std::string("B")) + "'.", CWE628, Certainty::inconclusive); } void CheckOther::funcArgOrderDifferent(const std::string & functionName, const Token* declaration, const Token* definition, const std::vector & declarations, const std::vector & definitions) { std::list tokens = { declarations.size() ? declarations[0] ? declarations[0] : declaration : nullptr, definitions.size() ? definitions[0] ? definitions[0] : definition : nullptr }; std::string msg = "$symbol:" + functionName + "\nFunction '$symbol' argument order different: declaration '"; for (int i = 0; i < declarations.size(); ++i) { if (i != 0) msg += ", "; if (declarations[i]) msg += declarations[i]->str(); } msg += "' definition '"; for (int i = 0; i < definitions.size(); ++i) { if (i != 0) msg += ", "; if (definitions[i]) msg += definitions[i]->str(); } msg += "'"; reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, Certainty::normal); } static const Token *findShadowed(const Scope *scope, const std::string &varname, int linenr) { if (!scope) return nullptr; for (const Variable &var : scope->varlist) { if (scope->isExecutable() && var.nameToken()->linenr() > linenr) continue; if (var.name() == varname) return var.nameToken(); } for (const Function &f : scope->functionList) { if (f.type == Function::Type::eFunction && f.name() == varname) return f.tokenDef; } if (scope->type == Scope::eLambda) return nullptr; return findShadowed(scope->nestedIn, varname, linenr); } void CheckOther::checkShadowVariables() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope & scope : symbolDatabase->scopeList) { if (!scope.isExecutable() || scope.type == Scope::eLambda) continue; const Scope *functionScope = &scope; while (functionScope && functionScope->type != Scope::ScopeType::eFunction && functionScope->type != Scope::ScopeType::eLambda) functionScope = functionScope->nestedIn; for (const Variable &var : scope.varlist) { if (var.nameToken() && var.nameToken()->isExpandedMacro()) // #8903 continue; if (functionScope && functionScope->type == Scope::ScopeType::eFunction && functionScope->function) { bool shadowArg = false; for (const Variable &arg : functionScope->function->argumentList) { if (arg.nameToken() && var.name() == arg.name()) { shadowError(var.nameToken(), arg.nameToken(), "argument"); shadowArg = true; break; } } if (shadowArg) continue; } const Token *shadowed = findShadowed(scope.nestedIn, var.name(), var.nameToken()->linenr()); if (!shadowed) continue; if (scope.type == Scope::eFunction && scope.className == var.name()) continue; if (functionScope->function && functionScope->function->isStatic() && shadowed->variable() && !shadowed->variable()->isLocal()) continue; shadowError(var.nameToken(), shadowed, (shadowed->varId() != 0) ? "variable" : "function"); } } } void CheckOther::shadowError(const Token *var, const Token *shadowed, std::string type) { ErrorPath errorPath; errorPath.push_back(ErrorPathItem(shadowed, "Shadowed declaration")); errorPath.push_back(ErrorPathItem(var, "Shadow variable")); const std::string &varname = var ? var->str() : type; const std::string Type = char(std::toupper(type[0])) + type.substr(1); const std::string id = "shadow" + Type; const std::string message = "$symbol:" + varname + "\nLocal variable \'$symbol\' shadows outer " + type; reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal); } static bool isVariableExpression(const Token* tok) { if (Token::Match(tok, "%var%")) return true; if (Token::simpleMatch(tok, ".")) return isVariableExpression(tok->astOperand1()) && isVariableExpression(tok->astOperand2()); if (Token::simpleMatch(tok, "[")) return isVariableExpression(tok->astOperand1()) && tok->astOperand2() && tok->astOperand2()->hasKnownIntValue(); return false; } void CheckOther::checkKnownArgument() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok->astParent(), "(")) continue; if (!Token::Match(tok->astParent()->previous(), "%name%")) continue; if (Token::Match(tok->astParent()->previous(), "if|while|switch|sizeof")) continue; if (tok == tok->astParent()->previous()) continue; if (!tok->hasKnownIntValue()) continue; if (Token::Match(tok, "++|--")) continue; if (isConstVarExpression(tok)) continue; const Token * tok2 = tok; if (isCPPCast(tok2)) tok2 = tok2->astOperand2(); if (isVariableExpression(tok2)) continue; // ensure that there is a integer variable in expression with unknown value std::string varexpr; bool isVariableExprHidden = false; // Is variable expression explicitly hidden auto setVarExpr = [&varexpr, &isVariableExprHidden](const Token *child) { if (Token::Match(child, "%var%|.|[")) { if (child->valueType() && child->valueType()->pointer == 0 && child->valueType()->isIntegral() && child->values().empty()) { varexpr = child->expressionString(); return ChildrenToVisit::done; } return ChildrenToVisit::none; } if (Token::simpleMatch(child->previous(), "sizeof (")) return ChildrenToVisit::none; // hide variable explicitly with 'x * 0' etc if (!isVariableExprHidden) { if (Token::simpleMatch(child, "*") && (Token::simpleMatch(child->astOperand1(), "0") || Token::simpleMatch(child->astOperand2(), "0"))) return ChildrenToVisit::none; if (Token::simpleMatch(child, "&&") && (Token::simpleMatch(child->astOperand1(), "false") || Token::simpleMatch(child->astOperand2(), "false"))) return ChildrenToVisit::none; if (Token::simpleMatch(child, "||") && (Token::simpleMatch(child->astOperand1(), "true") || Token::simpleMatch(child->astOperand2(), "true"))) return ChildrenToVisit::none; } return ChildrenToVisit::op1_and_op2; }; visitAstNodes(tok, setVarExpr); if (varexpr.empty()) { isVariableExprHidden = true; visitAstNodes(tok, setVarExpr); } if (varexpr.empty()) continue; // ensure that function name does not contain "assert" std::string funcname = tok->astParent()->previous()->str(); std::transform(funcname.begin(), funcname.end(), funcname.begin(), [](int c) { return std::tolower(c); }); if (funcname.find("assert") != std::string::npos) continue; knownArgumentError(tok, tok->astParent()->previous(), &tok->values().front(), varexpr, isVariableExprHidden); } } } void CheckOther::knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden) { if (!tok) { reportError(tok, Severity::style, "knownArgument", "Argument 'x-x' to function 'func' is always 0. It does not matter what value 'x' has."); reportError(tok, Severity::style, "knownArgumentHiddenVariableExpression", "Argument 'x*0' to function 'func' is always 0. Constant literal calculation disable/hide variable expression 'x'."); return; } const MathLib::bigint intvalue = value->intvalue; const std::string &expr = tok->expressionString(); const std::string &fun = ftok->str(); const char *id; std::string errmsg = "Argument '" + expr + "' to function " + fun + " is always " + std::to_string(intvalue) + ". "; if (!isVariableExpressionHidden) { id = "knownArgument"; errmsg += "It does not matter what value '" + varexpr + "' has."; } else { id = "knownArgumentHiddenVariableExpression"; errmsg += "Constant literal calculation disable/hide variable expression '" + varexpr + "'."; } const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::style, id, errmsg, CWE570, Certainty::normal); } void CheckOther::checkComparePointers() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "<|>|<=|>=|-")) continue; const Token *tok1 = tok->astOperand1(); const Token *tok2 = tok->astOperand2(); if (!astIsPointer(tok1) || !astIsPointer(tok2)) continue; ValueFlow::Value v1 = getLifetimeObjValue(tok1); ValueFlow::Value v2 = getLifetimeObjValue(tok2); if (!v1.isLocalLifetimeValue() || !v2.isLocalLifetimeValue()) continue; const Variable *var1 = v1.tokvalue->variable(); const Variable *var2 = v2.tokvalue->variable(); if (!var1 || !var2) continue; if (v1.tokvalue->varId() == v2.tokvalue->varId()) continue; if (var1->isReference() || var2->isReference()) continue; if (var1->isRValueReference() || var2->isRValueReference()) continue; comparePointersError(tok, &v1, &v2); } } } void CheckOther::comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2) { ErrorPath errorPath; std::string verb = "Comparing"; if (Token::simpleMatch(tok, "-")) verb = "Subtracting"; if (v1) { errorPath.emplace_back(v1->tokvalue->variable()->nameToken(), "Variable declared here."); errorPath.insert(errorPath.end(), v1->errorPath.begin(), v1->errorPath.end()); } if (v2) { errorPath.emplace_back(v2->tokvalue->variable()->nameToken(), "Variable declared here."); errorPath.insert(errorPath.end(), v2->errorPath.begin(), v2->errorPath.end()); } errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "comparePointers", verb + " pointers that point to different objects", CWE570, Certainty::normal); } void CheckOther::checkModuloOfOne() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand2() || !tok->astOperand1()) continue; if (tok->str() != "%") continue; if (!tok->valueType() || !tok->valueType()->isIntegral()) continue; // Value flow.. const ValueFlow::Value *value = tok->astOperand2()->getValue(1LL); if (value && value->isKnown()) checkModuloOfOneError(tok); } } void CheckOther::checkModuloOfOneError(const Token *tok) { reportError(tok, Severity::style, "moduloofone", "Modulo of one is always equal to zero"); } //----------------------------------------------------------------------------- // Overlapping write (undefined behavior) //----------------------------------------------------------------------------- static bool getBufAndOffset(const Token *expr, const Token **buf, MathLib::bigint *offset) { if (!expr) return false; const Token *bufToken, *offsetToken; if (expr->isUnaryOp("&") && Token::simpleMatch(expr->astOperand1(), "[")) { bufToken = expr->astOperand1()->astOperand1(); offsetToken = expr->astOperand1()->astOperand2(); } 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); if (pointer1 && !pointer2) { bufToken = expr->astOperand1(); offsetToken = expr->astOperand2(); } else if (!pointer1 && pointer2) { bufToken = expr->astOperand2(); offsetToken = expr->astOperand1(); } else { return false; } } else if (expr->valueType() && expr->valueType()->pointer > 0) { *buf = expr; *offset = 0; return true; } else { return false; } if (!bufToken->valueType() || !bufToken->valueType()->pointer) return false; if (!offsetToken->hasKnownIntValue()) return false; *buf = bufToken; *offset = offsetToken->getKnownIntValue(); return true; } void CheckOther::checkOverlappingWrite() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (tok->isAssignmentOp()) { // check if LHS is a union member.. const Token * const lhs = tok->astOperand1(); if (!Token::simpleMatch(lhs, ".") || !lhs->isBinaryOp()) continue; const Variable * const lhsvar = lhs->astOperand1()->variable(); if (!lhsvar || !lhsvar->typeScope() || lhsvar->typeScope()->type != Scope::ScopeType::eUnion) continue; const Token* const lhsmember = lhs->astOperand2(); if (!lhsmember) continue; // Is other union member used in RHS? const Token *errorToken = nullptr; visitAstNodes(tok->astOperand2(), [lhsvar, lhsmember, &errorToken](const Token *rhs) { if (!Token::simpleMatch(rhs, ".")) return ChildrenToVisit::op1_and_op2; if (!rhs->isBinaryOp() || rhs->astOperand1()->variable() != lhsvar) return ChildrenToVisit::none; if (lhsmember->str() == rhs->astOperand2()->str()) return ChildrenToVisit::none; errorToken = rhs->astOperand2(); return ChildrenToVisit::done; }); if (errorToken) overlappingWriteUnion(tok); } else if (Token::Match(tok, "%name% (")) { const Library::NonOverlappingData *nonOverlappingData = mSettings->library.getNonOverlappingData(tok); if (!nonOverlappingData) continue; const std::vector args = getArguments(tok); if (nonOverlappingData->ptr1Arg <= 0 || nonOverlappingData->ptr1Arg > args.size()) continue; if (nonOverlappingData->ptr2Arg <= 0 || nonOverlappingData->ptr2Arg > args.size()) continue; const Token *ptr1 = args[nonOverlappingData->ptr1Arg - 1]; if (ptr1->hasKnownIntValue() && ptr1->getKnownIntValue() == 0) continue; const Token *ptr2 = args[nonOverlappingData->ptr2Arg - 1]; if (ptr2->hasKnownIntValue() && ptr2->getKnownIntValue() == 0) continue; // TODO: nonOverlappingData->strlenArg if (nonOverlappingData->sizeArg <= 0 || nonOverlappingData->sizeArg > args.size()) { if (nonOverlappingData->sizeArg == -1) { ErrorPath errorPath; const bool macro = true; const bool pure = true; const bool follow = true; if (!isSameExpression(mTokenizer->isCPP(), macro, ptr1, ptr2, mSettings->library, pure, follow, &errorPath)) continue; overlappingWriteFunction(tok); } continue; } if (!args[nonOverlappingData->sizeArg-1]->hasKnownIntValue()) continue; const MathLib::bigint sizeValue = args[nonOverlappingData->sizeArg-1]->getKnownIntValue(); const Token *buf1, *buf2; MathLib::bigint offset1, offset2; if (!getBufAndOffset(ptr1, &buf1, &offset1)) continue; if (!getBufAndOffset(ptr2, &buf2, &offset2)) continue; if (offset1 < offset2 && offset1 + sizeValue <= offset2) continue; if (offset2 < offset1 && offset2 + sizeValue <= offset1) continue; ErrorPath errorPath; const bool macro = true; const bool pure = true; const bool follow = true; if (!isSameExpression(mTokenizer->isCPP(), macro, buf1, buf2, mSettings->library, pure, follow, &errorPath)) continue; overlappingWriteFunction(tok); } } } } void CheckOther::overlappingWriteUnion(const Token *tok) { reportError(tok, Severity::error, "overlappingWriteUnion", "Overlapping read/write of union is undefined behavior"); } void CheckOther::overlappingWriteFunction(const Token *tok) { const std::string funcname = tok ? tok->str() : ""; reportError(tok, Severity::error, "overlappingWriteFunction", "Overlapping read/write in " + funcname + "() is undefined behavior"); } cppcheck-2.7/lib/checkother.h000066400000000000000000000511741417746362400162260ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkotherH #define checkotherH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errortypes.h" #include "utils.h" #include #include namespace ValueFlow { class Value; } class Settings; class Token; class Tokenizer; class Function; class Variable; class ErrorLogger; /// @addtogroup Checks /// @{ /** @brief Various small checks */ class CPPCHECKLIB CheckOther : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckOther() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckOther(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckOther checkOther(tokenizer, settings, errorLogger); // Checks checkOther.warningOldStylePointerCast(); checkOther.invalidPointerCast(); checkOther.checkCharVariable(); checkOther.checkRedundantAssignment(); checkOther.checkRedundantAssignmentInSwitch(); checkOther.checkSuspiciousCaseInSwitch(); checkOther.checkDuplicateBranch(); checkOther.checkDuplicateExpression(); checkOther.checkUnreachableCode(); checkOther.checkSuspiciousSemicolon(); checkOther.checkVariableScope(); checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) checkOther.checkIncompleteArrayFill(); checkOther.checkVarFuncNullUB(); checkOther.checkNanInArithmeticExpression(); checkOther.checkCommaSeparatedReturn(); checkOther.checkRedundantPointerOp(); checkOther.checkZeroDivision(); checkOther.checkNegativeBitwiseShift(); checkOther.checkInterlockedDecrement(); checkOther.checkUnusedLabel(); checkOther.checkEvaluationOrder(); checkOther.checkFuncArgNamesDifferent(); checkOther.checkShadowVariables(); checkOther.checkKnownArgument(); checkOther.checkComparePointers(); checkOther.checkIncompleteStatement(); checkOther.checkPipeParameterSize(); checkOther.checkRedundantCopy(); checkOther.clarifyCalculation(); checkOther.checkPassByReference(); checkOther.checkConstVariable(); checkOther.checkConstPointer(); checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); checkOther.checkInvalidFree(); checkOther.clarifyStatement(); checkOther.checkCastIntToCharAndBack(); checkOther.checkMisusedScopedObject(); checkOther.checkAccessOfMovedVariable(); checkOther.checkModuloOfOne(); checkOther.checkOverlappingWrite(); } /** Is expression a comparison that checks if a nonzero (unsigned/pointer) expression is less than zero? */ static bool comparisonNonZeroExpressionLessThanZero(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr); /** Is expression a comparison that checks if a nonzero (unsigned/pointer) expression is positive? */ static bool testIfNonZeroExpressionIsPositive(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr); /** @brief Clarify calculation for ".. a * b ? .." */ void clarifyCalculation(); /** @brief Suspicious statement like '*A++;' */ void clarifyStatement(); /** @brief Are there C-style pointer casts in a c++ file? */ void warningOldStylePointerCast(); /** @brief Check for pointer casts to a type with an incompatible binary data representation */ void invalidPointerCast(); /** @brief %Check scope of variables */ void checkVariableScope(); static bool checkInnerScope(const Token *tok, const Variable* var, bool& used); /** @brief %Check for comma separated statements in return */ void checkCommaSeparatedReturn(); /** @brief %Check for function parameters that should be passed by reference */ void checkPassByReference(); void checkConstVariable(); void checkConstPointer(); /** @brief Using char variable as array index / as operand in bit operation */ void checkCharVariable(); /** @brief Incomplete statement. A statement that only contains a constant or variable */ void checkIncompleteStatement(); /** @brief %Check zero division*/ void checkZeroDivision(); /** @brief Check for NaN (not-a-number) in an arithmetic expression */ void checkNanInArithmeticExpression(); /** @brief copying to memory or assigning to a variable twice */ void checkRedundantAssignment(); /** @brief %Check for assigning to the same variable twice in a switch statement*/ void checkRedundantAssignmentInSwitch(); /** @brief %Check for code like 'case A||B:'*/ void checkSuspiciousCaseInSwitch(); /** @brief %Check for objects that are destroyed immediately */ void checkMisusedScopedObject(); /** @brief %Check for suspicious code where if and else branch are the same (e.g "if (a) b = true; else b = true;") */ void checkDuplicateBranch(); /** @brief %Check for suspicious code with the same expression on both sides of operator (e.g "if (a && a)") */ void checkDuplicateExpression(); /** @brief %Check for code that gets never executed, such as duplicate break statements */ void checkUnreachableCode(); /** @brief %Check for testing sign of unsigned variable */ void checkSignOfUnsignedVariable(); /** @brief %Check for suspicious use of semicolon */ void checkSuspiciousSemicolon(); /** @brief %Check for free() operations on invalid memory locations */ void checkInvalidFree(); void invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive); /** @brief %Check for code creating redundant copies */ void checkRedundantCopy(); /** @brief %Check for bitwise shift with negative right operand */ void checkNegativeBitwiseShift(); /** @brief %Check for buffers that are filled incompletely with memset and similar functions */ void checkIncompleteArrayFill(); /** @brief %Check that variadic function calls don't use NULL. If NULL is \#defined as 0 and the function expects a pointer, the behaviour is undefined. */ void checkVarFuncNullUB(); /** @brief %Check that calling the POSIX pipe() system call is called with an integer array of size two. */ void checkPipeParameterSize(); /** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */ void checkCastIntToCharAndBack(); /** @brief %Check for using of comparison functions evaluating always to true or false. */ void checkComparisonFunctionIsAlwaysTrueOrFalse(); /** @brief %Check for redundant pointer operations */ void checkRedundantPointerOp(); /** @brief %Check for race condition with non-interlocked access after InterlockedDecrement() */ void checkInterlockedDecrement(); /** @brief %Check for unused labels */ void checkUnusedLabel(); /** @brief %Check for expression that depends on order of evaluation of side effects */ void checkEvaluationOrder(); /** @brief %Check for access of moved or forwarded variable */ void checkAccessOfMovedVariable(); /** @brief %Check if function declaration and definition argument names different */ void checkFuncArgNamesDifferent(); /** @brief %Check for shadow variables. Less noisy than gcc/clang -Wshadow. */ void checkShadowVariables(); void checkKnownArgument(); void checkComparePointers(); void checkModuloOfOne(); void checkOverlappingWrite(); void overlappingWriteUnion(const Token *tok); void overlappingWriteFunction(const Token *tok); private: // Error messages.. void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result); void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); void checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim); void clarifyCalculationError(const Token *tok, const std::string &op); void clarifyStatementError(const Token* tok); void cstyleCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); void passedByValueError(const Token *tok, const std::string &parname, bool inconclusive); void constVariableError(const Variable *var, const Function *function); void constStatementError(const Token *tok, const std::string &type, bool inconclusive); void signedCharArrayIndexError(const Token *tok); void unknownSignCharArrayIndexError(const Token *tok); void charBitOpError(const Token *tok); void variableScopeError(const Token *tok, const std::string &varname); void zerodivError(const Token *tok, const ValueFlow::Value *value); void nanInArithmeticExpressionError(const Token *tok); void redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); void redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); void redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var); void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var); void redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var); void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname); void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString); void selfAssignmentError(const Token *tok, const std::string &varname); void misusedScopeObjectError(const Token *tok, const std::string &varname); void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors); void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive); void oppositeExpressionError(const Token *opTok, ErrorPath errors); void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors); void duplicateValueTernaryError(const Token *tok); void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors); void duplicateBreakError(const Token *tok, bool inconclusive); void unreachableCodeError(const Token* tok, bool inconclusive); void unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); void pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v); void unsignedPositiveError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); void pointerPositiveError(const Token *tok, const ValueFlow::Value *v); void suspiciousSemicolonError(const Token *tok); void negativeBitwiseShiftError(const Token *tok, int op); void redundantCopyError(const Token *tok, const std::string &varname); void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean); void varFuncNullUBError(const Token *tok); void commaSeparatedReturnError(const Token *tok); void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive); void raceAfterInterlockedDecrementError(const Token* tok); void unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef); void unknownEvaluationOrder(const Token* tok); static bool isMovedParameterAllowedForInconclusiveFunction(const Token * tok); void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); void funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition); void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); void shadowError(const Token *var, const Token *shadowed, std::string type); void knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden); void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2); void checkModuloOfOneError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckOther c(nullptr, settings, errorLogger); ErrorPath errorPath; // error c.zerodivError(nullptr, nullptr); c.misusedScopeObjectError(nullptr, "varname"); c.invalidPointerCastError(nullptr, "float *", "double *", false, false); c.negativeBitwiseShiftError(nullptr, 1); c.negativeBitwiseShiftError(nullptr, 2); c.checkPipeParameterSizeError(nullptr, "varname", "dimension"); c.raceAfterInterlockedDecrementError(nullptr); c.invalidFreeError(nullptr, "malloc", false); c.overlappingWriteUnion(nullptr); c.overlappingWriteFunction(nullptr); //performance c.redundantCopyError(nullptr, "varname"); c.redundantCopyError(nullptr, nullptr, "var"); // style/warning c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); c.checkCastIntToCharAndBackError(nullptr, "func_name"); c.cstyleCastError(nullptr); c.passedByValueError(nullptr, "parametername", false); c.constVariableError(nullptr, nullptr); c.constStatementError(nullptr, "type", false); c.signedCharArrayIndexError(nullptr); c.unknownSignCharArrayIndexError(nullptr); c.charBitOpError(nullptr); c.variableScopeError(nullptr, "varname"); c.redundantAssignmentInSwitchError(nullptr, nullptr, "var"); c.redundantCopyInSwitchError(nullptr, nullptr, "var"); c.suspiciousCaseInSwitchError(nullptr, "||"); c.selfAssignmentError(nullptr, "varname"); c.clarifyCalculationError(nullptr, "+"); c.clarifyStatementError(nullptr); c.duplicateBranchError(nullptr, nullptr, errorPath); c.duplicateAssignExpressionError(nullptr, nullptr, true); c.oppositeExpressionError(nullptr, errorPath); c.duplicateExpressionError(nullptr, nullptr, nullptr, errorPath); c.duplicateValueTernaryError(nullptr); c.duplicateExpressionTernaryError(nullptr, errorPath); c.duplicateBreakError(nullptr, false); c.unreachableCodeError(nullptr, false); c.unsignedLessThanZeroError(nullptr, nullptr, "varname"); c.unsignedPositiveError(nullptr, nullptr, "varname"); c.pointerLessThanZeroError(nullptr, nullptr); c.pointerPositiveError(nullptr, nullptr); c.suspiciousSemicolonError(nullptr); c.incompleteArrayFillError(nullptr, "buffer", "memset", false); c.varFuncNullUBError(nullptr); c.nanInArithmeticExpressionError(nullptr); c.commaSeparatedReturnError(nullptr); c.redundantPointerOpError(nullptr, "varname", false); c.unusedLabelError(nullptr, false, false); c.unusedLabelError(nullptr, false, true); c.unusedLabelError(nullptr, true, false); c.unusedLabelError(nullptr, true, true); c.unknownEvaluationOrder(nullptr); c.accessMovedError(nullptr, "v", nullptr, false); c.funcArgNamesDifferent("function", 1, nullptr, nullptr); c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); c.shadowError(nullptr, nullptr, "variable"); c.shadowError(nullptr, nullptr, "function"); c.shadowError(nullptr, nullptr, "argument"); c.knownArgumentError(nullptr, nullptr, nullptr, "x", false); c.comparePointersError(nullptr, nullptr, nullptr); c.redundantAssignmentError(nullptr, nullptr, "var", false); c.redundantInitializationError(nullptr, nullptr, "var", false); const std::vector nullvec; c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); c.checkModuloOfOneError(nullptr); } static std::string myName() { return "Other"; } std::string classInfo() const OVERRIDE { return "Other checks\n" // error "- division with zero\n" "- scoped object destroyed immediately after construction\n" "- assignment in an assert statement\n" "- free() or delete of an invalid memory location\n" "- bitwise operation with negative right operand\n" "- provide wrong dimensioned array to pipe() system command (--std=posix)\n" "- cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n" "- race condition with non-interlocked access after InterlockedDecrement() call\n" "- expression 'x = x++;' depends on order of evaluation of side effects\n" "- overlapping write of union\n" // warning "- either division by zero or useless condition\n" "- access of moved or forwarded variable.\n" // performance "- redundant data copying for const variable\n" "- subsequent assignment or copying to a variable or buffer\n" "- passing parameter by value\n" // portability "- Passing NULL pointer to function with variable number of arguments leads to UB.\n" // style "- C-style pointer cast in C++ code\n" "- casting between incompatible pointer types\n" "- [Incomplete statement](IncompleteStatement)\n" "- [check how signed char variables are used](CharVar)\n" "- variable scope can be limited\n" "- unusual pointer arithmetic. For example: \"abc\" + 'd'\n" "- redundant assignment, increment, or bitwise operation in a switch statement\n" "- redundant strcpy in a switch statement\n" "- Suspicious case labels in switch()\n" "- assignment of a variable to itself\n" "- Comparison of values leading always to true or false\n" "- Clarify calculation with parentheses\n" "- suspicious comparison of '\\0' with a char\\* variable\n" "- duplicate break statement\n" "- unreachable code\n" "- testing if unsigned variable is negative/positive\n" "- Suspicious use of ; at the end of 'if/for/while' statement.\n" "- Array filled incompletely using memset/memcpy/memmove.\n" "- NaN (not a number) value used in arithmetic expression.\n" "- comma in return statement (the comma can easily be misread as a semicolon).\n" "- prefer erfc, expm1 or log1p to avoid loss of precision.\n" "- identical code in both branches of if/else or ternary operator.\n" "- redundant pointer operation on pointer like &\\*some_ptr.\n" "- find unused 'goto' labels.\n" "- function declaration and definition argument names different.\n" "- function declaration and definition argument order different.\n" "- shadow variable.\n" "- variable can be declared const.\n" "- calculating modulo of one.\n" "- known function argument, suspicious calculation.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkotherH cppcheck-2.7/lib/checkpostfixoperator.cpp000066400000000000000000000064311417746362400207040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // You should use ++ and -- as prefix whenever possible as these are more // efficient than postfix operators //--------------------------------------------------------------------------- #include "checkpostfixoperator.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckPostfixOperator instance; } // CWE ids used static const struct CWE CWE398(398U); // Indicator of Poor Code Quality void CheckPostfixOperator::postfixOperator() { if (!mSettings->severity.isEnabled(Severity::performance)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const Variable *var = tok->variable(); if (!var || !Token::Match(tok, "%var% ++|--")) continue; const Token* parent = tok->next()->astParent(); if (!parent || parent->str() == ";" || (parent->str() == "," && (!parent->astParent() || parent->astParent()->str() != "("))) { if (var->isPointer() || var->isArray()) continue; if (Token::Match(var->nameToken()->previous(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator")) { // the variable is an iterator postfixOperatorError(tok); } else if (var->type()) { // the variable is an instance of class postfixOperatorError(tok); } } } } } //--------------------------------------------------------------------------- void CheckPostfixOperator::postfixOperatorError(const Token *tok) { reportError(tok, Severity::performance, "postfixOperator", "Prefer prefix ++/-- operators for non-primitive types.\n" "Prefix ++/-- operators should be preferred for non-primitive types. " "Pre-increment/decrement can be more efficient than " "post-increment/decrement. Post-increment/decrement usually " "involves keeping a copy of the previous value around and " "adds a little extra code.", CWE398, Certainty::normal); } cppcheck-2.7/lib/checkpostfixoperator.h000066400000000000000000000051031417746362400203440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkpostfixoperatorH #define checkpostfixoperatorH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "tokenize.h" #include class ErrorLogger; class Settings; class Token; /// @addtogroup Checks /// @{ /** * @brief Using postfix operators ++ or -- rather than postfix operator. */ class CPPCHECKLIB CheckPostfixOperator : public Check { public: /** This constructor is used when registering the CheckPostfixOperator */ CheckPostfixOperator() : Check(myName()) {} /** This constructor is used when running checks. */ CheckPostfixOperator(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (tokenizer->isC()) return; CheckPostfixOperator checkPostfixOperator(tokenizer, settings, errorLogger); checkPostfixOperator.postfixOperator(); } /** Check postfix operators */ void postfixOperator(); private: /** Report Error */ void postfixOperatorError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckPostfixOperator c(nullptr, settings, errorLogger); c.postfixOperatorError(nullptr); } static std::string myName() { return "Using postfix operators"; } std::string classInfo() const OVERRIDE { return "Warn if using postfix operators ++ or -- rather than prefix operator\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkpostfixoperatorH cppcheck-2.7/lib/checksizeof.cpp000066400000000000000000000506421417746362400167360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checksizeof.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckSizeof instance; } // CWE IDs used: static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE467(467U); // Use of sizeof() on a Pointer Type static const struct CWE CWE682(682U); // Incorrect Calculation //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckSizeof::checkSizeofForNumericParameter() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %num% )") || Token::Match(tok, "sizeof %num%")) { sizeofForNumericParameterError(tok); } } } } void CheckSizeof::sizeofForNumericParameterError(const Token *tok) { reportError(tok, Severity::warning, "sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n" "It is unusual to use a constant value with sizeof. For example, 'sizeof(10)'" " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10. 'sizeof('A')'" " and 'sizeof(char)' can return different results.", CWE682, Certainty::normal); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckSizeof::checkSizeofForArrayParameter() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %var% )") || Token::Match(tok, "sizeof %var% !![")) { const Token* varTok = tok->next(); if (varTok->str() == "(") { varTok = varTok->next(); } const Variable *var = varTok->variable(); if (var && var->isArray() && var->isArgument() && !var->isReference()) sizeofForArrayParameterError(tok); } } } } void CheckSizeof::sizeofForArrayParameterError(const Token *tok) { reportError(tok, Severity::warning, "sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument " "returns size of a pointer.\n" "Using 'sizeof' for array given as function argument returns the size of a pointer. " "It does not return the size of the whole array in bytes as might be " "expected. For example, this code:\n" " int f(char a[100]) {\n" " return sizeof(a);\n" " }\n" "returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the " "size of the array in bytes).", CWE467, Certainty::normal ); } void CheckSizeof::checkSizeofForPointerSize() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { const Token* tokSize; const Token* tokFunc; const Token *variable = nullptr; const Token *variable2 = nullptr; // Find any function that may use sizeof on a pointer // Once leaving those tests, it is mandatory to have: // - variable matching the used pointer // - tokVar pointing on the argument where sizeof may be used if (Token::Match(tok->tokAt(2), "malloc|alloca|calloc (")) { if (Token::Match(tok, "%var% =")) variable = tok; else if (tok->strAt(1) == ")" && Token::Match(tok->linkAt(1)->tokAt(-2), "%var% =")) variable = tok->linkAt(1)->tokAt(-2); else if (tok->link() && Token::Match(tok, "> ( malloc|alloca|calloc (") && Token::Match(tok->link()->tokAt(-3), "%var% =")) variable = tok->link()->tokAt(-3); tokSize = tok->tokAt(4); tokFunc = tok->tokAt(2); } else if (Token::simpleMatch(tok, "memset (") && tok->strAt(-1) != ".") { variable = tok->tokAt(2); tokSize = variable->nextArgument(); if (tokSize) tokSize = tokSize->nextArgument(); tokFunc = tok; } else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (") && tok->strAt(-1) != ".") { variable = tok->tokAt(2); variable2 = variable->nextArgument(); if (!variable2) continue; tokSize = variable2->nextArgument(); tokFunc = tok; } else { continue; } if (tokSize && tokFunc->str() == "calloc") tokSize = tokSize->nextArgument(); if (tokSize) { const Token * const paramsListEndTok = tokFunc->linkAt(1); for (const Token* tok2 = tokSize; tok2 != paramsListEndTok; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "/ sizeof")) { // Allow division with sizeof(char) if (Token::simpleMatch(tok2->next(), "sizeof (")) { const Token *sztok = tok2->tokAt(2)->astOperand2(); const ValueType *vt = ((sztok != nullptr) ? sztok->valueType() : nullptr); if (vt && vt->type == ValueType::CHAR && vt->pointer == 0) continue; } divideBySizeofError(tok2, tokFunc->str()); } } } if (!variable || !tokSize) continue; while (Token::Match(variable, "%var% ::|.")) variable = variable->tokAt(2); while (Token::Match(variable2, "%var% ::|.")) variable2 = variable2->tokAt(2); if (!variable) continue; // Ensure the variables are in the symbol database // Also ensure the variables are pointers // Only keep variables which are pointers const Variable *var = variable->variable(); if (!var || !var->isPointer() || var->isArray()) { variable = nullptr; } if (variable2) { var = variable2->variable(); if (!var || !var->isPointer() || var->isArray()) { variable2 = nullptr; } } // If there are no pointer variable at this point, there is // no need to continue if (variable == nullptr && variable2 == nullptr) { continue; } // Jump to the next sizeof token in the function and in the parameter // This is to allow generic operations with sizeof for (; tokSize && tokSize->str() != ")" && tokSize->str() != "," && tokSize->str() != "sizeof"; tokSize = tokSize->next()) {} if (tokSize->str() != "sizeof") continue; // Now check for the sizeof usage: Does the level of pointer indirection match? if (tokSize->linkAt(1)->strAt(-1) == "*") { if (variable && variable->valueType() && variable->valueType()->pointer == 1 && variable->valueType()->type != ValueType::VOID) sizeofForPointerError(variable, variable->str()); else if (variable2 && variable2->valueType() && variable2->valueType()->pointer == 1 && variable2->valueType()->type != ValueType::VOID) sizeofForPointerError(variable2, variable2->str()); } if (Token::simpleMatch(tokSize, "sizeof ( &")) tokSize = tokSize->tokAt(3); else if (Token::Match(tokSize, "sizeof (|&")) tokSize = tokSize->tokAt(2); else tokSize = tokSize->next(); while (Token::Match(tokSize, "%var% ::|.")) tokSize = tokSize->tokAt(2); if (Token::Match(tokSize, "%var% [|(")) continue; // Now check for the sizeof usage again. Once here, everything using sizeof(varid) or sizeof(&varid) // looks suspicious if (variable && tokSize->varId() == variable->varId()) sizeofForPointerError(variable, variable->str()); if (variable2 && tokSize->varId() == variable2->varId()) sizeofForPointerError(variable2, variable2->str()); } } } void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "pointerSize", "Size of pointer '" + varname + "' used instead of size of its data.\n" "Size of pointer '" + varname + "' used instead of size of its data. " "This is likely to lead to a buffer overflow. You probably intend to " "write 'sizeof(*" + varname + ")'.", CWE467, Certainty::normal); } void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc) { reportError(tok, Severity::warning, "sizeofDivisionMemfunc", "Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, Certainty::normal); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckSizeof::sizeofsizeof() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof (| sizeof")) { sizeofsizeofError(tok); tok = tok->next(); } } } void CheckSizeof::sizeofsizeofError(const Token *tok) { reportError(tok, Severity::warning, "sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n" "Calling sizeof for 'sizeof looks like a suspicious code and " "most likely there should be just one 'sizeof'. The current " "code is equivalent to 'sizeof(size_t)'", CWE682, Certainty::normal); } //----------------------------------------------------------------------------- void CheckSizeof::sizeofCalculation() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "sizeof (")) continue; // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro if (tok->isExpandedMacro() && tok->previous()) { const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) { continue; } } const Token *argument = tok->next()->astOperand2(); if (!argument || !argument->isCalculation()) continue; bool inconclusive = false; if (argument->isExpandedMacro()) inconclusive = true; else if (tok->next()->isExpandedMacro()) inconclusive = true; if (!inconclusive || printInconclusive) sizeofCalculationError(argument, inconclusive); } } void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) { reportError(tok, Severity::warning, "sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive ? Certainty::inconclusive : Certainty::normal); } //----------------------------------------------------------------------------- void CheckSizeof::sizeofFunction() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro if (tok->isExpandedMacro() && tok->previous()) { const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) { continue; } } if (const Token *argument = tok->next()->astOperand2()) { const Token *checkToken = argument->previous(); if (checkToken->tokType() == Token::eName) break; const Function * fun = checkToken->function(); // Don't report error if the function is overloaded if (fun && fun->nestedIn->functionMap.count(checkToken->str()) == 1) { sizeofFunctionError(tok); } } } } } void CheckSizeof::sizeofFunctionError(const Token *tok) { reportError(tok, Severity::warning, "sizeofFunctionCall", "Found function call inside sizeof().", CWE682, Certainty::normal); } //----------------------------------------------------------------------------- // Check for code like sizeof()*sizeof() or sizeof(ptr)/value //----------------------------------------------------------------------------- void CheckSizeof::suspiciousSizeofCalculation() { if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; // TODO: Use AST here. This should be possible as soon as sizeof without brackets is correctly parsed for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { const Token* const end = tok->linkAt(1); const Variable* var = end->previous()->variable(); if (end->strAt(-1) == "*" || (var && var->isPointer() && !var->isArray())) { if (end->strAt(1) == "/") divideSizeofError(tok); } else if (Token::simpleMatch(end, ") * sizeof") && end->next()->astOperand1() == tok->next()) multiplySizeofError(tok); } } } void CheckSizeof::multiplySizeofError(const Token *tok) { reportError(tok, Severity::warning, "multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, Certainty::inconclusive); } void CheckSizeof::divideSizeofError(const Token *tok) { reportError(tok, Severity::warning, "divideSizeof", "Division of result of sizeof() on pointer type.\n" "Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, " "not the size of the memory area it points to.", CWE682, Certainty::inconclusive); } void CheckSizeof::sizeofVoid() { if (!mSettings->severity.isEnabled(Severity::portability)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof ( void )")) { sizeofVoidError(tok); } else if (Token::simpleMatch(tok, "sizeof (") && tok->next()->astOperand2()) { const ValueType *vt = tok->next()->astOperand2()->valueType(); if (vt && vt->type == ValueType::Type::VOID && vt->pointer == 0U) sizeofDereferencedVoidPointerError(tok, tok->strAt(3)); } else if (tok->str() == "-") { // only warn for: 'void *' - 'integral' const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; const bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); const bool op2IsIntegral = (vt2 && vt2->isIntegral() && vt2->pointer == 0U); if (op1IsvoidPointer && op2IsIntegral) arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); } else if (Token::Match(tok, "+|++|--|+=|-=")) { // Arithmetic operations on variable of type "void*" const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; const bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); const bool voidpointer2 = (vt2 && vt2->type == ValueType::Type::VOID && vt2->pointer == 1U); if (voidpointer1) arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); if (!tok->isAssignmentOp() && voidpointer2) arithOperationsOnVoidPointerError(tok, tok->astOperand2()->expressionString(), vt2->str()); } } } void CheckSizeof::sizeofVoidError(const Token *tok) { const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard."; const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, Certainty::normal); } void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname) { const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard."; const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, Certainty::normal); } void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype) { const std::string message = "'$symbol' is of type '" + vartype + "'. When using void pointers in calculations, the behaviour is undefined."; const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", "$symbol:" + varname + '\n' + message + '\n' + verbose, CWE467, Certainty::normal); } cppcheck-2.7/lib/checksizeof.h000066400000000000000000000122701417746362400163760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checksizeofH #define checksizeofH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief checks on usage of sizeof() operator */ class CPPCHECKLIB CheckSizeof : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckSizeof() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckSizeof(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) OVERRIDE { CheckSizeof checkSizeof(tokenizer, settings, errorLogger); // Checks checkSizeof.sizeofsizeof(); checkSizeof.sizeofCalculation(); checkSizeof.sizeofFunction(); checkSizeof.suspiciousSizeofCalculation(); checkSizeof.checkSizeofForArrayParameter(); checkSizeof.checkSizeofForPointerSize(); checkSizeof.checkSizeofForNumericParameter(); checkSizeof.sizeofVoid(); } /** @brief %Check for 'sizeof sizeof ..' */ void sizeofsizeof(); /** @brief %Check for calculations inside sizeof */ void sizeofCalculation(); /** @brief %Check for function call inside sizeof */ void sizeofFunction(); /** @brief %Check for suspicious calculations with sizeof results */ void suspiciousSizeofCalculation(); /** @brief %Check for using sizeof with array given as function argument */ void checkSizeofForArrayParameter(); /** @brief %Check for using sizeof of a variable when allocating it */ void checkSizeofForPointerSize(); /** @brief %Check for using sizeof with numeric given as function argument */ void checkSizeofForNumericParameter(); /** @brief %Check for using sizeof(void) */ void sizeofVoid(); private: // Error messages.. void sizeofsizeofError(const Token* tok); void sizeofCalculationError(const Token* tok, bool inconclusive); void sizeofFunctionError(const Token* tok); void multiplySizeofError(const Token* tok); void divideSizeofError(const Token* tok); void sizeofForArrayParameterError(const Token* tok); void sizeofForPointerError(const Token* tok, const std::string &varname); void divideBySizeofError(const Token* tok, const std::string &memfunc); void sizeofForNumericParameterError(const Token* tok); void sizeofVoidError(const Token *tok); void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname); void arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype); void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE { CheckSizeof c(nullptr, settings, errorLogger); c.sizeofForArrayParameterError(nullptr); c.sizeofForPointerError(nullptr, "varname"); c.divideBySizeofError(nullptr, "memset"); c.sizeofForNumericParameterError(nullptr); c.sizeofsizeofError(nullptr); c.sizeofCalculationError(nullptr, false); c.sizeofFunctionError(nullptr); c.multiplySizeofError(nullptr); c.divideSizeofError(nullptr); c.sizeofVoidError(nullptr); c.sizeofDereferencedVoidPointerError(nullptr, "varname"); c.arithOperationsOnVoidPointerError(nullptr, "varname", "vartype"); } static std::string myName() { return "Sizeof"; } std::string classInfo() const OVERRIDE { return "sizeof() usage checks\n" "- sizeof for array given as function argument\n" "- sizeof for numeric given as function argument\n" "- using sizeof(pointer) instead of the size of pointed data\n" "- look for 'sizeof sizeof ..'\n" "- look for calculations inside sizeof()\n" "- look for function calls inside sizeof()\n" "- look for suspicious calculations with sizeof()\n" "- using 'sizeof(void)' which is undefined\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checksizeofH cppcheck-2.7/lib/checkstl.cpp000066400000000000000000003715001417746362400162400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkstl.h" #include "astutils.h" #include "check.h" #include "errortypes.h" #include "library.h" #include "mathlib.h" #include "pathanalysis.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include "valueflow.h" #include "checknullpointer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // Register this check class (by creating a static instance of it) namespace { CheckStl instance; } // CWE IDs used: static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE597(597U); // Use of Wrong Operator in String Comparison static const struct CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const struct CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime static const struct CWE CWE667(667U); // Improper Locking static const struct CWE CWE704(704U); // Incorrect Type Conversion or Cast static const struct CWE CWE762(762U); // Mismatched Memory Management Routines static const struct CWE CWE786(786U); // Access of Memory Location Before Start of Buffer static const struct CWE CWE788(788U); // Access of Memory Location After End of Buffer static const struct CWE CWE825(825U); // Expired Pointer Dereference static const struct CWE CWE833(833U); // Deadlock static const struct CWE CWE834(834U); // Excessive Iteration static bool isElementAccessYield(const Library::Container::Yield& yield) { return contains({Library::Container::Yield::ITEM, Library::Container::Yield::AT_INDEX}, yield); } static bool containerYieldsElement(const Library::Container* container, const Token* parent) { if (Token::Match(parent, ". %name% (")) { Library::Container::Yield yield = container->getYield(parent->strAt(1)); if (isElementAccessYield(yield)) return true; } return false; } static const Token* getContainerIndex(const Library::Container* container, const Token* parent) { if (Token::Match(parent, ". %name% (")) { Library::Container::Yield yield = container->getYield(parent->strAt(1)); if (isElementAccessYield(yield) && !Token::simpleMatch(parent->tokAt(2), "( )")) return parent->tokAt(2)->astOperand2(); } if (!container->arrayLike_indexOp && !container->stdStringLike) return nullptr; if (Token::simpleMatch(parent, "[")) return parent->astOperand2(); return nullptr; } static const Token* getContainerFromSize(const Library::Container* container, const Token* tok) { if (!tok) return nullptr; if (Token::Match(tok->tokAt(-2), ". %name% (")) { Library::Container::Yield yield = container->getYield(tok->strAt(-1)); if (yield == Library::Container::Yield::SIZE) return tok->tokAt(-2)->astOperand1(); } return nullptr; } void CheckStl::outOfBounds() { for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { const Library::Container *container = getLibraryContainer(tok); if (!container) continue; const Token * parent = astParentSkipParens(tok); const Token* accessTok = parent; if (Token::simpleMatch(accessTok, ".") && Token::simpleMatch(accessTok->astParent(), "(")) accessTok = accessTok->astParent(); if (astIsIterator(accessTok) && Token::simpleMatch(accessTok->astParent(), "+")) accessTok = accessTok->astParent(); const Token* indexTok = getContainerIndex(container, parent); if (indexTok == tok) continue; for (const ValueFlow::Value &value : tok->values()) { if (!value.isContainerSizeValue()) continue; if (value.isImpossible()) continue; if (value.isInconclusive() && !mSettings->certainty.isEnabled(Certainty::inconclusive)) continue; if (!value.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning)) continue; if (value.intvalue == 0 && (indexTok || containerYieldsElement(container, parent))) { std::string indexExpr; if (indexTok && !indexTok->hasKnownValue()) indexExpr = indexTok->expressionString(); outOfBoundsError(accessTok, tok->expressionString(), &value, indexExpr, nullptr); continue; } if (indexTok) { std::vector indexValues = ValueFlow::isOutOfBounds(value, indexTok, mSettings->severity.isEnabled(Severity::warning)); if (!indexValues.empty()) { outOfBoundsError( accessTok, tok->expressionString(), &value, indexTok->expressionString(), &indexValues.front()); continue; } } } if (indexTok && !indexTok->hasKnownIntValue()) { const ValueFlow::Value* value = ValueFlow::findValue(indexTok->values(), mSettings, [&](const ValueFlow::Value& v) { if (!v.isSymbolicValue()) return false; if (v.isImpossible()) return false; if (v.intvalue < 0) return false; const Token* sizeTok = v.tokvalue; if (sizeTok && sizeTok->isCast()) sizeTok = sizeTok->astOperand1(); const Token* containerTok = getContainerFromSize(container, sizeTok); if (!containerTok) return false; return containerTok->exprId() == tok->exprId(); }); if (!value) continue; outOfBoundsError(accessTok, tok->expressionString(), nullptr, indexTok->expressionString(), value); } } } } static std::string indexValueString(const ValueFlow::Value& indexValue, const std::string& containerName = "") { if (indexValue.isIteratorStartValue()) return "at position " + MathLib::toString(indexValue.intvalue) + " from the beginning"; if (indexValue.isIteratorEndValue()) return "at position " + MathLib::toString(-indexValue.intvalue) + " from the end"; std::string indexString = MathLib::toString(indexValue.intvalue); if (indexValue.isSymbolicValue()) { indexString = containerName + ".size()"; if (indexValue.intvalue != 0) indexString += "+" + MathLib::toString(indexValue.intvalue); } if (indexValue.bound == ValueFlow::Value::Bound::Lower) return "greater or equal to " + indexString; return indexString; } void CheckStl::outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue) { // Do not warn if both the container size and index value are possible if (containerSize && indexValue && containerSize->isPossible() && indexValue->isPossible()) return; const std::string expression = tok ? tok->expressionString() : (containerName+"[x]"); std::string errmsg; if (!containerSize) { if (indexValue && indexValue->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(indexValue->condition) + " or '" + index + "' can have the value " + indexValueString(*indexValue, containerName) + ". Expression '" + expression + "' cause access out of bounds."; else errmsg = "Out of bounds access in expression '" + expression + "'"; } else if (containerSize->intvalue == 0) { if (containerSize->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or expression '" + expression + "' cause access out of bounds."; else if (indexValue == nullptr && !index.empty()) errmsg = "Out of bounds access in expression '" + expression + "' because '$symbol' is empty and '" + index + "' may be non-zero."; else errmsg = "Out of bounds access in expression '" + expression + "' because '$symbol' is empty."; } else if (indexValue) { if (containerSize->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or $symbol size can be " + MathLib::toString(containerSize->intvalue) + ". Expression '" + expression + "' cause access out of bounds."; else if (indexValue->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(indexValue->condition) + " or '" + index + "' can have the value " + indexValueString(*indexValue) + ". Expression '" + expression + "' cause access out of bounds."; else errmsg = "Out of bounds access in '" + expression + "', if '$symbol' size is " + MathLib::toString(containerSize->intvalue) + " and '" + index + "' is " + indexValueString(*indexValue); } else { // should not happen return; } ErrorPath errorPath; if (!indexValue) errorPath = getErrorPath(tok, containerSize, "Access out of bounds"); else { ErrorPath errorPath1 = getErrorPath(tok, containerSize, "Access out of bounds"); ErrorPath errorPath2 = getErrorPath(tok, indexValue, "Access out of bounds"); if (errorPath1.size() <= 1) errorPath = errorPath2; else if (errorPath2.size() <= 1) errorPath = errorPath1; else { errorPath = errorPath1; errorPath.splice(errorPath.end(), errorPath2); } } reportError(errorPath, (containerSize && !containerSize->errorSeverity()) || (indexValue && !indexValue->errorSeverity()) ? Severity::warning : Severity::error, "containerOutOfBounds", "$symbol:" + containerName +"\n" + errmsg, CWE398, (containerSize && containerSize->isInconclusive()) || (indexValue && indexValue->isInconclusive()) ? Certainty::inconclusive : Certainty::normal); } bool CheckStl::isContainerSize(const Token *containerToken, const Token *expr) const { if (!Token::simpleMatch(expr, "( )")) return false; if (!Token::Match(expr->astOperand1(), ". %name% (")) return false; if (!isSameExpression(mTokenizer->isCPP(), false, containerToken, expr->astOperand1()->astOperand1(), mSettings->library, false, false)) return false; return containerToken->valueType()->container->getYield(expr->previous()->str()) == Library::Container::Yield::SIZE; } bool CheckStl::isContainerSizeGE(const Token * containerToken, const Token *expr) const { if (!expr) return false; if (isContainerSize(containerToken, expr)) return true; if (expr->str() == "*") { const Token *mul; if (isContainerSize(containerToken, expr->astOperand1())) mul = expr->astOperand2(); else if (isContainerSize(containerToken, expr->astOperand2())) mul = expr->astOperand1(); else return false; return mul && (!mul->hasKnownIntValue() || mul->values().front().intvalue != 0); } if (expr->str() == "+") { const Token *op; if (isContainerSize(containerToken, expr->astOperand1())) op = expr->astOperand2(); else if (isContainerSize(containerToken, expr->astOperand2())) op = expr->astOperand1(); else return false; return op && op->getValueGE(0, mSettings); } return false; } void CheckStl::outOfBoundsIndexExpression() { for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { if (!tok->isName() || !tok->valueType()) continue; const Library::Container *container = tok->valueType()->container; if (!container) continue; if (!container->arrayLike_indexOp && !container->stdStringLike) continue; if (!Token::Match(tok, "%name% [")) continue; if (isContainerSizeGE(tok, tok->next()->astOperand2())) outOfBoundsIndexExpressionError(tok, tok->next()->astOperand2()); } } } void CheckStl::outOfBoundsIndexExpressionError(const Token *tok, const Token *index) { const std::string varname = tok ? tok->str() : std::string("var"); const std::string i = index ? index->expressionString() : std::string(varname + ".size()"); std::string errmsg = "Out of bounds access of $symbol, index '" + i + "' is out of bounds."; reportError(tok, Severity::error, "containerOutOfBoundsIndexExpression", "$symbol:" + varname +"\n" + errmsg, CWE398, Certainty::normal); } // Error message for bad iterator usage.. void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName) { reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, Certainty::normal); } void CheckStl::iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2) { reportError(tok, Severity::error, "iterators1", "$symbol:" + containerName1 + "\n" "$symbol:" + containerName2 + "\n" "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, Certainty::normal); } void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2) { std::list callstack = { tok, containerTok }; reportError(callstack, Severity::error, "iterators2", "$symbol:" + containerName1 + "\n" "$symbol:" + containerName2 + "\n" "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, Certainty::normal); } void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName) { std::list callstack = { tok, containerTok }; reportError(callstack, Severity::error, "iterators3", "$symbol:" + containerName + "\n" "Same iterator is used with containers '$symbol' that are temporaries or defined in different scopes.", CWE664, Certainty::normal); } // Error message used when dereferencing an iterator that has been erased.. void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername, bool inconclusive) { if (erased) { std::list callstack = { deref, erased }; reportError(callstack, Severity::error, "eraseDereference", "$symbol:" + itername + "\n" "Iterator '$symbol' used after element has been erased.\n" "The iterator '$symbol' is invalid after the element it pointed to has been erased. " "Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive ? Certainty::inconclusive : Certainty::normal); } else { reportError(deref, Severity::error, "eraseDereference", "$symbol:" + itername + "\n" "Invalid iterator '$symbol' used.\n" "The iterator '$symbol' is invalid before being assigned. " "Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive ? Certainty::inconclusive : Certainty::normal); } } static const Token *skipMembers(const Token *tok) { while (Token::Match(tok, "%name% .")) tok = tok->tokAt(2); return tok; } static bool isIterator(const Variable *var, bool& inconclusiveType) { // Check that its an iterator if (!var || !var->isLocal() || !Token::Match(var->typeEndToken(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator|auto")) return false; inconclusiveType = false; if (var->typeEndToken()->str() == "auto") return (var->nameToken()->valueType() && var->nameToken()->valueType()->type == ValueType::Type::ITERATOR); if (var->type()) { // If it is defined, ensure that it is defined like an iterator // look for operator* and operator++ const Function* end = var->type()->getFunction("operator*"); const Function* incOperator = var->type()->getFunction("operator++"); if (!end || end->argCount() > 0 || !incOperator) { return false; } else { inconclusiveType = true; // heuristics only } } return true; } static std::string getContainerName(const Token *containerToken) { if (!containerToken) return std::string(); std::string ret(containerToken->str()); for (const Token *nametok = containerToken; nametok; nametok = nametok->tokAt(-2)) { if (!Token::Match(nametok->tokAt(-2), "%name% .")) break; ret = nametok->strAt(-2) + '.' + ret; } return ret; } enum OperandPosition { Left, Right }; static bool isVector(const Token* tok) { if (!tok) return false; const Variable *var = tok->variable(); const Token *decltok = var ? var->typeStartToken() : nullptr; return Token::simpleMatch(decltok, "std :: vector"); } void CheckStl::iterators() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Filling map of iterators id and their scope begin std::map iteratorScopeBeginInfo; for (const Variable* var : symbolDatabase->variableList()) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) continue; const int iteratorId = var->declarationId(); if (iteratorId != 0) iteratorScopeBeginInfo[iteratorId] = var->nameToken(); } for (const Variable* var : symbolDatabase->variableList()) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) continue; if (inconclusiveType && !mSettings->certainty.isEnabled(Certainty::inconclusive)) continue; const int iteratorId = var->declarationId(); // the validIterator flag says if the iterator has a valid value or not bool validIterator = Token::Match(var->nameToken()->next(), "[(=:{]"); const Scope* invalidationScope = nullptr; // The container this iterator can be used with const Token* containerToken = nullptr; const Scope* containerAssignScope = nullptr; // When "validatingToken" is reached the validIterator is set to true const Token* validatingToken = nullptr; const Token* eraseToken = nullptr; // Scan through the rest of the code and see if the iterator is // used against other containers. for (const Token *tok2 = var->nameToken(); tok2 && tok2 != var->scope()->bodyEnd; tok2 = tok2->next()) { if (invalidationScope && tok2 == invalidationScope->bodyEnd) validIterator = true; // Assume that the iterator becomes valid again if (containerAssignScope && tok2 == containerAssignScope->bodyEnd) containerToken = nullptr; // We don't know which containers might be used with the iterator if (tok2 == validatingToken) { validIterator = true; eraseToken = nullptr; invalidationScope = nullptr; } // Is the iterator used in a insert/erase operation? if (Token::Match(tok2, "%name% . insert|erase ( *| %varid% )|,", iteratorId) && !isVector(tok2)) { const Token* itTok = tok2->tokAt(4); if (itTok->str() == "*") { if (tok2->strAt(2) == "insert") continue; itTok = itTok->next(); } // It is bad to insert/erase an invalid iterator if (!validIterator) invalidIteratorError(tok2, itTok->str()); // If insert/erase is used on different container then // report an error if (containerToken && tok2->varId() != containerToken->varId()) { // skip error message if container is a set.. const Variable *variableInfo = tok2->variable(); const Token *decltok = variableInfo ? variableInfo->typeStartToken() : nullptr; if (Token::simpleMatch(decltok, "std :: set")) continue; // No warning // skip error message if the iterator is erased/inserted by value if (itTok->previous()->str() == "*") continue; // inserting iterator range.. if (tok2->strAt(2) == "insert") { const Token *par2 = itTok->nextArgument(); if (!par2 || par2->nextArgument()) continue; while (par2->str() != ")") { if (par2->varId() == containerToken->varId()) break; bool inconclusiveType2=false; if (isIterator(par2->variable(), inconclusiveType2)) break; // TODO: check if iterator points at same container if (par2->str() == "(") par2 = par2->link(); par2 = par2->next(); } if (par2->str() != ")") continue; } // Not different containers if a reference is used.. if (containerToken && containerToken->variable() && containerToken->variable()->isReference()) { const Token *nameToken = containerToken->variable()->nameToken(); if (Token::Match(nameToken, "%name% =")) { const Token *name1 = nameToken->tokAt(2); const Token *name2 = tok2; while (Token::Match(name1, "%name%|.|::") && name2 && name1->str() == name2->str()) { name1 = name1->next(); name2 = name2->next(); } if (!Token::simpleMatch(name1, ";") || !Token::Match(name2, "[;,()=]")) continue; } } // Show error message, mismatching iterator is used. iteratorsError(tok2, getContainerName(containerToken), getContainerName(tok2)); } // invalidate the iterator if it is erased else if (tok2->strAt(2) == "erase" && (tok2->strAt(4) != "*" || (containerToken && tok2->varId() == containerToken->varId()))) { validIterator = false; eraseToken = tok2; invalidationScope = tok2->scope(); } // skip the operation tok2 = itTok->next(); } // it = foo.erase(.. // taking the result of an erase is ok else if (Token::Match(tok2, "%varid% = %name% .", iteratorId) && Token::simpleMatch(skipMembers(tok2->tokAt(2)), "erase (")) { // the returned iterator is valid validatingToken = skipMembers(tok2->tokAt(2))->linkAt(1); tok2 = validatingToken->link(); } // Reassign the iterator else if (Token::Match(tok2, "%varid% = %name% .", iteratorId) && Token::Match(skipMembers(tok2->tokAt(2)), "begin|rbegin|cbegin|crbegin|find (")) { validatingToken = skipMembers(tok2->tokAt(2))->linkAt(1); containerToken = skipMembers(tok2->tokAt(2))->tokAt(-2); if (containerToken->varId() == 0 || Token::simpleMatch(validatingToken, ") .")) containerToken = nullptr; containerAssignScope = tok2->scope(); // skip ahead tok2 = validatingToken->link(); } // Reassign the iterator else if (Token::Match(tok2, "%varid% =", iteratorId)) { break; } // Passing iterator to function. Iterator might be initialized else if (Token::Match(tok2, "%varid% ,|)", iteratorId)) { validIterator = true; } // Dereferencing invalid iterator? else if (!validIterator && Token::Match(tok2, "* %varid%", iteratorId)) { dereferenceErasedError(eraseToken, tok2, tok2->strAt(1), inconclusiveType); tok2 = tok2->next(); } else if (!validIterator && Token::Match(tok2, "%varid% . %name%", iteratorId)) { dereferenceErasedError(eraseToken, tok2, tok2->str(), inconclusiveType); tok2 = tok2->tokAt(2); } // bailout handling. Assume that the iterator becomes valid if we see return/break. // TODO: better handling else if (tok2->scope() == invalidationScope && Token::Match(tok2, "return|break|continue")) { validatingToken = Token::findsimplematch(tok2->next(), ";"); } // bailout handling. Assume that the iterator becomes valid if we see else. // TODO: better handling else if (tok2->str() == "else") { validIterator = true; } } } } void CheckStl::mismatchingContainerIteratorError(const Token* tok, const Token* iterTok) { const std::string container(tok ? tok->expressionString() : std::string("v1")); const std::string iter(iterTok ? iterTok->expressionString() : std::string("it")); reportError(tok, Severity::error, "mismatchingContainerIterator", "Iterator '" + iter + "' from different container '" + container + "' are used together.", CWE664, Certainty::normal); } // Error message for bad iterator usage.. void CheckStl::mismatchingContainersError(const Token* tok1, const Token* tok2) { const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1")); const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2")); reportError(tok1, Severity::error, "mismatchingContainers", "Iterators of different containers '" + expr1 + "' and '" + expr2 + "' are used together.", CWE664, Certainty::normal); } void CheckStl::mismatchingContainerExpressionError(const Token *tok1, const Token *tok2) { const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1")); const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2")); reportError(tok1, Severity::warning, "mismatchingContainerExpression", "Iterators to containers from different expressions '" + expr1 + "' and '" + expr2 + "' are used together.", CWE664, Certainty::normal); } void CheckStl::sameIteratorExpressionError(const Token *tok) { reportError(tok, Severity::style, "sameIteratorExpression", "Same iterators expression are used for algorithm.", CWE664, Certainty::normal); } static const std::set algorithm2 = { // func(begin1, end1 "binary_search", "copy", "copy_if", "equal_range" , "generate", "is_heap", "is_heap_until", "is_partitioned" , "is_permutation", "is_sorted", "is_sorted_until", "lower_bound", "make_heap", "max_element", "minmax_element" , "min_element", "mismatch", "move", "move_backward", "next_permutation", "partition", "partition_copy" , "partition_point", "pop_heap", "prev_permutation", "push_heap", "random_shuffle", "remove", "remove_copy" , "remove_copy_if", "remove_if", "replace", "replace_copy", "replace_copy_if", "replace_if", "reverse", "reverse_copy" , "shuffle", "sort", "sort_heap", "stable_partition", "stable_sort", "swap_ranges", "transform", "unique" , "unique_copy", "upper_bound", "string", "wstring", "u16string", "u32string" }; static const std::set algorithm22 = { // func(begin1, end1, begin2, end2 "includes", "lexicographical_compare", "merge", "partial_sort_copy" , "set_difference", "set_intersection", "set_symmetric_difference", "set_union" }; static const std::set algorithm1x1 = { // func(begin1, x, end1 "nth_element", "partial_sort", "rotate", "rotate_copy" }; static const std::string iteratorBeginFuncPattern = "begin|cbegin|rbegin|crbegin"; static const std::string iteratorEndFuncPattern = "end|cend|rend|crend"; static const std::string pattern1x1_1 = "%name% . " + iteratorBeginFuncPattern + " ( ) , "; static const std::string pattern1x1_2 = "%name% . " + iteratorEndFuncPattern + " ( ) ,|)"; static const std::string pattern2 = pattern1x1_1 + pattern1x1_2; static const Token * getIteratorExpression(const Token * tok) { if (!tok) return nullptr; if (tok->isUnaryOp("*")) return nullptr; if (!tok->isName()) { const Token *iter1 = getIteratorExpression(tok->astOperand1()); if (iter1) return iter1; if (tok->str() == "(") return nullptr; const Token *iter2 = getIteratorExpression(tok->astOperand2()); if (iter2) return iter2; } else if (Token::Match(tok, "begin|cbegin|rbegin|crbegin|end|cend|rend|crend (")) { if (Token::Match(tok->previous(), ". %name% ( ) !!.")) return tok->previous()->astOperand1(); if (!Token::simpleMatch(tok->previous(), ".") && Token::Match(tok, "%name% ( !!)") && !Token::simpleMatch(tok->linkAt(1), ") .")) return tok->next()->astOperand2(); } return nullptr; } static const Token* getAddressContainer(const Token* tok) { if (Token::simpleMatch(tok, "[") && tok->astOperand1()) return tok->astOperand1(); return tok; } static bool isSameIteratorContainerExpression(const Token* tok1, const Token* tok2, const Library& library, ValueFlow::Value::LifetimeKind kind = ValueFlow::Value::LifetimeKind::Iterator) { if (isSameExpression(true, false, tok1, tok2, library, false, false)) { if (astIsContainerOwned(tok1) && isTemporary(true, tok1, &library)) return false; return true; } if (kind == ValueFlow::Value::LifetimeKind::Address) { return isSameExpression(true, false, getAddressContainer(tok1), getAddressContainer(tok2), library, false, false); } return false; } static ValueFlow::Value getLifetimeIteratorValue(const Token* tok, MathLib::bigint path = 0) { std::vector values = getLifetimeObjValues(tok, false, path); auto it = std::find_if(values.begin(), values.end(), [](const ValueFlow::Value& v) { return v.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator; }); if (it != values.end()) return *it; if (values.size() == 1) return values.front(); return ValueFlow::Value{}; } bool CheckStl::checkIteratorPair(const Token* tok1, const Token* tok2) { if (!tok1) return false; if (!tok2) return false; ValueFlow::Value val1 = getLifetimeIteratorValue(tok1); ValueFlow::Value val2 = getLifetimeIteratorValue(tok2); if (val1.tokvalue && val2.tokvalue && val1.lifetimeKind == val2.lifetimeKind) { if (val1.lifetimeKind == ValueFlow::Value::LifetimeKind::Lambda) return false; if (tok1->astParent() == tok2->astParent() && Token::Match(tok1->astParent(), "%comp%|-")) { if (val1.lifetimeKind == ValueFlow::Value::LifetimeKind::Address) return false; if (val1.lifetimeKind == ValueFlow::Value::LifetimeKind::Object && (!astIsContainer(val1.tokvalue) || !astIsContainer(val2.tokvalue))) return false; } if (isSameIteratorContainerExpression(val1.tokvalue, val2.tokvalue, mSettings->library, val1.lifetimeKind)) return false; if (val1.tokvalue->expressionString() == val2.tokvalue->expressionString()) iteratorsError(tok1, val1.tokvalue, val1.tokvalue->expressionString()); else mismatchingContainersError(val1.tokvalue, val2.tokvalue); return true; } if (Token::Match(tok1->astParent(), "%comp%|-")) { if (astIsIntegral(tok1, false) || astIsIntegral(tok2, false) || astIsFloat(tok1, false) || astIsFloat(tok2, false)) return false; } const Token* iter1 = getIteratorExpression(tok1); const Token* iter2 = getIteratorExpression(tok2); if (iter1 && iter2 && !isSameIteratorContainerExpression(iter1, iter2, mSettings->library)) { mismatchingContainerExpressionError(iter1, iter2); return true; } return false; } struct ArgIteratorInfo { const Token* tok; const Library::ArgumentChecks::IteratorInfo* info; }; void CheckStl::mismatchingContainers() { // Check if different containers are used in various calls of standard functions const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%comp%|-")) { if (checkIteratorPair(tok->astOperand1(), tok->astOperand2())) continue; } if (!Token::Match(tok, "%name% ( !!)")) continue; const Token * const ftok = tok; const std::vector args = getArguments(ftok); if (args.size() < 2) continue; // Group args together by container std::map> containers; for (int argnr = 1; argnr <= args.size(); ++argnr) { const Library::ArgumentChecks::IteratorInfo *i = mSettings->library.getArgIteratorInfo(ftok, argnr); if (!i) continue; const Token * const argTok = args[argnr - 1]; containers[i->container].push_back({argTok, i}); } // Lambda is used to escape the nested loops [&] { for (const auto& p : containers) { const std::vector& cargs = p.second; for (ArgIteratorInfo iter1 : cargs) { for (ArgIteratorInfo iter2 : cargs) { if (iter1.tok == iter2.tok) continue; if (iter1.info->first && iter2.info->last && isSameExpression(true, false, iter1.tok, iter2.tok, mSettings->library, false, false)) sameIteratorExpressionError(iter1.tok); if (checkIteratorPair(iter1.tok, iter2.tok)) return; } } } }(); } } for (const Variable *var : symbolDatabase->variableList()) { if (var && var->isStlStringType() && Token::Match(var->nameToken(), "%var% (") && Token::Match(var->nameToken()->tokAt(2), pattern2.c_str())) { if (var->nameToken()->strAt(2) != var->nameToken()->strAt(8)) { mismatchingContainersError(var->nameToken(), var->nameToken()->tokAt(2)); } } } } void CheckStl::mismatchingContainerIterator() { // Check if different containers are used in various calls of standard functions const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!astIsContainer(tok)) continue; if (!astIsLHS(tok)) continue; if (!Token::Match(tok->astParent(), ". %name% ( !!)")) continue; const Token* const ftok = tok->astParent()->next(); const std::vector args = getArguments(ftok); const Library::Container * c = tok->valueType()->container; Library::Container::Action action = c->getAction(tok->strAt(2)); const Token* iterTok = nullptr; if (action == Library::Container::Action::INSERT && args.size() == 2) { // Skip if iterator pair if (astIsIterator(args.back())) continue; if (!astIsIterator(args.front())) continue; iterTok = args.front(); } else if (action == Library::Container::Action::ERASE) { if (!astIsIterator(args.front())) continue; iterTok = args.front(); } else { continue; } ValueFlow::Value val = getLifetimeIteratorValue(iterTok); if (!val.tokvalue) continue; if (val.lifetimeKind != ValueFlow::Value::LifetimeKind::Iterator) continue; if (isSameIteratorContainerExpression(tok, val.tokvalue, mSettings->library)) continue; mismatchingContainerIteratorError(tok, iterTok); } } } static const Token* getInvalidMethod(const Token* tok) { if (!astIsLHS(tok)) return nullptr; if (Token::Match(tok->astParent(), ". assign|clear|swap")) return tok->astParent()->next(); if (Token::Match(tok->astParent(), "%assign%")) return tok->astParent(); const Token* ftok = nullptr; if (Token::Match(tok->astParent(), ". %name% (")) ftok = tok->astParent()->next(); if (!ftok) return nullptr; if (const Library::Container * c = tok->valueType()->container) { Library::Container::Action action = c->getAction(ftok->str()); if (c->unstableErase) { if (action == Library::Container::Action::ERASE) return ftok; } if (c->unstableInsert) { if (action == Library::Container::Action::RESIZE) return ftok; if (action == Library::Container::Action::CLEAR) return ftok; if (action == Library::Container::Action::PUSH) return ftok; if (action == Library::Container::Action::POP) return ftok; if (action == Library::Container::Action::INSERT) return ftok; if (action == Library::Container::Action::CHANGE) return ftok; if (action == Library::Container::Action::CHANGE_INTERNAL) return ftok; if (Token::Match(ftok, "insert|emplace")) return ftok; } } return nullptr; } struct InvalidContainerAnalyzer { struct Info { struct Reference { const Token* tok; ErrorPath errorPath; const Token* ftok; }; std::unordered_map expressions; ErrorPath errorPath; void add(const std::vector& refs) { for (const Reference& r : refs) { add(r); } } void add(const Reference& r) { if (!r.tok) return; expressions.insert(std::make_pair(r.tok->exprId(), r)); } std::vector invalidTokens() const { std::vector result; std::transform(expressions.begin(), expressions.end(), std::back_inserter(result), SelectMapValues{}); return result; } }; std::unordered_map invalidMethods; std::vector invalidatesContainer(const Token* tok) const { std::vector result; if (Token::Match(tok, "%name% (")) { const Function* f = tok->function(); if (!f) return result; ErrorPathItem epi = std::make_pair(tok, "Calling function " + tok->str()); const bool dependsOnThis = exprDependsOnThis(tok->next()); auto it = invalidMethods.find(f); if (it != invalidMethods.end()) { std::vector refs = it->second.invalidTokens(); std::copy_if(refs.begin(), refs.end(), std::back_inserter(result), [&](const Info::Reference& r) { const Variable* var = r.tok->variable(); if (!var) return false; if (dependsOnThis && !var->isLocal() && !var->isGlobal() && !var->isStatic()) return true; if (!var->isArgument()) return false; if (!var->isReference()) return false; return true; }); std::vector args = getArguments(tok); for (Info::Reference& r : result) { r.errorPath.push_front(epi); r.ftok = tok; const Variable* var = r.tok->variable(); if (!var) continue; if (var->isArgument()) { int n = getArgumentPos(var, f); const Token* tok2 = nullptr; if (n >= 0 && n < args.size()) tok2 = args[n]; r.tok = tok2; } } } } else if (astIsContainer(tok)) { const Token* ftok = getInvalidMethod(tok); if (ftok) { ErrorPath ep; ep.emplace_front(ftok, "After calling '" + ftok->expressionString() + "', iterators or references to the container's data may be invalid ."); result.push_back(Info::Reference{tok, ep, ftok}); } } return result; } void analyze(const SymbolDatabase* symboldatabase) { for (const Scope* scope : symboldatabase->functionScopes) { const Function* f = scope->function; if (!f) continue; for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "if|while|for|goto|return")) break; std::vector c = invalidatesContainer(tok); if (c.empty()) continue; invalidMethods[f].add(c); } } } }; static bool isVariableDecl(const Token* tok) { if (!tok) return false; const Variable* var = tok->variable(); if (!var) return false; if (var->nameToken() == tok) return true; if (Token::Match(var->declEndToken(), "; %var%") && var->declEndToken()->next() == tok) return true; return false; } static const Token* getLoopContainer(const Token* tok) { if (!Token::simpleMatch(tok, "for (")) return nullptr; const Token* sepTok = tok->next()->astOperand2(); if (!Token::simpleMatch(sepTok, ":")) return nullptr; return sepTok->astOperand2(); } void CheckStl::invalidContainer() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); const Library& library = mSettings->library; InvalidContainerAnalyzer analyzer; analyzer.analyze(symbolDatabase); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (const Token* contTok = getLoopContainer(tok)) { const Token* blockStart = tok->next()->link()->next(); const Token* blockEnd = blockStart->link(); if (contTok->exprId() == 0) continue; if (!astIsContainer(contTok)) continue; for (const Token* tok2 = blockStart; tok2 != blockEnd; tok2 = tok2->next()) { bool bail = false; for (const InvalidContainerAnalyzer::Info::Reference& r : analyzer.invalidatesContainer(tok2)) { if (!astIsContainer(r.tok)) continue; if (r.tok->exprId() != contTok->exprId()) continue; const Scope* s = tok2->scope(); if (!s) continue; if (isReturnScope(s->bodyEnd, &mSettings->library)) continue; invalidContainerLoopError(r.ftok, tok, r.errorPath); bail = true; break; } if (bail) break; } } else { for (const InvalidContainerAnalyzer::Info::Reference& r : analyzer.invalidatesContainer(tok)) { if (!astIsContainer(r.tok)) continue; std::set skipVarIds; // Skip if the variable is assigned to const Token* assignExpr = tok; while (assignExpr->astParent()) { bool isRHS = astIsRHS(assignExpr); assignExpr = assignExpr->astParent(); if (Token::Match(assignExpr, "%assign%")) { if (!isRHS) assignExpr = nullptr; break; } } if (Token::Match(assignExpr, "%assign%") && Token::Match(assignExpr->astOperand1(), "%var%")) skipVarIds.insert(assignExpr->astOperand1()->varId()); const Token* endToken = nextAfterAstRightmostLeaf(tok->next()->astParent()); if (!endToken) endToken = tok->next(); const ValueFlow::Value* v = nullptr; ErrorPath errorPath; PathAnalysis::Info info = PathAnalysis{endToken, library}.forwardFind([&](const PathAnalysis::Info& info) { if (!info.tok->variable()) return false; if (info.tok->varId() == 0) return false; if (skipVarIds.count(info.tok->varId()) > 0) return false; // if (Token::simpleMatch(info.tok->next(), ".")) // return false; if (Token::Match(info.tok->astParent(), "%assign%") && astIsLHS(info.tok)) skipVarIds.insert(info.tok->varId()); if (info.tok->variable()->isReference() && !isVariableDecl(info.tok) && reaches(info.tok->variable()->nameToken(), tok, library, nullptr)) { ErrorPath ep; bool addressOf = false; const Variable* var = getLifetimeVariable(info.tok, ep, &addressOf); // Check the reference is created before the change if (var && var->declarationId() == r.tok->varId() && !addressOf) { // An argument always reaches if (var->isArgument() || (!var->isReference() && !var->isRValueReference() && !isVariableDecl(tok) && reaches(var->nameToken(), tok, library, &ep))) { errorPath = ep; return true; } } } for (const ValueFlow::Value& val : info.tok->values()) { if (!val.isLocalLifetimeValue()) continue; if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::Address) continue; if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::SubObject) continue; if (!val.tokvalue->variable()) continue; if (val.tokvalue->varId() != r.tok->varId()) continue; ErrorPath ep; // Check the iterator is created before the change if (val.tokvalue != tok && reaches(val.tokvalue, tok, library, &ep)) { v = &val; errorPath = ep; return true; } } return false; }); if (!info.tok) continue; errorPath.insert(errorPath.end(), info.errorPath.begin(), info.errorPath.end()); errorPath.insert(errorPath.end(), r.errorPath.begin(), r.errorPath.end()); if (v) { invalidContainerError(info.tok, r.tok, v, errorPath); } else { invalidContainerReferenceError(info.tok, r.tok, errorPath); } } } } } } void CheckStl::invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath) { const std::string method = tok ? tok->str() : "erase"; errorPath.emplace_back(loopTok, "Iterating container here."); // Remove duplicate entries from error path errorPath.remove_if([&](const ErrorPathItem& epi) { return epi.first == tok; }); const std::string msg = "Calling '" + method + "' while iterating the container is invalid."; errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "invalidContainerLoop", msg, CWE664, Certainty::normal); } void CheckStl::invalidContainerError(const Token *tok, const Token * /*contTok*/, const ValueFlow::Value *val, ErrorPath errorPath) { const bool inconclusive = val ? val->isInconclusive() : false; if (val) errorPath.insert(errorPath.begin(), val->errorPath.begin(), val->errorPath.end()); std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "invalidContainer", msg + " that may be invalid.", CWE664, inconclusive ? Certainty::inconclusive : Certainty::normal); } void CheckStl::invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath) { std::string name = contTok ? contTok->expressionString() : "x"; std::string msg = "Reference to " + name; errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "invalidContainerReference", msg + " that may be invalid.", CWE664, Certainty::normal); } void CheckStl::stlOutOfBounds() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Scan through all scopes.. for (const Scope &scope : symbolDatabase->scopeList) { const Token* tok = scope.classDef; // only interested in conditions if ((!scope.isLoopScope() && scope.type != Scope::eIf) || !tok) continue; const Token *condition = nullptr; if (scope.type == Scope::eFor) { if (Token::simpleMatch(tok->next()->astOperand2(), ";") && Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) condition = tok->next()->astOperand2()->astOperand2()->astOperand1(); } else if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok->linkAt(1), "} while (")) condition = tok->linkAt(1)->tokAt(2)->astOperand2(); else condition = tok->next()->astOperand2(); if (!condition) continue; std::vector conds; visitAstNodes(condition, [&](const Token *cond) { if (Token::Match(cond, "%oror%|&&")) return ChildrenToVisit::op1_and_op2; if (cond->isComparisonOp()) conds.emplace_back(cond); return ChildrenToVisit::none; }); for (const Token *cond : conds) { const Token *vartok; const Token *containerToken; // check in the ast that cond is of the form "%var% <= %var% . %name% ( )" if (cond->str() == "<=" && Token::Match(cond->astOperand1(), "%var%") && cond->astOperand2()->str() == "(" && cond->astOperand2()->astOperand1()->str() == "." && Token::Match(cond->astOperand2()->astOperand1()->astOperand1(), "%var%") && Token::Match(cond->astOperand2()->astOperand1()->astOperand2(), "%name%")) { vartok = cond->astOperand1(); containerToken = cond->next(); } else { continue; } if (containerToken->hasKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE)) continue; // Is it a array like container? const Library::Container* container = containerToken->valueType() ? containerToken->valueType()->container : nullptr; if (!container) continue; if (container->getYield(containerToken->strAt(2)) != Library::Container::Yield::SIZE) continue; // variable id for loop variable. const int numId = vartok->varId(); // variable id for the container variable const int declarationId = containerToken->varId(); const std::string &containerName = containerToken->str(); for (const Token *tok3 = scope.bodyStart; tok3 && tok3 != scope.bodyEnd; tok3 = tok3->next()) { if (tok3->varId() == declarationId) { tok3 = tok3->next(); if (Token::Match(tok3, ". %name% ( )")) { if (container->getYield(tok3->strAt(1)) == Library::Container::Yield::SIZE) break; } else if (container->arrayLike_indexOp && Token::Match(tok3, "[ %varid% ]", numId)) stlOutOfBoundsError(tok3, tok3->strAt(1), containerName, false); else if (Token::Match(tok3, ". %name% ( %varid% )", numId)) { const Library::Container::Yield yield = container->getYield(tok3->strAt(1)); if (yield == Library::Container::Yield::AT_INDEX) stlOutOfBoundsError(tok3, tok3->strAt(3), containerName, true); } } } } } } void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at) { if (at) reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol.at(" + num + ") is out of bounds.", CWE788, Certainty::normal); else reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol[" + num + "] is out of bounds.", CWE788, Certainty::normal); } void CheckStl::negativeIndex() { // Negative index is out of bounds.. const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%var% [") || WRONG_DATA(!tok->next()->astOperand2(), tok)) continue; const Variable * const var = tok->variable(); if (!var || tok == var->nameToken()) continue; const Library::Container * const container = mSettings->library.detectContainer(var->typeStartToken()); if (!container || !container->arrayLike_indexOp) continue; const ValueFlow::Value *index = tok->next()->astOperand2()->getValueLE(-1, mSettings); if (!index) continue; negativeIndexError(tok, *index); } } } void CheckStl::negativeIndexError(const Token *tok, const ValueFlow::Value &index) { const ErrorPath errorPath = getErrorPath(tok, &index, "Negative array index"); std::ostringstream errmsg; if (index.condition) errmsg << ValueFlow::eitherTheConditionIsRedundant(index.condition) << ", otherwise there is negative array index " << index.intvalue << "."; else errmsg << "Array index " << index.intvalue << " is out of bounds."; reportError(errorPath, index.errorSeverity() ? Severity::error : Severity::warning, "negativeContainerIndex", errmsg.str(), CWE786, index.isInconclusive() ? Certainty::inconclusive : Certainty::normal); } void CheckStl::erase() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type == Scope::eFor && Token::simpleMatch(scope.classDef, "for (")) { const Token *tok = scope.classDef->linkAt(1); if (!Token::Match(tok->tokAt(-3), "; ++| %var% ++| ) {")) continue; tok = tok->previous(); if (!tok->isName()) tok = tok->previous(); eraseCheckLoopVar(scope, tok->variable()); } else if (scope.type == Scope::eWhile && Token::Match(scope.classDef, "while ( %var% !=")) { eraseCheckLoopVar(scope, scope.classDef->tokAt(2)->variable()); } } } void CheckStl::eraseCheckLoopVar(const Scope &scope, const Variable *var) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) return; for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() != "(") continue; if (!Token::Match(tok->tokAt(-2), ". erase ( ++| %varid% )", var->declarationId())) continue; // Vector erases are handled by invalidContainer check if (isVector(tok->tokAt(-3))) continue; if (Token::Match(tok->astParent(), "=|return")) continue; // Iterator is invalid.. int indentlevel = 0U; const Token *tok2 = tok->link(); for (; tok2 != scope.bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") { ++indentlevel; continue; } if (tok2->str() == "}") { if (indentlevel > 0U) --indentlevel; else if (Token::simpleMatch(tok2, "} else {")) tok2 = tok2->linkAt(2); continue; } if (tok2->varId() == var->declarationId()) { if (Token::simpleMatch(tok2->next(), "=")) break; dereferenceErasedError(tok, tok2, tok2->str(), inconclusiveType); break; } if (indentlevel == 0U && Token::Match(tok2, "break|return|goto")) break; } if (tok2 == scope.bodyEnd) dereferenceErasedError(tok, scope.classDef, var->nameToken()->str(), inconclusiveType); } } void CheckStl::stlBoundaries() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->scope() || !var->scope()->isExecutable()) continue; const Library::Container* container = mSettings->library.detectContainer(var->typeStartToken(), true); if (!container || container->opLessAllowed) continue; const Token* const end = var->scope()->bodyEnd; for (const Token *tok = var->nameToken(); tok != end; tok = tok->next()) { if (Token::Match(tok, "!!* %varid% <", var->declarationId())) { stlBoundariesError(tok); } else if (Token::Match(tok, "> %varid% !!.", var->declarationId())) { stlBoundariesError(tok); } } } } // Error message for bad boundary usage.. void CheckStl::stlBoundariesError(const Token *tok) { reportError(tok, Severity::error, "stlBoundaries", "Dangerous comparison using operator< on iterator.\n" "Iterator compared with operator<. This is dangerous since the order of items in the " "container is not guaranteed. One should use operator!= instead to compare iterators.", CWE664, Certainty::normal); } static bool if_findCompare(const Token * const tokBack, bool stdStringLike) { const Token *tok = tokBack->astParent(); if (!tok) return true; if (tok->isComparisonOp()) { if (stdStringLike) { const Token * const tokOther = tokBack->astSibling(); return !tokOther || !tokOther->hasKnownIntValue() || tokOther->getKnownIntValue() != 0; } return (!tok->astOperand1()->isNumber() && !tok->astOperand2()->isNumber()); } if (tok->isArithmeticalOp()) // result is used in some calculation return true; // TODO: check if there is a comparison of the result somewhere if (tok->str() == ".") return true; // Dereferencing is OK, the programmer might know that the element exists - TODO: An inconclusive warning might be appropriate if (tok->isAssignmentOp()) return if_findCompare(tok, stdStringLike); // Go one step upwards in the AST return false; } void CheckStl::if_find() { const bool printWarning = mSettings->severity.isEnabled(Severity::warning); const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); if (!printWarning && !printPerformance) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if ((scope.type != Scope::eIf && scope.type != Scope::eWhile) || !scope.classDef) continue; const Token *conditionStart = scope.classDef->next(); if (Token::simpleMatch(conditionStart->astOperand2(), ";")) conditionStart = conditionStart->astOperand2(); for (const Token *tok = conditionStart; tok->str() != "{"; tok = tok->next()) { const Token* funcTok = nullptr; const Library::Container* container = nullptr; if (Token::Match(tok, "%name% (")) tok = tok->linkAt(1); else if (tok->variable() && Token::Match(tok, "%var% . %name% (")) { container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); funcTok = tok->tokAt(2); } // check also for vector-like or pointer containers else if (tok->variable() && tok->astParent() && (tok->astParent()->str() == "*" || tok->astParent()->str() == "[")) { const Token *tok2 = tok->astParent(); if (!Token::Match(tok2->astParent(), ". %name% (")) continue; funcTok = tok2->astParent()->next(); if (tok->variable()->isArrayOrPointer()) container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); else { // Container of container - find the inner container container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); // outer container tok2 = Token::findsimplematch(tok->variable()->typeStartToken(), "<", tok->variable()->typeEndToken()); if (container && container->type_templateArgNo >= 0 && tok2) { tok2 = tok2->next(); for (int j = 0; j < container->type_templateArgNo; j++) tok2 = tok2->nextTemplateArgument(); container = mSettings->library.detectContainer(tok2); // innner container } else container = nullptr; } } if (container && container->getAction(funcTok->str()) == Library::Container::Action::FIND) { if (if_findCompare(funcTok->next(), container->stdStringLike)) continue; if (printWarning && container->getYield(funcTok->str()) == Library::Container::Yield::ITERATOR) if_findError(tok, false); else if (printPerformance && container->stdStringLike && funcTok->str() == "find") if_findError(tok, true); } else if (printWarning && Token::Match(tok, "std :: find|find_if (")) { // check that result is checked properly if (!if_findCompare(tok->tokAt(3), false)) { if_findError(tok, false); } } } } } void CheckStl::if_findError(const Token *tok, bool str) { if (str && mSettings->standards.cpp >= Standards::CPP20) reportError(tok, Severity::performance, "stlIfStrFind", "Inefficient usage of string::find() in condition; string::starts_with() could be faster.\n" "Either inefficient or wrong usage of string::find(). string::starts_with() will be faster if " "string::find's result is compared with 0, because it will not scan the whole " "string. If your intention is to check that there are no findings in the string, " "you should compare with std::string::npos.", CWE597, Certainty::normal); if (!str) reportError(tok, Severity::warning, "stlIfFind", "Suspicious condition. The result of find() is an iterator, but it is not properly checked.", CWE398, Certainty::normal); } static std::pair isMapFind(const Token *tok) { if (!Token::simpleMatch(tok, "(")) return {}; if (!Token::simpleMatch(tok->astOperand1(), ".")) return {}; if (!astIsContainer(tok->astOperand1()->astOperand1())) return {}; const Token * contTok = tok->astOperand1()->astOperand1(); const Library::Container * container = contTok->valueType()->container; if (!container) return {}; if (!container->stdAssociativeLike) return {}; if (!Token::Match(tok->astOperand1(), ". find|count (")) return {}; if (!tok->astOperand2()) return {}; return {contTok, tok->astOperand2()}; } static const Token *skipLocalVars(const Token *tok) { if (!tok) return tok; if (Token::simpleMatch(tok, "{")) return skipLocalVars(tok->next()); const Scope *scope = tok->scope(); const Token *top = tok->astTop(); if (!top) { const Token *semi = Token::findsimplematch(tok, ";"); if (!semi) return tok; if (!Token::Match(semi->previous(), "%var% ;")) return tok; const Token *varTok = semi->previous(); const Variable *var = varTok->variable(); if (!var) return tok; if (var->nameToken() != varTok) return tok; return skipLocalVars(semi->next()); } if (Token::Match(top, "%assign%")) { const Token *varTok = top->astOperand1(); if (!Token::Match(varTok, "%var%")) return tok; const Variable *var = varTok->variable(); if (!var) return tok; if (var->scope() != scope) return tok; const Token *endTok = nextAfterAstRightmostLeaf(top); if (!endTok) return tok; return skipLocalVars(endTok->next()); } return tok; } static const Token *findInsertValue(const Token *tok, const Token *containerTok, const Token *keyTok, const Library &library) { const Token *startTok = skipLocalVars(tok); const Token *top = startTok->astTop(); const Token *icontainerTok = nullptr; const Token *ikeyTok = nullptr; const Token *ivalueTok = nullptr; if (Token::simpleMatch(top, "=") && Token::simpleMatch(top->astOperand1(), "[")) { icontainerTok = top->astOperand1()->astOperand1(); ikeyTok = top->astOperand1()->astOperand2(); ivalueTok = top->astOperand2(); } if (Token::simpleMatch(top, "(") && Token::Match(top->astOperand1(), ". insert|emplace (") && !astIsIterator(top->astOperand1()->tokAt(2))) { icontainerTok = top->astOperand1()->astOperand1(); const Token *itok = top->astOperand1()->tokAt(2)->astOperand2(); if (Token::simpleMatch(itok, ",")) { ikeyTok = itok->astOperand1(); ivalueTok = itok->astOperand2(); } else { ikeyTok = itok; } } if (!ikeyTok || !icontainerTok) return nullptr; if (isSameExpression(true, true, containerTok, icontainerTok, library, true, false) && isSameExpression(true, true, keyTok, ikeyTok, library, true, true)) { if (ivalueTok) return ivalueTok; else return ikeyTok; } return nullptr; } void CheckStl::checkFindInsert() { if (!mSettings->severity.isEnabled(Severity::performance)) return; const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; if (!Token::simpleMatch(tok->next()->link(), ") {")) continue; if (!Token::Match(tok->next()->astOperand2(), "%comp%")) continue; const Token *condTok = tok->next()->astOperand2(); const Token *containerTok; const Token *keyTok; std::tie(containerTok, keyTok) = isMapFind(condTok->astOperand1()); if (!containerTok) continue; // In < C++17 we only warn for small simple types if (mSettings->standards.cpp < Standards::CPP17 && !(keyTok && keyTok->valueType() && (keyTok->valueType()->isIntegral() || keyTok->valueType()->pointer > 0))) continue; const Token *thenTok = tok->next()->link()->next(); const Token *valueTok = findInsertValue(thenTok, containerTok, keyTok, mSettings->library); if (!valueTok) continue; if (Token::simpleMatch(thenTok->link(), "} else {")) { const Token *valueTok2 = findInsertValue(thenTok->link()->tokAt(2), containerTok, keyTok, mSettings->library); if (!valueTok2) continue; if (isSameExpression(true, true, valueTok, valueTok2, mSettings->library, true, true)) { checkFindInsertError(valueTok); } } else { checkFindInsertError(valueTok); } } } } void CheckStl::checkFindInsertError(const Token *tok) { std::string replaceExpr; if (tok && Token::simpleMatch(tok->astParent(), "=") && tok == tok->astParent()->astOperand2() && Token::simpleMatch(tok->astParent()->astOperand1(), "[")) { if (mSettings->standards.cpp < Standards::CPP11) // We will recommend using emplace/try_emplace instead return; const std::string f = (mSettings->standards.cpp < Standards::CPP17) ? "emplace" : "try_emplace"; replaceExpr = " Instead of '" + tok->astParent()->expressionString() + "' consider using '" + tok->astParent()->astOperand1()->astOperand1()->expressionString() + "." + f + "(" + tok->astParent()->astOperand1()->astOperand2()->expressionString() + ", " + tok->expressionString() + ");'."; } reportError( tok, Severity::performance, "stlFindInsert", "Searching before insertion is not necessary." + replaceExpr, CWE398, Certainty::normal); } /** * Is container.size() slow? */ static bool isCpp03ContainerSizeSlow(const Token *tok) { if (!tok) return false; const Variable* var = tok->variable(); return var && var->isStlType("list"); } void CheckStl::size() { if (!mSettings->severity.isEnabled(Severity::performance)) return; if (mSettings->standards.cpp >= Standards::CPP11) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%var% . size ( )") || Token::Match(tok, "%name% . %var% . size ( )")) { // get the variable const Token *varTok = tok; if (tok->strAt(2) != "size") varTok = varTok->tokAt(2); const Token* const end = varTok->tokAt(5); // check for comparison to zero if ((!tok->previous()->isArithmeticalOp() && Token::Match(end, "==|<=|!=|> 0")) || (end->next() && !end->next()->isArithmeticalOp() && Token::Match(tok->tokAt(-2), "0 ==|>=|!=|<"))) { if (isCpp03ContainerSizeSlow(varTok)) { sizeError(varTok); continue; } } // check for comparison to one if ((!tok->previous()->isArithmeticalOp() && Token::Match(end, ">=|< 1") && !end->tokAt(2)->isArithmeticalOp()) || (end->next() && !end->next()->isArithmeticalOp() && Token::Match(tok->tokAt(-2), "1 <=|>") && !tok->tokAt(-3)->isArithmeticalOp())) { if (isCpp03ContainerSizeSlow(varTok)) sizeError(varTok); } // check for using as boolean expression else if ((Token::Match(tok->tokAt(-2), "if|while (") && end->str() == ")") || (tok->previous()->tokType() == Token::eLogicalOp && Token::Match(end, "&&|)|,|;|%oror%"))) { if (isCpp03ContainerSizeSlow(varTok)) sizeError(varTok); } } } } } void CheckStl::sizeError(const Token *tok) { const std::string varname = tok ? tok->str() : std::string("list"); reportError(tok, Severity::performance, "stlSize", "$symbol:" + varname + "\n" "Possible inefficient checking for '$symbol' emptiness.\n" "Checking for '$symbol' emptiness might be inefficient. " "Using $symbol.empty() instead of $symbol.size() can be faster. " "$symbol.size() can take linear time but $symbol.empty() is " "guaranteed to take constant time.", CWE398, Certainty::normal); } void CheckStl::redundantCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; const Token* tok = scope.classDef->tokAt(2); if (!Token::Match(tok, "%name% . find ( %any% ) != %name% . end|rend|cend|crend ( ) ) { %name% . remove|erase ( %any% ) ;")) continue; // Get tokens for the fields %name% and %any% const Token *var1 = tok; const Token *any1 = var1->tokAt(4); const Token *var2 = any1->tokAt(3); const Token *var3 = var2->tokAt(7); const Token *any2 = var3->tokAt(4); // Check if all the "%name%" fields are the same and if all the "%any%" are the same.. if (var1->str() == var2->str() && var2->str() == var3->str() && any1->str() == any2->str()) { redundantIfRemoveError(tok); } } } void CheckStl::redundantIfRemoveError(const Token *tok) { reportError(tok, Severity::style, "redundantIfRemove", "Redundant checking of STL container element existence before removing it.\n" "Redundant checking of STL container element existence before removing it. " "It is safe to call the remove method on a non-existing element.", CWE398, Certainty::normal); } void CheckStl::missingComparison() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eFor || !scope.classDef) continue; for (const Token *tok2 = scope.classDef->tokAt(2); tok2 != scope.bodyStart; tok2 = tok2->next()) { if (tok2->str() == ";") break; if (!Token::Match(tok2, "%var% = %name% . begin|rbegin|cbegin|crbegin ( ) ; %name% != %name% . end|rend|cend|crend ( ) ; ++| %name% ++| ) {")) continue; // same container if (tok2->strAt(2) != tok2->strAt(10)) break; const int iteratorId(tok2->varId()); // same iterator if (iteratorId == tok2->tokAt(10)->varId()) break; // increment iterator if (!Token::Match(tok2->tokAt(16), "++ %varid% )", iteratorId) && !Token::Match(tok2->tokAt(16), "%varid% ++ )", iteratorId)) { break; } const Token *incrementToken = nullptr; // Parse loop.. for (const Token *tok3 = scope.bodyStart; tok3 != scope.bodyEnd; tok3 = tok3->next()) { if (tok3->varId() == iteratorId) { if (Token::Match(tok3, "%varid% = %name% . insert ( ++| %varid% ++| ,", iteratorId)) { // skip insertion.. tok3 = tok3->linkAt(6); if (!tok3) break; } else if (Token::simpleMatch(tok3->astParent(), "++")) incrementToken = tok3; else if (Token::simpleMatch(tok3->astParent(), "+")) { if (Token::Match(tok3->astSibling(), "%num%")) { const Token* tokenGrandParent = tok3->astParent()->astParent(); if (Token::Match(tokenGrandParent, "==|!=")) break; } } else if (Token::Match(tok3->astParent(), "==|!=")) incrementToken = nullptr; } else if (tok3->str() == "break" || tok3->str() == "return") incrementToken = nullptr; } if (incrementToken) missingComparisonError(incrementToken, tok2->tokAt(16)); } } } void CheckStl::missingComparisonError(const Token *incrementToken1, const Token *incrementToken2) { std::list callstack = { incrementToken1,incrementToken2 }; std::ostringstream errmsg; errmsg << "Missing bounds check for extra iterator increment in loop.\n" << "The iterator incrementing is suspicious - it is incremented at line "; if (incrementToken1) errmsg << incrementToken1->linenr(); errmsg << " and then at line "; if (incrementToken2) errmsg << incrementToken2->linenr(); errmsg << ". The loop might unintentionally skip an element in the container. " << "There is no comparison between these increments to prevent that the iterator is " << "incremented beyond the end."; reportError(callstack, Severity::warning, "StlMissingComparison", errmsg.str(), CWE834, Certainty::normal); } static bool isLocal(const Token *tok) { const Variable *var = tok->variable(); return var && !var->isStatic() && var->isLocal(); } namespace { const std::set stl_string_stream = { "istringstream", "ostringstream", "stringstream", "wstringstream" }; } void CheckStl::string_c_str() { const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); // Find all functions that take std::string as argument std::multimap c_strFuncParam; if (printPerformance) { for (const Scope &scope : symbolDatabase->scopeList) { for (const Function &func : scope.functionList) { int numpar = 0; for (const Variable &var : func.argumentList) { numpar++; if (var.isStlStringType() && (!var.isReference() || var.isConst())) c_strFuncParam.insert(std::make_pair(&func, numpar)); } } } } // Try to detect common problems when using string::c_str() for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eFunction || !scope.function) continue; enum {charPtr, stdString, stdStringConstRef, Other} returnType = Other; if (Token::Match(scope.function->tokenDef->tokAt(-2), "char|wchar_t *")) returnType = charPtr; else if (Token::Match(scope.function->tokenDef->tokAt(-5), "const std :: string|wstring &")) returnType = stdStringConstRef; else if (Token::Match(scope.function->tokenDef->tokAt(-3), "std :: string|wstring !!&")) returnType = stdString; for (const Token *tok = scope.bodyStart; tok && tok != scope.bodyEnd; tok = tok->next()) { // Invalid usage.. if (Token::Match(tok, "throw %var% . c_str|data ( ) ;") && isLocal(tok->next()) && tok->next()->variable() && tok->next()->variable()->isStlStringType()) { string_c_strThrowError(tok); } else if (tok->variable() && tok->strAt(1) == "=") { if (Token::Match(tok->tokAt(2), "%var% . str ( ) . c_str|data ( ) ;")) { const Variable* var = tok->variable(); const Variable* var2 = tok->tokAt(2)->variable(); if (var->isPointer() && var2 && var2->isStlType(stl_string_stream)) string_c_strError(tok); } else if (Token::Match(tok->tokAt(2), "%name% (") && Token::Match(tok->linkAt(3), ") . c_str|data ( ) ;") && tok->tokAt(2)->function() && Token::Match(tok->tokAt(2)->function()->retDef, "std :: string|wstring %name%")) { const Variable* var = tok->variable(); if (var->isPointer()) string_c_strError(tok); } } else if (printPerformance && tok->function() && Token::Match(tok, "%name% ( !!)") && c_strFuncParam.find(tok->function()) != c_strFuncParam.end() && tok->str() != scope.className) { const std::pair::const_iterator, std::multimap::const_iterator> range = c_strFuncParam.equal_range(tok->function()); for (std::multimap::const_iterator i = range.first; i != range.second; ++i) { if (i->second == 0) continue; const Token* tok2 = tok->tokAt(2); int j; for (j = 0; tok2 && j < i->second-1; j++) tok2 = tok2->nextArgument(); if (tok2) tok2 = tok2->nextArgument(); else break; if (!tok2 && j == i->second-1) tok2 = tok->next()->link(); else if (tok2) tok2 = tok2->previous(); else break; if (tok2 && Token::Match(tok2->tokAt(-4), ". c_str|data ( )")) { const Variable* var = tok2->tokAt(-5)->variable(); if (var && var->isStlStringType()) { string_c_strParam(tok, i->second); } else if (Token::Match(tok2->tokAt(-9), "%name% . str ( )")) { // Check ss.str().c_str() as parameter const Variable* ssVar = tok2->tokAt(-9)->variable(); if (ssVar && ssVar->isStlType(stl_string_stream)) string_c_strParam(tok, i->second); } } } } // Using c_str() to get the return value is only dangerous if the function returns a char* else if ((returnType == charPtr || (printPerformance && (returnType == stdString || returnType == stdStringConstRef))) && tok->str() == "return") { bool err = false; const Token* tok2 = tok->next(); if (Token::Match(tok2, "std :: string|wstring (") && Token::Match(tok2->linkAt(3), ") . c_str|data ( ) ;")) { err = true; } else if (Token::simpleMatch(tok2, "(") && Token::Match(tok2->link(), ") . c_str|data ( ) ;")) { // Check for "+ localvar" or "+ std::string(" inside the bracket bool is_implicit_std_string = printInconclusive; const Token *search_end = tok2->link(); for (const Token *search_tok = tok2->next(); search_tok != search_end; search_tok = search_tok->next()) { if (Token::Match(search_tok, "+ %var%") && isLocal(search_tok->next()) && search_tok->next()->variable() && search_tok->next()->variable()->isStlStringType()) { is_implicit_std_string = true; break; } else if (Token::Match(search_tok, "+ std :: string|wstring (")) { is_implicit_std_string = true; break; } } if (is_implicit_std_string) err = true; } bool local = false; bool ptrOrRef = false; const Variable* lastVar = nullptr; const Function* lastFunc = nullptr; bool funcStr = false; if (Token::Match(tok2, "%var% .")) { local = isLocal(tok2); bool refToNonLocal = false; if (tok2->variable() && tok2->variable()->isReference()) { const Token *refTok = tok2->variable()->nameToken(); refToNonLocal = true; // safe assumption is default to avoid FPs if (Token::Match(refTok, "%var% = %var% .|;|[")) refToNonLocal = !isLocal(refTok->tokAt(2)); } ptrOrRef = refToNonLocal || (tok2->variable() && tok2->variable()->isPointer()); } while (tok2) { if (Token::Match(tok2, "%var% .|::")) { if (ptrOrRef) local = false; lastVar = tok2->variable(); tok2 = tok2->tokAt(2); } else if (Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") .")) { lastFunc = tok2->function(); local = false; funcStr = tok2->str() == "str"; tok2 = tok2->linkAt(1)->tokAt(2); } else break; } if (Token::Match(tok2, "c_str|data ( ) ;")) { if ((local || returnType != charPtr) && lastVar && lastVar->isStlStringType()) err = true; else if (funcStr && lastVar && lastVar->isStlType(stl_string_stream)) err = true; else if (lastFunc && Token::Match(lastFunc->tokenDef->tokAt(-3), "std :: string|wstring")) err = true; } if (err) { if (returnType == charPtr) string_c_strError(tok); else string_c_strReturn(tok); } } } } } void CheckStl::string_c_strThrowError(const Token* tok) { reportError(tok, Severity::error, "stlcstrthrow", "Dangerous usage of c_str(). The value returned by c_str() is invalid after throwing exception.\n" "Dangerous usage of c_str(). The string is destroyed after the c_str() call so the thrown pointer is invalid."); } void CheckStl::string_c_strError(const Token* tok) { reportError(tok, Severity::error, "stlcstr", "Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n" "Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted.", CWE664, Certainty::normal); } void CheckStl::string_c_strReturn(const Token* tok) { reportError(tok, Severity::performance, "stlcstrReturn", "Returning the result of c_str() in a function that returns std::string is slow and redundant.\n" "The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly returning the string.", CWE704, Certainty::normal); } void CheckStl::string_c_strParam(const Token* tok, nonneg int number) { std::ostringstream oss; oss << "Passing the result of c_str() to a function that takes std::string as argument no. " << number << " is slow and redundant.\n" "The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly passing the string."; reportError(tok, Severity::performance, "stlcstrParam", oss.str(), CWE704, Certainty::normal); } //--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- namespace { const std::set stl_containers_with_empty_and_clear = { "deque", "forward_list", "list", "map", "multimap", "multiset", "set", "string", "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector", "wstring" }; } void CheckStl::uselessCalls() { const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); if (!printPerformance && !printWarning) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (printWarning && Token::Match(tok, "%var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %name% [,)]") && tok->varId() == tok->tokAt(4)->varId()) { const Variable* var = tok->variable(); if (!var || !var->isStlType()) continue; uselessCallsReturnValueError(tok->tokAt(4), tok->str(), tok->strAt(2)); } else if (printPerformance && Token::Match(tok, "%var% . swap ( %name% )") && tok->varId() == tok->tokAt(4)->varId()) { const Variable* var = tok->variable(); if (!var || !var->isStlType()) continue; uselessCallsSwapError(tok, tok->str()); } else if (printPerformance && Token::Match(tok, "%var% . substr (") && tok->variable() && tok->variable()->isStlStringType()) { if (Token::Match(tok->tokAt(4), "0| )")) { uselessCallsSubstrError(tok, false); } else if (tok->strAt(4) == "0" && tok->linkAt(3)->strAt(-1) == "npos") { if (!tok->linkAt(3)->previous()->variable()) // Make sure that its no variable uselessCallsSubstrError(tok, false); } else if (Token::simpleMatch(tok->linkAt(3)->tokAt(-2), ", 0 )")) uselessCallsSubstrError(tok, true); } else if (printWarning && Token::Match(tok, "[{};] %var% . empty ( ) ;") && !tok->tokAt(4)->astParent() && tok->next()->variable() && tok->next()->variable()->isStlType(stl_containers_with_empty_and_clear)) uselessCallsEmptyError(tok->next()); else if (Token::Match(tok, "[{};] std :: remove|remove_if|unique (") && tok->tokAt(5)->nextArgument()) uselessCallsRemoveError(tok->next(), tok->strAt(3)); } } } void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string &varname, const std::string &function) { std::ostringstream errmsg; errmsg << "$symbol:" << varname << '\n'; errmsg << "$symbol:" << function << '\n'; errmsg << "It is inefficient to call '" << varname << "." << function << "(" << varname << ")' as it always returns 0.\n" << "'std::string::" << function << "()' returns zero when given itself as parameter " << "(" << varname << "." << function << "(" << varname << ")). As it is currently the " << "code is inefficient. It is possible either the string searched ('" << varname << "') or searched for ('" << varname << "') is wrong."; reportError(tok, Severity::warning, "uselessCallsCompare", errmsg.str(), CWE628, Certainty::normal); } void CheckStl::uselessCallsSwapError(const Token *tok, const std::string &varname) { reportError(tok, Severity::performance, "uselessCallsSwap", "$symbol:" + varname + "\n" "It is inefficient to swap a object with itself by calling '$symbol.swap($symbol)'\n" "The 'swap()' function has no logical effect when given itself as parameter " "($symbol.swap($symbol)). As it is currently the " "code is inefficient. Is the object or the parameter wrong here?", CWE628, Certainty::normal); } void CheckStl::uselessCallsSubstrError(const Token *tok, bool empty) { if (empty) reportError(tok, Severity::performance, "uselessCallsSubstr", "Ineffective call of function 'substr' because it returns an empty string.", CWE398, Certainty::normal); else reportError(tok, Severity::performance, "uselessCallsSubstr", "Ineffective call of function 'substr' because it returns a copy of the object. Use operator= instead.", CWE398, Certainty::normal); } void CheckStl::uselessCallsEmptyError(const Token *tok) { reportError(tok, Severity::warning, "uselessCallsEmpty", "Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead?", CWE398, Certainty::normal); } void CheckStl::uselessCallsRemoveError(const Token *tok, const std::string& function) { reportError(tok, Severity::warning, "uselessCallsRemove", "$symbol:" + function + "\n" "Return value of std::$symbol() ignored. Elements remain in container.\n" "The return value of std::$symbol() is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. " "Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them.", CWE762, Certainty::normal); } // Check for iterators being dereferenced before being checked for validity. // E.g. if (*i && i != str.end()) { } void CheckStl::checkDereferenceInvalidIterator() { if (!mSettings->severity.isEnabled(Severity::warning)) return; // Iterate over "if", "while", and "for" conditions where there may // be an iterator that is dereferenced before being checked for validity. for (const Scope &scope : mTokenizer->getSymbolDatabase()->scopeList) { if (!(scope.type == Scope::eIf || scope.isLoopScope())) continue; const Token* const tok = scope.classDef; const Token* startOfCondition = tok->next(); if (scope.type == Scope::eDo) startOfCondition = startOfCondition->link()->tokAt(2); if (!startOfCondition) // ticket #6626 invalid code continue; const Token* endOfCondition = startOfCondition->link(); if (!endOfCondition) continue; // For "for" loops, only search between the two semicolons if (scope.type == Scope::eFor) { startOfCondition = Token::findsimplematch(tok->tokAt(2), ";", endOfCondition); if (!startOfCondition) continue; endOfCondition = Token::findsimplematch(startOfCondition->next(), ";", endOfCondition); if (!endOfCondition) continue; } // Only consider conditions composed of all "&&" terms and // conditions composed of all "||" terms const bool isOrExpression = Token::findsimplematch(startOfCondition, "||", endOfCondition) != nullptr; const bool isAndExpression = Token::findsimplematch(startOfCondition, "&&", endOfCondition) != nullptr; // Look for a check of the validity of an iterator const Token* validityCheckTok = nullptr; if (!isOrExpression && isAndExpression) { validityCheckTok = Token::findmatch(startOfCondition, "&& %var% != %name% . end|rend|cend|crend ( )", endOfCondition); } else if (isOrExpression && !isAndExpression) { validityCheckTok = Token::findmatch(startOfCondition, "%oror% %var% == %name% . end|rend|cend|crend ( )", endOfCondition); } if (!validityCheckTok) continue; const int iteratorVarId = validityCheckTok->next()->varId(); // If the iterator dereference is to the left of the check for // the iterator's validity, report an error. const Token* const dereferenceTok = Token::findmatch(startOfCondition, "* %varid%", validityCheckTok, iteratorVarId); if (dereferenceTok) dereferenceInvalidIteratorError(dereferenceTok, dereferenceTok->strAt(1)); } } void CheckStl::checkDereferenceInvalidIterator2() { const bool printInconclusive = (mSettings->certainty.isEnabled(Certainty::inconclusive)); for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) { tok = tok->next()->link(); continue; } if (Token::Match(tok, "%assign%")) continue; std::vector contValues; std::copy_if(tok->values().begin(), tok->values().end(), std::back_inserter(contValues), [&](const ValueFlow::Value& value) { if (value.isImpossible()) return false; if (!printInconclusive && value.isInconclusive()) return false; return value.isContainerSizeValue(); }); // Can iterator point to END or before START? for (const ValueFlow::Value& value:tok->values()) { if (value.isImpossible()) continue; if (!printInconclusive && value.isInconclusive()) continue; if (!value.isIteratorValue()) continue; bool isInvalidIterator = false; const ValueFlow::Value* cValue = nullptr; if (value.isIteratorEndValue() && value.intvalue >= 0) { isInvalidIterator = value.intvalue > 0; } else if (value.isIteratorStartValue() && value.intvalue < 0) { isInvalidIterator = true; } else { auto it = std::find_if(contValues.begin(), contValues.end(), [&](const ValueFlow::Value& c) { if (value.path != c.path) return false; if (value.isIteratorStartValue() && value.intvalue >= c.intvalue) return true; if (value.isIteratorEndValue() && -value.intvalue > c.intvalue) return true; return false; }); if (it == contValues.end()) continue; cValue = &*it; if (value.isIteratorStartValue() && value.intvalue > cValue->intvalue) isInvalidIterator = true; } bool inconclusive = false; bool unknown = false; const Token* emptyAdvance = nullptr; const Token* advanceIndex = nullptr; if (cValue && cValue->intvalue == 0) { if (Token::Match(tok->astParent(), "+|-") && astIsIntegral(tok->astSibling(), false)) { if (tok->astSibling() && tok->astSibling()->hasKnownIntValue()) { if (tok->astSibling()->values().front().intvalue == 0) continue; } else { advanceIndex = tok->astSibling(); } emptyAdvance = tok->astParent(); } else if (Token::Match(tok->astParent(), "++|--")) { emptyAdvance = tok->astParent(); } } if (!CheckNullPointer::isPointerDeRef(tok, unknown, mSettings) && !isInvalidIterator && !emptyAdvance) { if (!unknown) continue; inconclusive = true; } if (cValue) { const ValueFlow::Value& lValue = getLifetimeIteratorValue(tok, cValue->path); assert(cValue->isInconclusive() || value.isInconclusive() || lValue.isLifetimeValue()); if (!lValue.isLifetimeValue()) continue; if (emptyAdvance) outOfBoundsError(emptyAdvance, lValue.tokvalue->expressionString(), cValue, advanceIndex ? advanceIndex->expressionString() : "", nullptr); else outOfBoundsError(tok, lValue.tokvalue->expressionString(), cValue, tok->expressionString(), &value); } else { dereferenceInvalidIteratorError(tok, &value, inconclusive); } } } } void CheckStl::dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) { const std::string& varname = tok ? tok->expressionString() : "var"; const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible dereference of an invalid iterator: $symbol."); if (!tok || !value) { reportError(tok, Severity::error, "derefInvalidIterator", "Dereference of an invalid iterator", CWE825, Certainty::normal); reportError(tok, Severity::warning, "derefInvalidIteratorRedundantCheck", errmsgcond, CWE825, Certainty::normal); return; } if (!mSettings->isEnabled(value, inconclusive)) return; const ErrorPath errorPath = getErrorPath(tok, value, "Dereference of an invalid iterator"); if (value->condition) { reportError(errorPath, Severity::warning, "derefInvalidIteratorRedundantCheck", errmsgcond, CWE825, (inconclusive || value->isInconclusive()) ? Certainty::inconclusive : Certainty::normal); } else { std::string errmsg; errmsg = std::string(value->isKnown() ? "Dereference" : "Possible dereference") + " of an invalid iterator"; if (!varname.empty()) errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; reportError(errorPath, value->isKnown() ? Severity::error : Severity::warning, "derefInvalidIterator", errmsg, CWE825, (inconclusive || value->isInconclusive()) ? Certainty::inconclusive : Certainty::normal); } } void CheckStl::dereferenceInvalidIteratorError(const Token* deref, const std::string &iterName) { reportError(deref, Severity::warning, "derefInvalidIterator", "$symbol:" + iterName + "\n" "Possible dereference of an invalid iterator: $symbol\n" "Possible dereference of an invalid iterator: $symbol. Make sure to check that the iterator is valid before dereferencing it - not after.", CWE825, Certainty::normal); } void CheckStl::readingEmptyStlContainerError(const Token *tok, const ValueFlow::Value *value) { const std::string varname = tok ? tok->str() : std::string("var"); std::string errmsg; if (value && value->condition) errmsg = "Reading from container '$symbol'. " + ValueFlow::eitherTheConditionIsRedundant(value->condition) + " or '$symbol' can be empty."; else errmsg = "Reading from empty STL container '$symbol'"; const ErrorPath errorPath = getErrorPath(tok, value, "Reading from empty container"); reportError(errorPath, value ? (value->errorSeverity() ? Severity::error : Severity::warning) : Severity::style, "reademptycontainer", "$symbol:" + varname +"\n" + errmsg, CWE398, !value ? Certainty::inconclusive : Certainty::normal); } void CheckStl::useStlAlgorithmError(const Token *tok, const std::string &algoName) { reportError(tok, Severity::style, "useStlAlgorithm", "Consider using " + algoName + " algorithm instead of a raw loop.", CWE398, Certainty::normal); } static bool isEarlyExit(const Token *start) { if (start->str() != "{") return false; const Token *endToken = start->link(); const Token *tok = Token::findmatch(start, "return|throw|break", endToken); if (!tok) return false; const Token *endStatement = Token::findsimplematch(tok, "; }", endToken); if (!endStatement) return false; if (endStatement->next() != endToken) return false; return true; } static const Token *singleStatement(const Token *start) { if (start->str() != "{") return nullptr; const Token *endToken = start->link(); const Token *endStatement = Token::findsimplematch(start->next(), ";"); if (!Token::simpleMatch(endStatement, "; }")) return nullptr; if (endStatement->next() != endToken) return nullptr; return endStatement; } static const Token *singleAssignInScope(const Token *start, nonneg int varid, bool &input) { const Token *endStatement = singleStatement(start); if (!endStatement) return nullptr; if (!Token::Match(start->next(), "%var% %assign%")) return nullptr; const Token *assignTok = start->tokAt(2); if (isVariableChanged(assignTok->next(), endStatement, assignTok->astOperand1()->varId(), false, nullptr, true)) return nullptr; if (isVariableChanged(assignTok->next(), endStatement, varid, false, nullptr, true)) return nullptr; input = Token::findmatch(assignTok->next(), "%varid%", endStatement, varid) || !Token::Match(start->next(), "%var% ="); return assignTok; } static const Token *singleMemberCallInScope(const Token *start, nonneg int varid, bool &input) { if (start->str() != "{") return nullptr; const Token *endToken = start->link(); if (!Token::Match(start->next(), "%var% . %name% (")) return nullptr; if (!Token::simpleMatch(start->linkAt(4), ") ; }")) return nullptr; const Token *endStatement = start->linkAt(4)->next(); if (endStatement->next() != endToken) return nullptr; const Token *dotTok = start->tokAt(2); if (!Token::findmatch(dotTok->tokAt(2), "%varid%", endStatement, varid)) return nullptr; input = Token::Match(start->next(), "%var% . %name% ( %varid% )", varid); if (isVariableChanged(dotTok->next(), endStatement, dotTok->astOperand1()->varId(), false, nullptr, true)) return nullptr; return dotTok; } static const Token *singleIncrementInScope(const Token *start, nonneg int varid, bool &input) { if (start->str() != "{") return nullptr; const Token *varTok = nullptr; if (Token::Match(start->next(), "++ %var% ; }")) varTok = start->tokAt(2); else if (Token::Match(start->next(), "%var% ++ ; }")) varTok = start->tokAt(1); if (!varTok) return nullptr; input = varTok->varId() == varid; return varTok; } static const Token *singleConditionalInScope(const Token *start, nonneg int varid) { if (start->str() != "{") return nullptr; const Token *endToken = start->link(); if (!Token::simpleMatch(start->next(), "if (")) return nullptr; if (!Token::simpleMatch(start->linkAt(2), ") {")) return nullptr; const Token *bodyTok = start->linkAt(2)->next(); const Token *endBodyTok = bodyTok->link(); if (!Token::simpleMatch(endBodyTok, "} }")) return nullptr; if (endBodyTok->next() != endToken) return nullptr; if (!Token::findmatch(start, "%varid%", bodyTok, varid)) return nullptr; if (isVariableChanged(start, bodyTok, varid, false, nullptr, true)) return nullptr; return bodyTok; } static bool addByOne(const Token *tok, nonneg int varid) { if (Token::Match(tok, "+= %any% ;") && tok->tokAt(1)->hasKnownIntValue() && tok->tokAt(1)->getValue(1)) { return true; } if (Token::Match(tok, "= %varid% + %any% ;", varid) && tok->tokAt(3)->hasKnownIntValue() && tok->tokAt(3)->getValue(1)) { return true; } return false; } static bool accumulateBoolLiteral(const Token *tok, nonneg int varid) { if (Token::Match(tok, "%assign% %bool% ;") && tok->tokAt(1)->hasKnownIntValue()) { return true; } if (Token::Match(tok, "= %varid% %oror%|%or%|&&|& %bool% ;", varid) && tok->tokAt(3)->hasKnownIntValue()) { return true; } return false; } static bool accumulateBool(const Token *tok, nonneg int varid) { // Missing %oreq% so we have to check both manually if (Token::simpleMatch(tok, "&=") || Token::simpleMatch(tok, "|=")) { return true; } if (Token::Match(tok, "= %varid% %oror%|%or%|&&|&", varid)) { return true; } return false; } static bool hasVarIds(const Token *tok, nonneg int var1, nonneg int var2) { if (tok->astOperand1()->varId() == tok->astOperand2()->varId()) return false; if (tok->astOperand1()->varId() == var1 || tok->astOperand1()->varId() == var2) { if (tok->astOperand2()->varId() == var1 || tok->astOperand2()->varId() == var2) { return true; } } return false; } static std::string flipMinMax(const std::string &algo) { if (algo == "std::max_element") return "std::min_element"; if (algo == "std::min_element") return "std::max_element"; return algo; } static std::string minmaxCompare(const Token *condTok, nonneg int loopVar, nonneg int assignVar, bool invert = false) { if (!Token::Match(condTok, "<|<=|>=|>")) return "std::accumulate"; if (!hasVarIds(condTok, loopVar, assignVar)) return "std::accumulate"; std::string algo = "std::max_element"; if (Token::Match(condTok, "<|<=")) algo = "std::min_element"; if (condTok->astOperand1()->varId() == assignVar) algo = flipMinMax(algo); if (invert) algo = flipMinMax(algo); return algo; } void CheckStl::useStlAlgorithm() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { // Parse range-based for loop if (!Token::simpleMatch(tok, "for (")) continue; if (!Token::simpleMatch(tok->next()->link(), ") {")) continue; const Token *bodyTok = tok->next()->link()->next(); const Token *splitTok = tok->next()->astOperand2(); if (!Token::simpleMatch(splitTok, ":")) continue; const Token *loopVar = splitTok->previous(); if (!Token::Match(loopVar, "%var%")) continue; // Check for single assignment bool useLoopVarInAssign; const Token *assignTok = singleAssignInScope(bodyTok, loopVar->varId(), useLoopVarInAssign); if (assignTok) { int assignVarId = assignTok->astOperand1()->varId(); std::string algo; if (assignVarId == loopVar->varId()) { if (useLoopVarInAssign) algo = "std::transform"; else if (Token::Match(assignTok->next(), "%var%|%bool%|%num%|%char% ;")) algo = "std::fill"; else if (Token::Match(assignTok->next(), "%name% ( )")) algo = "std::generate"; else algo = "std::fill or std::generate"; } else { if (addByOne(assignTok, assignVarId)) algo = "std::distance"; else if (accumulateBool(assignTok, assignVarId)) algo = "std::any_of, std::all_of, std::none_of, or std::accumulate"; else if (Token::Match(assignTok, "= %var% <|<=|>=|> %var% ? %var% : %var%") && hasVarIds(assignTok->tokAt(6), loopVar->varId(), assignVarId)) algo = minmaxCompare(assignTok->tokAt(2), loopVar->varId(), assignVarId, assignTok->tokAt(5)->varId() == assignVarId); else algo = "std::accumulate"; } useStlAlgorithmError(assignTok, algo); continue; } // Check for container calls bool useLoopVarInMemCall; const Token *memberAccessTok = singleMemberCallInScope(bodyTok, loopVar->varId(), useLoopVarInMemCall); if (memberAccessTok) { const Token *memberCallTok = memberAccessTok->astOperand2(); const int contVarId = memberAccessTok->astOperand1()->varId(); if (contVarId == loopVar->varId()) continue; if (memberCallTok->str() == "push_back" || memberCallTok->str() == "push_front" || memberCallTok->str() == "emplace_back") { std::string algo; if (useLoopVarInMemCall) algo = "std::copy"; else algo = "std::transform"; useStlAlgorithmError(memberCallTok, algo); } continue; } // Check for increment in loop bool useLoopVarInIncrement; const Token *incrementTok = singleIncrementInScope(bodyTok, loopVar->varId(), useLoopVarInIncrement); if (incrementTok) { std::string algo; if (useLoopVarInIncrement) algo = "std::transform"; else algo = "std::distance"; useStlAlgorithmError(incrementTok, algo); continue; } // Check for conditionals const Token *condBodyTok = singleConditionalInScope(bodyTok, loopVar->varId()); if (condBodyTok) { // Check for single assign assignTok = singleAssignInScope(condBodyTok, loopVar->varId(), useLoopVarInAssign); if (assignTok) { const int assignVarId = assignTok->astOperand1()->varId(); std::string algo; if (assignVarId == loopVar->varId()) { if (useLoopVarInAssign) algo = "std::transform"; else algo = "std::replace_if"; } else { if (addByOne(assignTok, assignVarId)) algo = "std::count_if"; else if (accumulateBoolLiteral(assignTok, assignVarId)) algo = "std::any_of, std::all_of, std::none_of, or std::accumulate"; else algo = "std::accumulate"; } useStlAlgorithmError(assignTok, algo); continue; } // Check for container call memberAccessTok = singleMemberCallInScope(condBodyTok, loopVar->varId(), useLoopVarInMemCall); if (memberAccessTok) { const Token *memberCallTok = memberAccessTok->astOperand2(); const int contVarId = memberAccessTok->astOperand1()->varId(); if (contVarId == loopVar->varId()) continue; if (memberCallTok->str() == "push_back" || memberCallTok->str() == "push_front" || memberCallTok->str() == "emplace_back") { if (useLoopVarInMemCall) useStlAlgorithmError(memberAccessTok, "std::copy_if"); // There is no transform_if to suggest } continue; } // Check for increment in loop incrementTok = singleIncrementInScope(condBodyTok, loopVar->varId(), useLoopVarInIncrement); if (incrementTok) { std::string algo; if (useLoopVarInIncrement) algo = "std::transform"; else algo = "std::count_if"; useStlAlgorithmError(incrementTok, algo); continue; } // Check early return if (isEarlyExit(condBodyTok)) { const Token *loopVar2 = Token::findmatch(condBodyTok, "%varid%", condBodyTok->link(), loopVar->varId()); std::string algo; if (loopVar2) algo = "std::find_if"; else algo = "std::any_of"; useStlAlgorithmError(condBodyTok, algo); continue; } } } } } void CheckStl::knownEmptyContainerError(const Token *tok, const std::string& algo) { const std::string var = tok ? tok->expressionString() : std::string("var"); std::string msg; if (astIsIterator(tok)) { msg = "Using " + algo + " with iterator '" + var + "' that is always empty."; } else { msg = "Iterating over container '" + var + "' that is always empty."; } reportError(tok, Severity::style, "knownEmptyContainer", msg, CWE398, Certainty::normal); } static bool isKnownEmptyContainer(const Token* tok) { if (!tok) return false; for (const ValueFlow::Value& v:tok->values()) { if (!v.isKnown()) continue; if (!v.isContainerSizeValue()) continue; if (v.intvalue != 0) continue; return true; } return false; } void CheckStl::knownEmptyContainer() { if (!mSettings->severity.isEnabled(Severity::style)) return; for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% ( !!)")) continue; // Parse range-based for loop if (tok->str() == "for") { if (!Token::simpleMatch(tok->next()->link(), ") {")) continue; const Token *splitTok = tok->next()->astOperand2(); if (!Token::simpleMatch(splitTok, ":")) continue; const Token* contTok = splitTok->astOperand2(); if (!isKnownEmptyContainer(contTok)) continue; knownEmptyContainerError(contTok, ""); } else { const std::vector args = getArguments(tok); if (args.empty()) continue; for (int argnr = 1; argnr <= args.size(); ++argnr) { const Library::ArgumentChecks::IteratorInfo *i = mSettings->library.getArgIteratorInfo(tok, argnr); if (!i) continue; const Token * const argTok = args[argnr - 1]; if (!isKnownEmptyContainer(argTok)) continue; knownEmptyContainerError(argTok, tok->str()); break; } } } } } static bool isMutex(const Variable* var) { const Token* tok = Token::typeDecl(var->nameToken()).first; return Token::Match(tok, "std :: mutex|recursive_mutex|timed_mutex|recursive_timed_mutex|shared_mutex"); } static bool isLockGuard(const Variable* var) { const Token* tok = Token::typeDecl(var->nameToken()).first; return Token::Match(tok, "std :: lock_guard|unique_lock|scoped_lock|shared_lock"); } static bool isLocalMutex(const Variable* var, const Scope* scope) { if (!var) return false; if (isLockGuard(var)) return false; return !var->isReference() && !var->isRValueReference() && !var->isStatic() && var->scope() == scope; } void CheckStl::globalLockGuardError(const Token* tok) { reportError(tok, Severity::warning, "globalLockGuard", "Lock guard is defined globally. Lock guards are intended to be local. A global lock guard could lead to a deadlock since it won't unlock until the end of the program.", CWE833, Certainty::normal); } void CheckStl::localMutexError(const Token* tok) { reportError(tok, Severity::warning, "localMutex", "The lock is ineffective because the mutex is locked at the same scope as the mutex itself.", CWE667, Certainty::normal); } void CheckStl::checkMutexes() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { std::set checkedVars; for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%var%")) continue; const Variable* var = tok->variable(); if (!var) continue; if (Token::Match(tok, "%var% . lock ( )")) { if (!isMutex(var)) continue; if (!checkedVars.insert(var->declarationId()).second) continue; if (isLocalMutex(var, tok->scope())) localMutexError(tok); } else if (Token::Match(tok, "%var% (|{ %var% )|}|,")) { if (!isLockGuard(var)) continue; const Variable* mvar = tok->tokAt(2)->variable(); if (!mvar) continue; if (!checkedVars.insert(mvar->declarationId()).second) continue; if (var->isStatic() || var->isGlobal()) globalLockGuardError(tok); else if (isLocalMutex(mvar, tok->scope())) localMutexError(tok); } } } } cppcheck-2.7/lib/checkstl.h000066400000000000000000000273001417746362400157010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkstlH #define checkstlH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errortypes.h" #include "tokenize.h" #include "utils.h" #include "valueflow.h" #include class Scope; class Settings; class Token; class Variable; class ErrorLogger; /// @addtogroup Checks /// @{ /** @brief %Check STL usage (invalidation of iterators, mismatching containers, etc) */ class CPPCHECKLIB CheckStl : public Check { public: /** This constructor is used when registering the CheckClass */ CheckStl() : Check(myName()) {} /** This constructor is used when running checks. */ CheckStl(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** run checks, the token list is not simplified */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (!tokenizer->isCPP()) { return; } CheckStl checkStl(tokenizer, settings, errorLogger); checkStl.erase(); checkStl.if_find(); checkStl.checkFindInsert(); checkStl.iterators(); checkStl.missingComparison(); checkStl.outOfBounds(); checkStl.outOfBoundsIndexExpression(); checkStl.redundantCondition(); checkStl.string_c_str(); checkStl.uselessCalls(); checkStl.useStlAlgorithm(); checkStl.stlOutOfBounds(); checkStl.negativeIndex(); checkStl.invalidContainer(); checkStl.mismatchingContainers(); checkStl.mismatchingContainerIterator(); checkStl.knownEmptyContainer(); checkStl.stlBoundaries(); checkStl.checkDereferenceInvalidIterator(); checkStl.checkDereferenceInvalidIterator2(); checkStl.checkMutexes(); // Style check checkStl.size(); } /** Accessing container out of bounds using ValueFlow */ void outOfBounds(); /** Accessing container out of bounds, following index expression */ void outOfBoundsIndexExpression(); /** * Finds errors like this: * for (unsigned ii = 0; ii <= foo.size(); ++ii) */ void stlOutOfBounds(); /** * negative index for array like containers */ void negativeIndex(); /** * Finds errors like this: * for (it = foo.begin(); it != bar.end(); ++it) */ void iterators(); void invalidContainer(); bool checkIteratorPair(const Token* tok1, const Token* tok2); /** * Mismatching containers: * std::find(foo.begin(), bar.end(), x) */ void mismatchingContainers(); void mismatchingContainerIterator(); /** * Dangerous usage of erase. The iterator is invalidated by erase so * it is bad to dereference it after the erase. */ void erase(); void eraseCheckLoopVar(const Scope& scope, const Variable* var); /** * bad condition.. "it < alist.end()" */ void stlBoundaries(); /** if (a.find(x)) - possibly incorrect condition */ void if_find(); void checkFindInsert(); /** * Suggest using empty() instead of checking size() against zero for containers. * Item 4 from Scott Meyers book "Effective STL". */ void size(); /** * Check for redundant condition 'if (ints.find(1) != ints.end()) ints.remove(123);' * */ void redundantCondition(); /** * @brief Missing inner comparison, when incrementing iterator inside loop * Dangers: * - may increment iterator beyond end * - may unintentionally skip elements in list/set etc */ void missingComparison(); /** Check for common mistakes when using the function string::c_str() */ void string_c_str(); /** @brief %Check calls that using them is useless */ void uselessCalls(); /** @brief %Check for dereferencing an iterator that is invalid */ void checkDereferenceInvalidIterator(); void checkDereferenceInvalidIterator2(); /** * Dereferencing an erased iterator * @param erased token where the erase occurs * @param deref token where the dereference occurs * @param itername iterator name * @param inconclusive inconclusive flag */ void dereferenceErasedError(const Token* erased, const Token* deref, const std::string& itername, bool inconclusive); /** @brief Look for loops that can replaced with std algorithms */ void useStlAlgorithm(); void knownEmptyContainer(); void checkMutexes(); private: bool isContainerSize(const Token *containerToken, const Token *expr) const; bool isContainerSizeGE(const Token * containerToken, const Token *expr) const; void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2); void string_c_strThrowError(const Token* tok); void string_c_strError(const Token* tok); void string_c_strReturn(const Token* tok); void string_c_strParam(const Token* tok, nonneg int number); void outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue); void outOfBoundsIndexExpressionError(const Token *tok, const Token *index); void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at); void negativeIndexError(const Token* tok, const ValueFlow::Value& index); void invalidIteratorError(const Token* tok, const std::string& iteratorName); void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2); void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2); void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName); void mismatchingContainerIteratorError(const Token* tok, const Token* iterTok); void mismatchingContainersError(const Token* tok1, const Token* tok2); void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2); void sameIteratorExpressionError(const Token *tok); void stlBoundariesError(const Token* tok); void if_findError(const Token* tok, bool str); void checkFindInsertError(const Token *tok); void sizeError(const Token* tok); void redundantIfRemoveError(const Token* tok); void invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath); void invalidContainerError(const Token *tok, const Token * contTok, const ValueFlow::Value *val, ErrorPath errorPath); void invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath); void uselessCallsReturnValueError(const Token* tok, const std::string& varname, const std::string& function); void uselessCallsSwapError(const Token* tok, const std::string& varname); void uselessCallsSubstrError(const Token* tok, bool empty); void uselessCallsEmptyError(const Token* tok); void uselessCallsRemoveError(const Token* tok, const std::string& function); void dereferenceInvalidIteratorError(const Token* deref, const std::string& iterName); void dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); void readingEmptyStlContainerError(const Token* tok, const ValueFlow::Value *value=nullptr); void useStlAlgorithmError(const Token *tok, const std::string &algoName); void knownEmptyContainerError(const Token *tok, const std::string& algo); void globalLockGuardError(const Token *tok); void localMutexError(const Token *tok); void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE { ErrorPath errorPath; CheckStl c(nullptr, settings, errorLogger); c.outOfBoundsError(nullptr, "container", nullptr, "x", nullptr); c.invalidIteratorError(nullptr, "iterator"); c.iteratorsError(nullptr, "container1", "container2"); c.iteratorsError(nullptr, nullptr, "container0", "container1"); c.iteratorsError(nullptr, nullptr, "container"); c.invalidContainerLoopError(nullptr, nullptr, errorPath); c.invalidContainerError(nullptr, nullptr, nullptr, errorPath); c.mismatchingContainerIteratorError(nullptr, nullptr); c.mismatchingContainersError(nullptr, nullptr); c.mismatchingContainerExpressionError(nullptr, nullptr); c.sameIteratorExpressionError(nullptr); c.dereferenceErasedError(nullptr, nullptr, "iter", false); c.stlOutOfBoundsError(nullptr, "i", "foo", false); c.negativeIndexError(nullptr, ValueFlow::Value(-1)); c.stlBoundariesError(nullptr); c.if_findError(nullptr, false); c.if_findError(nullptr, true); c.checkFindInsertError(nullptr); c.string_c_strError(nullptr); c.string_c_strReturn(nullptr); c.string_c_strParam(nullptr, 0); c.string_c_strThrowError(nullptr); c.sizeError(nullptr); c.missingComparisonError(nullptr, nullptr); c.redundantIfRemoveError(nullptr); c.uselessCallsReturnValueError(nullptr, "str", "find"); c.uselessCallsSwapError(nullptr, "str"); c.uselessCallsSubstrError(nullptr, false); c.uselessCallsEmptyError(nullptr); c.uselessCallsRemoveError(nullptr, "remove"); c.dereferenceInvalidIteratorError(nullptr, "i"); c.readingEmptyStlContainerError(nullptr); c.useStlAlgorithmError(nullptr, ""); c.knownEmptyContainerError(nullptr, ""); c.globalLockGuardError(nullptr); c.localMutexError(nullptr); } static std::string myName() { return "STL usage"; } std::string classInfo() const OVERRIDE { return "Check for invalid usage of STL:\n" "- out of bounds errors\n" "- misuse of iterators when iterating through a container\n" "- mismatching containers in calls\n" "- same iterators in calls\n" "- dereferencing an erased iterator\n" "- for vectors: using iterator/pointer after push_back has been used\n" "- optimisation: use empty() instead of size() to guarantee fast code\n" "- suspicious condition when using find\n" "- unnecessary searching in associative containers\n" "- redundant condition\n" "- common mistakes when using string::c_str()\n" "- useless calls of string and STL functions\n" "- dereferencing an invalid iterator\n" "- reading from empty STL container\n" "- iterating over an empty STL container\n" "- consider using an STL algorithm instead of raw loop\n" "- incorrect locking with mutex\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkstlH cppcheck-2.7/lib/checkstring.cpp000066400000000000000000000527231417746362400167470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkstring.h" #include "astutils.h" #include "errortypes.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckString instance; } // CWE ids used: static const struct CWE CWE570(570U); // Expression is Always False static const struct CWE CWE571(571U); // Expression is Always True static const struct CWE CWE595(595U); // Comparison of Object References Instead of Object Contents static const struct CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const struct CWE CWE665(665U); // Improper Initialization static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior //--------------------------------------------------------------------------- // Writing string literal is UB //--------------------------------------------------------------------------- void CheckString::stringLiteralWrite() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->variable() || !tok->variable()->isPointer()) continue; const Token *str = tok->getValueTokenMinStrSize(mSettings); if (!str) continue; if (Token::Match(tok, "%var% [") && Token::simpleMatch(tok->linkAt(1), "] =")) stringLiteralWriteError(tok, str); else if (Token::Match(tok->previous(), "* %var% =")) stringLiteralWriteError(tok, str); } } } void CheckString::stringLiteralWriteError(const Token *tok, const Token *strValue) { std::list callstack; callstack.push_back(tok); if (strValue) callstack.push_back(strValue); std::string errmsg("Modifying string literal"); if (strValue) { std::string s = strValue->str(); // 20 is an arbitrary value, the max string length shown in a warning message if (s.size() > 20U) s = s.substr(0,17) + "..\""; errmsg += " " + s; } errmsg += " directly or indirectly is undefined behaviour."; reportError(callstack, Severity::error, "stringLiteralWrite", errmsg, CWE758, Certainty::normal); } //--------------------------------------------------------------------------- // Check for string comparison involving two static strings. // if(strcmp("00FF00","00FF00")==0) // <- statement is always true //--------------------------------------------------------------------------- void CheckString::checkAlwaysTrueOrFalseStringCompare() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->isName() && tok->strAt(1) == "(" && Token::Match(tok, "memcmp|strncmp|strcmp|stricmp|strverscmp|bcmp|strcmpi|strcasecmp|strncasecmp|strncasecmp_l|strcasecmp_l|wcsncasecmp|wcscasecmp|wmemcmp|wcscmp|wcscasecmp_l|wcsncasecmp_l|wcsncmp|_mbscmp|_memicmp|_memicmp_l|_stricmp|_wcsicmp|_mbsicmp|_stricmp_l|_wcsicmp_l|_mbsicmp_l")) { if (Token::Match(tok->tokAt(2), "%str% , %str% ,|)")) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(4); if (!tok->isExpandedMacro() && !tok->tokAt(2)->isExpandedMacro() && !tok->tokAt(4)->isExpandedMacro()) alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(5); } else if (Token::Match(tok->tokAt(2), "%name% , %name% ,|)")) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(4); if (str1 == str2) alwaysTrueStringVariableCompareError(tok, str1, str2); tok = tok->tokAt(5); } else if (Token::Match(tok->tokAt(2), "%name% . c_str ( ) , %name% . c_str ( ) ,|)")) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(8); if (str1 == str2) alwaysTrueStringVariableCompareError(tok, str1, str2); tok = tok->tokAt(13); } } else if (tok->isName() && Token::Match(tok, "QString :: compare ( %str% , %str% )")) { const std::string &str1 = tok->strAt(4); const std::string &str2 = tok->strAt(6); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(7); } else if (Token::Match(tok, "!!+ %str% ==|!= %str% !!+")) { const std::string &str1 = tok->strAt(1); const std::string &str2 = tok->strAt(3); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(5); } if (!tok) break; } } void CheckString::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2) { const std::size_t stringLen = 10; const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + ".."); reportError(tok, Severity::warning, "staticStringCompare", "Unnecessary comparison of static strings.\n" "The compared strings, '" + string1 + "' and '" + string2 + "', are always " + (str1==str2?"identical":"unequal") + ". " "Therefore the comparison is unnecessary and looks suspicious.", (str1==str2)?CWE571:CWE570, Certainty::normal); } void CheckString::alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2) { reportError(tok, Severity::warning, "stringCompare", "Comparison of identical string variables.\n" "The compared strings, '" + str1 + "' and '" + str2 + "', are identical. " "This could be a logic bug.", CWE571, Certainty::normal); } //----------------------------------------------------------------------------- // Detect "str == '\0'" where "*str == '\0'" is correct. // Comparing char* with each other instead of using strcmp() //----------------------------------------------------------------------------- void CheckString::checkSuspiciousStringCompare() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token* varTok = tok->astOperand1(); const Token* litTok = tok->astOperand2(); if (!varTok || !litTok) // <- failed to create AST for comparison continue; if (Token::Match(varTok, "%char%|%num%|%str%")) std::swap(varTok, litTok); else if (!Token::Match(litTok, "%char%|%num%|%str%")) continue; if (varTok->isLiteral()) continue; const ValueType* varType = varTok->valueType(); if (mTokenizer->isCPP() && (!varType || !varType->isIntegral())) continue; if (litTok->tokType() == Token::eString) { if (mTokenizer->isC() || (varType && varType->pointer)) suspiciousStringCompareError(tok, varTok->expressionString(), litTok->isLong()); } else if (litTok->tokType() == Token::eChar && varType && varType->pointer) { suspiciousStringCompareError_char(tok, varTok->expressionString()); } } } } void CheckString::suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong) { const std::string cmpFunc = isLong ? "wcscmp" : "strcmp"; reportError(tok, Severity::warning, "literalWithCharPtrCompare", "$symbol:" + var + "\nString literal compared with variable '$symbol'. Did you intend to use " + cmpFunc + "() instead?", CWE595, Certainty::normal); } void CheckString::suspiciousStringCompareError_char(const Token* tok, const std::string& var) { reportError(tok, Severity::warning, "charLiteralWithCharPtrCompare", "$symbol:" + var + "\nChar literal compared with pointer '$symbol'. Did you intend to dereference it?", CWE595, Certainty::normal); } //--------------------------------------------------------------------------- // Adding C-string and char with operator+ //--------------------------------------------------------------------------- static bool isChar(const Variable* var) { return (var && !var->isPointer() && !var->isArray() && (var->typeStartToken()->str() == "char" || var->typeStartToken()->str() == "wchar_t")); } void CheckString::strPlusChar() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "+") { if (tok->astOperand1() && (tok->astOperand1()->tokType() == Token::eString)) { // string literal... if (tok->astOperand2() && (tok->astOperand2()->tokType() == Token::eChar || isChar(tok->astOperand2()->variable()))) // added to char variable or char constant strPlusCharError(tok); } } } } } void CheckString::strPlusCharError(const Token *tok) { std::string charType = "char"; if (tok && tok->astOperand2() && tok->astOperand2()->variable()) charType = tok->astOperand2()->variable()->typeStartToken()->str(); else if (tok && tok->astOperand2() && tok->astOperand2()->tokType() == Token::eChar && tok->astOperand2()->isLong()) charType = "wchar_t"; reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic. A value of type '" + charType +"' is added to a string literal.", CWE665, Certainty::normal); } //--------------------------------------------------------------------------- // Implicit casts of string literals to bool // Comparing string literal with strlen() with wrong length //--------------------------------------------------------------------------- void CheckString::checkIncorrectStringCompare() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // skip "assert(str && ..)" and "assert(.. && str)" if ((endsWith(tok->str(), "assert") || endsWith(tok->str(), "ASSERT")) && Token::Match(tok, "%name% (") && (Token::Match(tok->tokAt(2), "%str% &&") || Token::Match(tok->next()->link()->tokAt(-2), "&& %str% )"))) tok = tok->next()->link(); if (Token::simpleMatch(tok, ". substr (") && Token::Match(tok->tokAt(3)->nextArgument(), "%num% )")) { const MathLib::biguint clen = MathLib::toULongNumber(tok->linkAt(2)->strAt(-1)); const Token* begin = tok->previous(); for (;;) { // Find start of statement while (begin->link() && Token::Match(begin, "]|)|>")) begin = begin->link()->previous(); if (Token::Match(begin->previous(), ".|::")) begin = begin->tokAt(-2); else break; } begin = begin->previous(); const Token* end = tok->linkAt(2)->next(); if (Token::Match(begin->previous(), "%str% ==|!=") && begin->strAt(-2) != "+") { const std::size_t slen = Token::getStrLength(begin->previous()); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", begin->strAt(-1)); } } else if (Token::Match(end, "==|!= %str% !!+")) { const std::size_t slen = Token::getStrLength(end->next()); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", end->strAt(1)); } } } else if (Token::Match(tok, "&&|%oror%|( %str%|%char% &&|%oror%|)") && !Token::Match(tok, "( %str%|%char% )")) { incorrectStringBooleanError(tok->next(), tok->strAt(1)); } else if (Token::Match(tok, "if|while ( %str%|%char% )") && !tok->tokAt(2)->getValue(0)) { incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2)); } else if (tok->str() == "?" && Token::Match(tok->astOperand1(), "%str%|%char%")) incorrectStringBooleanError(tok->astOperand1(), tok->astOperand1()->str()); } } } void CheckString::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) { reportError(tok, Severity::warning, "incorrectStringCompare", "$symbol:" + func + "\nString literal " + string + " doesn't match length argument for $symbol().", CWE570, Certainty::normal); } void CheckString::incorrectStringBooleanError(const Token *tok, const std::string& string) { const bool charLiteral = isCharLiteral(string); const std::string literalType = charLiteral ? "char" : "string"; const std::string result = getCharLiteral(string) == "\\0" ? "false" : "true"; reportError(tok, Severity::warning, charLiteral ? "incorrectCharBooleanError" : "incorrectStringBooleanError", "Conversion of " + literalType + " literal " + string + " to bool always evaluates to " + result + '.', CWE571, Certainty::normal); } //--------------------------------------------------------------------------- // always true: strcmp(str,"a")==0 || strcmp(str,"b") // TODO: Library configuration for string comparison functions //--------------------------------------------------------------------------- void CheckString::overlappingStrcmp() { if (!mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() != "||") continue; std::list equals0; std::list notEquals0; visitAstNodes(tok, [&](const Token * t) { if (!t) return ChildrenToVisit::none; if (t->str() == "||") { return ChildrenToVisit::op1_and_op2; } if (t->str() == "==") { if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0")) equals0.push_back(t->astOperand1()); else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0")) equals0.push_back(t->astOperand2()); return ChildrenToVisit::none; } if (t->str() == "!=") { if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0")) notEquals0.push_back(t->astOperand1()); else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0")) notEquals0.push_back(t->astOperand2()); return ChildrenToVisit::none; } if (t->str() == "!" && Token::simpleMatch(t->astOperand1(), "(")) equals0.push_back(t->astOperand1()); else if (t->str() == "(") notEquals0.push_back(t); return ChildrenToVisit::none; }); for (const Token *eq0 : equals0) { for (const Token * ne0 : notEquals0) { if (!Token::Match(eq0->previous(), "strcmp|wcscmp (")) continue; if (!Token::Match(ne0->previous(), "strcmp|wcscmp (")) continue; const std::vector args1 = getArguments(eq0->previous()); const std::vector args2 = getArguments(ne0->previous()); if (args1.size() != 2 || args2.size() != 2) continue; if (args1[1]->isLiteral() && args2[1]->isLiteral() && args1[1]->str() != args2[1]->str() && isSameExpression(mTokenizer->isCPP(), true, args1[0], args2[0], mSettings->library, true, false)) overlappingStrcmpError(eq0, ne0); } } } } } void CheckString::overlappingStrcmpError(const Token *eq0, const Token *ne0) { std::string eq0Expr(eq0 ? eq0->expressionString() : std::string("strcmp(x,\"abc\")")); if (eq0 && eq0->astParent()->str() == "!") eq0Expr = "!" + eq0Expr; else eq0Expr += " == 0"; const std::string ne0Expr = (ne0 ? ne0->expressionString() : std::string("strcmp(x,\"def\")")) + " != 0"; reportError(ne0, Severity::warning, "overlappingStrcmp", "The expression '" + ne0Expr + "' is suspicious. It overlaps '" + eq0Expr + "'."); } //--------------------------------------------------------------------------- // Overlapping source and destination passed to sprintf(). // TODO: Library configuration for overlapping arguments //--------------------------------------------------------------------------- void CheckString::sprintfOverlappingData() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "sprintf|snprintf|swprintf (")) continue; const std::vector args = getArguments(tok); const int formatString = Token::simpleMatch(tok, "sprintf") ? 1 : 2; for (unsigned int argnr = formatString + 1; argnr < args.size(); ++argnr) { const Token *dest = args[0]; while (dest->isCast()) dest = dest->astOperand2() ? dest->astOperand2() : dest->astOperand1(); const Token *arg = args[argnr]; if (!arg->valueType() || arg->valueType()->pointer != 1) continue; while (arg->isCast()) arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1(); const bool same = isSameExpression(mTokenizer->isCPP(), false, dest, arg, mSettings->library, true, false); if (same) { sprintfOverlappingDataError(tok, args[argnr], arg->expressionString()); } } } } } void CheckString::sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname) { const std::string func = funcTok ? funcTok->str() : "s[n]printf"; reportError(tok, Severity::error, "sprintfOverlappingData", "$symbol:" + varname + "\n" "Undefined behavior: Variable '$symbol' is used as parameter and destination in " + func + "().\n" + "The variable '$symbol' is used both as a parameter and as destination in " + func + "(). The origin and destination buffers overlap. Quote from glibc (C-library) " "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): " "\"If copying takes place between objects that overlap as a result of a call " "to sprintf() or snprintf(), the results are undefined.\"", CWE628, Certainty::normal); } cppcheck-2.7/lib/checkstring.h000066400000000000000000000125561417746362400164140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkstringH #define checkstringH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief Detect misusage of C-style strings and related standard functions */ class CPPCHECKLIB CheckString : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckString() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckString(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckString checkString(tokenizer, settings, errorLogger); // Checks checkString.strPlusChar(); checkString.checkSuspiciousStringCompare(); checkString.stringLiteralWrite(); checkString.overlappingStrcmp(); checkString.checkIncorrectStringCompare(); checkString.sprintfOverlappingData(); checkString.checkAlwaysTrueOrFalseStringCompare(); } /** @brief undefined behaviour, writing string literal */ void stringLiteralWrite(); /** @brief str plus char (unusual pointer arithmetic) */ void strPlusChar(); /** @brief %Check for using bad usage of strncmp and substr */ void checkIncorrectStringCompare(); /** @brief %Check for comparison of a string literal with a char* variable */ void checkSuspiciousStringCompare(); /** @brief %Check for suspicious code that compares string literals for equality */ void checkAlwaysTrueOrFalseStringCompare(); /** @brief %Check for overlapping strcmp() */ void overlappingStrcmp(); /** @brief %Check for overlapping source and destination passed to sprintf() */ void sprintfOverlappingData(); private: void stringLiteralWriteError(const Token *tok, const Token *strValue); void sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname); void strPlusCharError(const Token *tok); void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string); void incorrectStringBooleanError(const Token *tok, const std::string& string); void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2); void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2); void suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong); void suspiciousStringCompareError_char(const Token* tok, const std::string& var); void overlappingStrcmpError(const Token* eq0, const Token *ne0); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckString c(nullptr, settings, errorLogger); c.stringLiteralWriteError(nullptr, nullptr); c.sprintfOverlappingDataError(nullptr, nullptr, "varname"); c.strPlusCharError(nullptr); c.incorrectStringCompareError(nullptr, "substr", "\"Hello World\""); c.suspiciousStringCompareError(nullptr, "foo", false); c.suspiciousStringCompareError_char(nullptr, "foo"); c.incorrectStringBooleanError(nullptr, "\"Hello World\""); c.incorrectStringBooleanError(nullptr, "\'x\'"); c.alwaysTrueFalseStringCompareError(nullptr, "str1", "str2"); c.alwaysTrueStringVariableCompareError(nullptr, "varname1", "varname2"); c.overlappingStrcmpError(nullptr, nullptr); } static std::string myName() { return "String"; } std::string classInfo() const OVERRIDE { return "Detect misusage of C-style strings:\n" "- overlapping buffers passed to sprintf as source and destination\n" "- incorrect length arguments for 'substr' and 'strncmp'\n" "- suspicious condition (runtime comparison of string literals)\n" "- suspicious condition (string/char literals as boolean)\n" "- suspicious comparison of a string literal with a char\\* variable\n" "- suspicious comparison of '\\0' with a char\\* variable\n" "- overlapping strcmp() expression\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkstringH cppcheck-2.7/lib/checktype.cpp000066400000000000000000000460641417746362400164230ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checktype.h" #include "errortypes.h" #include "mathlib.h" #include "platform.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckType instance; } //--------------------------------------------------------------------------- // Checking for shift by too many bits //--------------------------------------------------------------------------- // // CWE ids used: static const struct CWE CWE195(195U); // Signed to Unsigned Conversion Error static const struct CWE CWE197(197U); // Numeric Truncation Error static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const struct CWE CWE190(190U); // Integer Overflow or Wraparound void CheckType::checkTooBigBitwiseShift() { // unknown sizeof(int) => can't run this checker if (mSettings->platformType == Settings::Unspecified) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { // C++ and macro: OUT(x<isCPP() && Token::Match(tok, "[;{}] %name% (") && Token::simpleMatch(tok->linkAt(2), ") ;") && tok->next()->isUpperCaseName() && !tok->next()->function()) tok = tok->linkAt(2); if (!tok->astOperand1() || !tok->astOperand2()) continue; if (!Token::Match(tok, "<<|>>|<<=|>>=")) continue; // get number of bits of lhs const ValueType * const lhstype = tok->astOperand1()->valueType(); if (!lhstype || !lhstype->isIntegral() || lhstype->pointer >= 1) continue; // C11 Standard, section 6.5.7 Bitwise shift operators, states: // The integer promotions are performed on each of the operands. // The type of the result is that of the promoted left operand. int lhsbits; if ((lhstype->type == ValueType::Type::CHAR) || (lhstype->type == ValueType::Type::SHORT) || (lhstype->type == ValueType::Type::WCHAR_T) || (lhstype->type == ValueType::Type::BOOL) || (lhstype->type == ValueType::Type::INT)) lhsbits = mSettings->int_bit; else if (lhstype->type == ValueType::Type::LONG) lhsbits = mSettings->long_bit; else if (lhstype->type == ValueType::Type::LONGLONG) lhsbits = mSettings->long_long_bit; else continue; // Get biggest rhs value. preferably a value which doesn't have 'condition'. const ValueFlow::Value * value = tok->astOperand2()->getValueGE(lhsbits, mSettings); if (value && mSettings->isEnabled(value, false)) tooBigBitwiseShiftError(tok, lhsbits, *value); else if (lhstype->sign == ValueType::Sign::SIGNED) { value = tok->astOperand2()->getValueGE(lhsbits-1, mSettings); if (value && mSettings->isEnabled(value, false)) tooBigSignedBitwiseShiftError(tok, lhsbits, *value); } } } void CheckType::tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) { const char id[] = "shiftTooManyBits"; if (!tok) { reportError(tok, Severity::error, id, "Shifting 32-bit value by 40 bits is undefined behaviour", CWE758, Certainty::normal); return; } const ErrorPath errorPath = getErrorPath(tok, &rhsbits, "Shift"); std::ostringstream errmsg; errmsg << "Shifting " << lhsbits << "-bit value by " << rhsbits.intvalue << " bits is undefined behaviour"; if (rhsbits.condition) errmsg << ". See condition at line " << rhsbits.condition->linenr() << "."; reportError(errorPath, rhsbits.errorSeverity() ? Severity::error : Severity::warning, id, errmsg.str(), CWE758, rhsbits.isInconclusive() ? Certainty::inconclusive : Certainty::normal); } void CheckType::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) { const char id[] = "shiftTooManyBitsSigned"; const bool cpp14 = mSettings->standards.cpp >= Standards::CPP14; std::string behaviour = "undefined"; if (cpp14) behaviour = "implementation-defined"; if (!tok) { reportError(tok, Severity::error, id, "Shifting signed 32-bit value by 31 bits is " + behaviour + " behaviour", CWE758, Certainty::normal); return; } const ErrorPath errorPath = getErrorPath(tok, &rhsbits, "Shift"); std::ostringstream errmsg; errmsg << "Shifting signed " << lhsbits << "-bit value by " << rhsbits.intvalue << " bits is " + behaviour + " behaviour"; if (rhsbits.condition) errmsg << ". See condition at line " << rhsbits.condition->linenr() << "."; Severity::SeverityType severity = rhsbits.errorSeverity() ? Severity::error : Severity::warning; if (cpp14) severity = Severity::portability; if ((severity == Severity::portability) && !mSettings->severity.isEnabled(Severity::portability)) return; reportError(errorPath, severity, id, errmsg.str(), CWE758, rhsbits.isInconclusive() ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- // Checking for integer overflow //--------------------------------------------------------------------------- void CheckType::checkIntegerOverflow() { // unknown sizeof(int) => can't run this checker if (mSettings->platformType == Settings::Unspecified || mSettings->int_bit >= MathLib::bigint_bits) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->isArithmeticalOp()) continue; // is result signed integer? const ValueType *vt = tok->valueType(); if (!vt || !vt->isIntegral() || vt->sign != ValueType::Sign::SIGNED) continue; unsigned int bits; if (vt->type == ValueType::Type::INT) bits = mSettings->int_bit; else if (vt->type == ValueType::Type::LONG) bits = mSettings->long_bit; else if (vt->type == ValueType::Type::LONGLONG) bits = mSettings->long_long_bit; else continue; if (bits >= MathLib::bigint_bits) continue; // max value according to platform settings. const MathLib::bigint maxvalue = (((MathLib::biguint)1) << (bits - 1)) - 1; // is there a overflow result value const ValueFlow::Value *value = tok->getValueGE(maxvalue + 1, mSettings); if (!value) value = tok->getValueLE(-maxvalue - 2, mSettings); if (!value || !mSettings->isEnabled(value,false)) continue; // For left shift, it's common practice to shift into the sign bit //if (tok->str() == "<<" && value->intvalue > 0 && value->intvalue < (((MathLib::bigint)1) << bits)) // continue; integerOverflowError(tok, *value); } } void CheckType::integerOverflowError(const Token *tok, const ValueFlow::Value &value) { const std::string expr(tok ? tok->expressionString() : ""); std::string msg; if (value.condition) msg = ValueFlow::eitherTheConditionIsRedundant(value.condition) + " or there is signed integer overflow for expression '" + expr + "'."; else msg = "Signed integer overflow for expression '" + expr + "'."; if (value.safe) msg = "Safe checks: " + msg; reportError(getErrorPath(tok, &value, "Integer overflow"), value.errorSeverity() ? Severity::error : Severity::warning, getMessageId(value, "integerOverflow").c_str(), msg, CWE190, value.isInconclusive() ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- // Checking for sign conversion when operand can be negative //--------------------------------------------------------------------------- void CheckType::checkSignConversion() { if (!mSettings->severity.isEnabled(Severity::warning)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->isArithmeticalOp() || Token::Match(tok,"+|-")) continue; // Is result unsigned? if (!(tok->valueType() && tok->valueType()->sign == ValueType::Sign::UNSIGNED)) continue; // Check if an operand can be negative.. const Token * astOperands[] = { tok->astOperand1(), tok->astOperand2() }; for (const Token * tok1 : astOperands) { if (!tok1) continue; const ValueFlow::Value* negativeValue = ValueFlow::findValue(tok1->values(), mSettings, [&](const ValueFlow::Value& v) { return !v.isImpossible() && v.isIntValue() && (v.intvalue <= -1 || v.wideintvalue <= -1); }); if (!negativeValue) continue; if (tok1->valueType() && tok1->valueType()->sign != ValueType::Sign::UNSIGNED) signConversionError(tok1, negativeValue, tok1->isNumber()); } } } void CheckType::signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue) { const std::string expr(tok ? tok->expressionString() : "var"); std::ostringstream msg; if (tok && tok->isName()) msg << "$symbol:" << expr << "\n"; if (constvalue) msg << "Expression '" << expr << "' has a negative value. That is converted to an unsigned value and used in an unsigned calculation."; else msg << "Expression '" << expr << "' can have a negative value. That is converted to an unsigned value and used in an unsigned calculation."; if (!negativeValue) reportError(tok, Severity::warning, "signConversion", msg.str(), CWE195, Certainty::normal); else { const ErrorPath &errorPath = getErrorPath(tok,negativeValue,"Negative value is converted to an unsigned value"); reportError(errorPath, Severity::warning, Check::getMessageId(*negativeValue, "signConversion").c_str(), msg.str(), CWE195, negativeValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } } //--------------------------------------------------------------------------- // Checking for long cast of int result const long x = var1 * var2; //--------------------------------------------------------------------------- void CheckType::checkLongCast() { if (!mSettings->severity.isEnabled(Severity::style)) return; // Assignments.. for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "=" || !Token::Match(tok->astOperand2(), "*|<<")) continue; if (tok->astOperand2()->hasKnownIntValue()) { const ValueFlow::Value &v = tok->astOperand2()->values().front(); if (mSettings->isIntValue(v.intvalue)) continue; } const ValueType *lhstype = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *rhstype = tok->astOperand2()->valueType(); if (!lhstype || !rhstype) continue; // assign int result to long/longlong const nonpointer? if (rhstype->type == ValueType::Type::INT && rhstype->pointer == 0U && rhstype->originalTypeName.empty() && (lhstype->type == ValueType::Type::LONG || lhstype->type == ValueType::Type::LONGLONG) && lhstype->pointer == 0U && lhstype->constness == 1U && lhstype->originalTypeName.empty()) longCastAssignError(tok); } // Return.. const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // function must return long data const Token * def = scope->classDef; bool islong = false; while (Token::Match(def, "%type%|::")) { if (def->str() == "long" && def->originalName().empty()) { islong = true; break; } def = def->previous(); } if (!islong) continue; // return statements const Token *ret = nullptr; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "return") { if (Token::Match(tok->astOperand1(), "<<|*")) { const ValueType *type = tok->astOperand1()->valueType(); if (type && type->type == ValueType::Type::INT && type->pointer == 0U && type->originalTypeName.empty()) ret = tok; } // All return statements must have problem otherwise no warning if (ret != tok) { ret = nullptr; break; } } } if (ret) longCastReturnError(ret); } } void CheckType::longCastAssignError(const Token *tok) { reportError(tok, Severity::style, "truncLongCastAssignment", "int result is assigned to long variable. If the variable is long to avoid loss of information, then you have loss of information.\n" "int result is assigned to long variable. If the variable is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'l = a * b;' => 'l = (long)a * b;'.", CWE197, Certainty::normal); } void CheckType::longCastReturnError(const Token *tok) { reportError(tok, Severity::style, "truncLongCastReturn", "int result is returned as long value. If the return value is long to avoid loss of information, then you have loss of information.\n" "int result is returned as long value. If the return value is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.", CWE197, Certainty::normal); } //--------------------------------------------------------------------------- // Checking for float to integer overflow //--------------------------------------------------------------------------- void CheckType::checkFloatToIntegerOverflow() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { const ValueType *vtint, *vtfloat; const std::list *floatValues; // Explicit cast if (Token::Match(tok, "( %name%") && tok->astOperand1() && !tok->astOperand2()) { vtint = tok->valueType(); vtfloat = tok->astOperand1()->valueType(); floatValues = &tok->astOperand1()->values(); checkFloatToIntegerOverflow(tok, vtint, vtfloat, floatValues); } // Assignment else if (tok->str() == "=" && tok->astOperand1() && tok->astOperand2()) { vtint = tok->astOperand1()->valueType(); vtfloat = tok->astOperand2()->valueType(); floatValues = &tok->astOperand2()->values(); checkFloatToIntegerOverflow(tok, vtint, vtfloat, floatValues); } else if (tok->str() == "return" && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isFloat()) { const Scope *scope = tok->scope(); while (scope && scope->type != Scope::ScopeType::eLambda && scope->type != Scope::ScopeType::eFunction) scope = scope->nestedIn; if (scope && scope->type == Scope::ScopeType::eFunction && scope->function && scope->function->retDef) { const ValueType &valueType = ValueType::parseDecl(scope->function->retDef, mSettings); vtfloat = tok->astOperand1()->valueType(); floatValues = &tok->astOperand1()->values(); checkFloatToIntegerOverflow(tok, &valueType, vtfloat, floatValues); } } } } void CheckType::checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list *floatValues) { // Conversion of float to integer? if (!vtint || !vtint->isIntegral()) return; if (!vtfloat || !vtfloat->isFloat()) return; for (const ValueFlow::Value &f : *floatValues) { if (f.valueType != ValueFlow::Value::ValueType::FLOAT) continue; if (!mSettings->isEnabled(&f, false)) continue; if (f.floatValue >= std::exp2(mSettings->long_long_bit)) floatToIntegerOverflowError(tok, f); else if ((-f.floatValue) > std::exp2(mSettings->long_long_bit - 1)) floatToIntegerOverflowError(tok, f); else if (mSettings->platformType != Settings::Unspecified) { int bits = 0; if (vtint->type == ValueType::Type::CHAR) bits = mSettings->char_bit; else if (vtint->type == ValueType::Type::SHORT) bits = mSettings->short_bit; else if (vtint->type == ValueType::Type::INT) bits = mSettings->int_bit; else if (vtint->type == ValueType::Type::LONG) bits = mSettings->long_bit; else if (vtint->type == ValueType::Type::LONGLONG) bits = mSettings->long_long_bit; else continue; if (bits < MathLib::bigint_bits && f.floatValue >= (((MathLib::biguint)1) << bits)) floatToIntegerOverflowError(tok, f); } } } void CheckType::floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value) { std::ostringstream errmsg; errmsg << "Undefined behaviour: float (" << value.floatValue << ") to integer conversion overflow."; reportError(getErrorPath(tok, &value, "float to integer conversion"), value.errorSeverity() ? Severity::error : Severity::warning, "floatConversionOverflow", errmsg.str(), CWE190, value.isInconclusive() ? Certainty::inconclusive : Certainty::normal); } cppcheck-2.7/lib/checktype.h000066400000000000000000000112161417746362400160570ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checktypeH #define checktypeH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "valueflow.h" #include #include class ErrorLogger; class Settings; class Token; class Tokenizer; class ValueType; /// @addtogroup Checks /// @{ /** @brief Various small checks */ class CPPCHECKLIB CheckType : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckType() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckType(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { // These are not "simplified" because casts can't be ignored CheckType checkType(tokenizer, settings, errorLogger); checkType.checkTooBigBitwiseShift(); checkType.checkIntegerOverflow(); checkType.checkSignConversion(); checkType.checkLongCast(); checkType.checkFloatToIntegerOverflow(); } /** @brief %Check for bitwise shift with too big right operand */ void checkTooBigBitwiseShift(); /** @brief %Check for integer overflow */ void checkIntegerOverflow(); /** @brief %Check for dangerous sign conversion */ void checkSignConversion(); /** @brief %Check for implicit long cast of int result */ void checkLongCast(); /** @brief %Check for float to integer overflow */ void checkFloatToIntegerOverflow(); void checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list *floatValues); private: // Error messages.. void tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); void tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); void integerOverflowError(const Token *tok, const ValueFlow::Value &value); void signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue); void longCastAssignError(const Token *tok); void longCastReturnError(const Token *tok); void floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckType c(nullptr, settings, errorLogger); c.tooBigBitwiseShiftError(nullptr, 32, ValueFlow::Value(64)); c.tooBigSignedBitwiseShiftError(nullptr, 31, ValueFlow::Value(31)); c.integerOverflowError(nullptr, ValueFlow::Value(1LL<<32)); c.signConversionError(nullptr, nullptr, false); c.longCastAssignError(nullptr); c.longCastReturnError(nullptr); ValueFlow::Value f; f.valueType = ValueFlow::Value::ValueType::FLOAT; f.floatValue = 1E100; c.floatToIntegerOverflowError(nullptr, f); } static std::string myName() { return "Type"; } std::string classInfo() const OVERRIDE { return "Type checks\n" "- bitwise shift by too many bits (only enabled when --platform is used)\n" "- signed integer overflow (only enabled when --platform is used)\n" "- dangerous sign conversion, when signed value can be negative\n" "- possible loss of information when assigning int result to long variable\n" "- possible loss of information when returning int result as long return value\n" "- float conversion overflow\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checktypeH cppcheck-2.7/lib/checkuninitvar.cpp000066400000000000000000002166151417746362400174620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkuninitvar.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include "checknullpointer.h" // CheckNullPointer::isPointerDeref #include #include #include #include #include #include #include #include #include namespace tinyxml2 { class XMLElement; } //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckUninitVar instance; } //--------------------------------------------------------------------------- // get ast parent, skip possible address-of and casts static const Token *getAstParentSkipPossibleCastAndAddressOf(const Token *vartok, bool *unknown) { if (unknown) *unknown = false; if (!vartok) return nullptr; const Token *parent = vartok->astParent(); while (Token::Match(parent, ".|::")) parent = parent->astParent(); if (!parent) return nullptr; if (parent->isUnaryOp("&")) parent = parent->astParent(); else if (parent->str() == "&" && vartok == parent->astOperand2() && Token::Match(parent->astOperand1()->previous(), "( %type% )")) { parent = parent->astParent(); if (unknown) *unknown = true; } while (parent && parent->isCast()) parent = parent->astParent(); return parent; } void CheckUninitVar::check() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); std::set arrayTypeDefs; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% [") && tok->variable() && Token::Match(tok->variable()->typeStartToken(), "%type% %var% ;")) arrayTypeDefs.insert(tok->variable()->typeStartToken()->str()); } // check every executable scope for (const Scope &scope : symbolDatabase->scopeList) { if (scope.isExecutable()) { checkScope(&scope, arrayTypeDefs); } } } void CheckUninitVar::checkScope(const Scope* scope, const std::set &arrayTypeDefs) { for (const Variable &var : scope->varlist) { if ((mTokenizer->isCPP() && var.type() && !var.isPointer() && var.type()->needInitialization != Type::NeedInitialization::True) || var.isStatic() || var.isExtern() || var.isReference()) continue; // don't warn for try/catch exception variable if (var.isThrow()) continue; if (Token::Match(var.nameToken()->next(), "[({:]")) continue; if (Token::Match(var.nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not checkRhs(var.nameToken(), var, NO_ALLOC, 0U, emptyString); continue; } if (Token::Match(var.nameToken(), "%name% ) (") && Token::simpleMatch(var.nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not checkRhs(var.nameToken()->linkAt(2)->next(), var, NO_ALLOC, 0U, emptyString); continue; } if (var.isArray() || var.isPointerToArray()) { const Token *tok = var.nameToken()->next(); if (var.isPointerToArray()) tok = tok->next(); while (Token::simpleMatch(tok->link(), "] [")) tok = tok->link()->next(); if (Token::Match(tok->link(), "] =|{")) continue; } bool stdtype = mTokenizer->isC() && arrayTypeDefs.find(var.typeStartToken()->str()) == arrayTypeDefs.end(); const Token* tok = var.typeStartToken(); for (; tok != var.nameToken() && tok->str() != "<"; tok = tok->next()) { if (tok->isStandardType() || tok->isEnumType()) stdtype = true; } if (var.isArray() && !stdtype) { // std::array if (!(var.isStlType() && Token::simpleMatch(var.typeStartToken(), "std :: array") && var.valueType() && var.valueType()->containerTypeToken && var.valueType()->containerTypeToken->isStandardType())) continue; } while (tok && tok->str() != ";") tok = tok->next(); if (!tok) continue; if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") && checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? ARRAY : NO_ALLOC, emptyString, true)) continue; if (var.isArray()) { Alloc alloc = ARRAY; const std::map variableValue; bool init = false; for (const Token *parent = var.nameToken(); parent; parent = parent->astParent()) { if (parent->str() == "=") { init = true; break; } } if (!init) checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); continue; } if (stdtype || var.isPointer()) { Alloc alloc = NO_ALLOC; const std::map variableValue; checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); } if (var.type()) checkStruct(tok, var); } if (scope->function) { for (const Variable &arg : scope->function->argumentList) { if (arg.declarationId() && Token::Match(arg.typeStartToken(), "%type% * %name% [,)]")) { // Treat the pointer as initialized until it is assigned by malloc for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "[;{}] %varid% =", arg.declarationId())) continue; const Token *allocFuncCallToken = findAllocFuncCallToken(tok->tokAt(2)->astOperand2(), mSettings->library); if (!allocFuncCallToken) continue; const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken); if (!allocFunc || allocFunc->initData) continue; if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType())) checkStruct(tok, arg); else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) { Alloc alloc = NO_ALLOC; const std::map variableValue; checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue); } } } } } } void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar) { const Token *typeToken = structvar.typeStartToken(); while (Token::Match(typeToken, "%name% ::")) typeToken = typeToken->tokAt(2); const SymbolDatabase * symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope2 : symbolDatabase->classAndStructScopes) { if (scope2->className == typeToken->str() && scope2->numConstructors == 0U) { for (const Variable &var : scope2->varlist) { if (var.isStatic() || var.hasDefault() || var.isArray() || (!mTokenizer->isC() && var.isClass() && (!var.type() || var.type()->needInitialization != Type::NeedInitialization::True))) continue; // is the variable declared in a inner union? bool innerunion = false; for (const Scope *innerScope : scope2->nestedList) { if (innerScope->type == Scope::eUnion) { if (var.typeStartToken()->linenr() >= innerScope->bodyStart->linenr() && var.typeStartToken()->linenr() <= innerScope->bodyEnd->linenr()) { innerunion = true; break; } } } if (!innerunion) { Alloc alloc = NO_ALLOC; const Token *tok2 = tok; if (tok->str() == "}") tok2 = tok2->next(); const std::map variableValue; checkScopeForVariable(tok2, structvar, nullptr, nullptr, &alloc, var.name(), variableValue); } } } } } static VariableValue operator!(VariableValue v) { v.notEqual = !v.notEqual; return v; } static bool operator==(const VariableValue & v, MathLib::bigint i) { return v.notEqual ? (i != v.value) : (i == v.value); } static bool operator!=(const VariableValue & v, MathLib::bigint i) { return v.notEqual ? (i == v.value) : (i != v.value); } static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map &variableValue, bool *alwaysTrue, bool *alwaysFalse) { if (!tok) return; if (tok->hasKnownIntValue()) { if (tok->getKnownIntValue() == 0) *alwaysFalse = true; else *alwaysTrue = true; return; } if (tok->isName() || tok->str() == ".") { while (tok && tok->str() == ".") tok = tok->astOperand2(); const std::map::const_iterator it = variableValue.find(tok ? tok->varId() : ~0U); if (it != variableValue.end()) { *alwaysTrue = (it->second != 0LL); *alwaysFalse = (it->second == 0LL); } } else if (tok->isComparisonOp()) { if (variableValue.empty()) { return; } const Token *vartok, *numtok; if (tok->astOperand2() && tok->astOperand2()->isNumber()) { vartok = tok->astOperand1(); numtok = tok->astOperand2(); } else if (tok->astOperand1() && tok->astOperand1()->isNumber()) { vartok = tok->astOperand2(); numtok = tok->astOperand1(); } else { return; } while (vartok && vartok->str() == ".") vartok = vartok->astOperand2(); const std::map::const_iterator it = variableValue.find(vartok ? vartok->varId() : ~0U); if (it == variableValue.end()) return; if (tok->str() == "==") *alwaysTrue = (it->second == MathLib::toLongNumber(numtok->str())); else if (tok->str() == "!=") *alwaysTrue = (it->second != MathLib::toLongNumber(numtok->str())); else return; *alwaysFalse = !(*alwaysTrue); } else if (tok->str() == "!") { bool t=false,f=false; conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t, &f); if (t || f) { *alwaysTrue = !t; *alwaysFalse = !f; } } else if (tok->str() == "||") { bool t1=false, f1=false; conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1); bool t2=false, f2=false; if (!t1) conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2); *alwaysTrue = (t1 || t2); *alwaysFalse = (f1 && f2); } else if (tok->str() == "&&") { bool t1=false, f1=false; conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1); bool t2=false, f2=false; if (!f1) conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2); *alwaysTrue = (t1 && t2); *alwaysFalse = (f1 || f2); } } static bool isVariableUsed(const Token *tok, const Variable& var) { if (!tok) return false; if (tok->str() == "&" && !tok->astOperand2()) return false; if (tok->isConstOp()) return isVariableUsed(tok->astOperand1(),var) || isVariableUsed(tok->astOperand2(),var); if (tok->varId() != var.declarationId()) return false; if (!var.isArray()) return true; const Token *parent = tok->astParent(); while (Token::Match(parent, "[?:]")) parent = parent->astParent(); // no dereference, then array is not "used" if (!Token::Match(parent, "*|[")) return false; const Token *parent2 = parent->astParent(); // TODO: handle function calls. There is a TODO assertion in TestUninitVar::uninitvar_arrays return !parent2 || parent2->isConstOp() || (parent2->str() == "=" && parent2->astOperand2() == parent); } bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, Alloc* const alloc, const std::string &membervar, std::map variableValue) { const bool suppressErrors(possibleInit && *possibleInit); // Assume that this is a variable delaratkon, rather than a fundef const bool printDebug = mSettings->debugwarnings; if (possibleInit) *possibleInit = false; int number_of_if = 0; if (var.declarationId() == 0U) return true; for (; tok; tok = tok->next()) { // End of scope.. if (tok->str() == "}") { if (number_of_if && possibleInit) *possibleInit = true; // might be a noreturn function.. if (mTokenizer->isScopeNoReturn(tok)) { if (noreturn) *noreturn = true; return false; } break; } // Unconditional inner scope or try.. if (tok->str() == "{" && Token::Match(tok->previous(), ",|;|{|}|try")) { bool possibleInitInner = false; if (checkScopeForVariable(tok->next(), var, &possibleInitInner, noreturn, alloc, membervar, variableValue)) return true; tok = tok->link(); if (possibleInitInner) { number_of_if = 1; if (possibleInit) *possibleInit = true; } continue; } // assignment with nonzero constant.. if (Token::Match(tok->previous(), "[;{}] %var% = - %name% ;")) variableValue[tok->varId()] = !VariableValue(0); // Inner scope.. else if (Token::simpleMatch(tok, "if (")) { bool alwaysTrue = false; bool alwaysFalse = false; // Is variable assigned in condition? if (!membervar.empty()) { for (const Token *cond = tok->linkAt(1); cond != tok; cond = cond->previous()) { if (cond->varId() == var.declarationId() && isMemberVariableAssignment(cond, membervar)) return true; } } conditionAlwaysTrueOrFalse(tok->next()->astOperand2(), variableValue, &alwaysTrue, &alwaysFalse); // initialization / usage in condition.. if (!alwaysTrue && checkIfForWhileHead(tok->next(), var, suppressErrors, bool(number_of_if == 0), *alloc, membervar)) return true; // checking if a not-zero variable is zero => bail out nonneg int condVarId = 0; VariableValue condVarValue(0); const Token *condVarTok = nullptr; if (alwaysFalse) ; else if (Token::simpleMatch(tok, "if (") && astIsVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) { const std::map::const_iterator it = variableValue.find(condVarTok->varId()); if (it != variableValue.end() && it->second != 0) return true; // this scope is not fully analysed => return true else { condVarId = condVarTok->varId(); condVarValue = !VariableValue(0); } } else if (Token::simpleMatch(tok, "if (") && Token::Match(tok->next()->astOperand2(), "==|!=")) { const Token *condition = tok->next()->astOperand2(); const Token *lhs = condition->astOperand1(); const Token *rhs = condition->astOperand2(); const Token *vartok = (lhs && lhs->hasKnownIntValue()) ? rhs : lhs; const Token *numtok = (lhs == vartok) ? rhs : lhs; while (Token::simpleMatch(vartok, ".")) vartok = vartok->astOperand2(); if (vartok && vartok->varId() && numtok && numtok->hasKnownIntValue()) { const std::map::const_iterator it = variableValue.find(vartok->varId()); if (it != variableValue.end() && it->second != numtok->getKnownIntValue()) return true; // this scope is not fully analysed => return true condVarId = vartok->varId(); condVarValue = VariableValue(numtok->getKnownIntValue()); if (condition->str() == "!=") condVarValue = !condVarValue; } } // goto the { tok = tok->next()->link()->next(); if (!tok) break; if (tok->str() == "{") { bool possibleInitIf((!alwaysTrue && number_of_if > 0) || suppressErrors); bool noreturnIf = false; const bool initif = !alwaysFalse && checkScopeForVariable(tok->next(), var, &possibleInitIf, &noreturnIf, alloc, membervar, variableValue); // bail out for such code: // if (a) x=0; // conditional initialization // if (b) return; // cppcheck doesn't know if b can be false when a is false. // x++; // it's possible x is always initialized if (!alwaysTrue && noreturnIf && number_of_if > 0) { if (printDebug) { std::string condition; for (const Token *tok2 = tok->linkAt(-1); tok2 != tok; tok2 = tok2->next()) { condition += tok2->str(); if (tok2->isName() && tok2->next()->isName()) condition += ' '; } reportError(tok, Severity::debug, "bailoutUninitVar", "bailout uninitialized variable checking for '" + var.name() + "'. can't determine if this condition can be false when previous condition is false: " + condition); } return true; } if (alwaysTrue && (initif || noreturnIf)) return true; std::map varValueIf; if (!alwaysFalse && !initif && !noreturnIf) { for (const Token *tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}.] %name% = - %name% ;")) varValueIf[tok2->next()->varId()] = !VariableValue(0); else if (Token::Match(tok2, "[;{}.] %name% = %num% ;")) varValueIf[tok2->next()->varId()] = VariableValue(MathLib::toLongNumber(tok2->strAt(3))); } } if (initif && condVarId > 0) variableValue[condVarId] = !condVarValue; // goto the } tok = tok->link(); if (!Token::simpleMatch(tok, "} else {")) { if (initif || possibleInitIf) { ++number_of_if; if (number_of_if >= 2) return true; } } else { // goto the { tok = tok->tokAt(2); bool possibleInitElse((!alwaysFalse && number_of_if > 0) || suppressErrors); bool noreturnElse = false; const bool initelse = !alwaysTrue && checkScopeForVariable(tok->next(), var, &possibleInitElse, &noreturnElse, alloc, membervar, variableValue); std::map varValueElse; if (!alwaysTrue && !initelse && !noreturnElse) { for (const Token *tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}.] %var% = - %name% ;")) varValueElse[tok2->next()->varId()] = !VariableValue(0); else if (Token::Match(tok2, "[;{}.] %var% = %num% ;")) varValueElse[tok2->next()->varId()] = VariableValue(MathLib::toLongNumber(tok2->strAt(3))); } } if (initelse && condVarId > 0 && !noreturnIf && !noreturnElse) variableValue[condVarId] = condVarValue; // goto the } tok = tok->link(); if ((alwaysFalse || initif || noreturnIf) && (alwaysTrue || initelse || noreturnElse)) return true; if (initif || initelse || possibleInitElse) ++number_of_if; if (!initif && !noreturnIf) variableValue.insert(varValueIf.begin(), varValueIf.end()); if (!initelse && !noreturnElse) variableValue.insert(varValueElse.begin(), varValueElse.end()); } } } // = { .. } else if (Token::simpleMatch(tok, "= {")) { // end token const Token *end = tok->next()->link(); // If address of variable is taken in the block then bail out if (var.isPointer() || var.isArray()) { if (Token::findmatch(tok->tokAt(2), "%varid%", end, var.declarationId())) return true; } else if (Token::findmatch(tok->tokAt(2), "& %varid%", end, var.declarationId())) { return true; } const Token *errorToken = nullptr; visitAstNodes(tok->next(), [&](const Token *child) { if (child->isUnaryOp("&")) return ChildrenToVisit::none; if (child->str() == "," || child->str() == "{" || child->isConstOp()) return ChildrenToVisit::op1_and_op2; if (child->str() == "." && Token::Match(child->astOperand1(), "%varid%", var.declarationId()) && child->astOperand2() && child->astOperand2()->str() == membervar) { errorToken = child; return ChildrenToVisit::done; } return ChildrenToVisit::none; }); if (errorToken) { uninitStructMemberError(errorToken->astOperand2(), errorToken->astOperand1()->str() + "." + membervar); return true; } // Skip block tok = end; continue; } // skip sizeof / offsetof if (isSizeOfEtc(tok)) tok = tok->linkAt(1); // for/while.. else if (Token::Match(tok, "for|while (") || Token::simpleMatch(tok, "do {")) { const bool forwhile = Token::Match(tok, "for|while ("); // is variable initialized in for-head? if (forwhile && checkIfForWhileHead(tok->next(), var, tok->str() == "for", false, *alloc, membervar)) return true; // goto the { const Token *tok2 = forwhile ? tok->next()->link()->next() : tok->next(); if (tok2 && tok2->str() == "{") { const bool init = checkLoopBody(tok2, var, *alloc, membervar, (number_of_if > 0) || suppressErrors); // variable is initialized in the loop.. if (init) return true; // is variable used in for-head? bool initcond = false; if (!suppressErrors) { const Token *startCond = forwhile ? tok->next() : tok->next()->link()->tokAt(2); initcond = checkIfForWhileHead(startCond, var, false, bool(number_of_if == 0), *alloc, membervar); } // goto "}" tok = tok2->link(); // do-while => goto ")" if (!forwhile) { // Assert that the tokens are '} while (' if (!Token::simpleMatch(tok, "} while (")) { if (printDebug) reportError(tok,Severity::debug,"","assertion failed '} while ('"); break; } // Goto ')' tok = tok->linkAt(2); if (!tok) // bailout : invalid code / bad tokenizer break; if (initcond) // variable is initialized in while-condition return true; } } } // Unknown or unhandled inner scope else if (Token::simpleMatch(tok, ") {") || (Token::Match(tok, "%name% {") && tok->str() != "try")) { if (tok->str() == "struct" || tok->str() == "union") { tok = tok->linkAt(1); continue; } return true; } // bailout if there is ({ if (Token::simpleMatch(tok, "( {")) { return true; } // bailout if there is assembler code or setjmp if (Token::Match(tok, "asm|setjmp (")) { return true; } // bailout if there is a goto label if (Token::Match(tok, "[;{}] %name% :")) { return true; } if (tok->str() == "?") { if (!tok->astOperand2()) return true; const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); if (err) uninitvarError(tok, var.nameToken()->str(), *alloc); // Todo: skip expression if there is no error return true; } if (Token::Match(tok, "return|break|continue|throw|goto")) { if (noreturn) *noreturn = true; tok = tok->next(); while (tok && tok->str() != ";") { // variable is seen.. if (tok->varId() == var.declarationId()) { if (!membervar.empty()) { if (!suppressErrors && Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && Token::Match(tok->next()->astParent(), "%cop%|return|throw|?")) uninitStructMemberError(tok, tok->str() + "." + membervar); else if (mTokenizer->isCPP() && !suppressErrors && Token::Match(tok, "%name%") && Token::Match(tok->astParent(), "return|throw|?")) uninitStructMemberError(tok, tok->str() + "." + membervar); } // Use variable else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) uninitvarError(tok, tok->str(), *alloc); return true; } else if (isSizeOfEtc(tok)) tok = tok->linkAt(1); else if (tok->str() == "?") { if (!tok->astOperand2()) return true; const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); if (err) uninitvarError(tok, var.nameToken()->str(), *alloc); return true; } tok = tok->next(); } return (noreturn == nullptr); } // variable is seen.. if (tok->varId() == var.declarationId()) { // calling function that returns uninit data through pointer.. if (var.isPointer() && Token::simpleMatch(tok->next(), "=")) { const Token *rhs = tok->next()->astOperand2(); while (rhs && rhs->isCast()) rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); if (rhs && Token::Match(rhs->previous(), "%name% (")) { const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(rhs->astOperand1()); if (allocFunc && !allocFunc->initData) { *alloc = NO_CTOR_CALL; continue; } } } if (mTokenizer->isCPP() && var.isPointer() && (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True)) && Token::simpleMatch(tok->next(), "= new")) { *alloc = CTOR_CALL; // type has constructor(s) if (var.typeScope() && var.typeScope()->numConstructors > 0) return true; // standard or enum type: check if new initializes the allocated memory if (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType()) { // scalar new with initialization if (Token::Match(tok->next(), "= new %type% (")) return true; // array new if (Token::Match(tok->next(), "= new %type% [") && Token::simpleMatch(tok->linkAt(4), "] (")) return true; } continue; } if (!membervar.empty()) { if (isMemberVariableAssignment(tok, membervar)) { checkRhs(tok, var, *alloc, number_of_if, membervar); return true; } if (isMemberVariableUsage(tok, var.isPointer(), *alloc, membervar)) { uninitStructMemberError(tok, tok->str() + "." + membervar); return true; } if (Token::Match(tok->previous(), "[(,] %name% [,)]")) return true; if (Token::Match(tok->previous(), "= %var% . %var% ;") && membervar == tok->strAt(2)) return true; } else { // Use variable if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) { uninitvarError(tok, tok->str(), *alloc); return true; } else { const Token *parent = tok; while (parent->astParent() && ((astIsLHS(parent) && parent->astParent()->str() == "[") || parent->astParent()->isUnaryOp("*"))) { parent = parent->astParent(); if (parent->str() == "[") { if (const Token *errorToken = checkExpr(parent->astOperand2(), var, *alloc, number_of_if==0)) { if (!suppressErrors) uninitvarError(errorToken, errorToken->expressionString(), *alloc); return true; } } } if (Token::simpleMatch(parent->astParent(), "=") && astIsLHS(parent)) { const Token *eq = parent->astParent(); if (const Token *errorToken = checkExpr(eq->astOperand2(), var, *alloc, number_of_if==0)) { if (!suppressErrors) uninitvarError(errorToken, errorToken->expressionString(), *alloc); return true; } } // assume that variable is assigned return true; } } } } return false; } const Token *CheckUninitVar::checkExpr(const Token *tok, const Variable& var, const Alloc alloc, bool known, bool *bailout) { if (!tok) return nullptr; if (isSizeOfEtc(tok->previous())) return nullptr; if (tok->astOperand1()) { bool bailout1 = false; const Token *errorToken = checkExpr(tok->astOperand1(), var, alloc, known, &bailout1); if (bailout && bailout1) *bailout = true; if (errorToken) return errorToken; if ((bailout1 || !known) && Token::Match(tok, "%oror%|&&|?")) return nullptr; } if (tok->astOperand2()) return checkExpr(tok->astOperand2(), var, alloc, known, bailout); if (tok->varId() == var.declarationId()) { const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc); if (errorToken) return errorToken; else if (bailout) *bailout = true; } return nullptr; } bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar) { const Token * const endpar = startparentheses->link(); if (Token::Match(startparentheses, "( ! %name% %oror%") && startparentheses->tokAt(2)->getValue(0)) suppressErrors = true; for (const Token *tok = startparentheses->next(); tok && tok != endpar; tok = tok->next()) { if (tok->varId() == var.declarationId()) { if (Token::Match(tok, "%name% . %name%")) { if (membervar.empty()) return true; if (tok->strAt(2) == membervar) { if (isMemberVariableAssignment(tok, membervar)) return true; if (!suppressErrors && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) uninitStructMemberError(tok, tok->str() + "." + membervar); } continue; } if (const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc)) { if (suppressErrors) continue; uninitvarError(errorToken, errorToken->expressionString(), alloc); } return true; } // skip sizeof / offsetof if (isSizeOfEtc(tok)) tok = tok->linkAt(1); if ((!isuninit || !membervar.empty()) && tok->str() == "&&") suppressErrors = true; } return false; } /** recursively check loop, return error token */ const Token* CheckUninitVar::checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const { assert(start->str() == "{"); const Token *errorToken = nullptr; const Token *const end = start->link(); for (const Token *tok = start->next(); tok != end; tok = tok->next()) { // skip sizeof / offsetof if (isSizeOfEtc(tok)) { tok = tok->linkAt(1); continue; } if (Token::Match(tok, "asm ( %str% ) ;")) { bailout = true; return nullptr; } // for loop; skip third expression until loop body has been analyzed.. if (tok->str() == ";" && Token::simpleMatch(tok->astParent(), ";") && Token::simpleMatch(tok->astParent()->astParent(), "(")) { const Token *top = tok->astParent()->astParent(); if (!Token::simpleMatch(top->previous(), "for (") || !Token::simpleMatch(top->link(), ") {")) continue; const Token *bodyStart = top->link()->next(); const Token *errorToken1 = checkLoopBodyRecursive(bodyStart, var, alloc, membervar, bailout); if (!errorToken) errorToken = errorToken1; if (bailout) return nullptr; } // for loop; skip loop body if there is third expression if (Token::simpleMatch(tok, ") {") && Token::simpleMatch(tok->link()->previous(), "for (") && Token::simpleMatch(tok->link()->astOperand2(), ";") && Token::simpleMatch(tok->link()->astOperand2()->astOperand2(), ";")) { tok = tok->linkAt(1); } if (tok->str() == "{") { // switch => bailout if (tok->scope() && tok->scope()->type == Scope::ScopeType::eSwitch) { bailout = true; return nullptr; } const Token *errorToken1 = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout); tok = tok->link(); if (Token::simpleMatch(tok, "} else {")) { const Token *elseBody = tok->tokAt(2); const Token *errorToken2 = checkLoopBodyRecursive(elseBody, var, alloc, membervar, bailout); tok = elseBody->link(); if (errorToken1 && errorToken2) return errorToken1; if (errorToken2) errorToken = errorToken2; } if (bailout) return nullptr; if (!errorToken) errorToken = errorToken1; } if (tok->varId() != var.declarationId()) continue; bool conditionalUsage = false; for (const Token* parent = tok; parent; parent = parent->astParent()) { if (Token::Match(parent->astParent(), "%oror%|&&|?") && astIsRHS(parent)) { conditionalUsage = true; break; } } if (!membervar.empty()) { if (isMemberVariableAssignment(tok, membervar)) { bool assign = true; bool rhs = false; // Used for tracking if an ")" is inner or outer const Token *rpar = nullptr; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "=") rhs = true; // Look at inner expressions but not outer expressions if (!rpar && tok2->str() == "(") rpar = tok2->link(); else if (tok2->str() == ")") { // No rpar => this is an outer right parenthesis if (!rpar) break; if (rpar == tok2) rpar = nullptr; } if (tok2->str() == ";" || (!rpar && tok2->str() == ",")) break; if (rhs && tok2->varId() == var.declarationId() && isMemberVariableUsage(tok2, var.isPointer(), alloc, membervar)) { assign = false; break; } } if (assign) { bailout = true; return nullptr; } } if (isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) { if (!conditionalUsage) return tok; if (!errorToken) errorToken = tok; } else if (Token::Match(tok->previous(), "[(,] %name% [,)]")) { bailout = true; return nullptr; } } else { if (const Token *errtok = isVariableUsage(tok, var.isPointer(), alloc)) { if (!conditionalUsage) return errtok; if (!errorToken) errorToken = errtok; } else if (tok->strAt(1) == "=") { bool varIsUsedInRhs = false; visitAstNodes(tok->next()->astOperand2(), [&](const Token * t) { if (!t) return ChildrenToVisit::none; if (t->varId() == var.declarationId()) { varIsUsedInRhs = true; return ChildrenToVisit::done; } if (isSizeOfEtc(t->previous())) return ChildrenToVisit::none; return ChildrenToVisit::op1_and_op2; }); if (!varIsUsedInRhs) { bailout = true; return nullptr; } } else { bailout = true; return nullptr; } } } return errorToken; } bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors) { bool bailout = false; const Token *errorToken = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout); if (!suppressErrors && !bailout && errorToken) { if (membervar.empty()) uninitvarError(errorToken, errorToken->expressionString(), alloc); else uninitStructMemberError(errorToken, errorToken->expressionString() + "." + membervar); return true; } return bailout; } void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar) { bool rhs = false; int indent = 0; while (nullptr != (tok = tok->next())) { if (tok->str() == "=") rhs = true; else if (rhs && tok->varId() == var.declarationId()) { if (membervar.empty() && isVariableUsage(tok, var.isPointer(), alloc)) uninitvarError(tok, tok->str(), alloc); else if (!membervar.empty() && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) uninitStructMemberError(tok, tok->str() + "." + membervar); else if (Token::Match(tok, "%var% =")) break; else if (Token::Match(tok->previous(), "[(,&]")) break; } else if (tok->str() == ";" || (indent==0 && tok->str() == ",")) break; else if (tok->str() == "(") ++indent; else if (tok->str() == ")") { if (indent == 0) break; --indent; } else if (tok->str() == "?" && tok->astOperand2()) { const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); if (err) uninitvarError(tok, var.nameToken()->str(), alloc); break; } else if (isSizeOfEtc(tok)) tok = tok->linkAt(1); } } static bool astIsLhs(const Token *tok) { return tok && tok->astParent() && tok == tok->astParent()->astOperand1(); } static bool astIsRhs(const Token *tok) { return tok && tok->astParent() && tok == tok->astParent()->astOperand2(); } static bool isVoidCast(const Token *tok) { return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() && tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0; } const Token* CheckUninitVar::isVariableUsage(bool cpp, const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect) { const Token *valueExpr = vartok; // non-dereferenced , no address of value as variable while (Token::Match(valueExpr->astParent(), ".|::") && astIsRhs(valueExpr)) valueExpr = valueExpr->astParent(); // stuff we ignore.. while (valueExpr->astParent()) { // *&x if (valueExpr->astParent()->isUnaryOp("&") && valueExpr->astParent()->astParent() && valueExpr->astParent()->astParent()->isUnaryOp("*")) valueExpr = valueExpr->astParent()->astParent(); // (type &)x else if (valueExpr->astParent()->isCast() && valueExpr->astParent()->isUnaryOp("(") && Token::simpleMatch(valueExpr->astParent()->link()->previous(), "& )")) valueExpr = valueExpr->astParent(); else break; } if (!pointer) { if (Token::Match(vartok, "%name% [.(]") && vartok->variable() && !vartok->variable()->isPointer()) return nullptr; while (Token::simpleMatch(valueExpr->astParent(), ".") && astIsLhs(valueExpr) && valueExpr->astParent()->valueType() && valueExpr->astParent()->valueType()->pointer == 0) valueExpr = valueExpr->astParent(); } const Token *derefValue = nullptr; // dereferenced value expression if (alloc != NO_ALLOC) { const int arrayDim = (vartok->variable() && vartok->variable()->isArray()) ? vartok->variable()->dimensions().size() : 1; int deref = 0; derefValue = valueExpr; while (Token::Match(derefValue->astParent(), "+|-|*|[|.") || (derefValue->astParent() && derefValue->astParent()->isCast()) || (deref < arrayDim && Token::simpleMatch(derefValue->astParent(), "&") && derefValue->astParent()->isBinaryOp())) { const Token * const derefValueParent = derefValue->astParent(); if (derefValueParent->str() == "*") { if (derefValueParent->isUnaryOp("*")) ++deref; else break; } else if (derefValueParent->str() == "[") { if (astIsLhs(derefValue)) ++deref; else break; } else if (Token::Match(derefValueParent, "[+-]")) { if (deref >= arrayDim) break; } else if (derefValueParent->str() == ".") ++deref; derefValue = derefValueParent; if (deref < arrayDim) valueExpr = derefValue; } if (deref < arrayDim) { // todo compare deref with array dimensions derefValue = nullptr; } } else if (vartok->astParent() && vartok->astParent()->isUnaryOp("&")) { const Token *child = vartok->astParent(); const Token *parent = child->astParent(); while (parent && (parent->isCast() || parent->str() == "+")) { child = parent; parent = child->astParent(); } if (parent && (parent->isUnaryOp("*") || (parent->str() == "[" && astIsLhs(child)))) derefValue = parent; } if (!valueExpr->astParent()) return nullptr; // FIXME handle address of!! if (derefValue && derefValue->astParent() && derefValue->astParent()->isUnaryOp("&")) return nullptr; // BAILOUT for binary & without parent if (Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr) && Token::Match(valueExpr->astParent()->tokAt(-3), "( %name% ) &")) return nullptr; // safe operations if (isVoidCast(valueExpr->astParent())) return nullptr; if (Token::simpleMatch(valueExpr->astParent(), ".")) { const Token *parent = valueExpr->astParent(); while (Token::simpleMatch(parent, ".")) parent = parent->astParent(); if (isVoidCast(parent)) return nullptr; } if (alloc != NO_ALLOC) { if (Token::Match(valueExpr->astParent(), "%comp%|%oror%|&&|?|!")) return nullptr; if (Token::Match(valueExpr->astParent(), "%or%|&") && valueExpr->astParent()->isBinaryOp()) return nullptr; if (alloc == CTOR_CALL && derefValue && Token::simpleMatch(derefValue->astParent(), "(") && astIsLhs(derefValue)) return nullptr; if (Token::simpleMatch(valueExpr->astParent(), "return")) return nullptr; } // Passing variable to function.. if (Token::Match(valueExpr->astParent(), "[(,]") && (valueExpr->astParent()->str() == "," || astIsRhs(valueExpr))) { const Token *parent = valueExpr->astParent(); while (Token::simpleMatch(parent, ",")) parent = parent->astParent(); if (Token::simpleMatch(parent, "{")) return valueExpr; const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); return (use>0) ? valueExpr : nullptr; } if (derefValue && Token::Match(derefValue->astParent(), "[(,]") && (derefValue->astParent()->str() == "," || astIsRhs(derefValue))) { const int use = isFunctionParUsage(derefValue, library, false, NO_ALLOC, indirect); return (use>0) ? derefValue : nullptr; } if (valueExpr->astParent()->isUnaryOp("&")) { const Token *parent = valueExpr->astParent(); if (Token::Match(parent->astParent(), "[(,]") && (parent->astParent()->str() == "," || astIsRhs(parent))) { const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); return (use>0) ? valueExpr : nullptr; } return nullptr; } // Assignments; // * Is this LHS in assignment // * Passing address in RHS to pointer variable { const Token *tok = derefValue ? derefValue : valueExpr; if (alloc == NO_ALLOC) { while (tok->valueType() && tok->valueType()->pointer == 0 && Token::simpleMatch(tok->astParent(), ".")) tok = tok->astParent(); } if (Token::simpleMatch(tok->astParent(), "=")) { if (astIsLhs(tok)) return nullptr; if (alloc != NO_ALLOC && astIsRhs(valueExpr)) return nullptr; } } // Initialize reference variable if (Token::Match((derefValue ? derefValue : vartok)->astParent(), "(|=") && astIsRhs(derefValue ? derefValue : vartok)) { const Token *rhstok = derefValue ? derefValue : vartok; const Token *lhstok = rhstok->astParent()->astOperand1(); const Variable *lhsvar = lhstok->variable(); if (lhsvar && lhsvar->isReference() && lhsvar->nameToken() == lhstok) return nullptr; } // range for loop if (Token::simpleMatch(valueExpr->astParent(), ":") && valueExpr->astParent()->astParent() && Token::simpleMatch(valueExpr->astParent()->astParent()->previous(), "for (")) { if (astIsLhs(valueExpr)) return nullptr; // Taking value by reference? const Token *lhs = valueExpr->astParent()->astOperand1(); if (lhs && lhs->variable() && lhs->variable()->nameToken() == lhs && lhs->variable()->isReference()) return nullptr; } // Stream read/write // FIXME this code is a hack!! if (cpp && Token::Match(valueExpr->astParent(), "<<|>>")) { if (isLikelyStreamRead(cpp, vartok->previous())) return nullptr; if (const auto* vt = valueExpr->valueType()) { if (vt->type == ValueType::Type::VOID) return nullptr; // passing a char* to a stream will dereference it if ((alloc == CTOR_CALL || alloc == ARRAY) && vt->pointer && vt->type != ValueType::Type::CHAR && vt->type != ValueType::Type::WCHAR_T) return nullptr; } } if (astIsRhs(derefValue) && isLikelyStreamRead(cpp, derefValue->astParent())) return nullptr; // Assignment with overloaded & if (cpp && Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr)) { const Token *parent = valueExpr->astParent(); while (Token::simpleMatch(parent, "&") && parent->isBinaryOp()) parent = parent->astParent(); if (!parent) { const Token *lhs = valueExpr->astParent(); while (Token::simpleMatch(lhs, "&") && lhs->isBinaryOp()) lhs = lhs->astOperand1(); if (lhs && lhs->isName() && (!lhs->valueType() || lhs->valueType()->type <= ValueType::Type::CONTAINER)) return nullptr; // <- possible assignment } } return derefValue ? derefValue : valueExpr; } const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const { return CheckUninitVar::isVariableUsage(mTokenizer->isCPP(), vartok, mSettings->library, pointer, alloc, indirect); } /*** * Is function parameter "used" so a "usage of uninitialized variable" can * be written? If parameter is passed "by value" then it is "used". If it * is passed "by reference" then it is not necessarily "used". * @return -1 => unknown 0 => not used 1 => used */ int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect) { bool unknown = false; const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown); if (unknown || !Token::Match(parent, "[(,]")) return -1; // locate start parentheses in function call.. int argumentNumber = 0; const Token *start = vartok; while (start && !Token::Match(start, "[;{}(]")) { if (start->str() == ")") start = start->link(); else if (start->str() == ",") ++argumentNumber; start = start->previous(); } if (!start) return -1; if (Token::simpleMatch(start->link(), ") {") && Token::Match(start->previous(), "if|for|while|switch")) return (!pointer || alloc == NO_ALLOC); // is this a function call? if (Token::Match(start->previous(), "%name% (")) { const bool address(vartok->previous()->str() == "&"); const bool array(vartok->variable() && vartok->variable()->isArray()); // check how function handle uninitialized data arguments.. const Function *func = start->previous()->function(); if (func) { const Variable *arg = func->getArgumentVar(argumentNumber); if (arg) { const Token *argStart = arg->typeStartToken(); if (!address && !array && Token::Match(argStart, "%type% %name%| [,)]")) return 1; if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]")) return 1; while (argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); if (Token::Match(argStart, "const %type% & %name% [,)]")) { // If it's a record it's ok to pass a partially uninitialized struct. if (vartok->variable() && vartok->variable()->valueType() && vartok->variable()->valueType()->type == ValueType::Type::RECORD) return -1; return 1; } if ((pointer || address) && Token::Match(argStart, "const %type% %name% [") && Token::Match(argStart->linkAt(3), "] [,)]")) return 1; } } else if (Token::Match(start->previous(), "if|while|for")) { // control-flow statement reading the variable "by value" return alloc == NO_ALLOC; } else { const bool isnullbad = library.isnullargbad(start->previous(), argumentNumber + 1); if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC) return 1; bool hasIndirect = false; const bool isuninitbad = library.isuninitargbad(start->previous(), argumentNumber + 1, indirect, &hasIndirect); if (alloc != NO_ALLOC) return (isnullbad || hasIndirect) && isuninitbad; return isuninitbad && (!address || isnullbad); } } // unknown return -1; } int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const { return CheckUninitVar::isFunctionParUsage(vartok, mSettings->library, pointer, alloc, indirect); } bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const { if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) { if (Token::Match(tok->tokAt(3), "[=.[]")) return true; else if (Token::Match(tok->tokAt(-2), "[(,=] &")) return true; else if (isLikelyStreamRead(mTokenizer->isCPP(), tok->previous())) return true; else if ((tok->previous() && tok->previous()->isConstOp()) || Token::Match(tok->previous(), "[|=")) ; // member variable usage else if (tok->tokAt(3)->isConstOp()) ; // member variable usage else if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && 1 == isFunctionParUsage(tok, false, NO_ALLOC)) { return false; } else return true; } else if (tok->strAt(1) == "=") return true; else if (Token::Match(tok, "%var% . %name% (")) { const Token *ftok = tok->tokAt(2); if (!ftok->function() || !ftok->function()->isConst()) // TODO: Try to determine if membervar is assigned in method return true; } else if (tok->strAt(-1) == "&") { if (Token::Match(tok->tokAt(-2), "[(,] & %name%")) { // locate start parentheses in function call.. int argumentNumber = 0; const Token *ftok = tok; while (ftok && !Token::Match(ftok, "[;{}(]")) { if (ftok->str() == ")") ftok = ftok->link(); else if (ftok->str() == ",") ++argumentNumber; ftok = ftok->previous(); } // is this a function call? ftok = ftok ? ftok->previous() : nullptr; if (Token::Match(ftok, "%name% (")) { // check how function handle uninitialized data arguments.. const Function *function = ftok->function(); if (!function && mSettings) { // Function definition not seen, check if direction is specified in the library configuration const Library::ArgumentChecks::Direction argDirection = mSettings->library.getArgDirection(ftok, 1 + argumentNumber); if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) return false; else if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT) return true; } const Variable *arg = function ? function->getArgumentVar(argumentNumber) : nullptr; const Token *argStart = arg ? arg->typeStartToken() : nullptr; while (argStart && argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); if (Token::Match(argStart, "const struct| %type% * const| %name% [,)]")) return false; } else if (ftok && Token::simpleMatch(ftok->previous(), "= * (")) return false; } return true; } return false; } bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const { if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && tok->strAt(2) == membervar) { const int use = isFunctionParUsage(tok, isPointer, alloc); if (use == 1) return true; } if (isMemberVariableAssignment(tok, membervar)) return false; if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) { const Token *parent = tok->next()->astParent(); if (parent && parent->isUnaryOp("&")) return false; return true; } else if (!isPointer && Token::Match(tok->previous(), "[(,] %name% [,)]") && isVariableUsage(tok, isPointer, alloc)) return true; else if (!isPointer && Token::Match(tok->previous(), "= %name% ;")) return true; // = *(&var); else if (!isPointer && Token::simpleMatch(tok->astParent(),"&") && Token::simpleMatch(tok->astParent()->astParent(),"*") && Token::Match(tok->astParent()->astParent()->astParent(), "= * (| &") && tok->astParent()->astParent()->astParent()->astOperand2() == tok->astParent()->astParent()) return true; else if (mSettings->certainty.isEnabled(Certainty::experimental) && !isPointer && Token::Match(tok->tokAt(-2), "[(,] & %name% [,)]") && isVariableUsage(tok, isPointer, alloc)) return true; return false; } void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname, bool strncpy_) { reportError(tok, Severity::error, "uninitstring", "$symbol:" + varname + "\nDangerous usage of '$symbol'" + (strncpy_ ? " (strncpy doesn't always null-terminate it)." : " (not null-terminated)."), CWE_USE_OF_POTENTIALLY_DANGEROUS_FUNCTION, Certainty::normal); } void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "uninitdata", "$symbol:" + varname + "\nMemory is allocated but not initialized: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); } void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath) { errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "uninitvar", "$symbol:" + varname + "\nUninitialized variable: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); } void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v) { const Token* ltok = tok; if (tok && Token::simpleMatch(tok->astParent(), ".") && astIsRHS(tok)) ltok = tok->astParent(); const std::string& varname = ltok ? ltok->expressionString() : "x"; ErrorPath errorPath = v.errorPath; errorPath.emplace_back(tok, ""); if (v.subexpressions.empty()) { reportError(errorPath, Severity::error, "uninitvar", "$symbol:" + varname + "\nUninitialized variable: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); return; } std::string vars = v.subexpressions.size() == 1 ? "variable: " : "variables: "; std::string prefix; for (const std::string& var : v.subexpressions) { vars += prefix + varname + "." + var; prefix = ", "; } reportError(errorPath, Severity::error, "uninitvar", "$symbol:" + varname + "\nUninitialized " + vars, CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); } void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername) { reportError(tok, Severity::error, "uninitStructMember", "$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); } enum class FunctionUsage { None, PassedByReference, Used }; static FunctionUsage getFunctionUsage(const Token* tok, int indirect, const Settings* settings) { const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&"); int argnr; const Token* ftok = getTokenArgumentFunction(tok, argnr); if (!ftok) return FunctionUsage::None; if (ftok->function()) { std::vector args = getArgumentVars(ftok, argnr); for (const Variable* arg : args) { if (!arg) continue; if (arg->isReference()) return FunctionUsage::PassedByReference; } } else { const bool isnullbad = settings->library.isnullargbad(ftok, argnr + 1); if (indirect == 0 && astIsPointer(tok) && !addressOf && isnullbad) return FunctionUsage::Used; bool hasIndirect = false; const bool isuninitbad = settings->library.isuninitargbad(ftok, argnr + 1, indirect, &hasIndirect); if (isuninitbad && (!addressOf || isnullbad)) return FunctionUsage::Used; } return FunctionUsage::None; } static bool isLeafDot(const Token* tok) { if (!tok) return false; const Token * parent = tok->astParent(); if (!Token::simpleMatch(parent, ".")) return false; if (parent->astOperand2() == tok) return true; return isLeafDot(parent); } void CheckUninitVar::valueFlowUninit() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); std::unordered_set ids; for (bool subfunction : {false, true}) { // check every executable scope for (const Scope* scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (isSizeOfEtc(tok)) { tok = tok->linkAt(1); continue; } if (ids.count(tok->exprId()) > 0) continue; if (!tok->variable() && !tok->isUnaryOp("*")) continue; if (Token::Match(tok, "%name% (")) continue; const Token* parent = tok->astParent(); while (Token::simpleMatch(parent, ".")) parent = parent->astParent(); if (parent && parent->isUnaryOp("&")) continue; if (isVoidCast(parent)) continue; auto v = std::find_if( tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isUninitValue)); if (v == tok->values().end()) continue; if (v->tokvalue && ids.count(v->tokvalue->exprId()) > 0) continue; if (subfunction == (v->path == 0)) continue; if (v->isInconclusive()) continue; if (v->indirect > 1 || v->indirect < 0) continue; bool uninitderef = false; if (tok->variable()) { bool unknown; const bool isarray = !tok->variable() || tok->variable()->isArray(); const bool ispointer = astIsPointer(tok) && !isarray; const bool deref = CheckNullPointer::isPointerDeRef(tok, unknown, mSettings); if (ispointer && v->indirect == 1 && !deref) continue; if (isarray && !deref) continue; uninitderef = deref && v->indirect == 0; const bool isleaf = isLeafDot(tok) || uninitderef; if (Token::Match(tok->astParent(), ". %var%") && !isleaf) continue; } FunctionUsage fusage = getFunctionUsage(tok, v->indirect, mSettings); if (!v->subexpressions.empty() && fusage == FunctionUsage::PassedByReference) continue; if (fusage != FunctionUsage::Used) { if (!(Token::Match(tok->astParent(), ". %name% (") && uninitderef) && isVariableChanged(tok, v->indirect, mSettings, mTokenizer->isCPP())) continue; bool inconclusive = false; if (isVariableChangedByFunctionCall(tok, v->indirect, mSettings, &inconclusive) || inconclusive) continue; } uninitvarError(tok, *v); ids.insert(tok->exprId()); if (v->tokvalue) ids.insert(v->tokvalue->exprId()); const Token* nextTok = nextAfterAstRightmostLeaf(parent); if (nextTok == scope->bodyEnd) break; tok = nextTok ? nextTok : tok; } } } } std::string CheckUninitVar::MyFileInfo::toString() const { return CTU::toString(unsafeUsage); } Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { const CheckUninitVar checker(tokenizer, settings, nullptr); return checker.getFileInfo(); } static bool isVariableUsage(const Check *check, const Token *vartok, MathLib::bigint *value) { (void)value; const CheckUninitVar *c = dynamic_cast(check); return c && c->isVariableUsage(vartok, true, CheckUninitVar::Alloc::ARRAY); } Check::FileInfo *CheckUninitVar::getFileInfo() const { const std::list &unsafeUsage = CTU::getUnsafeUsage(mTokenizer, mSettings, this, ::isVariableUsage); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { const std::list &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { if (!ctu) return false; bool foundErrors = false; (void)settings; // This argument is unused const std::map> callsMap = ctu->getCallsMap(); for (Check::FileInfo *fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); if (!fi) continue; for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) { const CTU::FileInfo::FunctionCall *functionCall = nullptr; const std::list &locationList = CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::uninit, unsafeUsage, callsMap, "Using argument ARG", &functionCall, false); if (locationList.empty()) continue; const ErrorMessage errmsg(locationList, emptyString, Severity::error, "Using argument " + unsafeUsage.myArgumentName + " that points at uninitialized variable " + functionCall->callArgumentExpression, "ctuuninitvar", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); errorLogger.reportErr(errmsg); foundErrors = true; } } return foundErrors; } cppcheck-2.7/lib/checkuninitvar.h000066400000000000000000000152701417746362400171210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkuninitvarH #define checkuninitvarH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "ctu.h" #include "mathlib.h" #include "errortypes.h" #include "utils.h" #include "valueflow.h" #include #include #include #include class Scope; class Token; class Tokenizer; class Variable; class ErrorLogger; class Settings; class Library; namespace tinyxml2 { class XMLElement; } struct VariableValue { explicit VariableValue(MathLib::bigint val = 0) : value(val), notEqual(false) {} MathLib::bigint value; bool notEqual; }; /// @addtogroup Checks /// @{ /** @brief Checking for uninitialized variables */ class CPPCHECKLIB CheckUninitVar : public Check { public: /** @brief This constructor is used when registering the CheckUninitVar */ CheckUninitVar() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckUninitVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger); checkUninitVar.check(); checkUninitVar.valueFlowUninit(); } /** Check for uninitialized variables */ void check(); void checkScope(const Scope* scope, const std::set &arrayTypeDefs); void checkStruct(const Token *tok, const Variable &structvar); enum Alloc { NO_ALLOC, NO_CTOR_CALL, CTOR_CALL, ARRAY }; bool checkScopeForVariable(const Token *tok, const Variable& var, bool* const possibleInit, bool* const noreturn, Alloc* const alloc, const std::string &membervar, std::map variableValue); const Token *checkExpr(const Token *tok, const Variable& var, const Alloc alloc, bool known, bool *bailout=nullptr); bool checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar); bool checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors); const Token* checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const; void checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar); static const Token *isVariableUsage(bool cpp, const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0); const Token *isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; static int isFunctionParUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0); int isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const; bool isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const; /** ValueFlow-based checking for uninitialized variables */ void valueFlowUninit(); /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: /** function arguments that data are unconditionally read */ std::list unsafeUsage; /** Convert MyFileInfo data into xml string */ std::string toString() const OVERRIDE; }; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; void uninitvarError(const Token* tok, const ValueFlow::Value& v); void uninitstringError(const Token *tok, const std::string &varname, bool strncpy_); void uninitdataError(const Token *tok, const std::string &varname); void uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath); void uninitvarError(const Token *tok, const std::string &varname) { ErrorPath errorPath; uninitvarError(tok, varname, errorPath); } void uninitvarError(const Token *tok, const std::string &varname, Alloc alloc) { if (alloc == NO_CTOR_CALL || alloc == CTOR_CALL) uninitdataError(tok, varname); else uninitvarError(tok, varname); } void uninitStructMemberError(const Token *tok, const std::string &membername); private: Check::FileInfo *getFileInfo() const; bool isUnsafeFunction(const Scope *scope, int argnr, const Token **tok) const; void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckUninitVar c(nullptr, settings, errorLogger); ValueFlow::Value v{}; // error c.uninitvarError(nullptr, v); c.uninitstringError(nullptr, "varname", true); c.uninitdataError(nullptr, "varname"); c.uninitStructMemberError(nullptr, "a.b"); } static std::string myName() { return "Uninitialized variables"; } std::string classInfo() const OVERRIDE { return "Uninitialized variables\n" "- using uninitialized local variables\n" "- using allocated data before it has been initialized\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkuninitvarH cppcheck-2.7/lib/checkunusedfunctions.cpp000066400000000000000000000436151417746362400206750ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkunusedfunctions.h" #include "astutils.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include #include #include #include #include #include namespace CTU { class FileInfo; } //--------------------------------------------------------------------------- // Register this check class CheckUnusedFunctions CheckUnusedFunctions::instance; static const struct CWE CWE561(561U); // Dead Code //--------------------------------------------------------------------------- // FUNCTION USAGE - Check for unused functions etc //--------------------------------------------------------------------------- void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings) { const bool doMarkup = settings->library.markupFile(FileName); const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase(); // Function declarations.. for (const Scope* scope : symbolDatabase->functionScopes) { const Function* func = scope->function; if (!func || !func->token || scope->bodyStart->fileIndex() != 0) continue; // Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor)) if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction || func->isOperator()) continue; // Don't care about templates if (tokenizer.isCPP() && func->templateDef != nullptr) continue; mFunctionDecl.emplace_back(func); FunctionUsage &usage = mFunctions[func->name()]; if (!usage.lineNumber) usage.lineNumber = func->token->linenr(); // No filename set yet.. if (usage.filename.empty()) { usage.filename = tokenizer.list.getSourceFilePath(); } // Multiple files => filename = "+" else if (usage.filename != tokenizer.list.getSourceFilePath()) { //func.filename = "+"; usage.usedOtherFile |= usage.usedSameFile; } } // Function usage.. const Token *lambdaEndToken = nullptr; for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok == lambdaEndToken) lambdaEndToken = nullptr; else if (!lambdaEndToken && tok->str() == "[") lambdaEndToken = findLambdaEndToken(tok); // parsing of library code to find called functions if (settings->library.isexecutableblock(FileName, tok->str())) { const Token * markupVarToken = tok->tokAt(settings->library.blockstartoffset(FileName)); // not found if (!markupVarToken) continue; int scope = 0; bool start = true; // find all function calls in library code (starts with '(', not if or while etc) while ((scope || start) && markupVarToken) { if (markupVarToken->str() == settings->library.blockstart(FileName)) { scope++; if (start) { start = false; } } else if (markupVarToken->str() == settings->library.blockend(FileName)) scope--; else if (!settings->library.iskeyword(FileName, markupVarToken->str())) { mFunctionCalls.insert(markupVarToken->str()); if (mFunctions.find(markupVarToken->str()) != mFunctions.end()) mFunctions[markupVarToken->str()].usedOtherFile = true; else if (markupVarToken->next()->str() == "(") { FunctionUsage &func = mFunctions[markupVarToken->str()]; func.filename = tokenizer.list.getSourceFilePath(); if (func.filename.empty() || func.filename == "+") func.usedOtherFile = true; else func.usedSameFile = true; } } markupVarToken = markupVarToken->next(); } } if (!doMarkup // only check source files && settings->library.isexporter(tok->str()) && tok->next() != nullptr) { const Token * propToken = tok->next(); while (propToken && propToken->str() != ")") { if (settings->library.isexportedprefix(tok->str(), propToken->str())) { const Token* nextPropToken = propToken->next(); const std::string& value = nextPropToken->str(); if (mFunctions.find(value) != mFunctions.end()) { mFunctions[value].usedOtherFile = true; } mFunctionCalls.insert(value); } if (settings->library.isexportedsuffix(tok->str(), propToken->str())) { const Token* prevPropToken = propToken->previous(); const std::string& value = prevPropToken->str(); if (value != ")" && mFunctions.find(value) != mFunctions.end()) { mFunctions[value].usedOtherFile = true; } mFunctionCalls.insert(value); } propToken = propToken->next(); } } if (doMarkup && settings->library.isimporter(FileName, tok->str()) && tok->next()) { const Token * propToken = tok->next(); if (propToken->next()) { propToken = propToken->next(); while (propToken && propToken->str() != ")") { const std::string& value = propToken->str(); if (!value.empty()) { mFunctions[value].usedOtherFile = true; mFunctionCalls.insert(value); break; } propToken = propToken->next(); } } } if (settings->library.isreflection(tok->str())) { const int argIndex = settings->library.reflectionArgument(tok->str()); if (argIndex >= 0) { const Token * funcToken = tok->next(); int index = 0; std::string value; while (funcToken) { if (funcToken->str()==",") { if (++index == argIndex) break; value.clear(); } else value += funcToken->str(); funcToken = funcToken->next(); } if (index == argIndex) { value = value.substr(1, value.length() - 2); mFunctions[value].usedOtherFile = true; mFunctionCalls.insert(value); } } } const Token *funcname = nullptr; if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% (")) { funcname = tok; } else if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% <") && Token::simpleMatch(tok->linkAt(1), "> (")) { funcname = tok; } else if (Token::Match(tok, "< %name%") && tok->link()) { funcname = tok->next(); while (Token::Match(funcname, "%name% :: %name%")) funcname = funcname->tokAt(2); } else if (Token::Match(tok, "[;{}.,()[=+-/|!?:]")) { funcname = tok->next(); if (funcname && funcname->str() == "&") funcname = funcname->next(); if (funcname && funcname->str() == "::") funcname = funcname->next(); while (Token::Match(funcname, "%name% :: %name%")) funcname = funcname->tokAt(2); if (!Token::Match(funcname, "%name% [(),;]:}>]")) continue; } if (!funcname) continue; // funcname ( => Assert that the end parentheses isn't followed by { if (Token::Match(funcname, "%name% (|<")) { const Token *ftok = funcname->next(); if (ftok->str() == "<") ftok = ftok->link(); if (Token::Match(ftok->linkAt(1), ") const|throw|{")) funcname = nullptr; } if (funcname) { FunctionUsage &func = mFunctions[funcname->str()]; const std::string& called_from_file = tokenizer.list.getSourceFilePath(); if (func.filename.empty() || func.filename == "+" || func.filename != called_from_file) func.usedOtherFile = true; else func.usedSameFile = true; mFunctionCalls.insert(funcname->str()); } } } static bool isOperatorFunction(const std::string & funcName) { /* Operator functions are invalid function names for C, so no need to check * this in here. As result the returned error function might be incorrect. * * List of valid operators can be found at: * http://en.cppreference.com/w/cpp/language/operators * * Conversion functions must be a member function (at least for gcc), so no * need to cover them for unused functions. * * To speed up the comparison, not the whole list of operators is used. * Instead only the character after the operator prefix is checked to be a * none alpa numeric value, but the '_', to cover function names like * "operator_unused". In addition the following valid operators are checked: * - new * - new[] * - delete * - delete[] */ const std::string operatorPrefix = "operator"; if (funcName.compare(0, operatorPrefix.length(), operatorPrefix) != 0) { return false; } // Taking care of funcName == "operator", which is no valid operator if (funcName.length() == operatorPrefix.length()) { return false; } const char firstOperatorChar = funcName[operatorPrefix.length()]; if (firstOperatorChar == '_') { return false; } if (!std::isalnum(firstOperatorChar)) { return true; } const std::vector additionalOperators = { "new", "new[]", "delete", "delete[]" }; return std::find(additionalOperators.begin(), additionalOperators.end(), funcName.substr(operatorPrefix.length())) != additionalOperators.end(); } bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings& settings) { bool errors = false; for (std::map::const_iterator it = mFunctions.begin(); it != mFunctions.end(); ++it) { const FunctionUsage &func = it->second; if (func.usedOtherFile || func.filename.empty()) continue; if (it->first == "main" || (settings.isWindowsPlatform() && (it->first == "WinMain" || it->first == "_tmain")) || it->first == "if") continue; if (!func.usedSameFile) { if (isOperatorFunction(it->first)) continue; std::string filename; if (func.filename != "+") filename = func.filename; unusedFunctionError(errorLogger, filename, func.lineNumber, it->first); errors = true; } else if (!func.usedOtherFile) { /** @todo add error message "function is only used in it can be static" */ /* std::ostringstream errmsg; errmsg << "The function '" << it->first << "' is only used in the file it was declared in so it should have local linkage."; mErrorLogger->reportErr( errmsg.str() ); errors = true; */ } } return errors; } void CheckUnusedFunctions::unusedFunctionError(ErrorLogger * const errorLogger, const std::string &filename, unsigned int lineNumber, const std::string &funcname) { std::list locationList; if (!filename.empty()) { ErrorMessage::FileLocation fileLoc; fileLoc.setfile(filename); fileLoc.line = lineNumber; locationList.push_back(fileLoc); } const ErrorMessage errmsg(locationList, emptyString, Severity::style, "$symbol:" + funcname + "\nThe function '$symbol' is never used.", "unusedFunction", CWE561, Certainty::normal); if (errorLogger) errorLogger->reportErr(errmsg); else reportError(errmsg); } Check::FileInfo *CheckUnusedFunctions::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { if (!settings->checks.isEnabled(Checks::unusedFunction)) return nullptr; if (settings->jobs == 1 && settings->buildDir.empty()) instance.parseTokens(*tokenizer, tokenizer->list.getFiles().front().c_str(), settings); return nullptr; } bool CheckUnusedFunctions::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { (void)ctu; (void)fileInfo; return check(&errorLogger, settings); } CheckUnusedFunctions::FunctionDecl::FunctionDecl(const Function *f) : functionName(f->name()), lineNumber(f->token->linenr()) {} std::string CheckUnusedFunctions::analyzerInfo() const { std::ostringstream ret; for (const FunctionDecl &functionDecl : mFunctionDecl) { ret << " \n"; } for (const std::string &fc : mFunctionCalls) { ret << " \n"; } return ret.str(); } namespace { struct Location { Location() : lineNumber(0) {} Location(const std::string &f, const int l) : fileName(f), lineNumber(l) {} std::string fileName; int lineNumber; }; } void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir) { std::map decls; std::set calls; const std::string filesTxt(buildDir + "/files.txt"); std::ifstream fin(filesTxt.c_str()); std::string filesTxtLine; while (std::getline(fin, filesTxtLine)) { const std::string::size_type firstColon = filesTxtLine.find(':'); if (firstColon == std::string::npos) continue; const std::string::size_type secondColon = filesTxtLine.find(':', firstColon+1); if (secondColon == std::string::npos) continue; const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); const std::string sourcefile = filesTxtLine.substr(secondColon+1); tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); if (error != tinyxml2::XML_SUCCESS) continue; const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); if (rootNode == nullptr) continue; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "FileInfo") != 0) continue; const char *checkattr = e->Attribute("check"); if (checkattr == nullptr || std::strcmp(checkattr,"CheckUnusedFunctions") != 0) continue; for (const tinyxml2::XMLElement *e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { const char* functionName = e2->Attribute("functionName"); if (functionName == nullptr) continue; if (std::strcmp(e2->Name(),"functioncall") == 0) { calls.insert(functionName); continue; } else if (std::strcmp(e2->Name(),"functiondecl") == 0) { const char* lineNumber = e2->Attribute("lineNumber"); if (lineNumber) decls[functionName] = Location(sourcefile, std::atoi(lineNumber)); } } } } for (std::map::const_iterator decl = decls.begin(); decl != decls.end(); ++decl) { const std::string &functionName = decl->first; // TODO: move to configuration files // TODO: WinMain, wmain and _tmain only apply to Windows code // TODO: also skip other known entry functions i.e. annotated with "constructor" and "destructor" attributes if (functionName == "main" || functionName == "WinMain" || functionName == "wmain" || functionName == "_tmain" || functionName == "if") continue; if (calls.find(functionName) == calls.end() && !isOperatorFunction(functionName)) { const Location &loc = decl->second; unusedFunctionError(errorLogger, loc.fileName, loc.lineNumber, functionName); } } } cppcheck-2.7/lib/checkunusedfunctions.h000066400000000000000000000103121417746362400203260ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkunusedfunctionsH #define checkunusedfunctionsH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include #include #include #include class ErrorLogger; class Function; class Settings; class Tokenizer; namespace CTU { class FileInfo; } /// @addtogroup Checks /** @brief Check for functions never called */ /// @{ class CPPCHECKLIB CheckUnusedFunctions : public Check { public: /** @brief This constructor is used when registering the CheckUnusedFunctions */ CheckUnusedFunctions() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckUnusedFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} static void clear() { instance.mFunctions.clear(); instance.mFunctionCalls.clear(); } // Parse current tokens and determine.. // * Check what functions are used // * What functions are declared void parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings); // Return true if an error is reported. bool check(ErrorLogger * const errorLogger, const Settings& settings); /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; static CheckUnusedFunctions instance; std::string analyzerInfo() const; /** @brief Combine and analyze all analyzerInfos for all TUs */ static void analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir); private: void getErrorMessages(ErrorLogger *errorLogger, const Settings * /*settings*/) const OVERRIDE { CheckUnusedFunctions::unusedFunctionError(errorLogger, emptyString, 0, "funcName"); } void runChecks(const Tokenizer * /*tokenizer*/, const Settings * /*settings*/, ErrorLogger * /*errorLogger*/) OVERRIDE {} /** * Dummy implementation, just to provide error for --errorlist */ static void unusedFunctionError(ErrorLogger * const errorLogger, const std::string &filename, unsigned int lineNumber, const std::string &funcname); static std::string myName() { return "Unused functions"; } std::string classInfo() const OVERRIDE { return "Check for functions that are never called\n"; } class CPPCHECKLIB FunctionUsage { public: FunctionUsage() : lineNumber(0), usedSameFile(false), usedOtherFile(false) {} std::string filename; unsigned int lineNumber; bool usedSameFile; bool usedOtherFile; }; std::map mFunctions; class CPPCHECKLIB FunctionDecl { public: explicit FunctionDecl(const Function *f); std::string functionName; unsigned int lineNumber; }; std::list mFunctionDecl; std::set mFunctionCalls; }; /// @} //--------------------------------------------------------------------------- #endif // checkunusedfunctionsH cppcheck-2.7/lib/checkunusedvar.cpp000066400000000000000000002043461417746362400174550ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkunusedvar.h" #include "astutils.h" #include "errortypes.h" #include "library.h" #include "preprocessor.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckUnusedVar instance; } static const struct CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') static const struct CWE CWE665(665U); // Improper Initialization /** Is scope a raii class scope */ static bool isRaiiClassScope(const Scope *classScope) { return classScope && classScope->getDestructor() != nullptr; } /** Is ValueType a raii class? */ static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn = true) { if (!cpp) return false; if (!valueType) return defaultReturn; if (valueType->smartPointerType && isRaiiClassScope(valueType->smartPointerType->classScope)) return true; switch (valueType->type) { case ValueType::Type::UNKNOWN_TYPE: case ValueType::Type::NONSTD: return defaultReturn; case ValueType::Type::RECORD: if (isRaiiClassScope(valueType->typeScope)) return true; return defaultReturn; case ValueType::Type::SMART_POINTER: case ValueType::Type::CONTAINER: case ValueType::Type::ITERATOR: case ValueType::Type::VOID: case ValueType::Type::BOOL: case ValueType::Type::CHAR: case ValueType::Type::SHORT: case ValueType::Type::WCHAR_T: case ValueType::Type::INT: case ValueType::Type::LONG: case ValueType::Type::LONGLONG: case ValueType::Type::UNKNOWN_INT: case ValueType::Type::FLOAT: case ValueType::Type::DOUBLE: case ValueType::Type::LONGDOUBLE: return false; } return defaultReturn; } /** * @brief This class is used create a list of variables within a function. */ class Variables { public: enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer, none }; /** Store information about variable usage */ class VariableUsage { public: explicit VariableUsage(const Variable *var = nullptr, VariableType type = standard, bool read = false, bool write = false, bool modified = false, bool allocateMemory = false) : _var(var), _lastAccess(var ? var->nameToken() : nullptr), mType(type), _read(read), _write(write), _modified(modified), _allocateMemory(allocateMemory) {} /** variable is used.. set both read+write */ void use() { _read = true; _write = true; } /** is variable unused? */ bool unused() const { return (!_read && !_write); } std::set _aliases; std::set _assignments; const Variable* _var; const Token* _lastAccess; VariableType mType; bool _read; bool _write; bool _modified; // read/modify/write bool _allocateMemory; }; void clear() { mVarUsage.clear(); } const std::map &varUsage() const { return mVarUsage; } void addVar(const Variable *var, VariableType type, bool write_); void allocateMemory(nonneg int varid, const Token* tok); void read(nonneg int varid, const Token* tok); void readAliases(nonneg int varid, const Token* tok); void readAll(nonneg int varid, const Token* tok); void write(nonneg int varid, const Token* tok); void writeAliases(nonneg int varid, const Token* tok); void writeAll(nonneg int varid, const Token* tok); void use(nonneg int varid, const Token* tok); void modified(nonneg int varid, const Token* tok); VariableUsage *find(nonneg int varid); void alias(nonneg int varid1, nonneg int varid2, bool replace); void erase(nonneg int varid) { mVarUsage.erase(varid); } void eraseAliases(nonneg int varid); void eraseAll(nonneg int varid); void clearAliases(nonneg int varid); private: std::map mVarUsage; }; /** * Alias the 2 given variables. Either replace the existing aliases if * they exist or merge them. You would replace an existing alias when this * assignment is in the same scope as the previous assignment. You might * merge the aliases when this assignment is in a different scope from the * previous assignment depending on the relationship of the 2 scopes. */ void Variables::alias(nonneg int varid1, nonneg int varid2, bool replace) { VariableUsage *var1 = find(varid1); VariableUsage *var2 = find(varid2); if (!var1 || !var2) return; // alias to self if (varid1 == varid2) { var1->use(); return; } if (replace) { // remove var1 from all aliases for (std::set::const_iterator i = var1->_aliases.begin(); i != var1->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(var1->_var->declarationId()); } // remove all aliases from var1 var1->_aliases.clear(); } // var1 gets all var2s aliases for (std::set::const_iterator i = var2->_aliases.begin(); i != var2->_aliases.end(); ++i) { if (*i != varid1) var1->_aliases.insert(*i); } // var2 is an alias of var1 var2->_aliases.insert(varid1); var1->_aliases.insert(varid2); if (var2->mType == Variables::pointer) { var2->_read = true; } } void Variables::clearAliases(nonneg int varid) { VariableUsage *usage = find(varid); if (usage) { // remove usage from all aliases std::set::const_iterator i; for (i = usage->_aliases.begin(); i != usage->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(usage->_var->declarationId()); } // remove all aliases from usage usage->_aliases.clear(); } } void Variables::eraseAliases(nonneg int varid) { VariableUsage *usage = find(varid); if (usage) { for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) erase(*aliases); } } void Variables::eraseAll(nonneg int varid) { eraseAliases(varid); erase(varid); } void Variables::addVar(const Variable *var, VariableType type, bool write_) { if (var->declarationId() > 0) { mVarUsage.insert(std::make_pair(var->declarationId(), VariableUsage(var, type, false, write_, false))); } } void Variables::allocateMemory(nonneg int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->_allocateMemory = true; usage->_lastAccess = tok; } } void Variables::read(nonneg int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->_read = true; if (tok) usage->_lastAccess = tok; } } void Variables::readAliases(nonneg int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { for (nonneg int aliases : usage->_aliases) { VariableUsage *aliased = find(aliases); if (aliased) { aliased->_read = true; aliased->_lastAccess = tok; } } } } void Variables::readAll(nonneg int varid, const Token* tok) { read(varid, tok); readAliases(varid, tok); } void Variables::write(nonneg int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->_write = true; if (!usage->_var->isStatic() && !Token::simpleMatch(tok->next(), "= 0 ;")) usage->_read = false; usage->_lastAccess = tok; } } void Variables::writeAliases(nonneg int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->_write = true; aliased->_lastAccess = tok; } } } } void Variables::writeAll(nonneg int varid, const Token* tok) { write(varid, tok); writeAliases(varid, tok); } void Variables::use(nonneg int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->use(); usage->_lastAccess = tok; for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->use(); aliased->_lastAccess = tok; } } } } void Variables::modified(nonneg int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { if (!usage->_var->isStatic()) usage->_read = false; usage->_modified = true; usage->_lastAccess = tok; for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->_modified = true; aliased->_lastAccess = tok; } } } } Variables::VariableUsage *Variables::find(nonneg int varid) { if (varid) { std::map::iterator i = mVarUsage.find(varid); if (i != mVarUsage.end()) return &i->second; } return nullptr; } static const Token* doAssignment(Variables &variables, const Token *tok, bool dereference, const Scope *scope) { // a = a + b; if (Token::Match(tok, "%var% = %var% !!;")) { const Token* rhsVarTok = tok->tokAt(2); if (tok->varId() == rhsVarTok->varId()) { return rhsVarTok; } } if (Token::Match(tok, "%var% %assign%") && tok->strAt(1) != "=") return tok->next(); const Token* const tokOld = tok; // check for aliased variable const nonneg int varid1 = tok->varId(); Variables::VariableUsage *var1 = variables.find(varid1); if (var1) { // jump behind '=' tok = tok->next(); while (!tok->isAssignmentOp()) { if (tok->varId()) variables.read(tok->varId(), tok); tok = tok->next(); } tok = tok->next(); if (Token::Match(tok, "( const| struct|union| %type% * ) ( (")) tok = tok->link()->next(); if (Token::Match(tok, "( [(<] const| struct|union| %type% *| [>)]")) tok = tok->next(); if (Token::Match(tok, "(| &| %name%") || (Token::Match(tok->next(), "< const| struct|union| %type% *| > ( &| %name%"))) { bool addressOf = false; if (Token::Match(tok, "%var% .")) variables.use(tok->varId(), tok); // use = read + write // check for C style cast if (tok->str() == "(") { tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (Token::Match(tok, "struct|union")) tok = tok->next(); while ((tok->isName() && tok->varId() == 0) || (tok->str() == "*") || (tok->str() == ")")) tok = tok->next(); if (tok->str() == "&") { addressOf = true; tok = tok->next(); } else if (tok->str() == "(") { tok = tok->next(); if (tok->str() == "&") { addressOf = true; tok = tok->next(); } } else if (Token::Match(tok, "%cop% %var%")) { variables.read(tok->next()->varId(), tok); } } // check for C++ style cast else if (tok->str().find("cast") != std::string::npos && tok->strAt(1) == "<") { tok = tok->tokAt(2); if (tok->str() == "const") tok = tok->next(); if (Token::Match(tok, "struct|union")) tok = tok->next(); tok = tok->next(); if (tok->str() == "*") tok = tok->next(); tok = tok->tokAt(2); if (!tok) return tokOld; if (tok->str() == "&") { addressOf = true; tok = tok->next(); } } // no cast, no ? else if (!Token::Match(tok, "%name% ?")) { if (tok->str() == "&") { addressOf = true; tok = tok->next(); } else if (tok->str() == "new") return tokOld; } // check if variable is local const nonneg int varid2 = tok->varId(); const Variables::VariableUsage* var2 = variables.find(varid2); if (var2) { // local variable (alias or read it) if (var1->mType == Variables::pointer || var1->mType == Variables::pointerArray) { if (dereference) variables.read(varid2, tok); else { if (addressOf || var2->mType == Variables::array || var2->mType == Variables::pointer) { bool replace = true; // pointerArray => don't replace if (var1->mType == Variables::pointerArray) replace = false; // check if variable declared in same scope else if (scope == var1->_var->scope()) replace = true; // not in same scope as declaration else { // no other assignment in this scope if (var1->_assignments.find(scope) == var1->_assignments.end() || scope->type == Scope::eSwitch) { // nothing to replace // cppcheck-suppress duplicateBranch - remove when TODO below is address if (var1->_assignments.empty()) replace = false; // this variable has previous assignments else { // TODO: determine if existing aliases should be replaced or merged replace = false; } } // assignment in this scope else { // replace when only one other assignment, merge them otherwise replace = (var1->_assignments.size() == 1); } } variables.alias(varid1, varid2, replace); } else if (tok->strAt(1) == "?") { if (var2->mType == Variables::reference) variables.readAliases(varid2, tok); else variables.read(varid2, tok); } else { variables.readAll(varid2, tok); } } } else if (var1->mType == Variables::reference) { variables.alias(varid1, varid2, true); } else if (var1->mType == Variables::standard && addressOf) { variables.alias(varid1, varid2, true); } else { if ((var2->mType == Variables::pointer || var2->mType == Variables::pointerArray) && tok->strAt(1) == "[") variables.readAliases(varid2, tok); variables.read(varid2, tok); } } else { // not a local variable (or an unsupported local variable) if (var1->mType == Variables::pointer && !dereference) { // check if variable declaration is in this scope if (var1->_var->scope() == scope) { // If variable is used in RHS then "use" variable for (const Token *rhs = tok; rhs && rhs->str() != ";"; rhs = rhs->next()) { if (rhs->varId() == varid1) { variables.use(varid1, tok); break; } } variables.clearAliases(varid1); } else { // no other assignment in this scope if (var1->_assignments.find(scope) == var1->_assignments.end()) { /** * @todo determine if existing aliases should be discarded */ } // this assignment replaces the last assignment in this scope else { // aliased variables in a larger scope are not supported // remove all aliases variables.clearAliases(varid1); } } } } } else tok = tokOld; var1->_assignments.insert(scope); } // check for alias to struct member // char c[10]; a.b = c; else if (Token::Match(tok->tokAt(-2), "%name% .")) { const Token *rhsVarTok = tok->tokAt(2); if (rhsVarTok && rhsVarTok->varId()) { const nonneg int varid2 = rhsVarTok->varId(); const Variables::VariableUsage *var2 = variables.find(varid2); // struct member aliased to local variable if (var2 && (var2->mType == Variables::array || var2->mType == Variables::pointer)) { // erase aliased variable and all variables that alias it // to prevent false positives variables.eraseAll(varid2); } } } // Possible pointer alias else if (Token::Match(tok, "%name% = %name% ;")) { const nonneg int varid2 = tok->tokAt(2)->varId(); const Variables::VariableUsage *var2 = variables.find(varid2); if (var2 && (var2->mType == Variables::array || var2->mType == Variables::pointer)) { variables.use(varid2,tok); } } return tok; } static bool isPartOfClassStructUnion(const Token* tok) { for (; tok; tok = tok->previous()) { if (tok->str() == "}" || tok->str() == ")") tok = tok->link(); else if (tok->str() == "(") return (false); else if (tok->str() == "{") { return (tok->strAt(-1) == "struct" || tok->strAt(-2) == "struct" || tok->strAt(-1) == "class" || tok->strAt(-2) == "class" || tok->strAt(-1) == "union" || tok->strAt(-2) == "union"); } } return false; } static bool isVarDecl(const Token *tok) { return tok && tok->variable() && tok->variable()->nameToken() == tok; } // Skip [ .. ] static const Token * skipBrackets(const Token *tok) { while (tok && tok->str() == "[") tok = tok->link()->next(); return tok; } // Skip [ .. ] . x static const Token * skipBracketsAndMembers(const Token *tok) { while (tok) { if (tok->str() == "[") tok = tok->link()->next(); else if (Token::Match(tok, ". %name%")) tok = tok->tokAt(2); else break; } return tok; } static void useFunctionArgs(const Token *tok, Variables& variables) { // TODO: Match function args to see if they are const or not. Assume that const data is not written. if (!tok) return; if (tok->str() == ",") { useFunctionArgs(tok->astOperand1(), variables); useFunctionArgs(tok->astOperand2(), variables); } else if (Token::Match(tok, "[+:]") && (!tok->valueType() || tok->valueType()->pointer)) { useFunctionArgs(tok->astOperand1(), variables); useFunctionArgs(tok->astOperand2(), variables); } else if (tok->variable() && tok->variable()->isArray()) { variables.use(tok->varId(), tok); } } //--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) { // Find declarations if the scope is executable.. if (scope->isExecutable()) { // Find declarations for (std::list::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { if (i->isThrow() || i->isExtern()) continue; Variables::VariableType type = Variables::none; if (i->isArray() && (i->nameToken()->previous()->str() == "*" || i->nameToken()->strAt(-2) == "*")) type = Variables::pointerArray; else if (i->isArray() && i->nameToken()->previous()->str() == "&") type = Variables::referenceArray; else if (i->isArray()) type = (i->dimensions().size() == 1U) ? Variables::array : Variables::pointerArray; else if (i->isReference()) type = Variables::reference; else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*") type = Variables::pointerPointer; else if (i->isPointerToArray()) type = Variables::pointerPointer; else if (i->isPointer()) type = Variables::pointer; else if (mTokenizer->isC() || i->typeEndToken()->isStandardType() || isRecordTypeWithoutSideEffects(i->type()) || mSettings->library.detectContainer(i->typeStartToken(), /*iterator*/ false) || i->isStlType()) type = Variables::standard; if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken())) continue; const Token* defValTok = i->nameToken()->next(); if (Token::Match(i->nameToken()->previous(), "* %var% ) (")) // function pointer. Jump behind parameter list. defValTok = defValTok->linkAt(1)->next(); for (; defValTok; defValTok = defValTok->next()) { if (defValTok->str() == "[") defValTok = defValTok->link(); else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=" || defValTok->str() == ":") { variables.addVar(&*i, type, true); break; } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") { variables.addVar(&*i, type, i->isStatic()); break; } } if (i->isArray() && i->isClass() && // Array of class/struct members. Initialized by ctor except for std::array !(i->isStlType() && i->valueType() && i->valueType()->containerTypeToken && i->valueType()->containerTypeToken->isStandardType())) variables.write(i->declarationId(), i->nameToken()); if (i->isArray() && Token::Match(i->nameToken(), "%name% [ %var% ]")) // Array index variable read. variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken()); if (defValTok && defValTok->next()) { // simple assignment "var = 123" if (defValTok->str() == "=" && defValTok->next()->str() != "{") { doAssignment(variables, i->nameToken(), false, scope); } else { // could be "var = {...}" OR "var{...}" (since C++11) const Token* tokBraceStart = nullptr; if (Token::simpleMatch(defValTok, "= {")) { // "var = {...}" tokBraceStart = defValTok->next(); } else if (defValTok->str() == "{") { // "var{...}" tokBraceStart = defValTok; } if (tokBraceStart) { for (const Token* tok = tokBraceStart->next(); tok && tok != tokBraceStart->link(); tok = tok->next()) { if (tok->varId()) { // Variables used to initialize the array read. variables.read(tok->varId(), i->nameToken()); } } } } } } } // Check variable usage const Token *tok; if (scope->type == Scope::eFunction) tok = scope->bodyStart->next(); else tok = scope->classDef->next(); for (; tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "{" && tok != scope->bodyStart && !tok->previous()->varId()) { for (const Scope *i : scope->nestedList) { if (i->bodyStart == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(tok->scope(), variables); // Scan child scope tok = tok->link(); break; } } if (!tok) break; } if (Token::Match(tok, "asm ( %str% )")) { variables.clear(); break; } // templates if (tok->isName() && endsWith(tok->str(), '>')) { // TODO: This is a quick fix to handle when constants are used // as template parameters. Try to handle this better, perhaps // only remove constants. variables.clear(); } else if (Token::Match(tok->previous(), "[;{}]")) { for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId()) { // Is this a variable declaration? const Variable *var = tok2->variable(); if (!var || var->nameToken() != tok2) continue; // Mark template parameters used in declaration as use.. if (tok2->strAt(-1) == ">") { for (const Token *tok3 = tok; tok3 != tok2; tok3 = tok3->next()) { if (tok3->varId()) variables.use(tok3->varId(), tok3); } } // Skip variable declaration.. tok = tok2->next(); if (Token::Match(tok, "( %name% )")) // Simple initialization through copy ctor tok = tok->next(); else if (Token::Match(tok, "= %var% ;")) { // Simple initialization tok = tok->next(); if (!var->isReference()) variables.read(tok->varId(), tok); } else if (tok->str() == "[" && Token::simpleMatch(skipBrackets(tok),"= {")) { const Token * const rhs1 = skipBrackets(tok)->next(); for (const Token *rhs = rhs1->link(); rhs != rhs1; rhs = rhs->previous()) { if (rhs->varId()) variables.readAll(rhs->varId(), rhs); } } else if (var->typeEndToken()->str() == ">") // Be careful with types like std::vector tok = tok->previous(); break; } else if (Token::Match(tok2, "[;({=]")) break; } } // Freeing memory (not considered "using" the pointer if it was also allocated in this function) if (Token::Match(tok, "free|g_free|kfree|vfree ( %var% )") || (mTokenizer->isCPP() && (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")))) { nonneg int varid = 0; if (tok->str() != "delete") { const Token *varTok = tok->tokAt(2); varid = varTok->varId(); tok = varTok->next(); } else if (tok->strAt(1) == "[") { const Token *varTok = tok->tokAt(3); varid = varTok->varId(); tok = varTok; } else { varid = tok->next()->varId(); tok = tok->next(); } const Variables::VariableUsage *const var = variables.find(varid); if (var) { if (!var->_aliases.empty()) variables.use(varid, tok); else if (!var->_allocateMemory) variables.readAll(varid, tok); } } else if (Token::Match(tok, "return|throw")) { for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId()) variables.readAll(tok2->varId(), tok); else if (tok2->str() == ";") break; } } // assignment else if (Token::Match(tok, "*| ++|--| %name% ++|--| %assign%") || Token::Match(tok, "*| ( const| %type% *| ) %name% %assign%")) { bool dereference = false; bool pre = false; bool post = false; if (tok->str() == "*") { dereference = true; tok = tok->next(); } if (Token::Match(tok, "( const| %type% *| ) %name% %assign%")) tok = tok->link()->next(); else if (tok->str() == "(") tok = tok->next(); if (tok->tokType() == Token::eIncDecOp) { pre = true; tok = tok->next(); } if (tok->next()->tokType() == Token::eIncDecOp) post = true; const nonneg int varid1 = tok->varId(); const Token * const start = tok; // assignment in while head.. bool inwhile = false; { const Token *parent = tok->astParent(); while (parent) { if (Token::simpleMatch(parent->previous(), "while (")) { inwhile = true; break; } parent = parent->astParent(); } } tok = doAssignment(variables, tok, dereference, scope); if (tok && tok->isAssignmentOp() && tok->str() != "=") { variables.use(varid1, tok); if (Token::Match(tok, "%assign% %name%")) { tok = tok->next(); variables.read(tok->varId(), tok); } } if (pre || post) variables.use(varid1, tok); if (dereference) { const Variables::VariableUsage *const var = variables.find(varid1); if (var && var->mType == Variables::array) variables.write(varid1, tok); variables.writeAliases(varid1, tok); variables.read(varid1, tok); } else { const Variables::VariableUsage *const var = variables.find(varid1); if (var && (inwhile || start->strAt(-1) == ",")) { variables.use(varid1, tok); } else if (var && var->mType == Variables::reference) { variables.writeAliases(varid1, tok); variables.read(varid1, tok); } // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable else if (var && var->mType == Variables::pointer && Token::Match(start, "%name% =") && findAllocFuncCallToken(start->next()->astOperand2(), mSettings->library)) { const Token *allocFuncCallToken = findAllocFuncCallToken(start->next()->astOperand2(), mSettings->library); const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken); bool allocateMemory = !allocFunc || Library::ismemory(allocFunc->groupId); if (allocFuncCallToken->str() == "new") { const Token *type = allocFuncCallToken->next(); // skip nothrow if (mTokenizer->isCPP() && (Token::simpleMatch(type, "( nothrow )") || Token::simpleMatch(type, "( std :: nothrow )"))) type = type->link()->next(); // is it a user defined type? if (!type->isStandardType()) { const Variable *variable = start->variable(); if (!variable || !isRecordTypeWithoutSideEffects(variable->type())) allocateMemory = false; } } if (allocateMemory) variables.allocateMemory(varid1, tok); else variables.write(varid1, tok); } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) { variables.read(varid1, tok); variables.write(varid1, start); } else if (var && var->mType == Variables::pointer && Token::Match(tok, "%name% ;") && tok->varId() == 0 && tok->hasKnownIntValue() && tok->values().front().intvalue == 0) { variables.use(varid1, tok); } else { variables.write(varid1, tok); } } const Variables::VariableUsage * const var2 = variables.find(tok->varId()); if (var2) { if (var2->mType == Variables::reference) { variables.writeAliases(tok->varId(), tok); variables.read(tok->varId(), tok); } else if (tok->varId() != varid1 && Token::Match(tok, "%name% .|[")) variables.read(tok->varId(), tok); else if (tok->varId() != varid1 && var2->mType == Variables::standard && tok->strAt(-1) != "&") variables.use(tok->varId(), tok); } const Token * const equal = skipBracketsAndMembers(tok->next()); // checked for chained assignments if (tok != start && equal && equal->str() == "=") { const nonneg int varId = tok->varId(); const Variables::VariableUsage * const var = variables.find(varId); if (var && var->mType != Variables::reference) { variables.read(varId,tok); } tok = tok->previous(); } } // assignment else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) || (Token::simpleMatch(tok, "* (") && Token::simpleMatch(tok->next()->link(), ") ="))) { const Token *eq = tok; while (eq && !eq->isAssignmentOp()) eq = eq->astParent(); const bool deref = eq && eq->astOperand1() && eq->astOperand1()->valueType() && eq->astOperand1()->valueType()->pointer == 0U; if (tok->str() == "*") { tok = tok->tokAt(2); if (tok->str() == "(") tok = tok->link()->next(); } const nonneg int varid = tok->varId(); const Variables::VariableUsage *var = variables.find(varid); if (var) { // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable if (var->mType == Variables::pointer && Token::Match(skipBrackets(tok->next()), "= new|malloc|calloc|kmalloc|kzalloc|kcalloc|strdup|strndup|vmalloc|g_new0|g_try_new|g_new|g_malloc|g_malloc0|g_try_malloc|g_try_malloc0|g_strdup|g_strndup|g_strdup_printf")) { variables.allocateMemory(varid, tok); } else if (var->mType == Variables::pointer || var->mType == Variables::reference) { variables.read(varid, tok); variables.writeAliases(varid, tok); } else if (var->mType == Variables::pointerArray) { tok = doAssignment(variables, tok, deref, scope); } else variables.writeAll(varid, tok); } } else if (mTokenizer->isCPP() && Token::Match(tok, "[;{}] %var% <<")) { variables.erase(tok->next()->varId()); } else if (Token::Match(tok, "& %var%")) { if (tok->astOperand2()) { // bitop variables.read(tok->next()->varId(), tok); } else // addressof variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, ">>|>>= %name%")) { if (isLikelyStreamRead(mTokenizer->isCPP(), tok)) variables.use(tok->next()->varId(), tok); // use = read + write else variables.read(tok->next()->varId(), tok); } else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) { variables.read(tok->varId(), tok); } else if (isLikelyStreamRead(mTokenizer->isCPP(),tok->previous())) { variables.use(tok->varId(), tok); } // function parameter else if (Token::Match(tok, "[(,] %var% [")) { variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") { variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] & %var% [,)]")) { variables.eraseAll(tok->tokAt(2)->varId()); } else if (Token::Match(tok, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) { variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] *| %var% =")) { tok = tok->next(); if (tok->str() == "*") tok = tok->next(); variables.use(tok->varId(), tok); } // function else if (Token::Match(tok, "%name% (")) { variables.read(tok->varId(), tok); useFunctionArgs(tok->next()->astOperand2(), variables); } else if (Token::Match(tok, "std :: ref ( %var% )")) { variables.eraseAll(tok->tokAt(4)->varId()); } else if (Token::Match(tok->previous(), "[{,] %var% [,}]")) { variables.read(tok->varId(), tok); } else if (tok->varId() && Token::Match(tok, "%var% .")) { variables.use(tok->varId(), tok); // use = read + write } else if (tok->str() == ":" && (!tok->valueType() || tok->valueType()->pointer)) { if (tok->astOperand1()) variables.use(tok->astOperand1()->varId(), tok->astOperand1()); if (tok->astOperand2()) variables.use(tok->astOperand2()->varId(), tok->astOperand2()); } else if (tok->isExtendedOp() && tok->next() && tok->next()->varId() && tok->strAt(2) != "=" && !isVarDecl(tok->next())) { variables.readAll(tok->next()->varId(), tok); } else if (tok->varId() && !isVarDecl(tok) && tok->next() && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) { if (Token::Match(tok->tokAt(-2), "%name% ( %var% [,)]") && !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) variables.use(tok->varId(), tok); else variables.readAll(tok->varId(), tok); } else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) { variables.readAll(tok->varId(), tok); } // ++|-- else if (tok->next() && tok->next()->tokType() == Token::eIncDecOp && tok->next()->astOperand1() && tok->next()->astOperand1()->varId()) { if (tok->next()->astParent()) variables.use(tok->next()->astOperand1()->varId(), tok); else variables.modified(tok->next()->astOperand1()->varId(), tok); } else if (tok->isAssignmentOp()) { for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId()) { if (tok2->strAt(1) == "=") variables.write(tok2->varId(), tok); else if (tok2->next() && tok2->next()->isAssignmentOp()) variables.use(tok2->varId(), tok); else variables.read(tok2->varId(), tok); } } } else if (tok->variable() && tok->variable()->isClass() && tok->variable()->type() && (tok->variable()->type()->needInitialization == Type::NeedInitialization::False) && tok->next()->str() == ";") { variables.write(tok->varId(), tok); } } } void CheckUnusedVar::checkFunctionVariableUsage() { if (!mSettings->severity.isEnabled(Severity::style)) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // only check functions for (const Scope * scope : symbolDatabase->functionScopes) { // Bailout when there are lambdas or inline functions // TODO: Handle lambdas and inline functions properly if (scope->hasInlineOrLambdaFunction()) continue; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (findLambdaEndToken(tok)) // todo: handle lambdas break; if (Token::simpleMatch(tok, "try {")) // todo: check try blocks tok = tok->linkAt(1); const Token *varDecl = nullptr; if (tok->variable() && tok->variable()->nameToken() == tok) { const Token * eq = tok->next(); while (Token::simpleMatch(eq, "[")) eq = eq->link()->next(); if (Token::simpleMatch(eq, "=")) { varDecl = tok; tok = eq; } } // not assignment/initialization/increment => continue const bool isAssignment = tok->isAssignmentOp() && tok->astOperand1(); const bool isInitialization = (Token::Match(tok, "%var% (|{") && tok->variable() && tok->variable()->nameToken() == tok); const bool isIncrementOrDecrement = (tok->tokType() == Token::Type::eIncDecOp); if (!isAssignment && !isInitialization && !isIncrementOrDecrement) continue; if (isInitialization && Token::Match(tok, "%var% { }")) // don't warn for trivial initialization continue; if (isIncrementOrDecrement && tok->astParent() && precedes(tok, tok->astOperand1())) continue; if (tok->str() == "=" && isRaiiClass(tok->valueType(), mTokenizer->isCPP(), false)) continue; if (tok->isName()) { if (isRaiiClass(tok->valueType(), mTokenizer->isCPP(), false)) continue; tok = tok->next(); } if (tok->astParent() && tok->str() != "(") { const Token *parent = tok->astParent(); while (Token::Match(parent, "%oror%|%comp%|!|&&")) parent = parent->astParent(); if (!parent) continue; if (!Token::simpleMatch(parent->previous(), "if (")) continue; } // Do not warn about assignment with NULL if (isNullOperand(tok->astOperand2())) continue; 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(); const Variable *op1Var = op1tok ? op1tok->variable() : nullptr; if (!op1Var && Token::Match(tok, "(|{") && tok->previous() && tok->previous()->variable()) op1Var = tok->previous()->variable(); std::string bailoutTypeName; if (op1Var) { if (op1Var->isReference() && op1Var->nameToken() != tok->astOperand1()) // todo: check references continue; if (op1Var->isStatic()) // todo: check static variables continue; if (op1Var->nameToken()->isAttributeUnused()) continue; // Avoid FP for union.. if (op1Var->type() && op1Var->type()->isUnionType()) continue; // Bailout for unknown template classes, we have no idea what side effects such assignments have if (mTokenizer->isCPP() && op1Var->isClass() && (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) { // Check in the library if we should bailout or not.. const std::string typeName = op1Var->getTypeName(); switch (mSettings->library.getTypeCheck("unusedvar", typeName)) { case Library::TypeCheck::def: bailoutTypeName = typeName; break; case Library::TypeCheck::check: break; case Library::TypeCheck::suppress: continue; } } } // Is there a redundant assignment? const Token *start = tok->findExpressionStartEndTokens().second->next(); const Token *expr = varDecl ? varDecl : tok->astOperand1(); if (isInitialization) expr = tok->previous(); // Is variable in lhs a union member? if (tok->previous() && tok->previous()->variable() && tok->previous()->variable()->nameToken()->scope()->type == Scope::eUnion) continue; FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library); if (fwdAnalysis.unusedValue(expr, start, scope->bodyEnd)) { if (!bailoutTypeName.empty() && bailoutTypeName != "auto") { if (mSettings->checkLibrary && mSettings->severity.isEnabled(Severity::information)) { reportError(tok, Severity::information, "checkLibraryCheckType", "--check-library: Provide configuration for " + bailoutTypeName); } continue; } // warn if (!expr->variable() || !expr->variable()->isMaybeUnused()) unreadVariableError(tok, expr->expressionString(), false); } } // varId, usage {read, write, modified} Variables variables; checkFunctionVariableUsage_iterateScopes(scope, variables); // Check usage of all variables in the current scope.. for (std::map::const_iterator it = variables.varUsage().begin(); it != variables.varUsage().end(); ++it) { const Variables::VariableUsage &usage = it->second; // variable has been marked as unused so ignore it if (usage._var->nameToken()->isAttributeUnused() || usage._var->nameToken()->isAttributeUsed()) continue; // skip things that are only partially implemented to prevent false positives if (usage.mType == Variables::pointerPointer || usage.mType == Variables::pointerArray || usage.mType == Variables::referenceArray) continue; const std::string &varname = usage._var->name(); const Variable* var = symbolDatabase->getVariableFromVarId(it->first); // variable has had memory allocated for it, but hasn't done // anything with that memory other than, perhaps, freeing it if (usage.unused() && !usage._modified && usage._allocateMemory) allocatedButUnusedVariableError(usage._lastAccess, varname); // variable has not been written, read, or modified else if (usage.unused() && !usage._modified) { if (!usage._var->isMaybeUnused()) { unusedVariableError(usage._var->nameToken(), varname); } } // variable has not been written but has been modified else if (usage._modified && !usage._write && !usage._allocateMemory && var && !var->isStlType()) unassignedVariableError(usage._var->nameToken(), varname); // variable has been read but not written else if (!usage._write && !usage._allocateMemory && var && !var->isStlType() && !isEmptyType(var->type())) unassignedVariableError(usage._var->nameToken(), varname); } } } void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedVariable", "$symbol:" + varname + "\nUnused variable: $symbol", CWE563, Certainty::normal); } void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedAllocatedMemory", "$symbol:" + varname + "\nVariable '$symbol' is allocated memory that is never used.", CWE563, Certainty::normal); } void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified) { if (modified) reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is modified but its new value is never used.", CWE563, Certainty::normal); else reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is assigned a value that is never used.", CWE563, Certainty::normal); } void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unassignedVariable", "$symbol:" + varname + "\nVariable '$symbol' is not assigned a value.", CWE665, Certainty::normal); } //--------------------------------------------------------------------------- // Check that all struct members are used //--------------------------------------------------------------------------- void CheckUnusedVar::checkStructMemberUsage() { if (!mSettings->severity.isEnabled(Severity::style)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eStruct && scope.type != Scope::eUnion) continue; if (scope.bodyStart->fileIndex() != 0 || scope.className.empty()) continue; // Packed struct => possibly used by lowlevel code. Struct members might be required by hardware. if (scope.bodyEnd->isAttributePacked()) continue; if (const Preprocessor *preprocessor = mTokenizer->getPreprocessor()) { bool isPacked = false; for (const Directive &d: preprocessor->getDirectives()) { if (d.str == "#pragma pack(1)" && d.file == mTokenizer->list.getFiles().front() && d.linenr < scope.bodyStart->linenr()) { isPacked=true; break; } } if (isPacked) continue; } // Bail out if struct/union contains any functions if (!scope.functionList.empty()) continue; // Bail out for template struct, members might be used in non-matching instantiations if (scope.className.find("<") != std::string::npos) continue; // bail out if struct is inherited bool bailout = false; for (const Scope &derivedScope : symbolDatabase->scopeList) { if (derivedScope.definedType) { for (const Type::BaseInfo &derivedFrom : derivedScope.definedType->derivedFrom) { if (derivedFrom.type == scope.definedType) { bailout = true; break; } } } } if (bailout) continue; // bail out for extern/global struct for (const Variable* var : symbolDatabase->variableList()) { if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope.className) { bailout = true; break; } if (var && (var->typeStartToken()->str() == scope.className || var->typeEndToken()->str() == scope.className)) { const std::string addressPattern("!!" + scope.className + " & " + var->name()); // cast from struct const Token* addrTok = scope.bodyEnd; do { addrTok = Token::findmatch(addrTok, addressPattern.c_str()); if ((addrTok && addrTok->str() == ")" && addrTok->link()->isCast()) || isCPPCast(addrTok)) { bailout = true; break; } if (addrTok) addrTok = addrTok->next(); } while (addrTok); } } if (bailout) continue; // Bail out if some data is casted to struct.. const std::string castPattern("( struct| " + scope.className + " * ) & %name% ["); if (Token::findmatch(scope.bodyEnd, castPattern.c_str())) continue; // (struct S){..} const std::string initPattern("( struct| " + scope.className + " ) {"); if (Token::findmatch(scope.bodyEnd, initPattern.c_str())) continue; // Bail out if struct is used in sizeof.. for (const Token *tok = scope.bodyEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) { tok = tok->tokAt(2); if (Token::Match(tok, ("struct| " + scope.className).c_str())) { bailout = true; break; } } if (bailout) continue; // Try to prevent false positives when struct members are not used directly. if (Token::findmatch(scope.bodyEnd, (scope.className + " %type%| *").c_str())) continue; for (const Variable &var : scope.varlist) { // declaring a POD member variable? if (!var.typeStartToken()->isStandardType() && !var.isPointer()) continue; // Check if the struct member variable is used anywhere in the file bool use = false; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->variable() != &var) continue; if (tok != var.nameToken()) { use = true; break; } } if (!use) unusedStructMemberError(var.nameToken(), scope.className, var.name(), scope.type == Scope::eUnion); } } } void CheckUnusedVar::unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, bool isUnion) { const std::string prefix = isUnion ? "union member " : "struct member "; reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + "'$symbol' is never used.", CWE563, Certainty::normal); } bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type) { // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check constructors for side effects */ const std::pair::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert( std::pair(type,false)); //Initialize with side effects for possible recursions bool & withoutSideEffects = found.first->second; if (!found.second) return withoutSideEffects; // unknown types are assumed to have side effects if (!type || !type->classScope) return (withoutSideEffects = false); // Non-empty constructors => possible side effects for (const Function& f : type->classScope->functionList) { if (!f.isConstructor()) continue; if (f.argDef && Token::simpleMatch(f.argDef->link(), ") =")) continue; // ignore default/deleted constructors const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }")); Token* nextToken = f.argDef->link(); if (Token::simpleMatch(nextToken, ") :")) { // validating initialization list nextToken = nextToken->next(); // goto ":" for (const Token *initListToken = nextToken; Token::Match(initListToken, "[:,] %var% [({]"); initListToken = initListToken->linkAt(2)->next()) { const Token* varToken = initListToken->next(); const Variable* variable = varToken->variable(); if (variable && !isVariableWithoutSideEffects(*variable)) { return withoutSideEffects = false; } const Token* valueEnd = initListToken->linkAt(2); for (const Token* valueToken = initListToken->tokAt(3); valueToken != valueEnd; valueToken = valueToken->next()) { const Variable* initValueVar = valueToken->variable(); if (initValueVar && !isVariableWithoutSideEffects(*initValueVar)) { return withoutSideEffects = false; } if ((valueToken->tokType() == Token::Type::eName) || (valueToken->tokType() == Token::Type::eLambda) || (valueToken->tokType() == Token::Type::eOther)) { return withoutSideEffects = false; } const Function* initValueFunc = valueToken->function(); if (initValueFunc && !isFunctionWithoutSideEffects(*initValueFunc, valueToken, std::list {})) { return withoutSideEffects = false; } } } } if (!emptyBody) return (withoutSideEffects = false); } // Derived from type that has side effects? for (const Type::BaseInfo& derivedFrom : type->derivedFrom) { if (!isRecordTypeWithoutSideEffects(derivedFrom.type)) return (withoutSideEffects = false); } // Is there a member variable with possible side effects for (const Variable& var : type->classScope->varlist) { withoutSideEffects = isVariableWithoutSideEffects(var); if (!withoutSideEffects) { return withoutSideEffects; } } return (withoutSideEffects = true); } bool CheckUnusedVar::isVariableWithoutSideEffects(const Variable& var) { if (var.isPointer()) return true; const Type* variableType = var.type(); if (variableType) { if (!isRecordTypeWithoutSideEffects(variableType)) return false; } else { if (WRONG_DATA(!var.valueType(), var.typeStartToken())) return false; ValueType::Type valueType = var.valueType()->type; if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD)) return false; } return true; } bool CheckUnusedVar::isEmptyType(const Type* type) { // a type that has no variables and no constructor const std::pair::iterator,bool> found=mIsEmptyTypeMap.insert( std::pair(type,false)); bool & emptyType=found.first->second; if (!found.second) return emptyType; if (type && type->classScope && type->classScope->numConstructors == 0 && (type->classScope->varlist.empty())) { for (std::vector::const_iterator i = type->derivedFrom.begin(); i != type->derivedFrom.end(); ++i) { if (!isEmptyType(i->type)) { emptyType=false; return emptyType; } } emptyType=true; return emptyType; } emptyType=false; // unknown types are assumed to be nonempty return emptyType; } bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, std::list checkedFuncs) { // no body to analyze if (!func.hasBody()) { return false; } for (const Token* argsToken = functionUsageToken->next(); !Token::simpleMatch(argsToken, ")"); argsToken = argsToken->next()) { const Variable* argVar = argsToken->variable(); if (argVar && argVar->isGlobal()) { return false; // TODO: analyze global variable usage } } bool sideEffectReturnFound = false; std::set pointersToGlobals; for (Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd; bodyToken = bodyToken->next()) { // check variable inside function body const Variable* bodyVariable = bodyToken->variable(); if (bodyVariable) { if (!isVariableWithoutSideEffects(*bodyVariable)) { return false; } // check if global variable is changed if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end())) { const int depth = 20; if (isVariableChanged(bodyToken, depth, mSettings, mTokenizer->isCPP())) { return false; } // check if pointer to global variable assigned to another variable (another_var = &global_var) if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) { const Token* assigned_var_token = bodyToken->tokAt(-3); if (assigned_var_token && assigned_var_token->variable()) { pointersToGlobals.insert(assigned_var_token->variable()); } } } } // check nested function const Function* bodyFunction = bodyToken->function(); if (bodyFunction) { if (std::find(checkedFuncs.begin(), checkedFuncs.end(), bodyFunction) != checkedFuncs.end()) { // recursion found continue; } checkedFuncs.push_back(bodyFunction); if (!isFunctionWithoutSideEffects(*bodyFunction, bodyToken, checkedFuncs)) { return false; } } // check returned value if (Token::simpleMatch(bodyToken, "return")) { const Token* returnValueToken = bodyToken->next(); // TODO: handle complex return expressions if (!Token::simpleMatch(returnValueToken->next(), ";")) { sideEffectReturnFound = true; continue; } // simple one-token return const Variable* returnVariable = returnValueToken->variable(); if (returnValueToken->isLiteral() || (returnVariable && isVariableWithoutSideEffects(*returnVariable))) { continue; } sideEffectReturnFound = true; } // unknown name if (bodyToken->isNameOnly()) { return false; } } return !sideEffectReturnFound; } cppcheck-2.7/lib/checkunusedvar.h000066400000000000000000000102561417746362400171150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkunusedvarH #define checkunusedvarH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include #include #include class ErrorLogger; class Scope; class Settings; class Token; class Tokenizer; class Type; class Variables; class Variable; class Function; /// @addtogroup Checks /// @{ /** @brief Various small checks */ class CPPCHECKLIB CheckUnusedVar : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckUnusedVar() : Check(myName()) {} /** @brief This constructor is used when running checks. */ CheckUnusedVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckUnusedVar checkUnusedVar(tokenizer, settings, errorLogger); // Coding style checks checkUnusedVar.checkStructMemberUsage(); checkUnusedVar.checkFunctionVariableUsage(); } /** @brief %Check for unused function variables */ void checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables); void checkFunctionVariableUsage(); /** @brief %Check that all struct members are used */ void checkStructMemberUsage(); private: bool isRecordTypeWithoutSideEffects(const Type* type); bool isVariableWithoutSideEffects(const Variable& var); bool isEmptyType(const Type* type); bool isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, std::list checkedFuncs); // Error messages.. void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, bool isUnion = false); void unusedVariableError(const Token *tok, const std::string &varname); void allocatedButUnusedVariableError(const Token *tok, const std::string &varname); void unreadVariableError(const Token *tok, const std::string &varname, bool modified); void unassignedVariableError(const Token *tok, const std::string &varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckUnusedVar c(nullptr, settings, errorLogger); // style/warning c.unusedVariableError(nullptr, "varname"); c.allocatedButUnusedVariableError(nullptr, "varname"); c.unreadVariableError(nullptr, "varname", false); c.unassignedVariableError(nullptr, "varname"); c.unusedStructMemberError(nullptr, "structname", "variable"); } static std::string myName() { return "UnusedVar"; } std::string classInfo() const OVERRIDE { return "UnusedVar checks\n" // style "- unused variable\n" "- allocated but unused variable\n" "- unread variable\n" "- unassigned variable\n" "- unused struct member\n"; } std::map mIsRecordTypeWithoutSideEffectsMap; std::map mIsEmptyTypeMap; }; /// @} //--------------------------------------------------------------------------- #endif // checkunusedvarH cppcheck-2.7/lib/checkvaarg.cpp000066400000000000000000000167421417746362400165420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkvaarg.h" #include "astutils.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckVaarg instance; } //--------------------------------------------------------------------------- // Ensure that correct parameter is passed to va_start() //--------------------------------------------------------------------------- // CWE ids used: static const struct CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime static const struct CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior void CheckVaarg::va_start_argument() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); for (std::size_t i = 0; i < functions; ++i) { const Scope* scope = symbolDatabase->functionScopes[i]; const Function* function = scope->function; if (!function) continue; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->scope()->isExecutable()) tok = tok->scope()->bodyEnd; else if (Token::simpleMatch(tok, "va_start (")) { const Token* param2 = tok->tokAt(2)->nextArgument(); if (!param2) continue; const Variable* var = param2->variable(); if (var && var->isReference()) referenceAs_va_start_error(param2, var->name()); if (var && var->index() + 2 < function->argCount() && printWarnings) { std::list::const_reverse_iterator it = function->argumentList.rbegin(); ++it; wrongParameterTo_va_start_error(tok, var->name(), it->name()); } tok = tok->linkAt(1); } } } } void CheckVaarg::wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName) { reportError(tok, Severity::warning, "va_start_wrongParameter", "'" + paramIsName + "' given to va_start() is not last named argument of the function. Did you intend to pass '" + paramShouldName + "'?", CWE688, Certainty::normal); } void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string& paramName) { reportError(tok, Severity::error, "va_start_referencePassed", "Using reference '" + paramName + "' as parameter for va_start() results in undefined behaviour.", CWE758, Certainty::normal); } //--------------------------------------------------------------------------- // Detect missing va_end() if va_start() was used // Detect va_list usage after va_end() //--------------------------------------------------------------------------- void CheckVaarg::va_list_usage() { if (mSettings->clang) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || var->isPointer() || var->isReference() || var->isArray() || !var->scope() || var->typeStartToken()->str() != "va_list") continue; if (!var->isLocal() && !var->isArgument()) // Check only local variables and arguments continue; bool open = var->isArgument(); // va_list passed as argument are opened bool exitOnEndOfStatement = false; const Token* tok = var->nameToken()->next(); for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) { // Skip lambdas const Token* tok2 = findLambdaEndToken(tok); if (tok2) tok = tok2; if (Token::Match(tok, "va_start ( %varid%", var->declarationId())) { if (open) va_start_subsequentCallsError(tok, var->name()); open = true; tok = tok->linkAt(1); } else if (Token::Match(tok, "va_end ( %varid%", var->declarationId())) { if (!open) va_list_usedBeforeStartedError(tok, var->name()); open = false; tok = tok->linkAt(1); } else if (Token::simpleMatch(tok, "va_copy (")) { bool nopen = open; if (tok->linkAt(1)->previous()->varId() == var->declarationId()) { // Source if (!open) va_list_usedBeforeStartedError(tok, var->name()); } if (tok->tokAt(2)->varId() == var->declarationId()) { // Destination if (open) va_start_subsequentCallsError(tok, var->name()); nopen = true; } open = nopen; tok = tok->linkAt(1); } else if (Token::Match(tok, "throw|return")) exitOnEndOfStatement = true; else if (tok->str() == "break") { tok = findNextTokenFromBreak(tok); if (!tok) return; } else if (tok->str() == "goto" || (mTokenizer->isCPP() && tok->str() == "try")) { open = false; break; } else if (!open && tok->varId() == var->declarationId()) va_list_usedBeforeStartedError(tok, var->name()); else if (exitOnEndOfStatement && tok->str() == ";") break; } if (open && !var->isArgument()) va_end_missingError(tok, var->name()); } } void CheckVaarg::va_end_missingError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_end_missing", "va_list '" + varname + "' was opened but not closed by va_end().", CWE664, Certainty::normal); } void CheckVaarg::va_list_usedBeforeStartedError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_list_usedBeforeStarted", "va_list '" + varname + "' used before va_start() was called.", CWE664, Certainty::normal); } void CheckVaarg::va_start_subsequentCallsError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_start_subsequentCalls", "va_start() or va_copy() called subsequently on '" + varname + "' without va_end() in between.", CWE664, Certainty::normal); } cppcheck-2.7/lib/checkvaarg.h000066400000000000000000000061741417746362400162050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkvaargtH #define checkvaargtH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief Checking for misusage of variable argument lists */ class CPPCHECKLIB CheckVaarg : public Check { public: CheckVaarg() : Check(myName()) {} CheckVaarg(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) {} void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckVaarg check(tokenizer, settings, errorLogger); check.va_start_argument(); check.va_list_usage(); } void va_start_argument(); void va_list_usage(); private: void wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName); void referenceAs_va_start_error(const Token *tok, const std::string& paramName); void va_end_missingError(const Token *tok, const std::string& varname); void va_list_usedBeforeStartedError(const Token *tok, const std::string& varname); void va_start_subsequentCallsError(const Token *tok, const std::string& varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckVaarg c(nullptr, settings, errorLogger); c.wrongParameterTo_va_start_error(nullptr, "arg1", "arg2"); c.referenceAs_va_start_error(nullptr, "arg1"); c.va_end_missingError(nullptr, "vl"); c.va_list_usedBeforeStartedError(nullptr, "vl"); c.va_start_subsequentCallsError(nullptr, "vl"); } static std::string myName() { return "Vaarg"; } std::string classInfo() const OVERRIDE { return "Check for misusage of variable argument lists:\n" "- Wrong parameter passed to va_start()\n" "- Reference passed to va_start()\n" "- Missing va_end()\n" "- Using va_list before it is opened\n" "- Subsequent calls to va_start/va_copy()\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkvaargtH cppcheck-2.7/lib/clangimport.cpp000066400000000000000000001730441417746362400167620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "clangimport.h" #include "errortypes.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static const std::string AccessSpecDecl = "AccessSpecDecl"; static const std::string ArraySubscriptExpr = "ArraySubscriptExpr"; static const std::string BinaryOperator = "BinaryOperator"; static const std::string BreakStmt = "BreakStmt"; static const std::string CallExpr = "CallExpr"; static const std::string CaseStmt = "CaseStmt"; static const std::string CharacterLiteral = "CharacterLiteral"; static const std::string ClassTemplateDecl = "ClassTemplateDecl"; static const std::string ClassTemplateSpecializationDecl = "ClassTemplateSpecializationDecl"; static const std::string ConditionalOperator = "ConditionalOperator"; static const std::string ConstantExpr = "ConstantExpr"; static const std::string CompoundAssignOperator = "CompoundAssignOperator"; static const std::string CompoundStmt = "CompoundStmt"; static const std::string ContinueStmt = "ContinueStmt"; static const std::string CStyleCastExpr = "CStyleCastExpr"; static const std::string CXXBindTemporaryExpr = "CXXBindTemporaryExpr"; static const std::string CXXBoolLiteralExpr = "CXXBoolLiteralExpr"; static const std::string CXXConstructorDecl = "CXXConstructorDecl"; static const std::string CXXConstructExpr = "CXXConstructExpr"; static const std::string CXXDefaultArgExpr = "CXXDefaultArgExpr"; static const std::string CXXDeleteExpr = "CXXDeleteExpr"; static const std::string CXXDestructorDecl = "CXXDestructorDecl"; static const std::string CXXForRangeStmt = "CXXForRangeStmt"; static const std::string CXXFunctionalCastExpr = "CXXFunctionalCastExpr"; static const std::string CXXMemberCallExpr = "CXXMemberCallExpr"; static const std::string CXXMethodDecl = "CXXMethodDecl"; static const std::string CXXNewExpr = "CXXNewExpr"; static const std::string CXXNullPtrLiteralExpr = "CXXNullPtrLiteralExpr"; static const std::string CXXOperatorCallExpr = "CXXOperatorCallExpr"; static const std::string CXXRecordDecl = "CXXRecordDecl"; static const std::string CXXStaticCastExpr = "CXXStaticCastExpr"; static const std::string CXXStdInitializerListExpr = "CXXStdInitializerListExpr"; static const std::string CXXTemporaryObjectExpr = "CXXTemporaryObjectExpr"; static const std::string CXXThisExpr = "CXXThisExpr"; static const std::string CXXThrowExpr = "CXXThrowExpr"; static const std::string DeclRefExpr = "DeclRefExpr"; static const std::string DeclStmt = "DeclStmt"; static const std::string DefaultStmt = "DefaultStmt"; static const std::string DoStmt = "DoStmt"; static const std::string EnumConstantDecl = "EnumConstantDecl"; static const std::string EnumDecl = "EnumDecl"; static const std::string ExprWithCleanups = "ExprWithCleanups"; static const std::string FieldDecl = "FieldDecl"; static const std::string FloatingLiteral = "FloatingLiteral"; static const std::string ForStmt = "ForStmt"; static const std::string FunctionDecl = "FunctionDecl"; static const std::string FunctionTemplateDecl = "FunctionTemplateDecl"; static const std::string GotoStmt = "GotoStmt"; static const std::string IfStmt = "IfStmt"; static const std::string ImplicitCastExpr = "ImplicitCastExpr"; static const std::string InitListExpr = "InitListExpr"; static const std::string IntegerLiteral = "IntegerLiteral"; static const std::string LabelStmt = "LabelStmt"; static const std::string LinkageSpecDecl = "LinkageSpecDecl"; static const std::string MaterializeTemporaryExpr = "MaterializeTemporaryExpr"; static const std::string MemberExpr = "MemberExpr"; static const std::string NamespaceDecl = "NamespaceDecl"; static const std::string NullStmt = "NullStmt"; static const std::string ParenExpr = "ParenExpr"; static const std::string ParmVarDecl = "ParmVarDecl"; static const std::string RecordDecl = "RecordDecl"; static const std::string ReturnStmt = "ReturnStmt"; static const std::string StringLiteral = "StringLiteral"; static const std::string SwitchStmt = "SwitchStmt"; static const std::string TemplateArgument = "TemplateArgument"; static const std::string TypedefDecl = "TypedefDecl"; static const std::string UnaryOperator = "UnaryOperator"; static const std::string UnaryExprOrTypeTraitExpr = "UnaryExprOrTypeTraitExpr"; static const std::string VarDecl = "VarDecl"; static const std::string WhileStmt = "WhileStmt"; static std::string unquote(const std::string &s) { return (s[0] == '\'') ? s.substr(1, s.size() - 2) : s; } static std::vector splitString(const std::string &line) { std::vector ret; std::string::size_type pos1 = line.find_first_not_of(" "); while (pos1 < line.size()) { std::string::size_type pos2; if (std::strchr("*()", line[pos1])) { ret.push_back(line.substr(pos1,1)); pos1 = line.find_first_not_of(" ", pos1 + 1); continue; } if (line[pos1] == '<') pos2 = line.find(">", pos1); else if (line[pos1] == '\"') pos2 = line.find("\"", pos1+1); else if (line[pos1] == '\'') { pos2 = line.find("\'", pos1+1); if (pos2 < (int)line.size() - 3 && line.compare(pos2, 3, "\':\'", 0, 3) == 0) pos2 = line.find("\'", pos2 + 3); } else { pos2 = pos1; while (pos2 < line.size() && (line[pos2] == '_' || line[pos2] == ':' || std::isalnum((unsigned char)line[pos2]))) ++pos2; if (pos2 > pos1 && pos2 < line.size() && line[pos2] == '<' && std::isalpha(line[pos1])) { int tlevel = 1; while (++pos2 < line.size() && tlevel > 0) { if (line[pos2] == '<') ++tlevel; else if (line[pos2] == '>') --tlevel; } if (tlevel == 0 && pos2 < line.size() && line[pos2] == ' ') { ret.push_back(line.substr(pos1, pos2-pos1)); pos1 = pos2 + 1; continue; } } pos2 = line.find(" ", pos1) - 1; if ((std::isalpha(line[pos1]) || line[pos1] == '_') && line.find("::", pos1) < pos2 && line.find("::", pos1) < line.find("<", pos1)) { pos2 = line.find("::", pos1); ret.push_back(line.substr(pos1, pos2-pos1)); ret.push_back("::"); pos1 = pos2 + 2; continue; } if ((std::isalpha(line[pos1]) || line[pos1] == '_') && line.find("<", pos1) < pos2 && line.find("<<",pos1) != line.find("<",pos1) && line.find(">", pos1) != std::string::npos && line.find(">", pos1) > pos2) { int level = 0; for (pos2 = pos1; pos2 < line.size(); ++pos2) { if (line[pos2] == '<') ++level; else if (line[pos2] == '>') { if (level <= 1) break; --level; } } if (level > 1 && pos2 + 1 >= line.size()) return std::vector {}; pos2 = line.find(" ", pos2); if (pos2 != std::string::npos) --pos2; } } if (pos2 == std::string::npos) { ret.push_back(line.substr(pos1)); break; } ret.push_back(line.substr(pos1, pos2+1-pos1)); pos1 = line.find_first_not_of(" ", pos2 + 1); } return ret; } namespace clangimport { struct Data { struct Decl { explicit Decl(Scope *scope) : def(nullptr), enumerator(nullptr), function(nullptr), scope(scope), var(nullptr) {} Decl(Token *def, Variable *var) : def(def), enumerator(nullptr), function(nullptr), scope(nullptr), var(var) {} Decl(Token *def, Function *function) : def(def), enumerator(nullptr), function(function), scope(nullptr), var(nullptr) {} Decl(Token *def, Enumerator *enumerator) : def(def), enumerator(enumerator), function(nullptr), scope(nullptr), var(nullptr) {} void ref(Token *tok) { if (enumerator) tok->enumerator(enumerator); if (function) tok->function(function); if (var) { tok->variable(var); tok->varId(var->declarationId()); } } Token *def; Enumerator *enumerator; Function *function; Scope *scope; Variable *var; }; const Settings *mSettings = nullptr; SymbolDatabase *mSymbolDatabase = nullptr; int enumValue = 0; void enumDecl(const std::string &addr, Token *nameToken, Enumerator *enumerator) { Decl decl(nameToken, enumerator); mDeclMap.insert(std::pair(addr, decl)); nameToken->enumerator(enumerator); notFound(addr); } void funcDecl(const std::string &addr, Token *nameToken, Function *function) { Decl decl(nameToken, function); mDeclMap.insert(std::pair(addr, decl)); nameToken->function(function); notFound(addr); } void scopeDecl(const std::string &addr, Scope *scope) { Decl decl(scope); mDeclMap.insert(std::pair(addr, decl)); } void varDecl(const std::string &addr, Token *def, Variable *var) { Decl decl(def, var); mDeclMap.insert(std::pair(addr, decl)); def->varId(++mVarId); def->variable(var); if (def->valueType()) var->setValueType(*def->valueType()); notFound(addr); } void replaceVarDecl(const Variable *from, Variable *to) { for (auto &it: mDeclMap) { Decl &decl = it.second; if (decl.var == from) decl.var = to; } } void ref(const std::string &addr, Token *tok) { auto it = mDeclMap.find(addr); if (it != mDeclMap.end()) it->second.ref(tok); else mNotFound[addr].push_back(tok); } std::vector getVariableList() const { std::vector ret; ret.resize(mVarId + 1, nullptr); for (auto it: mDeclMap) { if (it.second.var) ret[it.second.var->declarationId()] = it.second.var; } return ret; } bool hasDecl(const std::string &addr) const { return mDeclMap.find(addr) != mDeclMap.end(); } const Scope *getScope(const std::string &addr) { auto it = mDeclMap.find(addr); return (it == mDeclMap.end() ? nullptr : it->second.scope); } // "}" tokens that are not end-of-scope std::set mNotScope; std::map scopeAccessControl; private: void notFound(const std::string &addr) { auto it = mNotFound.find(addr); if (it != mNotFound.end()) { for (Token *reftok: it->second) ref(addr, reftok); mNotFound.erase(it); } } std::map mDeclMap; std::map> mNotFound; int mVarId = 0; }; class AstNode; using AstNodePtr = std::shared_ptr; class AstNode { public: AstNode(const std::string &nodeType, const std::string &ext, Data *data) : nodeType(nodeType), mExtTokens(splitString(ext)), mData(data) {} std::string nodeType; std::vector children; void setLocations(TokenList *tokenList, int file, int line, int col); void dumpAst(int num = 0, int indent = 0) const; void createTokens1(TokenList *tokenList) { //dumpAst(); if (!tokenList->back()) setLocations(tokenList, 0, 1, 1); else setLocations(tokenList, tokenList->back()->fileIndex(), tokenList->back()->linenr(), 1); createTokens(tokenList); if (nodeType == VarDecl || nodeType == RecordDecl || nodeType == TypedefDecl) addtoken(tokenList, ";"); mData->mNotScope.clear(); } AstNodePtr getChild(int c) { if (c >= children.size()) { std::ostringstream err; err << "ClangImport: AstNodePtr::getChild(" << c << ") out of bounds. children.size=" << children.size() << " " << nodeType; for (const std::string &s: mExtTokens) err << " " << s; throw InternalError(nullptr, err.str()); } return children[c]; } private: Token *createTokens(TokenList *tokenList); Token *addtoken(TokenList *tokenList, const std::string &str, bool valueType=true); const ::Type *addTypeTokens(TokenList *tokenList, const std::string &str, const Scope *scope = nullptr); void addFullScopeNameTokens(TokenList *tokenList, const Scope *recordScope); Scope *createScope(TokenList *tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def); Scope *createScope(TokenList *tokenList, Scope::ScopeType scopeType, const std::vector &children2, const Token *def); Token *createTokensCall(TokenList *tokenList); void createTokensFunctionDecl(TokenList *tokenList); void createTokensForCXXRecord(TokenList *tokenList); Token *createTokensVarDecl(TokenList *tokenList); std::string getSpelling() const; std::string getType(int index = 0) const; std::string getFullType(int index = 0) const; bool isDefinition() const; std::string getTemplateParameters() const; const Scope *getNestedInScope(TokenList *tokenList); void setValueType(Token *tok); int mFile = 0; int mLine = 1; int mCol = 1; std::vector mExtTokens; Data *mData; }; } std::string clangimport::AstNode::getSpelling() const { if (nodeType == CompoundAssignOperator) { int typeIndex = 1; while (typeIndex < mExtTokens.size() && mExtTokens[typeIndex][0] != '\'') typeIndex++; // name is next quoted token int nameIndex = typeIndex + 1; while (nameIndex < mExtTokens.size() && mExtTokens[nameIndex][0] != '\'') nameIndex++; return (nameIndex < mExtTokens.size()) ? unquote(mExtTokens[nameIndex]) : ""; } if (nodeType == UnaryExprOrTypeTraitExpr) { int typeIndex = 1; while (typeIndex < mExtTokens.size() && mExtTokens[typeIndex][0] != '\'') typeIndex++; int nameIndex = typeIndex + 1; return (nameIndex < mExtTokens.size()) ? unquote(mExtTokens[nameIndex]) : ""; } int typeIndex = mExtTokens.size() - 1; if (nodeType == FunctionDecl || nodeType == CXXConstructorDecl || nodeType == CXXMethodDecl) { while (typeIndex >= 0 && mExtTokens[typeIndex][0] != '\'') typeIndex--; if (typeIndex <= 0) return ""; } if (nodeType == DeclRefExpr) { while (typeIndex > 0 && std::isalpha(mExtTokens[typeIndex][0])) typeIndex--; if (typeIndex <= 0) return ""; } const std::string &str = mExtTokens[typeIndex - 1]; if (str.compare(0,4,"col:") == 0) return ""; if (str.compare(0,8,"= mExtTokens.size()) return ""; std::string type = mExtTokens[typeIndex]; if (type.find("\':\'") != std::string::npos) { if (index == 0) type.erase(type.find("\':\'") + 1); else type.erase(0, type.find("\':\'") + 2); } return type; } bool clangimport::AstNode::isDefinition() const { return contains(mExtTokens, "definition"); } std::string clangimport::AstNode::getTemplateParameters() const { if (children.empty() || children[0]->nodeType != TemplateArgument) return ""; std::string templateParameters; for (AstNodePtr child: children) { if (child->nodeType == TemplateArgument) { if (templateParameters.empty()) templateParameters = "<"; else templateParameters += ","; templateParameters += unquote(child->mExtTokens.back()); } } return templateParameters + ">"; } void clangimport::AstNode::dumpAst(int num, int indent) const { (void)num; std::cout << std::string(indent, ' ') << nodeType; for (auto tok: mExtTokens) std::cout << " " << tok; std::cout << std::endl; for (int c = 0; c < children.size(); ++c) { if (children[c]) children[c]->dumpAst(c, indent + 2); else std::cout << std::string(indent + 2, ' ') << "<<<>>>>" << std::endl; } } void clangimport::AstNode::setLocations(TokenList *tokenList, int file, int line, int col) { for (const std::string &ext: mExtTokens) { if (ext.compare(0,5,"appendFileIfNew(ext.substr(1, sep1 - 1)); line = MathLib::toLongNumber(ext.substr(sep1+1, sep2-sep1)); } } mFile = file; mLine = line; mCol = col; for (auto child: children) { if (child) child->setLocations(tokenList, file, line, col); } } Token *clangimport::AstNode::addtoken(TokenList *tokenList, const std::string &str, bool valueType) { const Scope *scope = getNestedInScope(tokenList); tokenList->addtoken(str, mLine, mCol, mFile); tokenList->back()->scope(scope); if (valueType) setValueType(tokenList->back()); return tokenList->back(); } const ::Type * clangimport::AstNode::addTypeTokens(TokenList *tokenList, const std::string &str, const Scope *scope) { if (str.find("\':\'") != std::string::npos) { return addTypeTokens(tokenList, str.substr(0, str.find("\':\'") + 1), scope); } if (str.compare(0, 16, "'enum (anonymous") == 0) return nullptr; std::string type; if (str.find(" (") != std::string::npos) { if (str.find("<") != std::string::npos) type = str.substr(1, str.find("<")) + "...>"; else type = str.substr(1,str.find(" (")-1); } else type = unquote(str); if (type.find("(*)(") != std::string::npos) { type.erase(type.find("(*)(")); type += "*"; } if (type.find("(") != std::string::npos) type.erase(type.find("(")); std::stack lpar; for (const std::string &s: splitString(type)) { Token *tok = addtoken(tokenList, s, false); if (tok->str() == "(") lpar.push(tok); else if (tok->str() == ")") { Token::createMutualLinks(tok, lpar.top()); lpar.pop(); } } // Set Type if (!scope) { scope = tokenList->back() ? tokenList->back()->scope() : nullptr; if (!scope) return nullptr; } for (const Token *typeToken = tokenList->back(); Token::Match(typeToken, "&|*|%name%"); typeToken = typeToken->previous()) { if (!typeToken->isName()) continue; const ::Type *recordType = scope->check->findVariableType(scope, typeToken); if (recordType) { const_cast(typeToken)->type(recordType); return recordType; } } return nullptr; } void clangimport::AstNode::addFullScopeNameTokens(TokenList *tokenList, const Scope *recordScope) { if (!recordScope) return; std::list scopes; while (recordScope && recordScope != tokenList->back()->scope() && !recordScope->isExecutable()) { scopes.push_front(recordScope); recordScope = recordScope->nestedIn; } for (const Scope *s: scopes) { if (!s->className.empty()) { addtoken(tokenList, s->className); addtoken(tokenList, "::"); } } } const Scope *clangimport::AstNode::getNestedInScope(TokenList *tokenList) { if (!tokenList->back()) return &mData->mSymbolDatabase->scopeList.front(); if (tokenList->back()->str() == "}" && mData->mNotScope.find(tokenList->back()) == mData->mNotScope.end()) return tokenList->back()->scope()->nestedIn; return tokenList->back()->scope(); } void clangimport::AstNode::setValueType(Token *tok) { for (int i = 0; i < 2; i++) { const std::string &type = getType(i); if (type.find("<") != std::string::npos) // TODO continue; TokenList decl(nullptr); addTypeTokens(&decl, type, tok->scope()); if (!decl.front()) break; ValueType valueType = ValueType::parseDecl(decl.front(), mData->mSettings); if (valueType.type != ValueType::Type::UNKNOWN_TYPE) { tok->setValueType(new ValueType(valueType)); break; } } } Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def) { std::vector children2{astNode}; return createScope(tokenList, scopeType, children2, def); } Scope *clangimport::AstNode::createScope(TokenList *tokenList, Scope::ScopeType scopeType, const std::vector & children2, const Token *def) { SymbolDatabase *symbolDatabase = mData->mSymbolDatabase; Scope *nestedIn = const_cast(getNestedInScope(tokenList)); symbolDatabase->scopeList.push_back(Scope(nullptr, nullptr, nestedIn)); Scope *scope = &symbolDatabase->scopeList.back(); if (scopeType == Scope::ScopeType::eEnum) scope->enumeratorList.reserve(children2.size()); nestedIn->nestedList.push_back(scope); scope->type = scopeType; scope->classDef = def; scope->check = nestedIn->check; if (Token::Match(def, "if|for|while (")) { std::map replaceVar; for (const Token *vartok = def->tokAt(2); vartok; vartok = vartok->next()) { if (!vartok->variable()) continue; if (vartok->variable()->nameToken() == vartok) { const Variable *from = vartok->variable(); scope->varlist.emplace_back(*from, scope); Variable *to = &scope->varlist.back(); replaceVar[from] = to; mData->replaceVarDecl(from, to); } if (replaceVar.find(vartok->variable()) != replaceVar.end()) const_cast(vartok)->variable(replaceVar[vartok->variable()]); } std::list &varlist = const_cast(def->scope())->varlist; for (std::list::iterator var = varlist.begin(); var != varlist.end();) { if (replaceVar.find(&(*var)) != replaceVar.end()) varlist.erase(var++); else ++var; } } scope->bodyStart = addtoken(tokenList, "{"); tokenList->back()->scope(scope); mData->scopeAccessControl[scope] = scope->defaultAccess(); if (!children2.empty()) { for (AstNodePtr astNode: children2) { if (astNode->nodeType == "VisibilityAttr") continue; if (astNode->nodeType == AccessSpecDecl) { if (contains(astNode->mExtTokens, "private")) mData->scopeAccessControl[scope] = AccessControl::Private; else if (contains(astNode->mExtTokens, "protected")) mData->scopeAccessControl[scope] = AccessControl::Protected; else if (contains(astNode->mExtTokens, "public")) mData->scopeAccessControl[scope] = AccessControl::Public; continue; } astNode->createTokens(tokenList); if (scopeType == Scope::ScopeType::eEnum) astNode->addtoken(tokenList, ","); else if (!Token::Match(tokenList->back(), "[;{}]")) astNode->addtoken(tokenList, ";"); } } scope->bodyEnd = addtoken(tokenList, "}"); Token::createMutualLinks(const_cast(scope->bodyStart), const_cast(scope->bodyEnd)); mData->scopeAccessControl.erase(scope); return scope; } Token *clangimport::AstNode::createTokens(TokenList *tokenList) { if (nodeType == ArraySubscriptExpr) { Token *array = getChild(0)->createTokens(tokenList); Token *bracket1 = addtoken(tokenList, "["); Token *index = children[1]->createTokens(tokenList); Token *bracket2 = addtoken(tokenList, "]"); bracket1->astOperand1(array); bracket1->astOperand2(index); bracket1->link(bracket2); bracket2->link(bracket1); return bracket1; } if (nodeType == BinaryOperator) { Token *tok1 = getChild(0)->createTokens(tokenList); Token *binop = addtoken(tokenList, unquote(mExtTokens.back())); Token *tok2 = children[1]->createTokens(tokenList); binop->astOperand1(tok1); binop->astOperand2(tok2); return binop; } if (nodeType == BreakStmt) return addtoken(tokenList, "break"); if (nodeType == CharacterLiteral) { int c = MathLib::toLongNumber(mExtTokens.back()); if (c == 0) return addtoken(tokenList, "\'\\0\'"); if (c == '\r') return addtoken(tokenList, "\'\\r\'"); if (c == '\n') return addtoken(tokenList, "\'\\n\'"); if (c == '\t') return addtoken(tokenList, "\'\\t\'"); if (c == '\\') return addtoken(tokenList, "\'\\\\\'"); if (c < ' ' || c >= 0x80) { std::ostringstream hex; hex << std::hex << ((c>>4) & 0xf) << (c&0xf); return addtoken(tokenList, "\'\\x" + hex.str() + "\'"); } return addtoken(tokenList, std::string("\'") + char(c) + std::string("\'")); } if (nodeType == CallExpr) return createTokensCall(tokenList); if (nodeType == CaseStmt) { Token *caseToken = addtoken(tokenList, "case"); Token *exprToken = getChild(0)->createTokens(tokenList); caseToken->astOperand1(exprToken); addtoken(tokenList, ":"); children.back()->createTokens(tokenList); return nullptr; } if (nodeType == ClassTemplateDecl) { for (AstNodePtr child: children) { if (child->nodeType == ClassTemplateSpecializationDecl) child->createTokens(tokenList); } return nullptr; } if (nodeType == ClassTemplateSpecializationDecl) { createTokensForCXXRecord(tokenList); return nullptr; } if (nodeType == ConditionalOperator) { Token *expr1 = getChild(0)->createTokens(tokenList); Token *tok1 = addtoken(tokenList, "?"); Token *expr2 = children[1]->createTokens(tokenList); Token *tok2 = addtoken(tokenList, ":"); Token *expr3 = children[2]->createTokens(tokenList); tok2->astOperand1(expr2); tok2->astOperand2(expr3); tok1->astOperand1(expr1); tok1->astOperand2(tok2); return tok1; } if (nodeType == CompoundAssignOperator) { Token *lhs = getChild(0)->createTokens(tokenList); Token *assign = addtoken(tokenList, getSpelling()); Token *rhs = children[1]->createTokens(tokenList); assign->astOperand1(lhs); assign->astOperand2(rhs); return assign; } if (nodeType == CompoundStmt) { for (AstNodePtr child: children) { child->createTokens(tokenList); if (!Token::Match(tokenList->back(), "[;{}]")) child->addtoken(tokenList, ";"); } return nullptr; } if (nodeType == ConstantExpr) return children.back()->createTokens(tokenList); if (nodeType == ContinueStmt) return addtoken(tokenList, "continue"); if (nodeType == CStyleCastExpr) { Token *par1 = addtoken(tokenList, "("); addTypeTokens(tokenList, '\'' + getType() + '\''); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); par1->astOperand1(getChild(0)->createTokens(tokenList)); return par1; } if (nodeType == CXXBindTemporaryExpr) return getChild(0)->createTokens(tokenList); if (nodeType == CXXBoolLiteralExpr) { addtoken(tokenList, mExtTokens.back()); tokenList->back()->setValueType(new ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0)); return tokenList->back(); } if (nodeType == CXXConstructExpr) { if (!children.empty()) return getChild(0)->createTokens(tokenList); addTypeTokens(tokenList, '\'' + getType() + '\''); Token *type = tokenList->back(); Token *par1 = addtoken(tokenList, "("); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); par1->astOperand1(type); return par1; } if (nodeType == CXXConstructorDecl) { createTokensFunctionDecl(tokenList); return nullptr; } if (nodeType == CXXDeleteExpr) { addtoken(tokenList, "delete"); getChild(0)->createTokens(tokenList); return nullptr; } if (nodeType == CXXDestructorDecl) { createTokensFunctionDecl(tokenList); return nullptr; } if (nodeType == CXXForRangeStmt) { Token *forToken = addtoken(tokenList, "for"); Token *par1 = addtoken(tokenList, "("); AstNodePtr varDecl; if (children[6]->nodeType == DeclStmt) varDecl = getChild(6)->getChild(0); else varDecl = getChild(5)->getChild(0); varDecl->mExtTokens.pop_back(); varDecl->children.clear(); Token *expr1 = varDecl->createTokens(tokenList); Token *colon = addtoken(tokenList, ":"); AstNodePtr range; for (int i = 0; i < 2; i++) { if (children[i] && children[i]->nodeType == DeclStmt && children[i]->getChild(0)->nodeType == VarDecl) { range = children[i]->getChild(0)->getChild(0); break; } } if (!range) throw InternalError(tokenList->back(), "Failed to import CXXForRangeStmt. Range?"); Token *expr2 = range->createTokens(tokenList); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); colon->astOperand1(expr1); colon->astOperand2(expr2); par1->astOperand1(forToken); par1->astOperand2(colon); createScope(tokenList, Scope::ScopeType::eFor, children.back(), forToken); return nullptr; } if (nodeType == CXXMethodDecl) { for (int i = 0; i+1 < mExtTokens.size(); ++i) { if (mExtTokens[i] == "prev" && !mData->hasDecl(mExtTokens[i+1])) return nullptr; } createTokensFunctionDecl(tokenList); return nullptr; } if (nodeType == CXXMemberCallExpr) return createTokensCall(tokenList); if (nodeType == CXXNewExpr) { Token *newtok = addtoken(tokenList, "new"); if (children.size() == 1 && getChild(0)->nodeType == CXXConstructExpr) { newtok->astOperand1(getChild(0)->createTokens(tokenList)); return newtok; } std::string type = getType(); if (type.find("*") != std::string::npos) type = type.erase(type.rfind("*")); addTypeTokens(tokenList, type); if (!children.empty()) { Token *bracket1 = addtoken(tokenList, "["); getChild(0)->createTokens(tokenList); Token *bracket2 = addtoken(tokenList, "]"); bracket1->link(bracket2); bracket2->link(bracket1); } return newtok; } if (nodeType == CXXNullPtrLiteralExpr) return addtoken(tokenList, "nullptr"); if (nodeType == CXXOperatorCallExpr) return createTokensCall(tokenList); if (nodeType == CXXRecordDecl) { createTokensForCXXRecord(tokenList); return nullptr; } if (nodeType == CXXStaticCastExpr || nodeType == CXXFunctionalCastExpr) { Token *cast = addtoken(tokenList, getSpelling()); Token *par1 = addtoken(tokenList, "("); Token *expr = getChild(0)->createTokens(tokenList); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); par1->astOperand1(cast); par1->astOperand2(expr); setValueType(par1); return par1; } if (nodeType == CXXStdInitializerListExpr) return getChild(0)->createTokens(tokenList); if (nodeType == CXXTemporaryObjectExpr && !children.empty()) return getChild(0)->createTokens(tokenList); if (nodeType == CXXThisExpr) return addtoken(tokenList, "this"); if (nodeType == CXXThrowExpr) { Token *t = addtoken(tokenList, "throw"); t->astOperand1(getChild(0)->createTokens(tokenList)); return t; } if (nodeType == DeclRefExpr) { int addrIndex = mExtTokens.size() - 1; while (addrIndex > 1 && mExtTokens[addrIndex].compare(0,2,"0x") != 0) --addrIndex; const std::string addr = mExtTokens[addrIndex]; std::string name = unquote(getSpelling()); Token *reftok = addtoken(tokenList, name.empty() ? "" : name); mData->ref(addr, reftok); return reftok; } if (nodeType == DeclStmt) return getChild(0)->createTokens(tokenList); if (nodeType == DefaultStmt) { addtoken(tokenList, "default"); addtoken(tokenList, ":"); children.back()->createTokens(tokenList); return nullptr; } if (nodeType == DoStmt) { addtoken(tokenList, "do"); createScope(tokenList, Scope::ScopeType::eDo, getChild(0), tokenList->back()); Token *tok1 = addtoken(tokenList, "while"); Token *par1 = addtoken(tokenList, "("); Token *expr = children[1]->createTokens(tokenList); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); par1->astOperand1(tok1); par1->astOperand2(expr); return nullptr; } if (nodeType == EnumConstantDecl) { Token *nameToken = addtoken(tokenList, getSpelling()); Scope *scope = const_cast(nameToken->scope()); scope->enumeratorList.push_back(Enumerator(nameToken->scope())); Enumerator *e = &scope->enumeratorList.back(); e->name = nameToken; e->value = mData->enumValue++; e->value_known = true; mData->enumDecl(mExtTokens.front(), nameToken, e); return nameToken; } if (nodeType == EnumDecl) { int colIndex = mExtTokens.size() - 1; while (colIndex > 0 && mExtTokens[colIndex].compare(0,4,"col:") != 0 && mExtTokens[colIndex].compare(0,5,"line:") != 0) --colIndex; if (colIndex == 0) return nullptr; mData->enumValue = 0; Token *enumtok = addtoken(tokenList, "enum"); Token *nametok = nullptr; { int nameIndex = mExtTokens.size() - 1; while (nameIndex > colIndex && mExtTokens[nameIndex][0] == '\'') --nameIndex; if (nameIndex > colIndex) nametok = addtoken(tokenList, mExtTokens[nameIndex]); if (mExtTokens.back()[0] == '\'') { addtoken(tokenList, ":"); addTypeTokens(tokenList, mExtTokens.back()); } } Scope *enumscope = createScope(tokenList, Scope::ScopeType::eEnum, children, enumtok); if (nametok) enumscope->className = nametok->str(); if (enumscope->bodyEnd && Token::simpleMatch(enumscope->bodyEnd->previous(), ", }")) const_cast(enumscope->bodyEnd)->deletePrevious(); // Create enum type mData->mSymbolDatabase->typeList.push_back(Type(enumtok, enumscope, enumtok->scope())); enumscope->definedType = &mData->mSymbolDatabase->typeList.back(); if (nametok) const_cast(enumtok->scope())->definedTypesMap[nametok->str()] = enumscope->definedType; return nullptr; } if (nodeType == ExprWithCleanups) return getChild(0)->createTokens(tokenList); if (nodeType == FieldDecl) return createTokensVarDecl(tokenList); if (nodeType == FloatingLiteral) return addtoken(tokenList, mExtTokens.back()); if (nodeType == ForStmt) { Token *forToken = addtoken(tokenList, "for"); Token *par1 = addtoken(tokenList, "("); Token *expr1 = getChild(0) ? children[0]->createTokens(tokenList) : nullptr; Token *sep1 = addtoken(tokenList, ";"); Token *expr2 = children[2] ? children[2]->createTokens(tokenList) : nullptr; Token *sep2 = addtoken(tokenList, ";"); Token *expr3 = children[3] ? children[3]->createTokens(tokenList) : nullptr; Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); par1->astOperand1(forToken); par1->astOperand2(sep1); sep1->astOperand1(expr1); sep1->astOperand2(sep2); sep2->astOperand1(expr2); sep2->astOperand2(expr3); createScope(tokenList, Scope::ScopeType::eFor, children[4], forToken); return nullptr; } if (nodeType == FunctionDecl) { createTokensFunctionDecl(tokenList); return nullptr; } if (nodeType == FunctionTemplateDecl) { bool first = true; for (AstNodePtr child: children) { if (child->nodeType == FunctionDecl) { if (!first) child->createTokens(tokenList); first = false; } } return nullptr; } if (nodeType == GotoStmt) { addtoken(tokenList, "goto"); addtoken(tokenList, unquote(mExtTokens[mExtTokens.size() - 2])); addtoken(tokenList, ";"); return nullptr; } if (nodeType == IfStmt) { AstNodePtr cond; AstNodePtr thenCode; AstNodePtr elseCode; if (children.size() == 2) { cond = children[children.size() - 2]; thenCode = children[children.size() - 1]; } else { cond = children[children.size() - 3]; thenCode = children[children.size() - 2]; elseCode = children[children.size() - 1]; } Token *iftok = addtoken(tokenList, "if"); Token *par1 = addtoken(tokenList, "("); par1->astOperand1(iftok); par1->astOperand2(cond->createTokens(tokenList)); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); createScope(tokenList, Scope::ScopeType::eIf, thenCode, iftok); if (elseCode) { elseCode->addtoken(tokenList, "else"); createScope(tokenList, Scope::ScopeType::eElse, elseCode, tokenList->back()); } return nullptr; } if (nodeType == ImplicitCastExpr) { Token *expr = getChild(0)->createTokens(tokenList); if (!expr->valueType() || contains(mExtTokens, "")) setValueType(expr); return expr; } if (nodeType == InitListExpr) { const Scope *scope = tokenList->back()->scope(); Token *start = addtoken(tokenList, "{"); start->scope(scope); for (AstNodePtr child: children) { if (tokenList->back()->str() != "{") addtoken(tokenList, ","); child->createTokens(tokenList); } Token *end = addtoken(tokenList, "}"); end->scope(scope); start->link(end); end->link(start); mData->mNotScope.insert(end); return start; } if (nodeType == IntegerLiteral) return addtoken(tokenList, mExtTokens.back()); if (nodeType == LabelStmt) { addtoken(tokenList, unquote(mExtTokens.back())); addtoken(tokenList, ":"); for (auto child: children) child->createTokens(tokenList); return nullptr; } if (nodeType == LinkageSpecDecl) return nullptr; if (nodeType == MaterializeTemporaryExpr) return getChild(0)->createTokens(tokenList); if (nodeType == MemberExpr) { Token *s = getChild(0)->createTokens(tokenList); Token *dot = addtoken(tokenList, "."); std::string memberName = getSpelling(); if (memberName.compare(0, 2, "->") == 0) { dot->originalName("->"); memberName = memberName.substr(2); } else if (memberName.compare(0, 1, ".") == 0) { memberName = memberName.substr(1); } if (memberName.empty()) memberName = ""; Token *member = addtoken(tokenList, memberName); mData->ref(mExtTokens.back(), member); dot->astOperand1(s); dot->astOperand2(member); return dot; } if (nodeType == NamespaceDecl) { if (children.empty()) return nullptr; Token *defToken = addtoken(tokenList, "namespace"); const std::string &s = mExtTokens[mExtTokens.size() - 2]; Token *nameToken = (s.compare(0,4,"col:")==0 || s.compare(0,5,"line:")==0) ? addtoken(tokenList, mExtTokens.back()) : nullptr; Scope *scope = createScope(tokenList, Scope::ScopeType::eNamespace, children, defToken); if (nameToken) scope->className = nameToken->str(); return nullptr; } if (nodeType == NullStmt) return addtoken(tokenList, ";"); if (nodeType == ParenExpr) { Token *par1 = addtoken(tokenList, "("); Token *expr = getChild(0)->createTokens(tokenList); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); return expr; } if (nodeType == RecordDecl) { const Token *classDef = addtoken(tokenList, "struct"); const std::string &recordName = getSpelling(); if (!recordName.empty()) addtoken(tokenList, getSpelling()); if (!isDefinition()) { addtoken(tokenList, ";"); return nullptr; } Scope *recordScope = createScope(tokenList, Scope::ScopeType::eStruct, children, classDef); mData->mSymbolDatabase->typeList.push_back(Type(classDef, recordScope, classDef->scope())); recordScope->definedType = &mData->mSymbolDatabase->typeList.back(); if (!recordName.empty()) { recordScope->className = recordName; const_cast(classDef->scope())->definedTypesMap[recordName] = recordScope->definedType; } return nullptr; } if (nodeType == ReturnStmt) { Token *tok1 = addtoken(tokenList, "return"); if (!children.empty()) { getChild(0)->setValueType(tok1); tok1->astOperand1(getChild(0)->createTokens(tokenList)); } return tok1; } if (nodeType == StringLiteral) return addtoken(tokenList, mExtTokens.back()); if (nodeType == SwitchStmt) { Token *tok1 = addtoken(tokenList, "switch"); Token *par1 = addtoken(tokenList, "("); Token *expr = children[children.size() - 2]->createTokens(tokenList); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); par1->astOperand1(tok1); par1->astOperand2(expr); createScope(tokenList, Scope::ScopeType::eSwitch, children.back(), tok1); return nullptr; } if (nodeType == TypedefDecl) { addtoken(tokenList, "typedef"); addTypeTokens(tokenList, getType()); return addtoken(tokenList, getSpelling()); } if (nodeType == UnaryOperator) { int index = (int)mExtTokens.size() - 1; while (index > 0 && mExtTokens[index][0] != '\'') --index; Token *unop = addtoken(tokenList, unquote(mExtTokens[index])); unop->astOperand1(getChild(0)->createTokens(tokenList)); return unop; } if (nodeType == UnaryExprOrTypeTraitExpr) { Token *tok1 = addtoken(tokenList, getSpelling()); Token *par1 = addtoken(tokenList, "("); if (children.empty()) addTypeTokens(tokenList, mExtTokens.back()); else { AstNodePtr child = getChild(0); if (child && child->nodeType == ParenExpr) child = child->getChild(0); Token *expr = child->createTokens(tokenList); child->setValueType(expr); par1->astOperand2(expr); } Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); par1->astOperand1(tok1); par1->astOperand2(par1->next()); setValueType(par1); return par1; } if (nodeType == VarDecl) return createTokensVarDecl(tokenList); if (nodeType == WhileStmt) { AstNodePtr cond = children[children.size() - 2]; AstNodePtr body = children.back(); Token *whiletok = addtoken(tokenList, "while"); Token *par1 = addtoken(tokenList, "("); par1->astOperand1(whiletok); par1->astOperand2(cond->createTokens(tokenList)); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); createScope(tokenList, Scope::ScopeType::eWhile, body, whiletok); return nullptr; } return addtoken(tokenList, "?" + nodeType + "?"); } Token * clangimport::AstNode::createTokensCall(TokenList *tokenList) { int firstParam; Token *f; if (nodeType == CXXOperatorCallExpr) { firstParam = 2; Token *obj = getChild(1)->createTokens(tokenList); Token *dot = addtoken(tokenList, "."); Token *op = getChild(0)->createTokens(tokenList); dot->astOperand1(obj); dot->astOperand2(op); f = dot; } else { firstParam = 1; f = getChild(0)->createTokens(tokenList); } f->setValueType(nullptr); Token *par1 = addtoken(tokenList, "("); par1->astOperand1(f); int args = 0; while (args < children.size() && children[args]->nodeType != CXXDefaultArgExpr) args++; Token *child = nullptr; for (int c = firstParam; c < args; ++c) { if (child) { Token *comma = addtoken(tokenList, ","); comma->setValueType(nullptr); comma->astOperand1(child); comma->astOperand2(children[c]->createTokens(tokenList)); child = comma; } else { child = children[c]->createTokens(tokenList); } } par1->astOperand2(child); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); return par1; } void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList) { const bool prev = contains(mExtTokens, "prev"); const bool hasBody = !children.empty() && children.back()->nodeType == CompoundStmt; const bool isStatic = contains(mExtTokens, "static"); const bool isInline = contains(mExtTokens, "inline"); const Token *startToken = nullptr; SymbolDatabase *symbolDatabase = mData->mSymbolDatabase; if (nodeType != CXXConstructorDecl && nodeType != CXXDestructorDecl) { if (isStatic) addtoken(tokenList, "static"); if (isInline) addtoken(tokenList, "inline"); const Token * const before = tokenList->back(); addTypeTokens(tokenList, '\'' + getType() + '\''); startToken = before ? before->next() : tokenList->front(); } if (mExtTokens.size() > 4 && mExtTokens[1] == "parent") addFullScopeNameTokens(tokenList, mData->getScope(mExtTokens[2])); Token *nameToken = addtoken(tokenList, getSpelling() + getTemplateParameters()); Scope *nestedIn = const_cast(nameToken->scope()); if (prev) { const std::string addr = *(std::find(mExtTokens.begin(), mExtTokens.end(), "prev") + 1); mData->ref(addr, nameToken); } if (!nameToken->function()) { nestedIn->functionList.push_back(Function(nameToken, unquote(getFullType()))); mData->funcDecl(mExtTokens.front(), nameToken, &nestedIn->functionList.back()); if (nodeType == CXXConstructorDecl) nestedIn->functionList.back().type = Function::Type::eConstructor; else if (nodeType == CXXDestructorDecl) nestedIn->functionList.back().type = Function::Type::eDestructor; else nestedIn->functionList.back().retDef = startToken; } Function * const function = const_cast(nameToken->function()); if (!prev) { auto accessControl = mData->scopeAccessControl.find(tokenList->back()->scope()); if (accessControl != mData->scopeAccessControl.end()) function->access = accessControl->second; } Scope *scope = nullptr; if (hasBody) { symbolDatabase->scopeList.push_back(Scope(nullptr, nullptr, nestedIn)); scope = &symbolDatabase->scopeList.back(); scope->check = symbolDatabase; scope->function = function; scope->classDef = nameToken; scope->type = Scope::ScopeType::eFunction; scope->className = nameToken->str(); nestedIn->nestedList.push_back(scope); function->hasBody(true); function->functionScope = scope; } Token *par1 = addtoken(tokenList, "("); if (!function->arg) function->arg = par1; function->token = nameToken; if (!function->nestedIn) function->nestedIn = nestedIn; function->argDef = par1; // Function arguments for (int i = 0; i < children.size(); ++i) { AstNodePtr child = children[i]; if (child->nodeType != ParmVarDecl) continue; if (tokenList->back() != par1) addtoken(tokenList, ","); const Type *recordType = addTypeTokens(tokenList, child->mExtTokens.back(), nestedIn); const Token *typeEndToken = tokenList->back(); const std::string spelling = child->getSpelling(); Token *vartok = nullptr; if (!spelling.empty()) vartok = child->addtoken(tokenList, spelling); if (!prev) { function->argumentList.push_back(Variable(vartok, child->getType(), nullptr, typeEndToken, i, AccessControl::Argument, recordType, scope)); if (vartok) { const std::string addr = child->mExtTokens[0]; mData->varDecl(addr, vartok, &function->argumentList.back()); } } else if (vartok) { const std::string addr = child->mExtTokens[0]; mData->ref(addr, vartok); } } Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); if (function->isConst()) addtoken(tokenList, "const"); // Function body if (hasBody) { symbolDatabase->functionScopes.push_back(scope); Token *bodyStart = addtoken(tokenList, "{"); bodyStart->scope(scope); children.back()->createTokens(tokenList); Token *bodyEnd = addtoken(tokenList, "}"); scope->bodyStart = bodyStart; scope->bodyEnd = bodyEnd; bodyStart->link(bodyEnd); bodyEnd->link(bodyStart); } else { if (nodeType == CXXConstructorDecl && contains(mExtTokens, "default")) { addtoken(tokenList, "="); addtoken(tokenList, "default"); } addtoken(tokenList, ";"); } } void clangimport::AstNode::createTokensForCXXRecord(TokenList *tokenList) { bool isStruct = contains(mExtTokens, "struct"); Token * const classToken = addtoken(tokenList, isStruct ? "struct" : "class"); std::string className; if (mExtTokens[mExtTokens.size() - 2] == (isStruct?"struct":"class")) className = mExtTokens.back(); else className = mExtTokens[mExtTokens.size() - 2]; className += getTemplateParameters(); /*Token *nameToken =*/ addtoken(tokenList, className); // base classes bool firstBase = true; for (AstNodePtr child: children) { if (child->nodeType == "public" || child->nodeType == "protected" || child->nodeType == "private") { addtoken(tokenList, firstBase ? ":" : ","); addtoken(tokenList, child->nodeType); addtoken(tokenList, unquote(child->mExtTokens.back())); firstBase = false; } } // definition if (isDefinition()) { std::vector children2; for (AstNodePtr child: children) { if (child->nodeType == CXXConstructorDecl || child->nodeType == CXXDestructorDecl || child->nodeType == CXXMethodDecl || child->nodeType == FieldDecl || child->nodeType == VarDecl || child->nodeType == AccessSpecDecl || child->nodeType == TypedefDecl) children2.push_back(child); } Scope *scope = createScope(tokenList, isStruct ? Scope::ScopeType::eStruct : Scope::ScopeType::eClass, children2, classToken); const std::string addr = mExtTokens[0]; mData->scopeDecl(addr, scope); scope->className = className; mData->mSymbolDatabase->typeList.push_back(Type(classToken, scope, classToken->scope())); scope->definedType = &mData->mSymbolDatabase->typeList.back(); const_cast(classToken->scope())->definedTypesMap[className] = scope->definedType; } addtoken(tokenList, ";"); const_cast(tokenList->back())->scope(classToken->scope()); } Token * clangimport::AstNode::createTokensVarDecl(TokenList *tokenList) { const std::string addr = mExtTokens.front(); if (contains(mExtTokens, "static")) addtoken(tokenList, "static"); int typeIndex = mExtTokens.size() - 1; while (typeIndex > 1 && std::isalpha(mExtTokens[typeIndex][0])) typeIndex--; const std::string type = mExtTokens[typeIndex]; const std::string name = mExtTokens[typeIndex - 1]; const Token *startToken = tokenList->back(); const ::Type *recordType = addTypeTokens(tokenList, type); if (!startToken) startToken = tokenList->front(); else if (startToken->str() != "static") startToken = startToken->next(); Token *vartok1 = addtoken(tokenList, name); Scope *scope = const_cast(tokenList->back()->scope()); scope->varlist.push_back(Variable(vartok1, unquote(type), startToken, vartok1->previous(), 0, scope->defaultAccess(), recordType, scope)); mData->varDecl(addr, vartok1, &scope->varlist.back()); if (mExtTokens.back() == "cinit" && !children.empty()) { Token *eq = addtoken(tokenList, "="); eq->astOperand1(vartok1); eq->astOperand2(children.back()->createTokens(tokenList)); return eq; } else if (mExtTokens.back() == "callinit") { Token *par1 = addtoken(tokenList, "("); par1->astOperand1(vartok1); par1->astOperand2(getChild(0)->createTokens(tokenList)); Token *par2 = addtoken(tokenList, ")"); par1->link(par2); par2->link(par1); return par1; } else if (mExtTokens.back() == "listinit") { return getChild(0)->createTokens(tokenList); } return vartok1; } static void setTypes(TokenList *tokenList) { for (Token *tok = tokenList->front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { for (Token *typeToken = tok->tokAt(2); typeToken->str() != ")"; typeToken = typeToken->next()) { if (typeToken->type()) continue; typeToken->type(typeToken->scope()->findType(typeToken->str())); } } } } static void setValues(Tokenizer *tokenizer, SymbolDatabase *symbolDatabase) { const Settings * const settings = tokenizer->getSettings(); for (Scope &scope: symbolDatabase->scopeList) { if (!scope.definedType) continue; int typeSize = 0; for (const Variable &var: scope.varlist) { int mul = 1; for (const auto &dim: var.dimensions()) { mul *= dim.num; } if (var.valueType()) typeSize += mul * var.valueType()->typeSize(*settings, true); } scope.definedType->sizeOf = typeSize; } for (Token *tok = const_cast(tokenizer->tokens()); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { ValueType vt = ValueType::parseDecl(tok->tokAt(2), settings); int sz = vt.typeSize(*settings, true); if (sz <= 0) continue; long long mul = 1; for (Token *arrtok = tok->linkAt(1)->previous(); arrtok; arrtok = arrtok->previous()) { const std::string &a = arrtok->str(); if (a.size() > 2 && a[0] == '[' && a.back() == ']') mul *= std::atoi(a.substr(1).c_str()); else break; } ValueFlow::Value v(mul * sz); v.setKnown(); tok->next()->addValue(v); } } } void clangimport::parseClangAstDump(Tokenizer *tokenizer, std::istream &f) { TokenList *tokenList = &tokenizer->list; tokenizer->createSymbolDatabase(); SymbolDatabase *symbolDatabase = const_cast(tokenizer->getSymbolDatabase()); symbolDatabase->scopeList.push_back(Scope(nullptr, nullptr, nullptr)); symbolDatabase->scopeList.back().type = Scope::ScopeType::eGlobal; symbolDatabase->scopeList.back().check = symbolDatabase; clangimport::Data data; data.mSettings = tokenizer->getSettings(); data.mSymbolDatabase = symbolDatabase; std::string line; std::vector tree; while (std::getline(f,line)) { const std::string::size_type pos1 = line.find("-"); if (pos1 == std::string::npos) continue; if (!tree.empty() && line.substr(pos1) == "-<<>>") { const int level = (pos1 - 1) / 2; tree[level - 1]->children.push_back(nullptr); continue; } const std::string::size_type pos2 = line.find(" ", pos1); if (pos2 < pos1 + 4 || pos2 == std::string::npos) continue; const std::string nodeType = line.substr(pos1+1, pos2 - pos1 - 1); const std::string ext = line.substr(pos2); if (pos1 == 1 && endsWith(nodeType, "Decl")) { if (!tree.empty()) tree[0]->createTokens1(tokenList); tree.clear(); tree.push_back(std::make_shared(nodeType, ext, &data)); continue; } const int level = (pos1 - 1) / 2; if (level == 0 || level > tree.size()) continue; AstNodePtr newNode = std::make_shared(nodeType, ext, &data); tree[level - 1]->children.push_back(newNode); if (level >= tree.size()) tree.push_back(newNode); else tree[level] = newNode; } if (!tree.empty()) tree[0]->createTokens1(tokenList); // Validation for (const Token *tok = tokenList->front(); tok; tok = tok->next()) { if (Token::Match(tok, "(|)|[|]|{|}") && !tok->link()) throw InternalError(tok, "Token::link() is not set properly"); } if (tokenList->front()) tokenList->front()->assignIndexes(); symbolDatabase->clangSetVariables(data.getVariableList()); symbolDatabase->createSymbolDatabaseExprIds(); tokenList->clangSetOrigFiles(); setTypes(tokenList); setValues(tokenizer, symbolDatabase); } cppcheck-2.7/lib/clangimport.h000066400000000000000000000021021417746362400164110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef clangimportH #define clangimportH //--------------------------------------------------------------------------- #include class Tokenizer; namespace clangimport { void parseClangAstDump(Tokenizer *tokenizer, std::istream &f); } #endif cppcheck-2.7/lib/color.cpp000066400000000000000000000024131417746362400155500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "color.h" #ifndef _WIN32 #include #endif #include #include // IWYU pragma: keep #ifdef _WIN32 std::ostream& operator<<(std::ostream& os, const Color& /*c*/) { #else std::ostream& operator<<(std::ostream & os, const Color& c) { static const bool use_color = isatty(STDOUT_FILENO); if (use_color) return os << "\033[" << static_cast(c) << "m"; #endif return os; } std::string toString(const Color& c) { std::stringstream ss; ss << c; return ss.str(); } cppcheck-2.7/lib/color.h000066400000000000000000000023161417746362400152170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef colorH #define colorH #include "config.h" #include #include enum class Color { Reset = 0, Bold = 1, Dim = 2, FgRed = 31, FgGreen = 32, FgBlue = 34, FgMagenta = 35, FgDefault = 39, BgRed = 41, BgGreen = 42, BgBlue = 44, BgDefault = 49 }; CPPCHECKLIB std::ostream& operator<<(std::ostream& os, const Color& c); std::string toString(const Color& c); #endif cppcheck-2.7/lib/config.h000066400000000000000000000044241417746362400153500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef configH #define configH #ifdef _WIN32 # ifdef CPPCHECKLIB_EXPORT # define CPPCHECKLIB __declspec(dllexport) # elif defined(CPPCHECKLIB_IMPORT) # define CPPCHECKLIB __declspec(dllimport) # else # define CPPCHECKLIB # endif #else # define CPPCHECKLIB #endif // MS Visual C++ memory leak debug tracing #if defined(_MSC_VER) && defined(_DEBUG) # define _CRTDBG_MAP_ALLOC # include #endif // C++11 override #if defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ >= 5)) \ || (defined(__clang__) && (defined (__cplusplus)) && (__cplusplus >= 201103L)) \ || defined(__CPPCHECK__) # define OVERRIDE override # define FINAL final #else # define OVERRIDE # define FINAL #endif // C++11 noexcept #if (defined(__GNUC__) && (__GNUC__ >= 5)) \ || (defined(__clang__) && (defined (__cplusplus)) && (__cplusplus >= 201103L)) \ || defined(__CPPCHECK__) # define NOEXCEPT noexcept #else # define NOEXCEPT #endif // C++11 noreturn #if (defined(__GNUC__) && (__GNUC__ >= 5)) \ || (defined(__clang__) && (defined (__cplusplus)) && (__cplusplus >= 201103L)) \ || defined(__CPPCHECK__) # define NORETURN [[noreturn]] #else # define NORETURN #endif // fallthrough #if defined(__clang__) # define FALLTHROUGH [[clang::fallthrough]] #elif (defined(__GNUC__) && (__GNUC__ >= 7)) # define FALLTHROUGH __attribute__((fallthrough)) #else # define FALLTHROUGH #endif #define REQUIRES(msg, ...) class=typename std::enable_if<__VA_ARGS__::value>::type #include static const std::string emptyString; #endif // configH cppcheck-2.7/lib/cppcheck.cpp000066400000000000000000002144471417746362400162260ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppcheck.h" #include "check.h" #include "checkunusedfunctions.h" #include "clangimport.h" #include "color.h" #include "ctu.h" #include "errortypes.h" #include "exprengine.h" #include "library.h" #include "mathlib.h" #include "path.h" #include "platform.h" #include "preprocessor.h" // Preprocessor #include "standards.h" #include "suppressions.h" #include "timer.h" #include "token.h" #include "tokenize.h" // Tokenizer #include "tokenlist.h" #include "utils.h" #include "valueflow.h" #include "version.h" #include #include #include #include #include #include #include #include // <- TEMPORARY #include #include #include #include #include #include #include #define PICOJSON_USE_INT64 #include #include #include #ifdef HAVE_RULES #ifdef _WIN32 #define PCRE_STATIC #endif #include #endif class SymbolDatabase; static const char Version[] = CPPCHECK_VERSION_STRING; static const char ExtraVersion[] = ""; static const char FILELIST[] = "cppcheck-addon-ctu-file-list"; static TimerResults s_timerResults; // CWE ids used static const CWE CWE398(398U); // Indicator of Poor Code Quality namespace { struct AddonInfo { std::string name; std::string scriptFile; // addon script std::string executable; // addon executable std::string args; // special extra arguments std::string python; // script interpreter bool ctu = false; std::string runScript{}; static std::string getFullPath(const std::string &fileName, const std::string &exename) { if (Path::fileExists(fileName)) return fileName; const std::string exepath = Path::getPathFromFilename(exename); if (Path::fileExists(exepath + fileName)) return exepath + fileName; if (Path::fileExists(exepath + "addons/" + fileName)) return exepath + "addons/" + fileName; #ifdef FILESDIR if (Path::fileExists(FILESDIR + ("/" + fileName))) return FILESDIR + ("/" + fileName); if (Path::fileExists(FILESDIR + ("/addons/" + fileName))) return FILESDIR + ("/addons/" + fileName); #endif return ""; } std::string parseAddonInfo(const picojson::value &json, const std::string &fileName, const std::string &exename) { const std::string& json_error = picojson::get_last_error(); if (!json_error.empty()) { return "Loading " + fileName + " failed. " + json_error; } if (!json.is()) return "Loading " + fileName + " failed. Bad json."; picojson::object obj = json.get(); if (obj.count("args")) { if (!obj["args"].is()) return "Loading " + fileName + " failed. args must be array."; for (const picojson::value &v : obj["args"].get()) args += " " + v.get(); } if (obj.count("ctu")) { // ctu is specified in the config file if (!obj["ctu"].is()) return "Loading " + fileName + " failed. ctu must be boolean."; ctu = obj["ctu"].get(); } else { ctu = false; } if (obj.count("python")) { // Python was defined in the config file if (obj["python"].is()) { return "Loading " + fileName +" failed. python must not be an array."; } python = obj["python"].get(); } else { python = ""; } if (obj.count("executable")) { if (!obj["executable"].is()) return "Loading " + fileName + " failed. executable must be a string."; executable = getFullPath(obj["executable"].get(), fileName); return ""; } return getAddonInfo(obj["script"].get(), exename); } std::string getAddonInfo(const std::string &fileName, const std::string &exename) { if (fileName[0] == '{') { std::istringstream in(fileName); picojson::value json; in >> json; return parseAddonInfo(json, fileName, exename); } if (fileName.find(".") == std::string::npos) return getAddonInfo(fileName + ".py", exename); if (endsWith(fileName, ".py")) { scriptFile = getFullPath(fileName, exename); if (scriptFile.empty()) return "Did not find addon " + fileName; std::string::size_type pos1 = scriptFile.rfind("/"); if (pos1 == std::string::npos) pos1 = 0; else pos1++; std::string::size_type pos2 = scriptFile.rfind("."); if (pos2 < pos1) pos2 = std::string::npos; name = scriptFile.substr(pos1, pos2 - pos1); runScript = getFullPath("runaddon.py", exename); return ""; } if (!endsWith(fileName, ".json")) return "Failed to open addon " + fileName; std::ifstream fin(fileName); if (!fin.is_open()) return "Failed to open " + fileName; picojson::value json; fin >> json; return parseAddonInfo(json, fileName, exename); } }; } static std::string cmdFileName(std::string f) { f = Path::toNativeSeparators(f); if (f.find(" ") != std::string::npos) return "\"" + f + "\""; return f; } static std::vector split(const std::string &str, const std::string &sep=" ") { std::vector ret; for (std::string::size_type startPos = 0U; startPos < str.size();) { startPos = str.find_first_not_of(sep, startPos); if (startPos == std::string::npos) break; if (str[startPos] == '\"') { const std::string::size_type endPos = str.find("\"", startPos + 1); ret.push_back(str.substr(startPos + 1, endPos - startPos - 1)); startPos = (endPos < str.size()) ? (endPos + 1) : endPos; continue; } const std::string::size_type endPos = str.find(sep, startPos + 1); ret.push_back(str.substr(startPos, endPos - startPos)); startPos = endPos; } return ret; } static std::string getDumpFileName(const Settings& settings, const std::string& filename) { if (!settings.dumpFile.empty()) return settings.dumpFile; if (!settings.dump && !settings.buildDir.empty()) return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, "") + ".dump"; return filename + ".dump"; } static std::string getCtuInfoFileName(const std::string &dumpFile) { return dumpFile.substr(0, dumpFile.size()-4) + "ctu-info"; } static void createDumpFile(const Settings& settings, const std::string& filename, const std::vector& files, const simplecpp::Token* rawtokens, std::ofstream& fdump, std::string& dumpFile) { if (!settings.dump && settings.addons.empty()) return; dumpFile = getDumpFileName(settings, filename); fdump.open(dumpFile); if (!fdump.is_open()) return; { std::ofstream fout(getCtuInfoFileName(dumpFile)); } fdump << "" << std::endl; fdump << "" << std::endl; fdump << " \n"; if (rawtokens) { fdump << " " << std::endl; for (unsigned int i = 0; i < files.size(); ++i) fdump << " " << std::endl; for (const simplecpp::Token *tok = rawtokens; tok; tok = tok->next) { fdump << " location.fileIndex << "\" " << "linenr=\"" << tok->location.line << "\" " << "column=\"" << tok->location.col << "\" " << "str=\"" << ErrorLogger::toxml(tok->str()) << "\"" << "/>" << std::endl; } fdump << " " << std::endl; } } static std::string executeAddon(const AddonInfo &addonInfo, const std::string &defaultPythonExe, const std::string &file, std::function,std::string,std::string*)> executeCommand) { const std::string redirect = "2>&1"; std::string pythonExe; if (!addonInfo.executable.empty()) pythonExe = addonInfo.executable; else if (!addonInfo.python.empty()) pythonExe = cmdFileName(addonInfo.python); else if (!defaultPythonExe.empty()) pythonExe = cmdFileName(defaultPythonExe); else { #ifdef _WIN32 const char *py_exes[] = { "python3.exe", "python.exe" }; #else const char *py_exes[] = { "python3", "python" }; #endif for (const char* py_exe : py_exes) { std::string out; if (executeCommand(py_exe, split("--version"), redirect, &out) && out.compare(0, 7, "Python ") == 0 && std::isdigit(out[7])) { pythonExe = py_exe; break; } } if (pythonExe.empty()) throw InternalError(nullptr, "Failed to auto detect python"); } std::string args; if (addonInfo.executable.empty()) args = cmdFileName(addonInfo.runScript) + " " + cmdFileName(addonInfo.scriptFile); args += std::string(args.empty() ? "" : " ") + "--cli" + addonInfo.args; const std::string fileArg = (endsWith(file, FILELIST, sizeof(FILELIST)-1) ? " --file-list " : " ") + cmdFileName(file); args += fileArg; std::string result; if (!executeCommand(pythonExe, split(args), redirect, &result)) throw InternalError(nullptr, "Failed to execute addon (command: '" + pythonExe + " " + args + "')"); // Validate output.. std::istringstream istr(result); std::string line; while (std::getline(istr, line)) { if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{') { result.erase(result.find_last_not_of('\n') + 1, std::string::npos); // Remove trailing newlines throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result); } } // Valid results return result; } static std::string getDefinesFlags(const std::string &semicolonSeparatedString) { std::string flags; for (const std::string &d: split(semicolonSeparatedString, ";")) flags += "-D" + d + " "; return flags; } CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions, std::function,std::string,std::string*)> executeCommand) : mErrorLogger(errorLogger) , mExitCode(0) , mSuppressInternalErrorFound(false) , mUseGlobalSuppressions(useGlobalSuppressions) , mTooManyConfigs(false) , mSimplify(true) , mExecuteCommand(executeCommand) {} CppCheck::~CppCheck() { while (!mFileInfo.empty()) { delete mFileInfo.back(); mFileInfo.pop_back(); } s_timerResults.showResults(mSettings.showtime); } const char * CppCheck::version() { return Version; } const char * CppCheck::extraVersion() { return ExtraVersion; } static bool reportClangErrors(std::istream &is, std::function reportErr, std::vector *warnings) { std::string line; while (std::getline(is, line)) { if (line.empty() || line[0] == ' ' || line[0] == '`' || line[0] == '-') continue; std::string::size_type pos3 = line.find(": error: "); if (pos3 == std::string::npos) pos3 = line.find(": fatal error:"); if (warnings && pos3 == std::string::npos) pos3 = line.find(": warning:"); if (pos3 == std::string::npos) continue; // file:line:column: error: .... const std::string::size_type pos2 = line.rfind(":", pos3 - 1); const std::string::size_type pos1 = line.rfind(":", pos2 - 1); if (pos1 >= pos2 || pos2 >= pos3) continue; const std::string filename = line.substr(0, pos1); const std::string linenr = line.substr(pos1+1, pos2-pos1-1); const std::string colnr = line.substr(pos2+1, pos3-pos2-1); const std::string msg = line.substr(line.find(":", pos3+1) + 2); std::list locationList; ErrorMessage::FileLocation loc; loc.setfile(Path::toNativeSeparators(filename)); loc.line = std::atoi(linenr.c_str()); loc.column = std::atoi(colnr.c_str()); locationList.push_back(loc); ErrorMessage errmsg(locationList, loc.getfile(), Severity::error, msg, "syntaxError", Certainty::normal); if (line.compare(pos3, 10, ": warning:") == 0) { warnings->push_back(errmsg); continue; } reportErr(errmsg); return true; } return false; } unsigned int CppCheck::check(const std::string &path) { if (mSettings.clang) { if (!mSettings.quiet) mErrorLogger.reportOut(std::string("Checking ") + path + "...", Color::FgGreen); const std::string lang = Path::isCPP(path) ? "-x c++" : "-x c"; const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, ""); const std::string clangcmd = analyzerInfo + ".clang-cmd"; const std::string clangStderr = analyzerInfo + ".clang-stderr"; const std::string clangAst = analyzerInfo + ".clang-ast"; std::string exe = mSettings.clangExecutable; #ifdef _WIN32 // append .exe if it is not a path if (Path::fromNativeSeparators(mSettings.clangExecutable).find('/') == std::string::npos) { exe += ".exe"; } #endif std::string flags(lang + " "); if (Path::isCPP(path) && !mSettings.standards.stdValue.empty()) flags += "-std=" + mSettings.standards.stdValue + " "; for (const std::string &i: mSettings.includePaths) flags += "-I" + i + " "; flags += getDefinesFlags(mSettings.userDefines); const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path; const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr); if (!mSettings.buildDir.empty()) { std::ofstream fout(clangcmd); fout << exe << " " << args2 << " " << redirect2 << std::endl; } else if (mSettings.verbose && !mSettings.quiet) { mErrorLogger.reportOut(exe + " " + args2); } std::string output2; if (!mExecuteCommand(exe,split(args2),redirect2,&output2) || output2.find("TranslationUnitDecl") == std::string::npos) { std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "'" << std::endl; return 0; } // Ensure there are not syntax errors... std::vector compilerWarnings; if (!mSettings.buildDir.empty()) { std::ifstream fin(clangStderr); auto reportError = [this](const ErrorMessage& errorMessage) { reportErr(errorMessage); }; if (reportClangErrors(fin, reportError, &compilerWarnings)) return 0; } else { std::istringstream istr(output2); auto reportError = [this](const ErrorMessage& errorMessage) { reportErr(errorMessage); }; if (reportClangErrors(istr, reportError, &compilerWarnings)) return 0; } if (!mSettings.buildDir.empty()) { std::ofstream fout(clangAst); fout << output2 << std::endl; } try { std::istringstream ast(output2); Tokenizer tokenizer(&mSettings, this); tokenizer.list.appendFileIfNew(path); clangimport::parseClangAstDump(&tokenizer, ast); ValueFlow::setValues(&tokenizer.list, const_cast(tokenizer.getSymbolDatabase()), this, &mSettings); if (mSettings.debugnormal) tokenizer.printDebugOutput(1); checkNormalTokens(tokenizer); // create dumpfile std::ofstream fdump; std::string dumpFile; createDumpFile(mSettings, path, tokenizer.list.getFiles(), nullptr, fdump, dumpFile); if (fdump.is_open()) { fdump << "" << std::endl; for (const ErrorMessage& errmsg: compilerWarnings) fdump << " \n"; fdump << " " << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; tokenizer.dump(fdump); fdump << "" << std::endl; fdump << "" << std::endl; fdump.close(); } // run addons executeAddons(dumpFile); } catch (const InternalError &e) { internalError(path, e.errorMessage); mExitCode = 1; // e.g. reflect a syntax error } catch (const std::exception &e) { internalError(path, e.what()); } return mExitCode; } std::ifstream fin(path); return checkFile(Path::simplifyPath(path), emptyString, fin); } unsigned int CppCheck::check(const std::string &path, const std::string &content) { std::istringstream iss(content); return checkFile(Path::simplifyPath(path), emptyString, iss); } unsigned int CppCheck::check(const ImportProject::FileSettings &fs) { CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand); temp.mSettings = mSettings; if (!temp.mSettings.userDefines.empty()) temp.mSettings.userDefines += ';'; if (mSettings.clang) temp.mSettings.userDefines += fs.defines; else temp.mSettings.userDefines += fs.cppcheckDefines(); temp.mSettings.includePaths = fs.includePaths; temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend()); if (fs.standard.find("++") != std::string::npos) temp.mSettings.standards.setCPP(fs.standard); else if (!fs.standard.empty()) temp.mSettings.standards.setC(fs.standard); if (fs.platformType != Settings::Unspecified) temp.mSettings.platform(fs.platformType); if (mSettings.clang) { temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend()); return temp.check(Path::simplifyPath(fs.filename)); } std::ifstream fin(fs.filename); unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg, fin); mSettings.nomsg.addSuppressions(temp.mSettings.nomsg.getSuppressions()); return returnValue; } unsigned int CppCheck::checkFile(const std::string& filename, const std::string &cfgname, std::istream& fileStream) { mExitCode = 0; mSuppressInternalErrorFound = false; // only show debug warnings for accepted C/C++ source files if (!Path::acceptFile(filename)) mSettings.debugwarnings = false; if (Settings::terminated()) return mExitCode; if (!mSettings.quiet) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("..."), Color::FgGreen); if (mSettings.verbose) { mErrorLogger.reportOut("Defines:" + mSettings.userDefines); std::string undefs; for (const std::string& U : mSettings.userUndefs) { if (!undefs.empty()) undefs += ';'; undefs += ' ' + U; } mErrorLogger.reportOut("Undefines:" + undefs); std::string includePaths; for (const std::string &I : mSettings.includePaths) includePaths += " -I" + I; mErrorLogger.reportOut("Includes:" + includePaths); mErrorLogger.reportOut(std::string("Platform:") + mSettings.platformString()); } } if (plistFile.is_open()) { plistFile << ErrorLogger::plistFooter(); plistFile.close(); } CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr); try { Preprocessor preprocessor(mSettings, this); std::set configurations; simplecpp::OutputList outputList; std::vector files; simplecpp::TokenList tokens1(fileStream, files, filename, &outputList); // If there is a syntax error, report it and stop for (const simplecpp::Output &output : outputList) { bool err; switch (output.type) { case simplecpp::Output::ERROR: case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: err = true; break; case simplecpp::Output::WARNING: case simplecpp::Output::MISSING_HEADER: case simplecpp::Output::PORTABILITY_BACKSLASH: err = false; break; } if (err) { std::string file = Path::fromNativeSeparators(output.location.file()); if (mSettings.relativePaths) file = Path::getRelativePath(file, mSettings.basePaths); const ErrorMessage::FileLocation loc1(file, output.location.line, output.location.col); std::list callstack(1, loc1); ErrorMessage errmsg(callstack, "", Severity::error, output.msg, "syntaxError", Certainty::normal); reportErr(errmsg); return mExitCode; } } if (!preprocessor.loadFiles(tokens1, files)) return mExitCode; if (!mSettings.plistOutput.empty()) { std::string filename2; if (filename.find('/') != std::string::npos) filename2 = filename.substr(filename.rfind('/') + 1); else filename2 = filename; std::size_t fileNameHash = std::hash {}(filename); filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + "_" + std::to_string(fileNameHash) + ".plist"; plistFile.open(filename2); plistFile << ErrorLogger::plistHeader(version(), files); } // write dump file xml prolog std::ofstream fdump; std::string dumpFile; createDumpFile(mSettings, filename, files, tokens1.cfront(), fdump, dumpFile); // Parse comments and then remove them preprocessor.inlineSuppressions(tokens1); if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { mSettings.nomsg.dump(fdump); } tokens1.removeComments(); preprocessor.removeComments(); if (!mSettings.buildDir.empty()) { // Get toolinfo std::ostringstream toolinfo; toolinfo << CPPCHECK_VERSION_STRING; toolinfo << (mSettings.severity.isEnabled(Severity::warning) ? 'w' : ' '); toolinfo << (mSettings.severity.isEnabled(Severity::style) ? 's' : ' '); toolinfo << (mSettings.severity.isEnabled(Severity::performance) ? 'p' : ' '); toolinfo << (mSettings.severity.isEnabled(Severity::portability) ? 'p' : ' '); toolinfo << (mSettings.severity.isEnabled(Severity::information) ? 'i' : ' '); toolinfo << mSettings.userDefines; mSettings.nomsg.dump(toolinfo); // Calculate checksum so it can be compared with old checksum / future checksums const unsigned int checksum = preprocessor.calculateChecksum(tokens1, toolinfo.str()); std::list errors; if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, checksum, &errors)) { while (!errors.empty()) { reportErr(errors.front()); errors.pop_front(); } return mExitCode; // known results => no need to reanalyze file } } // Get directives preprocessor.setDirectives(tokens1); preprocessor.simplifyPragmaAsm(&tokens1); preprocessor.setPlatformInfo(&tokens1); // Get configurations.. if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) { Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults); configurations = preprocessor.getConfigs(tokens1); } else { configurations.insert(mSettings.userDefines); } if (mSettings.checkConfiguration) { for (const std::string &config : configurations) (void)preprocessor.getcode(tokens1, config, files, true); return 0; } // Run define rules on raw code for (const Settings::Rule &rule : mSettings.rules) { if (rule.tokenlist != "define") continue; std::string code; const std::list &directives = preprocessor.getDirectives(); for (const Directive &dir : directives) { if (dir.str.compare(0,8,"#define ") == 0 || dir.str.compare(0,9,"#include ") == 0) code += "#line " + MathLib::toString(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n'; } Tokenizer tokenizer2(&mSettings, this); std::istringstream istr2(code); tokenizer2.list.createTokens(istr2); executeRules("define", tokenizer2); break; } if (!mSettings.force && configurations.size() > mSettings.maxConfigs) { if (mSettings.severity.isEnabled(Severity::information)) { tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size()); } else { mTooManyConfigs = true; } } std::set checksums; int checkCount = 0; bool hasValidConfig = false; std::list configurationError; for (const std::string &currCfg : configurations) { // bail out if terminated if (Settings::terminated()) break; // Check only a few configurations (default 12), after that bail out, unless --force // was used. if (!mSettings.force && ++checkCount > mSettings.maxConfigs) break; if (!mSettings.userDefines.empty()) { mCurrentConfig = mSettings.userDefines; const std::vector v1(split(mSettings.userDefines, ";")); for (const std::string &cfg: split(currCfg, ";")) { if (std::find(v1.begin(), v1.end(), cfg) == v1.end()) { mCurrentConfig += ";" + cfg; } } } else { mCurrentConfig = currCfg; } if (mSettings.preprocessOnly) { Timer t("Preprocessor::getcode", mSettings.showtime, &s_timerResults); std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true); t.stop(); if (codeWithoutCfg.compare(0,5,"#file") == 0) codeWithoutCfg.insert(0U, "//"); std::string::size_type pos = 0; while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos) codeWithoutCfg.insert(pos+1U, "//"); pos = 0; while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos) codeWithoutCfg.insert(pos+1U, "//"); pos = 0; while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos) codeWithoutCfg[pos] = ' '; reportOut(codeWithoutCfg); continue; } Tokenizer tokenizer(&mSettings, this); tokenizer.setPreprocessor(&preprocessor); if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE) tokenizer.setTimerResults(&s_timerResults); try { // Create tokens, skip rest of iteration if failed { Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults); simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true); tokenizer.createTokens(std::move(tokensP)); } hasValidConfig = true; // If only errors are printed, print filename after the check if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...", Color::FgGreen); } if (!tokenizer.tokens()) continue; // skip rest of iteration if just checking configuration if (mSettings.checkConfiguration) continue; // Check raw tokens checkRawTokens(tokenizer); // Simplify tokens into normal form, skip rest of iteration if failed Timer timer2("Tokenizer::simplifyTokens1", mSettings.showtime, &s_timerResults); bool result = tokenizer.simplifyTokens1(mCurrentConfig); timer2.stop(); if (!result) continue; // dump xml if --dump if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { fdump << "" << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; preprocessor.dump(fdump); tokenizer.dump(fdump); fdump << "" << std::endl; } // Skip if we already met the same simplified token list if (mSettings.force || mSettings.maxConfigs > 1) { const unsigned long long checksum = tokenizer.list.calculateChecksum(); if (checksums.find(checksum) != checksums.end()) { if (mSettings.debugwarnings) purgedConfigurationMessage(filename, mCurrentConfig); continue; } checksums.insert(checksum); } // Check normal tokens checkNormalTokens(tokenizer); // Analyze info.. if (!mSettings.buildDir.empty()) checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings); // simplify more if required, skip rest of iteration if failed if (mSimplify && hasRule("simple")) { std::cout << "Handling of \"simple\" rules is deprecated and will be removed in Cppcheck 2.5." << std::endl; // if further simplification fails then skip rest of iteration Timer timer3("Tokenizer::simplifyTokenList2", mSettings.showtime, &s_timerResults); result = tokenizer.simplifyTokenList2(); timer3.stop(); if (!result) continue; if (!Settings::terminated()) executeRules("simple", tokenizer); } } catch (const simplecpp::Output &o) { // #error etc during preprocessing configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + MathLib::toString(o.location.line) + "] " + o.msg); --checkCount; // don't count invalid configurations continue; } catch (const InternalError &e) { std::list locationList; if (e.token) { ErrorMessage::FileLocation loc(e.token, &tokenizer.list); locationList.push_back(loc); } else { ErrorMessage::FileLocation loc(tokenizer.list.getSourceFilePath(), 0, 0); ErrorMessage::FileLocation loc2(filename, 0, 0); locationList.push_back(loc2); if (filename != tokenizer.list.getSourceFilePath()) locationList.push_back(loc); } ErrorMessage errmsg(locationList, tokenizer.list.getSourceFilePath(), Severity::error, e.errorMessage, e.id, Certainty::normal); if (errmsg.severity == Severity::error || mSettings.severity.isEnabled(errmsg.severity)) reportErr(errmsg); } } if (!hasValidConfig && configurations.size() > 1 && mSettings.severity.isEnabled(Severity::information)) { std::string msg; msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details."; msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:"; for (const std::string &s : configurationError) msg += '\n' + s; std::list locationList; ErrorMessage::FileLocation loc; loc.setfile(Path::toNativeSeparators(filename)); locationList.push_back(loc); ErrorMessage errmsg(locationList, loc.getfile(), Severity::information, msg, "noValidConfiguration", Certainty::normal); reportErr(errmsg); } // dumped all configs, close root element now if (fdump.is_open()) { fdump << "" << std::endl; fdump.close(); } executeAddons(dumpFile); } catch (const std::runtime_error &e) { internalError(filename, e.what()); } catch (const std::bad_alloc &e) { internalError(filename, e.what()); } catch (const InternalError &e) { internalError(filename, e.errorMessage); mExitCode=1; // e.g. reflect a syntax error } mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo()); mAnalyzerInformation.close(); // In jointSuppressionReport mode, unmatched suppressions are // collected after all files are processed if (!mSettings.jointSuppressionReport && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) { reportUnmatchedSuppressions(mSettings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled())); } mErrorList.clear(); return mExitCode; } void CppCheck::internalError(const std::string &filename, const std::string &msg) { const std::string fixedpath = Path::toNativeSeparators(filename); const std::string fullmsg("Bailing out from checking " + fixedpath + " since there was an internal error: " + msg); if (mSettings.severity.isEnabled(Severity::information)) { const ErrorMessage::FileLocation loc1(filename, 0, 0); std::list callstack(1, loc1); ErrorMessage errmsg(callstack, emptyString, Severity::information, fullmsg, "internalError", Certainty::normal); mErrorLogger.reportErr(errmsg); } else { // Report on stdout mErrorLogger.reportOut(fullmsg); } } //--------------------------------------------------------------------------- // CppCheck - A function that checks a raw token list //--------------------------------------------------------------------------- void CppCheck::checkRawTokens(const Tokenizer &tokenizer) { // Execute rules for "raw" code executeRules("raw", tokenizer); } //--------------------------------------------------------------------------- // CppCheck - A function that checks a normal token list //--------------------------------------------------------------------------- void CppCheck::checkNormalTokens(const Tokenizer &tokenizer) { mSettings.library.bugHunting = mSettings.bugHunting; if (mSettings.bugHunting) ExprEngine::runChecks(this, &tokenizer, &mSettings); else { // call all "runChecks" in all registered Check classes for (Check *check : Check::instances()) { if (Settings::terminated()) return; if (Tokenizer::isMaxTime()) return; Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults); check->runChecks(&tokenizer, &mSettings, this); } if (mSettings.clang) // TODO: Use CTU for Clang analysis return; // Analyse the tokens.. CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer); if (fi1) { mFileInfo.push_back(fi1); mAnalyzerInformation.setFileInfo("ctu", fi1->toString()); } for (const Check *check : Check::instances()) { Check::FileInfo *fi = check->getFileInfo(&tokenizer, &mSettings); if (fi != nullptr) { mFileInfo.push_back(fi); mAnalyzerInformation.setFileInfo(check->name(), fi->toString()); } } executeRules("normal", tokenizer); } } //--------------------------------------------------------------------------- bool CppCheck::hasRule(const std::string &tokenlist) const { #ifdef HAVE_RULES for (const Settings::Rule &rule : mSettings.rules) { if (rule.tokenlist == tokenlist) return true; } #else (void)tokenlist; #endif return false; } #ifdef HAVE_RULES static const char * pcreErrorCodeToString(const int pcreExecRet) { switch (pcreExecRet) { case PCRE_ERROR_NULL: return "Either code or subject was passed as NULL, or ovector was NULL " "and ovecsize was not zero (PCRE_ERROR_NULL)"; case PCRE_ERROR_BADOPTION: return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)"; case PCRE_ERROR_BADMAGIC: return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, " "to catch the case when it is passed a junk pointer and to detect when a " "pattern that was compiled in an environment of one endianness is run in " "an environment with the other endianness. This is the error that PCRE " "gives when the magic number is not present (PCRE_ERROR_BADMAGIC)"; case PCRE_ERROR_UNKNOWN_NODE: return "While running the pattern match, an unknown item was encountered in the " "compiled pattern. This error could be caused by a bug in PCRE or by " "overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)"; case PCRE_ERROR_NOMEMORY: return "If a pattern contains back references, but the ovector that is passed " "to pcre_exec() is not big enough to remember the referenced substrings, " "PCRE gets a block of memory at the start of matching to use for this purpose. " "If the call via pcre_malloc() fails, this error is given. The memory is " "automatically freed at the end of matching. This error is also given if " "pcre_stack_malloc() fails in pcre_exec(). " "This can happen only when PCRE has been compiled with " "--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)"; case PCRE_ERROR_NOSUBSTRING: return "This error is used by the pcre_copy_substring(), pcre_get_substring(), " "and pcre_get_substring_list() functions (see below). " "It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)"; case PCRE_ERROR_MATCHLIMIT: return "The backtracking limit, as specified by the match_limit field in a pcre_extra " "structure (or defaulted) was reached. " "See the description above (PCRE_ERROR_MATCHLIMIT)"; case PCRE_ERROR_CALLOUT: return "This error is never generated by pcre_exec() itself. " "It is provided for use by callout functions that want to yield a distinctive " "error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)"; case PCRE_ERROR_BADUTF8: return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, " "and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector " "(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 " "character is placed in the first element, and a reason code is placed in the " "second element. The reason codes are listed in the following section. For " "backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated " "UTF-8 character at the end of the subject (reason codes 1 to 5), " "PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8"; case PCRE_ERROR_BADUTF8_OFFSET: return "The UTF-8 byte sequence that was passed as a subject was checked and found to " "be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of " "startoffset did not point to the beginning of a UTF-8 character or the end of " "the subject (PCRE_ERROR_BADUTF8_OFFSET)"; case PCRE_ERROR_PARTIAL: return "The subject string did not match, but it did match partially. See the " "pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)"; case PCRE_ERROR_BADPARTIAL: return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL " "option was used with a compiled pattern containing items that were not supported " "for partial matching. From release 8.00 onwards, there are no restrictions on " "partial matching (PCRE_ERROR_BADPARTIAL)"; case PCRE_ERROR_INTERNAL: return "An unexpected internal error has occurred. This error could be caused by a bug " "in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)"; case PCRE_ERROR_BADCOUNT: return "This error is given if the value of the ovecsize argument is negative " "(PCRE_ERROR_BADCOUNT)"; case PCRE_ERROR_RECURSIONLIMIT: return "The internal recursion limit, as specified by the match_limit_recursion " "field in a pcre_extra structure (or defaulted) was reached. " "See the description above (PCRE_ERROR_RECURSIONLIMIT)"; case PCRE_ERROR_DFA_UITEM: return "PCRE_ERROR_DFA_UITEM"; case PCRE_ERROR_DFA_UCOND: return "PCRE_ERROR_DFA_UCOND"; case PCRE_ERROR_DFA_WSSIZE: return "PCRE_ERROR_DFA_WSSIZE"; case PCRE_ERROR_DFA_RECURSE: return "PCRE_ERROR_DFA_RECURSE"; case PCRE_ERROR_NULLWSLIMIT: return "PCRE_ERROR_NULLWSLIMIT"; case PCRE_ERROR_BADNEWLINE: return "An invalid combination of PCRE_NEWLINE_xxx options was " "given (PCRE_ERROR_BADNEWLINE)"; case PCRE_ERROR_BADOFFSET: return "The value of startoffset was negative or greater than the length " "of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)"; case PCRE_ERROR_SHORTUTF8: return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject " "string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. " "Information about the failure is returned as for PCRE_ERROR_BADUTF8. " "It is in fact sufficient to detect this case, but this special error code for " "PCRE_PARTIAL_HARD precedes the implementation of returned information; " "it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)"; case PCRE_ERROR_RECURSELOOP: return "This error is returned when pcre_exec() detects a recursion loop " "within the pattern. Specifically, it means that either the whole pattern " "or a subpattern has been called recursively for the second time at the same " "position in the subject string. Some simple patterns that might do this " "are detected and faulted at compile time, but more complicated cases, " "in particular mutual recursions between two different subpatterns, " "cannot be detected until run time (PCRE_ERROR_RECURSELOOP)"; case PCRE_ERROR_JIT_STACKLIMIT: return "This error is returned when a pattern that was successfully studied " "using a JIT compile option is being matched, but the memory available " "for the just-in-time processing stack is not large enough. See the pcrejit " "documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)"; case PCRE_ERROR_BADMODE: return "This error is given if a pattern that was compiled by the 8-bit library " "is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)"; case PCRE_ERROR_BADENDIANNESS: return "This error is given if a pattern that was compiled and saved is reloaded on a " "host with different endianness. The utility function pcre_pattern_to_host_byte_order() " "can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)"; case PCRE_ERROR_DFA_BADRESTART: return "PCRE_ERROR_DFA_BADRESTART"; #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32 case PCRE_ERROR_BADLENGTH: return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)"; case PCRE_ERROR_JIT_BADOPTION: return "This error is returned when a pattern that was successfully studied using a JIT compile " "option is being matched, but the matching mode (partial or complete match) does not correspond " "to any JIT compilation mode. When the JIT fast path function is used, this error may be " "also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)"; #endif } return ""; } #endif // HAVE_RULES void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer) { (void)tokenlist; (void)tokenizer; #ifdef HAVE_RULES // There is no rule to execute if (!hasRule(tokenlist)) return; // Write all tokens in a string that can be parsed by pcre std::ostringstream ostr; for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) ostr << " " << tok->str(); const std::string str(ostr.str()); for (const Settings::Rule &rule : mSettings.rules) { if (rule.pattern.empty() || rule.id.empty() || rule.severity == Severity::none || rule.tokenlist != tokenlist) continue; if (!mSettings.quiet) { reportOut("Processing rule: " + rule.pattern, Color::FgGreen); } const char *pcreCompileErrorStr = nullptr; int erroffset = 0; pcre * const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr); if (!re) { if (pcreCompileErrorStr) { const std::string msg = "pcre_compile failed: " + std::string(pcreCompileErrorStr); const ErrorMessage errmsg(std::list(), emptyString, Severity::error, msg, "pcre_compile", Certainty::normal); reportErr(errmsg); } continue; } // Optimize the regex, but only if PCRE_CONFIG_JIT is available #ifdef PCRE_CONFIG_JIT const char *pcreStudyErrorStr = nullptr; pcre_extra * const pcreExtra = pcre_study(re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr); // pcre_study() returns NULL for both errors and when it can not optimize the regex. // The last argument is how one checks for errors. // It is NULL if everything works, and points to an error string otherwise. if (pcreStudyErrorStr) { const std::string msg = "pcre_study failed: " + std::string(pcreStudyErrorStr); const ErrorMessage errmsg(std::list(), emptyString, Severity::error, msg, "pcre_study", Certainty::normal); reportErr(errmsg); // pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile(). pcre_free(re); continue; } #else const pcre_extra * const pcreExtra = nullptr; #endif int pos = 0; int ovector[30]= {0}; while (pos < (int)str.size()) { const int pcreExecRet = pcre_exec(re, pcreExtra, str.c_str(), (int)str.size(), pos, 0, ovector, 30); if (pcreExecRet < 0) { const std::string errorMessage = pcreErrorCodeToString(pcreExecRet); if (!errorMessage.empty()) { const ErrorMessage errmsg(std::list(), emptyString, Severity::error, std::string("pcre_exec failed: ") + errorMessage, "pcre_exec", Certainty::normal); reportErr(errmsg); } break; } const unsigned int pos1 = (unsigned int)ovector[0]; const unsigned int pos2 = (unsigned int)ovector[1]; // jump to the end of the match for the next pcre_exec pos = (int)pos2; // determine location.. ErrorMessage::FileLocation loc; loc.setfile(tokenizer.list.getSourceFilePath()); loc.line = 0; std::size_t len = 0; for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { len = len + 1U + tok->str().size(); if (len > pos1) { loc.setfile(tokenizer.list.getFiles().at(tok->fileIndex())); loc.line = tok->linenr(); break; } } const std::list callStack(1, loc); // Create error message std::string summary; if (rule.summary.empty()) summary = "found '" + str.substr(pos1, pos2 - pos1) + "'"; else summary = rule.summary; const ErrorMessage errmsg(callStack, tokenizer.list.getSourceFilePath(), rule.severity, summary, rule.id, Certainty::normal); // Report error reportErr(errmsg); } pcre_free(re); #ifdef PCRE_CONFIG_JIT // Free up the EXTRA PCRE value (may be NULL at this point) if (pcreExtra) { pcre_free_study(pcreExtra); } #endif } #endif } void CppCheck::executeAddons(const std::string& dumpFile) { if (!dumpFile.empty()) { std::vector f{dumpFile}; executeAddons(f); if (!mSettings.dump && mSettings.buildDir.empty()) std::remove(dumpFile.c_str()); } } void CppCheck::executeAddons(const std::vector& files) { if (mSettings.addons.empty() || files.empty()) return; std::string fileList; if (files.size() >= 2 || endsWith(files[0], ".ctu-info")) { fileList = Path::getPathFromFilename(files[0]) + FILELIST; std::ofstream fout(fileList); for (const std::string& f: files) fout << f << std::endl; } for (const std::string &addon : mSettings.addons) { struct AddonInfo addonInfo; const std::string &failedToGetAddonInfo = addonInfo.getAddonInfo(addon, mSettings.exename); if (!failedToGetAddonInfo.empty()) { reportOut(failedToGetAddonInfo, Color::FgRed); mExitCode = 1; continue; } if (addon != "misra" && !addonInfo.ctu && endsWith(files.back(), ".ctu-info")) continue; const std::string results = executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mExecuteCommand); std::istringstream istr(results); std::string line; while (std::getline(istr, line)) { if (line.compare(0,1,"{") != 0) continue; picojson::value res; std::istringstream istr2(line); istr2 >> res; if (!res.is()) continue; picojson::object obj = res.get(); ErrorMessage errmsg; if (obj.count("file") > 0) { const std::string fileName = obj["file"].get(); const int64_t lineNumber = obj["linenr"].get(); const int64_t column = obj["column"].get(); errmsg.callStack.emplace_back(ErrorMessage::FileLocation(fileName, lineNumber, column)); } else if (obj.count("loc") > 0) { for (const picojson::value &locvalue: obj["loc"].get()) { picojson::object loc = locvalue.get(); const std::string fileName = loc["file"].get(); const int64_t lineNumber = loc["linenr"].get(); const int64_t column = loc["column"].get(); const std::string info = loc["info"].get(); errmsg.callStack.emplace_back(ErrorMessage::FileLocation(fileName, info, lineNumber, column)); } } errmsg.id = obj["addon"].get() + "-" + obj["errorId"].get(); const std::string text = obj["message"].get(); errmsg.setmsg(text); const std::string severity = obj["severity"].get(); errmsg.severity = Severity::fromString(severity); if (errmsg.severity == Severity::SeverityType::none) continue; errmsg.file0 = ((files.size() == 1) ? files[0] : ""); reportErr(errmsg); } } if (!fileList.empty()) std::remove(fileList.c_str()); } void CppCheck::executeAddonsWholeProgram(const std::map &files) { if (mSettings.addons.empty()) return; std::vector ctuInfoFiles; for (const auto &f: files) { const std::string &dumpFileName = getDumpFileName(mSettings, f.first); ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName)); } executeAddons(ctuInfoFiles); for (const std::string &f: ctuInfoFiles) { std::remove(f.c_str()); } } Settings &CppCheck::settings() { return mSettings; } void CppCheck::tooManyConfigsError(const std::string &file, const int numberOfConfigurations) { if (!mSettings.severity.isEnabled(Severity::information) && !mTooManyConfigs) return; mTooManyConfigs = false; if (mSettings.severity.isEnabled(Severity::information) && file.empty()) return; std::list loclist; if (!file.empty()) { ErrorMessage::FileLocation location; location.setfile(file); loclist.push_back(location); } std::ostringstream msg; msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs; if (numberOfConfigurations > mSettings.maxConfigs) msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n"; if (file.empty()) msg << " configurations. Use --force to check all configurations. For more details, use --enable=information.\n"; msg << "The checking of the file will be interrupted because there are too many " "#ifdef configurations. Checking of all #ifdef configurations can be forced " "by --force command line option or from GUI preferences. However that may " "increase the checking time."; if (file.empty()) msg << " For more details, use --enable=information."; ErrorMessage errmsg(loclist, emptyString, Severity::information, msg.str(), "toomanyconfigs", CWE398, Certainty::normal); reportErr(errmsg); } void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration) { mTooManyConfigs = false; if (mSettings.severity.isEnabled(Severity::information) && file.empty()) return; std::list loclist; if (!file.empty()) { ErrorMessage::FileLocation location; location.setfile(file); loclist.push_back(location); } ErrorMessage errmsg(loclist, emptyString, Severity::information, "The configuration '" + configuration + "' was not checked because its code equals another one.", "purgedConfiguration", Certainty::normal); reportErr(errmsg); } //--------------------------------------------------------------------------- void CppCheck::reportErr(const ErrorMessage &msg) { mSuppressInternalErrorFound = false; if (!mSettings.library.reportErrors(msg.file0)) return; const std::string errmsg = msg.toString(mSettings.verbose); if (errmsg.empty()) return; // Alert only about unique errors if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) != mErrorList.end()) return; mAnalyzerInformation.reportErr(msg, mSettings.verbose); const Suppressions::ErrorMessage errorMessage = msg.toSuppressionsErrorMessage(); if (mUseGlobalSuppressions) { if (mSettings.nomsg.isSuppressed(errorMessage)) { mSuppressInternalErrorFound = true; return; } } else { if (mSettings.nomsg.isSuppressedLocal(errorMessage)) { mSuppressInternalErrorFound = true; return; } } if (!mSettings.nofail.isSuppressed(errorMessage) && !mSettings.nomsg.isSuppressed(errorMessage)) { mExitCode = 1; } mErrorList.push_back(errmsg); mErrorLogger.reportErr(msg); if (!mSettings.plistOutput.empty() && plistFile.is_open()) { plistFile << ErrorLogger::plistData(msg); } } void CppCheck::reportOut(const std::string &outmsg, Color c) { mErrorLogger.reportOut(outmsg, c); } void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value) { mErrorLogger.reportProgress(filename, stage, value); } void CppCheck::reportInfo(const ErrorMessage &msg) { const Suppressions::ErrorMessage &errorMessage = msg.toSuppressionsErrorMessage(); if (!mSettings.nomsg.isSuppressed(errorMessage)) mErrorLogger.reportInfo(msg); } void CppCheck::reportStatus(unsigned int /*fileindex*/, unsigned int /*filecount*/, std::size_t /*sizedone*/, std::size_t /*sizetotal*/) {} void CppCheck::bughuntingReport(const std::string &str) { mErrorLogger.bughuntingReport(str); } void CppCheck::getErrorMessages() { Settings s(mSettings); s.severity.enable(Severity::warning); s.severity.enable(Severity::style); s.severity.enable(Severity::portability); s.severity.enable(Severity::performance); s.severity.enable(Severity::information); purgedConfigurationMessage("",""); mTooManyConfigs = true; tooManyConfigsError("",0U); // call all "getErrorMessages" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) (*it)->getErrorMessages(this, &s); Preprocessor::getErrorMessages(this, &s); } void CppCheck::analyseClangTidy(const ImportProject::FileSettings &fileSettings) { std::string allIncludes; for (const std::string &inc : fileSettings.includePaths) { allIncludes = allIncludes + "-I\"" + inc + "\" "; } const std::string allDefines = getDefinesFlags(fileSettings.defines); #ifdef _WIN32 const char exe[] = "clang-tidy.exe"; #else const char exe[] = "clang-tidy"; #endif const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines; std::string output; if (!mExecuteCommand(exe, split(args), "", &output)) { std::cerr << "Failed to execute '" << exe << "'" << std::endl; return; } // parse output and create error messages std::istringstream istr(output); std::string line; if (!mSettings.buildDir.empty()) { const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename, ""); std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd"); fcmd << istr.str(); } while (std::getline(istr, line)) { if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos) continue; std::size_t endColumnPos = line.find(": error:"); if (endColumnPos == std::string::npos) { endColumnPos = line.find(": warning:"); } const std::size_t endLinePos = line.rfind(":", endColumnPos-1); const std::size_t endNamePos = line.rfind(":", endLinePos - 1); const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2); const std::size_t endErrorPos = line.rfind('[', std::string::npos); if (endLinePos==std::string::npos || endNamePos==std::string::npos || endMsgTypePos==std::string::npos || endErrorPos==std::string::npos) continue; const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1); const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1); const std::string errorTypeString = line.substr(endColumnPos + 1, endMsgTypePos - endColumnPos - 1); const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1); const std::string errorString = line.substr(endErrorPos, line.length()); std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos)); const int64_t lineNumber = std::atol(lineNumString.c_str()); const int64_t column = std::atol(columnNumString.c_str()); fixedpath = Path::toNativeSeparators(fixedpath); ErrorMessage errmsg; errmsg.callStack.emplace_back(ErrorMessage::FileLocation(fixedpath, lineNumber, column)); errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2); if (errmsg.id.find("performance") != std::string::npos) errmsg.severity = Severity::SeverityType::performance; else if (errmsg.id.find("portability") != std::string::npos) errmsg.severity = Severity::SeverityType::portability; else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos) errmsg.severity = Severity::SeverityType::warning; else errmsg.severity = Severity::SeverityType::style; errmsg.file0 = fixedpath; errmsg.setmsg(messageString); reportErr(errmsg); } } bool CppCheck::analyseWholeProgram() { bool errors = false; // Init CTU CTU::maxCtuDepth = mSettings.maxCtuDepth; // Analyse the tokens CTU::FileInfo ctu; for (const Check::FileInfo *fi : mFileInfo) { const CTU::FileInfo *fi2 = dynamic_cast(fi); if (fi2) { ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.begin(), fi2->functionCalls.end()); ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.begin(), fi2->nestedCalls.end()); } } for (Check *check : Check::instances()) errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this); // TODO: ctu return errors && (mExitCode > 0); } void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map &files) { executeAddonsWholeProgram(files); if (buildDir.empty()) return; if (mSettings.checks.isEnabled(Checks::unusedFunction)) CheckUnusedFunctions::analyseWholeProgram(this, buildDir); std::list fileInfoList; CTU::FileInfo ctuFileInfo; // Load all analyzer info data.. const std::string filesTxt(buildDir + "/files.txt"); std::ifstream fin(filesTxt); std::string filesTxtLine; while (std::getline(fin, filesTxtLine)) { const std::string::size_type firstColon = filesTxtLine.find(':'); if (firstColon == std::string::npos) continue; const std::string::size_type lastColon = filesTxtLine.rfind(':'); if (firstColon == lastColon) continue; const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); //const std::string sourcefile = filesTxtLine.substr(lastColon+1); tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); if (error != tinyxml2::XML_SUCCESS) continue; const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); if (rootNode == nullptr) continue; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "FileInfo") != 0) continue; const char *checkClassAttr = e->Attribute("check"); if (!checkClassAttr) continue; if (std::strcmp(checkClassAttr, "ctu") == 0) { ctuFileInfo.loadFromXml(e); continue; } for (Check *check : Check::instances()) { if (checkClassAttr == check->name()) fileInfoList.push_back(check->loadFileInfoFromXml(e)); } } } // Set CTU max depth CTU::maxCtuDepth = mSettings.maxCtuDepth; // Analyse the tokens for (Check *check : Check::instances()) check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this); for (Check::FileInfo *fi : fileInfoList) delete fi; } bool CppCheck::isUnusedFunctionCheckEnabled() const { return (mSettings.jobs == 1 && mSettings.checks.isEnabled(Checks::unusedFunction)); } cppcheck-2.7/lib/cppcheck.h000066400000000000000000000173021417746362400156620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef cppcheckH #define cppcheckH //--------------------------------------------------------------------------- #include "analyzerinfo.h" #include "check.h" #include "color.h" #include "config.h" #include "errorlogger.h" #include "importproject.h" #include "settings.h" #include #include #include #include #include #include #include class Tokenizer; /// @addtogroup Core /// @{ /** * @brief This is the base class which will use other classes to do * static code analysis for C and C++ code to find possible * errors or places that could be improved. * Usage: See check() for more info. */ class CPPCHECKLIB CppCheck : ErrorLogger { public: /** * @brief Constructor. */ CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions, std::function,std::string,std::string*)> executeCommand); /** * @brief Destructor. */ ~CppCheck() OVERRIDE; /** * @brief This starts the actual checking. Note that you must call * parseFromArgs() or settings() and addFile() before calling this. * @return amount of errors found or 0 if none were found. */ /** * @brief Check the file. * This function checks one given file for errors. * @param path Path to the file to check. * @return amount of errors found or 0 if none were found. * @note You must set settings before calling this function (by calling * settings()). */ unsigned int check(const std::string &path); unsigned int check(const ImportProject::FileSettings &fs); /** * @brief Check the file. * This function checks one "virtual" file. The file is not read from * the disk but the content is given in @p content. In errors the @p path * is used as a filename. * @param path Path to the file to check. * @param content File content as a string. * @return amount of errors found or 0 if none were found. * @note You must set settings before calling this function (by calling * settings()). */ unsigned int check(const std::string &path, const std::string &content); /** * @brief Get reference to current settings. * @return a reference to current settings */ Settings &settings(); /** * @brief Returns current version number as a string. * @return version, e.g. "1.38" */ static const char * version(); /** * @brief Returns extra version info as a string. * This is for returning extra version info, like Git commit id, build * time/date etc. * @return extra version info, e.g. "04d42151" (Git commit id). */ static const char * extraVersion(); virtual void reportStatus(unsigned int fileindex, unsigned int filecount, std::size_t sizedone, std::size_t sizetotal); /** * @brief Call all "getErrorMessages" in all registered Check classes. * Also print out XML header and footer. */ void getErrorMessages(); void tooManyConfigsError(const std::string &file, const int numberOfConfigurations); void purgedConfigurationMessage(const std::string &file, const std::string& configuration); void dontSimplify() { mSimplify = false; } /** Analyse whole program, run this after all TUs has been scanned. * This is deprecated and the plan is to remove this when * .analyzeinfo is good enough. * Return true if an error is reported. */ bool analyseWholeProgram(); /** Analyze all files using clang-tidy */ void analyseClangTidy(const ImportProject::FileSettings &fileSettings); /** analyse whole program use .analyzeinfo files */ void analyseWholeProgram(const std::string &buildDir, const std::map &files); /** Check if the user wants to check for unused functions * and if it's possible at all */ bool isUnusedFunctionCheckEnabled() const; private: /** Are there "simple" rules */ bool hasRule(const std::string &tokenlist) const; /** @brief There has been an internal error => Report information message */ void internalError(const std::string &filename, const std::string &msg); /** * @brief Check a file using stream * @param filename file name * @param cfgname cfg name * @param fileStream stream the file content can be read from * @return number of errors found */ unsigned int checkFile(const std::string& filename, const std::string &cfgname, std::istream& fileStream); /** * @brief Check raw tokens * @param tokenizer tokenizer instance */ void checkRawTokens(const Tokenizer &tokenizer); /** * @brief Check normal tokens * @param tokenizer tokenizer instance */ void checkNormalTokens(const Tokenizer &tokenizer); /** * Execute addons */ void executeAddons(const std::vector& files); void executeAddons(const std::string &dumpFile); /** * Execute addons */ void executeAddonsWholeProgram(const std::map &files); /** * @brief Execute rules, if any * @param tokenlist token list to use (normal / simple) * @param tokenizer tokenizer */ void executeRules(const std::string &tokenlist, const Tokenizer &tokenizer); /** * @brief Errors and warnings are directed here. * * @param msg Errors messages are normally in format * "[filepath:line number] Message", e.g. * "[main.cpp:4] Uninitialized member variable" */ void reportErr(const ErrorMessage &msg) OVERRIDE; /** * @brief Information about progress is directed here. * * @param outmsg Message to show, e.g. "Checking main.cpp..." */ void reportOut(const std::string &outmsg, Color c = Color::Reset) OVERRIDE; void bughuntingReport(const std::string &str) OVERRIDE; std::list mErrorList; Settings mSettings; void reportProgress(const std::string &filename, const char stage[], const std::size_t value) OVERRIDE; /** * Output information messages. */ void reportInfo(const ErrorMessage &msg) OVERRIDE; ErrorLogger &mErrorLogger; /** @brief Current preprocessor configuration */ std::string mCurrentConfig; unsigned int mExitCode; bool mSuppressInternalErrorFound; bool mUseGlobalSuppressions; /** Are there too many configs? */ bool mTooManyConfigs; /** Simplify code? true by default */ bool mSimplify; /** File info used for whole program analysis */ std::list mFileInfo; AnalyzerInformation mAnalyzerInformation; /** Callback for executing a shell command (exe, args, output) */ std::function,std::string,std::string*)> mExecuteCommand; }; /// @} //--------------------------------------------------------------------------- #endif // cppcheckH cppcheck-2.7/lib/cppcheck.natvis000066400000000000000000000043451417746362400167420ustar00rootroot00000000000000 {mStr} {front->mStr} - {back->mStr} {mTokensFrontBack.front->mStr} - {mTokensFrontBack.back->mStr} mFiles pCurr pCurr = pCurr->mNext {mNameToken->mStr} {tokenDef->mStr} {num} ? {type}: {className} {platformType} {files[fileIndex]} : {line} [col={col}] {intvalue} (INT) {tokvalue} (TOK) {floatValue} (FLOAT) [UNKNOWN] {name} = {value} (value_known=true) {name} = ? cppcheck-2.7/lib/cppcheck.vcxproj000066400000000000000000001045131417746362400171270ustar00rootroot00000000000000 Debug-PCRE Win32 Debug-PCRE x64 Debug Win32 Debug x64 Release-PCRE Win32 Release-PCRE x64 Release Win32 Release x64 Create Create Create Create Create Create Create Create {C183DB5B-AD6C-423D-80CA-1F9549555A1A} cppcheck_lib 10.0 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 $(SolutionDir)bin\debug\ $(SolutionDir)bin\debug\ $(SolutionDir)bin\debug\ $(SolutionDir)bin\debug\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck-core cppcheck-core cppcheck-core cppcheck-core false false false false $(SolutionDir)bin\ $(SolutionDir)bin\ $(SolutionDir)bin\ $(SolutionDir)bin\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck-core cppcheck-core cppcheck-core cppcheck-core false false false false true true true true ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 MultiThreadedDebugDLL Use precompiled.h precompiled.h true stdcpp14 /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) ../externals;%(AdditionalLibraryDirectories) true true false true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 MultiThreadedDebugDLL Use precompiled.h precompiled.h true stdcpplatest /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) pcre.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 MultiThreadedDebugDLL Use precompiled.h precompiled.h true stdcpp14 /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) ../externals;%(AdditionalLibraryDirectories) true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 MultiThreadedDebugDLL Use precompiled.h precompiled.h true stdcpplatest /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) pcre64.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDLL true false Use precompiled.h precompiled.h /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpp14 ../externals;%(AdditionalLibraryDirectories) false true true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) MultiThreadedDLL true false Use precompiled.h precompiled.h /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpplatest pcre.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false true true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true false Use precompiled.h precompiled.h /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpp14 ../externals;%(AdditionalLibraryDirectories) true true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) 4018;4146;4127;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true false Use precompiled.h precompiled.h /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpplatest pcre64.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y cppcheck-2.7/lib/cppcheck.vcxproj.filters000066400000000000000000000306101417746362400205720ustar00rootroot00000000000000 {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files cppcheck-2.7/lib/ctu.cpp000066400000000000000000000624511417746362400152350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "ctu.h" #include "astutils.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include // back_inserter #include #include #include //--------------------------------------------------------------------------- static const char ATTR_CALL_ID[] = "call-id"; static const char ATTR_CALL_FUNCNAME[] = "call-funcname"; static const char ATTR_CALL_ARGNR[] = "call-argnr"; static const char ATTR_CALL_ARGEXPR[] = "call-argexpr"; static const char ATTR_CALL_ARGVALUETYPE[] = "call-argvaluetype"; static const char ATTR_CALL_ARGVALUE[] = "call-argvalue"; static const char ATTR_WARNING[] = "warning"; static const char ATTR_LOC_FILENAME[] = "file"; static const char ATTR_LOC_LINENR[] = "line"; static const char ATTR_LOC_COLUMN[] = "col"; static const char ATTR_INFO[] = "info"; static const char ATTR_MY_ID[] = "my-id"; static const char ATTR_MY_ARGNR[] = "my-argnr"; static const char ATTR_MY_ARGNAME[] = "my-argname"; static const char ATTR_VALUE[] = "value"; int CTU::maxCtuDepth = 2; std::string CTU::getFunctionId(const Tokenizer *tokenizer, const Function *function) { return tokenizer->list.file(function->tokenDef) + ':' + MathLib::toString(function->tokenDef->linenr()) + ':' + MathLib::toString(function->tokenDef->column()); } CTU::FileInfo::Location::Location(const Tokenizer *tokenizer, const Token *tok) : fileName(tokenizer->list.file(tok)) , lineNumber(tok->linenr()) , column(tok->column()) {} std::string CTU::FileInfo::toString() const { std::ostringstream out; // Function calls.. for (const CTU::FileInfo::FunctionCall &functionCall : functionCalls) { out << functionCall.toXmlString(); } // Nested calls.. for (const CTU::FileInfo::NestedCall &nestedCall : nestedCalls) { out << nestedCall.toXmlString() << "\n"; } return out.str(); } std::string CTU::FileInfo::CallBase::toBaseXmlString() const { std::ostringstream out; out << " " << ATTR_CALL_ID << "=\"" << callId << "\"" << " " << ATTR_CALL_FUNCNAME << "=\"" << ErrorLogger::toxml(callFunctionName) << "\"" << " " << ATTR_CALL_ARGNR << "=\"" << callArgNr << "\"" << " " << ATTR_LOC_FILENAME << "=\"" << ErrorLogger::toxml(location.fileName) << "\"" << " " << ATTR_LOC_LINENR << "=\"" << location.lineNumber << "\"" << " " << ATTR_LOC_COLUMN << "=\"" << location.column << "\""; return out.str(); } std::string CTU::FileInfo::FunctionCall::toXmlString() const { std::ostringstream out; out << "(callValueType) << "\"" << " " << ATTR_CALL_ARGVALUE << "=\"" << callArgValue << "\""; if (warning) out << " " << ATTR_WARNING << "=\"true\""; if (callValuePath.empty()) out << "/>"; else { out << ">\n"; for (const ErrorMessage::FileLocation &loc : callValuePath) out << " \n"; out << ""; } return out.str(); } std::string CTU::FileInfo::NestedCall::toXmlString() const { std::ostringstream out; out << ""; return out.str(); } std::string CTU::FileInfo::UnsafeUsage::toString() const { std::ostringstream out; out << " \n"; return out.str(); } std::string CTU::toString(const std::list &unsafeUsage) { std::ostringstream ret; for (const CTU::FileInfo::UnsafeUsage &u : unsafeUsage) ret << u.toString(); return ret.str(); } CTU::FileInfo::CallBase::CallBase(const Tokenizer *tokenizer, const Token *callToken) : callId(getFunctionId(tokenizer, callToken->function())) , callArgNr(0) , callFunctionName(callToken->next()->astOperand1()->expressionString()) , location(CTU::FileInfo::Location(tokenizer, callToken)) {} CTU::FileInfo::NestedCall::NestedCall(const Tokenizer *tokenizer, const Function *myFunction, const Token *callToken) : CallBase(tokenizer, callToken) , myId(getFunctionId(tokenizer, myFunction)) , myArgNr(0) {} static std::string readAttrString(const tinyxml2::XMLElement *e, const char *attr, bool *error) { const char *value = e->Attribute(attr); if (!value && error) *error = true; return value ? value : ""; } static long long readAttrInt(const tinyxml2::XMLElement *e, const char *attr, bool *error) { int64_t value = 0; bool err = (e->QueryInt64Attribute(attr, &value) != tinyxml2::XML_SUCCESS); if (error) *error = err; return value; } bool CTU::FileInfo::CallBase::loadBaseFromXml(const tinyxml2::XMLElement *xmlElement) { bool error = false; callId = readAttrString(xmlElement, ATTR_CALL_ID, &error); callFunctionName = readAttrString(xmlElement, ATTR_CALL_FUNCNAME, &error); callArgNr = readAttrInt(xmlElement, ATTR_CALL_ARGNR, &error); location.fileName = readAttrString(xmlElement, ATTR_LOC_FILENAME, &error); location.lineNumber = readAttrInt(xmlElement, ATTR_LOC_LINENR, &error); location.column = readAttrInt(xmlElement, ATTR_LOC_COLUMN, &error); return !error; } bool CTU::FileInfo::FunctionCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) { if (!loadBaseFromXml(xmlElement)) return false; bool error=false; callArgumentExpression = readAttrString(xmlElement, ATTR_CALL_ARGEXPR, &error); callValueType = (ValueFlow::Value::ValueType)readAttrInt(xmlElement, ATTR_CALL_ARGVALUETYPE, &error); callArgValue = readAttrInt(xmlElement, ATTR_CALL_ARGVALUE, &error); const char *w = xmlElement->Attribute(ATTR_WARNING); warning = w && std::strcmp(w, "true") == 0; for (const tinyxml2::XMLElement *e2 = xmlElement->FirstChildElement(); !error && e2; e2 = e2->NextSiblingElement()) { if (std::strcmp(e2->Name(), "path") != 0) continue; ErrorMessage::FileLocation loc; loc.setfile(readAttrString(e2, ATTR_LOC_FILENAME, &error)); loc.line = readAttrInt(e2, ATTR_LOC_LINENR, &error); loc.column = readAttrInt(e2, ATTR_LOC_COLUMN, &error); loc.setinfo(readAttrString(e2, ATTR_INFO, &error)); } return !error; } bool CTU::FileInfo::NestedCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) { if (!loadBaseFromXml(xmlElement)) return false; bool error = false; myId = readAttrString(xmlElement, ATTR_MY_ID, &error); myArgNr = readAttrInt(xmlElement, ATTR_MY_ARGNR, &error); return !error; } void CTU::FileInfo::loadFromXml(const tinyxml2::XMLElement *xmlElement) { for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "function-call") == 0) { FunctionCall functionCall; if (functionCall.loadFromXml(e)) functionCalls.push_back(functionCall); } else if (std::strcmp(e->Name(), "nested-call") == 0) { NestedCall nestedCall; if (nestedCall.loadFromXml(e)) nestedCalls.push_back(nestedCall); } } } std::map> CTU::FileInfo::getCallsMap() const { std::map> ret; for (const CTU::FileInfo::NestedCall &nc : nestedCalls) ret[nc.callId].push_back(&nc); for (const CTU::FileInfo::FunctionCall &fc : functionCalls) ret[fc.callId].push_back(&fc); return ret; } std::list CTU::loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement) { std::list ret; for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "unsafe-usage") != 0) continue; bool error = false; FileInfo::UnsafeUsage unsafeUsage; unsafeUsage.myId = readAttrString(e, ATTR_MY_ID, &error); unsafeUsage.myArgNr = readAttrInt(e, ATTR_MY_ARGNR, &error); unsafeUsage.myArgumentName = readAttrString(e, ATTR_MY_ARGNAME, &error); unsafeUsage.location.fileName = readAttrString(e, ATTR_LOC_FILENAME, &error); unsafeUsage.location.lineNumber = readAttrInt(e, ATTR_LOC_LINENR, &error); unsafeUsage.location.column = readAttrInt(e, ATTR_LOC_COLUMN, &error); unsafeUsage.value = readAttrInt(e, ATTR_VALUE, &error); if (!error) ret.push_back(unsafeUsage); } return ret; } static int isCallFunction(const Scope *scope, int argnr, const Token **tok) { const Variable * const argvar = scope->function->getArgumentVar(argnr); if (!argvar->isPointer()) return -1; for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (tok2->variable() != argvar) continue; if (!Token::Match(tok2->previous(), "[(,] %var% [,)]")) break; int argnr2 = 1; const Token *prev = tok2; while (prev && prev->str() != "(") { if (Token::Match(prev,"]|)")) prev = prev->link(); else if (prev->str() == ",") ++argnr2; prev = prev->previous(); } if (!prev || !Token::Match(prev->previous(), "%name% (")) break; if (!prev->astOperand1() || !prev->astOperand1()->function()) break; *tok = prev->previous(); return argnr2; } return -1; } CTU::FileInfo *CTU::getFileInfo(const Tokenizer *tokenizer) { const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); FileInfo *fileInfo = new FileInfo; // Parse all functions in TU for (const Scope &scope : symbolDatabase->scopeList) { if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) continue; const Function *const scopeFunction = scope.function; // source function calls for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand2()) continue; const Function* tokFunction = tok->astOperand1()->function(); if (!tokFunction) continue; const std::vector args(getArguments(tok->previous())); for (int argnr = 0; argnr < args.size(); ++argnr) { const Token *argtok = args[argnr]; if (!argtok) continue; for (const ValueFlow::Value &value : argtok->values()) { if ((!value.isIntValue() || value.intvalue != 0 || value.isInconclusive()) && !value.isBufferSizeValue()) continue; // Skip impossible values since they cannot be represented if (value.isImpossible()) continue; FileInfo::FunctionCall functionCall; functionCall.callValueType = value.valueType; functionCall.callId = getFunctionId(tokenizer, tokFunction); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer,tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.callArgValue = value.intvalue; functionCall.warning = !value.errorSeverity(); for (const ErrorPathItem &i : value.errorPath) { ErrorMessage::FileLocation loc; loc.setfile(tokenizer->list.file(i.first)); loc.line = i.first->linenr(); loc.column = i.first->column(); loc.setinfo(i.second); functionCall.callValuePath.push_back(loc); } fileInfo->functionCalls.push_back(functionCall); } // array if (argtok->variable() && argtok->variable()->isArray() && argtok->variable()->dimensions().size()==1 && argtok->variable()->dimension(0)>1) { FileInfo::FunctionCall functionCall; functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; functionCall.callId = getFunctionId(tokenizer, tokFunction); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.callArgValue = argtok->variable()->dimension(0) * argtok->valueType()->typeSize(*tokenizer->getSettings()); functionCall.warning = false; fileInfo->functionCalls.push_back(functionCall); } // &var => buffer if (argtok->isUnaryOp("&") && argtok->astOperand1()->variable() && argtok->astOperand1()->valueType() && !argtok->astOperand1()->variable()->isArray()) { FileInfo::FunctionCall functionCall; functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; functionCall.callId = getFunctionId(tokenizer, tokFunction); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.callArgValue = argtok->astOperand1()->valueType()->typeSize(*tokenizer->getSettings()); functionCall.warning = false; fileInfo->functionCalls.push_back(functionCall); } // pointer/reference to uninitialized data auto isAddressOfArg = [](const Token* argtok) -> const Token* { if (!argtok->isUnaryOp("&")) return nullptr; argtok = argtok->astOperand1(); if (!argtok || !argtok->valueType() || argtok->valueType()->pointer != 0) return nullptr; return argtok; }; auto isReferenceArg = [&](const Token* argtok) -> const Token* { const Variable* argvar = tokFunction->getArgumentVar(argnr); if (!argvar || !argvar->valueType() || argvar->valueType()->reference == Reference::None) return nullptr; return argtok; }; const Token* addr = isAddressOfArg(argtok); argtok = addr ? addr : isReferenceArg(argtok); if (!argtok || argtok->values().size() != 1U) continue; const ValueFlow::Value &v = argtok->values().front(); if (v.valueType == ValueFlow::Value::ValueType::UNINIT && !v.isInconclusive()) { FileInfo::FunctionCall functionCall; functionCall.callValueType = ValueFlow::Value::ValueType::UNINIT; functionCall.callId = getFunctionId(tokenizer, tokFunction); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgValue = 0; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.warning = false; fileInfo->functionCalls.push_back(functionCall); continue; } } } // Nested function calls for (int argnr = 0; argnr < scopeFunction->argCount(); ++argnr) { const Token *tok; const int argnr2 = isCallFunction(&scope, argnr, &tok); if (argnr2 > 0) { FileInfo::NestedCall nestedCall(tokenizer, scopeFunction, tok); nestedCall.myArgNr = argnr + 1; nestedCall.callArgNr = argnr2; fileInfo->nestedCalls.push_back(nestedCall); } } } return fileInfo; } static std::list> getUnsafeFunction(const Tokenizer *tokenizer, const Settings *settings, const Scope *scope, int argnr, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *value)) { std::list> ret; const Variable * const argvar = scope->function->getArgumentVar(argnr); if (!argvar->isPointer() && !argvar->isReference()) return ret; for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (Token::Match(tok2, ")|else {")) { tok2 = tok2->linkAt(1); if (Token::findmatch(tok2->link(), "return|throw", tok2)) return ret; int indirect = 0; if (argvar->valueType()) indirect = argvar->valueType()->pointer; if (isVariableChanged(tok2->link(), tok2, indirect, argvar->declarationId(), false, settings, tokenizer->isCPP())) return ret; } if (Token::Match(tok2, "%oror%|&&|?")) { tok2 = tok2->findExpressionStartEndTokens().second; continue; } if (tok2->variable() != argvar) continue; MathLib::bigint value = 0; if (!isUnsafeUsage(check, tok2, &value)) return ret; // TODO: Is this a read? then continue.. ret.emplace_back(tok2, value); return ret; } return ret; } std::list CTU::getUnsafeUsage(const Tokenizer *tokenizer, const Settings *settings, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *value)) { std::list unsafeUsage; // Parse all functions in TU const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) continue; const Function *const function = scope.function; // "Unsafe" functions unconditionally reads data before it is written.. for (int argnr = 0; argnr < function->argCount(); ++argnr) { for (const std::pair &v : getUnsafeFunction(tokenizer, settings, &scope, argnr, check, isUnsafeUsage)) { const Token *tok = v.first; MathLib::bigint val = v.second; unsafeUsage.emplace_back(CTU::getFunctionId(tokenizer, function), argnr+1, tok->str(), CTU::FileInfo::Location(tokenizer,tok), val); } } } return unsafeUsage; } static bool findPath(const std::string &callId, nonneg int callArgNr, MathLib::bigint unsafeValue, CTU::FileInfo::InvalidValueType invalidValue, const std::map> &callsMap, const CTU::FileInfo::CallBase *path[10], int index, bool warning) { if (index >= CTU::maxCtuDepth || index >= 10) return false; const std::map>::const_iterator it = callsMap.find(callId); if (it == callsMap.end()) return false; for (const CTU::FileInfo::CallBase *c : it->second) { if (c->callArgNr != callArgNr) continue; const CTU::FileInfo::FunctionCall *functionCall = dynamic_cast(c); if (functionCall) { if (!warning && functionCall->warning) continue; switch (invalidValue) { case CTU::FileInfo::InvalidValueType::null: if (functionCall->callValueType != ValueFlow::Value::ValueType::INT || functionCall->callArgValue != 0) continue; break; case CTU::FileInfo::InvalidValueType::uninit: if (functionCall->callValueType != ValueFlow::Value::ValueType::UNINIT) continue; break; case CTU::FileInfo::InvalidValueType::bufferOverflow: if (functionCall->callValueType != ValueFlow::Value::ValueType::BUFFER_SIZE) continue; if (unsafeValue < 0 || unsafeValue >= functionCall->callArgValue) break; continue; } path[index] = functionCall; return true; } const CTU::FileInfo::NestedCall *nestedCall = dynamic_cast(c); if (!nestedCall) continue; if (findPath(nestedCall->myId, nestedCall->myArgNr, unsafeValue, invalidValue, callsMap, path, index + 1, warning)) { path[index] = nestedCall; return true; } } return false; } std::list CTU::FileInfo::getErrorPath(InvalidValueType invalidValue, const CTU::FileInfo::UnsafeUsage &unsafeUsage, const std::map> &callsMap, const char info[], const FunctionCall ** const functionCallPtr, bool warning) { std::list locationList; const CTU::FileInfo::CallBase *path[10] = {nullptr}; if (!findPath(unsafeUsage.myId, unsafeUsage.myArgNr, unsafeUsage.value, invalidValue, callsMap, path, 0, warning)) return locationList; const std::string value1 = (invalidValue == InvalidValueType::null) ? "null" : "uninitialized"; for (int index = 9; index >= 0; index--) { if (!path[index]) continue; const CTU::FileInfo::FunctionCall *functionCall = dynamic_cast(path[index]); if (functionCall) { if (functionCallPtr) *functionCallPtr = functionCall; std::copy(functionCall->callValuePath.cbegin(), functionCall->callValuePath.cend(), std::back_inserter(locationList)); } ErrorMessage::FileLocation fileLoc(path[index]->location.fileName, path[index]->location.lineNumber, path[index]->location.column); fileLoc.setinfo("Calling function " + path[index]->callFunctionName + ", " + MathLib::toString(path[index]->callArgNr) + getOrdinalText(path[index]->callArgNr) + " argument is " + value1); locationList.push_back(fileLoc); } ErrorMessage::FileLocation fileLoc2(unsafeUsage.location.fileName, unsafeUsage.location.lineNumber, unsafeUsage.location.column); fileLoc2.setinfo(replaceStr(info, "ARG", unsafeUsage.myArgumentName)); locationList.push_back(fileLoc2); return locationList; } cppcheck-2.7/lib/ctu.h000066400000000000000000000135551417746362400147030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef ctuH #define ctuH //--------------------------------------------------------------------------- #include "config.h" #include "check.h" #include "errorlogger.h" #include "mathlib.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include class Function; class Settings; class Token; class Tokenizer; namespace tinyxml2 { class XMLElement; } /// @addtogroup Core /// @{ /** @brief Whole program analysis (ctu=Cross Translation Unit) */ namespace CTU { class CPPCHECKLIB FileInfo : public Check::FileInfo { public: enum class InvalidValueType { null, uninit, bufferOverflow }; std::string toString() const OVERRIDE; struct Location { Location() = default; Location(const Tokenizer *tokenizer, const Token *tok); Location(const std::string &fileName, nonneg int lineNumber, nonneg int column) : fileName(fileName), lineNumber(lineNumber), column(column) {} std::string fileName; nonneg int lineNumber; nonneg int column; }; struct UnsafeUsage { UnsafeUsage() = default; UnsafeUsage(const std::string &myId, nonneg int myArgNr, const std::string &myArgumentName, const Location &location, MathLib::bigint value) : myId(myId), myArgNr(myArgNr), myArgumentName(myArgumentName), location(location), value(value) {} std::string myId; nonneg int myArgNr; std::string myArgumentName; Location location; MathLib::bigint value; std::string toString() const; }; class CallBase { public: CallBase() = default; CallBase(const std::string &callId, int callArgNr, const std::string &callFunctionName, const Location &loc) : callId(callId), callArgNr(callArgNr), callFunctionName(callFunctionName), location(loc) {} CallBase(const Tokenizer *tokenizer, const Token *callToken); virtual ~CallBase() {} std::string callId; int callArgNr; std::string callFunctionName; Location location; protected: std::string toBaseXmlString() const; bool loadBaseFromXml(const tinyxml2::XMLElement *xmlElement); }; class FunctionCall : public CallBase { public: std::string callArgumentExpression; MathLib::bigint callArgValue; ValueFlow::Value::ValueType callValueType; std::vector callValuePath; bool warning; std::string toXmlString() const; bool loadFromXml(const tinyxml2::XMLElement *xmlElement); }; class NestedCall : public CallBase { public: NestedCall() = default; NestedCall(const std::string &myId, nonneg int myArgNr, const std::string &callId, nonneg int callArgnr, const std::string &callFunctionName, const Location &location) : CallBase(callId, callArgnr, callFunctionName, location), myId(myId), myArgNr(myArgNr) {} NestedCall(const Tokenizer *tokenizer, const Function *myFunction, const Token *callToken); std::string toXmlString() const; bool loadFromXml(const tinyxml2::XMLElement *xmlElement); std::string myId; nonneg int myArgNr; }; std::list functionCalls; std::list nestedCalls; void loadFromXml(const tinyxml2::XMLElement *xmlElement); std::map> getCallsMap() const; static std::list getErrorPath(InvalidValueType invalidValue, const UnsafeUsage &unsafeUsage, const std::map> &callsMap, const char info[], const FunctionCall ** const functionCallPtr, bool warning); }; extern int maxCtuDepth; CPPCHECKLIB std::string toString(const std::list &unsafeUsage); CPPCHECKLIB std::string getFunctionId(const Tokenizer *tokenizer, const Function *function); /** @brief Parse current TU and extract file info */ CPPCHECKLIB FileInfo *getFileInfo(const Tokenizer *tokenizer); CPPCHECKLIB std::list getUnsafeUsage(const Tokenizer *tokenizer, const Settings *settings, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *value)); CPPCHECKLIB std::list loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement); } /// @} //--------------------------------------------------------------------------- #endif // ctuH cppcheck-2.7/lib/errorlogger.cpp000066400000000000000000000765641417746362400170050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "errorlogger.h" #include "color.h" #include "cppcheck.h" #include "mathlib.h" #include "path.h" #include "token.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) : token(tok), errorMessage(errorMsg), type(type) { switch (type) { case AST: id = "internalAstError"; break; case SYNTAX: id = "syntaxError"; break; case UNKNOWN_MACRO: id = "unknownMacro"; break; case INTERNAL: id = "cppcheckError"; break; case LIMIT: id = "cppcheckLimit"; break; case INSTANTIATION: id = "instantiationError"; break; } } static std::size_t calculateWarningHash(const TokenList *tokenList, const std::string &msg) { if (!tokenList) return 0; return std::hash {}(msg + "\n" + tokenList->front()->stringifyList(false, true, false, false, false)); } ErrorMessage::ErrorMessage() : incomplete(false), severity(Severity::none), cwe(0U), certainty(Certainty::normal), hash(0) {} ErrorMessage::ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, Certainty::CertaintyLevel certainty) : callStack(callStack), // locations for this error message id(id), // set the message id file0(file1), incomplete(false), severity(severity), // severity for this error message cwe(0U), certainty(certainty), hash(0) { // set the summary and verbose messages setmsg(msg); } ErrorMessage::ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, const CWE &cwe, Certainty::CertaintyLevel certainty) : callStack(callStack), // locations for this error message id(id), // set the message id file0(file1), incomplete(false), severity(severity), // severity for this error message cwe(cwe.id), certainty(certainty), hash(0) { // set the summary and verbose messages setmsg(msg); } ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, Certainty::CertaintyLevel certainty) : id(id), incomplete(false), severity(severity), cwe(0U), certainty(certainty), hash(0) { // Format callstack for (std::list::const_iterator it = callstack.begin(); it != callstack.end(); ++it) { // --errorlist can provide null values here if (!(*it)) continue; callStack.emplace_back(*it, list); } if (list && !list->getFiles().empty()) file0 = list->getFiles()[0]; setmsg(msg); } ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, const CWE &cwe, Certainty::CertaintyLevel certainty, bool bugHunting) : id(id), incomplete(false), severity(severity), cwe(cwe.id), certainty(certainty) { // Format callstack for (const Token *tok: callstack) { // --errorlist can provide null values here if (!tok) continue; callStack.emplace_back(tok, list); } if (list && !list->getFiles().empty()) file0 = list->getFiles()[0]; setmsg(msg); std::ostringstream hashWarning; for (const Token *tok: callstack) hashWarning << std::hex << (tok ? tok->index() : 0) << " "; hashWarning << mShortMessage; hash = bugHunting ? calculateWarningHash(list, hashWarning.str()) : 0; } ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty, bool bugHunting) : id(id), incomplete(false), severity(severity), cwe(cwe.id), certainty(certainty) { // Format callstack for (const ErrorPathItem& e: errorPath) { const Token *tok = e.first; std::string info = e.second; if (info.compare(0,8,"$symbol:") == 0 && info.find("\n") < info.size()) { const std::string::size_type pos = info.find("\n"); const std::string &symbolName = info.substr(8, pos - 8); info = replaceStr(info.substr(pos+1), "$symbol", symbolName); } // --errorlist can provide null values here if (tok) callStack.emplace_back(tok, info, tokenList); } if (tokenList && !tokenList->getFiles().empty()) file0 = tokenList->getFiles()[0]; setmsg(msg); std::ostringstream hashWarning; for (const ErrorPathItem &e: errorPath) hashWarning << std::hex << (e.first ? e.first->index() : 0) << " "; hashWarning << mShortMessage; hash = bugHunting ? calculateWarningHash(tokenList, hashWarning.str()) : 0; } ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg) : incomplete(false), severity(Severity::none), cwe(0U), certainty(Certainty::normal) { const char * const unknown = ""; const char *attr = errmsg->Attribute("id"); id = attr ? attr : unknown; attr = errmsg->Attribute("severity"); severity = attr ? Severity::fromString(attr) : Severity::none; attr = errmsg->Attribute("cwe"); std::istringstream(attr ? attr : "0") >> cwe.id; attr = errmsg->Attribute("inconclusive"); certainty = (attr && (std::strcmp(attr, "true") == 0)) ? Certainty::inconclusive : Certainty::normal; attr = errmsg->Attribute("msg"); mShortMessage = attr ? attr : ""; attr = errmsg->Attribute("verbose"); mVerboseMessage = attr ? attr : ""; attr = errmsg->Attribute("hash"); std::istringstream(attr ? attr : "0") >> hash; for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(),"location")==0) { const char *strfile = e->Attribute("file"); const char *strinfo = e->Attribute("info"); const char *strline = e->Attribute("line"); const char *strcolumn = e->Attribute("column"); const char *file = strfile ? strfile : unknown; const char *info = strinfo ? strinfo : ""; const int line = strline ? std::atoi(strline) : 0; const int column = strcolumn ? std::atoi(strcolumn) : 0; callStack.emplace_front(file, info, line, column); } } } void ErrorMessage::setmsg(const std::string &msg) { // If a message ends to a '\n' and contains only a one '\n' // it will cause the mVerboseMessage to be empty which will show // as an empty message to the user if --verbose is used. // Even this doesn't cause problems with messages that have multiple // lines, none of the error messages should end into it. assert(!endsWith(msg,'\n')); // The summary and verbose message are separated by a newline // If there is no newline then both the summary and verbose messages // are the given message const std::string::size_type pos = msg.find('\n'); const std::string symbolName = mSymbolNames.empty() ? std::string() : mSymbolNames.substr(0, mSymbolNames.find('\n')); if (pos == std::string::npos) { mShortMessage = replaceStr(msg, "$symbol", symbolName); mVerboseMessage = replaceStr(msg, "$symbol", symbolName); } else if (msg.compare(0,8,"$symbol:") == 0) { mSymbolNames += msg.substr(8, pos-7); setmsg(msg.substr(pos + 1)); } else { mShortMessage = replaceStr(msg.substr(0, pos), "$symbol", symbolName); mVerboseMessage = replaceStr(msg.substr(pos + 1), "$symbol", symbolName); } } Suppressions::ErrorMessage ErrorMessage::toSuppressionsErrorMessage() const { Suppressions::ErrorMessage ret; ret.hash = hash; ret.errorId = id; if (!callStack.empty()) { ret.setFileName(callStack.back().getfile(false)); ret.lineNumber = callStack.back().line; } else { ret.lineNumber = Suppressions::Suppression::NO_LINE; } ret.certainty = certainty; ret.symbolNames = mSymbolNames; return ret; } std::string ErrorMessage::serialize() const { // Serialize this message into a simple string std::ostringstream oss; oss << id.length() << " " << id; oss << Severity::toString(severity).length() << " " << Severity::toString(severity); oss << MathLib::toString(cwe.id).length() << " " << MathLib::toString(cwe.id); oss << MathLib::toString(hash).length() << " " << MathLib::toString(hash); oss << file0.size() << " " << file0; if (certainty == Certainty::inconclusive) { const std::string text("inconclusive"); oss << text.length() << " " << text; } const std::string saneShortMessage = fixInvalidChars(mShortMessage); const std::string saneVerboseMessage = fixInvalidChars(mVerboseMessage); oss << saneShortMessage.length() << " " << saneShortMessage; oss << saneVerboseMessage.length() << " " << saneVerboseMessage; oss << callStack.size() << " "; for (std::list::const_iterator loc = callStack.begin(); loc != callStack.end(); ++loc) { std::ostringstream smallStream; smallStream << (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo(); oss << smallStream.str().length() << " " << smallStream.str(); } return oss.str(); } bool ErrorMessage::deserialize(const std::string &data) { certainty = Certainty::normal; callStack.clear(); std::istringstream iss(data); std::array results; std::size_t elem = 0; while (iss.good() && elem < 7) { unsigned int len = 0; if (!(iss >> len)) return false; iss.get(); std::string temp; for (unsigned int i = 0; i < len && iss.good(); ++i) { const char c = static_cast(iss.get()); temp.append(1, c); } if (temp == "inconclusive") { certainty = Certainty::inconclusive; continue; } results[elem++] = temp; } if (elem != 7) throw InternalError(nullptr, "Internal Error: Deserialization of error message failed"); id = results[0]; severity = Severity::fromString(results[1]); std::istringstream(results[2]) >> cwe.id; std::istringstream(results[3]) >> hash; std::istringstream(results[4]) >> file0; mShortMessage = results[5]; mVerboseMessage = results[6]; unsigned int stackSize = 0; if (!(iss >> stackSize)) return false; while (iss.good()) { unsigned int len = 0; if (!(iss >> len)) return false; iss.get(); std::string temp; for (unsigned int i = 0; i < len && iss.good(); ++i) { const char c = static_cast(iss.get()); temp.append(1, c); } std::vector substrings; for (std::string::size_type pos = 0; pos < temp.size() && substrings.size() < 5; ++pos) { if (substrings.size() == 4) { substrings.push_back(temp.substr(pos)); break; } const std::string::size_type start = pos; pos = temp.find("\t", pos); if (pos == std::string::npos) { substrings.push_back(temp.substr(start)); break; } substrings.push_back(temp.substr(start, pos - start)); } if (substrings.size() < 4) throw InternalError(nullptr, "Internal Error: serializing/deserializing of error message failed!"); // (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo(); ErrorMessage::FileLocation loc(substrings[3], MathLib::toLongNumber(substrings[0]), MathLib::toLongNumber(substrings[1])); loc.setfile(substrings[2]); if (substrings.size() == 5) loc.setinfo(substrings[4]); callStack.push_back(loc); if (callStack.size() >= stackSize) break; } return true; } std::string ErrorMessage::getXMLHeader() { tinyxml2::XMLPrinter printer; // standard xml header printer.PushDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""); // header printer.OpenElement("results", false); printer.PushAttribute("version", 2); printer.OpenElement("cppcheck", false); printer.PushAttribute("version", CppCheck::version()); printer.CloseElement(false); printer.OpenElement("errors", false); return std::string(printer.CStr()) + '>'; } std::string ErrorMessage::getXMLFooter() { return " \n"; } // There is no utf-8 support around but the strings should at least be safe for to tinyxml2. // See #5300 "Invalid encoding in XML output" and #6431 "Invalid XML created - Invalid encoding of string literal " std::string ErrorMessage::fixInvalidChars(const std::string& raw) { std::string result; result.reserve(raw.length()); std::string::const_iterator from=raw.begin(); while (from!=raw.end()) { if (std::isprint(static_cast(*from))) { result.push_back(*from); } else { std::ostringstream es; // straight cast to (unsigned) doesn't work out. const unsigned uFrom = (unsigned char)*from; es << '\\' << std::setbase(8) << std::setw(3) << std::setfill('0') << uFrom; result += es.str(); } ++from; } return result; } std::string ErrorMessage::toXML() const { tinyxml2::XMLPrinter printer(nullptr, false, 2); printer.OpenElement("error", false); printer.PushAttribute("id", id.c_str()); printer.PushAttribute("severity", Severity::toString(severity).c_str()); printer.PushAttribute("msg", fixInvalidChars(mShortMessage).c_str()); printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str()); if (cwe.id) printer.PushAttribute("cwe", cwe.id); if (hash) printer.PushAttribute("hash", MathLib::toString(hash).c_str()); if (certainty == Certainty::inconclusive) printer.PushAttribute("inconclusive", "true"); if (!file0.empty()) printer.PushAttribute("file0", file0.c_str()); for (std::list::const_reverse_iterator it = callStack.rbegin(); it != callStack.rend(); ++it) { printer.OpenElement("location", false); printer.PushAttribute("file", (*it).getfile().c_str()); printer.PushAttribute("line", std::max((*it).line,0)); printer.PushAttribute("column", (*it).column); if (!it->getinfo().empty()) printer.PushAttribute("info", fixInvalidChars(it->getinfo()).c_str()); printer.CloseElement(false); } for (std::string::size_type pos = 0; pos < mSymbolNames.size();) { const std::string::size_type pos2 = mSymbolNames.find('\n', pos); std::string symbolName; if (pos2 == std::string::npos) { symbolName = mSymbolNames.substr(pos); pos = pos2; } else { symbolName = mSymbolNames.substr(pos, pos2-pos); pos = pos2 + 1; } printer.OpenElement("symbol", false); printer.PushText(symbolName.c_str()); printer.CloseElement(false); } printer.CloseElement(false); return printer.CStr(); } /** * Replace all occurrences of searchFor with replaceWith in the * given source. * @param source The string to modify * @param searchFor What should be searched for * @param replaceWith What will replace the found item */ static void findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith) { std::string::size_type index = 0; while ((index = source.find(searchFor, index)) != std::string::npos) { source.replace(index, searchFor.length(), replaceWith); index += replaceWith.length(); } } // TODO: read info from some shared resource instead? static std::string readCode(const std::string &file, int linenr, int column, const char endl[]) { std::ifstream fin(file); std::string line; while (linenr > 0 && std::getline(fin,line)) { linenr--; } const std::string::size_type endPos = line.find_last_not_of("\r\n\t "); if (endPos + 1 < line.size()) line.erase(endPos + 1); std::string::size_type pos = 0; while ((pos = line.find('\t', pos)) != std::string::npos) line[pos] = ' '; return line + endl + std::string((column>0 ? column-1 : column), ' ') + '^'; } static void replaceColors(std::string& source) { findAndReplace(source, "{reset}", ::toString(Color::Reset)); findAndReplace(source, "{bold}", ::toString(Color::Bold)); findAndReplace(source, "{dim}", ::toString(Color::Dim)); findAndReplace(source, "{red}", ::toString(Color::FgRed)); findAndReplace(source, "{green}", ::toString(Color::FgGreen)); findAndReplace(source, "{blue}", ::toString(Color::FgBlue)); findAndReplace(source, "{magenta}", ::toString(Color::FgMagenta)); findAndReplace(source, "{default}", ::toString(Color::FgDefault)); } std::string ErrorMessage::toString(bool verbose, const std::string &templateFormat, const std::string &templateLocation) const { // Save this ErrorMessage in plain text. // No template is given if (templateFormat.empty()) { std::ostringstream text; if (!callStack.empty()) text << ErrorLogger::callStackToString(callStack) << ": "; if (severity != Severity::none) { text << '(' << Severity::toString(severity); if (certainty == Certainty::inconclusive) text << ", inconclusive"; text << ") "; } text << (verbose ? mVerboseMessage : mShortMessage); return text.str(); } // template is given. Reformat the output according to it std::string result = templateFormat; // Support a few special characters to allow to specific formatting, see http://sourceforge.net/apps/phpbb/cppcheck/viewtopic.php?f=4&t=494&sid=21715d362c0dbafd3791da4d9522f814 // Substitution should be done first so messages from cppcheck never get translated. findAndReplace(result, "\\b", "\b"); findAndReplace(result, "\\n", "\n"); findAndReplace(result, "\\r", "\r"); findAndReplace(result, "\\t", "\t"); replaceColors(result); findAndReplace(result, "{id}", id); std::string::size_type pos1 = result.find("{inconclusive:"); while (pos1 != std::string::npos) { const std::string::size_type pos2 = result.find('}', pos1+1); const std::string replaceFrom = result.substr(pos1,pos2-pos1+1); const std::string replaceWith = (certainty == Certainty::inconclusive) ? result.substr(pos1+14, pos2-pos1-14) : std::string(); findAndReplace(result, replaceFrom, replaceWith); pos1 = result.find("{inconclusive:", pos1); } findAndReplace(result, "{severity}", Severity::toString(severity)); findAndReplace(result, "{cwe}", MathLib::toString(cwe.id)); findAndReplace(result, "{message}", verbose ? mVerboseMessage : mShortMessage); findAndReplace(result, "{callstack}", callStack.empty() ? emptyString : ErrorLogger::callStackToString(callStack)); if (!callStack.empty()) { findAndReplace(result, "{file}", callStack.back().getfile()); findAndReplace(result, "{line}", MathLib::toString(callStack.back().line)); findAndReplace(result, "{column}", MathLib::toString(callStack.back().column)); if (result.find("{code}") != std::string::npos) { const std::string::size_type pos = result.find('\r'); const char *endl; if (pos == std::string::npos) endl = "\n"; else if (pos+1 < result.size() && result[pos+1] == '\n') endl = "\r\n"; else endl = "\r"; findAndReplace(result, "{code}", readCode(callStack.back().getOrigFile(), callStack.back().line, callStack.back().column, endl)); } } else { findAndReplace(result, "{file}", "nofile"); findAndReplace(result, "{line}", "0"); findAndReplace(result, "{column}", "0"); findAndReplace(result, "{code}", emptyString); } if (!templateLocation.empty() && callStack.size() >= 2U) { for (const FileLocation &fileLocation : callStack) { std::string text = templateLocation; findAndReplace(text, "\\b", "\b"); findAndReplace(text, "\\n", "\n"); findAndReplace(text, "\\r", "\r"); findAndReplace(text, "\\t", "\t"); replaceColors(text); findAndReplace(text, "{file}", fileLocation.getfile()); findAndReplace(text, "{line}", MathLib::toString(fileLocation.line)); findAndReplace(text, "{column}", MathLib::toString(fileLocation.column)); findAndReplace(text, "{info}", fileLocation.getinfo().empty() ? mShortMessage : fileLocation.getinfo()); if (text.find("{code}") != std::string::npos) { const std::string::size_type pos = text.find('\r'); const char *endl; if (pos == std::string::npos) endl = "\n"; else if (pos+1 < text.size() && text[pos+1] == '\n') endl = "\r\n"; else endl = "\r"; findAndReplace(text, "{code}", readCode(fileLocation.getOrigFile(), fileLocation.line, fileLocation.column, endl)); } result += '\n' + text; } } return result; } bool ErrorLogger::reportUnmatchedSuppressions(const std::list &unmatched) { bool err = false; // Report unmatched suppressions for (const Suppressions::Suppression &s : unmatched) { // don't report "unmatchedSuppression" as unmatched if (s.errorId == "unmatchedSuppression") continue; // check if this unmatched suppression is suppressed bool suppressed = false; for (const Suppressions::Suppression &s2 : unmatched) { if (s2.errorId == "unmatchedSuppression") { if ((s2.fileName.empty() || s2.fileName == "*" || s2.fileName == s.fileName) && (s2.lineNumber == Suppressions::Suppression::NO_LINE || s2.lineNumber == s.lineNumber)) { suppressed = true; break; } } } if (suppressed) continue; std::list callStack; if (!s.fileName.empty()) callStack.emplace_back(s.fileName, s.lineNumber, 0); reportErr(ErrorMessage(callStack, emptyString, Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal)); err = true; } return err; } std::string ErrorLogger::callStackToString(const std::list &callStack) { std::string str; for (std::list::const_iterator tok = callStack.begin(); tok != callStack.end(); ++tok) { str += (tok == callStack.begin() ? "" : " -> "); str += tok->stringify(); } return str; } ErrorMessage::FileLocation::FileLocation(const Token* tok, const TokenList* tokenList) : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)) {} ErrorMessage::FileLocation::FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList) : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)), mInfo(info) {} std::string ErrorMessage::FileLocation::getfile(bool convert) const { if (convert) return Path::toNativeSeparators(mFileName); return mFileName; } std::string ErrorMessage::FileLocation::getOrigFile(bool convert) const { if (convert) return Path::toNativeSeparators(mOrigFileName); return mOrigFileName; } void ErrorMessage::FileLocation::setfile(const std::string &file) { mFileName = file; mFileName = Path::fromNativeSeparators(mFileName); mFileName = Path::simplifyPath(mFileName); } std::string ErrorMessage::FileLocation::stringify() const { std::string str; str += '['; str += Path::toNativeSeparators(mFileName); if (line != Suppressions::Suppression::NO_LINE) { str += ':'; str += std::to_string(line); } str += ']'; return str; } std::string ErrorLogger::toxml(const std::string &str) { std::ostringstream xml; for (unsigned char c : str) { switch (c) { case '<': xml << "<"; break; case '>': xml << ">"; break; case '&': xml << "&"; break; case '\"': xml << """; break; case '\'': xml << "'"; break; case '\0': xml << "\\0"; break; default: if (c >= ' ' && c <= 0x7f) xml << c; else xml << 'x'; break; } } return xml.str(); } std::string ErrorLogger::plistHeader(const std::string &version, const std::vector &files) { std::ostringstream ostr; ostr << "\r\n" << "\r\n" << "\r\n" << "\r\n" << " clang_version\r\n" << "cppcheck version " << version << "\r\n" << " files\r\n" << " \r\n"; for (const std::string & file : files) ostr << " " << ErrorLogger::toxml(file) << "\r\n"; ostr << " \r\n" << " diagnostics\r\n" << " \r\n"; return ostr.str(); } static std::string plistLoc(const char indent[], const ErrorMessage::FileLocation &loc) { std::ostringstream ostr; ostr << indent << "\r\n" << indent << ' ' << "line" << loc.line << "\r\n" << indent << ' ' << "col" << loc.column << "\r\n" << indent << ' ' << "file" << loc.fileIndex << "\r\n" << indent << "\r\n"; return ostr.str(); } std::string ErrorLogger::plistData(const ErrorMessage &msg) { std::ostringstream plist; plist << " \r\n" << " path\r\n" << " \r\n"; std::list::const_iterator prev = msg.callStack.begin(); for (std::list::const_iterator it = msg.callStack.begin(); it != msg.callStack.end(); ++it) { if (prev != it) { plist << " \r\n" << " kindcontrol\r\n" << " edges\r\n" << " \r\n" << " \r\n" << " start\r\n" << " \r\n" << plistLoc(" ", *prev) << plistLoc(" ", *prev) << " \r\n" << " end\r\n" << " \r\n" << plistLoc(" ", *it) << plistLoc(" ", *it) << " \r\n" << " \r\n" << " \r\n" << " \r\n"; prev = it; } std::list::const_iterator next = it; ++next; const std::string message = (it->getinfo().empty() && next == msg.callStack.end() ? msg.shortMessage() : it->getinfo()); plist << " \r\n" << " kindevent\r\n" << " location\r\n" << plistLoc(" ", *it) << " ranges\r\n" << " \r\n" << " \r\n" << plistLoc(" ", *it) << plistLoc(" ", *it) << " \r\n" << " \r\n" << " depth0\r\n" << " extended_message\r\n" << " " << ErrorLogger::toxml(message) << "\r\n" << " message\r\n" << " " << ErrorLogger::toxml(message) << "\r\n" << " \r\n"; } plist << " \r\n" << " description" << ErrorLogger::toxml(msg.shortMessage()) << "\r\n" << " category" << Severity::toString(msg.severity) << "\r\n" << " type" << ErrorLogger::toxml(msg.shortMessage()) << "\r\n" << " check_name" << msg.id << "\r\n" << " \r\n" << " issue_hash_content_of_line_in_context" << 0 << "\r\n" << " issue_context_kind\r\n" << " issue_context\r\n" << " issue_hash_function_offset\r\n" << " location\r\n" << plistLoc(" ", msg.callStack.back()) << " \r\n"; return plist.str(); } std::string replaceStr(std::string s, const std::string &from, const std::string &to) { std::string::size_type pos1 = 0; while (pos1 < s.size()) { pos1 = s.find(from, pos1); if (pos1 == std::string::npos) return s; if (pos1 > 0 && (s[pos1-1] == '_' || std::isalnum(s[pos1-1]))) { pos1++; continue; } const std::string::size_type pos2 = pos1 + from.size(); if (pos2 >= s.size()) return s.substr(0,pos1) + to; if (s[pos2] == '_' || std::isalnum(s[pos2])) { pos1++; continue; } s = s.substr(0,pos1) + to + s.substr(pos2); pos1 += to.size(); } return s; } cppcheck-2.7/lib/errorlogger.h000066400000000000000000000242161417746362400164350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef errorloggerH #define errorloggerH //--------------------------------------------------------------------------- #include "config.h" #include "errortypes.h" #include "suppressions.h" #include "color.h" #include #include #include #include #include /** * CWE id (Common Weakness Enumeration) * See https://cwe.mitre.org/ for further reference. * */ // CWE list: https://cwe.mitre.org/data/published/cwe_v3.4.1.pdf static const struct CWE CWE_USE_OF_UNINITIALIZED_VARIABLE(457U); static const struct CWE CWE_NULL_POINTER_DEREFERENCE(476U); static const struct CWE CWE_USE_OF_POTENTIALLY_DANGEROUS_FUNCTION(676U); static const struct CWE CWE_INCORRECT_CALCULATION(682U); static const struct CWE CWE_EXPIRED_POINTER_DEREFERENCE(825U); class Token; class TokenList; namespace tinyxml2 { class XMLElement; } /// @addtogroup Core /// @{ /** * Wrapper for error messages, provided by reportErr() */ class CPPCHECKLIB ErrorMessage { public: /** * File name and line number. * Internally paths are stored with / separator. When getting the filename * it is by default converted to native separators. */ class CPPCHECKLIB FileLocation { public: FileLocation() : fileIndex(0), line(0), column(0) {} FileLocation(const std::string &file, int line, unsigned int column) : fileIndex(0), line(line), column(column), mOrigFileName(file), mFileName(file) {} FileLocation(const std::string &file, const std::string &info, int line, unsigned int column) : fileIndex(0), line(line), column(column), mOrigFileName(file), mFileName(file), mInfo(info) {} FileLocation(const Token* tok, const TokenList* tokenList); FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList); /** * Return the filename. * @param convert If true convert path to native separators. * @return filename. */ std::string getfile(bool convert = true) const; /** * Filename with the whole path (no --rp) * @param convert If true convert path to native separators. * @return filename. */ std::string getOrigFile(bool convert = true) const; /** * Set the filename. * @param file Filename to set. */ void setfile(const std::string &file); /** * @return the location as a string. Format: [file:line] */ std::string stringify() const; unsigned int fileIndex; int line; // negative value means "no line" unsigned int column; std::string getinfo() const { return mInfo; } void setinfo(const std::string &i) { mInfo = i; } private: std::string mOrigFileName; std::string mFileName; std::string mInfo; }; ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, Certainty::CertaintyLevel certainty); ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, const CWE &cwe, Certainty::CertaintyLevel certainty); ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, Certainty::CertaintyLevel certainty); ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, const CWE &cwe, Certainty::CertaintyLevel certainty, bool bugHunting); ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, Certainty::CertaintyLevel certainty, bool bugHunting); ErrorMessage(); explicit ErrorMessage(const tinyxml2::XMLElement * const errmsg); /** * Format the error message in XML format */ std::string toXML() const; static std::string getXMLHeader(); static std::string getXMLFooter(); /** * Format the error message into a string. * @param verbose use verbose message * @param templateFormat Empty string to use default output format * or template to be used. E.g. "{file}:{line},{severity},{id},{message}" * @param templateLocation Format Empty string to use default output format * or template to be used. E.g. "{file}:{line},{info}" * @return formatted string */ std::string toString(bool verbose, const std::string &templateFormat = emptyString, const std::string &templateLocation = emptyString) const; std::string serialize() const; bool deserialize(const std::string &data); std::list callStack; std::string id; /** For GUI rechecking; source file (not header) */ std::string file0; /** For GUI bug hunting; function name */ std::string function; /** For GUI bug hunting; incomplete analysis */ bool incomplete; Severity::SeverityType severity; CWE cwe; Certainty::CertaintyLevel certainty; /** Warning hash */ std::size_t hash; /** set short and verbose messages */ void setmsg(const std::string &msg); /** Short message (single line short message) */ const std::string &shortMessage() const { return mShortMessage; } /** Verbose message (may be the same as the short message) */ const std::string &verboseMessage() const { return mVerboseMessage; } /** Symbol names */ const std::string &symbolNames() const { return mSymbolNames; } Suppressions::ErrorMessage toSuppressionsErrorMessage() const; private: static std::string fixInvalidChars(const std::string& raw); /** Short message */ std::string mShortMessage; /** Verbose message */ std::string mVerboseMessage; /** symbol names */ std::string mSymbolNames; }; /** * @brief This is an interface, which the class responsible of error logging * should implement. */ class CPPCHECKLIB ErrorLogger { protected: std::ofstream plistFile; public: ErrorLogger() {} virtual ~ErrorLogger() { if (plistFile.is_open()) { plistFile << ErrorLogger::plistFooter(); plistFile.close(); } } /** * Information about progress is directed here. * Override this to receive the progress messages. * * @param outmsg Message to show e.g. "Checking main.cpp..." */ virtual void reportOut(const std::string &outmsg, Color c = Color::Reset) = 0; /** * Information about found errors and warnings is directed * here. Override this to receive the errormessages. * * @param msg Location and other information about the found error. */ virtual void reportErr(const ErrorMessage &msg) = 0; /** * Report progress to client * @param filename main file that is checked * @param stage for example preprocess / tokenize / simplify / check * @param value progress value (0-100) */ virtual void reportProgress(const std::string &filename, const char stage[], const std::size_t value) { (void)filename; (void)stage; (void)value; } /** * Output information messages. * @param msg Location and other information about the found error. */ virtual void reportInfo(const ErrorMessage &msg) { reportErr(msg); } virtual void bughuntingReport(const std::string &str) = 0; /** * Report unmatched suppressions * @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions) * @return true is returned if errors are reported */ bool reportUnmatchedSuppressions(const std::list &unmatched); static std::string callStackToString(const std::list &callStack); /** * Convert XML-sensitive characters into XML entities * @param str The input string containing XML-sensitive characters * @return The output string containing XML entities */ static std::string toxml(const std::string &str); static std::string plistHeader(const std::string &version, const std::vector &files); static std::string plistData(const ErrorMessage &msg); static const char *plistFooter() { return " \r\n" "\r\n" ""; } }; /** Replace substring. Example replaceStr("1,NR,3", "NR", "2") => "1,2,3" */ std::string replaceStr(std::string s, const std::string &from, const std::string &to); /// @} //--------------------------------------------------------------------------- #endif // errorloggerH cppcheck-2.7/lib/errortypes.cpp000066400000000000000000000035151417746362400166540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "errortypes.h" std::string Severity::toString(Severity::SeverityType severity) { switch (severity) { case none: return ""; case error: return "error"; case warning: return "warning"; case style: return "style"; case performance: return "performance"; case portability: return "portability"; case information: return "information"; case debug: return "debug"; } throw InternalError(nullptr, "Unknown severity"); } Severity::SeverityType Severity::fromString(const std::string& severity) { if (severity.empty()) return none; if (severity == "none") return none; if (severity == "error") return error; if (severity == "warning") return warning; if (severity == "style") return style; if (severity == "performance") return performance; if (severity == "portability") return portability; if (severity == "information") return information; if (severity == "debug") return debug; return none; } cppcheck-2.7/lib/errortypes.h000066400000000000000000000076131417746362400163240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef errortypesH #define errortypesH //--------------------------------------------------------------------------- #include "config.h" #include #include #include /// @addtogroup Core /// @{ class Token; /** @brief Simple container to be thrown when internal error is detected. */ struct InternalError { enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL, LIMIT, INSTANTIATION}; InternalError(const Token *tok, const std::string &errorMsg, Type type = INTERNAL); const Token *token; std::string errorMessage; Type type; std::string id; }; class CPPCHECKLIB Certainty { public: enum CertaintyLevel { normal, inconclusive, safe, experimental }; }; class CPPCHECKLIB Checks { public: enum CheckList { unusedFunction, missingInclude, internalCheck }; }; /** @brief enum class for severity. Used when reporting errors. */ class CPPCHECKLIB Severity { public: /** * Message severities. */ enum SeverityType { /** * No severity (default value). */ none, /** * Programming error. * This indicates severe error like memory leak etc. * The error is certain. */ error, /** * Warning. * Used for dangerous coding style that can cause severe runtime errors. * For example: forgetting to initialize a member variable in a constructor. */ warning, /** * Style warning. * Used for general code cleanup recommendations. Fixing these * will not fix any bugs but will make the code easier to maintain. * For example: redundant code, unreachable code, etc. */ style, /** * Performance warning. * Not an error as is but suboptimal code and fixing it probably leads * to faster performance of the compiled code. */ performance, /** * Portability warning. * This warning indicates the code is not properly portable for * different platforms and bitnesses (32/64 bit). If the code is meant * to compile in different platforms and bitnesses these warnings * should be fixed. */ portability, /** * Checking information. * Information message about the checking (process) itself. These * messages inform about header files not found etc issues that are * not errors in the code but something user needs to know. */ information, /** * Debug message. * Debug-mode message useful for the developers. */ debug }; static std::string toString(SeverityType severity); static SeverityType fromString(const std::string &severity); }; struct CWE { explicit CWE(unsigned short cweId) : id(cweId) {} unsigned short id; }; typedef std::pair ErrorPathItem; typedef std::list ErrorPath; /// @} //--------------------------------------------------------------------------- #endif // errortypesH cppcheck-2.7/lib/exprengine.cpp000066400000000000000000003655431417746362400166160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * @brief This is the ExprEngine component in Cppcheck. Its job is to * convert the C/C++ code into expressions that the Z3 prover understands. * We can then ask Z3 prover for instance if variable "x" can be 123 and * the Z3 prover can tell us that. * * Overview * ======== * * The ExprEngine performs a "abstract execution" of each function. * - ExprEngine performs "forward" analysis only. It starts at the top * of the functions. * - There is a abstract program state `Data::memory`. * - The constraints are stored in the vector `Data::constraints`. * * Abstract program state * ====================== * * The map `Data::memory` contains the abstract values of all variables * that are used in the current function scope. * * Use `--debug-bug-hunting --verbose` to dump out `Data::memory`. * Example output: * 2:5: { x=$1 y=$2} * Explanation: * At line 2, column 5: The memory has two variables. Variable x has the * value $1. Variable y has the value $2. * * Different value names: * - Typical abstract value has name that starts with "$". The number is * just a incremented value. * - If a variable has a known value then the concrete value is written. * Example: `{ x=1 }`. * - For an uninitialized value the output says "?". For example: `{ a=? }` * - For buffers the output is something like `{ buf=($3,size=10,[:]=?,[$1]=$2) }` * The first item "$3" is the name of the buffer value. * The second item says that the size of this buffer is 10. * After that comes `[index]=value` items that show what values buffer items have: * `[:]=?` means that all items are uninitialized. * `[$1]=$2` means that the buffer item at index "$1" has value "$2". * * Abstract execution * ================== * * The function: * static std::string execute(const Token *start, const Token *end, Data &data) * * Perform abstract execution of the code from `start` to `end`. The * `data` is modified during the abstract execution. * * Each astTop token is executed. From that, operands are executed * recursively in the "execute.." functions. The result of an operand is * a abstract value. * * Branches * -------- * * Imagine: * code1 * if (x > 0) * code2 * else * code3 * code4 * * When "if" is reached.. the current `data` is branched into `thenData` * and `elseData`. * For "thenData" a constraint is added: x>0 * For "elseData" a constraint is added: !(x>0) * * Then analysis of `thenData` and `elseData` will continue separately, * by recursive execution. The "code4" block will be analysed both with * `thenData` and `elseData`. * * Z3 * == * * The ExprEngine will not execute Z3 unless a check wants it to. * * The abstract values and all their constraints is added to a Z3 solver * object and after that Z3 can tell us if some condition can be true. * * Z3 is a SMT solver: * https://en.wikipedia.org/wiki/Satisfiability_modulo_theories * * In SMT: * - all variables are "constant". A variable can not be changed or assigned. * - There is no "execution". The solver considers all equations simultaneously. * * Simple example (TestExpr::expr6): * * void f(unsigned char x) * { * unsigned char y = 8 - x;\n" * y > 1000; * } * * If a check wants to know if "y > 1000" can be true, ExprEngine will * generate this Z3 input: * * (declare-fun $1 () Int) * (assert (and (>= $1 0) (<= $1 255))) * (assert (> (- 8 $1) 1000)) * * A symbol "$1" is created. * assert that "$1" is a value 0-255. * assert that "8-$1" is greater than 1000. * * Z3 can now determine if these assertions are possible or not. In this * case these assertions are not possible, there is no value for $1 between * 0-255 that means that "8-$1" is greater than 1000. */ #include "exprengine.h" #include "astutils.h" #include "bughuntingchecks.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "platform.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_Z3 #include #include #define GET_VERSION_INT(A,B,C) ((A) * 10000 + (B) * 100 + (C)) #define Z3_VERSION_INT GET_VERSION_INT(Z3_MAJOR_VERSION, Z3_MINOR_VERSION, Z3_BUILD_NUMBER) #endif const uint32_t MAX_BUFFER_SIZE = ~0U >> 1; #define CONTRACT 1 namespace { struct ExprEngineException { ExprEngineException(const Token *tok, const std::string &what) : tok(tok), what(what) {} const Token *tok; const std::string what; }; struct TerminateExpression {}; } static std::string str(ExprEngine::ValuePtr val) { const char *typestr = "???UnknownValueType???"; switch (val->type) { case ExprEngine::ValueType::AddressOfValue: typestr = "AddressOfValue"; break; case ExprEngine::ValueType::ArrayValue: typestr = "ArrayValue"; break; case ExprEngine::ValueType::UninitValue: typestr = "UninitValue"; break; case ExprEngine::ValueType::IntRange: typestr = "IntRange"; break; case ExprEngine::ValueType::FloatRange: typestr = "FloatRange"; break; case ExprEngine::ValueType::ConditionalValue: typestr = "ConditionalValue"; break; case ExprEngine::ValueType::StringLiteralValue: typestr = "StringLiteralValue"; break; case ExprEngine::ValueType::StructValue: typestr = "StructValue"; break; case ExprEngine::ValueType::BinOpResult: typestr = "BinOpResult"; break; case ExprEngine::ValueType::IntegerTruncation: typestr = "IntegerTruncation"; break; case ExprEngine::ValueType::FunctionCallArgumentValues: typestr = "FunctionCallArgumentValues"; break; case ExprEngine::ValueType::BailoutValue: typestr = "BailoutValue"; break; } return val->name + "=" + std::string(typestr) + "(" + val->getRange() + ")"; } static size_t extfind(const std::string &str, const std::string &what, size_t pos) { int indent = 0; for (; pos < str.size(); ++pos) { if (indent <= 0 && str[pos] == what[0]) return pos; else if (str[pos] == '\"') { ++pos; while (pos < str.size()) { if (str[pos] == '\"') break; if (pos == '\\') ++pos; ++pos; } } else if (str[pos] == '(') ++indent; else if (str[pos] == ')') --indent; } return std::string::npos; } std::string ExprEngine::str(int128_t value) { std::ostringstream ostr; #ifdef __GNUC__ if (value == (int)value) { ostr << (int) value; return ostr.str(); } if (value < 0) { ostr << "-"; value = -value; } uint64_t high = value >> 64; uint64_t low = value; if (high > 0) ostr << "h" << std::hex << high << "l"; ostr << std::hex << low; #else ostr << value; #endif return ostr.str(); } static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform); namespace { class TrackExecution { public: TrackExecution() : mDataIndexCounter(0), mAbortLine(-1) {} int getNewDataIndex() { return mDataIndexCounter++; } void symbolRange(const Token *tok, ExprEngine::ValuePtr value) { if (!tok || !value) return; if (tok->index() == 0) return; const std::string &symbolicExpression = value->getSymbolicExpression(); if (std::isdigit(symbolicExpression[0]) || value->type == ExprEngine::ValueType::BinOpResult || value->type == ExprEngine::ValueType::UninitValue) return; if (mSymbols.find(symbolicExpression) != mSymbols.end()) return; mSymbols.insert(symbolicExpression); mMap[tok].push_back(str(value)); } void state(const Token *tok, const std::string &s) { mMap[tok].push_back(s); } void print(std::ostream &out) { std::set> locations; for (auto it : mMap) { locations.insert(std::pair(it.first->linenr(), it.first->column())); } for (const std::pair &loc : locations) { int lineNumber = loc.first; int column = loc.second; for (auto &it : mMap) { const Token *tok = it.first; if (lineNumber != tok->linenr()) continue; if (column != tok->column()) continue; const std::vector &dumps = it.second; for (const std::string &dump : dumps) out << lineNumber << ":" << column << ": " << dump << "\n"; } } } void report(std::ostream &out, const Scope *functionScope) const { int linenr = -1; std::string code; for (const Token *tok = functionScope->bodyStart->next(); tok != functionScope->bodyEnd; tok = tok->next()) { if (tok->linenr() > linenr) { if (!code.empty()) out << getStatus(linenr) << " " << code << std::endl; linenr = tok->linenr(); code.clear(); } code += " " + tok->str(); } out << getStatus(linenr) << " " << code << std::endl; } void setAbortLine(int linenr) { if (linenr > 0 && (mAbortLine == -1 || linenr < mAbortLine)) mAbortLine = linenr; } void addError(int linenr) { mErrors.insert(linenr); } bool isAllOk() const { return mErrors.empty(); } void addMissingContract(const std::string &f) { mMissingContracts.insert(f); } const std::set getMissingContracts() const { return mMissingContracts; } void ifSplit(const Token *tok, unsigned int thenIndex, unsigned int elseIndex) { mMap[tok].push_back("D" + std::to_string(thenIndex) + ": Split. Then:D" + std::to_string(thenIndex) + " Else:D" + std::to_string(elseIndex)); } private: const char *getStatus(int linenr) const { if (mErrors.find(linenr) != mErrors.end()) return "ERROR"; if (mAbortLine > 0 && linenr >= mAbortLine) return "--"; return "ok"; } std::map> mMap; int mDataIndexCounter; int mAbortLine; std::set mSymbols; std::set mErrors; std::set mMissingContracts; }; class Data : public ExprEngine::DataBase { public: Data(int *symbolValueIndex, ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings, const std::string ¤tFunction, const std::vector &callbacks, TrackExecution *trackExecution) : DataBase(currentFunction, settings) , symbolValueIndex(symbolValueIndex) , errorLogger(errorLogger) , tokenizer(tokenizer) , callbacks(callbacks) , recursion(0) , startTime(std::time(nullptr)) , mTrackExecution(trackExecution) , mDataIndex(trackExecution->getNewDataIndex()) {} Data(const Data &old) : DataBase(old.currentFunction, old.settings) , memory(old.memory) , symbolValueIndex(old.symbolValueIndex) , errorLogger(old.errorLogger) , tokenizer(old.tokenizer) , callbacks(old.callbacks) , constraints(old.constraints) , recursion(old.recursion) , startTime(old.startTime) , mTrackExecution(old.mTrackExecution) , mDataIndex(mTrackExecution->getNewDataIndex()) { for (auto &it: memory) { if (!it.second) continue; if (auto oldValue = std::dynamic_pointer_cast(it.second)) it.second = std::make_shared(getNewSymbolName(), *oldValue); } } using Memory = std::map; Memory memory; int * const symbolValueIndex; ErrorLogger *errorLogger; const Tokenizer * const tokenizer; const std::vector &callbacks; std::vector constraints; int recursion; std::time_t startTime; bool isC() const OVERRIDE { return tokenizer->isC(); } bool isCPP() const OVERRIDE { return tokenizer->isCPP(); } #ifdef CONTRACT ExprEngine::ValuePtr executeContract(const Function *function, ExprEngine::ValuePtr (*executeExpression)(const Token*, Data&)) { const auto it = settings->functionContracts.find(function->fullName()); if (it == settings->functionContracts.end()) return ExprEngine::ValuePtr(); const std::string &expects = it->second; TokenList tokenList(settings); std::istringstream istr(expects); tokenList.createTokens(istr); tokenList.createAst(); SymbolDatabase *symbolDatabase = const_cast(tokenizer->getSymbolDatabase()); for (Token *tok = tokenList.front(); tok; tok = tok->next()) { for (const Variable &arg: function->argumentList) { if (arg.name() == tok->str()) { tok->variable(&arg); tok->varId(arg.declarationId()); } } } symbolDatabase->setValueTypeInTokenList(false, tokenList.front()); return executeExpression(tokenList.front()->astTop(), *this); } void contractConstraints(const Function *function, ExprEngine::ValuePtr (*executeExpression)(const Token*, Data&)) { auto value = executeContract(function, executeExpression); if (value) constraints.push_back(value); } #endif void assignValue(const Token *tok, unsigned int varId, ExprEngine::ValuePtr value) { if (varId == 0) return; mTrackExecution->symbolRange(tok, value); if (value) { if (auto arr = std::dynamic_pointer_cast(value)) { for (const auto &dim: arr->size) mTrackExecution->symbolRange(tok, dim); for (const auto &indexAndValue: arr->data) mTrackExecution->symbolRange(tok, indexAndValue.value); } else if (auto s = std::dynamic_pointer_cast(value)) { for (const auto &m: s->member) mTrackExecution->symbolRange(tok, m.second); } } memory[varId] = value; } void assignStructMember(const Token *tok, ExprEngine::StructValue *structVal, const std::string &memberName, ExprEngine::ValuePtr value) { mTrackExecution->symbolRange(tok, value); structVal->member[memberName] = value; } void functionCall() { // Remove values for global variables const SymbolDatabase *symbolDatabase = tokenizer->getSymbolDatabase(); for (std::map::iterator it = memory.begin(); it != memory.end();) { unsigned int varid = it->first; const Variable *var = symbolDatabase->getVariableFromVarId(varid); if (var && var->isGlobal()) it = memory.erase(it); else ++it; } } std::string getNewSymbolName() FINAL { return "$" + std::to_string(++(*symbolValueIndex)); } std::shared_ptr getArrayValue(const Token *tok) { const Memory::iterator it = memory.find(tok->varId()); if (it != memory.end()) return std::dynamic_pointer_cast(it->second); if (tok->varId() == 0 || !tok->variable()) return std::shared_ptr(); auto val = std::make_shared(this, tok->variable()); assignValue(tok, tok->varId(), val); return val; } ExprEngine::ValuePtr getValue(unsigned int varId, const ValueType *valueType, const Token *tok) { const Memory::const_iterator it = memory.find(varId); if (it != memory.end()) return it->second; if (!valueType) return ExprEngine::ValuePtr(); // constant value.. const Variable *var = tokenizer->getSymbolDatabase()->getVariableFromVarId(varId); if (var && valueType->constness == 1 && Token::Match(var->nameToken(), "%var% =")) { const Token *initExpr = var->nameToken()->next()->astOperand2(); if (initExpr && initExpr->hasKnownIntValue()) { auto intval = initExpr->getKnownIntValue(); return std::make_shared(std::to_string(intval), intval, intval); } } ExprEngine::ValuePtr value = getValueRangeFromValueType(getNewSymbolName(), valueType, *settings); if (value) { if (tok->variable() && tok->variable()->nameToken()) addConstraints(value, tok->variable()->nameToken()); assignValue(tok, varId, value); } return value; } void trackCheckContract(const Token *tok, const std::string &solverOutput) { std::ostringstream os; os << "checkContract:{\n"; std::string line; std::istringstream istr(solverOutput); while (std::getline(istr, line)) os << " " << line << "\n"; os << "}"; mTrackExecution->state(tok, os.str()); } void trackProgramState(const Token *tok) { if (memory.empty()) return; const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); std::ostringstream s; s << "D" << mDataIndex << ":" << "memory:{"; bool first = true; for (auto mem : memory) { ExprEngine::ValuePtr value = mem.second; const Variable *var = symbolDatabase->getVariableFromVarId(mem.first); if (!var) continue; if (!first) s << " "; first = false; s << var->name() << "="; if (!value) s << "(null)"; else if (value->name[0] == '$' && value->getSymbolicExpression() != value->name) s << "(" << value->name << "," << value->getSymbolicExpression() << ")"; else s << value->name; } s << "}"; if (!constraints.empty()) { s << " constraints:{"; first = true; for (auto constraint: constraints) { if (!first) s << " "; first = false; s << constraint->getSymbolicExpression(); } s << "}"; } mTrackExecution->state(tok, s.str()); } void addMissingContract(const std::string &f) { mTrackExecution->addMissingContract(f); } ExprEngine::ValuePtr notValue(ExprEngine::ValuePtr v) { auto b = std::dynamic_pointer_cast(v); if (b) { std::string binop; if (b->binop == "==") binop = "!="; else if (b->binop == "!=") binop = "=="; else if (b->binop == ">=") binop = "<"; else if (b->binop == "<=") binop = ">"; else if (b->binop == ">") binop = "<="; else if (b->binop == "<") binop = ">="; if (!binop.empty()) return std::make_shared(binop, b->op1, b->op2); } if (std::dynamic_pointer_cast(v)) { auto zero = std::make_shared("0.0", 0.0, 0.0); return std::make_shared("==", v, zero); } auto zero = std::make_shared("0", 0, 0); return std::make_shared("==", v, zero); } void addConstraint(ExprEngine::ValuePtr condValue, bool trueCond) { if (!condValue) return; if (trueCond) constraints.push_back(condValue); else constraints.push_back(notValue(condValue)); } void addConstraint(ExprEngine::ValuePtr lhsValue, ExprEngine::ValuePtr rhsValue, bool equals) { if (!lhsValue || !rhsValue) return; constraints.push_back(std::make_shared(equals?"==":"!=", lhsValue, rhsValue)); } void addConstraints(ExprEngine::ValuePtr value, const Token *tok) { #ifdef CONTRACT MathLib::bigint low; if (tok->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low)) addConstraint(std::make_shared(">=", value, std::make_shared(std::to_string(low), low, low)), true); MathLib::bigint high; if (tok->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high)) addConstraint(std::make_shared("<=", value, std::make_shared(std::to_string(high), high, high)), true); #endif } void reportError(const Token *tok, Severity::SeverityType severity, const char id[], const std::string &text, CWE cwe, bool inconclusive, bool incomplete, const std::string &functionName) OVERRIDE { if (errorPath.empty()) mTrackExecution->addError(tok->linenr()); ErrorPath e = errorPath; e.push_back(ErrorPathItem(tok, text)); ErrorMessage errmsg(e, &tokenizer->list, severity, id, text, cwe, inconclusive ? Certainty::inconclusive : Certainty::normal, true); errmsg.incomplete = incomplete; errmsg.function = functionName.empty() ? currentFunction : functionName; errorLogger->reportErr(errmsg); } std::string str() const { std::ostringstream ret; std::map vars; for (const auto &mem: memory) { if (!mem.second) continue; const Variable *var = tokenizer->getSymbolDatabase()->getVariableFromVarId(mem.first); if (var && var->isLocal()) continue; ret << " @" << mem.first << ":" << mem.second->name; getSymbols(vars, mem.second); } for (const auto &var: vars) { if (var.second->name[0] == '$') ret << " " << ::str(var.second); } for (const auto &c: constraints) ret << " (" << c->getSymbolicExpression() << ")"; ret << std::endl; return ret.str(); } void load(const std::string &s) { std::vector importData; parsestr(s, &importData); //simplify(importData); if (importData.empty()) return; std::map symbols; for (auto mem: memory) { getSymbols(symbols, mem.second); } // TODO: combined symbolvalue std::map combinedMemory; for (const ImportData &d: importData) { for (const auto &mem: d.mem) { auto c = combinedMemory.find(mem.first); if (c == combinedMemory.end()) { combinedMemory[mem.first] = mem.second; continue; } if (c->second == mem.second) continue; if (c->second == "?" || mem.second == "?") c->second = "?"; else c->second.clear(); } } for (const auto &mem: combinedMemory) { int varid = mem.first; const std::string &name = mem.second; auto it = memory.find(varid); if (it != memory.end() && it->second && it->second->name == name) continue; if (name.empty()) { if (it != memory.end()) memory.erase(it); continue; } auto it2 = symbols.find(name); if (it2 != symbols.end()) { memory[varid] = it2->second; continue; } if (name == "?") { auto uninitValue = std::make_shared(); symbols[name] = uninitValue; memory[varid] = uninitValue; continue; } if (std::isdigit(name[0])) { long long v = std::stoi(name); auto intRange = std::make_shared(name, v, v); symbols[name] = intRange; memory[varid] = intRange; continue; } // TODO: handle this value.. if (it != memory.end()) memory.erase(it); } } static void ifSplit(const Token *tok, const Data& thenData, const Data& elseData) { thenData.mTrackExecution->ifSplit(tok, thenData.mDataIndex, elseData.mDataIndex); } private: TrackExecution * const mTrackExecution; const int mDataIndex; struct ImportData { std::map mem; std::map sym; std::vector constraints; }; static void parsestr(const std::string &s, std::vector *importData) { std::string line; std::istringstream istr(s); while (std::getline(istr, line)) { if (line.empty()) continue; line += " "; ImportData d; for (std::string::size_type pos = 0; pos < line.size();) { pos = line.find_first_not_of(" ", pos); if (pos == std::string::npos) break; if (line[pos] == '@') { ++pos; std::string::size_type colon = line.find(":", pos); std::string::size_type end = line.find(" ", colon); const std::string lhs = line.substr(pos, colon-pos); pos = colon + 1; const std::string rhs = line.substr(pos, end-pos); d.mem[std::stoi(lhs)] = rhs; pos = end; } else if (line[pos] == '$') { const std::string::size_type eq = line.find("=", pos); const std::string lhs = line.substr(pos, eq-pos); pos = eq + 1; const std::string::size_type end = extfind(line, " ", pos); const std::string rhs = line.substr(pos, end-pos); pos = end; d.sym[lhs] = rhs; } else if (line[pos] == '(') { const std::string::size_type end = extfind(line, " ", pos); const std::string c = line.substr(pos, end-pos); pos = end; d.constraints.push_back(c); } else { throw ExprEngineException(nullptr, "Internal Error: Data::parsestr(), line:" + line); } } importData->push_back(d); } } void getSymbols(std::map &symbols, ExprEngine::ValuePtr val) const { if (!val) return; symbols[val->name] = val; if (auto arrayValue = std::dynamic_pointer_cast(val)) { for (auto sizeValue: arrayValue->size) getSymbols(symbols, sizeValue); for (auto indexValue: arrayValue->data) { getSymbols(symbols, indexValue.index); getSymbols(symbols, indexValue.value); } } if (auto structValue = std::dynamic_pointer_cast(val)) { for (auto memberNameValue: structValue->member) getSymbols(symbols, memberNameValue.second); } } }; } #ifdef __clang__ // work around "undefined reference to `__muloti4'" linker error - see https://bugs.llvm.org/show_bug.cgi?id=16404 __attribute__((no_sanitize("undefined"))) #endif static ExprEngine::ValuePtr simplifyValue(ExprEngine::ValuePtr origValue) { auto b = std::dynamic_pointer_cast(origValue); if (!b) return origValue; if (!b->op1 || !b->op2) return origValue; auto intRange1 = std::dynamic_pointer_cast(b->op1); auto intRange2 = std::dynamic_pointer_cast(b->op2); if (intRange1 && intRange2 && intRange1->minValue == intRange1->maxValue && intRange2->minValue == intRange2->maxValue) { const std::string &binop = b->binop; int128_t v; if (binop == "+") v = intRange1->minValue + intRange2->minValue; else if (binop == "-") v = intRange1->minValue - intRange2->minValue; else if (binop == "*") v = intRange1->minValue * intRange2->minValue; else if (binop == "/" && intRange2->minValue != 0) v = intRange1->minValue / intRange2->minValue; else if (binop == "%" && intRange2->minValue != 0) v = intRange1->minValue % intRange2->minValue; else return origValue; return std::make_shared(ExprEngine::str(v), v, v); } return origValue; } static ExprEngine::ValuePtr translateUninitValueToRange(ExprEngine::ValuePtr value, const ::ValueType *valueType, Data &data) { if (!value) return value; if (value->type == ExprEngine::ValueType::UninitValue) { auto rangeValue = getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (rangeValue) return rangeValue; } if (auto conditionalValue = std::dynamic_pointer_cast(value)) { if (conditionalValue->values.size() == 1 && conditionalValue->values[0].second && conditionalValue->values[0].second->type == ExprEngine::ValueType::UninitValue) { auto rangeValue = getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (rangeValue) return rangeValue; } } return value; } static int128_t truncateInt(int128_t value, int bits, char sign) { value = value & (((int128_t)1 << bits) - 1); // Sign extension if (sign == 's' && value & (1ULL << (bits - 1))) value |= ~(((int128_t)1 << bits) - 1); return value; } ExprEngine::ArrayValue::ArrayValue(const std::string &name, ExprEngine::ValuePtr size, ExprEngine::ValuePtr value, bool pointer, bool nullPointer, bool uninitPointer) : Value(name, ExprEngine::ValueType::ArrayValue) , pointer(pointer), nullPointer(nullPointer), uninitPointer(uninitPointer) { this->size.push_back(size); assign(ExprEngine::ValuePtr(), value); } ExprEngine::ArrayValue::ArrayValue(DataBase *data, const Variable *var) : Value(data->getNewSymbolName(), ExprEngine::ValueType::ArrayValue) , pointer(var && var->isPointer()), nullPointer(var && var->isPointer()), uninitPointer(var && var->isPointer()) { if (var) { for (const auto &dim : var->dimensions()) { if (dim.known) size.push_back(std::make_shared(std::to_string(dim.num), dim.num, dim.num)); else size.push_back(std::make_shared(data->getNewSymbolName(), 1, ExprEngine::ArrayValue::MAXSIZE)); } } else { size.push_back(std::make_shared(data->getNewSymbolName(), 1, ExprEngine::ArrayValue::MAXSIZE)); } const Token *initToken = var ? var->nameToken() : nullptr; while (initToken && initToken->str() != "=") initToken = initToken->astParent(); ValuePtr val; if (var && !var->isGlobal() && !var->isStatic() && !(var->isArgument() && var->isConst()) && !initToken) val = std::make_shared(); else if (var && var->valueType()) { ::ValueType vt(*var->valueType()); vt.pointer = 0; val = getValueRangeFromValueType(data->getNewSymbolName(), &vt, *data->settings); } assign(ExprEngine::ValuePtr(), val); } ExprEngine::ArrayValue::ArrayValue(const std::string &name, const ExprEngine::ArrayValue &arrayValue) : Value(name, ExprEngine::ValueType::ArrayValue) , pointer(arrayValue.pointer), nullPointer(arrayValue.nullPointer), uninitPointer(arrayValue.uninitPointer) , data(arrayValue.data), size(arrayValue.size) {} std::string ExprEngine::ArrayValue::getRange() const { std::string r = getSymbolicExpression(); if (nullPointer) r += std::string(r.empty() ? "" : ",") + "null"; if (uninitPointer) r += std::string(r.empty() ? "" : ",") + "->?"; return r; } void ExprEngine::ArrayValue::assign(ExprEngine::ValuePtr index, ExprEngine::ValuePtr value) { if (!index) data.clear(); if (value) { if (index) { // Remove old item that will be "overwritten" for (size_t i = 0; i < data.size(); ++i) { if (data[i].index && data[i].index->name == index->name) { data.erase(data.begin() + i); break; } } } ExprEngine::ArrayValue::IndexAndValue indexAndValue = {index, value}; data.push_back(indexAndValue); } } void ExprEngine::ArrayValue::clear() { data.clear(); ExprEngine::ArrayValue::IndexAndValue indexAndValue = { ExprEngine::ValuePtr(), std::make_shared("0", 0, 0) }; data.push_back(indexAndValue); } static bool isEqual(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) { if (!v1 || !v2) return !v1 && !v2; return v1->name == v2->name; } static bool isNonOverlapping(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) { if (!v1 || !v2) return false; // Don't know! auto intRange1 = std::dynamic_pointer_cast(v1); auto intRange2 = std::dynamic_pointer_cast(v2); if (intRange1 && intRange2 && (intRange1->minValue > intRange2->maxValue || intRange1->maxValue < intRange2->maxValue)) return true; return false; } ExprEngine::ConditionalValue::Vector ExprEngine::ArrayValue::read(ExprEngine::ValuePtr index) const { ExprEngine::ConditionalValue::Vector ret; if (!index) return ret; for (const auto &indexAndValue : data) { if (::isEqual(index, indexAndValue.index)) ret.clear(); if (isNonOverlapping(index, indexAndValue.index)) continue; // Array contains string literal data... if (!indexAndValue.index && indexAndValue.value->type == ExprEngine::ValueType::StringLiteralValue) { auto stringLiteral = std::dynamic_pointer_cast(indexAndValue.value); if (!stringLiteral) { ret.push_back(std::pair(indexAndValue.index, std::make_shared("", -128, 128))); continue; } if (auto i = std::dynamic_pointer_cast(index)) { if (i->minValue >= 0 && i->minValue == i->maxValue) { int c = 0; if (i->minValue < stringLiteral->size()) c = stringLiteral->string[i->minValue]; ret.push_back(std::pair(indexAndValue.index, std::make_shared(std::to_string(c), c, c))); continue; } } int cmin = 0, cmax = 0; for (char c : stringLiteral->string) { if (c < cmin) cmin = c; else if (c > cmax) cmax = c; } ret.push_back(std::pair(indexAndValue.index, std::make_shared("", cmin, cmax))); continue; } // Rename IntRange if (auto i = std::dynamic_pointer_cast(indexAndValue.value)) { ret.push_back(std::pair(indexAndValue.index, std::make_shared(indexAndValue.value->name + ":" + index->name, i->minValue, i->maxValue))); continue; } ret.push_back(std::pair(indexAndValue.index, indexAndValue.value)); } if (ret.size() == 1) ret[0].first = ExprEngine::ValuePtr(); else if (ret.size() == 2 && !ret[0].first) { ret[0].first = std::make_shared("!=", index, ret[1].first); ret[1].first = std::make_shared("==", index, ret[1].first); } else { // FIXME!! ret.clear(); } return ret; } std::string ExprEngine::ConditionalValue::getSymbolicExpression() const { std::ostringstream ostr; ostr << "{"; bool first = true; for (auto condvalue : values) { ValuePtr cond = condvalue.first; ValuePtr value = condvalue.second; if (!first) ostr << ","; first = false; ostr << "{" << (cond ? cond->getSymbolicExpression() : std::string("(null)")) << "," << value->getSymbolicExpression() << "}"; } ostr << "}"; return ostr.str(); } std::string ExprEngine::ArrayValue::getSymbolicExpression() const { std::ostringstream ostr; if (size.empty()) ostr << "(null)"; else { for (const auto &dim: size) ostr << "[" << (dim ? dim->name : std::string("(null)")) << "]"; } for (const auto &indexAndValue : data) { ostr << ",[" << (!indexAndValue.index ? std::string(":") : indexAndValue.index->name) << "]="; if (indexAndValue.value->type == ExprEngine::ValueType::StructValue) ostr << "(" << indexAndValue.value->name << "," << indexAndValue.value->getSymbolicExpression() << ")"; else ostr << indexAndValue.value->name; } return ostr.str(); } std::string ExprEngine::StructValue::getSymbolicExpression() const { std::ostringstream ostr; ostr << "{"; bool first = true; for (const auto& m: member) { const std::string &memberName = m.first; auto memberValue = m.second; if (!first) ostr << ","; first = false; ostr << memberName << "=" << (memberValue ? memberValue->getSymbolicExpression() : std::string("(null)")); } ostr << "}"; return ostr.str(); } std::string ExprEngine::IntegerTruncation::getSymbolicExpression() const { return sign + std::to_string(bits) + "(" + inputValue->getSymbolicExpression() + ")"; } #ifdef USE_Z3 class ExprData { public: using ValueExpr = std::map; using AssertionList = std::vector; class BailoutValueException : public ExprEngineException { public: BailoutValueException() : ExprEngineException(nullptr, "Incomplete analysis") {} }; z3::context context; ValueExpr valueExpr; AssertionList assertionList; void addAssertions(z3::solver &solver) const { for (auto assertExpr : assertionList) solver.add(assertExpr); } void addConstraints(z3::solver &solver, const Data* data) { for (auto constraint : data->constraints) { try { solver.add(getConstraintExpr(constraint)); } catch (const BailoutValueException &) {} } } z3::expr addInt(const std::string &name, int128_t minValue, int128_t maxValue) { z3::expr e = context.int_const(name.c_str()); valueExpr.emplace(name, e); if (minValue >= INT_MIN && maxValue <= INT_MAX) assertionList.push_back(e >= int(minValue) && e <= int(maxValue)); else if (maxValue <= INT_MAX) assertionList.push_back(e <= int(maxValue)); else if (minValue >= INT_MIN) assertionList.push_back(e >= int(minValue)); return e; } z3::expr addFloat(const std::string &name) { z3::expr e = z3_fp_const(name); valueExpr.emplace(name, e); return e; } z3::expr getExpr(const ExprEngine::BinOpResult *b) { auto op1 = getExpr(b->op1); auto op2 = getExpr(b->op2); // floating point promotion if (b->binop != "&&" && b->binop != "||" && b->binop != "<<" && b->binop != ">>") { if (z3_is_fp(op1) || z3_is_fp(op2)) { z3_to_fp(op1); z3_to_fp(op2); } } if (b->binop == "+") return op1 + op2; if (b->binop == "-") return op1 - op2; if (b->binop == "*") return op1 * op2; if (b->binop == "/") return op1 / op2; if (b->binop == "%") #if Z3_VERSION_INT >= GET_VERSION_INT(4,8,5) return op1 % op2; #else return op1 - (op1 / op2) * op2; #endif if (b->binop == "==") return int_expr(op1) == int_expr(op2); if (b->binop == "!=") return op1 != op2; if (b->binop == ">=") return op1 >= op2; if (b->binop == "<=") return op1 <= op2; if (b->binop == ">") return op1 > op2; if (b->binop == "<") return op1 < op2; if (b->binop == "&&") return bool_expr(op1) && bool_expr(op2); if (b->binop == "||") return bool_expr(op1) || bool_expr(op2); if (b->binop == "<<") return op1 * z3::pw(context.int_val(2), op2); if (b->binop == ">>") return op1 / z3::pw(context.int_val(2), op2); throw ExprEngineException(nullptr, "Internal error: Unhandled operator " + b->binop); } z3::expr getExpr(ExprEngine::ValuePtr v) { if (!v) throw ExprEngineException(nullptr, "Can not solve expressions, operand value is null"); if (v->type == ExprEngine::ValueType::BailoutValue) throw BailoutValueException(); if (auto intRange = std::dynamic_pointer_cast(v)) { if (intRange->name[0] != '$') return z3_int_val(intRange->minValue); auto it = valueExpr.find(v->name); if (it != valueExpr.end()) return it->second; return addInt(v->name, intRange->minValue, intRange->maxValue); } if (auto floatRange = std::dynamic_pointer_cast(v)) { if (floatRange->name[0] != '$') return z3_fp_val(floatRange->minValue, floatRange->name); auto it = valueExpr.find(v->name); if (it != valueExpr.end()) return it->second; return addFloat(v->name); } if (auto b = std::dynamic_pointer_cast(v)) { return getExpr(b.get()); } if (auto c = std::dynamic_pointer_cast(v)) { if (c->values.empty()) throw ExprEngineException(nullptr, "ConditionalValue is empty"); if (c->values.size() == 1) return getExpr(c->values[0].second); return z3::ite(getExpr(c->values[1].first), getExpr(c->values[1].second), getExpr(c->values[0].second)); } if (auto integerTruncation = std::dynamic_pointer_cast(v)) { return getExpr(integerTruncation->inputValue); //return getExpr(integerTruncation->inputValue) & ((1 << integerTruncation->bits) - 1); } if (v->type == ExprEngine::ValueType::UninitValue) return context.int_val(0); throw ExprEngineException(nullptr, "Internal error: Unhandled value type"); } z3::expr getConstraintExpr(ExprEngine::ValuePtr v) { if (v->type == ExprEngine::ValueType::IntRange) return (getExpr(v) != 0); return bool_expr(getExpr(v)); } z3::expr bool_expr(z3::expr e) { if (e.is_bool()) return e; // Workaround for z3 bug: https://github.com/Z3Prover/z3/issues/4905 if (z3_is_fp(e)) return e != z3_fp_val(0.0, "0.0"); return e != 0; } z3::expr int_expr(z3::expr e) { if (e.is_bool()) return z3::ite(e, context.int_val(1), context.int_val(0)); return e; } // Wrapper functions for Z3 interface. Instead of having ifdefs embedded // in the code we have wrapper functions with ifdefs. The code that use // these will be cleaner and hopefully more robust. z3::expr z3_fp_const(const std::string &name) { return context.real_const(name.c_str()); } z3::expr z3_fp_val(long double value, std::string name) { (void)value; while (name.size() > 1 && (name.back() == 'f' || name.back() == 'F' || name.back() == 'l' || name.back() == 'L')) name.erase(name.size() - 1); return context.real_val(name.c_str()); } bool z3_is_fp(z3::expr e) const { return e.is_real(); } void z3_to_fp(z3::expr &e) { if (e.is_int()) e = z3::to_real(e); } z3::expr z3_int_val(int128_t value) { #if Z3_VERSION_INT >= GET_VERSION_INT(4,7,1) return context.int_val(int64_t(value)); #else return context.int_val((long long)(value)); #endif } }; #endif bool ExprEngine::UninitValue::isUninit(const DataBase *dataBase) const { const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { exprData.addConstraints(solver, data); exprData.addAssertions(solver); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else // The value may or may not be uninitialized return false; #endif } bool ExprEngine::IntRange::isEqual(const DataBase *dataBase, int value) const { if (value < minValue || value > maxValue) return false; const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { z3::expr e = exprData.addInt(name, minValue, maxValue); exprData.addConstraints(solver, data); exprData.addAssertions(solver); solver.add(e == value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else // The value may or may not be in range return false; #endif } bool ExprEngine::IntRange::isGreaterThan(const DataBase *dataBase, int value) const { if (maxValue <= value) return false; const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { z3::expr e = exprData.addInt(name, minValue, maxValue); exprData.addConstraints(solver, data); exprData.addAssertions(solver); solver.add(e > value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else // The value may or may not be in range return false; #endif } bool ExprEngine::IntRange::isLessThan(const DataBase *dataBase, int value) const { if (minValue >= value) return false; const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { z3::expr e = exprData.addInt(name, minValue, maxValue); exprData.addConstraints(solver, data); exprData.addAssertions(solver); solver.add(e < value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else // The value may or may not be in range return false; #endif } bool ExprEngine::FloatRange::isEqual(const DataBase *dataBase, int value) const { if (MathLib::isFloat(name)) { float f = MathLib::toDoubleNumber(name); return value >= f - 0.00001 && value <= f + 0.00001; } const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { z3::expr e = exprData.addFloat(name); exprData.addConstraints(solver, data); exprData.addAssertions(solver); // Workaround for z3 bug: https://github.com/Z3Prover/z3/issues/4905 #if Z3_VERSION_INT >= GET_VERSION_INT(4,8,0) z3::expr val_e = exprData.context.fpa_val(static_cast(value)); #else z3::expr val_e = exprData.context.real_val(value); #endif // Z3_VERSION_INT solver.add(e == val_e); return solver.check() != z3::unsat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else // The value may or may not be in range return false; #endif } bool ExprEngine::FloatRange::isGreaterThan(const DataBase *dataBase, int value) const { if (value < minValue || value > maxValue) return false; const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; if (MathLib::isFloat(name)) return value > MathLib::toDoubleNumber(name); #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { z3::expr e = exprData.addFloat(name); exprData.addConstraints(solver, data); exprData.addAssertions(solver); solver.add(e > value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else // The value may or may not be in range return false; #endif } bool ExprEngine::FloatRange::isLessThan(const DataBase *dataBase, int value) const { if (value < minValue || value > maxValue) return false; const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; if (MathLib::isFloat(name)) return value < MathLib::toDoubleNumber(name); #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { z3::expr e = exprData.addFloat(name); exprData.addConstraints(solver, data); exprData.addAssertions(solver); solver.add(e < value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else // The value may or may not be in range return false; #endif } bool ExprEngine::BinOpResult::isEqual(const ExprEngine::DataBase *dataBase, int value) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addConstraints(solver, dynamic_cast(dataBase)); exprData.addAssertions(solver); solver.add(exprData.int_expr(e) == value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3:" << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else (void)dataBase; (void)value; return false; #endif } bool ExprEngine::BinOpResult::isGreaterThan(const ExprEngine::DataBase *dataBase, int value) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addConstraints(solver, dynamic_cast(dataBase)); exprData.addAssertions(solver); solver.add(e > value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3:" << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else (void)dataBase; (void)value; return false; #endif } bool ExprEngine::BinOpResult::isLessThan(const ExprEngine::DataBase *dataBase, int value) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addConstraints(solver, dynamic_cast(dataBase)); exprData.addAssertions(solver); solver.add(e < value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3:" << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else (void)dataBase; (void)value; return false; #endif } bool ExprEngine::BinOpResult::isTrue(const ExprEngine::DataBase *dataBase) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addConstraints(solver, dynamic_cast(dataBase)); exprData.addAssertions(solver); solver.add(exprData.int_expr(e) != 0); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3:" << exception << std::endl; return true; // Safe option is to return true } catch (const ExprData::BailoutValueException &) { return true; // Safe option is to return true } catch (const ExprEngineException &) { return true; // Safe option is to return true } #else (void)dataBase; return false; #endif } std::string ExprEngine::BinOpResult::getExpr(ExprEngine::DataBase *dataBase) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addConstraints(solver, dynamic_cast(dataBase)); exprData.addAssertions(solver); solver.add(e); std::ostringstream os; os << solver; switch (solver.check()) { case z3::sat: os << "\nz3::sat\n"; break; case z3::unsat: os << "\nz3::unsat\n"; break; case z3::unknown: os << "\nz3::unknown\n"; break; } return os.str(); } catch (const z3::exception &exception) { std::ostringstream os; os << "\nz3:" << exception << "\n"; return os.str(); } #else (void)dataBase; return ""; #endif } // Todo: This is taken from ValueFlow and modified.. we should reuse it static int getIntBitsFromValueType(const ValueType *vt, const cppcheck::Platform &platform) { if (!vt) return 0; switch (vt->type) { case ValueType::Type::BOOL: return 1; case ValueType::Type::CHAR: return platform.char_bit; case ValueType::Type::SHORT: return platform.short_bit; case ValueType::Type::INT: return platform.int_bit; case ValueType::Type::LONG: return platform.long_bit; case ValueType::Type::LONGLONG: return platform.long_long_bit; default: return 0; } } static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform) { if (!vt || !(vt->isIntegral() || vt->isFloat()) || vt->pointer) return ExprEngine::ValuePtr(); int bits = getIntBitsFromValueType(vt, platform); if (bits == 1) { return std::make_shared(name, 0, 1); } else if (bits > 1) { if (vt->sign == ValueType::Sign::UNSIGNED) { return std::make_shared(name, 0, ((int128_t)1 << bits) - 1); } else { return std::make_shared(name, -((int128_t)1 << (bits - 1)), ((int128_t)1 << (bits - 1)) - 1); } } if (vt->isFloat()) return std::make_shared(name, -std::numeric_limits::infinity(), std::numeric_limits::infinity()); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr getValueRangeFromValueType(const ValueType *valueType, Data &data) { if (valueType && valueType->pointer) { ExprEngine::ValuePtr val = std::make_shared(); auto bufferSize = std::make_shared(data.getNewSymbolName(), 1, ExprEngine::ArrayValue::MAXSIZE); return std::make_shared(data.getNewSymbolName(), bufferSize, val, true, true, false); } if (!valueType || valueType->pointer) return ExprEngine::ValuePtr(); if (valueType->container) { ExprEngine::ValuePtr value; if (valueType->container->stdStringLike) value = std::make_shared(data.getNewSymbolName(), -128, 127); else if (valueType->containerTypeToken) { ValueType vt = ValueType::parseDecl(valueType->containerTypeToken, data.settings); value = getValueRangeFromValueType(&vt, data); } else return ExprEngine::ValuePtr(); auto bufferSize = std::make_shared(data.getNewSymbolName(), 0, ExprEngine::ArrayValue::MAXSIZE); return std::make_shared(data.getNewSymbolName(), bufferSize, value, false, false, false); } return getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); } static void call(const std::vector &callbacks, const Token *tok, ExprEngine::ValuePtr value, Data *dataBase) { if (value) { for (ExprEngine::Callback f : callbacks) { try { f(tok, *value, dataBase); } catch (const ExprEngineException &e) { throw ExprEngineException(tok, e.what); } } } } static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data); static ExprEngine::ValuePtr executeExpression1(const Token *tok, Data &data); static std::string execute(const Token *start, const Token *end, Data &data); static ExprEngine::ValuePtr calculateArrayIndex(const Token *tok, Data &data, const ExprEngine::ArrayValue &arrayValue) { int nr = 1; const Token *tok2 = tok; while (Token::simpleMatch(tok2->astOperand1(), "[")) { tok2 = tok2->astOperand1(); nr++; } ExprEngine::ValuePtr totalIndex; ExprEngine::ValuePtr dim; while (Token::simpleMatch(tok, "[")) { auto rawIndex = executeExpression(tok->astOperand2(), data); ExprEngine::ValuePtr index; if (dim) index = simplifyValue(std::make_shared("*", dim, rawIndex)); else index = rawIndex; if (!totalIndex) totalIndex = index; else totalIndex = simplifyValue(std::make_shared("+", index, totalIndex)); if (arrayValue.size.size() >= nr) { if (arrayValue.size[nr-1]) { if (!dim) dim = arrayValue.size[nr-1]; else dim = simplifyValue(std::make_shared("*", dim, arrayValue.size[nr-1])); } } nr--; tok = tok->astOperand1(); } return totalIndex; } static ExprEngine::ValuePtr executeReturn(const Token *tok, Data &data) { ExprEngine::ValuePtr retval = executeExpression(tok->astOperand1(), data); call(data.callbacks, tok, retval, &data); return retval; } static ExprEngine::ValuePtr truncateValue(ExprEngine::ValuePtr val, const ValueType *valueType, Data &data) { if (!valueType) return val; if (valueType->pointer != 0) return val; if (!valueType->isIntegral()) return val; // TODO int bits = getIntBitsFromValueType(valueType, *data.settings); if (bits == 0) // TODO return val; if (auto range = std::dynamic_pointer_cast(val)) { if (range->minValue == range->maxValue) { int128_t newValue = truncateInt(range->minValue, bits, valueType->sign == ValueType::Sign::SIGNED ? 's' : 'u'); if (newValue == range->minValue) return val; return std::make_shared(ExprEngine::str(newValue), newValue, newValue); } if (auto typeRange = getValueRangeFromValueType("", valueType, *data.settings)) { auto typeIntRange = std::dynamic_pointer_cast(typeRange); if (typeIntRange) { if (range->minValue >= typeIntRange->minValue && range->maxValue <= typeIntRange->maxValue) return val; } } return std::make_shared(data.getNewSymbolName(), val, bits, valueType->sign == ValueType::Sign::SIGNED ? 's' : 'u'); } // TODO return val; } static void assignExprValue(const Token *expr, ExprEngine::ValuePtr value, Data &data) { if (!expr) return; if (expr->varId() > 0) { data.assignValue(expr, expr->varId(), value); } else if (expr->str() == "[") { // Find array token const Token *arrayToken = expr; while (Token::simpleMatch(arrayToken, "[")) arrayToken = arrayToken->astOperand1(); if (!arrayToken) return; if (auto arrayValue = data.getArrayValue(arrayToken)) { // Is it array initialization? if (arrayToken->variable() && arrayToken->variable()->nameToken() == arrayToken) { if (value->type == ExprEngine::ValueType::StringLiteralValue) arrayValue->assign(ExprEngine::ValuePtr(), value); } else { auto indexValue = calculateArrayIndex(expr, data, *arrayValue); bool loopAssign = false; if (auto loopValue = std::dynamic_pointer_cast(indexValue)) { if (loopValue->loopScope == expr->scope()) { loopAssign = true; for (auto i = loopValue->minValue; i <= loopValue->maxValue; ++i) arrayValue->assign(std::make_shared(ExprEngine::str(i), i, i), value); } } if (!loopAssign) arrayValue->assign(indexValue, value); } } else { const Token * const indexToken = expr->astOperand2(); auto indexValue = executeExpression(indexToken, data); call(data.callbacks, indexToken, indexValue, &data); } } else if (expr->isUnaryOp("*")) { auto pval = executeExpression(expr->astOperand1(), data); if (pval && pval->type == ExprEngine::ValueType::AddressOfValue) { auto val = std::dynamic_pointer_cast(pval); if (val) data.assignValue(expr, val->varId, value); } else if (pval && pval->type == ExprEngine::ValueType::ArrayValue) { auto arrayValue = std::dynamic_pointer_cast(pval); auto indexValue = std::make_shared("0", 0, 0); arrayValue->assign(indexValue, value); } else if (pval && pval->type == ExprEngine::ValueType::BinOpResult) { auto b = std::dynamic_pointer_cast(pval); if (b && b->binop == "+") { std::shared_ptr arr; ExprEngine::ValuePtr offset; if (b->op1->type == ExprEngine::ValueType::ArrayValue) { arr = std::dynamic_pointer_cast(b->op1); offset = b->op2; } else { arr = std::dynamic_pointer_cast(b->op2); offset = b->op1; } if (arr && offset) { arr->assign(offset, value); } } } } else if (Token::Match(expr, ". %name%")) { auto structVal = executeExpression(expr->astOperand1(), data); if (structVal && structVal->type == ExprEngine::ValueType::StructValue) data.assignStructMember(expr, &*std::static_pointer_cast(structVal), expr->next()->str(), value); } } static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data) { ExprEngine::ValuePtr rhsValue = executeExpression(tok->astOperand2(), data); if (!rhsValue) { const ValueType * const vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType * const vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; rhsValue = getValueRangeFromValueType(vt1, data); if (!rhsValue && vt2 && vt2->pointer == 0) { rhsValue = getValueRangeFromValueType(vt2, data); if (rhsValue) call(data.callbacks, tok->astOperand2(), rhsValue, &data); } if (!rhsValue) rhsValue = std::make_shared(); } ExprEngine::ValuePtr assignValue; if (tok->str() == "=") assignValue = rhsValue; else { // "+=" => "+" std::string binop(tok->str()); binop = binop.substr(0, binop.size() - 1); ExprEngine::ValuePtr lhsValue = executeExpression(tok->astOperand1(), data); assignValue = simplifyValue(std::make_shared(binop, lhsValue, rhsValue)); } const Token *lhsToken = tok->astOperand1(); if (lhsToken) assignValue = truncateValue(assignValue, lhsToken->valueType(), data); call(data.callbacks, tok, assignValue, &data); assignExprValue(lhsToken, assignValue, data); return assignValue; } static ExprEngine::ValuePtr executeIncDec(const Token *tok, Data &data) { ExprEngine::ValuePtr beforeValue = executeExpression(tok->astOperand1(), data); ExprEngine::ValuePtr assignValue = simplifyValue(std::make_shared(tok->str().substr(0,1), beforeValue, std::make_shared("1", 1, 1))); assignExprValue(tok->astOperand1(), assignValue, data); auto retVal = (precedes(tok, tok->astOperand1())) ? assignValue : beforeValue; call(data.callbacks, tok, retVal, &data); return retVal; } #ifdef USE_Z3 static void checkContract(Data &data, const Token *tok, const Function *function, const std::vector &argValues) { #ifdef CONTRACT ExprData exprData; z3::solver solver(exprData.context); try { // Invert contract, we want to know if the contract might not be met try { solver.add(z3::ite(exprData.getConstraintExpr(data.executeContract(function, executeExpression1)), exprData.context.bool_val(false), exprData.context.bool_val(true))); } catch (const ExprData::BailoutValueException &) { throw ExprEngineException(tok, "Internal error: Bailout value used"); } bool bailoutValue = false; for (nonneg int i = 0; i < argValues.size(); ++i) { const Variable *argvar = function->getArgumentVar(i); if (!argvar || !argvar->nameToken()) continue; ExprEngine::ValuePtr argValue = argValues[i]; if (!argValue || argValue->type == ExprEngine::ValueType::BailoutValue) { bailoutValue = true; break; } if (argValue && argValue->type == ExprEngine::ValueType::IntRange) { solver.add(exprData.getExpr(data.getValue(argvar->declarationId(), nullptr, nullptr)) == exprData.getExpr(argValue)); } } if (!bailoutValue) { for (auto constraint : data.constraints) solver.add(exprData.getConstraintExpr(constraint)); exprData.addAssertions(solver); // Log solver expressions for debugging/testing purposes std::ostringstream os; os << solver; data.trackCheckContract(tok, os.str()); } if (bailoutValue || solver.check() == z3::sat) { const char id[] = "bughuntingFunctionCall"; const auto contractIt = data.settings->functionContracts.find(function->fullName()); const std::string functionName = contractIt->first; const std::string functionExpects = contractIt->second; data.reportError(tok, Severity::SeverityType::error, id, "Function '" + function->name() + "' is called, can not determine that its contract '" + functionExpects + "' is always met.", CWE(0), false, bailoutValue, functionName); } } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; } catch (const ExprEngineException &) { const char id[] = "internalErrorInExprEngine"; const auto contractIt = data.settings->functionContracts.find(function->fullName()); const std::string functionName = contractIt->first; const std::string functionExpects = contractIt->second; data.reportError(tok, Severity::SeverityType::error, id, "Function '" + function->name() + "' is called, can not determine that its contract '" + functionExpects + "' is always met.", CWE(0), false, true, functionName); } #endif } #endif static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) { if (Token::simpleMatch(tok->previous(), "sizeof (")) { ExprEngine::ValuePtr retVal; if (tok->hasKnownIntValue()) { const MathLib::bigint value = tok->getKnownIntValue(); retVal = std::make_shared(std::to_string(value), value, value); } else { retVal = std::make_shared(data.getNewSymbolName(), 1, 0x7fffffff); } call(data.callbacks, tok, retVal, &data); return retVal; } bool hasBody = tok->astOperand1()->function() && tok->astOperand1()->function()->hasBody(); if (hasBody) { const Scope *functionScope = tok->scope(); while (functionScope->isExecutable() && functionScope->type != Scope::ScopeType::eFunction) functionScope = functionScope->nestedIn; if (functionScope == tok->astOperand1()->function()->functionScope) hasBody = false; for (const auto &errorPathItem: data.errorPath) { if (errorPathItem.first == tok) { hasBody = false; break; } } } const std::vector &argTokens = getArguments(tok); std::vector argValues; for (const Token *argtok : argTokens) { auto val = hasBody ? executeExpression1(argtok, data) : executeExpression(argtok, data); argValues.push_back(val); if (hasBody) continue; if (!argtok->valueType() || (argtok->valueType()->constness & 1) == 1) continue; if (auto arrayValue = std::dynamic_pointer_cast(val)) { ValueType vt(*argtok->valueType()); vt.pointer = 0; auto anyVal = getValueRangeFromValueType(&vt, data); arrayValue->assign(ExprEngine::ValuePtr(), anyVal); } else if (auto addressOf = std::dynamic_pointer_cast(val)) { ValueType vt(*argtok->valueType()); vt.pointer = 0; if (vt.isIntegral() && argtok->valueType()->pointer == 1) data.assignValue(argtok, addressOf->varId, getValueRangeFromValueType(&vt, data)); } } call(data.callbacks, tok, std::make_shared(argValues), &data); if (tok->astOperand1()->function()) { const Function *function = tok->astOperand1()->function(); const std::string &functionName = function->fullName(); #ifdef CONTRACT const auto contractIt = data.settings->functionContracts.find(functionName); if (contractIt != data.settings->functionContracts.end()) { #ifdef USE_Z3 checkContract(data, tok, function, argValues); #endif } else if (!argValues.empty()) { bool bailout = false; for (const auto &v: argValues) bailout |= (v && v->type == ExprEngine::ValueType::BailoutValue); if (!bailout) data.addMissingContract(functionName); } #endif // Execute subfunction.. if (hasBody) { const Scope * const functionScope = function->functionScope; int argnr = 0; std::map refs; for (const Variable &arg: function->argumentList) { if (argnr < argValues.size() && arg.declarationId() > 0) { if (arg.isReference()) refs[argTokens[argnr]] = arg.declarationId(); else argValues[argnr] = translateUninitValueToRange(argValues[argnr], arg.valueType(), data); data.assignValue(function->functionScope->bodyStart, arg.declarationId(), argValues[argnr]); } // TODO default values! argnr++; } data.contractConstraints(function, executeExpression1); data.errorPath.push_back(ErrorPathItem(tok, "Calling " + function->name())); try { data.load(execute(functionScope->bodyStart, functionScope->bodyEnd, data)); for (auto ref: refs) { auto v = data.getValue(ref.second, nullptr, nullptr); assignExprValue(ref.first, v, data); } } catch (ExprEngineException &e) { data.errorPath.pop_back(); e.tok = tok; throw e; } data.errorPath.pop_back(); } } else if (const auto *f = data.settings->library.getAllocFuncInfo(tok->astOperand1())) { if (!f->initData) { const std::string name = data.getNewSymbolName(); auto size = std::make_shared(data.getNewSymbolName(), 1, MAX_BUFFER_SIZE); auto val = std::make_shared(); auto result = std::make_shared(name, size, val, false, false, false); call(data.callbacks, tok, result, &data); data.functionCall(); return result; } } auto result = getValueRangeFromValueType(tok->valueType(), data); call(data.callbacks, tok, result, &data); data.functionCall(); return result; } static ExprEngine::ValuePtr executeArrayIndex(const Token *tok, Data &data) { if (tok->tokType() == Token::eLambda) throw ExprEngineException(tok, "FIXME: lambda"); const Token *tok2 = tok; while (Token::simpleMatch(tok2->astOperand1(), "[")) tok2 = tok2->astOperand1(); auto arrayValue = data.getArrayValue(tok2->astOperand1()); if (arrayValue) { auto indexValue = calculateArrayIndex(tok, data, *arrayValue); auto conditionalValues = arrayValue->read(indexValue); for (auto value: conditionalValues) call(data.callbacks, tok, value.second, &data); if (conditionalValues.size() == 1 && !conditionalValues[0].first) return conditionalValues[0].second; return std::make_shared(data.getNewSymbolName(), conditionalValues); } // TODO: Pointer value.. executeExpression(tok->astOperand1(), data); executeExpression(tok->astOperand2(), data); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeCast(const Token *tok, Data &data) { const Token *expr = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); auto val = executeExpression(expr, data); if (expr->valueType() && expr->valueType()->type == ::ValueType::Type::VOID && expr->valueType()->pointer > 0) { if (!tok->valueType() || expr->valueType()->pointer < tok->valueType()->pointer) return std::make_shared(); auto range = std::make_shared(); if (tok->valueType()->pointer == 0) return range; bool uninitPointer = false, nullPointer = false; if (val && val->type == ExprEngine::ValueType::ArrayValue) { nullPointer = std::static_pointer_cast(val)->nullPointer; uninitPointer = std::static_pointer_cast(val)->uninitPointer; } auto bufferSize = std::make_shared(data.getNewSymbolName(), 1, MAX_BUFFER_SIZE); return std::make_shared(data.getNewSymbolName(), bufferSize, range, true, nullPointer, uninitPointer); } if (val) { // TODO: Cast this.. call(data.callbacks, tok, val, &data); return val; } val = getValueRangeFromValueType(tok->valueType(), data); call(data.callbacks, tok, val, &data); return val; } static ExprEngine::ValuePtr executeDot(const Token *tok, Data &data) { if (!tok->astOperand1()) { auto v = std::make_shared(); call(data.callbacks, tok, v, &data); return v; } std::shared_ptr structValue = std::dynamic_pointer_cast(executeExpression(tok->astOperand1(), data)); if (!structValue) { if (tok->originalName() == "->") { std::shared_ptr pointerValue = std::dynamic_pointer_cast(data.getValue(tok->astOperand1()->varId(), nullptr, nullptr)); if (pointerValue && pointerValue->pointer && !pointerValue->data.empty()) { call(data.callbacks, tok->astOperand1(), pointerValue, &data); auto indexValue = std::make_shared("0", 0, 0); ExprEngine::ValuePtr ret; for (auto val: pointerValue->read(indexValue)) { structValue = std::dynamic_pointer_cast(val.second); if (structValue) { auto memberValue = structValue->getValueOfMember(tok->astOperand2()->str()); call(data.callbacks, tok, memberValue, &data); if (!ret) ret = memberValue; } } return ret; } else { call(data.callbacks, tok->astOperand1(), data.getValue(tok->astOperand1()->varId(), nullptr, nullptr), &data); } } auto v = getValueRangeFromValueType(tok->valueType(), data); if (!v) v = std::make_shared(); call(data.callbacks, tok, v, &data); return v; } call(data.callbacks, tok->astOperand1(), structValue, &data); ExprEngine::ValuePtr memberValue = structValue->getValueOfMember(tok->astOperand2()->str()); call(data.callbacks, tok, memberValue, &data); return memberValue; } static void streamReadSetValue(const Token *tok, Data &data) { if (!tok || !tok->valueType()) return; if (tok->varId() > 0 && tok->valueType()->pointer) { const auto oldValue = data.getValue(tok->varId(), tok->valueType(), tok); if (oldValue && (oldValue->isUninit(&data))) call(data.callbacks, tok, oldValue, &data); } auto rangeValue = getValueRangeFromValueType(tok->valueType(), data); if (rangeValue) assignExprValue(tok, rangeValue, data); } static ExprEngine::ValuePtr executeStreamRead(const Token *tok, Data &data) { tok = tok->astOperand2(); while (Token::simpleMatch(tok, ">>")) { streamReadSetValue(tok->astOperand1(), data); tok = tok->astOperand2(); } streamReadSetValue(tok, data); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeBinaryOp(const Token *tok, Data &data) { ExprEngine::ValuePtr v1 = executeExpression(tok->astOperand1(), data); ExprEngine::ValuePtr v2; if (tok->str() == "?") { if (tok->astOperand1()->hasKnownIntValue()) { if (tok->astOperand1()->getKnownIntValue()) v2 = executeExpression(tok->astOperand2()->astOperand1(), data); else v2 = executeExpression(tok->astOperand2()->astOperand2(), data); call(data.callbacks, tok, v2, &data); return v2; } Data trueData(data); trueData.addConstraint(v1, true); auto trueValue = simplifyValue(executeExpression(tok->astOperand2()->astOperand1(), trueData)); Data falseData(data); falseData.addConstraint(v1, false); auto falseValue = simplifyValue(executeExpression(tok->astOperand2()->astOperand2(), falseData)); auto result = simplifyValue(std::make_shared("?", v1, std::make_shared(":", trueValue, falseValue))); call(data.callbacks, tok, result, &data); return result; } else if (tok->str() == "&&" || tok->str() == "||") { Data data2(data); data2.addConstraint(v1, tok->str() == "&&"); v2 = executeExpression(tok->astOperand2(), data2); } else { v2 = executeExpression(tok->astOperand2(), data); } if (v1 && v2) { auto result = simplifyValue(std::make_shared(tok->str(), v1, v2)); call(data.callbacks, tok, result, &data); return result; } if (tok->str() == "&&" && (v1 || v2)) { auto result = v1 ? v1 : v2; call(data.callbacks, tok, result, &data); return result; } return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeAddressOf(const Token *tok, Data &data) { auto addr = std::make_shared(data.getNewSymbolName(), tok->astOperand1()->varId()); call(data.callbacks, tok, addr, &data); return addr; } static ExprEngine::ValuePtr executeDeref(const Token *tok, Data &data) { ExprEngine::ValuePtr pval = executeExpression(tok->astOperand1(), data); if (!pval) { auto v = getValueRangeFromValueType(tok->valueType(), data); if (tok->astOperand1()->varId()) { pval = std::make_shared(data.getNewSymbolName(), ExprEngine::ValuePtr(), v, true, false, false); data.assignValue(tok->astOperand1(), tok->astOperand1()->varId(), pval); } call(data.callbacks, tok, v, &data); return v; } auto addressOf = std::dynamic_pointer_cast(pval); if (addressOf) { auto val = data.getValue(addressOf->varId, tok->valueType(), tok); call(data.callbacks, tok, val, &data); return val; } auto pointer = std::dynamic_pointer_cast(pval); if (pointer) { auto indexValue = std::make_shared("0", 0, 0); auto conditionalValues = pointer->read(indexValue); for (auto value: conditionalValues) call(data.callbacks, tok, value.second, &data); if (conditionalValues.size() == 1 && !conditionalValues[0].first) return conditionalValues[0].second; return std::make_shared(data.getNewSymbolName(), conditionalValues); } return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeNot(const Token *tok, Data &data) { ExprEngine::ValuePtr v = executeExpression(tok->astOperand1(), data); if (!v) return v; ExprEngine::ValuePtr zero = std::make_shared("0", 0, 0); auto result = simplifyValue(std::make_shared("==", v, zero)); call(data.callbacks, tok, result, &data); return result; } static ExprEngine::ValuePtr executeVariable(const Token *tok, Data &data) { auto val = data.getValue(tok->varId(), tok->valueType(), tok); call(data.callbacks, tok, val, &data); return val; } static ExprEngine::ValuePtr executeKnownMacro(const Token *tok, Data &data) { const auto intval = tok->getKnownIntValue(); auto val = std::make_shared(std::to_string(intval), intval, intval); call(data.callbacks, tok, val, &data); return val; } static ExprEngine::ValuePtr executeNumber(const Token *tok, Data &data) { if (tok->valueType()->isFloat()) { long double value = MathLib::toDoubleNumber(tok->str()); auto v = std::make_shared(tok->str(), value, value); call(data.callbacks, tok, v, &data); return v; } int128_t value = MathLib::toLongNumber(tok->str()); auto v = std::make_shared(tok->str(), value, value); call(data.callbacks, tok, v, &data); return v; } static ExprEngine::ValuePtr executeStringLiteral(const Token *tok, Data &data) { const std::string& s = tok->str(); return std::make_shared(data.getNewSymbolName(), s.substr(1, s.size()-2)); } static ExprEngine::ValuePtr executeExpression1(const Token *tok, Data &data) { if (Settings::terminated()) throw TerminateExpression(); if (tok->str() == "return") return executeReturn(tok, data); if (tok->isAssignmentOp()) // TODO: Handle more operators return executeAssign(tok, data); if (tok->tokType() == Token::Type::eIncDecOp) return executeIncDec(tok, data); if (tok->astOperand1() && tok->astOperand2() && tok->str() == "[") return executeArrayIndex(tok, data); if (tok->str() == "(") { if (!tok->isCast()) return executeFunctionCall(tok, data); return executeCast(tok, data); } if (tok->str() == ".") return executeDot(tok, data); if (tok->str() == "::" && tok->hasKnownIntValue()) { // TODO handle :: better auto v = tok->getKnownIntValue(); return std::make_shared(std::to_string(v), v, v); } if (data.tokenizer->isCPP() && tok->str() == ">>" && !tok->astParent() && tok->isBinaryOp() && Token::Match(tok->astOperand1(), "%name%|::")) return executeStreamRead(tok, data); if (tok->astOperand1() && tok->astOperand2()) return executeBinaryOp(tok, data); if (tok->isUnaryOp("&") && Token::Match(tok->astOperand1(), "%var%")) return executeAddressOf(tok, data); if (tok->isUnaryOp("*")) return executeDeref(tok, data); if (tok->isUnaryOp("!")) return executeNot(tok, data); if (tok->varId()) return executeVariable(tok, data); if (tok->isName() && tok->hasKnownIntValue()) return executeKnownMacro(tok, data); if (tok->isNumber() || tok->tokType() == Token::Type::eChar) return executeNumber(tok, data); if (tok->tokType() == Token::Type::eString) return executeStringLiteral(tok, data); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data) { return translateUninitValueToRange(executeExpression1(tok, data), tok->valueType(), data); } static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data); static std::tuple checkConditionBranches(const ExprEngine::ValuePtr &condValue, const Data &data) { bool canBeFalse = true; bool canBeTrue = true; if (auto b = std::dynamic_pointer_cast(condValue)) { canBeFalse = b->isEqual(&data, 0); canBeTrue = b->isTrue(&data); } else if (auto i = std::dynamic_pointer_cast(condValue)) { canBeFalse = i->isEqual(&data, 0); canBeTrue = ExprEngine::BinOpResult("!=", i, std::make_shared("0", 0, 0)).isTrue(&data); } else if (std::dynamic_pointer_cast(condValue)) { canBeFalse = false; canBeTrue = true; } else if (auto f = std::dynamic_pointer_cast(condValue)) { canBeFalse = f->isEqual(&data, 0); canBeTrue = ExprEngine::BinOpResult("!=", f, std::make_shared("0.0", 0.0, 0.0)).isTrue(&data); } return std::make_tuple(canBeFalse, canBeTrue); } static std::string execute(const Token *start, const Token *end, Data &data) { if (data.recursion > 20) // FIXME return data.str(); // Update data.recursion struct Recursion { Recursion(int *var, int value) : var(var), value(value) { *var = value + 1; } ~Recursion() { if (*var >= value) *var = value; } int *var; int value; }; Recursion updateRecursion(&data.recursion, data.recursion); const std::time_t stopTime = data.startTime + data.settings->bugHuntingCheckFunctionMaxTime; for (const Token *tok = start; tok != end; tok = tok->next()) { if (Token::Match(tok, "[;{}]")) { data.trackProgramState(tok); if (tok->str() == ";") { const Token *prev = tok->previous(); while (prev && !Token::Match(prev, "[;{}]")) prev = prev->previous(); if (Token::Match(prev, "[;{}] return|throw")) return data.str(); } while (Token::simpleMatch(tok, "} catch (") && Token::simpleMatch(tok->linkAt(2), ") {")) { tok = tok->linkAt(2)->next()->link(); } if (std::time(nullptr) > stopTime) return ""; } if (Token::simpleMatch(tok, "__CPPCHECK_BAILOUT__ ;")) // This is intended for testing throw ExprEngineException(tok, "__CPPCHECK_BAILOUT__"); if (Token::simpleMatch(tok, "while (") && Token::simpleMatch(tok->linkAt(1), ") ;") && tok->next()->astOperand1()->hasKnownIntValue() && tok->next()->astOperand1()->getKnownIntValue() == 0) { tok = tok->tokAt(4); continue; } if (tok->str() == "break") { const Scope *scope = tok->scope(); while (scope->type == Scope::eIf || scope->type == Scope::eElse) scope = scope->nestedIn; tok = scope->bodyEnd; if (!precedes(tok,end)) return data.str(); } if (Token::simpleMatch(tok, "try {") && Token::simpleMatch(tok->linkAt(1), "} catch (")) { const Token *catchTok = tok->linkAt(1); while (Token::simpleMatch(catchTok, "} catch (")) { Data catchData(data); catchTok = catchTok->linkAt(2)->next(); execute(catchTok, end, catchData); catchTok = catchTok->link(); } } // Variable declaration.. if (tok->variable() && tok->variable()->nameToken() == tok) { if (Token::Match(tok, "%varid% ; %varid% =", tok->varId())) { // if variable is not used in assignment rhs then we do not need to create a "confusing" variable value.. bool foundInRhs = false; visitAstNodes(tok->tokAt(3)->astOperand2(), [&](const Token *rhs) { if (rhs->varId()==tok->varId()) { foundInRhs = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (!foundInRhs) { tok = tok->tokAt(2); continue; } data.assignValue(tok, tok->varId(), createVariableValue(*tok->variable(), data)); } else if (tok->variable()->isArray()) { data.assignValue(tok, tok->varId(), std::make_shared(&data, tok->variable())); if (Token::Match(tok, "%name% [")) tok = tok->linkAt(1); } else if (Token::Match(tok, "%var% ;")) data.assignValue(tok, tok->varId(), createVariableValue(*tok->variable(), data)); } else if (!tok->astParent() && (tok->astOperand1() || tok->astOperand2())) { executeExpression(tok, data); if (Token::Match(tok, "throw|return")) return data.str(); } else if (Token::simpleMatch(tok, "if (")) { const Token *cond = tok->next()->astOperand2(); // TODO: C++17 condition const ExprEngine::ValuePtr condValue = executeExpression(cond, data); bool canBeFalse, canBeTrue; std::tie(canBeFalse, canBeTrue) = checkConditionBranches(condValue, data); Data &thenData(data); Data elseData(data); if (canBeFalse && canBeTrue) { // Avoid that constraints are overspecified thenData.addConstraint(condValue, true); elseData.addConstraint(condValue, false); } Data::ifSplit(tok, thenData, elseData); const Token *thenStart = tok->linkAt(1)->next(); const Token *thenEnd = thenStart->link(); const Token *exceptionToken = nullptr; std::string exceptionMessage; auto exec = [&](const Token *tok1, const Token *tok2, Data& data) { try { execute(tok1, tok2, data); } catch (ExprEngineException &e) { if (!exceptionToken || (e.tok && precedes(e.tok, exceptionToken))) { exceptionToken = e.tok; exceptionMessage = e.what; } } }; if (canBeTrue) exec(thenStart->next(), end, thenData); if (canBeFalse) { if (Token::simpleMatch(thenEnd, "} else {")) { const Token *elseStart = thenEnd->tokAt(2); exec(elseStart->next(), end, elseData); } else { exec(thenEnd, end, elseData); } } if (exceptionToken) throw ExprEngineException(exceptionToken, exceptionMessage); return (canBeTrue ? thenData.str() : std::string()) + (canBeFalse ? elseData.str() : std::string()); } else if (Token::simpleMatch(tok, "switch (")) { auto condValue = executeExpression(tok->next()->astOperand2(), data); // TODO: C++17 condition const Token *bodyStart = tok->linkAt(1)->next(); const Token *bodyEnd = bodyStart->link(); const Token *defaultStart = nullptr; Data defaultData(data); const Token *exceptionToken = nullptr; std::string exceptionMessage; std::ostringstream ret; auto exec = [&](const Token *tok1, const Token *tok2, Data& data) { try { execute(tok1, tok2, data); ret << data.str(); } catch (ExprEngineException &e) { if (!exceptionToken || (e.tok && precedes(e.tok, exceptionToken))) { exceptionToken = e.tok; exceptionMessage = e.what; } } }; for (const Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") tok2 = tok2->link(); else if (Token::Match(tok2, "case %char%|%num% :")) { const MathLib::bigint caseValue1 = tok2->next()->getKnownIntValue(); auto caseValue = std::make_shared(MathLib::toString(caseValue1), caseValue1, caseValue1); Data caseData(data); caseData.addConstraint(condValue, caseValue, true); defaultData.addConstraint(condValue, caseValue, false); exec(tok2->tokAt(2), end, caseData); } else if (Token::Match(tok2, "case %name% :") && !Token::Match(tok2->tokAt(3), ";| case")) { Data caseData(data); exec(tok2->tokAt(2), end, caseData); } else if (Token::simpleMatch(tok2, "default :")) defaultStart = tok2; } exec(defaultStart ? defaultStart : bodyEnd, end, defaultData); if (exceptionToken) throw ExprEngineException(exceptionToken, exceptionMessage); return ret.str(); } if (Token::simpleMatch(tok, "for (")) { nonneg int varid; bool hasKnownInitValue, partialCond; MathLib::bigint initValue, stepValue, lastValue; if (extractForLoopValues(tok, &varid, &hasKnownInitValue, &initValue, &partialCond, &stepValue, &lastValue) && hasKnownInitValue && !partialCond) { auto loopValues = std::make_shared(data.getNewSymbolName(), initValue, lastValue); data.assignValue(tok, varid, loopValues); tok = tok->linkAt(1); if (tok->next()) { loopValues->loopScope = tok->next()->scope(); // Check whether the condition expression is always false if (initValue > lastValue) { tok = tok->next()->link(); } } continue; } } if (Token::Match(tok, "for|while (") && Token::simpleMatch(tok->linkAt(1), ") {")) { const Token *cond = tok->next()->astOperand2(); const ExprEngine::ValuePtr condValue = executeExpression(cond, data); bool canBeFalse = false, canBeTrue = true; if (tok->str() == "while") std::tie(canBeFalse, canBeTrue) = checkConditionBranches(condValue, data); Data &bodyData(data); Data noexecData(data); if (canBeFalse && canBeTrue) { // Avoid that constraints are overspecified bodyData.addConstraint(condValue, true); } Data::ifSplit(tok, bodyData, noexecData); const Token *bodyStart = tok->linkAt(1)->next(); const Token *bodyEnd = bodyStart->link(); // TODO this is very rough code if (canBeTrue) { std::set changedVariables; for (const Token *tok2 = tok; tok2 != bodyEnd; tok2 = tok2->next()) { if (Token::Match(tok2, "%assign%")) { const Token *lhs = tok2->astOperand1(); while (Token::simpleMatch(lhs, "[")) lhs = lhs->astOperand1(); if (!lhs) throw ExprEngineException(tok2, "Unhandled assignment in loop"); if (Token::Match(lhs, ". %name% =|[") && Token::simpleMatch(lhs->astOperand1(), ".")) { const Token *structToken = lhs; while (Token::Match(structToken, ".|[")) structToken = structToken->astOperand1(); if (Token::Match(structToken, "%var%")) { bodyData.assignValue(structToken, structToken->varId(), std::make_shared()); changedVariables.insert(structToken->varId()); continue; } } if (Token::Match(lhs, ". %name% =|[") && lhs->astOperand1() && lhs->astOperand1()->valueType()) { const Token *structToken = lhs->astOperand1(); if (!structToken->valueType() || !structToken->varId()) throw ExprEngineException(tok2, "Unhandled assignment in loop"); const Scope *structScope = structToken->valueType()->typeScope; if (!structScope) throw ExprEngineException(tok2, "Unhandled assignment in loop"); const std::string &memberName = tok2->previous()->str(); ExprEngine::ValuePtr memberValue; for (const Variable &member : structScope->varlist) { if (memberName == member.name() && member.valueType()) { memberValue = createVariableValue(member, bodyData); break; } } if (!memberValue) throw ExprEngineException(tok2, "Unhandled assignment in loop"); ExprEngine::ValuePtr structVal1 = bodyData.getValue(structToken->varId(), structToken->valueType(), structToken); if (!structVal1) structVal1 = createVariableValue(*structToken->variable(), bodyData); auto structVal = std::dynamic_pointer_cast(structVal1); if (!structVal) { // Handle pointer to a struct if (auto structPtr = std::dynamic_pointer_cast(structVal1)) { if (structPtr->pointer && !structPtr->data.empty()) { auto indexValue = std::make_shared("0", 0, 0); for (auto val: structPtr->read(indexValue)) { structVal = std::dynamic_pointer_cast(val.second); } } } if (!structVal) throw ExprEngineException(tok2, "Unhandled assignment in loop"); } bodyData.assignStructMember(tok2, &*structVal, memberName, memberValue); continue; } if (lhs->isUnaryOp("*") && lhs->astOperand1()->varId()) { const Token *varToken = tok2->astOperand1()->astOperand1(); ExprEngine::ValuePtr val = bodyData.getValue(varToken->varId(), varToken->valueType(), varToken); if (val && val->type == ExprEngine::ValueType::ArrayValue) { // Try to assign "any" value auto arrayValue = std::dynamic_pointer_cast(val); arrayValue->assign(std::make_shared("0", 0, 0), std::make_shared()); continue; } } if (!lhs->variable()) throw ExprEngineException(tok2, "Unhandled assignment in loop"); // give variable "any" value int varid = lhs->varId(); if (changedVariables.find(varid) != changedVariables.end()) continue; changedVariables.insert(varid); auto oldValue = bodyData.getValue(varid, nullptr, nullptr); if (oldValue && oldValue->isUninit(&bodyData)) call(bodyData.callbacks, lhs, oldValue, &bodyData); if (oldValue && oldValue->type == ExprEngine::ValueType::ArrayValue) { // Try to assign "any" value auto arrayValue = std::dynamic_pointer_cast(oldValue); arrayValue->assign(std::make_shared(bodyData.getNewSymbolName(), 0, MAX_BUFFER_SIZE), std::make_shared()); continue; } bodyData.assignValue(tok2, varid, getValueRangeFromValueType(lhs->valueType(), bodyData)); continue; } else if (Token::Match(tok2, "++|--") && tok2->astOperand1() && tok2->astOperand1()->variable()) { // give variable "any" value const Token *vartok = tok2->astOperand1(); int varid = vartok->varId(); if (changedVariables.find(varid) != changedVariables.end()) continue; changedVariables.insert(varid); auto oldValue = bodyData.getValue(varid, nullptr, nullptr); if (oldValue && oldValue->type == ExprEngine::ValueType::UninitValue) call(bodyData.callbacks, tok2, oldValue, &bodyData); bodyData.assignValue(tok2, varid, getValueRangeFromValueType(vartok->valueType(), bodyData)); } } } const Token *exceptionToken = nullptr; std::string exceptionMessage; auto exec = [&](const Token *tok1, const Token *tok2, Data& data) { try { execute(tok1, tok2, data); } catch (ExprEngineException &e) { if (!exceptionToken || (e.tok && precedes(e.tok, exceptionToken))) { exceptionToken = e.tok; exceptionMessage = e.what; } } }; if (canBeTrue) exec(bodyStart->next(), end, bodyData); if (canBeFalse) exec(bodyEnd, end, noexecData); if (exceptionToken) throw ExprEngineException(exceptionToken, exceptionMessage); return (canBeTrue ? bodyData.str() : std::string()) + (canBeFalse ? noexecData.str() : std::string()); } if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); } return data.str(); } void ExprEngine::executeAllFunctions(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &report) { const SymbolDatabase *symbolDatabase = tokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { try { executeFunction(functionScope, errorLogger, tokenizer, settings, callbacks, report); } catch (const ExprEngineException &e) { // FIXME.. there should not be exceptions std::string functionName = functionScope->function->name(); std::cout << "Verify: Aborted analysis of function '" << functionName << "':" << e.tok->linenr() << ": " << e.what << std::endl; } catch (const std::exception &e) { // FIXME.. there should not be exceptions std::string functionName = functionScope->function->name(); std::cout << "Verify: Aborted analysis of function '" << functionName << "': " << e.what() << std::endl; } catch (const TerminateExpression &) { break; } } } static ExprEngine::ValuePtr createStructVal(const Token *tok, const Scope *structScope, bool uninitData, Data &data) { if (!structScope) return ExprEngine::ValuePtr(); std::shared_ptr structValue = std::make_shared(data.getNewSymbolName()); auto uninitValue = std::make_shared(); for (const Variable &member : structScope->varlist) { if (uninitData && !member.isInit()) { if (member.isPointer()) { structValue->member[member.name()] = uninitValue; continue; } if (member.valueType() && member.valueType()->type >= ::ValueType::Type::CHAR) { structValue->member[member.name()] = uninitValue; continue; } } if (member.valueType() && member.valueType()->isIntegral()) { ExprEngine::ValuePtr memberValue = createVariableValue(member, data); if (memberValue) data.assignStructMember(tok, structValue.get(), member.name(), memberValue); } } return structValue; } static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data) { if (!var.nameToken()) return ExprEngine::ValuePtr(); const ValueType *valueType = var.valueType(); if (!valueType || valueType->type == ValueType::Type::UNKNOWN_TYPE) valueType = var.nameToken()->valueType(); if (!valueType || valueType->type == ValueType::Type::UNKNOWN_TYPE) { // variable with unknown type if (var.isLocal() && var.isPointer() && !var.isArray()) return std::make_shared(); return ExprEngine::ValuePtr(); } if (valueType->pointer > 0) { if (var.isLocal()) return std::make_shared(); auto bufferSize = std::make_shared(data.getNewSymbolName(), 1, MAX_BUFFER_SIZE); ExprEngine::ValuePtr pointerValue; if (valueType->type == ValueType::Type::RECORD) pointerValue = createStructVal(var.nameToken(), valueType->typeScope, var.isLocal() && !var.isStatic(), data); else { ValueType vt(*valueType); vt.pointer = 0; if (vt.constness & 1) pointerValue = getValueRangeFromValueType(&vt, data); else pointerValue = std::make_shared(); } return std::make_shared(data.getNewSymbolName(), bufferSize, pointerValue, true, true, var.isLocal() && !var.isStatic()); } if (var.isArray()) return std::make_shared(&data, &var); if (valueType->isIntegral() || valueType->isFloat()) { ExprEngine::ValuePtr value; if (var.isLocal() && !var.isStatic()) value = std::make_shared(); else value = getValueRangeFromValueType(valueType, data); data.addConstraints(value, var.nameToken()); return value; } if (valueType->type == ValueType::Type::RECORD) { bool uninitData = true; if (var.isLocal() && !var.isStatic()) { uninitData = !valueType->typeScope || !valueType->typeScope->definedType || valueType->typeScope->definedType->needInitialization != Type::NeedInitialization::False; } if (var.isArgument() && var.isConst()) uninitData = false; return createStructVal(var.nameToken(), valueType->typeScope, uninitData, data); } if (valueType->smartPointerType) { auto structValue = createStructVal(var.nameToken(), valueType->smartPointerType->classScope, var.isLocal() && !var.isStatic(), data); auto size = std::make_shared(data.getNewSymbolName(), 1, MAX_BUFFER_SIZE); return std::make_shared(data.getNewSymbolName(), size, structValue, true, true, false); } return getValueRangeFromValueType(valueType, data); } void ExprEngine::executeFunction(const Scope *functionScope, ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &report) { if (!functionScope->bodyStart) return; const Function *function = functionScope->function; if (!function) return; if (functionScope->bodyStart->fileIndex() > 0) // TODO.. what about functions in headers? return; const std::string currentFunction = function->fullName(); int symbolValueIndex = 0; TrackExecution trackExecution; Data data(&symbolValueIndex, errorLogger, tokenizer, settings, currentFunction, callbacks, &trackExecution); for (const Variable &arg : function->argumentList) data.assignValue(functionScope->bodyStart, arg.declarationId(), createVariableValue(arg, data)); #ifdef CONTRACT data.contractConstraints(function, executeExpression1); #endif const std::time_t stopTime = data.startTime + data.settings->bugHuntingCheckFunctionMaxTime; try { execute(functionScope->bodyStart, functionScope->bodyEnd, data); } catch (const ExprEngineException &e) { if (settings->debugBugHunting) report << "ExprEngineException " << e.tok->linenr() << ":" << e.tok->column() << ": " << e.what << "\n"; trackExecution.setAbortLine(e.tok->linenr()); auto bailoutValue = std::make_shared(); for (const Token *tok = e.tok; tok != functionScope->bodyEnd; tok = tok->next()) { if (std::time(nullptr) >= stopTime) break; if (Token::Match(tok, "return|throw|while|if|for (")) { tok = tok->next(); continue; } call(callbacks, tok, bailoutValue, &data); } } const bool bugHuntingReport = !settings->bugHuntingReport.empty(); if (settings->debugBugHunting && (settings->verbose || callbacks.empty() || !trackExecution.isAllOk())) { if (bugHuntingReport) report << "[debug]" << std::endl; trackExecution.print(report); if (!callbacks.empty()) { if (bugHuntingReport) report << "[details]" << std::endl; trackExecution.report(report, functionScope); } } // Write a report if (bugHuntingReport) { std::set intvars; for (const Scope &scope: tokenizer->getSymbolDatabase()->scopeList) { if (scope.isExecutable()) continue; std::string path; bool valid = true; for (const Scope *s = &scope; s->type != Scope::ScopeType::eGlobal; s = s->nestedIn) { if (s->isExecutable()) { valid = false; break; } path = s->className + "::" + path; } if (!valid) continue; for (const Variable &var: scope.varlist) { if (var.nameToken() && !var.nameToken()->hasCppcheckAttributes() && var.valueType() && var.valueType()->pointer == 0 && var.valueType()->constness == 0 && var.valueType()->isIntegral()) intvars.insert(path + var.name()); } } for (const std::string &v: intvars) report << "[intvar] " << v << std::endl; for (const std::string &f: trackExecution.getMissingContracts()) report << "[missing contract] " << f << std::endl; } } void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings) { std::vector callbacks; addBughuntingChecks(&callbacks); std::ostringstream report; ExprEngine::executeAllFunctions(errorLogger, tokenizer, settings, callbacks, report); if (settings->bugHuntingReport.empty()) std::cout << report.str(); else if (errorLogger) errorLogger->bughuntingReport(report.str()); } static void dumpRecursive(ExprEngine::ValuePtr val) { if (!val) { std::cout << "NULL"; return; } switch (val->type) { case ExprEngine::ValueType::AddressOfValue: std::cout << "AddressOfValue(" << std::dynamic_pointer_cast(val)->varId << ")"; break; case ExprEngine::ValueType::ArrayValue: std::cout << "ArrayValue"; break; case ExprEngine::ValueType::BailoutValue: std::cout << "BailoutValue"; break; case ExprEngine::ValueType::BinOpResult: { auto b = std::dynamic_pointer_cast(val); std::cout << "("; dumpRecursive(b->op1); std::cout << " " << b->binop << " "; dumpRecursive(b->op2); std::cout << ")"; } break; case ExprEngine::ValueType::ConditionalValue: std::cout << "ConditionalValue"; break; case ExprEngine::ValueType::FloatRange: std::cout << "FloatRange"; break; case ExprEngine::ValueType::FunctionCallArgumentValues: { std::cout << "FunctionCallArgumentValues("; const char *sep = ""; for (auto arg: std::dynamic_pointer_cast(val)->argValues) { std::cout << sep; sep = ","; if (!arg) std::cout << "NULL"; else dumpRecursive(arg); } std::cout << ")"; } break; case ExprEngine::ValueType::IntRange: std::cout << "IntRange"; break; case ExprEngine::ValueType::IntegerTruncation: std::cout << "IntegerTruncation("; dumpRecursive(std::dynamic_pointer_cast(val)->inputValue); std::cout << ")"; break; case ExprEngine::ValueType::StringLiteralValue: std::cout << "StringLiteralValue"; break; case ExprEngine::ValueType::StructValue: std::cout << "StructValue"; break; case ExprEngine::ValueType::UninitValue: std::cout << "UninitValue"; break; } } void ExprEngine::dump(ExprEngine::ValuePtr val) { dumpRecursive(val); std::cout << "\n"; } cppcheck-2.7/lib/exprengine.h000066400000000000000000000266071417746362400162560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef exprengineH #define exprengineH //--------------------------------------------------------------------------- #include "config.h" #include "errortypes.h" #include #include #include #include #include #include #include #include class ErrorLogger; class Tokenizer; class Scope; class Settings; class Token; class Variable; #if defined(__GNUC__) && defined (__SIZEOF_INT128__) typedef __int128_t int128_t; #else typedef long long int128_t; #ifdef _MSC_VER #pragma message(__FILE__ "(" _CRT_STRINGIZE(__LINE__) ")" ": warning: TODO No 128-bit integer type is available => Limited analysis of large integers...") #else #warning TODO No 128-bit integer type is available => Limited analysis of large integers #endif #endif namespace ExprEngine { std::string str(int128_t); // TODO we need to handle floats, containers, pointers, aliases and structs and stuff enum class ValueType { UninitValue, IntRange, FloatRange, ConditionalValue, ArrayValue, StringLiteralValue, StructValue, AddressOfValue, BinOpResult, IntegerTruncation, FunctionCallArgumentValues, BailoutValue }; class Value; typedef std::shared_ptr ValuePtr; class DataBase { public: explicit DataBase(const std::string ¤tFunction, const Settings *settings) : currentFunction(currentFunction) , settings(settings) {} virtual std::string getNewSymbolName() = 0; const std::string currentFunction; const Settings * const settings; virtual bool isC() const = 0; virtual bool isCPP() const = 0; virtual void reportError(const Token *tok, Severity::SeverityType severity, const char id[], const std::string &text, CWE cwe, bool inconclusive, bool incomplete=false, const std::string &functionName = std::string()) = 0; ErrorPath errorPath; }; class Value { public: Value(const std::string &name, const ValueType type) : name(name), type(type) {} virtual ~Value() {} virtual std::string getRange() const { return name; } virtual std::string getSymbolicExpression() const { return name; } virtual bool isEqual(const DataBase *dataBase, int value) const { (void)dataBase; (void)value; return false; } virtual bool isGreaterThan(const DataBase *dataBase, int value) const { (void)dataBase; (void)value; return false; } virtual bool isLessThan(const DataBase *dataBase, int value) const { (void)dataBase; (void)value; return false; } virtual bool isUninit(const DataBase *dataBase) const { (void)dataBase; return false; } const std::string name; ValueType type; }; class UninitValue : public Value { public: UninitValue() : Value("?", ValueType::UninitValue) {} bool isEqual(const DataBase *dataBase, int value) const OVERRIDE { (void)dataBase; (void)value; return true; } bool isUninit(const DataBase *dataBase) const OVERRIDE; }; class IntRange : public Value { public: IntRange(const std::string &name, int128_t minValue, int128_t maxValue) : Value(name, ValueType::IntRange) , minValue(minValue) , maxValue(maxValue) , loopScope(nullptr) {} std::string getRange() const OVERRIDE { if (minValue == maxValue) return str(minValue); return str(minValue) + ":" + str(maxValue); } bool isEqual(const DataBase *dataBase, int value) const OVERRIDE; bool isGreaterThan(const DataBase *dataBase, int value) const OVERRIDE; bool isLessThan(const DataBase *dataBase, int value) const OVERRIDE; int128_t minValue; int128_t maxValue; const Scope *loopScope; }; class FloatRange : public Value { public: FloatRange(const std::string &name, long double minValue, long double maxValue) : Value(name, ValueType::FloatRange) , minValue(minValue) , maxValue(maxValue) {} std::string getRange() const OVERRIDE { return std::to_string(minValue) + ":" + std::to_string(maxValue); } bool isEqual(const DataBase *dataBase, int value) const OVERRIDE; bool isGreaterThan(const DataBase *dataBase, int value) const OVERRIDE; bool isLessThan(const DataBase *dataBase, int value) const OVERRIDE; long double minValue; long double maxValue; }; class ConditionalValue : public Value { public: typedef std::vector> Vector; ConditionalValue(const std::string &name, const Vector &values) : Value(name, ValueType::ConditionalValue), values(values) {} std::string getSymbolicExpression() const OVERRIDE; Vector values; }; // Array or pointer class ArrayValue : public Value { public: enum { MAXSIZE = 0x7fffffff }; ArrayValue(const std::string &name, ValuePtr size, ValuePtr value, bool pointer, bool nullPointer, bool uninitPointer); ArrayValue(DataBase *data, const Variable *var); ArrayValue(const std::string &name, const ArrayValue &arrayValue); std::string getRange() const OVERRIDE; std::string getSymbolicExpression() const OVERRIDE; void assign(ValuePtr index, ValuePtr value); void clear(); ConditionalValue::Vector read(ValuePtr index) const; bool pointer; bool nullPointer; bool uninitPointer; struct IndexAndValue { ValuePtr index; ValuePtr value; }; std::vector data; std::vector size; }; class StringLiteralValue : public Value { public: StringLiteralValue(const std::string &name, const std::string &s) : Value(name, ValueType::StringLiteralValue), string(s) {} std::string getRange() const OVERRIDE { return "\"" + string + "\""; } int size() const { return string.size(); } const std::string string; }; class StructValue : public Value { public: explicit StructValue(const std::string &name) : Value(name, ValueType::StructValue) {} std::string getSymbolicExpression() const OVERRIDE; std::string getRange() const OVERRIDE { return getSymbolicExpression(); } ValuePtr getValueOfMember(const std::string &n) const { auto it = member.find(n); return (it == member.end()) ? ValuePtr() : it->second; } std::string getUninitStructMember(const DataBase *dataBase) const { for (auto memberNameValue: member) { if (memberNameValue.second && memberNameValue.second->isUninit(dataBase)) return memberNameValue.first; } return std::string(); } std::map member; }; class AddressOfValue : public Value { public: AddressOfValue(const std::string &name, int varId) : Value(name, ValueType::AddressOfValue) , varId(varId) {} std::string getRange() const OVERRIDE { return "&@" + std::to_string(varId); } int varId; }; class BinOpResult : public Value { public: BinOpResult(const std::string &binop, ValuePtr op1, ValuePtr op2) : Value(getName(binop, op1, op2), ValueType::BinOpResult) , binop(binop) , op1(op1) , op2(op2) {} bool isEqual(const DataBase *dataBase, int value) const OVERRIDE; bool isGreaterThan(const DataBase *dataBase, int value) const OVERRIDE; virtual bool isLessThan(const DataBase *dataBase, int value) const OVERRIDE; bool isTrue(const DataBase *dataBase) const; std::string getExpr(DataBase *dataBase) const; std::string binop; ValuePtr op1; ValuePtr op2; private: static std::string getName(const std::string& binop, ValuePtr op1, ValuePtr op2) { std::string name1 = op1 ? op1->name : std::string("null"); std::string name2 = op2 ? op2->name : std::string("null"); return "(" + name1 + ")" + binop + "(" + name2 + ")"; } }; class IntegerTruncation : public Value { public: IntegerTruncation(const std::string &name, ValuePtr inputValue, int bits, char sign) : Value(name, ValueType::IntegerTruncation) , inputValue(inputValue) , bits(bits) , sign(sign) {} std::string getSymbolicExpression() const OVERRIDE; ExprEngine::ValuePtr inputValue; int bits; char sign; }; class FunctionCallArgumentValues : public Value { public: explicit FunctionCallArgumentValues(const std::vector &argValues) : Value("argValues", ValueType::FunctionCallArgumentValues) , argValues(argValues) {} const std::vector argValues; }; class BailoutValue : public Value { public: BailoutValue() : Value("bailout", ValueType::BailoutValue) {} bool isEqual(const DataBase * /*dataBase*/, int /*value*/) const OVERRIDE { return true; } bool isUninit(const DataBase *dataBase) const OVERRIDE { (void)dataBase; return true; } }; typedef std::function Callback; /** Execute all functions */ void CPPCHECKLIB executeAllFunctions(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &report); void executeFunction(const Scope *functionScope, ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &report); void runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings); void dump(ExprEngine::ValuePtr val); } #endif // exprengineH cppcheck-2.7/lib/forwardanalyzer.cpp000066400000000000000000001077651417746362400176640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "forwardanalyzer.h" #include "analyzer.h" #include "astutils.h" #include "config.h" #include "errortypes.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "valueptr.h" #include #include #include #include #include #include #include #include #include #include struct OnExit { std::function f; ~OnExit() { f(); } }; struct ForwardTraversal { enum class Progress { Continue, Break, Skip }; enum class Terminate { None, Bail, Escape, Modified, Inconclusive, Conditional }; ForwardTraversal(const ValuePtr& analyzer, const Settings* settings) : analyzer(analyzer), settings(settings), actions(Analyzer::Action::None), analyzeOnly(false), analyzeTerminate(false) {} ValuePtr analyzer; const Settings* settings; Analyzer::Action actions; bool analyzeOnly; bool analyzeTerminate; Analyzer::Terminate terminate = Analyzer::Terminate::None; bool forked = false; std::vector loopEnds = {}; Progress Break(Analyzer::Terminate t = Analyzer::Terminate::None) { if ((!analyzeOnly || analyzeTerminate) && t != Analyzer::Terminate::None) terminate = t; return Progress::Break; } struct Branch { Branch(Token* tok = nullptr) : endBlock(tok) {} Token* endBlock = nullptr; Analyzer::Action action = Analyzer::Action::None; bool check = false; bool escape = false; bool escapeUnknown = false; bool active = false; bool isEscape() const { return escape || escapeUnknown; } bool isConclusiveEscape() const { return escape && !escapeUnknown; } bool isModified() const { return action.isModified() && !isConclusiveEscape(); } bool isInconclusive() const { return action.isInconclusive() && !isConclusiveEscape(); } bool isDead() const { return action.isModified() || action.isInconclusive() || isEscape(); } }; bool stopUpdates() { analyzeOnly = true; return actions.isModified(); } std::pair evalCond(const Token* tok, const Token* ctx = nullptr) const { if (!tok) return std::make_pair(false, false); std::vector result = analyzer->evaluate(tok, ctx); // TODO: We should convert to bool bool checkThen = std::any_of(result.begin(), result.end(), [](int x) { return x != 0; }); bool checkElse = std::any_of(result.begin(), result.end(), [](int x) { return x == 0; }); return std::make_pair(checkThen, checkElse); } bool isConditionTrue(const Token* tok, const Token* ctx = nullptr) const { return evalCond(tok, ctx).first; } // cppcheck-suppress unusedFunction bool isConditionFalse(const Token* tok, const Token* ctx = nullptr) const { return evalCond(tok, ctx).second; } template )> Progress traverseTok(T* tok, F f, bool traverseUnknown, T** out = nullptr) { if (Token::Match(tok, "asm|goto|setjmp|longjmp")) return Break(Analyzer::Terminate::Bail); else if (Token::simpleMatch(tok, "continue")) { if (loopEnds.empty()) return Break(Analyzer::Terminate::Escape); // If we are in a loop then jump to the end if (out) *out = loopEnds.back(); } else if (Token::Match(tok, "return|throw") || isEscapeFunction(tok, &settings->library)) { traverseRecursive(tok->astOperand1(), f, traverseUnknown); traverseRecursive(tok->astOperand2(), f, traverseUnknown); return Break(Analyzer::Terminate::Escape); } else if (isUnevaluated(tok)) { if (out) *out = tok->link(); return Progress::Skip; } else if (tok->astOperand1() && tok->astOperand2() && Token::Match(tok, "?|&&|%oror%")) { if (traverseConditional(tok, f, traverseUnknown) == Progress::Break) return Break(); if (out) *out = nextAfterAstRightmostLeaf(tok); return Progress::Skip; // Skip lambdas } else if (T* lambdaEndToken = findLambdaEndToken(tok)) { if (checkScope(lambdaEndToken).isModified()) return Break(Analyzer::Terminate::Bail); if (out) *out = lambdaEndToken->next(); // Skip class scope } else if (tok->str() == "{" && tok->scope() && tok->scope()->isClassOrStruct()) { if (out) *out = tok->link(); } else { if (f(tok) == Progress::Break) return Break(); } return Progress::Continue; } template )> Progress traverseRecursive(T* tok, F f, bool traverseUnknown, unsigned int recursion=0) { if (!tok) return Progress::Continue; if (recursion > 10000) return Progress::Skip; T* firstOp = tok->astOperand1(); T* secondOp = tok->astOperand2(); // Evaluate: // 1. RHS of assignment before LHS // 2. Unary op before operand // 3. Function arguments before function call if (tok->isAssignmentOp() || !secondOp || isFunctionCall(tok)) std::swap(firstOp, secondOp); if (firstOp && traverseRecursive(firstOp, f, traverseUnknown, recursion+1) == Progress::Break) return Break(); Progress p = tok->isAssignmentOp() ? Progress::Continue : traverseTok(tok, f, traverseUnknown); if (p == Progress::Break) return Break(); if (p == Progress::Continue && secondOp && traverseRecursive(secondOp, f, traverseUnknown, recursion+1) == Progress::Break) return Break(); if (tok->isAssignmentOp() && traverseTok(tok, f, traverseUnknown) == Progress::Break) return Break(); return Progress::Continue; } template )> Progress traverseConditional(T* tok, F f, bool traverseUnknown) { if (Token::Match(tok, "?|&&|%oror%") && tok->astOperand1() && tok->astOperand2()) { T* condTok = tok->astOperand1(); T* childTok = tok->astOperand2(); bool checkThen, checkElse; std::tie(checkThen, checkElse) = evalCond(condTok); if (!checkThen && !checkElse) { if (!traverseUnknown && analyzer->stopOnCondition(condTok) && stopUpdates()) { return Progress::Continue; } checkThen = true; checkElse = true; } if (childTok->str() == ":") { if (checkThen && traverseRecursive(childTok->astOperand1(), f, traverseUnknown) == Progress::Break) return Break(); if (checkElse && traverseRecursive(childTok->astOperand2(), f, traverseUnknown) == Progress::Break) return Break(); } else { if (!checkThen && tok->str() == "&&") return Progress::Continue; if (!checkElse && tok->str() == "||") return Progress::Continue; if (traverseRecursive(childTok, f, traverseUnknown) == Progress::Break) return Break(); } } return Progress::Continue; } Progress update(Token* tok) { Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward); actions |= action; if (!action.isNone() && !analyzeOnly) analyzer->update(tok, action, Analyzer::Direction::Forward); if (action.isInconclusive() && !analyzer->lowerToInconclusive()) return Break(Analyzer::Terminate::Inconclusive); if (action.isInvalid()) return Break(Analyzer::Terminate::Modified); if (action.isWrite() && !action.isRead()) // Analysis of this write will continue separately return Break(Analyzer::Terminate::Modified); return Progress::Continue; } Progress updateTok(Token* tok, Token** out = nullptr) { auto f = [this](Token* tok2) { return update(tok2); }; return traverseTok(tok, f, false, out); } Progress updateRecursive(Token* tok) { forked = false; auto f = [this](Token* tok2) { return update(tok2); }; return traverseRecursive(tok, f, false); } template T* findRange(T* start, const Token* end, F pred) { for (T* tok = start; tok && tok != end; tok = tok->next()) { Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward); if (pred(action)) return tok; } return nullptr; } Analyzer::Action analyzeRecursive(const Token* start) { Analyzer::Action result = Analyzer::Action::None; auto f = [&](const Token* tok) { result = analyzer->analyze(tok, Analyzer::Direction::Forward); if (result.isModified() || result.isInconclusive()) return Break(); return Progress::Continue; }; traverseRecursive(start, f, true); return result; } Analyzer::Action analyzeRange(const Token* start, const Token* end) { Analyzer::Action result = Analyzer::Action::None; for (const Token* tok = start; tok && tok != end; tok = tok->next()) { Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward); if (action.isModified() || action.isInconclusive()) return action; result |= action; } return result; } ForwardTraversal fork(bool analyze = false) const { ForwardTraversal ft = *this; if (analyze) { ft.analyzeOnly = true; ft.analyzeTerminate = true; } ft.actions = Analyzer::Action::None; ft.forked = true; return ft; } std::vector tryForkScope(Token* endBlock, bool isModified = false) { if (analyzer->updateScope(endBlock, isModified)) { ForwardTraversal ft = fork(); return {ft}; } return std::vector {}; } std::vector tryForkUpdateScope(Token* endBlock, bool isModified = false) { std::vector result = tryForkScope(endBlock, isModified); for (ForwardTraversal& ft : result) ft.updateScope(endBlock); return result; } static bool hasGoto(const Token* endBlock) { return Token::findsimplematch(endBlock->link(), "goto", endBlock); } bool hasJump(const Token* endBlock) { return Token::findmatch(endBlock->link(), "goto|break", endBlock); } bool hasInnerReturnScope(const Token* start, const Token* end) const { for (const Token* tok=start; tok != end; tok = tok->previous()) { if (Token::simpleMatch(tok, "}")) { const Token* ftok = nullptr; bool r = isReturnScope(tok, &settings->library, &ftok); if (r) return true; } } return false; } bool isEscapeScope(const Token* endBlock, bool& unknown) { const Token* ftok = nullptr; bool r = isReturnScope(endBlock, &settings->library, &ftok); if (!r && ftok) unknown = true; return r; } enum class Status { None, Escaped, Modified, Inconclusive, }; Analyzer::Action analyzeScope(const Token* endBlock) { return analyzeRange(endBlock->link(), endBlock); } Analyzer::Action checkScope(Token* endBlock) { Analyzer::Action a = analyzeScope(endBlock); tryForkUpdateScope(endBlock, a.isModified()); return a; } Analyzer::Action checkScope(const Token* endBlock) { Analyzer::Action a = analyzeScope(endBlock); return a; } bool checkBranch(Branch& branch) { Analyzer::Action a = analyzeScope(branch.endBlock); branch.action = a; std::vector ft1 = tryForkUpdateScope(branch.endBlock, a.isModified()); bool bail = hasGoto(branch.endBlock); if (!a.isModified() && !bail) { if (ft1.empty()) { // Traverse into the branch to see if there is a conditional escape if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) { ForwardTraversal ft2 = fork(true); ft2.updateScope(branch.endBlock); if (ft2.terminate == Analyzer::Terminate::Escape) { branch.escape = true; branch.escapeUnknown = false; } } } else { if (ft1.front().terminate == Analyzer::Terminate::Escape) { branch.escape = true; branch.escapeUnknown = false; } } } return bail; } bool reentersLoop(Token* endBlock, const Token* condTok, const Token* stepTok) { if (!condTok) return true; if (Token::simpleMatch(condTok, ":")) return true; bool changed = false; if (stepTok) { std::pair exprToks = stepTok->findExpressionStartEndTokens(); if (exprToks.first != nullptr && exprToks.second != nullptr) changed |= isExpressionChanged(condTok, exprToks.first, exprToks.second->next(), settings, true); } changed |= isExpressionChanged(condTok, endBlock->link(), endBlock, settings, true); // Check for mutation in the condition changed |= nullptr != findAstNode(condTok, [&](const Token* tok) { return isVariableChanged(tok, 0, settings, true); }); if (!changed) return true; ForwardTraversal ft = fork(true); ft.analyzer->assume(condTok, false, Analyzer::Assume::Absolute); ft.updateScope(endBlock); return ft.isConditionTrue(condTok); } Progress updateInnerLoop(Token* endBlock, Token* stepTok, Token* condTok) { loopEnds.push_back(endBlock); OnExit oe{[&] { loopEnds.pop_back(); }}; if (endBlock && updateScope(endBlock) == Progress::Break) return Break(); if (stepTok && updateRecursive(stepTok) == Progress::Break) return Break(); if (condTok && updateRecursive(condTok) == Progress::Break) return Break(); return Progress::Continue; } Progress updateLoop(const Token* endToken, Token* endBlock, Token* condTok, Token* initTok = nullptr, Token* stepTok = nullptr, bool exit = false) { if (initTok && updateRecursive(initTok) == Progress::Break) return Break(); const bool isDoWhile = precedes(endBlock, condTok); bool checkThen = true; bool checkElse = false; if (condTok && !Token::simpleMatch(condTok, ":")) std::tie(checkThen, checkElse) = evalCond(condTok, isDoWhile ? endBlock->previous() : nullptr); // exiting a do while(false) if (checkElse && exit) { if (hasJump(endBlock)) { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); if (analyzer->isConditional() && stopUpdates()) return Break(Analyzer::Terminate::Conditional); } return Progress::Continue; } Analyzer::Action bodyAnalysis = analyzeScope(endBlock); Analyzer::Action allAnalysis = bodyAnalysis; Analyzer::Action condAnalysis; if (condTok) { condAnalysis = analyzeRecursive(condTok); allAnalysis |= condAnalysis; } if (stepTok) allAnalysis |= analyzeRecursive(stepTok); actions |= allAnalysis; // do while(false) is not really a loop if (checkElse && isDoWhile && (condTok->hasKnownIntValue() || (!bodyAnalysis.isModified() && !condAnalysis.isModified() && condAnalysis.isRead()))) { if (updateRange(endBlock->link(), endBlock) == Progress::Break) return Break(); return updateRecursive(condTok); } if (allAnalysis.isInconclusive()) { if (!analyzer->lowerToInconclusive()) return Break(Analyzer::Terminate::Bail); } else if (allAnalysis.isModified() || (exit && allAnalysis.isIdempotent())) { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); } if (condTok && !Token::simpleMatch(condTok, ":")) { if (!isDoWhile || (!bodyAnalysis.isModified() && !bodyAnalysis.isIdempotent())) if (updateRecursive(condTok) == Progress::Break) return Break(); } if (!checkThen && !checkElse && !isDoWhile && analyzer->stopOnCondition(condTok) && stopUpdates()) return Break(Analyzer::Terminate::Conditional); // condition is false, we don't enter the loop if (checkElse) return Progress::Continue; if (checkThen || isDoWhile) { // Since we are re-entering the loop then assume the condition is true to update the state if (exit) analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) return Break(); // If loop re-enters then it could be modified again if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok)) return Break(Analyzer::Terminate::Bail); if (allAnalysis.isIncremental()) return Break(Analyzer::Terminate::Bail); } else if (allAnalysis.isModified()) { std::vector ftv = tryForkScope(endBlock, allAnalysis.isModified()); bool forkContinue = true; for (ForwardTraversal& ft : ftv) { if (condTok) ft.analyzer->assume(condTok, false, Analyzer::Assume::Quiet); if (ft.updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) forkContinue = false; } if (allAnalysis.isModified() || !forkContinue) { // TODO: Don't bail on missing condition if (!condTok) return Break(Analyzer::Terminate::Bail); if (analyzer->isConditional() && stopUpdates()) return Break(Analyzer::Terminate::Conditional); analyzer->assume(condTok, false); } if (forkContinue) { for (ForwardTraversal& ft : ftv) { if (!ft.actions.isIncremental()) ft.updateRange(endBlock, endToken); } } if (allAnalysis.isIncremental()) return Break(Analyzer::Terminate::Bail); } else { if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) return Progress::Break; if (allAnalysis.isIncremental()) return Break(Analyzer::Terminate::Bail); } return Progress::Continue; } Progress updateLoopExit(const Token* endToken, Token* endBlock, Token* condTok, Token* initTok = nullptr, Token* stepTok = nullptr) { return updateLoop(endToken, endBlock, condTok, initTok, stepTok, true); } Progress updateScope(Token* endBlock) { if (forked) analyzer->forkScope(endBlock); return updateRange(endBlock->link(), endBlock); } Progress updateRange(Token* start, const Token* end, int depth = 20) { forked = false; if (depth < 0) return Break(Analyzer::Terminate::Bail); std::size_t i = 0; for (Token* tok = start; precedes(tok, end); tok = tok->next()) { Token* next = nullptr; if (tok->index() <= i) throw InternalError(tok, "Cyclic forward analysis."); i = tok->index(); if (tok->link()) { // Skip casts.. if (tok->str() == "(" && !tok->astOperand2() && tok->isCast()) { tok = tok->link(); continue; } // Skip template arguments.. if (tok->str() == "<") { tok = tok->link(); continue; } } // Evaluate RHS of assignment before LHS if (Token* assignTok = assignExpr(tok)) { if (updateRecursive(assignTok) == Progress::Break) return Break(); tok = nextAfterAstRightmostLeaf(assignTok); if (!tok) return Break(); } else if (tok->str() == "break") { const Token *scopeEndToken = findNextTokenFromBreak(tok); if (!scopeEndToken) return Break(); tok = skipTo(tok, scopeEndToken, end); if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); // TODO: Don't break, instead move to the outer scope if (!tok) return Break(); } else if (Token::Match(tok, "%name% :") || tok->str() == "case") { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); } else if (tok->link() && tok->str() == "}") { const Scope* scope = tok->scope(); if (!scope) return Break(); if (Token::Match(tok->link()->previous(), ")|else {")) { const Token* tok2 = tok->link()->previous(); const bool inElse = Token::simpleMatch(tok2, "else {"); const bool inLoop = inElse ? false : Token::Match(tok2->link()->previous(), "while|for ("); Token* condTok = getCondTokFromEnd(tok); if (!condTok) return Break(); if (!condTok->hasKnownIntValue() || inLoop) { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); } else if (condTok->values().front().intvalue == inElse) { return Break(); } // Handle for loop Token* stepTok = getStepTokFromEnd(tok); bool checkThen, checkElse; std::tie(checkThen, checkElse) = evalCond(condTok); if (stepTok && !checkElse) { if (updateRecursive(stepTok) == Progress::Break) return Break(); if (updateRecursive(condTok) == Progress::Break) return Break(); } analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet); if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); } else if (scope->type == Scope::eTry) { if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); } else if (scope->type == Scope::eLambda) { return Break(); } else if (scope->type == Scope::eDo && Token::simpleMatch(tok, "} while (")) { if (updateLoopExit(end, tok, tok->tokAt(2)->astOperand2()) == Progress::Break) return Break(); tok = tok->linkAt(2); } else if (Token::simpleMatch(tok->next(), "else {")) { tok = tok->linkAt(2); } } else if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) { Token* endCond = tok->next()->link(); Token* endBlock = endCond->next()->link(); Token* condTok = getCondTok(tok); Token* initTok = getInitTok(tok); if (initTok && updateRecursive(initTok) == Progress::Break) return Break(); if (Token::Match(tok, "for|while (")) { // For-range loop if (Token::simpleMatch(condTok, ":")) { Token* conTok = condTok->astOperand2(); if (conTok && updateRecursive(conTok) == Progress::Break) return Break(); bool isEmpty = false; std::vector result = analyzer->evaluate(Analyzer::Evaluate::ContainerEmpty, conTok); if (result.empty()) analyzer->assume(conTok, false, Analyzer::Assume::ContainerEmpty); else isEmpty = result.front() != 0; if (!isEmpty && updateLoop(end, endBlock, condTok) == Progress::Break) return Break(); } else { Token* stepTok = getStepTok(tok); if (updateLoop(end, endBlock, condTok, initTok, stepTok) == Progress::Break) return Break(); } tok = endBlock; } else { // Traverse condition if (updateRecursive(condTok) == Progress::Break) return Break(); Branch thenBranch{endBlock}; Branch elseBranch{endBlock->tokAt(2) ? endBlock->linkAt(2) : nullptr}; // Check if condition is true or false std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok); if (!thenBranch.check && !elseBranch.check && analyzer->stopOnCondition(condTok) && stopUpdates()) return Break(Analyzer::Terminate::Conditional); bool hasElse = Token::simpleMatch(endBlock, "} else {"); bool bail = false; // Traverse then block thenBranch.escape = isEscapeScope(endBlock, thenBranch.escapeUnknown); if (thenBranch.check) { thenBranch.active = true; if (updateRange(endCond->next(), endBlock, depth - 1) == Progress::Break) return Break(); } else if (!elseBranch.check) { thenBranch.active = true; if (checkBranch(thenBranch)) bail = true; } // Traverse else block if (hasElse) { elseBranch.escape = isEscapeScope(endBlock->linkAt(2), elseBranch.escapeUnknown); if (elseBranch.check) { elseBranch.active = true; Progress result = updateRange(endBlock->tokAt(2), endBlock->linkAt(2), depth - 1); if (result == Progress::Break) return Break(); } else if (!thenBranch.check) { elseBranch.active = true; if (checkBranch(elseBranch)) bail = true; } tok = endBlock->linkAt(2); } else { tok = endBlock; } if (thenBranch.active) actions |= thenBranch.action; if (elseBranch.active) actions |= elseBranch.action; if (bail) return Break(Analyzer::Terminate::Bail); if (thenBranch.isDead() && elseBranch.isDead()) { if (thenBranch.isModified() && elseBranch.isModified()) return Break(Analyzer::Terminate::Modified); if (thenBranch.isConclusiveEscape() && elseBranch.isConclusiveEscape()) return Break(Analyzer::Terminate::Escape); return Break(Analyzer::Terminate::Bail); } // Conditional return if (thenBranch.active && thenBranch.isEscape() && !hasElse) { if (!thenBranch.isConclusiveEscape()) { if (!analyzer->lowerToInconclusive()) return Break(Analyzer::Terminate::Bail); } else if (thenBranch.check) { return Break(); } else { if (analyzer->isConditional() && stopUpdates()) return Break(Analyzer::Terminate::Conditional); analyzer->assume(condTok, false); } } if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) { if (!analyzer->lowerToInconclusive()) return Break(Analyzer::Terminate::Bail); } else if (thenBranch.isModified() || elseBranch.isModified()) { if (!hasElse && analyzer->isConditional() && stopUpdates()) return Break(Analyzer::Terminate::Conditional); if (!analyzer->lowerToPossible()) return Break(Analyzer::Terminate::Bail); analyzer->assume(condTok, elseBranch.isModified()); } } } else if (Token::simpleMatch(tok, "try {")) { Token* endBlock = tok->next()->link(); ForwardTraversal tryTraversal = fork(); tryTraversal.updateRange(tok->next(), endBlock, depth - 1); bool bail = tryTraversal.actions.isModified(); if (bail) analyzer->lowerToPossible(); while (Token::simpleMatch(endBlock, "} catch (")) { Token* endCatch = endBlock->linkAt(2); if (!Token::simpleMatch(endCatch, ") {")) return Break(); endBlock = endCatch->linkAt(1); ForwardTraversal ft = fork(); ft.updateRange(endBlock->link(), endBlock, depth - 1); bail |= ft.terminate != Analyzer::Terminate::None || ft.actions.isModified(); } if (bail) return Break(); tok = endBlock; } else if (Token::simpleMatch(tok, "do {")) { Token* endBlock = tok->next()->link(); Token* condTok = Token::simpleMatch(endBlock, "} while (") ? endBlock->tokAt(2)->astOperand2() : nullptr; if (updateLoop(end, endBlock, condTok) == Progress::Break) return Break(); if (condTok) tok = endBlock->linkAt(2)->next(); else tok = endBlock; } else if (Token::Match(tok, "assert|ASSERT (")) { const Token* condTok = tok->next()->astOperand2(); bool checkThen, checkElse; std::tie(checkThen, checkElse) = evalCond(condTok); if (checkElse) return Break(); if (!checkThen) analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); } else if (Token::simpleMatch(tok, "switch (")) { if (updateRecursive(tok->next()->astOperand2()) == Progress::Break) return Break(); return Break(); } else { if (updateTok(tok, &next) == Progress::Break) return Break(); if (next) { if (precedes(next, end)) tok = next->previous(); else return Break(); } } // Prevent infinite recursion if (tok->next() == start) break; } return Progress::Continue; } static bool isUnevaluated(const Token* tok) { if (Token::Match(tok->previous(), "sizeof|decltype (")) return true; return false; } static bool isFunctionCall(const Token* tok) { if (!Token::simpleMatch(tok, "(")) return false; if (tok->isCast()) return false; if (!tok->isBinaryOp()) return false; if (Token::simpleMatch(tok->link(), ") {")) return false; if (isUnevaluated(tok)) return false; return Token::Match(tok->previous(), "%name%|)|]|>"); } static Token* assignExpr(Token* tok) { while (tok->astParent() && astIsLHS(tok)) { if (tok->astParent()->isAssignmentOp()) return tok->astParent(); tok = tok->astParent(); } return nullptr; } static Token* skipTo(Token* tok, const Token* dest, const Token* end = nullptr) { if (end && dest->index() > end->index()) return nullptr; int i = dest->index() - tok->index(); if (i > 0) return tok->tokAt(dest->index() - tok->index()); return nullptr; } static bool isConditional(const Token* tok) { const Token* parent = tok->astParent(); while (parent && !Token::Match(parent, "%oror%|&&|:")) { tok = parent; parent = parent->astParent(); } return parent && (parent->str() == ":" || parent->astOperand2() == tok); } static Token* getStepTokFromEnd(Token* tok) { if (!Token::simpleMatch(tok, "}")) return nullptr; Token* end = tok->link()->previous(); if (!Token::simpleMatch(end, ")")) return nullptr; return getStepTok(end->link()); } }; Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr& a, const Settings* settings) { ForwardTraversal ft{a, settings}; ft.updateRange(start, end); return {ft.actions, ft.terminate}; } Analyzer::Result valueFlowGenericForward(Token* start, const ValuePtr& a, const Settings* settings) { ForwardTraversal ft{a, settings}; ft.updateRecursive(start); return {ft.actions, ft.terminate}; } cppcheck-2.7/lib/forwardanalyzer.h000066400000000000000000000023551417746362400173160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef forwardanalyzerH #define forwardanalyzerH #include "analyzer.h" class Settings; class Token; template class ValuePtr; Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr& a, const Settings* settings); Analyzer::Result valueFlowGenericForward(Token* start, const ValuePtr& a, const Settings* settings); #endif cppcheck-2.7/lib/importproject.cpp000066400000000000000000001622501417746362400173410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "importproject.h" #include "path.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include #include #include #include // IWYU pragma: keep #include #include #include #include #define PICOJSON_USE_INT64 #include ImportProject::ImportProject() { projectType = Type::UNKNOWN; } void ImportProject::ignorePaths(const std::vector &ipaths) { for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { bool ignore = false; for (std::string i : ipaths) { if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { ignore = true; break; } if (isValidGlobPattern(i) && matchglob(i, it->filename)) { ignore = true; break; } if (!Path::isAbsolute(i)) { i = mPath + i; if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { ignore = true; break; } } } if (ignore) fileSettings.erase(it++); else ++it; } } void ImportProject::ignoreOtherConfigs(const std::string &cfg) { for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { if (it->cfg != cfg) fileSettings.erase(it++); else ++it; } } void ImportProject::FileSettings::setDefines(std::string defs) { while (defs.find(";%(") != std::string::npos) { const std::string::size_type pos1 = defs.find(";%("); const std::string::size_type pos2 = defs.find(';', pos1+1); defs.erase(pos1, pos2 == std::string::npos ? pos2 : (pos2-pos1)); } while (defs.find(";;") != std::string::npos) defs.erase(defs.find(";;"),1); while (!defs.empty() && defs[0] == ';') defs.erase(0, 1); while (!defs.empty() && endsWith(defs,';')) defs.erase(defs.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it bool eq = false; for (std::size_t pos = 0; pos < defs.size(); ++pos) { if (defs[pos] == '(' || defs[pos] == '=') eq = true; else if (defs[pos] == ';') { if (!eq) { defs.insert(pos,"=1"); pos += 3; } if (pos < defs.size()) eq = false; } } if (!eq && !defs.empty()) defs += "=1"; defines.swap(defs); } static bool simplifyPathWithVariables(std::string &s, std::map &variables) { std::set expanded; std::string::size_type start = 0; while ((start = s.find("$(")) != std::string::npos) { const std::string::size_type end = s.find(')',start); if (end == std::string::npos) break; const std::string var = s.substr(start+2,end-start-2); if (expanded.find(var) != expanded.end()) break; expanded.insert(var); std::map::const_iterator it1 = variables.find(var); // variable was not found within defined variables if (it1 == variables.end()) { const char *envValue = std::getenv(var.c_str()); if (!envValue) { //! \todo generate a debug/info message about undefined variable break; } variables[var] = std::string(envValue); it1 = variables.find(var); } s = s.substr(0, start) + it1->second + s.substr(end + 1); } if (s.find("$(") != std::string::npos) return false; s = Path::simplifyPath(Path::fromNativeSeparators(s)); return true; } void ImportProject::FileSettings::setIncludePaths(const std::string &basepath, const std::list &in, std::map &variables) { std::list listInc; // only parse each includePath once - so remove duplicates std::list uniqueIncludePaths = in; uniqueIncludePaths.sort(); uniqueIncludePaths.unique(); for (const std::string &it : uniqueIncludePaths) { if (it.empty()) continue; if (it.compare(0,2,"%(")==0) continue; std::string s(Path::fromNativeSeparators(it)); if (s[0] == '/' || (s.size() > 1U && s.compare(1,2,":/") == 0)) { if (!endsWith(s,'/')) s += '/'; listInc.push_back(s); continue; } if (endsWith(s,'/')) // this is a temporary hack, simplifyPath can crash if path ends with '/' s.erase(s.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it if (s.find("$(") == std::string::npos) { s = Path::simplifyPath(basepath + s); } else { if (!simplifyPathWithVariables(s, variables)) continue; } if (s.empty()) continue; listInc.push_back(s + '/'); } includePaths.swap(listInc); } ImportProject::Type ImportProject::import(const std::string &filename, Settings *settings) { std::ifstream fin(filename); if (!fin.is_open()) return ImportProject::Type::MISSING; mPath = Path::getPathFromFilename(Path::fromNativeSeparators(filename)); if (!mPath.empty() && !endsWith(mPath,'/')) mPath += '/'; const std::vector fileFilters = settings ? settings->fileFilters : std::vector(); if (endsWith(filename, ".json")) { if (importCompileCommands(fin)) { setRelativePaths(filename); return ImportProject::Type::COMPILE_DB; } } else if (endsWith(filename, ".sln")) { if (importSln(fin, mPath, fileFilters)) { setRelativePaths(filename); return ImportProject::Type::VS_SLN; } } else if (endsWith(filename, ".vcxproj")) { std::map variables; if (importVcxproj(filename, variables, emptyString, fileFilters)) { setRelativePaths(filename); return ImportProject::Type::VS_VCXPROJ; } } else if (endsWith(filename, ".bpr")) { if (importBcb6Prj(filename)) { setRelativePaths(filename); return ImportProject::Type::BORLAND; } } else if (settings && endsWith(filename, ".cppcheck")) { if (importCppcheckGuiProject(fin, settings)) { setRelativePaths(filename); return ImportProject::Type::CPPCHECK_GUI; } } else { return ImportProject::Type::UNKNOWN; } return ImportProject::Type::FAILURE; } static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[]) { std::string ret; bool escapedString = false; bool str = false; bool escape = false; for (; *pos < command.size() && (str || !std::strchr(until, command[*pos])); (*pos)++) { if (escape) escape = false; else if (command[*pos] == '\\') { if (str) escape = true; else if (command[*pos + 1] == '"') { if (escapedString) return ret + "\\\""; escapedString = true; ret += "\\\""; (*pos)++; continue; } } else if (command[*pos] == '\"') str = !str; ret += command[*pos]; } return ret; } static std::string unescape(const std::string &in) { std::string out; bool escape = false; for (char c: in) { if (escape) { escape = false; if (!std::strchr("\\\"\'",c)) out += "\\"; out += c; } else if (c == '\\') escape = true; else out += c; } return out; } void ImportProject::FileSettings::parseCommand(std::string command) { std::string defs; // Parse command.. std::string::size_type pos = 0; while (std::string::npos != (pos = command.find(' ',pos))) { while (pos < command.size() && command[pos] == ' ') pos++; if (pos >= command.size()) break; if (command[pos] != '/' && command[pos] != '-') continue; pos++; if (pos >= command.size()) break; const char F = command[pos++]; if (std::strchr("DUI", F)) { while (pos < command.size() && command[pos] == ' ') ++pos; } const std::string fval = readUntil(command, &pos, " ="); if (F=='D') { std::string defval = readUntil(command, &pos, " "); defs += fval; if (defval.size() >= 3 && defval.compare(0,2,"=\"")==0 && defval.back()=='\"') defval = "=" + unescape(defval.substr(2, defval.size() - 3)); else if (defval.size() >= 5 && defval.compare(0, 3, "=\\\"") == 0 && endsWith(defval, "\\\"")) defval = "=\"" + unescape(defval.substr(3, defval.size() - 5)) + "\""; if (!defval.empty()) defs += defval; defs += ';'; } else if (F=='U') undefs.insert(fval); else if (F=='I') { std::string i = fval; if (i.size() > 1 && i[0] == '\"' && i.back() == '\"') i = unescape(i.substr(1, i.size() - 2)); if (std::find(includePaths.begin(), includePaths.end(), i) == includePaths.end()) includePaths.push_back(i); } else if (F=='s' && fval.compare(0,2,"td") == 0) { ++pos; const std::string stdval = readUntil(command, &pos, " "); standard = stdval; if (standard.compare(0, 3, "c++") || standard.compare(0, 5, "gnu++")) { std::string stddef; if (standard == "c++98" || standard == "gnu++98" || standard == "c++03" || standard == "gnu++03") { stddef = "199711L"; } else if (standard == "c++11" || standard == "gnu++11" || standard == "c++0x" || standard == "gnu++0x") { stddef = "201103L"; } else if (standard == "c++14" || standard == "gnu++14" || standard == "c++1y" || standard == "gnu++1y") { stddef = "201402L"; } else if (standard == "c++17" || standard == "gnu++17" || standard == "c++1z" || standard == "gnu++1z") { stddef = "201703L"; } if (stddef.empty()) { // TODO: log error continue; } defs += "__cplusplus="; defs += stddef; defs += ";"; } else if (standard.compare(0, 1, "c") || standard.compare(0, 3, "gnu")) { if (standard == "c90" || standard == "iso9899:1990" || standard == "gnu90" || standard == "iso9899:199409") { // __STDC_VERSION__ is not set for C90 although the macro was added in the 1994 amendments continue; } std::string stddef; if (standard == "c99" || standard == "iso9899:1999" || standard == "gnu99") { stddef = "199901L"; } else if (standard == "c11" || standard == "iso9899:2011" || standard == "gnu11" || standard == "c1x" || standard == "gnu1x") { stddef = "201112L"; } else if (standard == "c17") { stddef = "201710L"; } if (stddef.empty()) { // TODO: log error continue; } defs += "__STDC_VERSION__="; defs += stddef; defs += ";"; } } else if (F == 'i' && fval == "system") { ++pos; const std::string isystem = readUntil(command, &pos, " "); systemIncludePaths.push_back(isystem); } else if (F=='m') { if (fval == "unicode") { defs += "UNICODE"; defs += ";"; } } else if (F=='f') { if (fval == "pic") { defs += "__pic__"; defs += ";"; } else if (fval == "PIC") { defs += "__PIC__"; defs += ";"; } else if (fval == "pie") { defs += "__pie__"; defs += ";"; } else if (fval == "PIE") { defs += "__PIE__"; defs += ";"; } } } setDefines(defs); } bool ImportProject::importCompileCommands(std::istream &istr) { picojson::value compileCommands; istr >> compileCommands; if (!compileCommands.is()) { printError("compilation database is not a JSON array"); return false; } for (const picojson::value &fileInfo : compileCommands.get()) { picojson::object obj = fileInfo.get(); std::string dirpath = Path::fromNativeSeparators(obj["directory"].get()); /* CMAKE produces the directory without trailing / so add it if not * there - it is needed by setIncludePaths() */ if (!endsWith(dirpath, '/')) dirpath += '/'; const std::string directory = dirpath; std::ostringstream comm; if (obj.find("arguments") != obj.end()) { if (obj["arguments"].is()) { for (const picojson::value& arg : obj["arguments"].get()) { if (arg.is()) { comm << arg.get() << " "; } } } else { printError("'arguments' field in compilation database entry is not a JSON array"); return false; } } else if (obj.find("command") != obj.end()) { if (obj["command"].is()) { comm << obj["command"].get(); } } else { printError("no 'arguments' or 'command' field found in compilation database entry"); return false; } const std::string command = comm.str(); const std::string file = Path::fromNativeSeparators(obj["file"].get()); // Accept file? if (!Path::acceptFile(file)) continue; struct FileSettings fs; if (Path::isAbsolute(file)) fs.filename = Path::simplifyPath(file); #ifdef _WIN32 else if (file[0] == '/' && directory.size() > 2 && std::isalpha(directory[0]) && directory[1] == ':') // directory: C:\foo\bar // file: /xy/z.c // => c:/xy/z.c fs.filename = Path::simplifyPath(directory.substr(0,2) + file); #endif else fs.filename = Path::simplifyPath(directory + file); if (!sourceFileExists(fs.filename)) { printError("'" + fs.filename + "' from compilation database does not exist"); return false; } fs.parseCommand(command); // read settings; -D, -I, -U, -std, -m*, -f* std::map variables; fs.setIncludePaths(directory, fs.includePaths, variables); fileSettings.push_back(fs); } return true; } bool ImportProject::importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters) { std::string line; // skip magic word if (!std::getline(istr,line)) { printError("Visual Studio solution file is empty"); return false; } if (!std::getline(istr, line) || line.find("Microsoft Visual Studio Solution File") != 0) { printError("Visual Studio solution file header not found"); return false; } std::map variables; variables["SolutionDir"] = path; bool found = false; while (std::getline(istr,line)) { if (line.compare(0,8,"Project(")!=0) continue; const std::string::size_type pos = line.find(".vcxproj"); if (pos == std::string::npos) continue; const std::string::size_type pos1 = line.rfind('\"',pos); if (pos1 == std::string::npos) continue; std::string vcxproj(line.substr(pos1+1, pos-pos1+7)); if (!Path::isAbsolute(vcxproj)) vcxproj = path + vcxproj; if (!importVcxproj(Path::fromNativeSeparators(vcxproj), variables, emptyString, fileFilters)) { printError("failed to load '" + vcxproj + "' from Visual Studio solution"); return false; } found = true; } if (!found) { printError("no projects found in Visual Studio solution file"); return false; } return true; } namespace { struct ProjectConfiguration { explicit ProjectConfiguration(const tinyxml2::XMLElement *cfg) : platform(Unknown) { const char *a = cfg->Attribute("Include"); if (a) name = a; for (const tinyxml2::XMLElement *e = cfg->FirstChildElement(); e; e = e->NextSiblingElement()) { if (!e->GetText()) continue; if (std::strcmp(e->Name(),"Configuration")==0) configuration = e->GetText(); else if (std::strcmp(e->Name(),"Platform")==0) { platformStr = e->GetText(); if (platformStr == "Win32") platform = Win32; else if (platformStr == "x64") platform = x64; else platform = Unknown; } } } std::string name; std::string configuration; enum { Win32, x64, Unknown } platform; std::string platformStr; }; struct ItemDefinitionGroup { explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, const std::string &includePaths) : additionalIncludePaths(includePaths) { const char *condAttr = idg->Attribute("Condition"); if (condAttr) condition = condAttr; for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) { if (std::strcmp(e1->Name(), "ClCompile") != 0) continue; enhancedInstructionSet = "StreamingSIMDExtensions2"; for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { if (e->GetText()) { if (std::strcmp(e->Name(), "PreprocessorDefinitions") == 0) preprocessorDefinitions = e->GetText(); else if (std::strcmp(e->Name(), "AdditionalIncludeDirectories") == 0) { if (!additionalIncludePaths.empty()) additionalIncludePaths += ';'; additionalIncludePaths += e->GetText(); } else if (std::strcmp(e->Name(), "LanguageStandard") == 0) { if (std::strcmp(e->GetText(), "stdcpp14") == 0) cppstd = Standards::CPP14; else if (std::strcmp(e->GetText(), "stdcpp17") == 0) cppstd = Standards::CPP17; else if (std::strcmp(e->GetText(), "stdcpp20") == 0) cppstd = Standards::CPP20; else if (std::strcmp(e->GetText(), "stdcpplatest") == 0) cppstd = Standards::CPPLatest; } else if (std::strcmp(e->Name(), "EnableEnhancedInstructionSet") == 0) { enhancedInstructionSet = e->GetText(); } } } } } static void replaceAll(std::string &c, const std::string &from, const std::string &to) { std::string::size_type pos; while ((pos = c.find(from)) != std::string::npos) { c.erase(pos,from.size()); c.insert(pos,to); } } bool conditionIsTrue(const ProjectConfiguration &p) const { if (condition.empty()) return true; std::string c = '(' + condition + ");"; replaceAll(c, "$(Configuration)", p.configuration); replaceAll(c, "$(Platform)", p.platformStr); // TODO : Better evaluation Settings s; std::istringstream istr(c); Tokenizer tokenizer(&s, nullptr); tokenizer.tokenize(istr,"vcxproj"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() == "(" && tok->astOperand1() && tok->astOperand2()) { if (tok->astOperand1()->expressionString() == "Configuration.Contains") return ('\'' + p.configuration + '\'') == tok->astOperand2()->str(); } if (tok->str() == "==" && tok->astOperand1() && tok->astOperand2() && tok->astOperand1()->str() == tok->astOperand2()->str()) return true; } return false; } std::string condition; std::string enhancedInstructionSet; std::string preprocessorDefinitions; std::string additionalIncludePaths; Standards::cppstd_t cppstd = Standards::CPPLatest; }; } static std::list toStringList(const std::string &s) { std::list ret; std::string::size_type pos1 = 0; std::string::size_type pos2; while ((pos2 = s.find(';',pos1)) != std::string::npos) { ret.push_back(s.substr(pos1, pos2-pos1)); pos1 = pos2 + 1; if (pos1 >= s.size()) break; } if (pos1 < s.size()) ret.push_back(s.substr(pos1)); return ret; } static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map *variables, std::string *includePath, bool *useOfMfc) { if (useOfMfc) { for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "UseOfMfc") == 0) { *useOfMfc = true; break; } } } const char* labelAttribute = node->Attribute("Label"); if (labelAttribute && std::strcmp(labelAttribute, "UserMacros") == 0) { for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { const std::string name(propertyGroup->Name()); const char *text = propertyGroup->GetText(); (*variables)[name] = std::string(text ? text : ""); } } else if (!labelAttribute) { for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { if (std::strcmp(propertyGroup->Name(), "IncludePath") != 0) continue; const char *text = propertyGroup->GetText(); if (!text) continue; std::string path(text); const std::string::size_type pos = path.find("$(IncludePath)"); if (pos != std::string::npos) path = path.substr(0,pos) + *includePath + path.substr(pos+14U); *includePath = path; } } } static void loadVisualStudioProperties(const std::string &props, std::map *variables, std::string *includePath, const std::string &additionalIncludeDirectories, std::list &itemDefinitionGroupList) { std::string filename(props); // variables can't be resolved if (!simplifyPathWithVariables(filename, *variables)) return; // prepend project dir (if it exists) to transform relative paths into absolute ones if (!Path::isAbsolute(filename) && variables->count("ProjectDir") > 0) filename = Path::getAbsoluteFilePath(variables->at("ProjectDir") + filename); tinyxml2::XMLDocument doc; if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) return; const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) return; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "ImportGroup") == 0) { const char *labelAttribute = node->Attribute("Label"); if (labelAttribute == nullptr || std::strcmp(labelAttribute, "PropertySheets") != 0) continue; for (const tinyxml2::XMLElement *importGroup = node->FirstChildElement(); importGroup; importGroup = importGroup->NextSiblingElement()) { if (std::strcmp(importGroup->Name(), "Import") == 0) { const char *projectAttribute = importGroup->Attribute("Project"); if (projectAttribute == nullptr) continue; std::string loadprj(projectAttribute); if (loadprj.find('$') == std::string::npos) { loadprj = Path::getPathFromFilename(filename) + loadprj; } loadVisualStudioProperties(loadprj, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList); } } } else if (std::strcmp(node->Name(),"PropertyGroup")==0) { importPropertyGroup(node, variables, includePath, nullptr); } else if (std::strcmp(node->Name(),"ItemDefinitionGroup")==0) { itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); } } } bool ImportProject::importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters) { variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename)); std::list projectConfigurationList; std::list compileList; std::list itemDefinitionGroupList; std::string includePath; bool useOfMfc = false; tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(filename.c_str()); if (error != tinyxml2::XML_SUCCESS) { printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); return false; } const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) { printError("Visual Studio project file has no XML root node"); return false; } for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "ItemGroup") == 0) { const char *labelAttribute = node->Attribute("Label"); if (labelAttribute && std::strcmp(labelAttribute, "ProjectConfigurations") == 0) { for (const tinyxml2::XMLElement *cfg = node->FirstChildElement(); cfg; cfg = cfg->NextSiblingElement()) { if (std::strcmp(cfg->Name(), "ProjectConfiguration") == 0) { const ProjectConfiguration p(cfg); if (p.platform != ProjectConfiguration::Unknown) { projectConfigurationList.emplace_back(cfg); mAllVSConfigs.insert(p.configuration); } } } } else { for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "ClCompile") == 0) { const char *include = e->Attribute("Include"); if (include && Path::acceptFile(include)) compileList.emplace_back(include); } } } } else if (std::strcmp(node->Name(), "ItemDefinitionGroup") == 0) { itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); } else if (std::strcmp(node->Name(), "PropertyGroup") == 0) { importPropertyGroup(node, &variables, &includePath, &useOfMfc); } else if (std::strcmp(node->Name(), "ImportGroup") == 0) { const char *labelAttribute = node->Attribute("Label"); if (labelAttribute && std::strcmp(labelAttribute, "PropertySheets") == 0) { for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "Import") == 0) { const char *projectAttribute = e->Attribute("Project"); if (projectAttribute) loadVisualStudioProperties(projectAttribute, &variables, &includePath, additionalIncludeDirectories, itemDefinitionGroupList); } } } } } for (const std::string &c : compileList) { const std::string cfilename = Path::simplifyPath(Path::isAbsolute(c) ? c : Path::getPathFromFilename(filename) + c); if (!fileFilters.empty() && !matchglobs(fileFilters, cfilename)) continue; for (const ProjectConfiguration &p : projectConfigurationList) { if (!guiProject.checkVsConfigs.empty()) { bool doChecking = false; for (std::string config : guiProject.checkVsConfigs) if (config == p.configuration) { doChecking = true; break; } if (!doChecking) continue; } FileSettings fs; fs.filename = cfilename; fs.cfg = p.name; fs.msc = true; fs.useMfc = useOfMfc; fs.defines = "_WIN32=1"; if (p.platform == ProjectConfiguration::Win32) fs.platformType = cppcheck::Platform::Win32W; else if (p.platform == ProjectConfiguration::x64) { fs.platformType = cppcheck::Platform::Win64; fs.defines += ";_WIN64=1"; } std::string additionalIncludePaths; for (const ItemDefinitionGroup &i : itemDefinitionGroupList) { if (!i.conditionIsTrue(p)) continue; if (i.cppstd == Standards::CPP11) fs.standard = "c++11"; else if (i.cppstd == Standards::CPP14) fs.standard = "c++14"; else if (i.cppstd == Standards::CPP17) fs.standard = "c++17"; else if (i.cppstd == Standards::CPP20) fs.standard = "c++20"; fs.defines += ';' + i.preprocessorDefinitions; if (i.enhancedInstructionSet == "StreamingSIMDExtensions") fs.defines += ";__SSE__"; else if (i.enhancedInstructionSet == "StreamingSIMDExtensions2") fs.defines += ";__SSE2__"; else if (i.enhancedInstructionSet == "AdvancedVectorExtensions") fs.defines += ";__AVX__"; else if (i.enhancedInstructionSet == "AdvancedVectorExtensions2") fs.defines += ";__AVX2__"; else if (i.enhancedInstructionSet == "AdvancedVectorExtensions512") fs.defines += ";__AVX512__"; additionalIncludePaths += ';' + i.additionalIncludePaths; } fs.setDefines(fs.defines); fs.setIncludePaths(Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables); fileSettings.push_back(fs); } } return true; } bool ImportProject::importBcb6Prj(const std::string &projectFilename) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(projectFilename.c_str()); if (error != tinyxml2::XML_SUCCESS) { printError(std::string("Borland project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); return false; } const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) { printError("Borland project file has no XML root node"); return false; } const std::string& projectDir = Path::simplifyPath(Path::getPathFromFilename(projectFilename)); std::list compileList; std::string includePath; std::string userdefines; std::string sysdefines; std::string cflag1; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "FILELIST") == 0) { for (const tinyxml2::XMLElement *f = node->FirstChildElement(); f; f = f->NextSiblingElement()) { if (std::strcmp(f->Name(), "FILE") == 0) { const char *filename = f->Attribute("FILENAME"); if (filename && Path::acceptFile(filename)) compileList.emplace_back(filename); } } } else if (std::strcmp(node->Name(), "MACROS") == 0) { for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { if (std::strcmp(m->Name(), "INCLUDEPATH") == 0) { const char *v = m->Attribute("value"); if (v) includePath = v; } else if (std::strcmp(m->Name(), "USERDEFINES") == 0) { const char *v = m->Attribute("value"); if (v) userdefines = v; } else if (std::strcmp(m->Name(), "SYSDEFINES") == 0) { const char *v = m->Attribute("value"); if (v) sysdefines = v; } } } else if (std::strcmp(node->Name(), "OPTIONS") == 0) { for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { if (std::strcmp(m->Name(), "CFLAG1") == 0) { const char *v = m->Attribute("value"); if (v) cflag1 = v; } } } } std::set cflags; // parse cflag1 and fill the cflags set { std::string arg; for (char i : cflag1) { if (i == ' ' && !arg.empty()) { cflags.insert(arg); arg.clear(); continue; } arg += i; } if (!arg.empty()) { cflags.insert(arg); } // cleanup: -t is "An alternate name for the -Wxxx switches; there is no difference" // -> Remove every known -txxx argument and replace it with its -Wxxx counterpart. // This way, we know what we have to check for later on. static const std::map synonyms = { { "-tC","-WC" }, { "-tCDR","-WCDR" }, { "-tCDV","-WCDV" }, { "-tW","-W" }, { "-tWC","-WC" }, { "-tWCDR","-WCDR" }, { "-tWCDV","-WCDV" }, { "-tWD","-WD" }, { "-tWDR","-WDR" }, { "-tWDV","-WDV" }, { "-tWM","-WM" }, { "-tWP","-WP" }, { "-tWR","-WR" }, { "-tWU","-WU" }, { "-tWV","-WV" } }; for (std::map::const_iterator i = synonyms.begin(); i != synonyms.end(); ++i) { if (cflags.erase(i->first) > 0) { cflags.insert(i->second); } } } std::string predefines; std::string cppPredefines; // Collecting predefines. See BCB6 help topic "Predefined macros" { cppPredefines += // Defined if you've selected C++ compilation; will increase in later releases. // value 0x0560 (but 0x0564 for our BCB6 SP4) // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros ";__BCPLUSPLUS__=0x0560" // Defined if in C++ mode; otherwise, undefined. ";__cplusplus=1" // Defined as 1 for C++ files(meaning that templates are supported); otherwise, it is undefined. ";__TEMPLATES__=1" // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. ";_WCHAR_T" // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. ";_WCHAR_T_DEFINED" // Defined in any compiler that has an optimizer. ";__BCOPT__=1" // Version number. // BCB6 is 0x056X (SP4 is 0x0564) // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros ";__BORLANDC__=0x0560" ";__TCPLUSPLUS__=0x0560" ";__TURBOC__=0x0560"; // Defined if Calling Convention is set to cdecl; otherwise undefined. const bool useCdecl = (cflags.find("-p") == cflags.end() && cflags.find("-pm") == cflags.end() && cflags.find("-pr") == cflags.end() && cflags.find("-ps") == cflags.end()); if (useCdecl) predefines += ";__CDECL=1"; // Defined by default indicating that the default char is unsigned char. Use the -K compiler option to undefine this macro. const bool treatCharAsUnsignedChar = (cflags.find("-K") != cflags.end()); if (treatCharAsUnsignedChar) predefines += ";_CHAR_UNSIGNED=1"; // Defined whenever one of the CodeGuard compiler options is used; otherwise it is undefined. const bool codeguardUsed = (cflags.find("-vGd") != cflags.end() || cflags.find("-vGt") != cflags.end() || cflags.find("-vGc") != cflags.end()); if (codeguardUsed) predefines += ";__CODEGUARD__"; // When defined, the macro indicates that the program is a console application. const bool isConsoleApp = (cflags.find("-WC") != cflags.end()); if (isConsoleApp) predefines += ";__CONSOLE__=1"; // Enable stack unwinding. This is true by default; use -xd- to disable. const bool enableStackUnwinding = (cflags.find("-xd-") == cflags.end()); if (enableStackUnwinding) predefines += ";_CPPUNWIND=1"; // Defined whenever the -WD compiler option is used; otherwise it is undefined. const bool isDLL = (cflags.find("-WD") != cflags.end()); if (isDLL) predefines += ";__DLL__=1"; // Defined when compiling in 32-bit flat memory model. // TODO: not sure how to switch to another memory model or how to read configuration from project file predefines += ";__FLAT__=1"; // Always defined. The default value is 300. You can change the value to 400 or 500 by using the /4 or /5 compiler options. if (cflags.find("-6") != cflags.end()) predefines += ";_M_IX86=600"; else if (cflags.find("-5") != cflags.end()) predefines += ";_M_IX86=500"; else if (cflags.find("-4") != cflags.end()) predefines += ";_M_IX86=400"; else predefines += ";_M_IX86=300"; // Defined only if the -WM option is used. It specifies that the multithread library is to be linked. const bool linkMtLib = (cflags.find("-WM") != cflags.end()); if (linkMtLib) predefines += ";__MT__=1"; // Defined if Calling Convention is set to Pascal; otherwise undefined. const bool usePascalCallingConvention = (cflags.find("-p") != cflags.end()); if (usePascalCallingConvention) predefines += ";__PASCAL__=1"; // Defined if you compile with the -A compiler option; otherwise, it is undefined. const bool useAnsiKeywordExtensions = (cflags.find("-A") != cflags.end()); if (useAnsiKeywordExtensions) predefines += ";__STDC__=1"; // Thread Local Storage. Always true in C++Builder. predefines += ";__TLC__=1"; // Defined for Windows-only code. const bool isWindowsTarget = (cflags.find("-WC") != cflags.end() || cflags.find("-WCDR") != cflags.end() || cflags.find("-WCDV") != cflags.end() || cflags.find("-WD") != cflags.end() || cflags.find("-WDR") != cflags.end() || cflags.find("-WDV") != cflags.end() || cflags.find("-WM") != cflags.end() || cflags.find("-WP") != cflags.end() || cflags.find("-WR") != cflags.end() || cflags.find("-WU") != cflags.end() || cflags.find("-WV") != cflags.end()); if (isWindowsTarget) predefines += ";_Windows"; // Defined for console and GUI applications. // TODO: I'm not sure about the difference to define "_Windows". // From description, I would assume __WIN32__ is only defined for // executables, while _Windows would also be defined for DLLs, etc. // However, in a newly created DLL project, both __WIN32__ and // _Windows are defined. -> treating them the same for now. // Also boost uses __WIN32__ for OS identification. const bool isConsoleOrGuiApp = isWindowsTarget; if (isConsoleOrGuiApp) predefines += ";__WIN32__=1"; } // Include paths may contain variables like "$(BCB)\include" or "$(BCB)\include\vcl". // Those get resolved by ImportProject::FileSettings::setIncludePaths by // 1. checking the provided variables map ("BCB" => "C:\\Program Files (x86)\\Borland\\CBuilder6") // 2. checking env variables as a fallback // Setting env is always possible. Configuring the variables via cli might be an addition. // Reading the BCB6 install location from registry in windows environments would also be possible, // but I didn't see any such functionality around the source. Not in favor of adding it only // for the BCB6 project loading. std::map variables; const std::string defines = predefines + ";" + sysdefines + ";" + userdefines; const std::string cppDefines = cppPredefines + ";" + defines; const bool forceCppMode = (cflags.find("-P") != cflags.end()); for (const std::string &c : compileList) { // C++ compilation is selected by file extension by default, so these // defines have to be configured on a per-file base. // // > Files with the .CPP extension compile as C++ files. Files with a .C // > extension, with no extension, or with extensions other than .CPP, // > .OBJ, .LIB, or .ASM compile as C files. // (http://docwiki.embarcadero.com/RADStudio/Tokyo/en/BCC32.EXE,_the_C%2B%2B_32-bit_Command-Line_Compiler) // // We can also force C++ compilation for all files using the -P command line switch. const bool cppMode = forceCppMode || Path::getFilenameExtensionInLowerCase(c) == ".cpp"; FileSettings fs; fs.setIncludePaths(projectDir, toStringList(includePath), variables); fs.setDefines(cppMode ? cppDefines : defines); fs.filename = Path::simplifyPath(Path::isAbsolute(c) ? c : projectDir + c); fileSettings.push_back(fs); } return true; } static std::string joinRelativePath(const std::string &path1, const std::string &path2) { if (!path1.empty() && !Path::isAbsolute(path2)) return path1 + path2; return path2; } static std::list readXmlStringList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[]) { std::list ret; for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { if (strcmp(child->Name(), name) != 0) continue; const char *attr = attribute ? child->Attribute(attribute) : child->GetText(); if (attr) ret.push_back(joinRelativePath(path, attr)); } return ret; } static std::string join(const std::list &strlist, const char *sep) { std::string ret; for (const std::string &s : strlist) { ret += (ret.empty() ? "" : sep) + s; } return ret; } static std::string istream_to_string(std::istream &istr) { std::istreambuf_iterator eos; return std::string(std::istreambuf_iterator(istr), eos); } bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *settings) { tinyxml2::XMLDocument doc; const std::string xmldata = istream_to_string(istr); const tinyxml2::XMLError error = doc.Parse(xmldata.data(), xmldata.size()); if (error != tinyxml2::XML_SUCCESS) { printError(std::string("Cppcheck GUI project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); return false; } const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr || strcmp(rootnode->Name(), CppcheckXml::ProjectElementName) != 0) { printError("Cppcheck GUI project file has no XML root node"); return false; } const std::string &path = mPath; std::list paths; std::list suppressions; Settings temp; guiProject.analyzeAllVsConfigs.clear(); for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0 && node->Attribute(CppcheckXml::RootPathNameAttrib)) { temp.basePaths.push_back(joinRelativePath(path, node->Attribute(CppcheckXml::RootPathNameAttrib))); temp.relativePaths = true; } else if (strcmp(node->Name(), CppcheckXml::BugHunting) == 0) temp.bugHunting = true; else if (strcmp(node->Name(), CppcheckXml::BuildDirElementName) == 0) temp.buildDir = joinRelativePath(path, node->GetText() ? node->GetText() : ""); else if (strcmp(node->Name(), CppcheckXml::IncludeDirElementName) == 0) temp.includePaths = readXmlStringList(node, path, CppcheckXml::DirElementName, CppcheckXml::DirNameAttrib); else if (strcmp(node->Name(), CppcheckXml::DefinesElementName) == 0) temp.userDefines = join(readXmlStringList(node, "", CppcheckXml::DefineName, CppcheckXml::DefineNameAttrib), ";"); else if (strcmp(node->Name(), CppcheckXml::UndefinesElementName) == 0) { for (const std::string &u : readXmlStringList(node, "", CppcheckXml::UndefineName, nullptr)) temp.userUndefs.insert(u); } else if (strcmp(node->Name(), CppcheckXml::ImportProjectElementName) == 0) guiProject.projectFile = path + (node->GetText() ? node->GetText() : ""); else if (strcmp(node->Name(), CppcheckXml::PathsElementName) == 0) paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib); else if (strcmp(node->Name(), CppcheckXml::ExcludeElementName) == 0) guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); else if (strcmp(node->Name(), CppcheckXml::FunctionContracts) == 0) { for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { if (strcmp(child->Name(), CppcheckXml::FunctionContract) == 0) { const char *function = child->Attribute(CppcheckXml::ContractFunction); const char *expects = child->Attribute(CppcheckXml::ContractExpects); if (function && expects) temp.functionContracts[function] = expects; } } } else if (strcmp(node->Name(), CppcheckXml::VariableContractsElementName) == 0) { for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { if (strcmp(child->Name(), CppcheckXml::VariableContractItemElementName) == 0) { const char *name = child->Attribute(CppcheckXml::VariableContractVarName); const char *min = child->Attribute(CppcheckXml::VariableContractMin); const char *max = child->Attribute(CppcheckXml::VariableContractMax); if (name) temp.variableContracts[name] = Settings::VariableContracts{min?min:"", max?max:""}; } } } else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0) guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0) guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr); else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) { for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { if (strcmp(child->Name(), CppcheckXml::SuppressionElementName) != 0) continue; auto read = [](const char *s, const char *def) { return s ? s : def; }; Suppressions::Suppression s; s.errorId = read(child->GetText(), ""); s.fileName = read(child->Attribute("fileName"), ""); if (!s.fileName.empty()) s.fileName = joinRelativePath(path, s.fileName); s.lineNumber = child->IntAttribute("lineNumber", Suppressions::Suppression::NO_LINE); s.symbolName = read(child->Attribute("symbolName"), ""); std::istringstream(read(child->Attribute("hash"), "0")) >> s.hash; suppressions.push_back(s); } } else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0) guiProject.checkVsConfigs = readXmlStringList(node, "", CppcheckXml::VSConfigurationName, nullptr); else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0) guiProject.platform = node->GetText(); else if (strcmp(node->Name(), CppcheckXml::AnalyzeAllVsConfigsElementName) == 0) guiProject.analyzeAllVsConfigs = node->GetText(); else if (strcmp(node->Name(), CppcheckXml::Parser) == 0) temp.clang = true; else if (strcmp(node->Name(), CppcheckXml::AddonsElementName) == 0) temp.addons = readXmlStringList(node, "", CppcheckXml::AddonElementName, nullptr); else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0) node->Attribute(CppcheckXml::TagElementName); // FIXME: Write some warning else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) { const std::list toolList = readXmlStringList(node, "", CppcheckXml::ToolElementName, nullptr); for (const std::string &toolName : toolList) { if (toolName == std::string(CppcheckXml::ClangTidy)) temp.clangTidy = true; } } else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0) temp.checkHeaders = (strcmp(node->GetText(), "true") == 0); else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0) temp.checkUnusedTemplates = (strcmp(node->GetText(), "true") == 0); else if (strcmp(node->Name(), CppcheckXml::MaxCtuDepthElementName) == 0) temp.maxCtuDepth = std::atoi(node->GetText()); else if (strcmp(node->Name(), CppcheckXml::MaxTemplateRecursionElementName) == 0) temp.maxTemplateRecursion = std::atoi(node->GetText()); else if (strcmp(node->Name(), CppcheckXml::CheckUnknownFunctionReturn) == 0) ; // TODO else if (strcmp(node->Name(), Settings::SafeChecks::XmlRootName) == 0) { for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { if (strcmp(child->Name(), Settings::SafeChecks::XmlClasses) == 0) temp.safeChecks.classes = true; else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalFunctions) == 0) temp.safeChecks.externalFunctions = true; else if (strcmp(child->Name(), Settings::SafeChecks::XmlInternalFunctions) == 0) temp.safeChecks.internalFunctions = true; else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalVariables) == 0) temp.safeChecks.externalVariables = true; else return false; } } else if (strcmp(node->Name(), CppcheckXml::TagWarningsElementName) == 0) ; // TODO else return false; } settings->basePaths = temp.basePaths; settings->relativePaths |= temp.relativePaths; settings->buildDir = temp.buildDir; settings->includePaths = temp.includePaths; settings->userDefines = temp.userDefines; settings->userUndefs = temp.userUndefs; settings->addons = temp.addons; settings->clang = temp.clang; settings->clangTidy = temp.clangTidy; for (const std::string &p : paths) guiProject.pathNames.push_back(p); for (const Suppressions::Suppression &supp : suppressions) settings->nomsg.addSuppression(supp); settings->checkHeaders = temp.checkHeaders; settings->checkUnusedTemplates = temp.checkUnusedTemplates; settings->maxCtuDepth = temp.maxCtuDepth; settings->maxTemplateRecursion = temp.maxTemplateRecursion; settings->safeChecks = temp.safeChecks; settings->bugHunting = temp.bugHunting; settings->functionContracts = temp.functionContracts; return true; } void ImportProject::selectOneVsConfig(Settings::PlatformType platform) { std::set filenames; for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { if (it->cfg.empty()) { ++it; continue; } const ImportProject::FileSettings &fs = *it; bool remove = false; if (fs.cfg.compare(0,5,"Debug") != 0) remove = true; if (platform == Settings::Win64 && fs.platformType != platform) remove = true; else if ((platform == Settings::Win32A || platform == Settings::Win32W) && fs.platformType == Settings::Win64) remove = true; else if (fs.platformType != Settings::Win64 && platform == Settings::Win64) remove = true; else if (filenames.find(fs.filename) != filenames.end()) remove = true; if (remove) { it = fileSettings.erase(it); } else { filenames.insert(fs.filename); ++it; } } } std::list ImportProject::getVSConfigs() { return std::list(mAllVSConfigs.begin(), mAllVSConfigs.end()); } void ImportProject::setRelativePaths(const std::string &filename) { if (Path::isAbsolute(filename)) return; const std::vector basePaths{Path::fromNativeSeparators(Path::getCurrentPath())}; for (auto &fs: fileSettings) { fs.filename = Path::getRelativePath(fs.filename, basePaths); for (auto &includePath: fs.includePaths) includePath = Path::getRelativePath(includePath, basePaths); } } void ImportProject::printError(const std::string &message) { std::cout << "cppcheck: error: " << message << std::endl; } bool ImportProject::sourceFileExists(const std::string &file) { return Path::fileExists(file); } cppcheck-2.7/lib/importproject.h000066400000000000000000000161201417746362400170000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef importprojectH #define importprojectH //--------------------------------------------------------------------------- #include "config.h" #include "platform.h" #include "utils.h" #include #include #include #include #include #include /// @addtogroup Core /// @{ namespace cppcheck { struct stricmp { bool operator()(const std::string &lhs, const std::string &rhs) const { return caseInsensitiveStringCompare(lhs,rhs) < 0; } }; } class Settings; /** * @brief Importing project settings. */ class CPPCHECKLIB ImportProject { public: enum class Type { UNKNOWN, MISSING, FAILURE, COMPILE_DB, VS_SLN, VS_VCXPROJ, BORLAND, CPPCHECK_GUI }; /** File settings. Multiple configurations for a file is allowed. */ struct CPPCHECKLIB FileSettings { FileSettings() : platformType(cppcheck::Platform::Unspecified), msc(false), useMfc(false) {} std::string cfg; std::string filename; std::string defines; std::string cppcheckDefines() const { return defines + (msc ? ";_MSC_VER=1900" : "") + (useMfc ? ";__AFXWIN_H__=1" : ""); } std::set undefs; std::list includePaths; std::list systemIncludePaths; std::string standard; cppcheck::Platform::PlatformType platformType; bool msc; bool useMfc; void parseCommand(std::string command); void setDefines(std::string defs); void setIncludePaths(const std::string &basepath, const std::list &in, std::map &variables); }; std::list fileSettings; Type projectType; ImportProject(); void selectOneVsConfig(cppcheck::Platform::PlatformType platform); std::list getVSConfigs(); // Cppcheck GUI output struct { std::string analyzeAllVsConfigs; std::vector pathNames; std::list libraries; std::list excludedPaths; std::list checkVsConfigs; std::string projectFile; std::string platform; } guiProject; void ignorePaths(const std::vector &ipaths); void ignoreOtherConfigs(const std::string &cfg); Type import(const std::string &filename, Settings *settings=nullptr); protected: bool importCompileCommands(std::istream &istr); bool importCppcheckGuiProject(std::istream &istr, Settings *settings); virtual bool sourceFileExists(const std::string &file); private: bool importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters); bool importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters); bool importBcb6Prj(const std::string &projectFilename); void printError(const std::string &message); void setRelativePaths(const std::string &filename); std::string mPath; std::set mAllVSConfigs; }; namespace CppcheckXml { const char ProjectElementName[] = "project"; const char ProjectVersionAttrib[] = "version"; const char ProjectFileVersion[] = "1"; const char BuildDirElementName[] = "builddir"; const char ImportProjectElementName[] = "importproject"; const char AnalyzeAllVsConfigsElementName[] = "analyze-all-vs-configs"; const char Parser[] = "parser"; const char BugHunting[] = "bug-hunting"; const char IncludeDirElementName[] = "includedir"; const char DirElementName[] = "dir"; const char DirNameAttrib[] = "name"; const char DefinesElementName[] = "defines"; const char DefineName[] = "define"; const char DefineNameAttrib[] = "name"; const char UndefinesElementName[] = "undefines"; const char UndefineName[] = "undefine"; const char PathsElementName[] = "paths"; const char PathName[] = "dir"; const char PathNameAttrib[] = "name"; const char RootPathName[] = "root"; const char RootPathNameAttrib[] = "name"; const char IgnoreElementName[] = "ignore"; const char IgnorePathName[] = "path"; const char IgnorePathNameAttrib[] = "name"; const char ExcludeElementName[] = "exclude"; const char ExcludePathName[] = "path"; const char ExcludePathNameAttrib[] = "name"; const char FunctionContracts[] = "function-contracts"; const char FunctionContract[] = "contract"; const char ContractFunction[] = "function"; const char ContractExpects[] = "expects"; const char VariableContractsElementName[] = "variable-contracts"; const char VariableContractItemElementName[] = "var"; const char VariableContractVarName[] = "name"; const char VariableContractMin[] = "min"; const char VariableContractMax[] = "max"; const char LibrariesElementName[] = "libraries"; const char LibraryElementName[] = "library"; const char PlatformElementName[] = "platform"; const char SuppressionsElementName[] = "suppressions"; const char SuppressionElementName[] = "suppression"; const char AddonElementName[] = "addon"; const char AddonsElementName[] = "addons"; const char ToolElementName[] = "tool"; const char ToolsElementName[] = "tools"; const char TagsElementName[] = "tags"; const char TagElementName[] = "tag"; const char TagWarningsElementName[] = "tag-warnings"; const char TagAttributeName[] = "tag"; const char WarningElementName[] = "warning"; const char HashAttributeName[] = "hash"; const char CheckHeadersElementName[] = "check-headers"; const char CheckUnusedTemplatesElementName[] = "check-unused-templates"; const char MaxCtuDepthElementName[] = "max-ctu-depth"; const char MaxTemplateRecursionElementName[] = "max-template-recursion"; const char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values"; const char ClangTidy[] = "clang-tidy"; const char Name[] = "name"; const char VSConfigurationElementName[] = "vs-configurations"; const char VSConfigurationName[] = "config"; } /// @} //--------------------------------------------------------------------------- #endif // importprojectH cppcheck-2.7/lib/infer.cpp000066400000000000000000000331321417746362400155370ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "infer.h" #include "calculate.h" #include "errortypes.h" #include "valueptr.h" #include #include #include #include #include #include class Token; template static const ValueFlow::Value* getCompareValue(const std::list& values, Predicate pred, Compare compare) { const ValueFlow::Value* result = nullptr; for (const ValueFlow::Value& value : values) { if (!pred(value)) continue; if (result) result = &std::min(value, *result, [compare](const ValueFlow::Value& x, const ValueFlow::Value& y) { return compare(x.intvalue, y.intvalue); }); else result = &value; } return result; } struct Interval { std::vector minvalue = {}; std::vector maxvalue = {}; std::vector minRef = {}; std::vector maxRef = {}; std::string str() const { std::string result = "["; if (minvalue.size() == 1) result += std::to_string(minvalue.front()); else result += "*"; result += ","; if (maxvalue.size() == 1) result += std::to_string(maxvalue.front()); else result += "*"; result += "]"; return result; } void setMinValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) { minvalue = {x}; if (ref) minRef = {ref}; } void setMaxValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) { maxvalue = {x}; if (ref) maxRef = {ref}; } bool isLessThan(MathLib::bigint x, std::vector* ref = nullptr) const { if (!this->maxvalue.empty() && this->maxvalue.front() < x) { if (ref) *ref = maxRef; return true; } return false; } bool isGreaterThan(MathLib::bigint x, std::vector* ref = nullptr) const { if (!this->minvalue.empty() && this->minvalue.front() > x) { if (ref) *ref = minRef; return true; } return false; } bool isScalar() const { return minvalue.size() == 1 && minvalue == maxvalue; } bool empty() const { return minvalue.empty() && maxvalue.empty(); } bool isScalarOrEmpty() const { return empty() || isScalar(); } MathLib::bigint getScalar() const { assert(isScalar()); return minvalue.front(); } std::vector getScalarRef() const { assert(isScalar()); if (minRef != maxRef) return merge(minRef, maxRef); return minRef; } static Interval fromInt(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) { Interval result; result.setMinValue(x, ref); result.setMaxValue(x, ref); return result; } template static Interval fromValues(const std::list& values, Predicate predicate) { Interval result; const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less{}); if (minValue) { if (minValue->isImpossible() && minValue->bound == ValueFlow::Value::Bound::Upper) result.setMinValue(minValue->intvalue + 1, minValue); if (minValue->isPossible() && minValue->bound == ValueFlow::Value::Bound::Lower) result.setMinValue(minValue->intvalue, minValue); if (minValue->isKnown()) return Interval::fromInt(minValue->intvalue, minValue); } const ValueFlow::Value* maxValue = getCompareValue(values, predicate, std::greater{}); if (maxValue) { if (maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower) result.setMaxValue(maxValue->intvalue - 1, maxValue); if (maxValue->isPossible() && maxValue->bound == ValueFlow::Value::Bound::Upper) result.setMaxValue(maxValue->intvalue, maxValue); assert(!maxValue->isKnown()); } return result; } static Interval fromValues(const std::list& values) { return Interval::fromValues(values, [](const ValueFlow::Value&) { return true; }); } template static std::vector apply(const std::vector& x, const std::vector& y, F f) { if (x.empty()) return {}; if (y.empty()) return {}; return {f(x.front(), y.front())}; } static std::vector merge(std::vector x, const std::vector& y) { x.insert(x.end(), y.begin(), y.end()); return x; } friend Interval operator-(const Interval& lhs, const Interval& rhs) { Interval result; result.minvalue = Interval::apply(lhs.minvalue, rhs.maxvalue, std::minus{}); result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus{}); if (!result.minvalue.empty()) result.minRef = merge(lhs.minRef, rhs.maxRef); if (!result.maxvalue.empty()) result.maxRef = merge(lhs.maxRef, rhs.minRef); return result; } static std::vector equal(const Interval& lhs, const Interval& rhs, std::vector* ref = nullptr) { if (!lhs.isScalar()) return {}; if (!rhs.isScalar()) return {}; if (ref) *ref = merge(lhs.getScalarRef(), rhs.getScalarRef()); return {lhs.minvalue == rhs.minvalue}; } static std::vector compare(const Interval& lhs, const Interval& rhs, std::vector* ref = nullptr) { Interval diff = lhs - rhs; if (diff.isGreaterThan(0, ref)) return {1}; if (diff.isLessThan(0, ref)) return {-1}; std::vector eq = Interval::equal(lhs, rhs, ref); if (!eq.empty()) { if (eq.front() == 0) return {1, -1}; else return {0}; } if (diff.isGreaterThan(-1, ref)) return {0, 1}; if (diff.isLessThan(1, ref)) return {0, -1}; return {}; } static std::vector compare(const std::string& op, const Interval& lhs, const Interval& rhs, std::vector* ref = nullptr) { std::vector r = compare(lhs, rhs, ref); if (r.empty()) return {}; bool b = calculate(op, r.front(), 0); if (std::all_of(r.begin() + 1, r.end(), [&](int i) { return b == calculate(op, i, 0); })) return {b}; return {}; } }; std::string toString(const Interval& i) { return i.str(); } static void addToErrorPath(ValueFlow::Value& value, const std::vector& refs) { std::unordered_set locations; for (const ValueFlow::Value* ref : refs) { if (ref->condition && !value.condition) value.condition = ref->condition; std::copy_if(ref->errorPath.begin(), ref->errorPath.end(), std::back_inserter(value.errorPath), [&](const ErrorPathItem& e) { return locations.insert(e.first).second; }); } } static void setValueKind(ValueFlow::Value& value, const std::vector& refs) { bool isPossible = false; bool isInconclusive = false; for (const ValueFlow::Value* ref : refs) { if (ref->isPossible()) isPossible = true; if (ref->isInconclusive()) isInconclusive = true; } if (isInconclusive) value.setInconclusive(); else if (isPossible) value.setPossible(); else value.setKnown(); } static bool inferNotEqual(const std::list& values, MathLib::bigint x) { return std::any_of(values.begin(), values.end(), [&](const ValueFlow::Value& value) { return value.isImpossible() && value.intvalue == x; }); } std::vector infer(const ValuePtr& model, const std::string& op, std::list lhsValues, std::list rhsValues) { std::vector result; auto notMatch = [&](const ValueFlow::Value& value) { return !model->match(value); }; lhsValues.remove_if(notMatch); rhsValues.remove_if(notMatch); if (lhsValues.empty() || rhsValues.empty()) return result; Interval lhs = Interval::fromValues(lhsValues); Interval rhs = Interval::fromValues(rhsValues); if (op == "-") { Interval diff = lhs - rhs; if (diff.isScalar()) { std::vector refs = diff.getScalarRef(); ValueFlow::Value value(diff.getScalar()); addToErrorPath(value, refs); setValueKind(value, refs); result.push_back(value); } else { if (!diff.minvalue.empty()) { ValueFlow::Value value(diff.minvalue.front() - 1); value.setImpossible(); value.bound = ValueFlow::Value::Bound::Upper; addToErrorPath(value, diff.minRef); result.push_back(value); } if (!diff.maxvalue.empty()) { ValueFlow::Value value(diff.maxvalue.front() + 1); value.setImpossible(); value.bound = ValueFlow::Value::Bound::Lower; addToErrorPath(value, diff.maxRef); result.push_back(value); } } } else if ((op == "!=" || op == "==") && lhs.isScalarOrEmpty() && rhs.isScalarOrEmpty()) { if (lhs.isScalar() && rhs.isScalar()) { std::vector refs = Interval::merge(lhs.getScalarRef(), rhs.getScalarRef()); ValueFlow::Value value(calculate(op, lhs.getScalar(), rhs.getScalar())); addToErrorPath(value, refs); setValueKind(value, refs); result.push_back(value); } else { std::vector refs; if (lhs.isScalar() && inferNotEqual(rhsValues, lhs.getScalar())) refs = lhs.getScalarRef(); else if (rhs.isScalar() && inferNotEqual(lhsValues, rhs.getScalar())) refs = rhs.getScalarRef(); if (!refs.empty()) { ValueFlow::Value value(op == "!="); addToErrorPath(value, refs); setValueKind(value, refs); result.push_back(value); } } } else { std::vector refs; std::vector r = Interval::compare(op, lhs, rhs, &refs); if (!r.empty()) { ValueFlow::Value value(r.front()); addToErrorPath(value, refs); setValueKind(value, refs); result.push_back(value); } } return result; } std::vector infer(const ValuePtr& model, const std::string& op, MathLib::bigint lhs, std::list rhsValues) { return infer(model, op, {model->yield(lhs)}, std::move(rhsValues)); } std::vector infer(const ValuePtr& model, const std::string& op, std::list lhsValues, MathLib::bigint rhs) { return infer(model, op, std::move(lhsValues), {model->yield(rhs)}); } std::vector getMinValue(const ValuePtr& model, const std::list& values) { return Interval::fromValues(values, [&](const ValueFlow::Value& v) { return model->match(v); }).minvalue; } std::vector getMaxValue(const ValuePtr& model, const std::list& values) { return Interval::fromValues(values, [&](const ValueFlow::Value& v) { return model->match(v); }).maxvalue; } cppcheck-2.7/lib/infer.h000066400000000000000000000043131417746362400152030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef inferH #define inferH #include "config.h" #include "mathlib.h" #include "valueflow.h" #include #include #include struct Interval; template class ValuePtr; struct InferModel { virtual bool match(const ValueFlow::Value& value) const = 0; virtual ValueFlow::Value yield(MathLib::bigint value) const = 0; virtual ~InferModel() {} }; std::vector infer(const ValuePtr& model, const std::string& op, std::list lhsValues, std::list rhsValues); std::vector infer(const ValuePtr& model, const std::string& op, MathLib::bigint lhs, std::list rhsValues); std::vector infer(const ValuePtr& model, const std::string& op, std::list lhsValues, MathLib::bigint rhs); CPPCHECKLIB std::vector getMinValue(const ValuePtr& model, const std::list& values); std::vector getMaxValue(const ValuePtr& model, const std::list& values); std::string toString(const Interval& i); #endif cppcheck-2.7/lib/lib.pri000066400000000000000000000104521417746362400152120ustar00rootroot00000000000000# no manual edits - this file is autogenerated by dmake include($$PWD/pcrerules.pri) include($$PWD/../externals/externals.pri) INCLUDEPATH += $$PWD HEADERS += $${PWD}/analyzerinfo.h \ $${PWD}/astutils.h \ $${PWD}/bughuntingchecks.h \ $${PWD}/check.h \ $${PWD}/check64bit.h \ $${PWD}/checkassert.h \ $${PWD}/checkautovariables.h \ $${PWD}/checkbool.h \ $${PWD}/checkboost.h \ $${PWD}/checkbufferoverrun.h \ $${PWD}/checkclass.h \ $${PWD}/checkcondition.h \ $${PWD}/checkexceptionsafety.h \ $${PWD}/checkfunctions.h \ $${PWD}/checkinternal.h \ $${PWD}/checkio.h \ $${PWD}/checkleakautovar.h \ $${PWD}/checkmemoryleak.h \ $${PWD}/checknullpointer.h \ $${PWD}/checkother.h \ $${PWD}/checkpostfixoperator.h \ $${PWD}/checksizeof.h \ $${PWD}/checkstl.h \ $${PWD}/checkstring.h \ $${PWD}/checktype.h \ $${PWD}/checkuninitvar.h \ $${PWD}/checkunusedfunctions.h \ $${PWD}/checkunusedvar.h \ $${PWD}/checkvaarg.h \ $${PWD}/clangimport.h \ $${PWD}/color.h \ $${PWD}/cppcheck.h \ $${PWD}/ctu.h \ $${PWD}/errorlogger.h \ $${PWD}/errortypes.h \ $${PWD}/exprengine.h \ $${PWD}/forwardanalyzer.h \ $${PWD}/importproject.h \ $${PWD}/infer.h \ $${PWD}/library.h \ $${PWD}/mathlib.h \ $${PWD}/path.h \ $${PWD}/pathanalysis.h \ $${PWD}/pathmatch.h \ $${PWD}/platform.h \ $${PWD}/preprocessor.h \ $${PWD}/programmemory.h \ $${PWD}/reverseanalyzer.h \ $${PWD}/settings.h \ $${PWD}/summaries.h \ $${PWD}/suppressions.h \ $${PWD}/symboldatabase.h \ $${PWD}/templatesimplifier.h \ $${PWD}/timer.h \ $${PWD}/token.h \ $${PWD}/tokenize.h \ $${PWD}/tokenlist.h \ $${PWD}/utils.h \ $${PWD}/valueflow.h SOURCES += $${PWD}/analyzerinfo.cpp \ $${PWD}/astutils.cpp \ $${PWD}/bughuntingchecks.cpp \ $${PWD}/check.cpp \ $${PWD}/check64bit.cpp \ $${PWD}/checkassert.cpp \ $${PWD}/checkautovariables.cpp \ $${PWD}/checkbool.cpp \ $${PWD}/checkboost.cpp \ $${PWD}/checkbufferoverrun.cpp \ $${PWD}/checkclass.cpp \ $${PWD}/checkcondition.cpp \ $${PWD}/checkexceptionsafety.cpp \ $${PWD}/checkfunctions.cpp \ $${PWD}/checkinternal.cpp \ $${PWD}/checkio.cpp \ $${PWD}/checkleakautovar.cpp \ $${PWD}/checkmemoryleak.cpp \ $${PWD}/checknullpointer.cpp \ $${PWD}/checkother.cpp \ $${PWD}/checkpostfixoperator.cpp \ $${PWD}/checksizeof.cpp \ $${PWD}/checkstl.cpp \ $${PWD}/checkstring.cpp \ $${PWD}/checktype.cpp \ $${PWD}/checkuninitvar.cpp \ $${PWD}/checkunusedfunctions.cpp \ $${PWD}/checkunusedvar.cpp \ $${PWD}/checkvaarg.cpp \ $${PWD}/clangimport.cpp \ $${PWD}/color.cpp \ $${PWD}/cppcheck.cpp \ $${PWD}/ctu.cpp \ $${PWD}/errorlogger.cpp \ $${PWD}/errortypes.cpp \ $${PWD}/exprengine.cpp \ $${PWD}/forwardanalyzer.cpp \ $${PWD}/importproject.cpp \ $${PWD}/infer.cpp \ $${PWD}/library.cpp \ $${PWD}/mathlib.cpp \ $${PWD}/path.cpp \ $${PWD}/pathanalysis.cpp \ $${PWD}/pathmatch.cpp \ $${PWD}/platform.cpp \ $${PWD}/preprocessor.cpp \ $${PWD}/programmemory.cpp \ $${PWD}/reverseanalyzer.cpp \ $${PWD}/settings.cpp \ $${PWD}/summaries.cpp \ $${PWD}/suppressions.cpp \ $${PWD}/symboldatabase.cpp \ $${PWD}/templatesimplifier.cpp \ $${PWD}/timer.cpp \ $${PWD}/token.cpp \ $${PWD}/tokenize.cpp \ $${PWD}/tokenlist.cpp \ $${PWD}/utils.cpp \ $${PWD}/valueflow.cpp cppcheck-2.7/lib/library.cpp000066400000000000000000002143441417746362400161060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "library.h" #include "astutils.h" #include "mathlib.h" #include "path.h" #include "symboldatabase.h" #include "token.h" #include "tokenlist.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include static std::vector getnames(const char *names) { std::vector ret; while (const char *p = std::strchr(names,',')) { ret.emplace_back(names, p-names); names = p + 1; } ret.emplace_back(names); return ret; } static void gettokenlistfromvalid(const std::string& valid, TokenList& tokenList) { std::istringstream istr(valid + ','); tokenList.createTokens(istr); for (Token *tok = tokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok,"- %num%")) { tok->str("-" + tok->strAt(1)); tok->deleteNext(); } } } Library::Library() : bugHunting(false), mAllocId(0) {} Library::Error Library::load(const char exename[], const char path[]) { if (std::strchr(path,',') != nullptr) { std::string p(path); for (;;) { const std::string::size_type pos = p.find(','); if (pos == std::string::npos) break; const Error &e = load(exename, p.substr(0,pos).c_str()); if (e.errorcode != ErrorCode::OK) return e; p = p.substr(pos+1); } if (!p.empty()) return load(exename, p.c_str()); return Error(); } std::string absolute_path; // open file.. tinyxml2::XMLDocument doc; tinyxml2::XMLError error = doc.LoadFile(path); if (error == tinyxml2::XML_ERROR_FILE_READ_ERROR && Path::getFilenameExtension(path).empty()) // Reading file failed, try again... error = tinyxml2::XML_ERROR_FILE_NOT_FOUND; if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { // failed to open file.. is there no extension? std::string fullfilename(path); if (Path::getFilenameExtension(fullfilename).empty()) { fullfilename += ".cfg"; error = doc.LoadFile(fullfilename.c_str()); if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) absolute_path = Path::getAbsoluteFilePath(fullfilename); } std::list cfgfolders; #ifdef FILESDIR cfgfolders.emplace_back(FILESDIR "/cfg"); #endif if (exename) { const std::string exepath(Path::fromNativeSeparators(Path::getPathFromFilename(exename))); cfgfolders.push_back(exepath + "cfg"); cfgfolders.push_back(exepath); } while (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND && !cfgfolders.empty()) { const std::string cfgfolder(cfgfolders.back()); cfgfolders.pop_back(); const char *sep = (!cfgfolder.empty() && endsWith(cfgfolder,'/') ? "" : "/"); const std::string filename(cfgfolder + sep + fullfilename); error = doc.LoadFile(filename.c_str()); if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) absolute_path = Path::getAbsoluteFilePath(filename); } } else absolute_path = Path::getAbsoluteFilePath(path); if (error == tinyxml2::XML_SUCCESS) { if (mFiles.find(absolute_path) == mFiles.end()) { Error err = load(doc); if (err.errorcode == ErrorCode::OK) mFiles.insert(absolute_path); return err; } return Error(ErrorCode::OK); // ignore duplicates } if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) return Error(ErrorCode::FILE_NOT_FOUND); else { doc.PrintError(); return Error(ErrorCode::BAD_XML); } } Library::Container::Yield Library::Container::yieldFrom(const std::string& yieldName) { if (yieldName == "at_index") return Container::Yield::AT_INDEX; else if (yieldName == "item") return Container::Yield::ITEM; else if (yieldName == "buffer") return Container::Yield::BUFFER; else if (yieldName == "buffer-nt") return Container::Yield::BUFFER_NT; else if (yieldName == "start-iterator") return Container::Yield::START_ITERATOR; else if (yieldName == "end-iterator") return Container::Yield::END_ITERATOR; else if (yieldName == "iterator") return Container::Yield::ITERATOR; else if (yieldName == "size") return Container::Yield::SIZE; else if (yieldName == "empty") return Container::Yield::EMPTY; else return Container::Yield::NO_YIELD; } Library::Container::Action Library::Container::actionFrom(const std::string& actionName) { if (actionName == "resize") return Container::Action::RESIZE; else if (actionName == "clear") return Container::Action::CLEAR; else if (actionName == "push") return Container::Action::PUSH; else if (actionName == "pop") return Container::Action::POP; else if (actionName == "find") return Container::Action::FIND; else if (actionName == "insert") return Container::Action::INSERT; else if (actionName == "erase") return Container::Action::ERASE; else if (actionName == "change-content") return Container::Action::CHANGE_CONTENT; else if (actionName == "change-internal") return Container::Action::CHANGE_INTERNAL; else if (actionName == "change") return Container::Action::CHANGE; else return Container::Action::NO_ACTION; } // cppcheck-suppress unusedFunction - only used in unit tests bool Library::loadxmldata(const char xmldata[], std::size_t len) { tinyxml2::XMLDocument doc; return (tinyxml2::XML_SUCCESS == doc.Parse(xmldata, len)) && (load(doc).errorcode == ErrorCode::OK); } Library::Error Library::load(const tinyxml2::XMLDocument &doc) { const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) { doc.PrintError(); return Error(ErrorCode::BAD_XML); } if (strcmp(rootnode->Name(),"def") != 0) return Error(ErrorCode::UNSUPPORTED_FORMAT, rootnode->Name()); int format = rootnode->IntAttribute("format", 1); // Assume format version 1 if nothing else is specified (very old .cfg files had no 'format' attribute) if (format > 2 || format <= 0) return Error(ErrorCode::UNSUPPORTED_FORMAT); std::set unknown_elements; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { const std::string nodename = node->Name(); if (nodename == "memory" || nodename == "resource") { // get allocationId to use.. int allocationId = 0; for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { if (strcmp(memorynode->Name(),"dealloc")==0) { const std::map::const_iterator it = mDealloc.find(memorynode->GetText()); if (it != mDealloc.end()) { allocationId = it->second.groupId; break; } } } if (allocationId == 0) { if (nodename == "memory") while (!ismemory(++mAllocId)); else while (!isresource(++mAllocId)); allocationId = mAllocId; } // add alloc/dealloc/use functions.. for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { const std::string memorynodename = memorynode->Name(); if (memorynodename == "alloc" || memorynodename == "realloc") { AllocFunc temp = {0}; temp.groupId = allocationId; temp.initData = memorynode->BoolAttribute("init", true); temp.arg = memorynode->IntAttribute("arg", -1); const char *bufferSize = memorynode->Attribute("buffer-size"); if (!bufferSize) temp.bufferSize = AllocFunc::BufferSize::none; else { if (std::strncmp(bufferSize, "malloc", 6) == 0) temp.bufferSize = AllocFunc::BufferSize::malloc; else if (std::strncmp(bufferSize, "calloc", 6) == 0) temp.bufferSize = AllocFunc::BufferSize::calloc; else if (std::strncmp(bufferSize, "strdup", 6) == 0) temp.bufferSize = AllocFunc::BufferSize::strdup; else return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, bufferSize); temp.bufferSizeArg1 = 1; temp.bufferSizeArg2 = 2; if (bufferSize[6] == 0) { // use default values } else if (bufferSize[6] == ':' && bufferSize[7] >= '1' && bufferSize[7] <= '5') { temp.bufferSizeArg1 = bufferSize[7] - '0'; if (bufferSize[8] == ',' && bufferSize[9] >= '1' && bufferSize[9] <= '5') temp.bufferSizeArg2 = bufferSize[9] - '0'; } else return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, bufferSize); } if (memorynodename == "realloc") temp.reallocArg = memorynode->IntAttribute("realloc-arg", 1); if (memorynodename != "realloc") mAlloc[memorynode->GetText()] = temp; else mRealloc[memorynode->GetText()] = temp; } else if (memorynodename == "dealloc") { AllocFunc temp = {0}; temp.groupId = allocationId; temp.arg = memorynode->IntAttribute("arg", 1); mDealloc[memorynode->GetText()] = temp; } else if (memorynodename == "use") functions[memorynode->GetText()].use = true; else unknown_elements.insert(memorynodename); } } else if (nodename == "define") { const char *name = node->Attribute("name"); if (name == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); const char *value = node->Attribute("value"); if (value == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); defines.push_back(std::string(name) + " " + value); } else if (nodename == "function") { const char *name = node->Attribute("name"); if (name == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); for (const std::string &s : getnames(name)) { const Error &err = loadFunction(node, s, unknown_elements); if (err.errorcode != ErrorCode::OK) return err; } } else if (nodename == "reflection") { for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) { if (strcmp(reflectionnode->Name(), "call") != 0) { unknown_elements.insert(reflectionnode->Name()); continue; } const char * const argString = reflectionnode->Attribute("arg"); if (!argString) return Error(ErrorCode::MISSING_ATTRIBUTE, "arg"); mReflection[reflectionnode->GetText()] = atoi(argString); } } else if (nodename == "markup") { const char * const extension = node->Attribute("ext"); if (!extension) return Error(ErrorCode::MISSING_ATTRIBUTE, "ext"); mMarkupExtensions.insert(extension); mReportErrors[extension] = (node->Attribute("reporterrors", "true") != nullptr); mProcessAfterCode[extension] = (node->Attribute("aftercode", "true") != nullptr); for (const tinyxml2::XMLElement *markupnode = node->FirstChildElement(); markupnode; markupnode = markupnode->NextSiblingElement()) { const std::string markupnodename = markupnode->Name(); if (markupnodename == "keywords") { for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { if (strcmp(librarynode->Name(), "keyword") == 0) { const char* nodeName = librarynode->Attribute("name"); if (nodeName == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); mKeywords[extension].insert(nodeName); } else unknown_elements.insert(librarynode->Name()); } } else if (markupnodename == "exported") { for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) { if (strcmp(exporter->Name(), "exporter") != 0) { unknown_elements.insert(exporter->Name()); continue; } const char * const prefix = exporter->Attribute("prefix"); if (!prefix) return Error(ErrorCode::MISSING_ATTRIBUTE, "prefix"); for (const tinyxml2::XMLElement *e = exporter->FirstChildElement(); e; e = e->NextSiblingElement()) { const std::string ename = e->Name(); if (ename == "prefix") mExporters[prefix].addPrefix(e->GetText()); else if (ename == "suffix") mExporters[prefix].addSuffix(e->GetText()); else unknown_elements.insert(ename); } } } else if (markupnodename == "imported") { for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { if (strcmp(librarynode->Name(), "importer") == 0) mImporters[extension].insert(librarynode->GetText()); else unknown_elements.insert(librarynode->Name()); } } else if (markupnodename == "codeblocks") { for (const tinyxml2::XMLElement *blocknode = markupnode->FirstChildElement(); blocknode; blocknode = blocknode->NextSiblingElement()) { const std::string blocknodename = blocknode->Name(); if (blocknodename == "block") { const char * blockName = blocknode->Attribute("name"); if (blockName) mExecutableBlocks[extension].addBlock(blockName); } else if (blocknodename == "structure") { const char * start = blocknode->Attribute("start"); if (start) mExecutableBlocks[extension].setStart(start); const char * end = blocknode->Attribute("end"); if (end) mExecutableBlocks[extension].setEnd(end); const char * offset = blocknode->Attribute("offset"); if (offset) mExecutableBlocks[extension].setOffset(atoi(offset)); } else unknown_elements.insert(blocknodename); } } else unknown_elements.insert(markupnodename); } } else if (nodename == "container") { const char* const id = node->Attribute("id"); if (!id) return Error(ErrorCode::MISSING_ATTRIBUTE, "id"); Container& container = containers[id]; const char* const inherits = node->Attribute("inherits"); if (inherits) { const std::map::const_iterator i = containers.find(inherits); if (i != containers.end()) container = i->second; // Take values from parent and overwrite them if necessary else return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, inherits); } const char* const startPattern = node->Attribute("startPattern"); if (startPattern) { container.startPattern = startPattern; container.startPattern2 = container.startPattern + " !!::"; } const char* const endPattern = node->Attribute("endPattern"); if (endPattern) container.endPattern = endPattern; const char* const itEndPattern = node->Attribute("itEndPattern"); if (itEndPattern) container.itEndPattern = itEndPattern; const char* const opLessAllowed = node->Attribute("opLessAllowed"); if (opLessAllowed) container.opLessAllowed = std::string(opLessAllowed) == "true"; const char* const hasInitializerListConstructor = node->Attribute("hasInitializerListConstructor"); if (hasInitializerListConstructor) container.hasInitializerListConstructor = std::string(hasInitializerListConstructor) == "true"; const char* const view = node->Attribute("view"); if (view) container.view = std::string(view) == "true"; for (const tinyxml2::XMLElement *containerNode = node->FirstChildElement(); containerNode; containerNode = containerNode->NextSiblingElement()) { const std::string containerNodeName = containerNode->Name(); if (containerNodeName == "size" || containerNodeName == "access" || containerNodeName == "other") { for (const tinyxml2::XMLElement *functionNode = containerNode->FirstChildElement(); functionNode; functionNode = functionNode->NextSiblingElement()) { if (std::string(functionNode->Name()) != "function") { unknown_elements.insert(functionNode->Name()); continue; } const char* const functionName = functionNode->Attribute("name"); if (!functionName) return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); const char* const action_ptr = functionNode->Attribute("action"); Container::Action action = Container::Action::NO_ACTION; if (action_ptr) { std::string actionName = action_ptr; action = Container::actionFrom(actionName); if (action == Container::Action::NO_ACTION) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName); } const char* const yield_ptr = functionNode->Attribute("yields"); Container::Yield yield = Container::Yield::NO_YIELD; if (yield_ptr) { std::string yieldName = yield_ptr; yield = Container::yieldFrom(yieldName); if (yield == Container::Yield::NO_YIELD) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName); } container.functions[functionName].action = action; container.functions[functionName].yield = yield; } if (containerNodeName == "size") { const char* const templateArg = containerNode->Attribute("templateParameter"); if (templateArg) container.size_templateArgNo = atoi(templateArg); } else if (containerNodeName == "access") { const char* const indexArg = containerNode->Attribute("indexOperator"); if (indexArg) container.arrayLike_indexOp = std::string(indexArg) == "array-like"; } } else if (containerNodeName == "type") { const char* const templateArg = containerNode->Attribute("templateParameter"); if (templateArg) container.type_templateArgNo = atoi(templateArg); const char* const string = containerNode->Attribute("string"); if (string) container.stdStringLike = std::string(string) == "std-like"; const char* const associative = containerNode->Attribute("associative"); if (associative) container.stdAssociativeLike = std::string(associative) == "std-like"; const char* const unstable = containerNode->Attribute("unstable"); if (unstable) { std::string unstableType = unstable; if (unstableType.find("erase") != std::string::npos) container.unstableErase = true; if (unstableType.find("insert") != std::string::npos) container.unstableInsert = true; } } else if (containerNodeName == "rangeItemRecordType") { for (const tinyxml2::XMLElement* memberNode = node->FirstChildElement(); memberNode; memberNode = memberNode->NextSiblingElement()) { const char *memberName = memberNode->Attribute("name"); const char *memberTemplateParameter = memberNode->Attribute("templateParameter"); struct Container::RangeItemRecordTypeItem member; member.name = memberName ? memberName : ""; member.templateParameter = memberTemplateParameter ? std::atoi(memberTemplateParameter) : -1; container.rangeItemRecordType.emplace_back(member); } } else unknown_elements.insert(containerNodeName); } } else if (nodename == "smart-pointer") { const char *className = node->Attribute("class-name"); if (!className) return Error(ErrorCode::MISSING_ATTRIBUTE, "class-name"); SmartPointer& smartPointer = smartPointers[className]; smartPointer.name = className; for (const tinyxml2::XMLElement* smartPointerNode = node->FirstChildElement(); smartPointerNode; smartPointerNode = smartPointerNode->NextSiblingElement()) { const std::string smartPointerNodeName = smartPointerNode->Name(); if (smartPointerNodeName == "unique") smartPointer.unique = true; } } else if (nodename == "type-checks") { for (const tinyxml2::XMLElement *checkNode = node->FirstChildElement(); checkNode; checkNode = checkNode->NextSiblingElement()) { const std::string &checkName = checkNode->Name(); for (const tinyxml2::XMLElement *checkTypeNode = checkNode->FirstChildElement(); checkTypeNode; checkTypeNode = checkTypeNode->NextSiblingElement()) { const std::string checkTypeName = checkTypeNode->Name(); const char *typeName = checkTypeNode->GetText(); if (!typeName) continue; if (checkTypeName == "check") mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::check; else if (checkTypeName == "suppress") mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::suppress; } } } else if (nodename == "podtype") { const char * const name = node->Attribute("name"); if (!name) return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); PodType podType = {0}; podType.stdtype = PodType::Type::NO; const char * const stdtype = node->Attribute("stdtype"); if (stdtype) { if (std::strcmp(stdtype, "bool") == 0) podType.stdtype = PodType::Type::BOOL; else if (std::strcmp(stdtype, "char") == 0) podType.stdtype = PodType::Type::CHAR; else if (std::strcmp(stdtype, "short") == 0) podType.stdtype = PodType::Type::SHORT; else if (std::strcmp(stdtype, "int") == 0) podType.stdtype = PodType::Type::INT; else if (std::strcmp(stdtype, "long") == 0) podType.stdtype = PodType::Type::LONG; else if (std::strcmp(stdtype, "long long") == 0) podType.stdtype = PodType::Type::LONGLONG; } const char * const size = node->Attribute("size"); if (size) podType.size = atoi(size); const char * const sign = node->Attribute("sign"); if (sign) podType.sign = *sign; for (const std::string &s : getnames(name)) mPodTypes[s] = podType; } else if (nodename == "platformtype") { const char * const type_name = node->Attribute("name"); if (type_name == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); const char *value = node->Attribute("value"); if (value == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); PlatformType type; type.mType = value; std::set platform; for (const tinyxml2::XMLElement *typenode = node->FirstChildElement(); typenode; typenode = typenode->NextSiblingElement()) { const std::string typenodename = typenode->Name(); if (typenodename == "platform") { const char * const type_attribute = typenode->Attribute("type"); if (type_attribute == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "type"); platform.insert(type_attribute); } else if (typenodename == "signed") type.mSigned = true; else if (typenodename == "unsigned") type.mUnsigned = true; else if (typenodename == "long") type.mLong = true; else if (typenodename == "pointer") type.mPointer= true; else if (typenodename == "ptr_ptr") type.mPtrPtr = true; else if (typenodename == "const_ptr") type.mConstPtr = true; else unknown_elements.insert(typenodename); } if (platform.empty()) { const PlatformType * const type_ptr = platform_type(type_name, emptyString); if (type_ptr) { if (*type_ptr == type) return Error(ErrorCode::DUPLICATE_PLATFORM_TYPE, type_name); return Error(ErrorCode::PLATFORM_TYPE_REDEFINED, type_name); } mPlatformTypes[type_name] = type; } else { for (const std::string &p : platform) { const PlatformType * const type_ptr = platform_type(type_name, p); if (type_ptr) { if (*type_ptr == type) return Error(ErrorCode::DUPLICATE_PLATFORM_TYPE, type_name); return Error(ErrorCode::PLATFORM_TYPE_REDEFINED, type_name); } mPlatforms[p].mPlatformTypes[type_name] = type; } } } else unknown_elements.insert(nodename); } if (!unknown_elements.empty()) { std::string str; for (std::set::const_iterator i = unknown_elements.begin(); i != unknown_elements.end();) { str += *i; if (++i != unknown_elements.end()) str += ", "; } return Error(ErrorCode::UNKNOWN_ELEMENT, str); } return Error(ErrorCode::OK); } Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements) { if (name.empty()) return Error(ErrorCode::OK); Function& func = functions[name]; for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { const std::string functionnodename = functionnode->Name(); if (functionnodename == "noreturn") { if (strcmp(functionnode->GetText(), "false") == 0) mNoReturn[name] = FalseTrueMaybe::False; else if (strcmp(functionnode->GetText(), "maybe") == 0) mNoReturn[name] = FalseTrueMaybe::Maybe; else mNoReturn[name] = FalseTrueMaybe::True; // Safe } else if (functionnodename == "pure") func.ispure = true; else if (functionnodename == "const") { func.ispure = true; func.isconst = true; // a constant function is pure } else if (functionnodename == "leak-ignore") func.leakignore = true; else if (functionnodename == "not-overlapping-data") { NonOverlappingData nonOverlappingData; nonOverlappingData.ptr1Arg = functionnode->IntAttribute("ptr1-arg", -1); nonOverlappingData.ptr2Arg = functionnode->IntAttribute("ptr2-arg", -1); nonOverlappingData.sizeArg = functionnode->IntAttribute("size-arg", -1); nonOverlappingData.strlenArg = functionnode->IntAttribute("strlen-arg", -1); mNonOverlappingData[name] = nonOverlappingData; } else if (functionnodename == "use-retval") { func.useretval = Library::UseRetValType::DEFAULT; if (const char *type = functionnode->Attribute("type")) if (std::strcmp(type, "error-code") == 0) func.useretval = Library::UseRetValType::ERROR_CODE; } else if (functionnodename == "returnValue") { if (const char *expr = functionnode->GetText()) mReturnValue[name] = expr; if (const char *type = functionnode->Attribute("type")) mReturnValueType[name] = type; if (const char *container = functionnode->Attribute("container")) mReturnValueContainer[name] = std::atoi(container); if (const char *unknownReturnValues = functionnode->Attribute("unknownValues")) { if (std::strcmp(unknownReturnValues, "all") == 0) { std::vector values{LLONG_MIN, LLONG_MAX}; mUnknownReturnValues[name] = values; } } } else if (functionnodename == "arg") { const char* argNrString = functionnode->Attribute("nr"); if (!argNrString) return Error(ErrorCode::MISSING_ATTRIBUTE, "nr"); const bool bAnyArg = strcmp(argNrString, "any") == 0; const bool bVariadicArg = strcmp(argNrString, "variadic") == 0; const int nr = (bAnyArg || bVariadicArg) ? -1 : std::atoi(argNrString); ArgumentChecks &ac = func.argumentChecks[nr]; ac.optional = functionnode->Attribute("default") != nullptr; ac.variadic = bVariadicArg; const char * const argDirection = functionnode->Attribute("direction"); if (argDirection) { const size_t argDirLen = strlen(argDirection); if (!strncmp(argDirection, "in", argDirLen)) { ac.direction = ArgumentChecks::Direction::DIR_IN; } else if (!strncmp(argDirection, "out", argDirLen)) { ac.direction = ArgumentChecks::Direction::DIR_OUT; } else if (!strncmp(argDirection, "inout", argDirLen)) { ac.direction = ArgumentChecks::Direction::DIR_INOUT; } } for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { const std::string argnodename = argnode->Name(); int indirect = 0; const char * const indirectStr = argnode->Attribute("indirect"); if (indirectStr) indirect = atoi(indirectStr); if (argnodename == "not-bool") ac.notbool = true; else if (argnodename == "not-null") ac.notnull = true; else if (argnodename == "not-uninit") ac.notuninit = indirect; else if (argnodename == "formatstr") ac.formatstr = true; else if (argnodename == "strz") ac.strz = true; else if (argnodename == "valid") { // Validate the validation expression const char *p = argnode->GetText(); if (!isCompliantValidationExpression(p)) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, (!p ? "\"\"" : argnode->GetText())); // Set validation expression ac.valid = argnode->GetText(); } else if (argnodename == "minsize") { const char *typeattr = argnode->Attribute("type"); if (!typeattr) return Error(ErrorCode::MISSING_ATTRIBUTE, "type"); ArgumentChecks::MinSize::Type type; if (strcmp(typeattr,"strlen")==0) type = ArgumentChecks::MinSize::Type::STRLEN; else if (strcmp(typeattr,"argvalue")==0) type = ArgumentChecks::MinSize::Type::ARGVALUE; else if (strcmp(typeattr,"sizeof")==0) type = ArgumentChecks::MinSize::Type::SIZEOF; else if (strcmp(typeattr,"mul")==0) type = ArgumentChecks::MinSize::Type::MUL; else if (strcmp(typeattr,"value")==0) type = ArgumentChecks::MinSize::Type::VALUE; else return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, typeattr); if (type == ArgumentChecks::MinSize::Type::VALUE) { const char *valueattr = argnode->Attribute("value"); if (!valueattr) return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); long long minsizevalue = 0; try { minsizevalue = MathLib::toLongNumber(valueattr); } catch (const InternalError&) { return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, valueattr); } if (minsizevalue <= 0) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, valueattr); ac.minsizes.emplace_back(type, 0); ac.minsizes.back().value = minsizevalue; } else { const char *argattr = argnode->Attribute("arg"); if (!argattr) return Error(ErrorCode::MISSING_ATTRIBUTE, "arg"); if (strlen(argattr) != 1 || argattr[0]<'0' || argattr[0]>'9') return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, argattr); ac.minsizes.reserve(type == ArgumentChecks::MinSize::Type::MUL ? 2 : 1); ac.minsizes.emplace_back(type, argattr[0] - '0'); if (type == ArgumentChecks::MinSize::Type::MUL) { const char *arg2attr = argnode->Attribute("arg2"); if (!arg2attr) return Error(ErrorCode::MISSING_ATTRIBUTE, "arg2"); if (strlen(arg2attr) != 1 || arg2attr[0]<'0' || arg2attr[0]>'9') return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, arg2attr); ac.minsizes.back().arg2 = arg2attr[0] - '0'; } } } else if (argnodename == "iterator") { ac.iteratorInfo.it = true; const char* str = argnode->Attribute("type"); ac.iteratorInfo.first = (str && std::strcmp(str, "first") == 0); ac.iteratorInfo.last = (str && std::strcmp(str, "last") == 0); ac.iteratorInfo.container = argnode->IntAttribute("container", 0); } else unknown_elements.insert(argnodename); } if (ac.notuninit == 0) ac.notuninit = ac.notnull ? 1 : 0; } else if (functionnodename == "ignorefunction") { func.ignore = true; } else if (functionnodename == "formatstr") { func.formatstr = true; const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan"); const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure"); func.formatstr_scan = scan && scan->BoolValue(); func.formatstr_secure = secure && secure->BoolValue(); } else if (functionnodename == "warn") { WarnInfo wi; const char* const severity = functionnode->Attribute("severity"); if (severity == nullptr) return Error(ErrorCode::MISSING_ATTRIBUTE, "severity"); wi.severity = Severity::fromString(severity); const char* const cstd = functionnode->Attribute("cstd"); if (cstd) { if (!wi.standards.setC(cstd)) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, cstd); } else wi.standards.c = Standards::C89; const char* const cppstd = functionnode->Attribute("cppstd"); if (cppstd) { if (!wi.standards.setCPP(cppstd)) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, cppstd); } else wi.standards.cpp = Standards::CPP03; const char* const reason = functionnode->Attribute("reason"); const char* const alternatives = functionnode->Attribute("alternatives"); if (reason && alternatives) { // Construct message wi.message = std::string(reason) + " function '" + name + "' called. It is recommended to use "; std::vector alt = getnames(alternatives); for (std::size_t i = 0; i < alt.size(); ++i) { wi.message += "'" + alt[i] + "'"; if (i == alt.size() - 1) wi.message += " instead."; else if (i == alt.size() - 2) wi.message += " or "; else wi.message += ", "; } } else { const char * const message = functionnode->GetText(); if (!message) { return Error(ErrorCode::MISSING_ATTRIBUTE, "\"reason\" and \"alternatives\" or some text."); } else wi.message = message; } functionwarn[name] = wi; } else if (functionnodename == "container") { const char* const action_ptr = functionnode->Attribute("action"); Container::Action action = Container::Action::NO_ACTION; if (action_ptr) { std::string actionName = action_ptr; action = Container::actionFrom(actionName); if (action == Container::Action::NO_ACTION) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName); } func.containerAction = action; const char* const yield_ptr = functionnode->Attribute("yields"); Container::Yield yield = Container::Yield::NO_YIELD; if (yield_ptr) { std::string yieldName = yield_ptr; yield = Container::yieldFrom(yieldName); if (yield == Container::Yield::NO_YIELD) return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName); } func.containerYield = yield; } else unknown_elements.insert(functionnodename); } return Error(ErrorCode::OK); } std::vector Library::getInvalidArgValues(const std::string &validExpr) { std::vector valid; TokenList tokenList(nullptr); gettokenlistfromvalid(validExpr, tokenList); for (const Token *tok = tokenList.front(); tok; tok = tok ? tok->next() : nullptr) { if (tok->str() == ",") continue; if (Token::Match(tok, ": %num%")) { valid.push_back(InvalidArgValue{InvalidArgValue::Type::le, tok->next()->str(), std::string()}); tok = tok->tokAt(2); } else if (Token::Match(tok, "%num% : %num%")) { valid.push_back(InvalidArgValue{InvalidArgValue::Type::range, tok->str(), tok->strAt(2)}); tok = tok->tokAt(3); } else if (Token::Match(tok, "%num% :")) { valid.push_back(InvalidArgValue{InvalidArgValue::Type::ge, tok->str(), std::string()}); tok = tok->tokAt(2); } else if (Token::Match(tok, "%num%")) { valid.push_back(InvalidArgValue{InvalidArgValue::Type::eq, tok->str(), std::string()}); tok = tok->next(); } } std::vector invalid; if (valid.empty()) return invalid; if (valid[0].type == InvalidArgValue::Type::ge || valid[0].type == InvalidArgValue::Type::eq) invalid.push_back(InvalidArgValue{InvalidArgValue::Type::lt, valid[0].op1, std::string()}); if (valid.back().type == InvalidArgValue::Type::le || valid.back().type == InvalidArgValue::Type::eq) invalid.push_back(InvalidArgValue{InvalidArgValue::Type::gt, valid[0].op1, std::string()}); for (int i = 0; i + 1 < valid.size(); i++) { const InvalidArgValue &v1 = valid[i]; const InvalidArgValue &v2 = valid[i + 1]; if (v1.type == InvalidArgValue::Type::le && v2.type == InvalidArgValue::Type::ge) { if (v1.isInt()) { MathLib::bigint op1 = MathLib::toLongNumber(v1.op1); MathLib::bigint op2 = MathLib::toLongNumber(v2.op1); if (op1 + 1 == op2 - 1) invalid.push_back(InvalidArgValue{InvalidArgValue::Type::eq, MathLib::toString(op1 + 1), std::string()}); else invalid.push_back(InvalidArgValue{InvalidArgValue::Type::range, MathLib::toString(op1 + 1), MathLib::toString(op2 - 1)}); } } } return invalid; } bool Library::isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const { const ArgumentChecks *ac = getarg(ftok, argnr); if (!ac || ac->valid.empty()) return true; else if (ac->valid.find('.') != std::string::npos) return isFloatArgValid(ftok, argnr, argvalue); TokenList tokenList(nullptr); gettokenlistfromvalid(ac->valid, tokenList); for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { if (tok->isNumber() && argvalue == MathLib::toLongNumber(tok->str())) return true; if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toLongNumber(tok->str()) && argvalue <= MathLib::toLongNumber(tok->strAt(2))) return true; if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toLongNumber(tok->str())) return true; if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toLongNumber(tok->strAt(1))) return true; } return false; } bool Library::isFloatArgValid(const Token *ftok, int argnr, double argvalue) const { const ArgumentChecks *ac = getarg(ftok, argnr); if (!ac || ac->valid.empty()) return true; TokenList tokenList(nullptr); gettokenlistfromvalid(ac->valid, tokenList); for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toDoubleNumber(tok->str()) && argvalue <= MathLib::toDoubleNumber(tok->strAt(2))) return true; if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toDoubleNumber(tok->str())) return true; if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toDoubleNumber(tok->strAt(1))) return true; } return false; } std::string Library::getFunctionName(const Token *ftok, bool *error) const { if (!ftok) { *error = true; return ""; } if (ftok->isName()) { for (const Scope *scope = ftok->scope(); scope; scope = scope->nestedIn) { if (!scope->isClassOrStruct()) continue; const std::vector &derivedFrom = scope->definedType->derivedFrom; for (const Type::BaseInfo & baseInfo : derivedFrom) { const std::string name(baseInfo.name + "::" + ftok->str()); if (functions.find(name) != functions.end() && matchArguments(ftok, name)) return name; } } return ftok->str(); } if (ftok->str() == "::") { if (!ftok->astOperand2()) return getFunctionName(ftok->astOperand1(), error); return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error); } if (ftok->str() == "." && ftok->astOperand1()) { const std::string type = astCanonicalType(ftok->astOperand1()); if (type.empty()) { *error = true; return ""; } return type + "::" + getFunctionName(ftok->astOperand2(),error); } *error = true; return ""; } std::string Library::getFunctionName(const Token *ftok) const { if (!Token::Match(ftok, "%name% (") && (ftok->strAt(-1) != "&" || ftok->previous()->astOperand2())) return ""; // Lookup function name using AST.. if (ftok->astParent()) { bool error = false; const Token * tok = ftok->astParent()->isUnaryOp("&") ? ftok->astParent()->astOperand1() : ftok->next()->astOperand1(); const std::string ret = getFunctionName(tok, &error); return error ? std::string() : ret; } // Lookup function name without using AST.. if (Token::simpleMatch(ftok->previous(), ".")) return ""; if (!Token::Match(ftok->tokAt(-2), "%name% ::")) return ftok->str(); std::string ret(ftok->str()); ftok = ftok->tokAt(-2); while (Token::Match(ftok, "%name% ::")) { ret = ftok->str() + "::" + ret; ftok = ftok->tokAt(-2); } return ret; } bool Library::isnullargbad(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); if (!arg) { // scan format string argument should not be null const std::string funcname = getFunctionName(ftok); const std::unordered_map::const_iterator it = functions.find(funcname); if (it != functions.cend() && it->second.formatstr && it->second.formatstr_scan) return true; } return arg && arg->notnull; } bool Library::isuninitargbad(const Token *ftok, int argnr, int indirect, bool *hasIndirect) const { const ArgumentChecks *arg = getarg(ftok, argnr); if (!arg) { // non-scan format string argument should not be uninitialized const std::string funcname = getFunctionName(ftok); const std::unordered_map::const_iterator it = functions.find(funcname); if (it != functions.cend() && it->second.formatstr && !it->second.formatstr_scan) return true; } if (hasIndirect && arg && arg->notuninit >= 1) *hasIndirect = true; return arg && arg->notuninit >= indirect; } /** get allocation info for function */ const Library::AllocFunc* Library::getAllocFuncInfo(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mAlloc, funcname); } /** get deallocation info for function */ const Library::AllocFunc* Library::getDeallocFuncInfo(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mDealloc, funcname); } /** get reallocation info for function */ const Library::AllocFunc* Library::getReallocFuncInfo(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mRealloc, funcname); } /** get allocation id for function */ int Library::getAllocId(const Token *tok, int arg) const { const Library::AllocFunc* af = getAllocFuncInfo(tok); return (af && af->arg == arg) ? af->groupId : 0; } /** get deallocation id for function */ int Library::getDeallocId(const Token *tok, int arg) const { const Library::AllocFunc* af = getDeallocFuncInfo(tok); return (af && af->arg == arg) ? af->groupId : 0; } /** get reallocation id for function */ int Library::getReallocId(const Token *tok, int arg) const { const Library::AllocFunc* af = getReallocFuncInfo(tok); return (af && af->arg == arg) ? af->groupId : 0; } const Library::ArgumentChecks * Library::getarg(const Token *ftok, int argnr) const { if (isNotLibraryFunction(ftok)) return nullptr; const std::unordered_map::const_iterator it1 = functions.find(getFunctionName(ftok)); if (it1 == functions.cend()) return nullptr; const std::map::const_iterator it2 = it1->second.argumentChecks.find(argnr); if (it2 != it1->second.argumentChecks.cend()) return &it2->second; const std::map::const_iterator it3 = it1->second.argumentChecks.find(-1); if (it3 != it1->second.argumentChecks.cend()) return &it3->second; return nullptr; } bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const { if (unknownFunc) unknownFunc->clear(); if (Token::Match(end->tokAt(-2), "!!{ ; }")) { const Token *lastTop = end->tokAt(-2)->astTop(); if (Token::simpleMatch(lastTop, "<<") && Token::simpleMatch(lastTop->astOperand1(), "(") && Token::Match(lastTop->astOperand1()->previous(), "%name% (")) return isnoreturn(lastTop->astOperand1()->previous()); } if (!Token::simpleMatch(end->tokAt(-2), ") ; }")) return false; const Token *funcname = end->linkAt(-2)->previous(); const Token *start = funcname; if (Token::Match(funcname->tokAt(-3),"( * %name% )")) { funcname = funcname->previous(); start = funcname->tokAt(-3); } else if (funcname->isName()) { while (Token::Match(start, "%name%|.|::")) start = start->previous(); } else { return false; } if (Token::Match(start,"[;{}]") && Token::Match(funcname, "%name% )| (")) { if (funcname->str() == "exit") return true; if (!isnotnoreturn(funcname)) { if (unknownFunc && !isnoreturn(funcname)) *unknownFunc = funcname->str(); return true; } } return false; } const Library::Container* Library::detectContainer(const Token* typeStart, bool iterator) const { for (std::map::const_iterator i = containers.begin(); i != containers.end(); ++i) { const Container& container = i->second; if (container.startPattern.empty()) continue; if (!Token::Match(typeStart, container.startPattern2.c_str())) continue; if (!iterator && container.endPattern.empty()) // If endPattern is undefined, it will always match, but itEndPattern has to be defined. return &container; for (const Token* tok = typeStart; tok && !tok->varId(); tok = tok->next()) { if (tok->link()) { const std::string& endPattern = iterator ? container.itEndPattern : container.endPattern; if (Token::Match(tok->link(), endPattern.c_str())) return &container; break; } } } return nullptr; } bool Library::isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback) { if (!cond) return false; if (cond->str() == "(") { const Token* tok = cond->astOperand1(); if (tok && tok->str() == ".") { if (tok->astOperand1() && tok->astOperand1()->valueType()) { if (const Library::Container *container = tok->astOperand1()->valueType()->container) { return tok->astOperand2() && y == container->getYield(tok->astOperand2()->str()); } } else if (!fallback.empty()) { return Token::simpleMatch(cond, "( )") && cond->previous()->str() == fallback; } } } return false; } // returns true if ftok is not a library function bool Library::isNotLibraryFunction(const Token *ftok) const { if (ftok->function() && ftok->function()->nestedIn && ftok->function()->nestedIn->type != Scope::eGlobal) return true; // variables are not library functions. if (ftok->varId()) return true; return !matchArguments(ftok, getFunctionName(ftok)); } bool Library::matchArguments(const Token *ftok, const std::string &functionName) const { const int callargs = numberOfArguments(ftok); const std::unordered_map::const_iterator it = functions.find(functionName); if (it == functions.cend()) return (callargs == 0); int args = 0; int firstOptionalArg = -1; for (std::map::const_iterator it2 = it->second.argumentChecks.cbegin(); it2 != it->second.argumentChecks.cend(); ++it2) { if (it2->first > args) args = it2->first; if (it2->second.optional && (firstOptionalArg == -1 || firstOptionalArg > it2->first)) firstOptionalArg = it2->first; if (it2->second.formatstr || it2->second.variadic) return args <= callargs; } return (firstOptionalArg < 0) ? args == callargs : (callargs >= firstOptionalArg-1 && callargs <= args); } const Library::WarnInfo* Library::getWarnInfo(const Token* ftok) const { if (isNotLibraryFunction(ftok)) return nullptr; std::map::const_iterator i = functionwarn.find(getFunctionName(ftok)); if (i == functionwarn.cend()) return nullptr; return &i->second; } bool Library::isCompliantValidationExpression(const char* p) { if (!p) return false; bool error = false; bool range = false; bool has_dot = false; bool has_E = false; error = *p == '.'; for (; *p; p++) { if (std::isdigit(*p)) error |= (*(p + 1) == '-'); else if (*p == ':') { error |= range | (*(p + 1) == '.'); range = true; has_dot = false; has_E = false; } else if ((*p == '-') || (*p == '+')) error |= (!std::isdigit(*(p + 1))); else if (*p == ',') { range = false; error |= *(p + 1) == '.'; has_dot = false; has_E = false; } else if (*p == '.') { error |= has_dot | (!std::isdigit(*(p + 1))); has_dot = true; } else if (*p == 'E' || *p == 'e') { error |= has_E; has_E = true; } else return false; } return !error; } bool Library::formatstr_function(const Token* ftok) const { if (isNotLibraryFunction(ftok)) return false; const std::unordered_map::const_iterator it = functions.find(getFunctionName(ftok)); if (it != functions.cend()) return it->second.formatstr; return false; } int Library::formatstr_argno(const Token* ftok) const { const std::map& argumentChecksFunc = functions.at(getFunctionName(ftok)).argumentChecks; for (std::map::const_iterator i = argumentChecksFunc.cbegin(); i != argumentChecksFunc.cend(); ++i) { if (i->second.formatstr) { return i->first - 1; } } return -1; } bool Library::formatstr_scan(const Token* ftok) const { return functions.at(getFunctionName(ftok)).formatstr_scan; } bool Library::formatstr_secure(const Token* ftok) const { return functions.at(getFunctionName(ftok)).formatstr_secure; } const Library::NonOverlappingData* Library::getNonOverlappingData(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return nullptr; const std::unordered_map::const_iterator it = mNonOverlappingData.find(getFunctionName(ftok)); return (it != mNonOverlappingData.cend()) ? &it->second : nullptr; } Library::UseRetValType Library::getUseRetValType(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return Library::UseRetValType::NONE; const std::unordered_map::const_iterator it = functions.find(getFunctionName(ftok)); if (it != functions.cend()) return it->second.useretval; return Library::UseRetValType::NONE; } const std::string& Library::returnValue(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return emptyString; const std::map::const_iterator it = mReturnValue.find(getFunctionName(ftok)); return it != mReturnValue.end() ? it->second : emptyString; } const std::string& Library::returnValueType(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return emptyString; const std::map::const_iterator it = mReturnValueType.find(getFunctionName(ftok)); return it != mReturnValueType.end() ? it->second : emptyString; } int Library::returnValueContainer(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return -1; const std::map::const_iterator it = mReturnValueContainer.find(getFunctionName(ftok)); return it != mReturnValueContainer.end() ? it->second : -1; } std::vector Library::unknownReturnValues(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return std::vector(); const std::map>::const_iterator it = mUnknownReturnValues.find(getFunctionName(ftok)); return (it == mUnknownReturnValues.end()) ? std::vector() : it->second; } const Library::Function *Library::getFunction(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return nullptr; const std::unordered_map::const_iterator it1 = functions.find(getFunctionName(ftok)); if (it1 == functions.cend()) return nullptr; return &it1->second; } bool Library::hasminsize(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return false; const std::unordered_map::const_iterator it1 = functions.find(getFunctionName(ftok)); if (it1 == functions.cend()) return false; for (std::map::const_iterator it2 = it1->second.argumentChecks.cbegin(); it2 != it1->second.argumentChecks.cend(); ++it2) { if (!it2->second.minsizes.empty()) return true; } return false; } Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr) const { const ArgumentChecks* arg = getarg(ftok, argnr); if (arg) return arg->direction; if (formatstr_function(ftok)) { const int fs_argno = formatstr_argno(ftok); if (fs_argno >= 0 && argnr >= fs_argno) { if (formatstr_scan(ftok)) return ArgumentChecks::Direction::DIR_OUT; else return ArgumentChecks::Direction::DIR_IN; } } return ArgumentChecks::Direction::DIR_UNKNOWN; } bool Library::ignorefunction(const std::string& functionName) const { const std::unordered_map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return it->second.ignore; return false; } bool Library::isUse(const std::string& functionName) const { const std::unordered_map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return it->second.use; return false; } bool Library::isLeakIgnore(const std::string& functionName) const { const std::unordered_map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return it->second.leakignore; return false; } bool Library::isFunctionConst(const std::string& functionName, bool pure) const { const std::unordered_map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return pure ? it->second.ispure : it->second.isconst; return false; } bool Library::isFunctionConst(const Token *ftok) const { if (ftok->function() && ftok->function()->isConst()) return true; if (isNotLibraryFunction(ftok)) return false; const std::unordered_map::const_iterator it = functions.find(getFunctionName(ftok)); return (it != functions.end() && it->second.isconst); } bool Library::isnoreturn(const Token *ftok) const { if (ftok->function() && ftok->function()->isAttributeNoreturn()) return true; if (isNotLibraryFunction(ftok)) return false; const std::unordered_map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); if (it == mNoReturn.end()) return false; if (it->second == FalseTrueMaybe::Maybe) return !bugHunting; // in bugHunting "maybe" means function is not noreturn return it->second == FalseTrueMaybe::True; } bool Library::isnotnoreturn(const Token *ftok) const { if (ftok->function() && ftok->function()->isAttributeNoreturn()) return false; if (isNotLibraryFunction(ftok)) return false; const std::unordered_map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); if (it == mNoReturn.end()) return false; if (it->second == FalseTrueMaybe::Maybe) return bugHunting; // in bugHunting "maybe" means function is not noreturn return it->second == FalseTrueMaybe::False; } bool Library::markupFile(const std::string &path) const { return mMarkupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != mMarkupExtensions.end(); } bool Library::processMarkupAfterCode(const std::string &path) const { const std::map::const_iterator it = mProcessAfterCode.find(Path::getFilenameExtensionInLowerCase(path)); return (it == mProcessAfterCode.end() || it->second); } bool Library::reportErrors(const std::string &path) const { const std::map::const_iterator it = mReportErrors.find(Path::getFilenameExtensionInLowerCase(path)); return (it == mReportErrors.end() || it->second); } bool Library::isexecutableblock(const std::string &file, const std::string &token) const { const std::map::const_iterator it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); return (it != mExecutableBlocks.end() && it->second.isBlock(token)); } int Library::blockstartoffset(const std::string &file) const { int offset = -1; const std::map::const_iterator map_it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != mExecutableBlocks.end()) { offset = map_it->second.offset(); } return offset; } const std::string& Library::blockstart(const std::string &file) const { const std::map::const_iterator map_it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != mExecutableBlocks.end()) { return map_it->second.start(); } return emptyString; } const std::string& Library::blockend(const std::string &file) const { const std::map::const_iterator map_it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != mExecutableBlocks.end()) { return map_it->second.end(); } return emptyString; } bool Library::iskeyword(const std::string &file, const std::string &keyword) const { const std::map>::const_iterator it = mKeywords.find(Path::getFilenameExtensionInLowerCase(file)); return (it != mKeywords.end() && it->second.count(keyword)); } bool Library::isimporter(const std::string& file, const std::string &importer) const { const std::map>::const_iterator it = mImporters.find(Path::getFilenameExtensionInLowerCase(file)); return (it != mImporters.end() && it->second.count(importer) > 0); } const Token* Library::getContainerFromYield(const Token* tok, Library::Container::Yield yield) const { if (!tok) return nullptr; if (Token::Match(tok->tokAt(-2), ". %name% (")) { const Token* containerTok = tok->tokAt(-2)->astOperand1(); if (!astIsContainer(containerTok)) return nullptr; if (containerTok->valueType()->container && containerTok->valueType()->container->getYield(tok->strAt(-1)) == yield) return containerTok; if (yield == Library::Container::Yield::EMPTY && Token::simpleMatch(tok->tokAt(-1), "empty ( )")) return containerTok; if (yield == Library::Container::Yield::SIZE && Token::Match(tok->tokAt(-1), "size|length ( )")) return containerTok; } else if (Token::Match(tok->previous(), "%name% (")) { if (const Library::Function* f = this->getFunction(tok->previous())) { if (f->containerYield == yield) { return tok->astOperand2(); } } } return nullptr; } // cppcheck-suppress unusedFunction const Token* Library::getContainerFromAction(const Token* tok, Library::Container::Action action) const { if (!tok) return nullptr; if (Token::Match(tok->tokAt(-2), ". %name% (")) { const Token* containerTok = tok->tokAt(-2)->astOperand1(); if (!astIsContainer(containerTok)) return nullptr; if (containerTok->valueType()->container && containerTok->valueType()->container->getAction(tok->strAt(-1)) == action) return containerTok; if (Token::simpleMatch(tok->tokAt(-1), "empty ( )")) return containerTok; } else if (Token::Match(tok->previous(), "%name% (")) { if (const Library::Function* f = this->getFunction(tok->previous())) { if (f->containerAction == action) { return tok->astOperand2(); } } } return nullptr; } bool Library::isSmartPointer(const Token* tok) const { return detectSmartPointer(tok); } const Library::SmartPointer* Library::detectSmartPointer(const Token* tok) const { std::string typestr; while (Token::Match(tok, "%name%|::")) { typestr += tok->str(); tok = tok->next(); } auto it = smartPointers.find(typestr); if (it == smartPointers.end()) return nullptr; return &it->second; } CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok) { if (!tok) return nullptr; // TODO: Support dereferencing iterators // TODO: Support dereferencing with -> if (tok->isUnaryOp("*") && astIsPointer(tok->astOperand1())) { for (const ValueFlow::Value& v:tok->astOperand1()->values()) { if (!v.isLocalLifetimeValue()) continue; if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) continue; return getLibraryContainer(v.tokvalue); } } if (!tok->valueType()) return nullptr; return tok->valueType()->container; } Library::TypeCheck Library::getTypeCheck(const std::string &check, const std::string &typeName) const { auto it = mTypeChecks.find(std::pair(check, typeName)); return it == mTypeChecks.end() ? TypeCheck::def : it->second; } cppcheck-2.7/lib/library.h000066400000000000000000000545631417746362400155600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef libraryH #define libraryH //--------------------------------------------------------------------------- #include "config.h" #include "mathlib.h" #include "errortypes.h" #include "standards.h" #include #include #include #include #include #include #include class Token; namespace tinyxml2 { class XMLDocument; class XMLElement; } /// @addtogroup Core /// @{ /** * @brief Library definitions handling */ class CPPCHECKLIB Library { friend class TestSymbolDatabase; // For testing only public: Library(); enum class ErrorCode { OK, FILE_NOT_FOUND, BAD_XML, UNKNOWN_ELEMENT, MISSING_ATTRIBUTE, BAD_ATTRIBUTE_VALUE, UNSUPPORTED_FORMAT, DUPLICATE_PLATFORM_TYPE, PLATFORM_TYPE_REDEFINED }; class Error { public: Error() : errorcode(ErrorCode::OK) {} explicit Error(ErrorCode e) : errorcode(e) {} template Error(ErrorCode e, T&& r) : errorcode(e), reason(r) {} ErrorCode errorcode; std::string reason; }; Error load(const char exename[], const char path[]); Error load(const tinyxml2::XMLDocument &doc); /** this is used for unit tests */ bool loadxmldata(const char xmldata[], std::size_t len); struct AllocFunc { int groupId; int arg; enum class BufferSize {none,malloc,calloc,strdup}; BufferSize bufferSize; int bufferSizeArg1; int bufferSizeArg2; int reallocArg; bool initData; }; /** get allocation info for function */ const AllocFunc* getAllocFuncInfo(const Token *tok) const; /** get deallocation info for function */ const AllocFunc* getDeallocFuncInfo(const Token *tok) const; /** get reallocation info for function */ const AllocFunc* getReallocFuncInfo(const Token *tok) const; /** get allocation id for function */ int getAllocId(const Token *tok, int arg) const; /** get deallocation id for function */ int getDeallocId(const Token *tok, int arg) const; /** get reallocation id for function */ int getReallocId(const Token *tok, int arg) const; /** get allocation info for function by name (deprecated, use other alloc) */ const AllocFunc* getAllocFuncInfo(const char name[]) const { return getAllocDealloc(mAlloc, name); } /** get deallocation info for function by name (deprecated, use other alloc) */ const AllocFunc* getDeallocFuncInfo(const char name[]) const { return getAllocDealloc(mDealloc, name); } /** get allocation id for function by name (deprecated, use other alloc) */ int allocId(const char name[]) const { const AllocFunc* af = getAllocDealloc(mAlloc, name); return af ? af->groupId : 0; } /** get deallocation id for function by name (deprecated, use other alloc) */ int deallocId(const char name[]) const { const AllocFunc* af = getAllocDealloc(mDealloc, name); return af ? af->groupId : 0; } /** set allocation id for function */ void setalloc(const std::string &functionname, int id, int arg) { mAlloc[functionname].groupId = id; mAlloc[functionname].arg = arg; } void setdealloc(const std::string &functionname, int id, int arg) { mDealloc[functionname].groupId = id; mDealloc[functionname].arg = arg; } void setrealloc(const std::string &functionname, int id, int arg, int reallocArg = 1) { mRealloc[functionname].groupId = id; mRealloc[functionname].arg = arg; mRealloc[functionname].reallocArg = reallocArg; } /** add noreturn function setting */ void setnoreturn(const std::string& funcname, bool noreturn) { mNoReturn[funcname] = noreturn ? FalseTrueMaybe::True : FalseTrueMaybe::False; } static bool isCompliantValidationExpression(const char* p); /** is allocation type memory? */ static bool ismemory(const int id) { return ((id > 0) && ((id & 1) == 0)); } static bool ismemory(const AllocFunc* const func) { return ((func->groupId > 0) && ((func->groupId & 1) == 0)); } /** is allocation type resource? */ static bool isresource(const int id) { return ((id > 0) && ((id & 1) == 1)); } static bool isresource(const AllocFunc* const func) { return ((func->groupId > 0) && ((func->groupId & 1) == 1)); } bool formatstr_function(const Token* ftok) const; int formatstr_argno(const Token* ftok) const; bool formatstr_scan(const Token* ftok) const; bool formatstr_secure(const Token* ftok) const; struct NonOverlappingData { int ptr1Arg; int ptr2Arg; int sizeArg; int strlenArg; }; const NonOverlappingData* getNonOverlappingData(const Token *ftok) const; struct WarnInfo { std::string message; Standards standards; Severity::SeverityType severity; }; std::map functionwarn; const WarnInfo* getWarnInfo(const Token* ftok) const; // returns true if ftok is not a library function bool isNotLibraryFunction(const Token *ftok) const; bool matchArguments(const Token *ftok, const std::string &functionName) const; enum class UseRetValType { NONE, DEFAULT, ERROR_CODE }; UseRetValType getUseRetValType(const Token* ftok) const; const std::string& returnValue(const Token *ftok) const; const std::string& returnValueType(const Token *ftok) const; int returnValueContainer(const Token *ftok) const; std::vector unknownReturnValues(const Token *ftok) const; bool isnoreturn(const Token *ftok) const; bool isnotnoreturn(const Token *ftok) const; bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const; class Container { public: Container() : type_templateArgNo(-1), size_templateArgNo(-1), arrayLike_indexOp(false), stdStringLike(false), stdAssociativeLike(false), opLessAllowed(true), hasInitializerListConstructor(false), unstableErase(false), unstableInsert(false), view(false) {} enum class Action { RESIZE, CLEAR, PUSH, POP, FIND, INSERT, ERASE, CHANGE_CONTENT, CHANGE, CHANGE_INTERNAL, NO_ACTION }; enum class Yield { AT_INDEX, ITEM, BUFFER, BUFFER_NT, START_ITERATOR, END_ITERATOR, ITERATOR, SIZE, EMPTY, NO_YIELD }; struct Function { Action action; Yield yield; }; struct RangeItemRecordTypeItem { std::string name; int templateParameter; }; std::string startPattern, startPattern2, endPattern, itEndPattern; std::map functions; int type_templateArgNo; std::vector rangeItemRecordType; int size_templateArgNo; bool arrayLike_indexOp; bool stdStringLike; bool stdAssociativeLike; bool opLessAllowed; bool hasInitializerListConstructor; bool unstableErase; bool unstableInsert; bool view; Action getAction(const std::string& function) const { const std::map::const_iterator i = functions.find(function); if (i != functions.end()) return i->second.action; return Action::NO_ACTION; } Yield getYield(const std::string& function) const { const std::map::const_iterator i = functions.find(function); if (i != functions.end()) return i->second.yield; return Yield::NO_YIELD; } static Yield yieldFrom(const std::string& yieldName); static Action actionFrom(const std::string& actionName); }; std::map containers; const Container* detectContainer(const Token* typeStart, bool iterator = false) const; class ArgumentChecks { public: ArgumentChecks() : notbool(false), notnull(false), notuninit(-1), formatstr(false), strz(false), optional(false), variadic(false), iteratorInfo(), direction(Direction::DIR_UNKNOWN) {} bool notbool; bool notnull; int notuninit; bool formatstr; bool strz; bool optional; bool variadic; std::string valid; class IteratorInfo { public: IteratorInfo() : container(0), it(false), first(false), last(false) {} int container; bool it; bool first; bool last; }; IteratorInfo iteratorInfo; class MinSize { public: enum class Type { NONE, STRLEN, ARGVALUE, SIZEOF, MUL, VALUE }; MinSize(Type t, int a) : type(t), arg(a), arg2(0), value(0) {} Type type; int arg; int arg2; long long value; }; std::vector minsizes; enum class Direction { DIR_IN, ///< Input to called function. Data is treated as read-only. DIR_OUT, ///< Output to caller. Data is passed by reference or address and is potentially written. DIR_INOUT, ///< Input to called function, and output to caller. Data is passed by reference or address and is potentially modified. DIR_UNKNOWN ///< direction not known / specified }; Direction direction; }; struct Function { std::map argumentChecks; // argument nr => argument data bool use; bool leakignore; bool isconst; bool ispure; UseRetValType useretval; bool ignore; // ignore functions/macros from a library (gtk, qt etc) bool formatstr; bool formatstr_scan; bool formatstr_secure; Container::Action containerAction; Container::Yield containerYield; Function() : use(false), leakignore(false), isconst(false), ispure(false), useretval(UseRetValType::NONE), ignore(false), formatstr(false), formatstr_scan(false), formatstr_secure(false), containerAction(Container::Action::NO_ACTION), containerYield(Container::Yield::NO_YIELD) {} }; const Function *getFunction(const Token *ftok) const; std::unordered_map functions; bool isUse(const std::string& functionName) const; bool isLeakIgnore(const std::string& functionName) const; bool isFunctionConst(const std::string& functionName, bool pure) const; bool isFunctionConst(const Token *ftok) const; bool isboolargbad(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->notbool; } bool isnullargbad(const Token *ftok, int argnr) const; bool isuninitargbad(const Token *ftok, int argnr, int indirect = 0, bool *hasIndirect=nullptr) const; bool isargformatstr(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->formatstr; } bool isargstrz(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->strz; } bool isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const; bool isFloatArgValid(const Token *ftok, int argnr, double argvalue) const; const std::string& validarg(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg ? arg->valid : emptyString; } struct InvalidArgValue { enum class Type {le, lt, eq, ge, gt, range} type; std::string op1; std::string op2; bool isInt() const { return MathLib::isInt(op1); } }; static std::vector getInvalidArgValues(const std::string &validExpr); const ArgumentChecks::IteratorInfo *getArgIteratorInfo(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->iteratorInfo.it ? &arg->iteratorInfo : nullptr; } bool hasminsize(const Token *ftok) const; const std::vector *argminsizes(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg ? &arg->minsizes : nullptr; } ArgumentChecks::Direction getArgDirection(const Token* ftok, int argnr) const; bool markupFile(const std::string &path) const; bool processMarkupAfterCode(const std::string &path) const; const std::set &markupExtensions() const { return mMarkupExtensions; } bool reportErrors(const std::string &path) const; bool ignorefunction(const std::string &functionName) const; bool isexecutableblock(const std::string &file, const std::string &token) const; int blockstartoffset(const std::string &file) const; const std::string& blockstart(const std::string &file) const; const std::string& blockend(const std::string &file) const; bool iskeyword(const std::string &file, const std::string &keyword) const; bool isexporter(const std::string &prefix) const { return mExporters.find(prefix) != mExporters.end(); } bool isexportedprefix(const std::string &prefix, const std::string &token) const { const std::map::const_iterator it = mExporters.find(prefix); return (it != mExporters.end() && it->second.isPrefix(token)); } bool isexportedsuffix(const std::string &prefix, const std::string &token) const { const std::map::const_iterator it = mExporters.find(prefix); return (it != mExporters.end() && it->second.isSuffix(token)); } bool isimporter(const std::string& file, const std::string &importer) const; const Token* getContainerFromYield(const Token* tok, Container::Yield yield) const; const Token* getContainerFromAction(const Token* tok, Container::Action action) const; bool isreflection(const std::string &token) const { return mReflection.find(token) != mReflection.end(); } int reflectionArgument(const std::string &token) const { const std::map::const_iterator it = mReflection.find(token); if (it != mReflection.end()) return it->second; return -1; } std::vector defines; // to provide some library defines struct SmartPointer { std::string name = ""; bool unique = false; }; std::map smartPointers; bool isSmartPointer(const Token *tok) const; const SmartPointer* detectSmartPointer(const Token* tok) const; struct PodType { unsigned int size; char sign; enum class Type { NO, BOOL, CHAR, SHORT, INT, LONG, LONGLONG } stdtype; }; const struct PodType *podtype(const std::string &name) const { const std::unordered_map::const_iterator it = mPodTypes.find(name); return (it != mPodTypes.end()) ? &(it->second) : nullptr; } struct PlatformType { PlatformType() : mSigned(false) , mUnsigned(false) , mLong(false) , mPointer(false) , mPtrPtr(false) , mConstPtr(false) {} bool operator == (const PlatformType & type) const { return (mSigned == type.mSigned && mUnsigned == type.mUnsigned && mLong == type.mLong && mPointer == type.mPointer && mPtrPtr == type.mPtrPtr && mConstPtr == type.mConstPtr && mType == type.mType); } bool operator != (const PlatformType & type) const { return !(*this == type); } std::string mType; bool mSigned; bool mUnsigned; bool mLong; bool mPointer; bool mPtrPtr; bool mConstPtr; }; struct Platform { const PlatformType *platform_type(const std::string &name) const { const std::map::const_iterator it = mPlatformTypes.find(name); return (it != mPlatformTypes.end()) ? &(it->second) : nullptr; } std::map mPlatformTypes; }; const PlatformType *platform_type(const std::string &name, const std::string & platform) const { const std::map::const_iterator it = mPlatforms.find(platform); if (it != mPlatforms.end()) { const PlatformType * const type = it->second.platform_type(name); if (type) return type; } const std::map::const_iterator it2 = mPlatformTypes.find(name); return (it2 != mPlatformTypes.end()) ? &(it2->second) : nullptr; } /** * Get function name for function call */ std::string getFunctionName(const Token *ftok) const; static bool isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback=""); /** Suppress/check a type */ enum class TypeCheck { def, check, suppress }; TypeCheck getTypeCheck(const std::string &check, const std::string &typeName) const; bool bugHunting; private: // load a xml node Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements); class ExportedFunctions { public: void addPrefix(const std::string& prefix) { mPrefixes.insert(prefix); } void addSuffix(const std::string& suffix) { mSuffixes.insert(suffix); } bool isPrefix(const std::string& prefix) const { return (mPrefixes.find(prefix) != mPrefixes.end()); } bool isSuffix(const std::string& suffix) const { return (mSuffixes.find(suffix) != mSuffixes.end()); } private: std::set mPrefixes; std::set mSuffixes; }; class CodeBlock { public: CodeBlock() : mOffset(0) {} void setStart(const char* s) { mStart = s; } void setEnd(const char* e) { mEnd = e; } void setOffset(const int o) { mOffset = o; } void addBlock(const char* blockName) { mBlocks.insert(blockName); } const std::string& start() const { return mStart; } const std::string& end() const { return mEnd; } int offset() const { return mOffset; } bool isBlock(const std::string& blockName) const { return mBlocks.find(blockName) != mBlocks.end(); } private: std::string mStart; std::string mEnd; int mOffset; std::set mBlocks; }; enum class FalseTrueMaybe { False, True, Maybe }; int mAllocId; std::set mFiles; std::map mAlloc; // allocation functions std::map mDealloc; // deallocation functions std::map mRealloc; // reallocation functions std::unordered_map mNoReturn; // is function noreturn? std::map mReturnValue; std::map mReturnValueType; std::map mReturnValueContainer; std::map> mUnknownReturnValues; std::map mReportErrors; std::map mProcessAfterCode; std::set mMarkupExtensions; // file extensions of markup files std::map> mKeywords; // keywords for code in the library std::map mExecutableBlocks; // keywords for blocks of executable code std::map mExporters; // keywords that export variables/functions to libraries (meta-code/macros) std::map> mImporters; // keywords that import variables/functions std::map mReflection; // invocation of reflection std::unordered_map mPodTypes; // pod types std::map mPlatformTypes; // platform independent typedefs std::map mPlatforms; // platform dependent typedefs std::map, TypeCheck> mTypeChecks; std::unordered_map mNonOverlappingData; const ArgumentChecks * getarg(const Token *ftok, int argnr) const; std::string getFunctionName(const Token *ftok, bool *error) const; static const AllocFunc* getAllocDealloc(const std::map &data, const std::string &name) { const std::map::const_iterator it = data.find(name); return (it == data.end()) ? nullptr : &it->second; } }; CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok); /// @} //--------------------------------------------------------------------------- #endif // libraryH cppcheck-2.7/lib/matchcompiler.h000066400000000000000000000036371417746362400167370ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef matchcompilerH #define matchcompilerH #include namespace MatchCompiler { template class ConstString { public: typedef const char(&StringRef)[n]; explicit ConstString(StringRef s) : _s(s) {} operator StringRef() const { return _s; } private: StringRef _s; }; template inline bool equalN(const char s1[], const char s2[]) { return (*s1 == *s2) && equalN(s1+1, s2+1); } template<> inline bool equalN<0>(const char[], const char[]) { return true; } template inline bool operator==(const std::string & s1, ConstString const & s2) { return equalN(s1.c_str(), s2); } template inline bool operator!=(const std::string & s1, ConstString const & s2) { return !operator==(s1,s2); } template inline ConstString makeConstString(const char (&s)[n]) { return ConstString(s); } } #endif // matchcompilerH cppcheck-2.7/lib/mathlib.cpp000066400000000000000000001210771417746362400160620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mathlib.h" #include "errortypes.h" #include "utils.h" #include #include #include #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER <= 1700 // VS2012 doesn't have std::isinf and std::isnan #define ISINF(x) (!_finite(x)) #define ISNAN(x) (_isnan(x)) #elif defined(__INTEL_COMPILER) #define ISINF(x) (isinf(x)) #define ISNAN(x) (isnan(x)) #else // Use C++11 functions #define ISINF(x) (std::isinf(x)) #define ISNAN(x) (std::isnan(x)) #endif const int MathLib::bigint_bits = 64; MathLib::value::value(const std::string &s) : mIntValue(0), mDoubleValue(0), mIsUnsigned(false) { if (MathLib::isFloat(s)) { mType = MathLib::value::Type::FLOAT; mDoubleValue = MathLib::toDoubleNumber(s); return; } if (!MathLib::isInt(s)) throw InternalError(nullptr, "Invalid value: " + s); mType = MathLib::value::Type::INT; mIntValue = MathLib::toLongNumber(s); if (isIntHex(s) && mIntValue < 0) mIsUnsigned = true; // read suffix if (s.size() >= 2U) { for (std::size_t i = s.size() - 1U; i > 0U; --i) { const char c = s[i]; if (c == 'u' || c == 'U') mIsUnsigned = true; else if (c == 'l' || c == 'L') { if (mType == MathLib::value::Type::INT) mType = MathLib::value::Type::LONG; else if (mType == MathLib::value::Type::LONG) mType = MathLib::value::Type::LONGLONG; } else if (i > 2U && c == '4' && s[i-1] == '6' && s[i-2] == 'i') mType = MathLib::value::Type::LONGLONG; } } } std::string MathLib::value::str() const { std::ostringstream ostr; if (mType == MathLib::value::Type::FLOAT) { if (ISNAN(mDoubleValue)) return "nan.0"; if (ISINF(mDoubleValue)) return (mDoubleValue > 0) ? "inf.0" : "-inf.0"; ostr.precision(9); ostr << std::fixed << mDoubleValue; // remove trailing zeros std::string ret(ostr.str()); std::string::size_type pos = ret.size() - 1U; while (ret[pos] == '0') pos--; if (ret[pos] == '.') ++pos; return ret.substr(0, pos+1); } if (mIsUnsigned) ostr << static_cast(mIntValue) << "U"; else ostr << mIntValue; if (mType == MathLib::value::Type::LONG) ostr << "L"; else if (mType == MathLib::value::Type::LONGLONG) ostr << "LL"; return ostr.str(); } void MathLib::value::promote(const MathLib::value &v) { if (isInt() && v.isInt()) { if (mType < v.mType) { mType = v.mType; mIsUnsigned = v.mIsUnsigned; } else if (mType == v.mType) { mIsUnsigned |= v.mIsUnsigned; } } else if (!isFloat()) { mIsUnsigned = false; mDoubleValue = mIntValue; mType = MathLib::value::Type::FLOAT; } } MathLib::value MathLib::value::calc(char op, const MathLib::value &v1, const MathLib::value &v2) { value temp(v1); temp.promote(v2); if (temp.isFloat()) { switch (op) { case '+': temp.mDoubleValue += v2.getDoubleValue(); break; case '-': temp.mDoubleValue -= v2.getDoubleValue(); break; case '*': temp.mDoubleValue *= v2.getDoubleValue(); break; case '/': temp.mDoubleValue /= v2.getDoubleValue(); break; case '%': case '&': case '|': case '^': throw InternalError(nullptr, "Invalid calculation"); default: throw InternalError(nullptr, "Unhandled calculation"); } } else if (temp.mIsUnsigned) { switch (op) { case '+': temp.mIntValue += (unsigned long long)v2.mIntValue; break; case '-': temp.mIntValue -= (unsigned long long)v2.mIntValue; break; case '*': temp.mIntValue *= (unsigned long long)v2.mIntValue; break; case '/': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (v1.mIntValue == std::numeric_limits::min() && std::abs(v2.mIntValue)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); temp.mIntValue /= (unsigned long long)v2.mIntValue; break; case '%': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); temp.mIntValue %= (unsigned long long)v2.mIntValue; break; case '&': temp.mIntValue &= (unsigned long long)v2.mIntValue; break; case '|': temp.mIntValue |= (unsigned long long)v2.mIntValue; break; case '^': temp.mIntValue ^= (unsigned long long)v2.mIntValue; break; default: throw InternalError(nullptr, "Unhandled calculation"); } } else { switch (op) { case '+': temp.mIntValue += v2.mIntValue; break; case '-': temp.mIntValue -= v2.mIntValue; break; case '*': temp.mIntValue *= v2.mIntValue; break; case '/': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (v1.mIntValue == std::numeric_limits::min() && std::abs(v2.mIntValue)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); temp.mIntValue /= v2.mIntValue; break; case '%': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); temp.mIntValue %= v2.mIntValue; break; case '&': temp.mIntValue &= v2.mIntValue; break; case '|': temp.mIntValue |= v2.mIntValue; break; case '^': temp.mIntValue ^= v2.mIntValue; break; default: throw InternalError(nullptr, "Unhandled calculation"); } } return temp; } int MathLib::value::compare(const MathLib::value &v) const { value temp(*this); temp.promote(v); if (temp.isFloat()) { if (temp.mDoubleValue < v.getDoubleValue()) return -1; if (temp.mDoubleValue > v.getDoubleValue()) return 1; return 0; } if (temp.mIsUnsigned) { if ((unsigned long long)mIntValue < (unsigned long long)v.mIntValue) return -1; if ((unsigned long long)mIntValue > (unsigned long long)v.mIntValue) return 1; return 0; } if (mIntValue < v.mIntValue) return -1; if (mIntValue > v.mIntValue) return 1; return 0; } MathLib::value MathLib::value::add(int v) const { MathLib::value temp(*this); if (temp.isInt()) temp.mIntValue += v; else temp.mDoubleValue += v; return temp; } MathLib::value MathLib::value::shiftLeft(const MathLib::value &v) const { if (!isInt() || !v.isInt()) throw InternalError(nullptr, "Shift operand is not integer"); MathLib::value ret(*this); if (v.mIntValue >= MathLib::bigint_bits) { return ret; } ret.mIntValue <<= v.mIntValue; return ret; } MathLib::value MathLib::value::shiftRight(const MathLib::value &v) const { if (!isInt() || !v.isInt()) throw InternalError(nullptr, "Shift operand is not integer"); MathLib::value ret(*this); if (v.mIntValue >= MathLib::bigint_bits) { return ret; } ret.mIntValue >>= v.mIntValue; return ret; } MathLib::biguint MathLib::toULongNumber(const std::string & str) { // hexadecimal numbers: if (isIntHex(str)) { try { const biguint ret = std::stoull(str, nullptr, 16); return ret; } catch (const std::out_of_range& e) { throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str + " (" + e.what() +")"); } } // octal numbers: if (isOct(str)) { try { const biguint ret = std::stoull(str, nullptr, 8); return ret; } catch (const std::out_of_range& e) { throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str + " (" + e.what() +")"); } } // binary numbers: if (isBin(str)) { biguint ret = 0; for (std::string::size_type i = str[0] == '0'?2:3; i < str.length(); i++) { if (str[i] != '1' && str[i] != '0') break; ret <<= 1; if (str[i] == '1') ret |= 1; } /* if (str[0] == '-') ret = -ret; */ return ret; } if (isFloat(str)) { // Things are going to be less precise now: the value can't b represented in the biguint type. // Use min/max values as an approximation. See #5843 const double doubleval = std::atof(str.c_str()); if (doubleval > (double)std::numeric_limits::max()) return std::numeric_limits::max(); else return static_cast(doubleval); } try { const biguint ret = std::stoull(str, nullptr, 10); return ret; } catch (const std::out_of_range& e) { throw InternalError(nullptr, "Internal Error. MathLib::toULongNumber: out_of_range: " + str + " (" + e.what() +")"); } } unsigned int MathLib::encodeMultiChar(const std::string& str) { unsigned int retval = 0; for (char it : str) { retval = (retval << 8) | it; } return retval; } MathLib::bigint MathLib::toLongNumber(const std::string & str) { // hexadecimal numbers: if (isIntHex(str)) { try { const biguint ret = std::stoull(str, nullptr, 16); return (bigint)ret; } catch (const std::out_of_range& e) { throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str + " (" + e.what() +")"); } } // octal numbers: if (isOct(str)) { try { const biguint ret = std::stoull(str, nullptr, 8); return ret; } catch (const std::out_of_range& e) { throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str + " (" + e.what() +")"); } } // binary numbers: if (isBin(str)) { bigint ret = 0; for (std::string::size_type i = str[0] == '0'?2:3; i < str.length(); i++) { if (str[i] != '1' && str[i] != '0') break; ret <<= 1; if (str[i] == '1') ret |= 1; } if (str[0] == '-') ret = -ret; return ret; } if (isFloat(str)) { // Things are going to be less precise now: the value can't be represented in the bigint type. // Use min/max values as an approximation. See #5843 const double doubleval = toDoubleNumber(str); if (doubleval > (double)std::numeric_limits::max()) return std::numeric_limits::max(); else if (doubleval < (double)std::numeric_limits::min()) return std::numeric_limits::min(); else return static_cast(doubleval); } if (isCharLiteral(str)) return simplecpp::characterLiteralToLL(str); try { const biguint ret = std::stoull(str, nullptr, 10); return ret; } catch (const std::out_of_range& e) { throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: out_of_range: " + str + " (" + e.what() +")"); } } // in-place conversion of (sub)string to double. Requires no heap. static double myStod(const std::string& str, std::string::const_iterator from, std::string::const_iterator to, int base) { double result = 0.; bool positivesign = true; std::string::const_iterator it; if ('+' == *from) { it = from + 1; } else if ('-' == *from) { it = from + 1; positivesign = false; } else it = from; const std::size_t decimalsep = str.find('.', it-str.begin()); int distance; if (std::string::npos == decimalsep) { distance = to - it; } else if (decimalsep > (to - str.begin())) return 0.; // error handling?? else distance = int(decimalsep)-(from - str.begin()); auto digitval = [&](char c) { if ((10 < base) && (c > '9')) return 10 + std::tolower(c) - 'a'; else return c - '0'; }; for (; it!=to; ++it) { if ('.' == *it) continue; else --distance; result += digitval(*it)* std::pow(base, distance); } return (positivesign)?result:-result; } // Assuming a limited support of built-in hexadecimal floats (see C99, C++17) that is a fall-back implementation. // Performance has been optimized WRT to heap activity, however the calculation part is not optimized. static double floatHexToDoubleNumber(const std::string& str) { const std::size_t p = str.find_first_of("pP",3); const double factor1 = myStod(str, str.begin() + 2, str.begin()+p, 16); const bool suffix = (str.back() == 'f') || (str.back() == 'F') || (str.back() == 'l') || (str.back() == 'L'); const double exponent = myStod(str, str.begin() + p + 1, (suffix)?str.end()-1:str.end(), 10); const double factor2 = std::pow(2, exponent); return factor1 * factor2; } double MathLib::toDoubleNumber(const std::string &str) { if (isCharLiteral(str)) { try { return simplecpp::characterLiteralToLL(str); } catch (const std::exception& e) { throw InternalError(nullptr, "Internal Error. MathLib::toLongNumber: characterLiteralToLL(" + str + ") => " + e.what()); } } if (isIntHex(str)) return static_cast(toLongNumber(str)); // nullcheck if (isNullValue(str)) return 0.0; #ifdef _LIBCPP_VERSION if (isFloat(str)) // Workaround libc++ bug at http://llvm.org/bugs/show_bug.cgi?id=17782 // TODO : handle locale return std::strtod(str.c_str(), nullptr); #endif if (isFloatHex(str)) return floatHexToDoubleNumber(str); // otherwise, convert to double std::istringstream istr(str); istr.imbue(std::locale::classic()); double ret; istr >> ret; return ret; } template<> std::string MathLib::toString(double value) { std::ostringstream result; result.precision(12); result << value; if (result.str() == "-0") return "0.0"; if (result.str().find('.') == std::string::npos) return result.str() + ".0"; return result.str(); } bool MathLib::isFloat(const std::string &str) { return isDecimalFloat(str) || isFloatHex(str); } bool MathLib::isDecimalFloat(const std::string &str) { if (str.empty()) return false; enum class State { START, BASE_DIGITS1, LEADING_DECIMAL, TRAILING_DECIMAL, BASE_DIGITS2, E, MANTISSA_PLUSMINUS, MANTISSA_DIGITS, SUFFIX_F, SUFFIX_L } state = State::START; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case State::START: if (*it=='.') state = State::LEADING_DECIMAL; else if (std::isdigit(static_cast(*it))) state = State::BASE_DIGITS1; else return false; break; case State::LEADING_DECIMAL: if (std::isdigit(static_cast(*it))) state = State::BASE_DIGITS2; else return false; break; case State::BASE_DIGITS1: if (*it=='e' || *it=='E') state = State::E; else if (*it=='.') state = State::TRAILING_DECIMAL; else if (!std::isdigit(static_cast(*it))) return false; break; case State::TRAILING_DECIMAL: if (*it=='e' || *it=='E') state = State::E; else if (*it=='f' || *it=='F') state = State::SUFFIX_F; else if (*it=='l' || *it=='L') state = State::SUFFIX_L; else if (std::isdigit(static_cast(*it))) state = State::BASE_DIGITS2; else return false; break; case State::BASE_DIGITS2: if (*it=='e' || *it=='E') state = State::E; else if (*it=='f' || *it=='F') state = State::SUFFIX_F; else if (*it=='l' || *it=='L') state = State::SUFFIX_L; else if (!std::isdigit(static_cast(*it))) return false; break; case State::E: if (*it=='+' || *it=='-') state = State::MANTISSA_PLUSMINUS; else if (std::isdigit(static_cast(*it))) state = State::MANTISSA_DIGITS; else return false; break; case State::MANTISSA_PLUSMINUS: if (!std::isdigit(static_cast(*it))) return false; else state = State::MANTISSA_DIGITS; break; case State::MANTISSA_DIGITS: if (*it=='f' || *it=='F') state = State::SUFFIX_F; else if (*it=='l' || *it=='L') state = State::SUFFIX_L; else if (!std::isdigit(static_cast(*it))) return false; break; case State::SUFFIX_F: return false; case State::SUFFIX_L: return false; } } return (state==State::BASE_DIGITS2 || state==State::MANTISSA_DIGITS || state==State::TRAILING_DECIMAL || state==State::SUFFIX_F || state==State::SUFFIX_L); } bool MathLib::isNegative(const std::string &str) { if (str.empty()) return false; return (str[0] == '-'); } bool MathLib::isPositive(const std::string &str) { if (str.empty()) return false; return !MathLib::isNegative(str); } static bool isValidIntegerSuffixIt(std::string::const_iterator it, std::string::const_iterator end, bool supportMicrosoftExtensions=true) { enum class Status { START, SUFFIX_U, SUFFIX_UL, SUFFIX_ULL, SUFFIX_L, SUFFIX_LU, SUFFIX_LL, SUFFIX_LLU, SUFFIX_I, SUFFIX_I6, SUFFIX_I64, SUFFIX_UI, SUFFIX_UI6, SUFFIX_UI64 } state = Status::START; for (; it != end; ++it) { switch (state) { case Status::START: if (*it == 'u' || *it == 'U') state = Status::SUFFIX_U; else if (*it == 'l' || *it == 'L') state = Status::SUFFIX_L; else if (supportMicrosoftExtensions && (*it == 'i' || *it == 'I')) state = Status::SUFFIX_I; else return false; break; case Status::SUFFIX_U: if (*it == 'l' || *it == 'L') state = Status::SUFFIX_UL; // UL else if (supportMicrosoftExtensions && (*it == 'i' || *it == 'I')) state = Status::SUFFIX_UI; else return false; break; case Status::SUFFIX_UL: if (*it == 'l' || *it == 'L') state = Status::SUFFIX_ULL; // ULL else return false; break; case Status::SUFFIX_L: if (*it == 'u' || *it == 'U') state = Status::SUFFIX_LU; // LU else if (*it == 'l' || *it == 'L') state = Status::SUFFIX_LL; // LL else return false; break; case Status::SUFFIX_LU: return false; case Status::SUFFIX_LL: if (*it == 'u' || *it == 'U') state = Status::SUFFIX_LLU; // LLU else return false; break; case Status::SUFFIX_I: if (*it == '6') state = Status::SUFFIX_I6; else return false; break; case Status::SUFFIX_I6: if (*it == '4') state = Status::SUFFIX_I64; else return false; break; case Status::SUFFIX_UI: if (*it == '6') state = Status::SUFFIX_UI6; else return false; break; case Status::SUFFIX_UI6: if (*it == '4') state = Status::SUFFIX_UI64; else return false; break; default: return false; } } return ((state == Status::SUFFIX_U) || (state == Status::SUFFIX_L) || (state == Status::SUFFIX_UL) || (state == Status::SUFFIX_LU) || (state == Status::SUFFIX_LL) || (state == Status::SUFFIX_ULL) || (state == Status::SUFFIX_LLU) || (state == Status::SUFFIX_I64) || (state == Status::SUFFIX_UI64)); } // cppcheck-suppress unusedFunction bool MathLib::isValidIntegerSuffix(const std::string& str, bool supportMicrosoftExtensions) { return isValidIntegerSuffixIt(str.begin(), str.end(), supportMicrosoftExtensions); } /*! \brief Does the string represent an octal number? * In case leading or trailing white space is provided, the function * returns false. * Additional information can be found here: * http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html * * \param str The string to check. In case the string is empty, the function returns false. * \return Return true in case a octal number is provided and false otherwise. **/ bool MathLib::isOct(const std::string& str) { enum class Status { START, OCTAL_PREFIX, DIGITS } state = Status::START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case Status::START: if (*it == '0') state = Status::OCTAL_PREFIX; else return false; break; case Status::OCTAL_PREFIX: if (isOctalDigit(static_cast(*it))) state = Status::DIGITS; else return false; break; case Status::DIGITS: if (isOctalDigit(static_cast(*it))) state = Status::DIGITS; else return isValidIntegerSuffixIt(it,str.end()); break; } } return state == Status::DIGITS; } bool MathLib::isIntHex(const std::string& str) { enum class Status { START, HEX_0, HEX_X, DIGIT } state = Status::START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case Status::START: if (*it == '0') state = Status::HEX_0; else return false; break; case Status::HEX_0: if (*it == 'x' || *it == 'X') state = Status::HEX_X; else return false; break; case Status::HEX_X: if (isxdigit(static_cast(*it))) state = Status::DIGIT; else return false; break; case Status::DIGIT: if (isxdigit(static_cast(*it))) ; // state = Status::DIGIT; else return isValidIntegerSuffixIt(it,str.end()); break; } } return Status::DIGIT == state; } bool MathLib::isFloatHex(const std::string& str) { enum class Status { START, HEX_0, HEX_X, WHOLE_NUMBER_DIGIT, POINT, FRACTION, EXPONENT_P, EXPONENT_SIGN, EXPONENT_DIGITS, EXPONENT_SUFFIX } state = Status::START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case Status::START: if (*it == '0') state = Status::HEX_0; else return false; break; case Status::HEX_0: if (*it == 'x' || *it == 'X') state = Status::HEX_X; else return false; break; case Status::HEX_X: if (isxdigit(static_cast(*it))) state = Status::WHOLE_NUMBER_DIGIT; else if (*it == '.') state = Status::POINT; else return false; break; case Status::WHOLE_NUMBER_DIGIT: if (isxdigit(static_cast(*it))) ; // state = Status::WHOLE_NUMBER_DIGITS; else if (*it=='.') state = Status::FRACTION; else if (*it=='p' || *it=='P') state = Status::EXPONENT_P; else return false; break; case Status::POINT: case Status::FRACTION: if (isxdigit(static_cast(*it))) state = Status::FRACTION; else if (*it == 'p' || *it == 'P') state = Status::EXPONENT_P; else return false; break; case Status::EXPONENT_P: if (isdigit(static_cast(*it))) state = Status::EXPONENT_DIGITS; else if (*it == '+' || *it == '-') state = Status::EXPONENT_SIGN; else return false; break; case Status::EXPONENT_SIGN: if (isdigit(static_cast(*it))) state = Status::EXPONENT_DIGITS; else return false; break; case Status::EXPONENT_DIGITS: if (isdigit(static_cast(*it))) ; // state = Status::EXPONENT_DIGITS; else if (*it == 'f' || *it == 'F' || *it == 'l' || *it == 'L') state = Status::EXPONENT_SUFFIX; else return false; break; case Status::EXPONENT_SUFFIX: return false; } } return (Status::EXPONENT_DIGITS == state) || (Status::EXPONENT_SUFFIX == state); } /*! \brief Does the string represent a binary number? * In case leading or trailing white space is provided, the function * returns false. * Additional information can be found here: * http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html * * \param str The string to check. In case the string is empty, the function returns false. * \return Return true in case a binary number is provided and false otherwise. **/ bool MathLib::isBin(const std::string& str) { enum class Status { START, GNU_BIN_PREFIX_0, GNU_BIN_PREFIX_B, DIGIT } state = Status::START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case Status::START: if (*it == '0') state = Status::GNU_BIN_PREFIX_0; else return false; break; case Status::GNU_BIN_PREFIX_0: if (*it == 'b' || *it == 'B') state = Status::GNU_BIN_PREFIX_B; else return false; break; case Status::GNU_BIN_PREFIX_B: if (*it == '0' || *it == '1') state = Status::DIGIT; else return false; break; case Status::DIGIT: if (*it == '0' || *it == '1') ; // state = Status::DIGIT; else return isValidIntegerSuffixIt(it,str.end()); break; } } return state == Status::DIGIT; } bool MathLib::isDec(const std::string & str) { enum class Status { START, DIGIT } state = Status::START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case Status::START: if (isdigit(static_cast(*it))) state = Status::DIGIT; else return false; break; case Status::DIGIT: if (isdigit(static_cast(*it))) state = Status::DIGIT; else return isValidIntegerSuffixIt(it,str.end()); break; } } return state == Status::DIGIT; } bool MathLib::isInt(const std::string & str) { return isDec(str) || isIntHex(str) || isOct(str) || isBin(str); } std::string MathLib::getSuffix(const std::string& value) { if (value.size() > 3 && value[value.size() - 3] == 'i' && value[value.size() - 2] == '6' && value[value.size() - 1] == '4') { if (value[value.size() - 4] == 'u') return "ULL"; return "LL"; } bool isUnsigned = false; unsigned int longState = 0; for (std::size_t i = 1U; i < value.size(); ++i) { const char c = value[value.size() - i]; if (c == 'u' || c == 'U') isUnsigned = true; else if (c == 'L' || c == 'l') longState++; else break; } if (longState == 0) return isUnsigned ? "U" : ""; if (longState == 1) return isUnsigned ? "UL" : "L"; if (longState == 2) return isUnsigned ? "ULL" : "LL"; else return ""; } static std::string intsuffix(const std::string & first, const std::string & second) { const std::string suffix1 = MathLib::getSuffix(first); const std::string suffix2 = MathLib::getSuffix(second); if (suffix1 == "ULL" || suffix2 == "ULL") return "ULL"; if (suffix1 == "LL" || suffix2 == "LL") return "LL"; if (suffix1 == "UL" || suffix2 == "UL") return "UL"; if (suffix1 == "L" || suffix2 == "L") return "L"; if (suffix1 == "U" || suffix2 == "U") return "U"; return suffix1.empty() ? suffix2 : suffix1; } std::string MathLib::add(const std::string & first, const std::string & second) { #ifdef TEST_MATHLIB_VALUE return (value(first) + value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) + toLongNumber(second)) + intsuffix(first, second); } double d1 = toDoubleNumber(first); double d2 = toDoubleNumber(second); int count = 0; while (d1 > 100000.0 * d2 && toString(d1+d2)==first && ++count<5) d2 *= 10.0; while (d2 > 100000.0 * d1 && toString(d1+d2)==second && ++count<5) d1 *= 10.0; return toString(d1 + d2); #endif } std::string MathLib::subtract(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) - value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) - toLongNumber(second)) + intsuffix(first, second); } if (first == second) return "0.0"; double d1 = toDoubleNumber(first); double d2 = toDoubleNumber(second); int count = 0; while (d1 > 100000.0 * d2 && toString(d1-d2)==first && ++count<5) d2 *= 10.0; while (d2 > 100000.0 * d1 && toString(d1-d2)==second && ++count<5) d1 *= 10.0; return toString(d1 - d2); #endif } std::string MathLib::incdec(const std::string & var, const std::string & op) { #ifdef TEST_MATHLIB_VALUE if (op == "++") return value(var).add(1).str(); else if (op == "--") return value(var).add(-1).str(); #else if (op == "++") return MathLib::add(var, "1"); else if (op == "--") return MathLib::subtract(var, "1"); #endif throw InternalError(nullptr, std::string("Unexpected operation '") + op + "' in MathLib::incdec(). Please report this to Cppcheck developers."); } std::string MathLib::divide(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) / value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { const bigint a = toLongNumber(first); const bigint b = toLongNumber(second); if (b == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (a == std::numeric_limits::min() && std::abs(b)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); return toString(toLongNumber(first) / b) + intsuffix(first, second); } else if (isNullValue(second)) { if (isNullValue(first)) return "nan.0"; return isPositive(first) ? "inf.0" : "-inf.0"; } return toString(toDoubleNumber(first) / toDoubleNumber(second)); #endif } std::string MathLib::multiply(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) * value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) * toLongNumber(second)) + intsuffix(first, second); } return toString(toDoubleNumber(first) * toDoubleNumber(second)); #endif } std::string MathLib::mod(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) % value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { const bigint b = toLongNumber(second); if (b == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); return toString(toLongNumber(first) % b) + intsuffix(first, second); } return toString(std::fmod(toDoubleNumber(first),toDoubleNumber(second))); #endif } std::string MathLib::calculate(const std::string &first, const std::string &second, char action) { switch (action) { case '+': return MathLib::add(first, second); case '-': return MathLib::subtract(first, second); case '*': return MathLib::multiply(first, second); case '/': return MathLib::divide(first, second); case '%': return MathLib::mod(first, second); case '&': return MathLib::toString(MathLib::toLongNumber(first) & MathLib::toLongNumber(second)) + intsuffix(first,second); case '|': return MathLib::toString(MathLib::toLongNumber(first) | MathLib::toLongNumber(second)) + intsuffix(first,second); case '^': return MathLib::toString(MathLib::toLongNumber(first) ^ MathLib::toLongNumber(second)) + intsuffix(first,second); default: throw InternalError(nullptr, std::string("Unexpected action '") + action + "' in MathLib::calculate(). Please report this to Cppcheck developers."); } } std::string MathLib::sin(const std::string &tok) { return toString(std::sin(toDoubleNumber(tok))); } std::string MathLib::cos(const std::string &tok) { return toString(std::cos(toDoubleNumber(tok))); } std::string MathLib::tan(const std::string &tok) { return toString(std::tan(toDoubleNumber(tok))); } std::string MathLib::abs(const std::string &tok) { if (isNegative(tok)) return tok.substr(1, tok.length() - 1); return tok; } bool MathLib::isEqual(const std::string &first, const std::string &second) { // this conversion is needed for formatting // e.g. if first=0.1 and second=1.0E-1, the direct comparison of the strings would fail return toString(toDoubleNumber(first)) == toString(toDoubleNumber(second)); } bool MathLib::isNotEqual(const std::string &first, const std::string &second) { return !isEqual(first, second); } // cppcheck-suppress unusedFunction bool MathLib::isGreater(const std::string &first, const std::string &second) { return toDoubleNumber(first) > toDoubleNumber(second); } // cppcheck-suppress unusedFunction bool MathLib::isGreaterEqual(const std::string &first, const std::string &second) { return toDoubleNumber(first) >= toDoubleNumber(second); } // cppcheck-suppress unusedFunction bool MathLib::isLess(const std::string &first, const std::string &second) { return toDoubleNumber(first) < toDoubleNumber(second); } bool MathLib::isLessEqual(const std::string &first, const std::string &second) { return toDoubleNumber(first) <= toDoubleNumber(second); } /*! \brief Does the string represent the numerical value of 0? * In case leading or trailing white space is provided, the function * returns false. * Requirement for this function: * - This code is allowed to be slow because of simplicity of the code. * * \param[in] str The string to check. In case the string is empty, the function returns false. * \return Return true in case the string represents a numerical null value. **/ bool MathLib::isNullValue(const std::string &str) { if (str.empty() || (!std::isdigit(static_cast(str[0])) && (str[0] != '.' && str[0] != '-' && str[0] != '+'))) return false; // Has to be a number if (!isInt(str) && !isFloat(str)) return false; bool isHex = isIntHex(str) || isFloatHex(str); for (char i : str) { if (std::isdigit(static_cast(i)) && i != '0') // May not contain digits other than 0 return false; if (i == 'p' || i == 'P' || (!isHex && (i == 'E' || i == 'e'))) return true; if (isHex && isxdigit(i) && i != '0') return false; } return true; } bool MathLib::isOctalDigit(char c) { return (c >= '0' && c <= '7'); } bool MathLib::isDigitSeparator(const std::string& iCode, std::string::size_type iPos) { if (iPos == 0 || iPos >= iCode.size() || iCode[iPos] != '\'') return false; std::string::size_type i = iPos - 1; while (std::isxdigit(iCode[i])) { if (i == 0) return true; // Only xdigits before ' --i; } if (i == iPos - 1) { // No xdigit before ' return false; } else { switch (iCode[i]) { case ' ': case '.': case ',': case 'x': case '(': case '{': case '+': case '-': case '*': case '%': case '/': case '&': case '|': case '^': case '~': case '=': return true; case '\'': return isDigitSeparator(iCode, i); default: return false; } } } MathLib::value operator+(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('+',v1,v2); } MathLib::value operator-(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('-',v1,v2); } MathLib::value operator*(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('*',v1,v2); } MathLib::value operator/(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('/',v1,v2); } MathLib::value operator%(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('%',v1,v2); } MathLib::value operator&(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('&',v1,v2); } MathLib::value operator|(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('|',v1,v2); } MathLib::value operator^(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('^',v1,v2); } MathLib::value operator<<(const MathLib::value &v1, const MathLib::value &v2) { return v1.shiftLeft(v2); } MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2) { return v1.shiftRight(v2); } cppcheck-2.7/lib/mathlib.h000066400000000000000000000143011417746362400155160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef mathlibH #define mathlibH //--------------------------------------------------------------------------- #include "config.h" #include #include /// @addtogroup Core /// @{ /** @brief simple math functions that uses operands stored in std::string. useful when performing math on tokens. */ class CPPCHECKLIB MathLib { friend class TestMathLib; public: /** @brief value class */ class value { private: long long mIntValue; double mDoubleValue; enum class Type { INT, LONG, LONGLONG, FLOAT } mType; bool mIsUnsigned; void promote(const value &v); public: explicit value(const std::string &s); std::string str() const; bool isInt() const { return mType != Type::FLOAT; } bool isFloat() const { return mType == Type::FLOAT; } double getDoubleValue() const { return isFloat() ? mDoubleValue : (double)mIntValue; } static value calc(char op, const value &v1, const value &v2); int compare(const value &v) const; value add(int v) const; value shiftLeft(const value &v) const; value shiftRight(const value &v) const; }; typedef long long bigint; typedef unsigned long long biguint; static const int bigint_bits; static bigint toLongNumber(const std::string & str); static biguint toULongNumber(const std::string & str); template static std::string toString(T value) { std::ostringstream result; result << value; return result.str(); } static double toDoubleNumber(const std::string & str); static bool isInt(const std::string & str); static bool isFloat(const std::string &str); static bool isDecimalFloat(const std::string &str); static bool isNegative(const std::string &str); static bool isPositive(const std::string &str); static bool isDec(const std::string & str); static bool isFloatHex(const std::string& str); static bool isIntHex(const std::string& str); static bool isOct(const std::string& str); static bool isBin(const std::string& str); static std::string getSuffix(const std::string& value); /** * Only used in unit tests * * \param[in] str string * \param[in] supportMicrosoftExtensions support Microsoft extension: i64 * \return true if str is a non-empty valid integer suffix */ static bool isValidIntegerSuffix(const std::string& str, bool supportMicrosoftExtensions=true); static std::string add(const std::string & first, const std::string & second); static std::string subtract(const std::string & first, const std::string & second); static std::string multiply(const std::string & first, const std::string & second); static std::string divide(const std::string & first, const std::string & second); static std::string mod(const std::string & first, const std::string & second); static std::string incdec(const std::string & var, const std::string & op); static std::string calculate(const std::string & first, const std::string & second, char action); static std::string sin(const std::string & tok); static std::string cos(const std::string & tok); static std::string tan(const std::string & tok); static std::string abs(const std::string & tok); static bool isEqual(const std::string & first, const std::string & second); static bool isNotEqual(const std::string & first, const std::string & second); static bool isGreater(const std::string & first, const std::string & second); static bool isGreaterEqual(const std::string & first, const std::string & second); static bool isLess(const std::string & first, const std::string & second); static bool isLessEqual(const std::string & first, const std::string & second); static bool isNullValue(const std::string & str); /** * Return true if given character is 0,1,2,3,4,5,6 or 7. * @param[in] c The character to check * @return true if given character is octal digit. */ static bool isOctalDigit(char c); static unsigned int encodeMultiChar(const std::string& str); /** * \param[in] iCode Code being considered * \param[in] iPos A posision within iCode * \return Whether iCode[iPos] is a C++14 digit separator */ static bool isDigitSeparator(const std::string& iCode, std::string::size_type iPos); }; MathLib::value operator+(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator-(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator*(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator/(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator%(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator&(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator|(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator^(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator<<(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2); template<> CPPCHECKLIB std::string MathLib::toString(double value); // Declare specialization to avoid linker problems /// @} //--------------------------------------------------------------------------- #endif // mathlibH cppcheck-2.7/lib/path.cpp000066400000000000000000000155771417746362400154050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #if defined(__GNUC__) && (defined(_WIN32) || defined(__CYGWIN__)) #undef __STRICT_ANSI__ #endif #include "path.h" #include "utils.h" #include #include #include #include #ifndef _WIN32 #include #else #include #endif #if defined(__CYGWIN__) #include #endif #include /** Is the filesystem case insensitive? */ static bool caseInsensitiveFilesystem() { #ifdef _WIN32 return true; #else // TODO: Non-windows filesystems might be case insensitive return false; #endif } std::string Path::toNativeSeparators(std::string path) { #if defined(_WIN32) const char separ = '/'; const char native = '\\'; #else const char separ = '\\'; const char native = '/'; #endif std::replace(path.begin(), path.end(), separ, native); return path; } std::string Path::fromNativeSeparators(std::string path) { const char nonnative = '\\'; const char newsepar = '/'; std::replace(path.begin(), path.end(), nonnative, newsepar); return path; } std::string Path::simplifyPath(std::string originalPath) { return simplecpp::simplifyPath(originalPath); } std::string Path::getPathFromFilename(const std::string &filename) { const std::size_t pos = filename.find_last_of("\\/"); if (pos != std::string::npos) return filename.substr(0, 1 + pos); return ""; } bool Path::sameFileName(const std::string &fname1, const std::string &fname2) { return caseInsensitiveFilesystem() ? (caseInsensitiveStringCompare(fname1, fname2) == 0) : (fname1 == fname2); } // This wrapper exists because Sun's CC does not allow a static_cast // from extern "C" int(*)(int) to int(*)(int). static int tolowerWrapper(int c) { return std::tolower(c); } std::string Path::removeQuotationMarks(std::string path) { path.erase(std::remove(path.begin(), path.end(), '\"'), path.end()); return path; } std::string Path::getFilenameExtension(const std::string &path) { const std::string::size_type dotLocation = path.find_last_of('.'); if (dotLocation == std::string::npos) return ""; std::string extension = path.substr(dotLocation); if (caseInsensitiveFilesystem()) { // on a case insensitive filesystem the case doesn't matter so // let's return the extension in lowercase std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper); } return extension; } std::string Path::getFilenameExtensionInLowerCase(const std::string &path) { std::string extension = getFilenameExtension(path); std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper); return extension; } std::string Path::getCurrentPath() { char currentPath[4096]; #ifndef _WIN32 if (getcwd(currentPath, 4096) != nullptr) #else if (_getcwd(currentPath, 4096) != nullptr) #endif return std::string(currentPath); return emptyString; } bool Path::isAbsolute(const std::string& path) { const std::string& nativePath = toNativeSeparators(path); #ifdef _WIN32 if (path.length() < 2) return false; // On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not if (nativePath.compare(0, 2, "\\\\") == 0 || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0)) return true; #else if (!nativePath.empty() && nativePath[0] == '/') return true; #endif return false; } std::string Path::getRelativePath(const std::string& absolutePath, const std::vector& basePaths) { for (const std::string &bp : basePaths) { if (absolutePath == bp || bp.empty()) // Seems to be a file, or path is empty continue; if (absolutePath.compare(0, bp.length(), bp) != 0) continue; if (endsWith(bp,'/')) return absolutePath.substr(bp.length()); else if (absolutePath.size() > bp.size() && absolutePath[bp.length()] == '/') return absolutePath.substr(bp.length() + 1); } return absolutePath; } bool Path::isC(const std::string &path) { // In unix, ".C" is considered C++ file const std::string extension = getFilenameExtension(path); return extension == ".c" || extension == ".cl"; } bool Path::isCPP(const std::string &path) { const std::string extension = getFilenameExtensionInLowerCase(path); return extension == ".cpp" || extension == ".cxx" || extension == ".cc" || extension == ".c++" || extension == ".hpp" || extension == ".hxx" || extension == ".hh" || extension == ".tpp" || extension == ".txx" || extension == ".ipp" || extension == ".ixx" || getFilenameExtension(path) == ".C"; // In unix, ".C" is considered C++ file } bool Path::acceptFile(const std::string &path, const std::set &extra) { return !Path::isHeader(path) && (Path::isCPP(path) || Path::isC(path) || extra.find(getFilenameExtension(path)) != extra.end()); } bool Path::isHeader(const std::string &path) { const std::string extension = getFilenameExtensionInLowerCase(path); return (extension.compare(0, 2, ".h") == 0); } std::string Path::getAbsoluteFilePath(const std::string& filePath) { std::string absolute_path; #ifdef _WIN32 char absolute[_MAX_PATH]; if (_fullpath(absolute, filePath.c_str(), _MAX_PATH)) absolute_path = absolute; #elif defined(__linux__) || defined(__sun) || defined(__hpux) || defined(__GNUC__) || defined(__CPPCHECK__) char * absolute = realpath(filePath.c_str(), nullptr); if (absolute) absolute_path = absolute; free(absolute); #else #error Platform absolute path function needed #endif return absolute_path; } std::string Path::stripDirectoryPart(const std::string &file) { #if defined(_WIN32) && !defined(__MINGW32__) const char native = '\\'; #else const char native = '/'; #endif const std::string::size_type p = file.rfind(native); if (p != std::string::npos) { return file.substr(p + 1); } return file; } bool Path::fileExists(const std::string &file) { std::ifstream f(file.c_str()); return f.is_open(); } cppcheck-2.7/lib/path.h000066400000000000000000000151131417746362400150340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef pathH #define pathH //--------------------------------------------------------------------------- #include "config.h" #include #include #include /// @addtogroup Core /// @{ /** * @brief Path handling routines. * Internally cppcheck wants to store paths with / separator which is also * native separator for Unix-derived systems. When giving path to user * or for other functions we convert path separators back to native type. */ class CPPCHECKLIB Path { public: /** * Convert path to use native separators. * @param path Path string to convert. * @return converted path. */ static std::string toNativeSeparators(std::string path); /** * Convert path to use internal path separators. * @param path Path string to convert. * @return converted path. */ static std::string fromNativeSeparators(std::string path); /** * @brief Simplify path "foo/bar/.." => "foo" * @param originalPath path to be simplified, must have / -separators. * @return simplified path */ static std::string simplifyPath(std::string originalPath); /** * @brief Lookup the path part from a filename (e.g., '/tmp/a.h' -> '/tmp/', 'a.h' -> '') * @param filename filename to lookup, must have / -separators. * @return path part of the filename */ static std::string getPathFromFilename(const std::string &filename); /** * @brief Compare filenames to see if they are the same. * On Linux the comparison is case-sensitive. On Windows it is case-insensitive. * @param fname1 one filename * @param fname2 other filename * @return true if the filenames match on the current platform */ static bool sameFileName(const std::string &fname1, const std::string &fname2); /** * @brief Remove quotation marks (") from the path. * @param path path to be cleaned. * @return Cleaned path without quotation marks. */ static std::string removeQuotationMarks(std::string path); /** * @brief Get an extension of the filename. * @param path Path containing filename. * @return Filename extension (containing the dot, e.g. ".h" or ".CPP"). */ static std::string getFilenameExtension(const std::string &path); /** * @brief Get an extension of the filename in lower case. * @param path Path containing filename. * @return Filename extension (containing the dot, e.g. ".h"). */ static std::string getFilenameExtensionInLowerCase(const std::string &path); /** * @brief Returns the absolute path of current working directory * @return absolute path of current working directory */ static std::string getCurrentPath(); /** * @brief Check if given path is absolute * @param path Path to check * @return true if given path is absolute */ static bool isAbsolute(const std::string& path); /** * @brief Create a relative path from an absolute one, if absolute path is inside the basePaths. * @param absolutePath Path to be made relative. * @param basePaths Paths to which it may be made relative. * @return relative path, if possible. Otherwise absolutePath is returned unchanged */ static std::string getRelativePath(const std::string& absolutePath, const std::vector& basePaths); /** * @brief Get an absolute file path from a relative one. * @param filePath File path to be made absolute. * @return absolute path, if possible. Otherwise an empty path is returned */ static std::string getAbsoluteFilePath(const std::string& filePath); /** * @brief Check if the file extension indicates that it's a C/C++ source file. * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx * @param filename filename to check. path info is optional * @return true if the file extension indicates it should be checked */ static bool acceptFile(const std::string &filename) { const std::set extra; return acceptFile(filename, extra); } /** * @brief Check if the file extension indicates that it's a C/C++ source file. * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx * @param path filename to check. path info is optional * @param extra extra file extensions * @return true if the file extension indicates it should be checked */ static bool acceptFile(const std::string &path, const std::set &extra); /** * @brief Identify language based on file extension. * @param path filename to check. path info is optional * @return true if extension is meant for C files */ static bool isC(const std::string &path); /** * @brief Identify language based on file extension. * @param path filename to check. path info is optional * @return true if extension is meant for C++ files */ static bool isCPP(const std::string &path); /** * @brief Is filename a header based on file extension * @param path filename to check. path info is optional * @return true if filename extension is meant for headers */ static bool isHeader(const std::string &path); /** * @brief Get filename without a directory path part. * @param file filename to be stripped. path info is optional * @return filename without directory path part. */ static std::string stripDirectoryPart(const std::string &file); /** * @brief Checks if a File exists * @param file Path to be checked if it is a File * @return true if given path is a File */ static bool fileExists(const std::string &file); }; /// @} //--------------------------------------------------------------------------- #endif // pathH cppcheck-2.7/lib/pathanalysis.cpp000066400000000000000000000172431417746362400171410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "pathanalysis.h" #include "astutils.h" #include "symboldatabase.h" #include "token.h" #include "valueflow.h" #include #include #include #include #include const Scope* PathAnalysis::findOuterScope(const Scope * scope) { if (!scope) return nullptr; if (scope->isLocal() && scope->type != Scope::eSwitch) return findOuterScope(scope->nestedIn); return scope; } static const Token* assignExpr(const Token* tok) { while (tok->astParent() && astIsLHS(tok)) { if (Token::Match(tok->astParent(), "%assign%")) return tok->astParent(); tok = tok->astParent(); } return nullptr; } std::pair PathAnalysis::checkCond(const Token * tok, bool& known) { if (tok->hasKnownIntValue()) { known = true; return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue); } auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) { return v.isIntValue(); }); // If all possible values are the same, then assume all paths have the same value if (it != tok->values().end() && std::all_of(it, tok->values().end(), [&](const ValueFlow::Value& v) { if (v.isIntValue()) return v.intvalue == it->intvalue; return true; })) { known = false; return std::make_pair(it->intvalue, !it->intvalue); } return std::make_pair(true, true); } PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function& f) const { if (!tok) return Progress::Continue; if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break) return Progress::Break; info.tok = tok; if (f(info) == Progress::Break) return Progress::Break; if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), info, f) == Progress::Break) return Progress::Break; return Progress::Continue; } PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const { for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { if (Token::Match(tok, "asm|goto|break|continue")) return Progress::Break; else if (Token::Match(tok, "return|throw")) { forwardRecursive(tok, info, f); return Progress::Break; // Evaluate RHS of assignment before LHS } else if (const Token* assignTok = assignExpr(tok)) { if (forwardRecursive(assignTok->astOperand2(), info, f) == Progress::Break) return Progress::Break; if (forwardRecursive(assignTok->astOperand1(), info, f) == Progress::Break) return Progress::Break; tok = nextAfterAstRightmostLeaf(assignTok); if (!tok) return Progress::Break; } else if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) { const Token * blockStart = tok->link()->linkAt(-1)->previous(); const Token * condTok = getCondTok(blockStart); if (!condTok) continue; info.errorPath.emplace_back(condTok, "Assuming condition is true."); // Traverse a loop a second time if (Token::Match(blockStart, "for|while (")) { const Token* endCond = blockStart->linkAt(1); bool traverseLoop = true; // Only traverse simple for loops if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {")) traverseLoop = false; // Traverse loop a second time if (traverseLoop) { // Traverse condition if (forwardRecursive(condTok, info, f) == Progress::Break) return Progress::Break; // TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)? } } if (Token::simpleMatch(tok, "} else {")) { tok = tok->linkAt(2); } } else if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) { const Token * endCond = tok->next()->link(); const Token * endBlock = endCond->next()->link(); const Token * condTok = getCondTok(tok); if (!condTok) continue; // Traverse condition if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break) return Progress::Break; Info i = info; i.known = false; i.errorPath.emplace_back(condTok, "Assuming condition is true."); // Check if condition is true or false bool checkThen = false; bool checkElse = false; std::tie(checkThen, checkElse) = checkCond(condTok, i.known); // Traverse then block if (checkThen) { if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break) return Progress::Break; } // Traverse else block if (Token::simpleMatch(endBlock, "} else {")) { if (checkElse) { i.errorPath.back().second = "Assuming condition is false."; Progress result = forwardRange(endCond->next(), endBlock, i, f); if (result == Progress::Break) return Progress::Break; } tok = endBlock->linkAt(2); } else { tok = endBlock; } } else if (Token::simpleMatch(tok, "} else {")) { tok = tok->linkAt(2); } else { info.tok = tok; if (f(info) == Progress::Break) return Progress::Break; } // Prevent infinite recursion if (tok->next() == start) break; } return Progress::Continue; } void PathAnalysis::forward(const std::function& f) const { const Scope * endScope = findOuterScope(start->scope()); if (!endScope) return; const Token * endToken = endScope->bodyEnd; Info info{start, ErrorPath{}, true}; forwardRange(start, endToken, info, f); } bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath) { PathAnalysis::Info info = PathAnalysis{start, library}.forwardFind([&](const PathAnalysis::Info& i) { return (i.tok == dest); }); if (!info.tok) return false; if (errorPath) errorPath->insert(errorPath->end(), info.errorPath.begin(), info.errorPath.end()); return true; } cppcheck-2.7/lib/pathanalysis.h000066400000000000000000000045711417746362400166060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GUARD_PATHANALYSIS_H #define GUARD_PATHANALYSIS_H #include "errortypes.h" #include #include #include class Library; class Scope; class Token; struct PathAnalysis { enum class Progress { Continue, Break }; PathAnalysis(const Token* start, const Library& library) : start(start), library(&library) {} const Token * start; const Library * library; struct Info { const Token* tok; ErrorPath errorPath; bool known; }; void forward(const std::function& f) const; Info forwardFind(std::function pred) { Info result{}; forward([&](const Info& info) { if (pred(info)) { result = info; return Progress::Break; } return Progress::Continue; }); return result; } private: Progress forwardRecursive(const Token* tok, Info info, const std::function& f) const; Progress forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const; static const Scope* findOuterScope(const Scope * scope); static std::pair checkCond(const Token * tok, bool& known); }; /** * @brief Returns true if there is a path between the two tokens * * @param start Starting point of the path * @param dest The path destination * @param errorPath Adds the path traversal to the errorPath */ bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath); #endif cppcheck-2.7/lib/pathmatch.cpp000066400000000000000000000062411417746362400164060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "pathmatch.h" #include "path.h" #include "utils.h" #include #include #include PathMatch::PathMatch(const std::vector &excludedPaths, bool caseSensitive) : mExcludedPaths(excludedPaths), mCaseSensitive(caseSensitive) { if (!mCaseSensitive) for (std::string& excludedPath : mExcludedPaths) std::transform(excludedPath.begin(), excludedPath.end(), excludedPath.begin(), ::tolower); mWorkingDirectory.push_back(Path::getCurrentPath()); } bool PathMatch::match(const std::string &path) const { if (path.empty()) return false; for (std::vector::const_iterator i = mExcludedPaths.begin(); i != mExcludedPaths.end(); ++i) { const std::string excludedPath((!Path::isAbsolute(path) && Path::isAbsolute(*i)) ? Path::getRelativePath(*i, mWorkingDirectory) : *i); std::string findpath = Path::fromNativeSeparators(path); if (!mCaseSensitive) std::transform(findpath.begin(), findpath.end(), findpath.begin(), ::tolower); // Filtering directory name if (endsWith(excludedPath,'/')) { if (!endsWith(findpath,'/')) findpath = removeFilename(findpath); if (excludedPath.length() > findpath.length()) continue; // Match relative paths starting with mask // -isrc matches src/foo.cpp if (findpath.compare(0, excludedPath.size(), excludedPath) == 0) return true; // Match only full directory name in middle or end of the path // -isrc matches myproject/src/ but does not match // myproject/srcfiles/ or myproject/mysrc/ if (findpath.find("/" + excludedPath) != std::string::npos) return true; } // Filtering filename else { if (excludedPath.length() > findpath.length()) continue; // Check if path ends with mask // -ifoo.cpp matches (./)foo.c, src/foo.cpp and proj/src/foo.cpp // -isrc/file.cpp matches src/foo.cpp and proj/src/foo.cpp if (findpath.compare(findpath.size() - excludedPath.size(), findpath.size(), excludedPath) == 0) return true; } } return false; } std::string PathMatch::removeFilename(const std::string &path) { const std::size_t ind = path.find_last_of('/'); return path.substr(0, ind + 1); } cppcheck-2.7/lib/pathmatch.h000066400000000000000000000034771417746362400160630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PATHMATCH_H #define PATHMATCH_H #include "config.h" #include #include /// @addtogroup CLI /// @{ /** * @brief Simple path matching for ignoring paths in CLI. */ class CPPCHECKLIB PathMatch { public: /** * The constructor. * @param excludedPaths List of masks. * @param caseSensitive Match the case of the characters when * matching paths? */ explicit PathMatch(const std::vector &excludedPaths, bool caseSensitive = true); /** * @brief Match path against list of masks. * @param path Path to match. * @return true if any of the masks match the path, false otherwise. */ bool match(const std::string &path) const; protected: /** * @brief Remove filename part from the path. * @param path Path to edit. * @return path without filename part. */ static std::string removeFilename(const std::string &path); private: std::vector mExcludedPaths; bool mCaseSensitive; std::vector mWorkingDirectory; }; /// @} #endif // PATHMATCH_H cppcheck-2.7/lib/pcrerules.pri000066400000000000000000000007241417746362400164510ustar00rootroot00000000000000# If HAVE_RULES=yes is passed to qmake, use PCRE and enable rules contains(HAVE_RULES, [yY][eE][sS]) { CONFIG += use_pcre_rules } use_pcre_rules { DEFINES += HAVE_RULES LIBS += -L../externals -lpcre INCLUDEPATH += ../externals message("Rules enabled - to disable them and remove the dependency on PCRE, pass HAVE_RULES=no to qmake.") } else { message("Rules disabled - to enable them, make PCRE available and pass HAVE_RULES=yes to qmake.") } cppcheck-2.7/lib/platform.cpp000066400000000000000000000210501417746362400162540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "platform.h" #include "path.h" #include #include #include #include #include #include cppcheck::Platform::Platform() { // This assumes the code you are checking is for the same architecture this is compiled on. #if defined(_WIN64) platform(Win64); #elif defined(_WIN32) platform(Win32A); #else platform(Native); #endif } bool cppcheck::Platform::platform(cppcheck::Platform::PlatformType type) { switch (type) { case Unspecified: // unknown type sizes (sizes etc are set but are not known) case Native: // same as system this code was compile on platformType = type; sizeof_bool = sizeof(bool); sizeof_short = sizeof(short); sizeof_int = sizeof(int); sizeof_long = sizeof(long); sizeof_long_long = sizeof(long long); sizeof_float = sizeof(float); sizeof_double = sizeof(double); sizeof_long_double = sizeof(long double); sizeof_wchar_t = sizeof(wchar_t); sizeof_size_t = sizeof(std::size_t); sizeof_pointer = sizeof(void *); if (type == Unspecified) { defaultSign = '\0'; } else { defaultSign = (std::numeric_limits::is_signed) ? 's' : 'u'; } char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Win32W: case Win32A: platformType = type; sizeof_bool = 1; // 4 in Visual C++ 4.2 sizeof_short = 2; sizeof_int = 4; sizeof_long = 4; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 8; sizeof_wchar_t = 2; sizeof_size_t = 4; sizeof_pointer = 4; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Win64: platformType = type; sizeof_bool = 1; sizeof_short = 2; sizeof_int = 4; sizeof_long = 4; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 8; sizeof_wchar_t = 2; sizeof_size_t = 8; sizeof_pointer = 8; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Unix32: platformType = type; sizeof_bool = 1; sizeof_short = 2; sizeof_int = 4; sizeof_long = 4; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 12; sizeof_wchar_t = 4; sizeof_size_t = 4; sizeof_pointer = 4; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Unix64: platformType = type; sizeof_bool = 1; sizeof_short = 2; sizeof_int = 4; sizeof_long = 8; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 16; sizeof_wchar_t = 4; sizeof_size_t = 8; sizeof_pointer = 8; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case PlatformFile: // sizes are not set. return false; } // unsupported platform return false; } bool cppcheck::Platform::loadPlatformFile(const char exename[], const std::string &filename) { // open file.. tinyxml2::XMLDocument doc; if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) { std::vector filenames; filenames.push_back(filename + ".xml"); if (exename && (std::string::npos != Path::fromNativeSeparators(exename).find('/'))) { filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + filename); filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + filename); filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename); filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename + ".xml"); } #ifdef FILESDIR std::string filesdir = FILESDIR; if (!filesdir.empty() && filesdir[filesdir.size()-1] != '/') filesdir += '/'; filenames.push_back(filesdir + ("platforms/" + filename)); filenames.push_back(filesdir + ("platforms/" + filename + ".xml")); #endif bool success = false; for (const std::string & f : filenames) { if (doc.LoadFile(f.c_str()) == tinyxml2::XML_SUCCESS) { success = true; break; } } if (!success) return false; } return loadFromXmlDocument(&doc); } bool cppcheck::Platform::loadFromXmlDocument(const tinyxml2::XMLDocument *doc) { const tinyxml2::XMLElement * const rootnode = doc->FirstChildElement(); if (!rootnode || std::strcmp(rootnode->Name(), "platform") != 0) return false; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "default-sign") == 0) defaultSign = *node->GetText(); else if (std::strcmp(node->Name(), "char_bit") == 0) char_bit = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "sizeof") == 0) { for (const tinyxml2::XMLElement *sz = node->FirstChildElement(); sz; sz = sz->NextSiblingElement()) { if (std::strcmp(sz->Name(), "short") == 0) sizeof_short = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "bool") == 0) sizeof_bool = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "int") == 0) sizeof_int = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "long") == 0) sizeof_long = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "long-long") == 0) sizeof_long_long = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "float") == 0) sizeof_float = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "double") == 0) sizeof_double = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "long-double") == 0) sizeof_long_double = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "pointer") == 0) sizeof_pointer = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "size_t") == 0) sizeof_size_t = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "wchar_t") == 0) sizeof_wchar_t = std::atoi(sz->GetText()); } } } short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; platformType = PlatformFile; return true; } cppcheck-2.7/lib/platform.h000066400000000000000000000125721417746362400157320ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef platformH #define platformH //--------------------------------------------------------------------------- #include "config.h" #include "utils.h" #include #include /// @addtogroup Core /// @{ namespace tinyxml2 { class XMLDocument; } namespace cppcheck { /** * @brief Platform settings */ class CPPCHECKLIB Platform { private: static long long min_value(int bit) { if (bit >= 64) return LLONG_MIN; return -(1LL << (bit-1)); } static long long max_value(int bit) { if (bit >= 64) return (~0ULL) >> 1; return (1LL << (bit-1)) - 1LL; } public: Platform(); virtual ~Platform() {} bool isIntValue(long long value) const { return value >= min_value(int_bit) && value <= max_value(int_bit); } bool isIntValue(unsigned long long value) const { unsigned long long intMax = max_value(int_bit); return value <= intMax; } bool isLongValue(long long value) const { return value >= min_value(long_bit) && value <= max_value(long_bit); } bool isLongValue(unsigned long long value) const { unsigned long long longMax = max_value(long_bit); return value <= longMax; } bool isLongLongValue(unsigned long long value) const { unsigned long long longLongMax = max_value(long_long_bit); return value <= longLongMax; } nonneg int char_bit; /// bits in char nonneg int short_bit; /// bits in short nonneg int int_bit; /// bits in int nonneg int long_bit; /// bits in long nonneg int long_long_bit; /// bits in long long /** size of standard types */ nonneg int sizeof_bool; nonneg int sizeof_short; nonneg int sizeof_int; nonneg int sizeof_long; nonneg int sizeof_long_long; nonneg int sizeof_float; nonneg int sizeof_double; nonneg int sizeof_long_double; nonneg int sizeof_wchar_t; nonneg int sizeof_size_t; nonneg int sizeof_pointer; char defaultSign; // unsigned:'u', signed:'s', unknown:'\0' enum PlatformType { Unspecified, // No platform specified Native, // whatever system this code was compiled on Win32A, Win32W, Win64, Unix32, Unix64, PlatformFile }; /** platform type */ PlatformType platformType; /** set the platform type for predefined platforms */ bool platform(PlatformType type); /** * load platform file * @param exename application path * @param filename platform filename * @return returns true if file was loaded successfully */ bool loadPlatformFile(const char exename[], const std::string &filename); /** load platform from xml document, primarily for testing */ bool loadFromXmlDocument(const tinyxml2::XMLDocument *doc); /** * @brief Returns true if platform type is Windows * @return true if Windows platform type. */ bool isWindowsPlatform() const { return platformType == Win32A || platformType == Win32W || platformType == Win64; } const char *platformString() const { return platformString(platformType); } static const char *platformString(PlatformType pt) { switch (pt) { case Unspecified: return "Unspecified"; case Native: return "Native"; case Win32A: return "win32A"; case Win32W: return "win32W"; case Win64: return "win64"; case Unix32: return "unix32"; case Unix64: return "unix64"; case PlatformFile: return "platformFile"; default: return "unknown"; } } long long unsignedCharMax() const { return max_value(char_bit + 1); } long long signedCharMax() const { return max_value(char_bit); } long long signedCharMin() const { return min_value(char_bit); } }; } /// @} //--------------------------------------------------------------------------- #endif // platformH cppcheck-2.7/lib/precompiled.h000066400000000000000000000016521417746362400164060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "config.h" #include "check.h" #include "library.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include "symboldatabase.h" #include "settings.h" cppcheck-2.7/lib/preprocessor.cpp000066400000000000000000001251171417746362400171670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "preprocessor.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" #include "path.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include #include #include #include // back_inserter #include #include #include static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) { return tok1 && tok2 && tok1->location.sameline(tok2->location); } /** * Remove heading and trailing whitespaces from the input parameter. * If string is all spaces/tabs, return empty string. * @param s The string to trim. */ static std::string trim(const std::string& s) { const std::string::size_type beg = s.find_first_not_of(" \t"); if (beg == std::string::npos) return ""; const std::string::size_type end = s.find_last_not_of(" \t"); return s.substr(beg, end - beg + 1); } Directive::Directive(const std::string &_file, const int _linenr, const std::string &_str) : file(_file), linenr(_linenr), str(trim(_str)) {} std::atomic Preprocessor::missingIncludeFlag; std::atomic Preprocessor::missingSystemIncludeFlag; char Preprocessor::macroChar = char(1); Preprocessor::Preprocessor(Settings& settings, ErrorLogger *errorLogger) : mSettings(settings), mErrorLogger(errorLogger) {} Preprocessor::~Preprocessor() { for (std::pair& tokenList : mTokenLists) delete tokenList.second; } namespace { struct BadInlineSuppression { BadInlineSuppression(const simplecpp::Location &l, const std::string &msg) : location(l), errmsg(msg) {} simplecpp::Location location; std::string errmsg; }; } static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std::list &inlineSuppressions, std::list *bad) { const std::string cppchecksuppress("cppcheck-suppress"); const std::string &comment = tok->str(); if (comment.size() < cppchecksuppress.size()) return false; 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; // skip spaces after "cppcheck-suppress" const std::string::size_type pos2 = comment.find_first_not_of(" ", pos1+cppchecksuppress.size()); if (pos2 == std::string::npos) return false; if (comment[pos2] == '[') { // multi suppress format std::string errmsg; std::vector suppressions = Suppressions::parseMultiSuppressComment(comment, &errmsg); if (!errmsg.empty()) bad->push_back(BadInlineSuppression(tok->location, errmsg)); for (const Suppressions::Suppression &s : suppressions) { if (!s.errorId.empty()) inlineSuppressions.push_back(s); } } else { //single suppress format std::string errmsg; Suppressions::Suppression s; if (!s.parseComment(comment, &errmsg)) return false; if (!s.errorId.empty()) inlineSuppressions.push_back(s); if (!errmsg.empty()) bad->push_back(BadInlineSuppression(tok->location, errmsg)); } return true; } static void addinlineSuppressions(const simplecpp::TokenList &tokens, Settings &mSettings, std::list *bad) { for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (!tok->comment) continue; std::list inlineSuppressions; if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad)) continue; if (!sameline(tok->previous, tok)) { // find code after comment.. tok = tok->next; while (tok && tok->comment) { parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad); tok = tok->next; } if (!tok) break; } if (inlineSuppressions.empty()) continue; // Relative filename std::string relativeFilename(tok->location.file()); if (mSettings.relativePaths) { for (const std::string & basePath : mSettings.basePaths) { const std::string bp = basePath + "/"; if (relativeFilename.compare(0,bp.size(),bp)==0) { relativeFilename = relativeFilename.substr(bp.size()); } } } relativeFilename = Path::simplifyPath(relativeFilename); // special handling when suppressing { warnings for backwards compatibility const bool thisAndNextLine = tok->previous && tok->previous->previous && tok->next && !sameline(tok->previous->previous, tok->previous) && tok->location.line + 1 == tok->next->location.line && tok->location.fileIndex == tok->next->location.fileIndex && tok->previous->str() == "{"; // Add the suppressions. for (Suppressions::Suppression &suppr : inlineSuppressions) { suppr.fileName = relativeFilename; suppr.lineNumber = tok->location.line; suppr.thisAndNextLine = thisAndNextLine; mSettings.nomsg.addSuppression(suppr); } } } void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens) { if (!mSettings.inlineSuppressions) return; std::list err; ::addinlineSuppressions(tokens, mSettings, &err); for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { if (it->second) ::addinlineSuppressions(*it->second, mSettings, &err); } for (const BadInlineSuppression &bad : err) { error(bad.location.file(), bad.location.line, bad.errmsg); } } void Preprocessor::setDirectives(const simplecpp::TokenList &tokens) { // directive list.. mDirectives.clear(); std::vector list; list.reserve(1U + mTokenLists.size()); list.push_back(&tokens); for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { list.push_back(it->second); } for (const simplecpp::TokenList *tokenList : list) { for (const simplecpp::Token *tok = tokenList->cfront(); tok; tok = tok->next) { if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line)) continue; if (tok->next && tok->next->str() == "endfile") continue; Directive directive(tok->location.file(), tok->location.line, emptyString); for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) { if (tok2->comment) continue; if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str().size())) directive.str += ' '; if (directive.str == "#" && tok2->str() == "file") directive.str += "include"; else directive.str += tok2->str(); } mDirectives.push_back(directive); } } } static std::string readcondition(const simplecpp::Token *iftok, const std::set &defined, const std::set &undefined) { const simplecpp::Token *cond = iftok->next; if (!sameline(iftok,cond)) return ""; const simplecpp::Token *next1 = cond->next; const simplecpp::Token *next2 = next1 ? next1->next : nullptr; const simplecpp::Token *next3 = next2 ? next2->next : nullptr; unsigned int len = 1; if (sameline(iftok,next1)) len = 2; if (sameline(iftok,next2)) len = 3; if (sameline(iftok,next3)) len = 4; if (len == 1 && cond->str() == "0") return "0"; if (len == 1 && cond->name) { if (defined.find(cond->str()) == defined.end()) return cond->str(); } if (len == 2 && cond->op == '!' && next1->name) { if (defined.find(next1->str()) == defined.end()) return next1->str() + "=0"; } if (len == 3 && cond->op == '(' && next1->name && next2->op == ')') { if (defined.find(next1->str()) == defined.end() && undefined.find(next1->str()) == undefined.end()) return next1->str(); } if (len == 3 && cond->name && next1->str() == "==" && next2->number) { if (defined.find(cond->str()) == defined.end()) return cond->str() + '=' + cond->next->next->str(); } std::set configset; for (; sameline(iftok,cond); cond = cond->next) { if (cond->op == '!') { if (!sameline(iftok,cond->next) || !cond->next->name) break; if (cond->next->str() == "defined") continue; configset.insert(cond->next->str() + "=0"); continue; } if (cond->str() != "defined") continue; const simplecpp::Token *dtok = cond->next; if (!dtok) break; if (dtok->op == '(') dtok = dtok->next; if (sameline(iftok,dtok) && dtok->name && defined.find(dtok->str()) == defined.end() && undefined.find(dtok->str()) == undefined.end()) configset.insert(dtok->str()); } std::string cfgStr; for (const std::string &s : configset) { if (!cfgStr.empty()) cfgStr += ';'; cfgStr += s; } return cfgStr; } static bool hasDefine(const std::string &userDefines, const std::string &cfg) { if (cfg.empty()) { return false; } std::string::size_type pos = 0; while (pos < userDefines.size()) { pos = userDefines.find(cfg, pos); if (pos == std::string::npos) break; const std::string::size_type pos2 = pos + cfg.size(); if ((pos == 0 || userDefines[pos-1U] == ';') && (pos2 == userDefines.size() || userDefines[pos2] == '=')) return true; pos = pos2; } return false; } static std::string cfg(const std::vector &configs, const std::string &userDefines) { std::set configs2(configs.begin(), configs.end()); std::string ret; for (const std::string &c : configs2) { if (c.empty()) continue; if (c == "0") return ""; if (hasDefine(userDefines, c)) continue; if (!ret.empty()) ret += ';'; ret += c; } return ret; } static bool isUndefined(const std::string &cfg, const std::set &undefined) { for (std::string::size_type pos1 = 0U; pos1 < cfg.size();) { const std::string::size_type pos2 = cfg.find(';',pos1); const std::string def = (pos2 == std::string::npos) ? cfg.substr(pos1) : cfg.substr(pos1, pos2 - pos1); const std::string::size_type eq = def.find('='); if (eq == std::string::npos && undefined.find(def) != undefined.end()) return true; if (eq != std::string::npos && undefined.find(def.substr(0,eq)) != undefined.end() && def.substr(eq) != "=0") return true; pos1 = (pos2 == std::string::npos) ? pos2 : pos2 + 1U; } return false; } static bool getConfigsElseIsFalse(const std::vector &configs_if, const std::string &userDefines) { return std::any_of(configs_if.cbegin(), configs_if.cend(), [=](const std::string &cfg) { return hasDefine(userDefines, cfg); }); } static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok) { int level = 0; while (nullptr != (cmdtok = cmdtok->next)) { if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) { if (cmdtok->next->str().compare(0,2,"if")==0) ++level; else if (cmdtok->next->str() == "endif") { --level; if (level < 0) return cmdtok; } } } return nullptr; } static void getConfigs(const simplecpp::TokenList &tokens, std::set &defined, const std::string &userDefines, const std::set &undefined, std::set &ret) { std::vector configs_if; std::vector configs_ifndef; std::string elseError; for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (tok->op != '#' || sameline(tok->previous, tok)) continue; const simplecpp::Token *cmdtok = tok->next; if (!sameline(tok, cmdtok)) continue; if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef" || cmdtok->str() == "if") { std::string config; if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef") { const simplecpp::Token *expr1 = cmdtok->next; if (sameline(tok,expr1) && expr1->name && !sameline(tok,expr1->next)) config = expr1->str(); if (defined.find(config) != defined.end()) config.clear(); } else if (cmdtok->str() == "if") { config = readcondition(cmdtok, defined, undefined); } // skip undefined configurations.. if (isUndefined(config, undefined)) config.clear(); bool ifndef = false; if (cmdtok->str() == "ifndef") ifndef = true; else { const std::vector match{"if", "!", "defined", "(", config, ")"}; int i = 0; ifndef = true; for (const simplecpp::Token *t = cmdtok; i < match.size(); t = t->next) { if (!t || t->str() != match[i++]) { ifndef = false; break; } } } // include guard.. if (ifndef && tok->location.fileIndex > 0) { bool includeGuard = true; for (const simplecpp::Token *t = tok->previous; t; t = t->previous) { if (t->location.fileIndex == tok->location.fileIndex) { includeGuard = false; break; } } if (includeGuard) { configs_if.push_back(std::string()); configs_ifndef.push_back(std::string()); continue; } } configs_if.push_back((cmdtok->str() == "ifndef") ? std::string() : config); configs_ifndef.push_back((cmdtok->str() == "ifndef") ? config : std::string()); ret.insert(cfg(configs_if,userDefines)); } else if (cmdtok->str() == "elif" || cmdtok->str() == "else") { if (getConfigsElseIsFalse(configs_if,userDefines)) { tok = gotoEndIf(tok); if (!tok) break; tok = tok->previous; continue; } if (cmdtok->str() == "else" && cmdtok->next && !sameline(cmdtok,cmdtok->next) && sameline(cmdtok->next, cmdtok->next->next) && cmdtok->next->op == '#' && cmdtok->next->next->str() == "error") { const std::string &ifcfg = cfg(configs_if, userDefines); if (!ifcfg.empty()) { if (!elseError.empty()) elseError += ';'; elseError += ifcfg; } } if (!configs_if.empty()) configs_if.pop_back(); if (cmdtok->str() == "elif") { std::string config = readcondition(cmdtok, defined, undefined); if (isUndefined(config,undefined)) config.clear(); configs_if.push_back(config); ret.insert(cfg(configs_if, userDefines)); } else if (!configs_ifndef.empty()) { configs_if.push_back(configs_ifndef.back()); ret.insert(cfg(configs_if, userDefines)); } } else if (cmdtok->str() == "endif" && !sameline(tok, cmdtok->next)) { if (!configs_if.empty()) configs_if.pop_back(); if (!configs_ifndef.empty()) configs_ifndef.pop_back(); } else if (cmdtok->str() == "error") { if (!configs_ifndef.empty() && !configs_ifndef.back().empty()) { if (configs_ifndef.size() == 1U) ret.erase(""); std::vector configs(configs_if); configs.push_back(configs_ifndef.back()); ret.erase(cfg(configs, userDefines)); std::set temp; temp.swap(ret); for (const std::string &c: temp) { if (c.find(configs_ifndef.back()) != std::string::npos) ret.insert(c); else if (c.empty()) ret.insert(configs.empty() ? configs_ifndef.back() : ""); else ret.insert(c + ";" + configs_ifndef.back()); } if (!elseError.empty()) elseError += ';'; elseError += cfg(configs_ifndef, userDefines); } if (!configs_if.empty() && !configs_if.back().empty()) { const std::string &last = configs_if.back(); if (last.size() > 2U && last.compare(last.size()-2U,2,"=0") == 0) { std::vector configs(configs_if); ret.erase(cfg(configs, userDefines)); configs[configs.size() - 1U] = last.substr(0,last.size()-2U); if (configs.size() == 1U) ret.erase(""); if (!elseError.empty()) elseError += ';'; elseError += cfg(configs, userDefines); } } } else if (cmdtok->str() == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) { defined.insert(cmdtok->next->str()); } } if (!elseError.empty()) ret.insert(elseError); } std::set Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const { std::set ret = { "" }; if (!tokens.cfront()) return ret; std::set defined = { "__cplusplus" }; ::getConfigs(tokens, defined, mSettings.userDefines, mSettings.userUndefs, ret); for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { if (!mSettings.configurationExcluded(it->first)) ::getConfigs(*(it->second), defined, mSettings.userDefines, mSettings.userUndefs, ret); } return ret; } void Preprocessor::preprocess(std::istream &istr, std::map &result, const std::string &filename, const std::list &includePaths) { (void)includePaths; simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1(istr, files, filename, &outputList); const std::set configs = getConfigs(tokens1); for (const std::string &c : configs) { if (mSettings.userUndefs.find(c) == mSettings.userUndefs.end()) { result[c] = getcode(tokens1, c, files, false); } } } void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list &resultConfigurations, const std::string &filename, const std::list &includePaths) { (void)includePaths; if (mFile0.empty()) mFile0 = filename; simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1(srcCodeStream, files, filename, &outputList); const std::set configs = getConfigs(tokens1); std::copy(configs.cbegin(), configs.cend(), std::back_inserter(resultConfigurations)); processedFile = tokens1.stringify(); } static void splitcfg(const std::string &cfg, std::list &defines, const std::string &defaultValue) { for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) { const std::string::size_type defineEndPos = cfg.find(';', defineStartPos); std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos); if (!defaultValue.empty() && def.find('=') == std::string::npos) def += '=' + defaultValue; defines.push_back(def); if (defineEndPos == std::string::npos) break; defineStartPos = defineEndPos + 1U; } } static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename) { simplecpp::DUI dui; splitcfg(mSettings.userDefines, dui.defines, "1"); if (!cfg.empty()) splitcfg(cfg, dui.defines, emptyString); for (const std::string &def : mSettings.library.defines) { const std::string::size_type pos = def.find_first_of(" ("); if (pos == std::string::npos) { dui.defines.push_back(def); continue; } std::string s = def; if (s[pos] == ' ') { s[pos] = '='; } else { s[s.find(')')+1] = '='; } dui.defines.push_back(s); } dui.undefined = mSettings.userUndefs; // -U dui.includePaths = mSettings.includePaths; // -I dui.includes = mSettings.userIncludes; // --include if (Path::isCPP(filename)) dui.std = mSettings.standards.getCPP(); return dui; } static bool hasErrors(const simplecpp::OutputList &outputList) { for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) { switch (it->type) { case simplecpp::Output::ERROR: case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: return true; case simplecpp::Output::WARNING: case simplecpp::Output::MISSING_HEADER: case simplecpp::Output::PORTABILITY_BACKSLASH: break; } } return false; } void Preprocessor::handleErrors(const simplecpp::OutputList& outputList, bool throwError) { const bool showerror = (!mSettings.userDefines.empty() && !mSettings.force); reportOutput(outputList, showerror); if (throwError) { for (const simplecpp::Output& output : outputList) { switch (output.type) { case simplecpp::Output::ERROR: case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: throw output; case simplecpp::Output::WARNING: case simplecpp::Output::MISSING_HEADER: case simplecpp::Output::PORTABILITY_BACKSLASH: break; } } } } bool Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files) { const simplecpp::DUI dui = createDUI(mSettings, emptyString, files[0]); simplecpp::OutputList outputList; mTokenLists = simplecpp::load(rawtokens, files, dui, &outputList); handleErrors(outputList, false); return !hasErrors(outputList); } void Preprocessor::removeComments() { for (std::pair& tokenList : mTokenLists) { if (tokenList.second) tokenList.second->removeComments(); } } void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const { tokens->sizeOfType["bool"] = mSettings.sizeof_bool; tokens->sizeOfType["short"] = mSettings.sizeof_short; tokens->sizeOfType["int"] = mSettings.sizeof_int; tokens->sizeOfType["long"] = mSettings.sizeof_long; tokens->sizeOfType["long long"] = mSettings.sizeof_long_long; tokens->sizeOfType["float"] = mSettings.sizeof_float; tokens->sizeOfType["double"] = mSettings.sizeof_double; tokens->sizeOfType["long double"] = mSettings.sizeof_long_double; tokens->sizeOfType["bool *"] = mSettings.sizeof_pointer; tokens->sizeOfType["short *"] = mSettings.sizeof_pointer; tokens->sizeOfType["int *"] = mSettings.sizeof_pointer; tokens->sizeOfType["long *"] = mSettings.sizeof_pointer; tokens->sizeOfType["long long *"] = mSettings.sizeof_pointer; tokens->sizeOfType["float *"] = mSettings.sizeof_pointer; tokens->sizeOfType["double *"] = mSettings.sizeof_pointer; tokens->sizeOfType["long double *"] = mSettings.sizeof_pointer; } simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError) { const simplecpp::DUI dui = createDUI(mSettings, cfg, files[0]); simplecpp::OutputList outputList; std::list macroUsage; std::list ifCond; simplecpp::TokenList tokens2(files); simplecpp::preprocess(tokens2, tokens1, files, mTokenLists, dui, &outputList, ¯oUsage, &ifCond); mMacroUsage = macroUsage; mIfCond = ifCond; handleErrors(outputList, throwError); tokens2.removeComments(); // ensure that guessed define macros without value are not used in the code if (!validateCfg(cfg, macroUsage)) return simplecpp::TokenList(files); return tokens2; } std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, const bool writeLocations) { simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files, false); unsigned int prevfile = 0; unsigned int line = 1; std::ostringstream ret; for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) { if (writeLocations && tok->location.fileIndex != prevfile) { ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; prevfile = tok->location.fileIndex; line = tok->location.line; } if (tok->previous && line >= tok->location.line) // #7912 ret << ' '; while (tok->location.line > line) { ret << '\n'; line++; } if (!tok->macro.empty()) ret << Preprocessor::macroChar; ret << tok->str(); } return ret.str(); } std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename) { simplecpp::OutputList outputList; std::vector files; std::istringstream istr(filedata); simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList); inlineSuppressions(tokens1); tokens1.removeComments(); removeComments(); setDirectives(tokens1); reportOutput(outputList, true); if (hasErrors(outputList)) return ""; std::string ret; try { ret = getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos); // Since "files" is a local variable the tracking info must be cleared.. mMacroUsage.clear(); mIfCond.clear(); } catch (const simplecpp::Output &) { ret.clear(); } return ret; } void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror) { for (const simplecpp::Output &out : outputList) { switch (out.type) { case simplecpp::Output::ERROR: if (out.msg.compare(0,6,"#error")!=0 || showerror) error(out.location.file(), out.location.line, out.msg); break; case simplecpp::Output::WARNING: case simplecpp::Output::PORTABILITY_BACKSLASH: break; case simplecpp::Output::MISSING_HEADER: { const std::string::size_type pos1 = out.msg.find_first_of("<\""); const std::string::size_type pos2 = out.msg.find_first_of(">\"", pos1 + 1U); if (pos1 < pos2 && pos2 != std::string::npos) missingInclude(out.location.file(), out.location.line, out.msg.substr(pos1+1, pos2-pos1-1), out.msg[pos1] == '\"' ? UserHeader : SystemHeader); } break; case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: error(out.location.file(), out.location.line, out.msg); break; case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: error(emptyString, 0, out.msg); break; } } } void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg) { std::list locationList; if (!filename.empty()) { std::string file = Path::fromNativeSeparators(filename); if (mSettings.relativePaths) file = Path::getRelativePath(file, mSettings.basePaths); const ErrorMessage::FileLocation loc(file, linenr, 0); locationList.push_back(loc); } mErrorLogger->reportErr(ErrorMessage(locationList, mFile0, Severity::error, msg, "preprocessorErrorDirective", Certainty::normal)); } // Report that include is missing void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType) { const std::string fname = Path::fromNativeSeparators(filename); Suppressions::ErrorMessage errorMessage; errorMessage.errorId = "missingInclude"; errorMessage.setFileName(fname); errorMessage.lineNumber = linenr; if (mSettings.nomsg.isSuppressed(errorMessage)) return; errorMessage.errorId = "missingIncludeSystem"; if (headerType == SystemHeader && mSettings.nomsg.isSuppressed(errorMessage)) return; if (headerType == SystemHeader) missingSystemIncludeFlag = true; else missingIncludeFlag = true; if (mErrorLogger && mSettings.checkConfiguration) { std::list locationList; if (!filename.empty()) { ErrorMessage::FileLocation loc; loc.line = linenr; loc.setfile(Path::toNativeSeparators(filename)); locationList.push_back(loc); } ErrorMessage errmsg(locationList, mFile0, Severity::information, (headerType==SystemHeader) ? "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." : "Include file: \"" + header + "\" not found.", (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude", Certainty::normal); mErrorLogger->reportInfo(errmsg); } } bool Preprocessor::validateCfg(const std::string &cfg, const std::list ¯oUsageList) { bool ret = true; std::list defines; splitcfg(cfg, defines, emptyString); for (const std::string &define : defines) { if (define.find('=') != std::string::npos) continue; const std::string macroName(define.substr(0, define.find('('))); for (const simplecpp::MacroUsage &mu : macroUsageList) { if (mu.macroValueKnown) continue; if (mu.macroName != macroName) continue; bool directiveLocation = std::any_of(mDirectives.cbegin(), mDirectives.cend(), [=](const Directive &dir) { return mu.useLocation.file() == dir.file && mu.useLocation.line == dir.linenr; }); if (!directiveLocation) { if (mSettings.severity.isEnabled(Severity::information)) validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName); ret = false; } } } return ret; } void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o) { const std::string id = "ConfigurationNotChecked"; std::list locationList; const ErrorMessage::FileLocation loc(file, line, 0); locationList.push_back(loc); const ErrorMessage errmsg(locationList, mFile0, Severity::information, "Skipping configuration '" + cfg + "' since the value of '" + macro + "' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.", id, Certainty::normal); mErrorLogger->reportInfo(errmsg); } void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) { Settings settings2(*settings); Preprocessor preprocessor(settings2, errorLogger); settings2.checkConfiguration = true; preprocessor.missingInclude(emptyString, 1, emptyString, UserHeader); preprocessor.missingInclude(emptyString, 1, emptyString, SystemHeader); preprocessor.validateCfgError(emptyString, 1, "X", "X"); preprocessor.error(emptyString, 1, "#error message"); // #error .. } void Preprocessor::dump(std::ostream &out) const { // Create a xml dump. out << " " << std::endl; for (const Directive &dir : mDirectives) { out << " ' which // could result in invalid XML, so run it through toxml(). << "str=\"" << ErrorLogger::toxml(dir.str) << "\"/>" << std::endl; } out << " " << std::endl; if (!mMacroUsage.empty()) { out << " " << std::endl; for (const simplecpp::MacroUsage ¯oUsage: mMacroUsage) { out << " " << std::endl; } out << " " << std::endl; } if (!mIfCond.empty()) { out << " " << std::endl; for (const simplecpp::IfCond &ifCond: mIfCond) { out << " " << std::endl; } out << " " << std::endl; } } static const std::uint32_t crc32Table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; static std::uint32_t crc32(const std::string &data) { std::uint32_t crc = ~0U; for (char c : data) { crc = crc32Table[(crc ^ (unsigned char)c) & 0xFF] ^ (crc >> 8); } return crc ^ ~0U; } unsigned int Preprocessor::calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const { std::ostringstream ostr; ostr << toolinfo << '\n'; for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { if (!tok->comment) ostr << tok->str(); } for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) { if (!tok->comment) ostr << tok->str(); } } return crc32(ostr.str()); } void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList) { Preprocessor::simplifyPragmaAsmPrivate(tokenList); for (std::pair& list : mTokenLists) { Preprocessor::simplifyPragmaAsmPrivate(list.second); } } void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList) { // assembler code.. for (simplecpp::Token *tok = tokenList->front(); tok; tok = tok->next) { if (tok->op != '#') continue; if (sameline(tok, tok->previousSkipComments())) continue; const simplecpp::Token * const tok2 = tok->nextSkipComments(); if (!tok2 || !sameline(tok, tok2) || tok2->str() != "pragma") continue; const simplecpp::Token * const tok3 = tok2->nextSkipComments(); if (!tok3 || !sameline(tok, tok3) || tok3->str() != "asm") continue; const simplecpp::Token *endasm = tok3; while ((endasm = endasm->next) != nullptr) { if (endasm->op != '#' || sameline(endasm,endasm->previousSkipComments())) continue; const simplecpp::Token * const endasm2 = endasm->nextSkipComments(); if (!endasm2 || !sameline(endasm, endasm2) || endasm2->str() != "pragma") continue; const simplecpp::Token * const endasm3 = endasm2->nextSkipComments(); if (!endasm3 || !sameline(endasm2, endasm3) || endasm3->str() != "endasm") continue; while (sameline(endasm,endasm3)) endasm = endasm->next; break; } const simplecpp::Token * const tok4 = tok3->next; tok->setstr("asm"); const_cast(tok2)->setstr("("); const_cast(tok3)->setstr(")"); const_cast(tok4)->setstr(";"); while (tok4->next != endasm) tokenList->deleteToken(tok4->next); } } cppcheck-2.7/lib/preprocessor.h000066400000000000000000000175661417746362400166440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef preprocessorH #define preprocessorH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include #include #include #include #include class ErrorLogger; class Settings; /** * @brief A preprocessor directive * Each preprocessor directive (\#include, \#define, \#undef, \#if, \#ifdef, \#else, \#endif) * will be recorded as an instance of this class. * * file and linenr denote the location where where the directive is defined. * */ class CPPCHECKLIB Directive { public: /** name of (possibly included) file where directive is defined */ std::string file; /** line number in (possibly included) file where directive is defined */ unsigned int linenr; /** the actual directive text */ std::string str; /** record a directive (possibly filtering src) */ Directive(const std::string &_file, const int _linenr, const std::string &_str); }; /// @addtogroup Core /// @{ /** * @brief The cppcheck preprocessor. * The preprocessor has special functionality for extracting the various ifdef * configurations that exist in a source file. */ class CPPCHECKLIB Preprocessor { public: /** * Include file types. */ enum HeaderTypes { NoHeader = 0, UserHeader, SystemHeader }; /** character that is inserted in expanded macros */ static char macroChar; explicit Preprocessor(Settings& settings, ErrorLogger *errorLogger = nullptr); virtual ~Preprocessor(); static std::atomic missingIncludeFlag; static std::atomic missingSystemIncludeFlag; void inlineSuppressions(const simplecpp::TokenList &tokens); void setDirectives(const simplecpp::TokenList &tokens); void setDirectives(const std::list &directives) { mDirectives = directives; } /** list of all directives met while preprocessing file */ const std::list &getDirectives() const { return mDirectives; } std::set getConfigs(const simplecpp::TokenList &tokens) const; void handleErrors(const simplecpp::OutputList &outputList, bool throwError); bool loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files); void removeComments(); void setPlatformInfo(simplecpp::TokenList *tokens) const; /** * Extract the code for each configuration * @param istr The (file/string) stream to read from. * @param result The map that will get the results * @param filename The name of the file to check e.g. "src/main.cpp" * @param includePaths List of paths where include files should be searched from, * single path can be e.g. in format "include/". * There must be a path separator at the end. Default parameter is empty list. * Note that if path from given filename is also extracted and that is used as * a last include path if include file was not found from earlier paths. */ void preprocess(std::istream &istr, std::map &result, const std::string &filename, const std::list &includePaths = std::list()); /** * Extract the code for each configuration. Use this with getcode() to get the * file data for each individual configuration. * * @param srcCodeStream The (file/string) stream to read from. * @param processedFile Give reference to empty string as a parameter, * function will fill processed file here. Use this also as a filedata parameter * to getcode() if you received more than once configurations. * @param resultConfigurations List of configurations. Pass these one by one * to getcode() with processedFile. * @param filename The name of the file to check e.g. "src/main.cpp" * @param includePaths List of paths where include files should be searched from, * single path can be e.g. in format "include/". * There must be a path separator at the end. Default parameter is empty list. * Note that if path from given filename is also extracted and that is used as * a last include path if include file was not found from earlier paths. */ void preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list &resultConfigurations, const std::string &filename, const std::list &includePaths); simplecpp::TokenList preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError = false); std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, const bool writeLocations); /** * Get preprocessed code for a given configuration * @param filedata file data including preprocessing 'if', 'define', etc * @param cfg configuration to read out * @param filename name of source file */ std::string getcode(const std::string &filedata, const std::string &cfg, const std::string &filename); /** * make sure empty configuration macros are not used in code. the given code must be a single configuration * @param cfg configuration * @param macroUsageList macro usage list * @return true => configuration is valid */ bool validateCfg(const std::string &cfg, const std::list ¯oUsageList); void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o); /** * Calculate CRC32 checksum. Using toolinfo, tokens1, filedata. * * @param tokens1 Sourcefile tokens * @param toolinfo Arbitrary extra toolinfo * @return CRC32 checksum */ unsigned int calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const; void simplifyPragmaAsm(simplecpp::TokenList *tokenList); private: static void simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList); public: static void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings); void setFile0(const std::string &f) { mFile0 = f; } /** * dump all directives present in source file */ void dump(std::ostream &out) const; void reportOutput(const simplecpp::OutputList &outputList, bool showerror); private: void missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType); void error(const std::string &filename, unsigned int linenr, const std::string &msg); Settings& mSettings; ErrorLogger *mErrorLogger; /** list of all directives met while preprocessing file */ std::list mDirectives; std::map mTokenLists; /** filename for cpp/c file - useful when reporting errors */ std::string mFile0; /** simplecpp tracking info */ std::list mMacroUsage; std::list mIfCond; }; /// @} //--------------------------------------------------------------------------- #endif // preprocessorH cppcheck-2.7/lib/programmemory.cpp000066400000000000000000000662771417746362400173540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "programmemory.h" #include "astutils.h" #include "calculate.h" #include "infer.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "valueflow.h" #include "valueptr.h" #include #include #include #include #include #include #include #include void ProgramMemory::setValue(nonneg int exprid, const ValueFlow::Value& value) { values[exprid] = value; } const ValueFlow::Value* ProgramMemory::getValue(nonneg int exprid, bool impossible) const { const ProgramMemory::Map::const_iterator it = values.find(exprid); const bool found = it != values.end() && (impossible || !it->second.isImpossible()); if (found) return &it->second; else return nullptr; } // cppcheck-suppress unusedFunction bool ProgramMemory::getIntValue(nonneg int exprid, MathLib::bigint* result) const { const ValueFlow::Value* value = getValue(exprid); if (value && value->isIntValue()) { *result = value->intvalue; return true; } return false; } void ProgramMemory::setIntValue(nonneg int exprid, MathLib::bigint value, bool impossible) { ValueFlow::Value v(value); if (impossible) v.setImpossible(); values[exprid] = v; } bool ProgramMemory::getTokValue(nonneg int exprid, const Token** result) const { const ValueFlow::Value* value = getValue(exprid); if (value && value->isTokValue()) { *result = value->tokvalue; return true; } return false; } // cppcheck-suppress unusedFunction bool ProgramMemory::getContainerSizeValue(nonneg int exprid, MathLib::bigint* result) const { const ValueFlow::Value* value = getValue(exprid); if (value && value->isContainerSizeValue()) { *result = value->intvalue; return true; } return false; } bool ProgramMemory::getContainerEmptyValue(nonneg int exprid, MathLib::bigint* result) const { const ValueFlow::Value* value = getValue(exprid, true); if (value && value->isContainerSizeValue()) { if (value->isImpossible() && value->intvalue == 0) { *result = false; return true; } if (!value->isImpossible()) { *result = (value->intvalue == 0); return true; } } return false; } void ProgramMemory::setContainerSizeValue(nonneg int exprid, MathLib::bigint value, bool isEqual) { ValueFlow::Value v(value); v.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; if (!isEqual) v.valueKind = ValueFlow::Value::ValueKind::Impossible; values[exprid] = v; } void ProgramMemory::setUnknown(nonneg int exprid) { values[exprid].valueType = ValueFlow::Value::ValueType::UNINIT; } bool ProgramMemory::hasValue(nonneg int exprid) { return values.find(exprid) != values.end(); } void ProgramMemory::swap(ProgramMemory &pm) { values.swap(pm.values); } void ProgramMemory::clear() { values.clear(); } bool ProgramMemory::empty() const { return values.empty(); } void ProgramMemory::replace(const ProgramMemory &pm) { for (auto&& p : pm.values) { values[p.first] = p.second; } } void ProgramMemory::insert(const ProgramMemory &pm) { for (auto&& p:pm.values) values.insert(p); } bool evaluateCondition(const std::string& op, MathLib::bigint r, const Token* condition, ProgramMemory& pm, const Settings* settings) { if (!condition) return false; if (condition->str() == op) { return evaluateCondition(op, r, condition->astOperand1(), pm, settings) || evaluateCondition(op, r, condition->astOperand2(), pm, settings); } MathLib::bigint result = 0; bool error = false; execute(condition, &pm, &result, &error, settings); return !error && result == r; } bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings) { return evaluateCondition("&&", 0, condition, pm, settings); } bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings) { return evaluateCondition("||", 1, condition, pm, settings); } static bool frontIs(const std::vector& v, bool i) { if (v.empty()) return false; if (v.front()) return i; return !i; } void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then) { auto eval = [&](const Token* t) -> std::vector { if (t->hasKnownIntValue()) return {t->values().front().intvalue}; MathLib::bigint result = 0; bool error = false; execute(t, &pm, &result, &error); if (!error) return {result}; return std::vector{}; }; if (Token::Match(tok, "==|>=|<=|<|>|!=")) { ValueFlow::Value truevalue; ValueFlow::Value falsevalue; const Token* vartok = parseCompareInt(tok, truevalue, falsevalue, eval); if (!vartok) return; if (vartok->exprId() == 0) return; if (!truevalue.isIntValue()) return; if (endTok && isExpressionChanged(vartok, tok->next(), endTok, settings, true)) return; bool impossible = (tok->str() == "==" && !then) || (tok->str() == "!=" && then); pm.setIntValue(vartok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, impossible); const Token* containerTok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE); if (containerTok) pm.setContainerSizeValue(containerTok->exprId(), then ? truevalue.intvalue : falsevalue.intvalue, !impossible); } else if (Token::simpleMatch(tok, "!")) { programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then); } else if (then && Token::simpleMatch(tok, "&&")) { programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); } else if (!then && Token::simpleMatch(tok, "||")) { programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); } else if (Token::Match(tok, "&&|%oror%")) { std::vector lhs = eval(tok->astOperand1()); std::vector rhs = eval(tok->astOperand2()); if (lhs.empty() || rhs.empty()) { if (frontIs(lhs, !then)) programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); if (frontIs(rhs, !then)) programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); } } else if (tok->exprId() > 0) { if (endTok && isExpressionChanged(tok, tok->next(), endTok, settings, true)) return; pm.setIntValue(tok->exprId(), 0, then); const Token* containerTok = settings->library.getContainerFromYield(tok, Library::Container::Yield::EMPTY); if (containerTok) pm.setContainerSizeValue(containerTok->exprId(), 0, then); } } static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings) { if (!scope) return; if (!scope->isLocal()) return; assert(scope != scope->nestedIn); fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings); if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse || scope->type == Scope::eFor) { const Token* condTok = getCondTokFromEnd(scope->bodyEnd); if (!condTok) return; MathLib::bigint result = 0; bool error = false; execute(condTok, &pm, &result, &error); if (error) programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse); } } static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings) { fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings); } static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const ProgramMemory& state, const ProgramMemory::Map& vars) { int indentlevel = 0; for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) { if ((Token::simpleMatch(tok2, "=") || Token::Match(tok2->previous(), "%var% (|{")) && tok2->astOperand1() && tok2->astOperand2()) { bool setvar = false; const Token* vartok = tok2->astOperand1(); const Token* valuetok = tok2->astOperand2(); for (const auto& p:vars) { if (p.first != vartok->exprId()) continue; if (vartok == tok) continue; pm.setValue(vartok->exprId(), p.second); setvar = true; } if (!setvar) { if (!pm.hasValue(vartok->exprId())) { MathLib::bigint result = 0; bool error = false; execute(valuetok, &pm, &result, &error); if (!error) pm.setIntValue(vartok->exprId(), result); else pm.setUnknown(vartok->exprId()); } } } else if (tok2->exprId() > 0 && Token::Match(tok2, ".|(|[|*|%var%") && !pm.hasValue(tok2->exprId()) && isVariableChanged(tok2, 0, nullptr, true)) { pm.setUnknown(tok2->exprId()); } if (tok2->str() == "{") { if (indentlevel <= 0) { // Keep progressing with anonymous/do scopes if (!Token::Match(tok2->previous(), "do|; {")) break; } else --indentlevel; if (Token::simpleMatch(tok2->previous(), "else {")) tok2 = tok2->linkAt(-2)->previous(); } if (tok2->str() == "}") { const Token *cond = getCondTokFromEnd(tok2); const bool inElse = Token::simpleMatch(tok2->link()->previous(), "else {"); if (cond) { if (conditionIsFalse(cond, state)) { if (inElse) { ++indentlevel; continue; } tok2 = cond->astParent()->previous(); } else if (conditionIsTrue(cond, state)) { if (inElse) tok2 = tok2->link()->tokAt(-2); ++indentlevel; continue; } } break; } } } static void removeModifiedVars(ProgramMemory& pm, const Token* tok, const Token* origin) { for (auto i = pm.values.begin(), last = pm.values.end(); i != last;) { if (isVariableChanged(origin, tok, i->first, false, nullptr, true)) { i = pm.values.erase(i); } else { ++i; } } } static ProgramMemory getInitialProgramState(const Token* tok, const Token* origin, const ProgramMemory::Map& vars = ProgramMemory::Map {}) { ProgramMemory pm; if (origin) { fillProgramMemoryFromConditions(pm, origin, nullptr); const ProgramMemory state = pm; fillProgramMemoryFromAssignments(pm, tok, state, vars); removeModifiedVars(pm, tok, origin); } return pm; } ProgramMemoryState::ProgramMemoryState(const Settings* s) : state(), origins(), settings(s) {} void ProgramMemoryState::insert(const ProgramMemory &pm, const Token* origin) { if (origin) for (auto&& p:pm.values) origins.insert(std::make_pair(p.first, origin)); state.insert(pm); } void ProgramMemoryState::replace(const ProgramMemory &pm, const Token* origin) { if (origin) for (auto&& p:pm.values) origins[p.first] = origin; state.replace(pm); } static void addVars(ProgramMemory& pm, const ProgramMemory::Map& vars) { for (const auto& p:vars) { nonneg int exprid = p.first; const ValueFlow::Value &value = p.second; pm.setValue(exprid, value); if (value.varId) pm.setIntValue(value.varId, value.varvalue); } } void ProgramMemoryState::addState(const Token* tok, const ProgramMemory::Map& vars) { ProgramMemory pm = state; addVars(pm, vars); fillProgramMemoryFromConditions(pm, tok, settings); ProgramMemory local = pm; fillProgramMemoryFromAssignments(pm, tok, local, vars); addVars(pm, vars); replace(pm, tok); } void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty) { ProgramMemory pm = state; if (isEmpty) pm.setContainerSizeValue(tok->exprId(), 0, b); else programMemoryParseCondition(pm, tok, nullptr, settings, b); const Token* origin = tok; const Token* top = tok->astTop(); if (top && Token::Match(top->previous(), "for|while (")) origin = top->link(); replace(pm, origin); } void ProgramMemoryState::removeModifiedVars(const Token* tok) { for (auto i = state.values.begin(), last = state.values.end(); i != last;) { const Token* start = origins[i->first]; const Token* expr = findExpression(start ? start : tok, i->first); if (!expr || isExpressionChanged(expr, start, tok, settings, true)) { origins.erase(i->first); i = state.values.erase(i); } else { ++i; } } } ProgramMemory ProgramMemoryState::get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const { ProgramMemoryState local = *this; if (ctx) local.addState(ctx, vars); const Token* start = previousBeforeAstLeftmostLeaf(tok); if (!start) start = tok; if (!ctx || precedes(start, ctx)) { local.removeModifiedVars(start); local.addState(start, vars); } else { local.removeModifiedVars(ctx); } return local.state; } ProgramMemory getProgramMemory(const Token *tok, const ProgramMemory::Map& vars) { ProgramMemory programMemory; for (const auto& p:vars) { const ValueFlow::Value &value = p.second; programMemory.replace(getInitialProgramState(tok, value.tokvalue)); programMemory.replace(getInitialProgramState(tok, value.condition)); } fillProgramMemoryFromConditions(programMemory, tok, nullptr); ProgramMemory state; for (const auto& p:vars) { nonneg int exprid = p.first; const ValueFlow::Value &value = p.second; programMemory.setValue(exprid, value); if (value.varId) programMemory.setIntValue(value.varId, value.varvalue); } state = programMemory; fillProgramMemoryFromAssignments(programMemory, tok, state, vars); return programMemory; } ProgramMemory getProgramMemory(const Token* tok, nonneg int exprid, const ValueFlow::Value& value, const Settings *settings) { ProgramMemory programMemory; programMemory.replace(getInitialProgramState(tok, value.tokvalue)); programMemory.replace(getInitialProgramState(tok, value.condition)); fillProgramMemoryFromConditions(programMemory, tok, settings); programMemory.setValue(exprid, value); if (value.varId) programMemory.setIntValue(value.varId, value.varvalue); const ProgramMemory state = programMemory; fillProgramMemoryFromAssignments(programMemory, tok, state, {{exprid, value}}); return programMemory; } static bool isNumericValue(const ValueFlow::Value& value) { return value.isIntValue() || value.isFloatValue(); } static double asFloat(const ValueFlow::Value& value) { return value.isFloatValue() ? value.floatValue : value.intvalue; } static std::string removeAssign(const std::string& assign) { return std::string{assign.begin(), assign.end() - 1}; } struct assign { template void operator()(T& x, const U& y) const { x = y; } }; static ValueFlow::Value evaluate(const std::string& op, const ValueFlow::Value& lhs, const ValueFlow::Value& rhs) { ValueFlow::Value result; if (lhs.isImpossible() && rhs.isImpossible()) return ValueFlow::Value::unknown(); if (lhs.isImpossible() || rhs.isImpossible()) { // noninvertible if (contains({"%", "/", "&", "|"}, op)) return ValueFlow::Value::unknown(); result.setImpossible(); } if (isNumericValue(lhs) && isNumericValue(rhs)) { if (lhs.isFloatValue() || rhs.isFloatValue()) { result.valueType = ValueFlow::Value::ValueType::FLOAT; bool error = false; result.floatValue = calculate(op, asFloat(lhs), asFloat(rhs), &error); if (error) return ValueFlow::Value::unknown(); return result; } } result.valueType = ValueFlow::Value::ValueType::INT; if (op == "+") { if (lhs.isIteratorValue()) result.valueType = lhs.valueType; else if (rhs.isIteratorValue()) result.valueType = rhs.valueType; } else if (lhs.valueType != rhs.valueType) { return ValueFlow::Value::unknown(); } bool error = false; result.intvalue = calculate(op, lhs.intvalue, rhs.intvalue, &error); if (error) return ValueFlow::Value::unknown(); if (result.isImpossible()) { if ((result.intvalue == 0 && op == "!=") || (result.intvalue != 0 && op == "==")) { result.setPossible(); result.intvalue = !result.intvalue; } } return result; } static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings = nullptr) { ValueFlow::Value unknown = ValueFlow::Value::unknown(); const ValueFlow::Value* value = nullptr; if (!expr) return unknown; else if (expr->hasKnownIntValue() && !expr->isAssignmentOp()) { return expr->values().front(); } else if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) || (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) || (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) || (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) { return *value; } else if (expr->isNumber()) { if (MathLib::isFloat(expr->str())) return unknown; return ValueFlow::Value{MathLib::toLongNumber(expr->str())}; } else if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { const Token* containerTok = expr->tokAt(-2)->astOperand1(); Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); if (yield == Library::Container::Yield::SIZE) { ValueFlow::Value v = execute(containerTok, pm); if (!v.isContainerSizeValue()) return unknown; v.valueType = ValueFlow::Value::ValueType::INT; return v; } else if (yield == Library::Container::Yield::EMPTY) { ValueFlow::Value v = execute(containerTok, pm); if (!v.isContainerSizeValue()) return unknown; if (v.isImpossible() && v.intvalue == 0) return ValueFlow::Value{0}; else if (!v.isImpossible()) return ValueFlow::Value{v.intvalue == 0}; } } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && expr->astOperand1()->exprId() > 0) { ValueFlow::Value rhs = execute(expr->astOperand2(), pm); if (rhs.isUninitValue()) return unknown; if (expr->str() != "=") { if (!pm.hasValue(expr->astOperand1()->exprId())) return unknown; ValueFlow::Value& lhs = pm.values.at(expr->astOperand1()->exprId()); rhs = evaluate(removeAssign(expr->str()), lhs, rhs); if (lhs.isIntValue()) ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1)); else if (lhs.isFloatValue()) ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1)); else return unknown; return lhs; } else { pm.values[expr->astOperand1()->exprId()] = rhs; return rhs; } } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { ValueFlow::Value lhs = execute(expr->astOperand1(), pm); if (!lhs.isIntValue()) return unknown; if (lhs.intvalue == 0) return lhs; return execute(expr->astOperand2(), pm); } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) { ValueFlow::Value lhs = execute(expr->astOperand1(), pm); if (!lhs.isIntValue()) return unknown; if (lhs.intvalue != 0) return lhs; return execute(expr->astOperand2(), pm); } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { execute(expr->astOperand1(), pm); return execute(expr->astOperand2(), pm); } else if (Token::Match(expr, "++|--") && expr->astOperand1() && expr->astOperand1()->exprId() != 0) { if (!pm.hasValue(expr->astOperand1()->exprId())) return unknown; ValueFlow::Value& lhs = pm.values.at(expr->astOperand1()->exprId()); if (!lhs.isIntValue()) return unknown; // overflow if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1())) return unknown; if (expr->str() == "++") lhs.intvalue++; else lhs.intvalue--; return lhs; } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { const Token *tokvalue = nullptr; if (!pm.getTokValue(expr->astOperand1()->exprId(), &tokvalue)) { auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(), expr->astOperand1()->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue)); if (tokvalue_it == expr->astOperand1()->values().end()) { return unknown; } tokvalue = tokvalue_it->tokvalue; } if (!tokvalue || !tokvalue->isLiteral()) { return unknown; } const std::string strValue = tokvalue->strValue(); ValueFlow::Value rhs = execute(expr->astOperand2(), pm); if (!rhs.isIntValue()) return unknown; MathLib::bigint index = rhs.intvalue; if (index >= 0 && index < strValue.size()) return ValueFlow::Value{strValue[index]}; else if (index == strValue.size()) return ValueFlow::Value{}; } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { ValueFlow::Value lhs = execute(expr->astOperand1(), pm); ValueFlow::Value rhs = execute(expr->astOperand2(), pm); if (!lhs.isUninitValue() && !rhs.isUninitValue()) return evaluate(expr->str(), lhs, rhs); if (expr->isComparisonOp()) { if (rhs.isIntValue()) { std::vector result = infer(makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs}); if (result.empty() || !result.front().isKnown()) return unknown; return result.front(); } else if (lhs.isIntValue()) { std::vector result = infer(makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values()); if (result.empty() || !result.front().isKnown()) return unknown; return result.front(); } } } // Unary ops else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) { ValueFlow::Value lhs = execute(expr->astOperand1(), pm); if (!lhs.isIntValue()) return unknown; if (expr->str() == "!") lhs.intvalue = !lhs.intvalue; if (expr->str() == "-") lhs.intvalue = -lhs.intvalue; return lhs; } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { ValueFlow::Value cond = execute(expr->astOperand1(), pm); if (!cond.isIntValue()) return unknown; const Token* child = expr->astOperand2(); if (cond.intvalue == 0) return execute(child->astOperand2(), pm); else return execute(child->astOperand1(), pm); } else if (expr->str() == "(" && expr->isCast()) { if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) return execute(expr->astOperand2(), pm); else return execute(expr->astOperand1(), pm); } if (expr->exprId() > 0 && pm.hasValue(expr->exprId())) { ValueFlow::Value result = pm.values.at(expr->exprId()); if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) { result.intvalue = !result.intvalue; result.setKnown(); } return result; } if (Token::Match(expr->previous(), ">|%name% {|(")) { visitAstNodes(expr->astOperand2(), [&](const Token* child) { if (child->exprId() > 0 && pm.hasValue(child->exprId())) { ValueFlow::Value& v = pm.values.at(child->exprId()); if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { if (isContainerSizeChanged(child, settings)) v = unknown; } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { if (isVariableChanged(child, v.indirect, settings, true)) v = unknown; } } return ChildrenToVisit::op1_and_op2; }); } return unknown; } void execute(const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result, bool* error, const Settings* settings) { ValueFlow::Value v = execute(expr, *programMemory, settings); if (!v.isIntValue() || v.isImpossible()) { if (error) *error = true; } else if (result) *result = v.intvalue; } cppcheck-2.7/lib/programmemory.h000066400000000000000000000072121417746362400170010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef GUARD_PROGRAMMEMORY_H #define GUARD_PROGRAMMEMORY_H #include "mathlib.h" #include "utils.h" #include "valueflow.h" // needed for alias #include #include class Token; class Settings; struct ProgramMemory { using Map = std::unordered_map; Map values; void setValue(nonneg int exprid, const ValueFlow::Value& value); const ValueFlow::Value* getValue(nonneg int exprid, bool impossible = false) const; bool getIntValue(nonneg int exprid, MathLib::bigint* result) const; void setIntValue(nonneg int exprid, MathLib::bigint value, bool impossible = false); bool getContainerSizeValue(nonneg int exprid, MathLib::bigint* result) const; bool getContainerEmptyValue(nonneg int exprid, MathLib::bigint* result) const; void setContainerSizeValue(nonneg int exprid, MathLib::bigint value, bool isEqual = true); void setUnknown(nonneg int exprid); bool getTokValue(nonneg int exprid, const Token** result) const; bool hasValue(nonneg int exprid); void swap(ProgramMemory &pm); void clear(); bool empty() const; void replace(const ProgramMemory &pm); void insert(const ProgramMemory &pm); }; void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then); struct ProgramMemoryState { ProgramMemory state; std::map origins; const Settings* settings; explicit ProgramMemoryState(const Settings* s); void insert(const ProgramMemory &pm, const Token* origin = nullptr); void replace(const ProgramMemory &pm, const Token* origin = nullptr); void addState(const Token* tok, const ProgramMemory::Map& vars); void assume(const Token* tok, bool b, bool isEmpty = false); void removeModifiedVars(const Token* tok); ProgramMemory get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const; }; void execute(const Token* expr, ProgramMemory* const programMemory, MathLib::bigint* result, bool* error, const Settings* settings = nullptr); /** * Is condition always false when variable has given value? * \param condition top ast token in condition * \param programMemory program memory */ bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings = nullptr); /** * Is condition always true when variable has given value? * \param condition top ast token in condition * \param programMemory program memory */ bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings = nullptr); /** * Get program memory by looking backwards from given token. */ ProgramMemory getProgramMemory(const Token* tok, nonneg int exprid, const ValueFlow::Value& value, const Settings *settings); ProgramMemory getProgramMemory(const Token *tok, const ProgramMemory::Map& vars); #endif cppcheck-2.7/lib/reverseanalyzer.cpp000066400000000000000000000375271417746362400176710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "reverseanalyzer.h" #include "analyzer.h" #include "astutils.h" #include "errortypes.h" #include "forwardanalyzer.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "valueptr.h" #include #include #include #include #include #include struct ReverseTraversal { ReverseTraversal(const ValuePtr& analyzer, const Settings* settings) : analyzer(analyzer), settings(settings) {} ValuePtr analyzer; const Settings* settings; std::pair evalCond(const Token* tok) { std::vector result = analyzer->evaluate(tok); // TODO: We should convert to bool bool checkThen = std::any_of(result.begin(), result.end(), [](int x) { return x == 1; }); bool checkElse = std::any_of(result.begin(), result.end(), [](int x) { return x == 0; }); return std::make_pair(checkThen, checkElse); } bool update(Token* tok) { Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse); if (!action.isNone()) analyzer->update(tok, action, Analyzer::Direction::Reverse); if (action.isInconclusive() && !analyzer->lowerToInconclusive()) return false; if (action.isInvalid()) return false; return true; } Token* getParentFunction(Token* tok) { if (!tok) return nullptr; if (!tok->astParent()) return nullptr; int argn = -1; if (Token* ftok = getTokenArgumentFunction(tok, argn)) { while (!Token::Match(ftok, "(|{")) { if (!ftok) return nullptr; if (ftok->index() >= tok->index()) return nullptr; if (ftok->link()) ftok = ftok->link()->next(); else ftok = ftok->next(); } if (ftok == tok) return nullptr; return ftok; } return nullptr; } Token* getTopFunction(Token* tok) { if (!tok) return nullptr; if (!tok->astParent()) return tok; Token* parent = tok; Token* top = tok; while ((parent = getParentFunction(parent))) top = parent; return top; } bool updateRecursive(Token* start) { bool continueB = true; visitAstNodes(start, [&](Token* tok) { const Token* parent = tok->astParent(); while (Token::simpleMatch(parent, ":")) parent = parent->astParent(); if (isUnevaluated(tok) || isDeadCode(tok, parent)) return ChildrenToVisit::none; continueB &= update(tok); if (continueB) return ChildrenToVisit::op1_and_op2; else return ChildrenToVisit::done; }); return continueB; } Analyzer::Action analyzeRecursive(const Token* start) { Analyzer::Action result = Analyzer::Action::None; visitAstNodes(start, [&](const Token* tok) { result |= analyzer->analyze(tok, Analyzer::Direction::Reverse); if (result.isModified()) return ChildrenToVisit::done; return ChildrenToVisit::op1_and_op2; }); return result; } Analyzer::Action analyzeRange(const Token* start, const Token* end) { Analyzer::Action result = Analyzer::Action::None; for (const Token* tok = start; tok && tok != end; tok = tok->next()) { Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse); if (action.isModified()) return action; result |= action; } return result; } Token* isDeadCode(Token* tok, const Token* end = nullptr) { int opSide = 0; for (; tok && tok->astParent(); tok = tok->astParent()) { if (tok == end) break; Token* parent = tok->astParent(); if (Token::simpleMatch(parent, ":")) { if (astIsLHS(tok)) opSide = 1; else if (astIsRHS(tok)) opSide = 2; else opSide = 0; } if (tok != parent->astOperand2()) continue; if (Token::simpleMatch(parent, ":")) parent = parent->astParent(); if (!Token::Match(parent, "%oror%|&&|?")) continue; Token* condTok = parent->astOperand1(); if (!condTok) continue; bool checkThen, checkElse; std::tie(checkThen, checkElse) = evalCond(condTok); if (parent->str() == "?") { if (checkElse && opSide == 1) return parent; if (checkThen && opSide == 2) return parent; } if (!checkThen && parent->str() == "&&") return parent; if (!checkElse && parent->str() == "||") return parent; } return nullptr; } void traverse(Token* start, const Token* end = nullptr) { if (start == end) return; std::size_t i = start->index(); for (Token* tok = start->previous(); succeeds(tok, end); tok = tok->previous()) { if (tok->index() >= i) throw InternalError(tok, "Cyclic reverse analysis."); i = tok->index(); if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction || tok->scope()->type == Scope::ScopeType::eLambda))) { const Function* f = tok->scope()->function; if (f && f->isConstructor()) { if (const Token* initList = f->constructorMemberInitialization()) traverse(tok->previous(), tok->tokAt(initList->index() - tok->index())); } break; } if (Token::Match(tok, "return|break|continue")) break; if (Token::Match(tok, "%name% :")) break; if (Token::simpleMatch(tok, ":")) continue; // Evaluate LHS of assignment before RHS if (Token* assignTok = assignExpr(tok)) { // If assignTok has broken ast then stop if (!assignTok->astOperand1() || !assignTok->astOperand2()) break; Token* assignTop = assignTok; bool continueB = true; while (assignTop->isAssignmentOp()) { if (!Token::Match(assignTop->astOperand1(), "%assign%")) { continueB &= updateRecursive(assignTop->astOperand1()); } if (!assignTop->astParent()) break; assignTop = assignTop->astParent(); } // Is assignment in dead code if (Token* parent = isDeadCode(assignTok)) { tok = parent; continue; } // Simple assign if (assignTok->astParent() == assignTop || assignTok == assignTop) { Analyzer::Action rhsAction = analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse); Analyzer::Action lhsAction = analyzer->analyze(assignTok->astOperand1(), Analyzer::Direction::Reverse); // Assignment from if (rhsAction.isRead() && !lhsAction.isInvalid() && assignTok->astOperand1()->exprId() > 0) { const std::string info = "Assignment from '" + assignTok->expressionString() + "'"; ValuePtr a = analyzer->reanalyze(assignTok->astOperand1(), info); if (a) { valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()), assignTok->astOperand2()->scope()->bodyEnd, a, settings); } // Assignment to } else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownIntValue() && assignTok->astOperand2()->exprId() > 0 && isConstExpression(assignTok->astOperand2(), settings->library, true, true)) { const std::string info = "Assignment to '" + assignTok->expressionString() + "'"; ValuePtr a = analyzer->reanalyze(assignTok->astOperand2(), info); if (a) { valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()), assignTok->astOperand2()->scope()->bodyEnd, a, settings); valueFlowGenericReverse(assignTok->astOperand1()->previous(), end, a, settings); } } } if (!continueB) break; if (!updateRecursive(assignTop->astOperand2())) break; tok = previousBeforeAstLeftmostLeaf(assignTop)->next(); continue; } if (tok->str() == ")" && !isUnevaluated(tok)) { if (Token* top = getTopFunction(tok->link())) { if (!updateRecursive(top)) break; Token* next = previousBeforeAstLeftmostLeaf(top); if (next && precedes(next, tok)) tok = next->next(); } continue; } if (tok->str() == "}") { Token* condTok = getCondTokFromEnd(tok); if (!condTok) break; Analyzer::Action condAction = analyzeRecursive(condTok); const bool inLoop = condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while ("); // Evaluate condition of for and while loops first if (inLoop) { if (condAction.isModified()) break; valueFlowGenericForward(condTok, analyzer, settings); } Token* thenEnd; const bool hasElse = Token::simpleMatch(tok->link()->tokAt(-2), "} else {"); if (hasElse) { thenEnd = tok->link()->tokAt(-2); } else { thenEnd = tok; } Analyzer::Action thenAction = analyzeRange(thenEnd->link(), thenEnd); Analyzer::Action elseAction = Analyzer::Action::None; if (hasElse) { elseAction = analyzeRange(tok->link(), tok); } if (thenAction.isModified() && inLoop) break; else if (thenAction.isModified() && !elseAction.isModified()) analyzer->assume(condTok, hasElse); else if (elseAction.isModified() && !thenAction.isModified()) analyzer->assume(condTok, !hasElse); // Bail if one of the branches are read to avoid FPs due to over constraints else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() || elseAction.isRead()) break; if (thenAction.isInvalid() || elseAction.isInvalid()) break; if (!thenAction.isModified() && !elseAction.isModified()) valueFlowGenericForward(condTok, analyzer, settings); else if (condAction.isRead()) break; // If the condition modifies the variable then bail if (condAction.isModified()) break; tok = condTok->astTop()->previous(); continue; } if (tok->str() == "{") { if (tok->previous() && (Token::simpleMatch(tok->previous(), "do") || (tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "for|while (")))) { Analyzer::Action action = analyzeRange(tok, tok->link()); if (action.isModified()) break; } Token* condTok = getCondTokFromEnd(tok->link()); if (condTok) { Analyzer::Result r = valueFlowGenericForward(condTok, analyzer, settings); if (r.action.isModified()) break; } if (Token::simpleMatch(tok->tokAt(-2), "} else {")) tok = tok->linkAt(-2); if (Token::simpleMatch(tok->previous(), ") {")) tok = tok->previous()->link(); continue; } if (Token* next = isUnevaluated(tok)) { tok = next; continue; } if (Token* parent = isDeadCode(tok)) { tok = parent; continue; } if (tok->str() == "case") { const Scope* scope = tok->scope(); while (scope && scope->type != Scope::eSwitch) scope = scope->nestedIn; if (!scope || scope->type != Scope::eSwitch) break; tok = tok->tokAt(scope->bodyStart->index() - tok->index() - 1); continue; } if (!update(tok)) break; } } static Token* assignExpr(Token* tok) { if (Token::Match(tok, ")|}")) tok = tok->link(); while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) { if (tok->astParent()->isAssignmentOp()) return tok->astParent(); tok = tok->astParent(); } return nullptr; } static Token* isUnevaluated(Token* tok) { if (Token::Match(tok, ")|>") && tok->link()) { Token* start = tok->link(); if (Token::Match(start->previous(), "sizeof|decltype (")) return start->previous(); if (Token::simpleMatch(start, "<")) return start; } return nullptr; } }; void valueFlowGenericReverse(Token* start, const ValuePtr& a, const Settings* settings) { ReverseTraversal rt{a, settings}; rt.traverse(start); } void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr& a, const Settings* settings) { ReverseTraversal rt{a, settings}; rt.traverse(start, end); } cppcheck-2.7/lib/reverseanalyzer.h000066400000000000000000000021231417746362400173160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef reverseanalyzerH #define reverseanalyzerH struct Analyzer; class Settings; class Token; template class ValuePtr; void valueFlowGenericReverse(Token* start, const ValuePtr& a, const Settings* settings); void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr& a, const Settings* settings); #endif cppcheck-2.7/lib/settings.cpp000066400000000000000000000133221417746362400162730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "settings.h" #include "path.h" #include "summaries.h" #include "valueflow.h" #include #define PICOJSON_USE_INT64 #include std::atomic Settings::mTerminated; const char Settings::SafeChecks::XmlRootName[] = "safe-checks"; const char Settings::SafeChecks::XmlClasses[] = "class-public"; const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions"; const char Settings::SafeChecks::XmlInternalFunctions[] = "internal-functions"; const char Settings::SafeChecks::XmlExternalVariables[] = "external-variables"; Settings::Settings() : bugHunting(false), bugHuntingCheckFunctionMaxTime(60), checkAllConfigurations(true), checkConfiguration(false), checkHeaders(true), checkLibrary(false), checkUnusedTemplates(true), clang(false), clangExecutable("clang"), clangTidy(false), daca(false), debugBugHunting(false), debugnormal(false), debugSimplified(false), debugtemplate(false), debugwarnings(false), dump(false), enforcedLang(None), exceptionHandling(false), exitCode(0), force(false), inlineSuppressions(false), jobs(1), jointSuppressionReport(false), loadAverage(0), maxConfigs(12), maxCtuDepth(2), maxTemplateRecursion(100), preprocessOnly(false), quiet(false), relativePaths(false), reportProgress(false), showtime(SHOWTIME_MODES::SHOWTIME_NONE), verbose(false), xml(false), xml_version(2) { severity.setEnabled(Severity::error, true); certainty.setEnabled(Certainty::normal, true); } void Settings::loadCppcheckCfg(const std::string &executable) { std::string fileName = Path::getPathFromFilename(executable) + "cppcheck.cfg"; #ifdef FILESDIR if (Path::fileExists(FILESDIR "/cppcheck.cfg")) fileName = FILESDIR "/cppcheck.cfg"; #endif std::ifstream fin(fileName); if (!fin.is_open()) return; picojson::value json; fin >> json; if (!picojson::get_last_error().empty()) return; picojson::object obj = json.get(); if (obj.count("addons") && obj["addons"].is()) { for (const picojson::value &v : obj["addons"].get()) { const std::string &s = v.get(); if (!Path::isAbsolute(s)) addons.push_back(Path::getPathFromFilename(fileName) + s); else addons.push_back(s); } } if (obj.count("suppressions") && obj["suppressions"].is()) { for (const picojson::value &v : obj["suppressions"].get()) nomsg.addSuppressionLine(v.get()); } } std::string Settings::addEnabled(const std::string &str) { // Enable parameters may be comma separated... if (str.find(',') != std::string::npos) { std::string::size_type prevPos = 0; std::string::size_type pos = 0; while ((pos = str.find(',', pos)) != std::string::npos) { if (pos == prevPos) return std::string("--enable parameter is empty"); const std::string errmsg(addEnabled(str.substr(prevPos, pos - prevPos))); if (!errmsg.empty()) return errmsg; ++pos; prevPos = pos; } if (prevPos >= str.length()) return std::string("--enable parameter is empty"); return addEnabled(str.substr(prevPos)); } if (str == "all") { severity.fill(); checks.enable(Checks::missingInclude); checks.enable(Checks::unusedFunction); } else if (str == "warning") { severity.enable(Severity::warning); } else if (str == "style") { severity.enable(Severity::style); } else if (str == "performance") { severity.enable(Severity::performance); } else if (str == "portability") { severity.enable(Severity::portability); } else if (str == "information") { severity.enable(Severity::information); checks.enable(Checks::missingInclude); } else if (str == "unusedFunction") { checks.enable(Checks::unusedFunction); } else if (str == "missingInclude") { checks.enable(Checks::missingInclude); } #ifdef CHECK_INTERNAL else if (str == "internal") { checks.enable(Checks::internalCheck); } #endif else { if (str.empty()) return std::string("cppcheck: --enable parameter is empty"); else return std::string("cppcheck: there is no --enable parameter with the name '" + str + "'"); } return std::string(); } bool Settings::isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck) const { if (!severity.isEnabled(Severity::warning) && (value->condition || value->defaultArg)) return false; if (!certainty.isEnabled(Certainty::inconclusive) && (inconclusiveCheck || value->isInconclusive())) return false; return true; } void Settings::loadSummaries() { Summaries::loadReturn(buildDir, summaryReturn); } cppcheck-2.7/lib/settings.h000066400000000000000000000272051417746362400157450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef settingsH #define settingsH //--------------------------------------------------------------------------- #include "config.h" #include "errortypes.h" #include "importproject.h" #include "library.h" #include "platform.h" #include "standards.h" #include "suppressions.h" #include "timer.h" #include #include #include #include #include #include #include #include namespace ValueFlow { class Value; } /// @addtogroup Core /// @{ template class SimpleEnableGroup { uint32_t mFlags = 0; public: uint32_t intValue() const { return mFlags; } void clear() { mFlags = 0; } void fill() { mFlags = 0xFFFFFFFF; } void setEnabledAll(bool enabled) { if (enabled) fill(); else clear(); } bool isEnabled(T flag) const { return (mFlags & (1U << (uint32_t)flag)) != 0; } void enable(T flag) { mFlags |= (1U << (uint32_t)flag); } void disable(T flag) { mFlags &= ~(1U << (uint32_t)flag); } void setEnabled(T flag, bool enabled) { if (enabled) enable(flag); else disable(flag); } }; /** * @brief This is just a container for general settings so that we don't need * to pass individual values to functions or constructors now or in the * future when we might have even more detailed settings. */ class CPPCHECKLIB Settings : public cppcheck::Platform { private: /** @brief terminate checking */ static std::atomic mTerminated; public: Settings(); void loadCppcheckCfg(const std::string &executable); /** @brief addons, either filename of python/json file or json data */ std::list addons; /** @brief Path to the python interpreter to be used to run addons. */ std::string addonPython; /** @brief Paths used as base for conversion to relative paths. */ std::vector basePaths; /** @brief Bug hunting */ bool bugHunting; /** @brief Max time for bug hunting analysis in seconds, after * timeout the analysis will just stop. */ int bugHuntingCheckFunctionMaxTime; /** Filename for bug hunting report */ std::string bugHuntingReport; /** @brief --cppcheck-build-dir. Always uses / as path separator. No trailing path separator. */ std::string buildDir; /** @brief check all configurations (false if -D or --max-configs is used */ bool checkAllConfigurations; /** Is the 'configuration checking' wanted? */ bool checkConfiguration; /** * Check code in the headers, this is on by default but can * be turned off to save CPU */ bool checkHeaders; /** Check for incomplete info in library files? */ bool checkLibrary; /** @brief List of selected Visual Studio configurations that should be checks */ std::list checkVsConfigs; /** @brief check unknown function return values */ std::set checkUnknownFunctionReturn; /** Check unused/uninstantiated templates */ bool checkUnusedTemplates; /** Use Clang */ bool clang; /** Custom Clang executable */ std::string clangExecutable; /** Use clang-tidy */ bool clangTidy; /** @brief include paths excluded from checking the configuration */ std::set configExcludePaths; /** @brief Are we running from DACA script? */ bool daca; /** @brief Debug bug hunting */ bool debugBugHunting; /** @brief Is --debug-normal given? */ bool debugnormal; /** @brief Is --debug-simplified given? */ bool debugSimplified; /** @brief Is --debug-template given? */ bool debugtemplate; /** @brief Is --debug-warnings given? */ bool debugwarnings; /** @brief Is --dump given? */ bool dump; std::string dumpFile; enum Language { None, C, CPP }; /** @brief Name of the language that is enforced. Empty per default. */ Language enforcedLang; /** @brief Is --exception-handling given */ bool exceptionHandling; // argv[0] std::string exename; /** @brief If errors are found, this value is returned from main(). Default value is 0. */ int exitCode; /** @brief List of --file-filter for analyzing special files */ std::vector fileFilters; /** @brief Force checking the files with "too many" configurations (--force). */ bool force; std::map functionContracts; struct VariableContracts { std::string minValue; std::string maxValue; }; std::map variableContracts; /** @brief List of include paths, e.g. "my/includes/" which should be used for finding include files inside source files. (-I) */ std::list includePaths; /** @brief Is --inline-suppr given? */ bool inlineSuppressions; /** @brief How many processes/threads should do checking at the same time. Default is 1. (-j N) */ unsigned int jobs; /** @brief Collect unmatched suppressions in one run. * This delays the reporting until all files are checked. * It is needed by checks that analyse the whole code base. */ bool jointSuppressionReport; /** @brief --library= */ std::list libraries; /** Library */ Library library; /** @brief Load average value */ int loadAverage; /** @brief Maximum number of configurations to check before bailing. Default is 12. (--max-configs=N) */ int maxConfigs; /** @brief --max-ctu-depth */ int maxCtuDepth; /** @brief max template recursion */ int maxTemplateRecursion; /** @brief suppress exitcode */ Suppressions nofail; /** @brief suppress message (--suppressions) */ Suppressions nomsg; /** @brief write results (--output-file=<file>) */ std::string outputFile; /** @brief plist output (--plist-output=<dir>) */ std::string plistOutput; /** @brief Using -E for debugging purposes */ bool preprocessOnly; ImportProject project; /** @brief Is --quiet given? */ bool quiet; /** @brief Use relative paths in output. */ bool relativePaths; /** @brief --report-progress */ bool reportProgress; /** Rule */ class CPPCHECKLIB Rule { public: Rule() : tokenlist("simple") // use simple tokenlist , id("rule") // default id , severity(Severity::style) { // default severity } std::string tokenlist; std::string pattern; std::string id; std::string summary; Severity::SeverityType severity; }; /** * @brief Extra rules */ std::list rules; /** Do not only check how interface is used. Also check that interface is safe. */ class CPPCHECKLIB SafeChecks { public: SafeChecks() : classes(false), externalFunctions(false), internalFunctions(false), externalVariables(false) {} static const char XmlRootName[]; static const char XmlClasses[]; static const char XmlExternalFunctions[]; static const char XmlInternalFunctions[]; static const char XmlExternalVariables[]; void clear() { classes = externalFunctions = internalFunctions = externalVariables = false; } /** * Public interface of classes * - public function parameters can have any value * - public functions can be called in any order * - public variables can have any value */ bool classes; /** * External functions * - external functions can be called in any order * - function parameters can have any values */ bool externalFunctions; /** * Experimental: assume that internal functions can be used in any way * This is only available in the GUI. */ bool internalFunctions; /** * Global variables that can be modified outside the TU. * - Such variable can have "any" value */ bool externalVariables; }; SafeChecks safeChecks; SimpleEnableGroup severity; SimpleEnableGroup certainty; SimpleEnableGroup checks; /** @brief show timing information (--showtime=file|summary|top5) */ SHOWTIME_MODES showtime; /** Struct contains standards settings */ Standards standards; /** @brief The output format in which the errors are printed in text mode, e.g. "{severity} {file}:{line} {message} {id}" */ std::string templateFormat; /** @brief The output format in which the error locations are printed in * text mode, e.g. "{file}:{line} {info}" */ std::string templateLocation; /** @brief defines given by the user */ std::string userDefines; /** @brief undefines given by the user */ std::set userUndefs; /** @brief forced includes given by the user */ std::list userIncludes; /** @brief Is --verbose given? */ bool verbose; /** @brief write XML results (--xml) */ bool xml; /** @brief XML version (--xml-version=..) */ int xml_version; /** * @brief return true if a included file is to be excluded in Preprocessor::getConfigs * @return true for the file to be excluded. */ bool configurationExcluded(const std::string &file) const { for (const std::string & configExcludePath : configExcludePaths) { if (file.length()>=configExcludePath.length() && file.compare(0,configExcludePath.length(),configExcludePath)==0) { return true; } } return false; } /** * @brief Enable extra checks by id. See isEnabled() * @param str single id or list of id values to be enabled * or empty string to enable all. e.g. "style,possibleError" * @return error message. empty upon success */ std::string addEnabled(const std::string &str); /** * @brief Returns true if given value can be shown * @return true if the value can be shown */ bool isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck=false) const; /** Is posix library specified? */ bool posix() const { return std::find(libraries.begin(), libraries.end(), "posix") != libraries.end(); } /** @brief Request termination of checking */ static void terminate(bool t = true) { Settings::mTerminated = t; } /** @brief termination requested? */ static bool terminated() { return Settings::mTerminated; } std::set summaryReturn; void loadSummaries(); }; /// @} //--------------------------------------------------------------------------- #endif // settingsH cppcheck-2.7/lib/standards.h000066400000000000000000000062151417746362400160660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef standardsH #define standardsH //--------------------------------------------------------------------------- #include /// @addtogroup Core /// @{ /** * @brief This is just a container for standards settings. * This struct contains all possible standards that cppcheck recognize. */ struct Standards { /** C code C89/C99/C11 standard */ enum cstd_t { C89, C99, C11, CLatest=C11 } c; /** C++ code standard */ enum cppstd_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPPLatest=CPP20 } cpp; /** --std value given on command line */ std::string stdValue; /** This constructor clear all the variables **/ Standards() : c(C11), cpp(CPPLatest) {} bool setC(const std::string& str) { stdValue = str; if (str == "c89" || str == "C89") { c = C89; return true; } if (str == "c99" || str == "C99") { c = C99; return true; } if (str == "c11" || str == "C11") { c = C11; return true; } return false; } const std::string getC() const { switch (c) { case C89: return "c89"; case C99: return "c99"; case C11: return "c11"; } return ""; } bool setCPP(const std::string& str) { stdValue = str; if (str == "c++03" || str == "C++03") { cpp = CPP03; return true; } if (str == "c++11" || str == "C++11") { cpp = CPP11; return true; } if (str == "c++14" || str == "C++14") { cpp = CPP14; return true; } if (str == "c++17" || str == "C++17") { cpp = CPP17; return true; } if (str == "c++20" || str == "C++20") { cpp = CPP20; return true; } return false; } std::string getCPP() const { switch (cpp) { case CPP03: return "c++03"; case CPP11: return "c++11"; case CPP14: return "c++14"; case CPP17: return "c++17"; case CPP20: return "c++20"; } return ""; } }; /// @} //--------------------------------------------------------------------------- #endif // standardsH cppcheck-2.7/lib/summaries.cpp000066400000000000000000000152461417746362400164470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "summaries.h" #include "analyzerinfo.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include std::string Summaries::create(const Tokenizer *tokenizer, const std::string &cfg) { const SymbolDatabase *symbolDatabase = tokenizer->getSymbolDatabase(); const Settings *settings = tokenizer->getSettings(); std::ostringstream ostr; for (const Scope *scope : symbolDatabase->functionScopes) { const Function *f = scope->function; if (!f) continue; // Summarize function std::set noreturn; std::set globalVars; std::set calledFunctions; for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->variable() && tok->variable()->isGlobal()) globalVars.insert(tok->variable()->name()); if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { calledFunctions.insert(tok->str()); if (Token::simpleMatch(tok->linkAt(1), ") ; }")) noreturn.insert(tok->str()); } } // Write summary for function auto join = [](const std::set &data) -> std::string { std::string ret; const char *sep = ""; for (std::string d: data) { ret += sep + d; sep = ","; } return ret; }; ostr << f->name(); if (!globalVars.empty()) ostr << " global:[" << join(globalVars) << "]"; if (!calledFunctions.empty()) ostr << " call:[" << join(calledFunctions) << "]"; if (!noreturn.empty()) ostr << " noreturn:[" << join(noreturn) << "]"; ostr << std::endl; } if (!settings->buildDir.empty()) { std::string filename = AnalyzerInformation::getAnalyzerInfoFile(settings->buildDir, tokenizer->list.getSourceFilePath(), cfg); std::string::size_type pos = filename.rfind(".a"); if (pos != std::string::npos) { filename[pos+1] = 's'; std::ofstream fout(filename); fout << ostr.str(); } } return ostr.str(); } static std::vector getSummaryFiles(const std::string &filename) { std::vector ret; std::ifstream fin(filename); if (!fin.is_open()) return ret; std::string line; while (std::getline(fin, line)) { std::string::size_type dotA = line.find(".a"); std::string::size_type colon = line.find(":"); if (colon > line.size() || dotA > colon) continue; std::string f = line.substr(0,colon); f[dotA + 1] = 's'; ret.push_back(f); } return ret; } static std::vector getSummaryData(const std::string &line, const std::string &data) { std::vector ret; const std::string::size_type start = line.find(" " + data + ":["); if (start == std::string::npos) return ret; const std::string::size_type end = line.find("]", start); if (end >= line.size()) return ret; std::string::size_type pos1 = start + 3 + data.size(); while (pos1 < end) { std::string::size_type pos2 = line.find_first_of(",]",pos1); ret.push_back(line.substr(pos1, pos2-pos1-1)); pos1 = pos2 + 1; } return ret; } static void removeFunctionCalls(const std::string& calledFunction, std::map> &functionCalledBy, std::map> &functionCalls, std::vector &add) { std::vector calledBy = functionCalledBy[calledFunction]; functionCalledBy.erase(calledFunction); for (const std::string &c: calledBy) { std::vector &calls = functionCalls[c]; calls.erase(std::remove(calls.begin(), calls.end(), calledFunction), calls.end()); if (calls.empty()) { add.push_back(calledFunction); removeFunctionCalls(c, functionCalledBy, functionCalls, add); } } } void Summaries::loadReturn(const std::string &buildDir, std::set &summaryReturn) { if (buildDir.empty()) return; std::vector return1; std::map> functionCalls; std::map> functionCalledBy; // extract "functionNoreturn" and "functionCalledBy" from summaries std::vector summaryFiles = getSummaryFiles(buildDir + "/files.txt"); for (const std::string &filename: summaryFiles) { std::ifstream fin(buildDir + '/' + filename); if (!fin.is_open()) continue; std::string line; while (std::getline(fin, line)) { // Get function name const std::string::size_type pos1 = 0; const std::string::size_type pos2 = line.find(" ", pos1); const std::string functionName = (pos2 == std::string::npos) ? line : line.substr(0, pos2); std::vector call = getSummaryData(line, "call"); functionCalls[functionName] = call; if (call.empty()) return1.push_back(functionName); else { for (const std::string &c: call) { functionCalledBy[c].push_back(functionName); } } } } summaryReturn.insert(return1.cbegin(), return1.cend()); // recursively set "summaryNoreturn" for (const std::string &f: return1) { std::vector return2; removeFunctionCalls(f, functionCalledBy, functionCalls, return2); summaryReturn.insert(return2.cbegin(), return2.cend()); } } cppcheck-2.7/lib/summaries.h000066400000000000000000000025631417746362400161120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef summariesH #define summariesH //--------------------------------------------------------------------------- #include "config.h" #include #include class Tokenizer; namespace Summaries { CPPCHECKLIB std::string create(const Tokenizer *tokenizer, const std::string &cfg); CPPCHECKLIB void loadReturn(const std::string &buildDir, std::set &summaryReturn); } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- cppcheck-2.7/lib/suppressions.cpp000066400000000000000000000362411417746362400172150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "suppressions.h" #include "errorlogger.h" #include "mathlib.h" #include "path.h" #include "utils.h" #include #include // std::isdigit, std::isalnum, etc #include #include #include // std::bind, std::placeholders #include static bool isAcceptedErrorIdChar(char c) { switch (c) { case '_': case '-': case '.': return true; default: return std::isalnum(c); } } std::string Suppressions::parseFile(std::istream &istr) { // Change '\r' to '\n' in the istr std::string filedata; std::string line; while (std::getline(istr, line)) filedata += line + "\n"; std::replace(filedata.begin(), filedata.end(), '\r', '\n'); // Parse filedata.. std::istringstream istr2(filedata); while (std::getline(istr2, line)) { // Skip empty lines if (line.empty()) continue; // Skip comments if (line.length() > 1 && line[0] == '#') continue; if (line.length() >= 2 && line[0] == '/' && line[1] == '/') continue; const std::string errmsg(addSuppressionLine(line)); if (!errmsg.empty()) return errmsg; } return ""; } std::string Suppressions::parseXmlFile(const char *filename) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(filename); if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) return "File not found"; if (error != tinyxml2::XML_SUCCESS) return "Failed to parse XML file"; const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); for (const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "suppress") != 0) return "Invalid suppression xml file format, expected element but got a \"" + std::string(e->Name()) + '\"'; Suppression s; for (const tinyxml2::XMLElement * e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { const char *text = e2->GetText() ? e2->GetText() : ""; if (std::strcmp(e2->Name(), "id") == 0) s.errorId = text; else if (std::strcmp(e2->Name(), "fileName") == 0) s.fileName = text; else if (std::strcmp(e2->Name(), "lineNumber") == 0) s.lineNumber = std::atoi(text); else if (std::strcmp(e2->Name(), "symbolName") == 0) s.symbolName = text; else if (*text && std::strcmp(e2->Name(), "hash") == 0) std::istringstream(text) >> s.hash; else return "Unknown suppression element \"" + std::string(e2->Name()) + "\", expected id/fileName/lineNumber/symbolName/hash"; } const std::string err = addSuppression(s); if (!err.empty()) return err; } return ""; } std::vector Suppressions::parseMultiSuppressComment(const std::string &comment, std::string *errorMessage) { std::vector suppressions; // If this function is called we assume that comment starts with "cppcheck-suppress[". const std::string::size_type start_position = comment.find("["); const std::string::size_type end_position = comment.find("]", start_position); if (end_position == std::string::npos) { if (errorMessage && errorMessage->empty()) *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; return suppressions; } // parse all suppressions for (std::string::size_type pos = start_position; pos < end_position;) { const std::string::size_type pos1 = pos + 1; pos = comment.find(",", pos1); const std::string::size_type pos2 = (pos < end_position) ? pos : end_position; if (pos1 == pos2) continue; Suppression s; std::istringstream iss(comment.substr(pos1, pos2-pos1)); iss >> s.errorId; if (!iss) { if (errorMessage && errorMessage->empty()) *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; suppressions.clear(); return suppressions; } while (iss) { std::string word; iss >> word; if (!iss) break; if (word.find_first_not_of("+-*/%#;") == std::string::npos) break; if (word.compare(0, 11, "symbolName=") == 0) { s.symbolName = word.substr(11); } else { if (errorMessage && errorMessage->empty()) *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; suppressions.clear(); return suppressions; } } suppressions.push_back(s); } return suppressions; } std::string Suppressions::addSuppressionLine(const std::string &line) { std::istringstream lineStream; Suppressions::Suppression suppression; // Strip any end of line comments std::string::size_type endpos = std::min(line.find("#"), line.find("//")); if (endpos != std::string::npos) { while (endpos > 0 && std::isspace(line[endpos-1])) { endpos--; } lineStream.str(line.substr(0, endpos)); } else { lineStream.str(line); } if (std::getline(lineStream, suppression.errorId, ':')) { if (std::getline(lineStream, suppression.fileName)) { // If there is not a dot after the last colon in "file" then // the colon is a separator and the contents after the colon // is a line number.. // Get position of last colon const std::string::size_type pos = suppression.fileName.rfind(':'); // if a colon is found and there is no dot after it.. if (pos != std::string::npos && suppression.fileName.find('.', pos) == std::string::npos) { // Try to parse out the line number try { std::istringstream istr1(suppression.fileName.substr(pos+1)); istr1 >> suppression.lineNumber; } catch (...) { suppression.lineNumber = Suppressions::Suppression::NO_LINE; } if (suppression.lineNumber != Suppressions::Suppression::NO_LINE) { suppression.fileName.erase(pos); } } } } suppression.fileName = Path::simplifyPath(suppression.fileName); return addSuppression(suppression); } std::string Suppressions::addSuppression(const Suppressions::Suppression &suppression) { // Check if suppression is already in list auto foundSuppression = std::find_if(mSuppressions.begin(), mSuppressions.end(), std::bind(&Suppression::isSameParameters, &suppression, std::placeholders::_1)); if (foundSuppression != mSuppressions.end()) { // Update matched state of existing global suppression if (!suppression.isLocal() && suppression.matched) foundSuppression->matched = suppression.matched; return ""; } // Check that errorId is valid.. if (suppression.errorId.empty() && suppression.hash == 0) return "Failed to add suppression. No id."; if (suppression.errorId != "*") { for (std::string::size_type pos = 0; pos < suppression.errorId.length(); ++pos) { if (suppression.errorId[pos] < 0 || !isAcceptedErrorIdChar(suppression.errorId[pos])) { return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\""; } if (pos == 0 && std::isdigit(suppression.errorId[pos])) { return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\""; } } } if (!isValidGlobPattern(suppression.errorId)) return "Failed to add suppression. Invalid glob pattern '" + suppression.errorId + "'."; if (!isValidGlobPattern(suppression.fileName)) return "Failed to add suppression. Invalid glob pattern '" + suppression.fileName + "'."; mSuppressions.push_back(suppression); return ""; } std::string Suppressions::addSuppressions(const std::list &suppressions) { for (const auto &newSuppression : suppressions) { auto errmsg = addSuppression(newSuppression); if (errmsg != "") return errmsg; } return ""; } void Suppressions::ErrorMessage::setFileName(const std::string &s) { mFileName = Path::simplifyPath(s); } bool Suppressions::Suppression::parseComment(std::string comment, std::string *errorMessage) { if (comment.size() < 2) return false; if (comment.find(';') != std::string::npos) comment.erase(comment.find(';')); if (comment.find("//", 2) != std::string::npos) comment.erase(comment.find("//",2)); if (comment.compare(comment.size() - 2, 2, "*/") == 0) comment.erase(comment.size() - 2, 2); std::istringstream iss(comment.substr(2)); std::string word; iss >> word; if (word != "cppcheck-suppress") return false; iss >> errorId; if (!iss) return false; while (iss) { iss >> word; if (!iss) break; if (word.find_first_not_of("+-*/%#;") == std::string::npos) break; if (word.compare(0,11,"symbolName=")==0) symbolName = word.substr(11); else if (errorMessage && errorMessage->empty()) *errorMessage = "Bad suppression attribute '" + word + "'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym"; } return true; } bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &errmsg) const { if (hash > 0 && hash != errmsg.hash) return false; if (!errorId.empty() && !matchglob(errorId, errmsg.errorId)) return false; if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) return false; if (lineNumber != NO_LINE && lineNumber != errmsg.lineNumber) { if (!thisAndNextLine || lineNumber + 1 != errmsg.lineNumber) return false; } if (!symbolName.empty()) { for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) { const std::string::size_type pos2 = errmsg.symbolNames.find('\n',pos); std::string symname; if (pos2 == std::string::npos) { symname = errmsg.symbolNames.substr(pos); pos = pos2; } else { symname = errmsg.symbolNames.substr(pos,pos2-pos); pos = pos2+1; } if (matchglob(symbolName, symname)) return true; } return false; } return true; } bool Suppressions::Suppression::isMatch(const Suppressions::ErrorMessage &errmsg) { if (!isSuppressed(errmsg)) return false; matched = true; return true; } std::string Suppressions::Suppression::getText() const { std::string ret; if (!errorId.empty()) ret = errorId; if (!fileName.empty()) ret += " fileName=" + fileName; if (lineNumber != NO_LINE) ret += " lineNumber=" + MathLib::toString(lineNumber); if (!symbolName.empty()) ret += " symbolName=" + symbolName; if (hash > 0) ret += " hash=" + MathLib::toString(hash); if (ret.compare(0,1," ")==0) return ret.substr(1); return ret; } bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg) { const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression"); for (Suppression &s : mSuppressions) { if (unmatchedSuppression && s.errorId != errmsg.errorId) continue; if (s.isMatch(errmsg)) return true; } return false; } bool Suppressions::isSuppressedLocal(const Suppressions::ErrorMessage &errmsg) { const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression"); for (Suppression &s : mSuppressions) { if (!s.isLocal()) continue; if (unmatchedSuppression && s.errorId != errmsg.errorId) continue; if (s.isMatch(errmsg)) return true; } return false; } void Suppressions::dump(std::ostream & out) const { out << " " << std::endl; for (const Suppression &suppression : mSuppressions) { out << " 0) out << " hash=\"" << suppression.hash << '\"'; out << " />" << std::endl; } out << " " << std::endl; } std::list Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const { std::string tmpFile = Path::simplifyPath(file); std::list result; for (const Suppression &s : mSuppressions) { if (s.matched) continue; if (s.hash > 0) continue; if (!unusedFunctionChecking && s.errorId == "unusedFunction") continue; if (tmpFile.empty() || !s.isLocal() || s.fileName != tmpFile) continue; result.push_back(s); } return result; } std::list Suppressions::getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const { std::list result; for (const Suppression &s : mSuppressions) { if (s.matched) continue; if (s.hash > 0) continue; if (!unusedFunctionChecking && s.errorId == "unusedFunction") continue; if (s.isLocal()) continue; result.push_back(s); } return result; } const std::list &Suppressions::getSuppressions() const { return mSuppressions; } cppcheck-2.7/lib/suppressions.h000066400000000000000000000164571417746362400166710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef suppressionsH #define suppressionsH //--------------------------------------------------------------------------- #include "config.h" #include "errortypes.h" #include #include #include #include #include /// @addtogroup Core /// @{ /** @brief class for handling suppressions */ class CPPCHECKLIB Suppressions { public: struct CPPCHECKLIB ErrorMessage { std::size_t hash; std::string errorId; void setFileName(const std::string &s); const std::string &getFileName() const { return mFileName; } int lineNumber; Certainty::CertaintyLevel certainty; std::string symbolNames; private: std::string mFileName; }; struct CPPCHECKLIB Suppression { Suppression() : lineNumber(NO_LINE), hash(0), thisAndNextLine(false), matched(false) {} Suppression(const Suppression &other) { *this = other; } Suppression(const std::string &id, const std::string &file, int line=NO_LINE) : errorId(id), fileName(file), lineNumber(line), hash(0), thisAndNextLine(false), matched(false) {} Suppression & operator=(const Suppression &other) { errorId = other.errorId; fileName = other.fileName; lineNumber = other.lineNumber; symbolName = other.symbolName; hash = other.hash; thisAndNextLine = other.thisAndNextLine; matched = other.matched; return *this; } bool operator<(const Suppression &other) const { if (errorId != other.errorId) return errorId < other.errorId; if (lineNumber < other.lineNumber) return true; if (fileName != other.fileName) return fileName < other.fileName; if (symbolName != other.symbolName) return symbolName < other.symbolName; if (hash != other.hash) return hash < other.hash; if (thisAndNextLine != other.thisAndNextLine) return thisAndNextLine; return false; } /** * Parse inline suppression in comment * @param comment the full comment text * @param errorMessage output parameter for error message (wrong suppression attribute) * @return true if it is a inline comment. */ bool parseComment(std::string comment, std::string *errorMessage); bool isSuppressed(const ErrorMessage &errmsg) const; bool isMatch(const ErrorMessage &errmsg); std::string getText() const; bool isLocal() const { return !fileName.empty() && fileName.find_first_of("?*") == std::string::npos; } bool isSameParameters(const Suppression &other) const { return errorId == other.errorId && fileName == other.fileName && lineNumber == other.lineNumber && symbolName == other.symbolName && hash == other.hash && thisAndNextLine == other.thisAndNextLine; } std::string errorId; std::string fileName; int lineNumber; std::string symbolName; std::size_t hash; bool thisAndNextLine; // Special case for backwards compatibility: { // cppcheck-suppress something bool matched; enum { NO_LINE = -1 }; }; /** * @brief Don't show errors listed in the file. * @param istr Open file stream where errors can be read. * @return error message. empty upon success */ std::string parseFile(std::istream &istr); /** * @brief Don't show errors listed in the file. * @param filename file name * @return error message. empty upon success */ std::string parseXmlFile(const char *filename); /** * Parse multi inline suppression in comment * @param comment the full comment text * @param errorMessage output parameter for error message (wrong suppression attribute) * @return empty vector if something wrong. */ static std::vector parseMultiSuppressComment(const std::string &comment, std::string *errorMessage); /** * @brief Don't show the given error. * @param line Description of error to suppress (in id:file:line format). * @return error message. empty upon success */ std::string addSuppressionLine(const std::string &line); /** * @brief Don't show this error. File and/or line are optional. In which case * the errorId alone is used for filtering. * @param suppression suppression details * @return error message. empty upon success */ std::string addSuppression(const Suppression &suppression); /** * @brief Combine list of suppressions into the current suppressions. * @param suppressions list of suppression details * @return error message. empty upon success */ std::string addSuppressions(const std::list &suppressions); /** * @brief Returns true if this message should not be shown to the user. * @param errmsg error message * @return true if this error is suppressed. */ bool isSuppressed(const ErrorMessage &errmsg); /** * @brief Returns true if this message should not be shown to the user, only uses local suppressions. * @param errmsg error message * @return true if this error is suppressed. */ bool isSuppressedLocal(const ErrorMessage &errmsg); /** * @brief Create an xml dump of suppressions * @param out stream to write XML to */ void dump(std::ostream &out) const; /** * @brief Returns list of unmatched local (per-file) suppressions. * @return list of unmatched suppressions */ std::list getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const; /** * @brief Returns list of unmatched global (glob pattern) suppressions. * @return list of unmatched suppressions */ std::list getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const; /** * @brief Returns list of all suppressions. * @return list of suppressions */ const std::list &getSuppressions() const; private: /** @brief List of error which the user doesn't want to see. */ std::list mSuppressions; }; /// @} //--------------------------------------------------------------------------- #endif // suppressionsH cppcheck-2.7/lib/symboldatabase.cpp000066400000000000000000010450671417746362400174410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "symboldatabase.h" #include "astutils.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" #include "mathlib.h" #include "platform.h" #include "settings.h" #include "standards.h" #include "templatesimplifier.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger) { if (!tokenizer || !tokenizer->tokens()) return; mIsCpp = isCPP(); if (mSettings->defaultSign == 's' || mSettings->defaultSign == 'S') mDefaultSignedness = ValueType::SIGNED; else if (mSettings->defaultSign == 'u' || mSettings->defaultSign == 'U') mDefaultSignedness = ValueType::UNSIGNED; else mDefaultSignedness = ValueType::UNKNOWN_SIGN; createSymbolDatabaseFindAllScopes(); createSymbolDatabaseClassInfo(); createSymbolDatabaseVariableInfo(); createSymbolDatabaseCopyAndMoveConstructors(); createSymbolDatabaseFunctionScopes(); createSymbolDatabaseClassAndStructScopes(); createSymbolDatabaseFunctionReturnTypes(); createSymbolDatabaseNeedInitialization(); createSymbolDatabaseVariableSymbolTable(); createSymbolDatabaseSetScopePointers(); createSymbolDatabaseSetVariablePointers(); setValueTypeInTokenList(false); createSymbolDatabaseSetTypePointers(); createSymbolDatabaseSetFunctionPointers(true); createSymbolDatabaseSetSmartPointerType(); setValueTypeInTokenList(false); createSymbolDatabaseEnums(); createSymbolDatabaseEscapeFunctions(); createSymbolDatabaseIncompleteVars(); createSymbolDatabaseExprIds(); } static const Token* skipScopeIdentifiers(const Token* tok) { if (Token::Match(tok, ":: %name%")) tok = tok->next(); while (Token::Match(tok, "%name% ::") || (Token::Match(tok, "%name% <") && Token::Match(tok->linkAt(1), ">|>> ::"))) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } return tok; } static bool isExecutableScope(const Token* tok) { if (!Token::simpleMatch(tok, "{")) return false; const Token * tok2 = tok->link()->previous(); if (Token::simpleMatch(tok2, "; }")) return true; if (Token::Match(tok2, "{|} }")) { const Token* startTok = tok2->str() == "{" ? tok2 : tok2->link(); if (Token::Match(startTok->previous(), "do|try|else {")) return true; if (Token::simpleMatch(startTok->previous(), ") {")) return !findLambdaStartToken(tok2); if (tok->str() == "{") return false; else return isExecutableScope(startTok); } return false; } void SymbolDatabase::createSymbolDatabaseFindAllScopes() { // create global scope scopeList.emplace_back(this, nullptr, nullptr); // pointer to current scope Scope *scope = &scopeList.back(); // Store the edning of init lists std::stack> endInitList; auto inInitList = [&] { if (endInitList.empty()) return false; return endInitList.top().second == scope; }; // Store current access in each scope (depends on evaluation progress) std::map access; // find all scopes for (const Token *tok = mTokenizer->tokens(); tok; tok = tok ? tok->next() : nullptr) { // #5593 suggested to add here: if (mErrorLogger) mErrorLogger->reportProgress(mTokenizer->list.getSourceFilePath(), "SymbolDatabase", tok->progressValue()); // Locate next class if ((mTokenizer->isCPP() && tok->isKeyword() && ((Token::Match(tok, "class|struct|union|namespace ::| %name% final| {|:|::|<") && !Token::Match(tok->previous(), "new|friend|const|enum|typedef|mutable|volatile|using|)|(|<")) || (Token::Match(tok, "enum class| %name% {") || Token::Match(tok, "enum class| %name% : %name% {")))) || (mTokenizer->isC() && tok->isKeyword() && Token::Match(tok, "struct|union|enum %name% {"))) { const Token *tok2 = tok->tokAt(2); if (tok->strAt(1) == "::") tok2 = tok2->next(); else if (mTokenizer->isCPP() && tok->strAt(1) == "class") tok2 = tok2->next(); while (Token::Match(tok2, ":: %name%")) tok2 = tok2->tokAt(2); while (Token::Match(tok2, "%name% :: %name%")) tok2 = tok2->tokAt(2); // skip over template args while (tok2 && tok2->str() == "<" && tok2->link()) { tok2 = tok2->link()->next(); while (Token::Match(tok2, ":: %name%")) tok2 = tok2->tokAt(2); } // skip over final if (mTokenizer->isCPP() && Token::simpleMatch(tok2, "final")) tok2 = tok2->next(); // make sure we have valid code if (!Token::Match(tok2, "{|:")) { // check for qualified variable if (tok2 && tok2->next()) { if (tok2->next()->str() == ";") tok = tok2->next(); else if (Token::simpleMatch(tok2->next(), "= {") && Token::simpleMatch(tok2->linkAt(2), "} ;")) tok = tok2->linkAt(2)->next(); else if (Token::Match(tok2->next(), "(|{") && tok2->next()->link()->strAt(1) == ";") tok = tok2->next()->link()->next(); // skip variable declaration else if (Token::Match(tok2, "*|&|>")) continue; else if (Token::Match(tok2, "%name% (") && mTokenizer->isFunctionHead(tok2->next(), "{;")) continue; else if (Token::Match(tok2, "%name% [")) continue; // skip template else if (Token::simpleMatch(tok2, ";") && Token::Match(tok->previous(), "template|> class|struct")) { tok = tok2; continue; } // forward declaration else if (Token::simpleMatch(tok2, ";") && Token::Match(tok, "class|struct|union")) { // TODO: see if it can be used tok = tok2; continue; } // skip constructor else if (Token::simpleMatch(tok2, "(") && Token::simpleMatch(tok2->link(), ") ;")) { tok = tok2->link()->next(); continue; } else throw InternalError(tok2, "SymbolDatabase bailout; unhandled code", InternalError::SYNTAX); continue; } break; // bail } const Token * name = tok->next(); if (name->str() == "class" && name->strAt(-1) == "enum") name = name->next(); Scope *new_scope = findScope(name, scope); if (new_scope) { // only create base list for classes and structures if (new_scope->isClassOrStruct()) { // goto initial '{' if (!new_scope->definedType) mTokenizer->syntaxError(nullptr); // #6808 tok2 = new_scope->definedType->initBaseInfo(tok, tok2); // make sure we have valid code if (!tok2) { break; } } // definition may be different than declaration if (mTokenizer->isCPP() && tok->str() == "class") { access[new_scope] = AccessControl::Private; new_scope->type = Scope::eClass; } else if (tok->str() == "struct") { access[new_scope] = AccessControl::Public; new_scope->type = Scope::eStruct; } new_scope->classDef = tok; new_scope->setBodyStartEnd(tok2); // make sure we have valid code if (!new_scope->bodyEnd) { mTokenizer->syntaxError(tok); } scope = new_scope; tok = tok2; } else { scopeList.emplace_back(this, tok, scope); new_scope = &scopeList.back(); if (tok->str() == "class") access[new_scope] = AccessControl::Private; else if (tok->str() == "struct" || tok->str() == "union") access[new_scope] = AccessControl::Public; // fill typeList... if (new_scope->isClassOrStructOrUnion() || new_scope->type == Scope::eEnum) { Type* new_type = findType(name, scope); if (!new_type) { typeList.emplace_back(new_scope->classDef, new_scope, scope); new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; } else new_type->classScope = new_scope; new_scope->definedType = new_type; } // only create base list for classes and structures if (new_scope->isClassOrStruct()) { // goto initial '{' tok2 = new_scope->definedType->initBaseInfo(tok, tok2); // make sure we have valid code if (!tok2) { mTokenizer->syntaxError(tok); } } else if (new_scope->type == Scope::eEnum) { if (tok2->str() == ":") tok2 = tok2->tokAt(2); } new_scope->setBodyStartEnd(tok2); // make sure we have valid code if (!new_scope->bodyEnd) { mTokenizer->syntaxError(tok); } if (new_scope->type == Scope::eEnum) { tok2 = new_scope->addEnum(tok, mTokenizer->isCPP()); scope->nestedList.push_back(new_scope); if (!tok2) mTokenizer->syntaxError(tok); } else { // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = new_scope; } tok = tok2; } } // Namespace and unknown macro (#3854) else if (mTokenizer->isCPP() && tok->isKeyword() && Token::Match(tok, "namespace %name% %type% (") && tok->tokAt(2)->isUpperCaseName() && Token::simpleMatch(tok->linkAt(3), ") {")) { scopeList.emplace_back(this, tok, scope); Scope *new_scope = &scopeList.back(); access[new_scope] = AccessControl::Public; const Token *tok2 = tok->linkAt(3)->next(); new_scope->setBodyStartEnd(tok2); // make sure we have valid code if (!new_scope->bodyEnd) { scopeList.pop_back(); break; } // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = &scopeList.back(); tok = tok2; } // forward declaration else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;") && tok->strAt(-1) != "friend") { if (!findType(tok->next(), scope)) { // fill typeList.. typeList.emplace_back(tok, nullptr, scope); Type* new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; } tok = tok->tokAt(2); } // using namespace else if (mTokenizer->isCPP() && tok->isKeyword() && Token::Match(tok, "using namespace ::| %type% ;|::")) { Scope::UsingInfo using_info; using_info.start = tok; // save location using_info.scope = findNamespace(tok->tokAt(2), scope); scope->usingList.push_back(using_info); // check for global namespace if (tok->strAt(2) == "::") tok = tok->tokAt(4); else tok = tok->tokAt(3); // skip over qualification while (Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); } // using type alias else if (mTokenizer->isCPP() && tok->isKeyword() && Token::Match(tok, "using %name% =")) { if (tok->strAt(-1) != ">" && !findType(tok->next(), scope)) { // fill typeList.. typeList.emplace_back(tok, nullptr, scope); Type* new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; } tok = tok->tokAt(3); while (tok && tok->str() != ";") tok = tok->next(); } // unnamed struct and union else if (tok->isKeyword() && Token::Match(tok, "struct|union {") && Token::Match(tok->next()->link(), "} *|&| %name% ;|[|=")) { scopeList.emplace_back(this, tok, scope); Scope *new_scope = &scopeList.back(); access[new_scope] = AccessControl::Public; const Token* varNameTok = tok->next()->link()->next(); if (varNameTok->str() == "*") { varNameTok = varNameTok->next(); } else if (varNameTok->str() == "&") { varNameTok = varNameTok->next(); } typeList.emplace_back(tok, new_scope, scope); { Type* new_type = &typeList.back(); new_scope->definedType = new_type; scope->definedTypesMap[new_type->name()] = new_type; } scope->addVariable(varNameTok, tok, tok, access[scope], new_scope->definedType, scope, mSettings); const Token *tok2 = tok->next(); new_scope->setBodyStartEnd(tok2); // make sure we have valid code if (!new_scope->bodyEnd) { scopeList.pop_back(); break; } // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = new_scope; tok = tok2; } // anonymous struct, union and namespace else if (tok->isKeyword() && ((Token::Match(tok, "struct|union {") && Token::simpleMatch(tok->next()->link(), "} ;")) || Token::simpleMatch(tok, "namespace {"))) { scopeList.emplace_back(this, tok, scope); Scope *new_scope = &scopeList.back(); access[new_scope] = AccessControl::Public; const Token *tok2 = tok->next(); new_scope->setBodyStartEnd(tok2); typeList.emplace_back(tok, new_scope, scope); { Type* new_type = &typeList.back(); new_scope->definedType = new_type; scope->definedTypesMap[new_type->name()] = new_type; } // make sure we have valid code if (!new_scope->bodyEnd) { scopeList.pop_back(); break; } // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = new_scope; tok = tok2; } // forward declared enum else if (tok->isKeyword() && (Token::Match(tok, "enum class| %name% ;") || Token::Match(tok, "enum class| %name% : %name% ;"))) { typeList.emplace_back(tok, nullptr, scope); Type* new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; tok = tok->tokAt(2); } // check for end of scope else if (tok == scope->bodyEnd) { access.erase(scope); scope = const_cast(scope->nestedIn); continue; } // check for end of init list else if (inInitList() && tok == endInitList.top().first) { endInitList.pop(); continue; } // check if in class or structure or union else if (scope->isClassOrStructOrUnion()) { const Token *funcStart = nullptr; const Token *argStart = nullptr; const Token *declEnd = nullptr; // What section are we in.. if (tok->str() == "private:") access[scope] = AccessControl::Private; else if (tok->str() == "protected:") access[scope] = AccessControl::Protected; else if (tok->str() == "public:" || tok->str() == "__published:") access[scope] = AccessControl::Public; else if (Token::Match(tok, "public|protected|private %name% :")) { if (tok->str() == "private") access[scope] = AccessControl::Private; else if (tok->str() == "protected") access[scope] = AccessControl::Protected; else access[scope] = AccessControl::Public; tok = tok->tokAt(2); } // class function? else if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) { if (tok->previous()->str() != "::" || tok->strAt(-2) == scope->className) { Function function(mTokenizer, tok, scope, funcStart, argStart); // save the access type function.access = access[scope]; const Token *end = function.argDef->link(); // count the number of constructors if (function.isConstructor()) scope->numConstructors++; // assume implementation is inline (definition and implementation same) function.token = function.tokenDef; function.arg = function.argDef; // out of line function if (const Token *endTok = mTokenizer->isFunctionHead(end, ";")) { tok = endTok; scope->addFunction(function); } // inline function else { // find start of function '{' bool foundInitList = false; while (end && end->str() != "{" && end->str() != ";") { if (end->link() && Token::Match(end, "(|<")) { end = end->link(); } else if (foundInitList && Token::Match(end, "%name%|> {") && Token::Match(end->linkAt(1), "} ,|{")) { end = end->linkAt(1); } else { if (end->str() == ":") foundInitList = true; end = end->next(); } } if (!end || end->str() == ";") continue; scope->addFunction(function); Function* funcptr = &scope->functionList.back(); const Token *tok2 = funcStart; addNewFunction(&scope, &tok2); if (scope) { scope->functionOf = function.nestedIn; scope->function = funcptr; scope->function->functionScope = scope; } tok = tok2; } } // nested class or friend function? else { /** @todo check entire qualification for match */ const Scope * const nested = scope->findInNestedListRecursive(tok->strAt(-2)); if (nested) addClassFunction(&scope, &tok, argStart); else { /** @todo handle friend functions */ } } } // friend class declaration? else if (mTokenizer->isCPP() && tok->isKeyword() && Token::Match(tok, "friend class| ::| %any% ;|::")) { Type::FriendInfo friendInfo; // save the name start friendInfo.nameStart = tok->strAt(1) == "class" ? tok->tokAt(2) : tok->next(); friendInfo.nameEnd = friendInfo.nameStart; // skip leading "::" if (friendInfo.nameEnd->str() == "::") friendInfo.nameEnd = friendInfo.nameEnd->next(); // skip qualification "name ::" while (friendInfo.nameEnd && friendInfo.nameEnd->strAt(1) == "::") friendInfo.nameEnd = friendInfo.nameEnd->tokAt(2); // fill this in after parsing is complete friendInfo.type = nullptr; if (!scope->definedType) mTokenizer->syntaxError(tok); scope->definedType->friendList.push_back(friendInfo); } } else if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) { const Token *funcStart = nullptr; const Token *argStart = nullptr; const Token *declEnd = nullptr; // function? if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) { // has body? if (declEnd && declEnd->str() == "{") { tok = funcStart; // class function if (tok->previous() && tok->previous()->str() == "::") addClassFunction(&scope, &tok, argStart); // class destructor else if (tok->previous() && tok->previous()->str() == "~" && tok->strAt(-2) == "::") addClassFunction(&scope, &tok, argStart); // regular function else { const Function* const function = addGlobalFunction(scope, tok, argStart, funcStart); if (!function) mTokenizer->syntaxError(tok); } // syntax error? if (!scope) mTokenizer->syntaxError(tok); } // function prototype? else if (declEnd && declEnd->str() == ";") { if (tok->astParent() && tok->astParent()->str() == "::" && Token::Match(declEnd->previous(), "default|delete")) { addClassFunction(&scope, &tok, argStart); continue; } bool newFunc = true; // Is this function already in the database? for (std::multimap::const_iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) { if (i->second->argsMatch(scope, i->second->argDef, argStart, emptyString, 0)) { newFunc = false; break; } } // save function prototype in database if (newFunc) { addGlobalFunctionDecl(scope, tok, argStart, funcStart); } tok = declEnd; continue; } } } else if (scope->isExecutable()) { if (tok->isKeyword() && Token::Match(tok, "else|try|do {")) { const Token* tok1 = tok->next(); if (tok->str() == "else") scopeList.emplace_back(this, tok, scope, Scope::eElse, tok1); else if (tok->str() == "do") scopeList.emplace_back(this, tok, scope, Scope::eDo, tok1); else //if (tok->str() == "try") scopeList.emplace_back(this, tok, scope, Scope::eTry, tok1); tok = tok1; scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); } else if (tok->isKeyword() && Token::Match(tok, "if|for|while|catch|switch (") && Token::simpleMatch(tok->next()->link(), ") {")) { const Token *scopeStartTok = tok->next()->link()->next(); if (tok->str() == "if") scopeList.emplace_back(this, tok, scope, Scope::eIf, scopeStartTok); else if (tok->str() == "for") { scopeList.emplace_back(this, tok, scope, Scope::eFor, scopeStartTok); } else if (tok->str() == "while") scopeList.emplace_back(this, tok, scope, Scope::eWhile, scopeStartTok); else if (tok->str() == "catch") { scopeList.emplace_back(this, tok, scope, Scope::eCatch, scopeStartTok); } else // if (tok->str() == "switch") scopeList.emplace_back(this, tok, scope, Scope::eSwitch, scopeStartTok); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); if (scope->type == Scope::eFor) scope->checkVariable(tok->tokAt(2), AccessControl::Local, mSettings); // check for variable declaration and add it to new scope if found else if (scope->type == Scope::eCatch) scope->checkVariable(tok->tokAt(2), AccessControl::Throw, mSettings); // check for variable declaration and add it to new scope if found tok = scopeStartTok; } else if (Token::Match(tok, "%var% {")) { endInitList.push(std::make_pair(tok->next()->link(), scope)); tok = tok->next(); } else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { const Token *lambdaStartToken = lambdaEndToken->link(); const Token * argStart = lambdaStartToken->astParent(); const Token * funcStart = Token::simpleMatch(argStart, "[") ? argStart : argStart->astParent(); const Function * function = addGlobalFunction(scope, tok, argStart, funcStart); if (!function) mTokenizer->syntaxError(tok); tok = lambdaStartToken; } else if (tok->str() == "{") { if (inInitList()) { endInitList.push(std::make_pair(tok->link(), scope)); } else if (isExecutableScope(tok)) { scopeList.emplace_back(this, tok, scope, Scope::eUnconditional, tok); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); } else if (scope->isExecutable()) { endInitList.push(std::make_pair(tok->link(), scope)); } else { tok = tok->link(); } } // syntax error? if (!scope) mTokenizer->syntaxError(tok); // End of scope or list should be handled above if (tok->str() == "}") mTokenizer->syntaxError(tok); } } } void SymbolDatabase::createSymbolDatabaseClassInfo() { if (mTokenizer->isC()) return; // fill in using info for (Scope& scope : scopeList) { for (Scope::UsingInfo& usingInfo : scope.usingList) { // only find if not already found if (usingInfo.scope == nullptr) { // check scope for match const Scope * const found = findScope(usingInfo.start->tokAt(2), &scope); if (found) { // set found scope usingInfo.scope = found; break; } } } } // fill in base class info for (Type& type : typeList) { // finish filling in base class info for (Type::BaseInfo & i : type.derivedFrom) { const Type* found = findType(i.nameTok, type.enclosingScope); if (found && found->findDependency(&type)) { // circular dependency //mTokenizer->syntaxError(nullptr); } else { i.type = found; } } } // fill in friend info for (Type & type : typeList) { for (Type::FriendInfo &friendInfo : type.friendList) { friendInfo.type = findType(friendInfo.nameStart, type.enclosingScope); } } } void SymbolDatabase::createSymbolDatabaseVariableInfo() { // fill in variable info for (Scope& scope : scopeList) { // find variables scope.getVariableList(mSettings); } // fill in function arguments for (Scope& scope : scopeList) { std::list::iterator func; for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { // add arguments func->addArguments(this, &scope); } } } void SymbolDatabase::createSymbolDatabaseCopyAndMoveConstructors() { // fill in class and struct copy/move constructors for (Scope& scope : scopeList) { if (!scope.isClassOrStruct()) continue; std::list::iterator func; for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { if (!func->isConstructor() || func->minArgCount() != 1) continue; const Variable* firstArg = func->getArgumentVar(0); if (firstArg->type() == scope.definedType) { if (firstArg->isRValueReference()) func->type = Function::eMoveConstructor; else if (firstArg->isReference() && !firstArg->isPointer()) func->type = Function::eCopyConstructor; } if (func->type == Function::eCopyConstructor || func->type == Function::eMoveConstructor) scope.numCopyOrMoveConstructors++; } } } void SymbolDatabase::createSymbolDatabaseFunctionScopes() { // fill in function scopes for (Scope & scope : scopeList) { if (scope.type == Scope::eFunction) functionScopes.push_back(&scope); } } void SymbolDatabase::createSymbolDatabaseClassAndStructScopes() { // fill in class and struct scopes for (Scope& scope : scopeList) { if (scope.isClassOrStruct()) classAndStructScopes.push_back(&scope); } } void SymbolDatabase::createSymbolDatabaseFunctionReturnTypes() { // fill in function return types for (Scope& scope : scopeList) { std::list::iterator func; for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { // add return types if (func->retDef) { const Token *type = func->retDef; while (Token::Match(type, "static|const|struct|union|enum")) type = type->next(); if (type) { func->retType = findVariableTypeInBase(&scope, type); if (!func->retType) func->retType = findTypeInNested(type, func->nestedIn); } } } } } void SymbolDatabase::createSymbolDatabaseNeedInitialization() { if (mTokenizer->isC()) { // For C code it is easy, as there are no constructors and no default values for (Scope& scope : scopeList) { if (scope.definedType) scope.definedType->needInitialization = Type::NeedInitialization::True; } } else { // For C++, it is more difficult: Determine if user defined type needs initialization... unsigned int unknowns = 0; // stop checking when there are no unknowns unsigned int retry = 0; // bail if we don't resolve all the variable types for some reason do { unknowns = 0; for (Scope& scope : scopeList) { if (!scope.isClassOrStructOrUnion()) continue; if (!scope.definedType) { mBlankTypes.emplace_back(); scope.definedType = &mBlankTypes.back(); } if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) { // check for default constructor bool hasDefaultConstructor = false; for (Function& func: scope.functionList) { if (func.type == Function::eConstructor) { // check for no arguments: func ( ) if (func.argCount() == 0) { hasDefaultConstructor = true; break; } /** check for arguments with default values */ else if (func.argCount() == func.initializedArgCount()) { hasDefaultConstructor = true; break; } } } // User defined types with user defined default constructor doesn't need initialization. // We assume the default constructor initializes everything. // Another check will figure out if the constructor actually initializes everything. if (hasDefaultConstructor) scope.definedType->needInitialization = Type::NeedInitialization::False; // check each member variable to see if it needs initialization else { bool needInitialization = false; bool unknown = false; for (const Variable& var: scope.varlist) { if (var.isClass()) { if (var.type()) { // does this type need initialization? if (var.type()->needInitialization == Type::NeedInitialization::True && !var.hasDefault()) needInitialization = true; else if (var.type()->needInitialization == Type::NeedInitialization::Unknown) { if (!(var.valueType() && var.valueType()->type == ValueType::CONTAINER)) unknown = true; } } } else if (!var.hasDefault() && !var.isStatic()) { needInitialization = true; break; } } if (needInitialization) scope.definedType->needInitialization = Type::NeedInitialization::True; else if (!unknown) scope.definedType->needInitialization = Type::NeedInitialization::False; else { if (scope.definedType->needInitialization == Type::NeedInitialization::Unknown) unknowns++; } } } else if (scope.type == Scope::eUnion && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) scope.definedType->needInitialization = Type::NeedInitialization::True; } retry++; } while (unknowns && retry < 100); // this shouldn't happen so output a debug warning if (retry == 100 && mSettings->debugwarnings) { for (const Scope& scope : scopeList) { if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) debugMessage(scope.classDef, "debug", "SymbolDatabase couldn't resolve all user defined types."); } } } } void SymbolDatabase::createSymbolDatabaseVariableSymbolTable() { // create variable symbol table mVariableList.resize(mTokenizer->varIdCount() + 1); std::fill_n(mVariableList.begin(), mVariableList.size(), (const Variable*)nullptr); // check all scopes for variables for (Scope& scope : scopeList) { // add all variables for (Variable& var: scope.varlist) { const unsigned int varId = var.declarationId(); if (varId) mVariableList[varId] = &var; // fix up variables without type if (!var.type() && !var.typeStartToken()->isStandardType()) { const Type *type = findType(var.typeStartToken(), &scope); if (type) var.type(type); } } // add all function parameters for (Function& func : scope.functionList) { for (Variable& arg: func.argumentList) { // check for named parameters if (arg.nameToken() && arg.declarationId()) { const unsigned int declarationId = arg.declarationId(); mVariableList[declarationId] = &arg; // fix up parameters without type if (!arg.type() && !arg.typeStartToken()->isStandardType()) { const Type *type = findTypeInNested(arg.typeStartToken(), &scope); if (type) arg.type(type); } } } } } // fill in missing variables if possible const std::size_t functions = functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope *func = functionScopes[i]; for (const Token *tok = func->bodyStart->next(); tok && tok != func->bodyEnd; tok = tok->next()) { // check for member variable if (tok->varId() && tok->next() && (tok->next()->str() == "." || (tok->next()->str() == "[" && tok->linkAt(1)->strAt(1) == "."))) { const Token *tok1 = tok->next()->str() == "." ? tok->tokAt(2) : tok->linkAt(1)->tokAt(2); if (tok1 && tok1->varId() && mVariableList[tok1->varId()] == nullptr) { const Variable *var = mVariableList[tok->varId()]; if (var && var->typeScope()) { // find the member variable of this variable const Variable *var1 = var->typeScope()->getVariable(tok1->str()); if (var1) { // add this variable to the look up table mVariableList[tok1->varId()] = var1; } } } } } } } void SymbolDatabase::createSymbolDatabaseSetScopePointers() { auto setScopePointers = [](const Scope &scope, const Token *bodyStart, const Token *bodyEnd) { assert(bodyStart); assert(bodyEnd); const_cast(bodyEnd)->scope(&scope); for (Token* tok = const_cast(bodyStart); tok != bodyEnd; tok = tok->next()) { if (bodyStart != bodyEnd && tok->str() == "{") { bool isEndOfScope = false; for (Scope* innerScope: scope.nestedList) { const auto &list = innerScope->bodyStartList; if (std::find(list.begin(), list.end(), tok) != list.end()) { // Is begin of inner scope tok = tok->link(); if (tok->next() == bodyEnd || !tok->next()) { isEndOfScope = true; break; } tok = tok->next(); break; } } if (isEndOfScope) break; } tok->scope(&scope); } }; // Set scope pointers for (Scope& scope: scopeList) { if (scope.type == Scope::eGlobal) setScopePointers(scope, mTokenizer->list.front(), mTokenizer->list.back()); else { for (const Token *bodyStart: scope.bodyStartList) setScopePointers(scope, bodyStart, bodyStart->link()); } } } void SymbolDatabase::createSymbolDatabaseSetFunctionPointers(bool firstPass) { if (firstPass) { // Set function definition and declaration pointers for (const Scope& scope: scopeList) { for (const Function& func: scope.functionList) { if (func.tokenDef) const_cast(func.tokenDef)->function(&func); if (func.token) const_cast(func.token)->function(&func); } } } // Set function call pointers for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (tok->isName() && !tok->function() && tok->varId() == 0 && Token::Match(tok, "%name% [(,)>]") && !isReservedName(tok->str())) { if (tok->next()->str() == ">" && !tok->next()->link()) continue; if (tok->next()->str() != "(") { const Token *start = tok; while (Token::Match(start->tokAt(-2), "%name% ::")) start = start->tokAt(-2); if (!Token::Match(start->previous(), "[(,<]") && !Token::Match(start->tokAt(-2), "[(,<] &")) continue; } const Function *function = findFunction(tok); if (!function) continue; const_cast(tok)->function(function); if (tok->next()->str() != "(") const_cast(function)->functionPointerUsage = tok; } } // Set C++ 11 delegate constructor function call pointers for (const Scope& scope: scopeList) { for (const Function& func: scope.functionList) { // look for initializer list if (func.isConstructor() && func.functionScope && func.functionScope->functionOf && func.arg) { const Token * tok = func.arg->link()->next(); if (tok->str() == "noexcept") { const Token * closingParenTok = tok->linkAt(1); if (!closingParenTok || !closingParenTok->next()) { continue; } tok = closingParenTok->next(); } if (tok->str() != ":") { continue; } tok = tok->next(); while (tok && tok != func.functionScope->bodyStart) { if (Token::Match(tok, "%name% {|(")) { if (tok->str() == func.tokenDef->str()) { const Function *function = func.functionScope->functionOf->findFunction(tok); if (function) const_cast(tok)->function(function); break; } tok = tok->linkAt(1); } tok = tok->next(); } } } } } void SymbolDatabase::createSymbolDatabaseSetTypePointers() { std::set typenames; for (const Type &t : typeList) { typenames.insert(t.name()); } // Set type pointers for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (!tok->isName() || tok->varId() || tok->function() || tok->type() || tok->enumerator()) continue; if (typenames.find(tok->str()) == typenames.end()) continue; const Type *type = findVariableType(tok->scope(), tok); if (type) const_cast(tok)->type(type); } } void SymbolDatabase::createSymbolDatabaseSetSmartPointerType() { for (Scope &scope: scopeList) { for (Variable &var: scope.varlist) { if (var.valueType() && var.valueType()->smartPointerTypeToken && !var.valueType()->smartPointerType) { ValueType vt(*var.valueType()); vt.smartPointerType = vt.smartPointerTypeToken->type(); var.setValueType(vt); } } } } void SymbolDatabase::fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar) { VarIdMap::iterator varId = varIds.find(vartok->varId()); if (varId == varIds.end()) { MemberIdMap memberId; if (membertok->varId() == 0) { memberId[membervar->nameToken()->varId()] = const_cast(mTokenizer)->newVarId(); mVariableList.push_back(membervar); } else mVariableList[membertok->varId()] = membervar; varIds.insert(std::make_pair(vartok->varId(), memberId)); varId = varIds.find(vartok->varId()); } MemberIdMap::iterator memberId = varId->second.find(membervar->nameToken()->varId()); if (memberId == varId->second.end()) { if (membertok->varId() == 0) { varId->second.insert(std::make_pair(membervar->nameToken()->varId(), const_cast(mTokenizer)->newVarId())); mVariableList.push_back(membervar); memberId = varId->second.find(membervar->nameToken()->varId()); } else mVariableList[membertok->varId()] = membervar; } if (membertok->varId() == 0) membertok->varId(memberId->second); } void SymbolDatabase::createSymbolDatabaseSetVariablePointers() { VarIdMap varIds; // Set variable pointers for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (tok->varId()) const_cast(tok)->variable(getVariableFromVarId(tok->varId())); // Set Token::variable pointer for array member variable // Since it doesn't point at a fixed location it doesn't have varid if (tok->variable() != nullptr && (tok->variable()->typeScope() || tok->variable()->isSmartPointer() || (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER)) && Token::Match(tok, "%name% [|.")) { Token *tok2 = tok->next(); // Locate "]" while (tok2 && tok2->str() == "[") tok2 = tok2->link()->next(); Token *membertok = nullptr; if (Token::Match(tok2, ". %name%")) membertok = tok2->next(); else if (Token::Match(tok2, ") . %name%") && tok->strAt(-1) == "(") membertok = tok2->tokAt(2); if (membertok) { const Variable *var = tok->variable(); if (var->typeScope()) { const Variable *membervar = var->typeScope()->getVariable(membertok->str()); if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) fixVarId(varIds, tok, const_cast(membertok), membervar); } } else if (const ::Type *type = var->smartPointerType()) { const Scope *classScope = type->classScope; const Variable *membervar = classScope ? classScope->getVariable(membertok->str()) : nullptr; if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) fixVarId(varIds, tok, const_cast(membertok), membervar); } } else if (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) { if (Token::Match(var->typeStartToken(), "std :: %type% < %type% *| *| >")) { const Type * type2 = var->typeStartToken()->tokAt(4)->type(); if (type2 && type2->classScope && type2->classScope->definedType) { const Variable *membervar = type2->classScope->getVariable(membertok->str()); if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) fixVarId(varIds, tok, const_cast(membertok), membervar); } } } } } } // check for function returning record type // func(...).var // func(...)[...].var else if (tok->function() && tok->next()->str() == "(" && (Token::Match(tok->next()->link(), ") . %name% !!(") || (Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) { const Type *type = tok->function()->retType; if (type) { Token *membertok; if (tok->next()->link()->next()->str() == ".") membertok = tok->next()->link()->next()->next(); else membertok = tok->next()->link()->next()->link()->next()->next(); const Variable *membervar = membertok->variable(); if (!membervar) { if (type->classScope) { membervar = type->classScope->getVariable(membertok->str()); if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) { if (tok->function()->retDef) fixVarId(varIds, tok->function()->retDef, const_cast(membertok), membervar); } } } } } } } } void SymbolDatabase::createSymbolDatabaseEnums() { // fill in enumerators in enum for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->type != Scope::eEnum) continue; // add enumerators to enumerator tokens for (Enumerator & i : it->enumeratorList) const_cast(i.name)->enumerator(&i); } for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->type != Scope::eEnum) continue; for (Enumerator & enumerator : it->enumeratorList) { // look for initialization tokens that can be converted to enumerators and convert them if (enumerator.start) { if (!enumerator.end) mTokenizer->syntaxError(enumerator.start); for (const Token * tok3 = enumerator.start; tok3 && tok3 != enumerator.end->next(); tok3 = tok3->next()) { if (tok3->tokType() == Token::eName) { const Enumerator * e = findEnumerator(tok3); if (e) const_cast(tok3)->enumerator(e); } } } } } // find enumerators for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (tok->tokType() != Token::eName) continue; const Enumerator * enumerator = findEnumerator(tok); if (enumerator) const_cast(tok)->enumerator(enumerator); } } void SymbolDatabase::createSymbolDatabaseIncompleteVars() { static const std::unordered_set cpp20keywords = { "alignas", "alignof", "axiom", "co_await", "co_return", "co_yield", "concept", "synchronized", "consteval", "reflexpr", "requires", }; static const std::unordered_set cppkeywords = { "asm", "auto", "catch", "char", "class", "const", "constexpr", "decltype", "default", "do", "enum", "explicit", "export", "extern", "final", "friend", "inline", "mutable", "namespace", "new", "noexcept", "nullptr", "override", "private", "protected", "public", "register", "sizeof", "static", "static_assert", "struct", "template", "this", "thread_local", "throw", "try", "typedef", "typeid", "typename", "union", "using", "virtual", "void", "volatile", "NULL", }; for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { const Scope * scope = tok->scope(); if (!scope) continue; if (!scope->isExecutable()) continue; if (!Token::Match(tok, "%name%")) continue; if (!tok->isNameOnly()) continue; if (Token::Match(tok, "%var%")) continue; if (tok->type()) continue; if (Token::Match(tok->next(), "::|.|(|:|%var%")) continue; if (Token::Match(tok->next(), "&|&&|* )|%var%")) continue; if (Token::simpleMatch(tok->next(), ")") && Token::simpleMatch(tok->next()->link()->previous(), "catch (")) continue; // Very likely a typelist if (Token::Match(tok->tokAt(-2), "%type% ,")) continue; // Inside template brackets if (Token::Match(tok->next(), "<|>") && tok->next()->link()) continue; if (Token::simpleMatch(tok->previous(), "<") && tok->previous()->link()) continue; // Skip goto labels if (Token::simpleMatch(tok->previous(), "goto")) continue; if (cppkeywords.count(tok->str()) > 0) continue; if (mSettings->standards.cpp >= Standards::CPP20 && cpp20keywords.count(tok->str()) > 0) continue; const_cast(tok)->isIncompleteVar(true); } } void SymbolDatabase::createSymbolDatabaseEscapeFunctions() { for (Scope & scope : scopeList) { if (scope.type != Scope::eFunction) continue; Function * function = scope.function; if (!function) continue; function->isEscapeFunction(isReturnScope(scope.bodyEnd, &mSettings->library, nullptr, true)); } } static bool isExpression(const Token* tok) { if (!Token::Match(tok, "(|.|[|::|?|:|++|--|%cop%|%assign%")) return false; if (Token::Match(tok, "*|&|&&")) { const Token* vartok = findAstNode(tok, [&](const Token* tok2) { const Variable* var = tok2->variable(); if (!var) return false; return var->nameToken() == tok2; }); if (vartok) return false; } return true; } void SymbolDatabase::createSymbolDatabaseExprIds() { nonneg int base = 0; // Find highest varId for (const Variable *var : mVariableList) { if (!var) continue; base = std::max(base, var->declarationId()); } nonneg int id = base + 1; for (const Scope * scope : functionScopes) { nonneg int thisId = 0; std::unordered_map> exprs; // Assign IDs for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId() > 0) { tok->exprId(tok->varId()); } else if (isExpression(tok)) { exprs[tok->str()].push_back(tok); tok->exprId(id++); if (id == std::numeric_limits::max()) { throw InternalError(nullptr, "Ran out of expression ids.", InternalError::INTERNAL); } } else if (isCPP() && Token::simpleMatch(tok, "this")) { if (thisId == 0) thisId = id++; tok->exprId(thisId); } } // Apply CSE for (const auto& p:exprs) { const std::vector& tokens = p.second; for (Token* tok1:tokens) { for (Token* tok2:tokens) { if (tok1 == tok2) continue; if (tok1->exprId() == tok2->exprId()) continue; if (!isSameExpression(isCPP(), true, tok1, tok2, mSettings->library, false, false)) continue; nonneg int cid = std::min(tok1->exprId(), tok2->exprId()); tok1->exprId(cid); tok2->exprId(cid); } } } } } void SymbolDatabase::setArrayDimensionsUsingValueFlow() { // set all unknown array dimensions for (const Variable *var : mVariableList) { // check each array variable if (!var || !var->isArray()) continue; // check each array dimension for (const Dimension &const_dimension : var->dimensions()) { Dimension &dimension = const_cast(const_dimension); if (dimension.num != 0 || !dimension.tok) continue; if (Token::Match(dimension.tok->previous(), "[<,]")) { if (dimension.known) continue; if (!Token::Match(dimension.tok->previous(), "[<,]")) continue; // In template arguments, there might not be AST // Determine size by using the "raw tokens" TokenList tokenList(mSettings); tokenList.addtoken(";", 0, 0, 0, false); bool fail = false; for (const Token *tok = dimension.tok; tok && !Token::Match(tok, "[,>]"); tok = tok->next()) { if (!tok->isName()) tokenList.addtoken(tok->str(), 0, 0, 0, false); else if (tok->hasKnownIntValue()) tokenList.addtoken(std::to_string(tok->getKnownIntValue()), 0, 0, 0, false); else { fail = true; break; } } if (fail) continue; tokenList.addtoken(";", 0, 0, 0, false); for (Token *tok = tokenList.front(); tok;) { if (TemplateSimplifier::simplifyNumericCalculations(tok, false)) tok = tokenList.front(); else tok = tok->next(); } if (Token::Match(tokenList.front(), "; %num% ;")) { dimension.known = true; dimension.num = MathLib::toLongNumber(tokenList.front()->next()->str()); } continue; } // Normal array [..dimension..] dimension.known = false; // check for a single token dimension if (dimension.tok->hasKnownIntValue()) { dimension.known = true; dimension.num = dimension.tok->getKnownIntValue(); continue; } else if (dimension.tok->valueType() && dimension.tok->valueType()->pointer == 0) { int bits = 0; switch (dimension.tok->valueType()->type) { case ValueType::Type::CHAR: bits = mSettings->char_bit; break; case ValueType::Type::SHORT: bits = mSettings->short_bit; break; case ValueType::Type::INT: bits = mSettings->int_bit; break; case ValueType::Type::LONG: bits = mSettings->long_bit; break; case ValueType::Type::LONGLONG: bits = mSettings->long_long_bit; break; default: break; } if (bits > 0 && bits <= 62) { if (dimension.tok->valueType()->sign == ValueType::Sign::UNSIGNED) dimension.num = 1LL << bits; else dimension.num = 1LL << (bits - 1); } } } } } SymbolDatabase::~SymbolDatabase() { // Clear scope, type, function and variable pointers for (const Token* tok = mTokenizer->list.front(); tok; tok = tok->next()) { const_cast(tok)->scope(nullptr); const_cast(tok)->type(nullptr); const_cast(tok)->function(nullptr); const_cast(tok)->variable(nullptr); const_cast(tok)->enumerator(nullptr); const_cast(tok)->setValueType(nullptr); } } bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart, const Token** declEnd) const { if (tok->varId()) return false; // function returning function pointer? '... ( ... %name% ( ... ))( ... ) {' // function returning reference to array '... ( & %name% ( ... ))[ ... ] {' // TODO: Activate this again if (false && tok->str() == "(" && tok->strAt(1) != "*" && (tok->link()->previous()->str() == ")" || Token::simpleMatch(tok->link()->tokAt(-2), ") const"))) { const Token* tok2 = tok->link()->next(); if (tok2 && tok2->str() == "(" && Token::Match(tok2->link()->next(), "{|;|const|=")) { const Token* argStartTok; if (tok->link()->previous()->str() == "const") argStartTok = tok->link()->linkAt(-2); else argStartTok = tok->link()->linkAt(-1); *funcStart = argStartTok->previous(); *argStart = argStartTok; *declEnd = Token::findmatch(tok2->link()->next(), "{|;"); return true; } else if (tok2 && tok2->str() == "[") { while (tok2 && tok2->str() == "[") tok2 = tok2->link()->next(); if (Token::Match(tok2, "{|;|const|=")) { const Token* argStartTok; if (tok->link()->previous()->str() == "const") argStartTok = tok->link()->linkAt(-2); else argStartTok = tok->link()->linkAt(-1); *funcStart = argStartTok->previous(); *argStart = argStartTok; *declEnd = Token::findmatch(tok2, "{|;"); return true; } } } else if (!tok->isName() || !tok->next() || !tok->next()->link()) return false; // regular function? else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && tok->previous() && (Token::Match(tok->previous(), "%name%|>|&|*|::|~") || // Either a return type or scope qualifier in front of tok outerScope->isClassOrStructOrUnion())) { // or a ctor/dtor const Token* tok1 = tok->previous(); const Token* tok2 = tok->next()->link()->next(); if (!mTokenizer->isFunctionHead(tok->next(), ";:{")) return false; // skip over destructor "~" if (tok1->str() == "~") tok1 = tok1->previous(); // skip over qualification while (Token::simpleMatch(tok1, "::")) { tok1 = tok1->previous(); if (Token::Match(tok1, "%name%")) tok1 = tok1->previous(); else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) tok1 = tok1->link()->tokAt(-2); } // skip over const, noexcept, throw, override, final and volatile specifiers while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile|&|&&")) { tok2 = tok2->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); } // skip over trailing return type if (tok2 && tok2->str() == ".") { for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, ";|{|=|override|final")) break; if (tok2->link() && Token::Match(tok2, "<|[|(")) tok2 = tok2->link(); } } // done if constructor or destructor if (!Token::Match(tok1, "{|}|;|public:|protected:|private:") && tok1) { // skip over pointers and references while (Token::Match(tok1, "%type%|*|&") && !endsWith(tok1->str(), ':') && (!isReservedName(tok1->str()) || tok1->str() == "const")) tok1 = tok1->previous(); // skip over decltype if (Token::simpleMatch(tok1, ")") && tok1->link() && Token::simpleMatch(tok1->link()->previous(), "decltype (")) tok1 = tok1->link()->tokAt(-2); // skip over template if (tok1 && tok1->str() == ">") { if (tok1->link()) tok1 = tok1->link()->previous(); else return false; } // function can't have number or variable as return type if (tok1 && (tok1->isNumber() || tok1->varId())) return false; // skip over return type if (Token::Match(tok1, "%name%")) { if (tok1->str() == "return") return false; tok1 = tok1->previous(); } // skip over qualification while (Token::simpleMatch(tok1, "::")) { tok1 = tok1->previous(); if (Token::Match(tok1, "%name%")) tok1 = tok1->previous(); else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) tok1 = tok1->link()->tokAt(-2); else if (Token::simpleMatch(tok1, ")") && tok1->link() && Token::simpleMatch(tok1->link()->previous(), "decltype (")) tok1 = tok1->link()->tokAt(-2); } // skip over modifiers and other stuff while (Token::Match(tok1, "const|static|extern|template|virtual|struct|class|enum|%name%")) { // friend type func(); is not a function if (isCPP() && tok1->str() == "friend" && tok2->str() == ";") return false; tok1 = tok1->previous(); } // should be at a sequence point if this is a function if (!Token::Match(tok1, ">|{|}|;|public:|protected:|private:") && tok1) return false; } if (tok2 && (Token::Match(tok2, ";|{|=") || (tok2->isUpperCaseName() && Token::Match(tok2, "%name% ;|{")) || (tok2->isUpperCaseName() && Token::Match(tok2, "%name% (") && tok2->next()->link()->strAt(1) == "{") || Token::Match(tok2, ": ::| %name% (|::|<|{") || Token::Match(tok2, "&|&&| ;|{") || Token::Match(tok2, "= delete|default ;"))) { *funcStart = tok; *argStart = tok->next(); *declEnd = Token::findmatch(tok2, "{|;"); return true; } } // UNKNOWN_MACRO(a,b) { ... } else if (outerScope->type == Scope::eGlobal && Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::simpleMatch(tok->linkAt(1), ") {") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { *funcStart = tok; *argStart = tok->next(); *declEnd = tok->linkAt(1)->next(); return true; } // template constructor? else if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) { const Token* tok2 = tok->next()->link()->next()->link(); if (Token::Match(tok2, ") const| ;|{|=") || Token::Match(tok2, ") : ::| %name% (|::|<|{") || Token::Match(tok2, ") const| noexcept {|;|(")) { *funcStart = tok; *argStart = tok2->link(); *declEnd = Token::findmatch(tok2->next(), "{|;"); return true; } } // regular C function with missing return or invalid C++ ? else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && Token::simpleMatch(tok->linkAt(1), ") {") && (!tok->previous() || Token::Match(tok->previous(), ";|}"))) { if (mTokenizer->isC()) { debugMessage(tok, "debug", "SymbolDatabase::isFunction found C function '" + tok->str() + "' without a return type."); *funcStart = tok; *argStart = tok->next(); *declEnd = tok->linkAt(1)->next(); return true; } mTokenizer->syntaxError(tok); } return false; } void SymbolDatabase::validateExecutableScopes() const { const std::size_t functions = functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope* const scope = functionScopes[i]; const Function* const function = scope->function; if (scope->isExecutable() && !function) { const std::list callstack(1, scope->classDef); const std::string msg = std::string("Executable scope '") + scope->classDef->str() + "' with unknown function."; const ErrorMessage errmsg(callstack, &mTokenizer->list, Severity::debug, "symbolDatabaseWarning", msg, Certainty::normal); mErrorLogger->reportErr(errmsg); } } } namespace { const Function* getFunctionForArgumentvariable(const Variable * const var, const std::vector& functionScopes) { const std::size_t functions = functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope* const scope = functionScopes[i]; const Function* const function = scope->function; if (function) { for (std::size_t arg=0; arg < function->argCount(); ++arg) { if (var==function->getArgumentVar(arg)) return function; } } } return nullptr; } } void SymbolDatabase::validateVariables() const { for (std::vector::const_iterator iter = mVariableList.begin(); iter!=mVariableList.end(); ++iter) { const Variable * const var = *iter; if (var) { if (!var->scope()) { const Function* function = getFunctionForArgumentvariable(var, functionScopes); if (!var->isArgument() || (function && function->hasBody())) { throw InternalError(var->nameToken(), "Analysis failed (variable without scope). If the code is valid then please report this failure.", InternalError::INTERNAL); //std::cout << "!!!Variable found without scope: " << var->nameToken()->str() << std::endl; } } } } } void SymbolDatabase::validate() const { if (mSettings->debugwarnings) { validateExecutableScopes(); } // TODO //validateVariables(); } void SymbolDatabase::clangSetVariables(const std::vector &variableList) { mVariableList = variableList; } Variable::Variable(const Token *name_, const std::string &clangType, const Token *typeStart, const Token *typeEnd, nonneg int index_, AccessControl access_, const Type *type_, const Scope *scope_) : mNameToken(name_), mTypeStartToken(typeStart), mTypeEndToken(typeEnd), mIndex(index_), mAccess(access_), mFlags(0), mType(type_), mScope(scope_), mValueType(nullptr) { if (!mTypeStartToken && mTypeEndToken) { mTypeStartToken = mTypeEndToken; while (Token::Match(mTypeStartToken->previous(), "%type%|*|&")) mTypeStartToken = mTypeStartToken->previous(); } while (Token::Match(mTypeStartToken, "const|struct|static")) { if (mTypeStartToken->str() == "static") setFlag(fIsStatic, true); mTypeStartToken = mTypeStartToken->next(); } if (Token::simpleMatch(mTypeEndToken, "&")) setFlag(fIsReference, true); else if (Token::simpleMatch(mTypeEndToken, "&&")) { setFlag(fIsReference, true); setFlag(fIsRValueRef, true); } std::string::size_type pos = clangType.find("["); if (pos != std::string::npos) { setFlag(fIsArray, true); do { const std::string::size_type pos1 = pos+1; pos = clangType.find("]", pos1); Dimension dim; dim.tok = nullptr; dim.known = pos > pos1; if (pos > pos1) dim.num = MathLib::toLongNumber(clangType.substr(pos1, pos-pos1)); else dim.num = 0; mDimensions.push_back(dim); ++pos; } while (pos < clangType.size() && clangType[pos] == '['); } // Is there initialization in variable declaration const Token *initTok = mNameToken ? mNameToken->next() : nullptr; while (initTok && initTok->str() == "[") initTok = initTok->link()->next(); if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq())) setFlag(fIsInit, true); } Variable::Variable(const Variable &var, const Scope *scope) : mValueType(nullptr) { *this = var; mScope = scope; } Variable::Variable(const Variable &var) : mValueType(nullptr) { *this = var; } Variable::~Variable() { delete mValueType; } Variable& Variable::operator=(const Variable &var) { if (this == &var) return *this; mNameToken = var.mNameToken; mTypeStartToken = var.mTypeStartToken; mTypeEndToken = var.mTypeEndToken; mIndex = var.mIndex; mAccess = var.mAccess; mFlags = var.mFlags; mType = var.mType; mScope = var.mScope; mDimensions = var.mDimensions; delete mValueType; if (var.mValueType) mValueType = new ValueType(*var.mValueType); else mValueType = nullptr; return *this; } bool Variable::isPointerArray() const { return isArray() && nameToken() && nameToken()->previous() && (nameToken()->previous()->str() == "*"); } bool Variable::isUnsigned() const { return mValueType ? (mValueType->sign == ValueType::Sign::UNSIGNED) : mTypeStartToken->isUnsigned(); } const Token * Variable::declEndToken() const { Token const * declEnd = typeStartToken(); while (declEnd && !Token::Match(declEnd, "[;,)={]")) { if (declEnd->link() && Token::Match(declEnd,"(|[|<")) declEnd = declEnd->link(); declEnd = declEnd->next(); } return declEnd; } void Variable::evaluate(const Settings* settings) { // Is there initialization in variable declaration const Token *initTok = mNameToken ? mNameToken->next() : nullptr; while (initTok && initTok->str() == "[") initTok = initTok->link()->next(); if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq())) setFlag(fIsInit, true); if (!settings) return; const Library * const lib = &settings->library; bool isContainer = false; if (mNameToken) setFlag(fIsArray, arrayDimensions(settings, &isContainer)); if (mTypeStartToken) setValueType(ValueType::parseDecl(mTypeStartToken,settings)); const Token* tok = mTypeStartToken; while (tok && tok->previous() && tok->previous()->isName()) tok = tok->previous(); const Token* end = mTypeEndToken; if (end) end = end->next(); while (tok != end) { if (tok->str() == "static") setFlag(fIsStatic, true); else if (tok->str() == "extern") setFlag(fIsExtern, true); else if (tok->str() == "volatile" || Token::simpleMatch(tok, "std :: atomic <")) setFlag(fIsVolatile, true); else if (tok->str() == "mutable") setFlag(fIsMutable, true); else if (tok->str() == "const") setFlag(fIsConst, true); else if (tok->str() == "constexpr") { setFlag(fIsConst, true); setFlag(fIsStatic, true); } else if (tok->str() == "*") { setFlag(fIsPointer, !isArray() || (isContainer && !Token::Match(tok->next(), "%name% [")) || Token::Match(tok->previous(), "( * %name% )")); setFlag(fIsConst, false); // Points to const, isn't necessarily const itself } else if (tok->str() == "&") { if (isReference()) setFlag(fIsRValueRef, true); setFlag(fIsReference, true); } else if (tok->str() == "&&") { // Before simplification, && isn't split up setFlag(fIsRValueRef, true); setFlag(fIsReference, true); // Set also fIsReference } if (tok->isAttributeMaybeUnused()) { setFlag(fIsMaybeUnused, true); } if (tok->str() == "<" && tok->link()) tok = tok->link(); else tok = tok->next(); } while (Token::Match(mTypeStartToken, "static|const|constexpr|volatile %any%")) mTypeStartToken = mTypeStartToken->next(); while (mTypeEndToken && mTypeEndToken->previous() && Token::Match(mTypeEndToken, "const|volatile")) mTypeEndToken = mTypeEndToken->previous(); if (mTypeStartToken) { std::string strtype = mTypeStartToken->str(); for (const Token *typeToken = mTypeStartToken; Token::Match(typeToken, "%type% :: %type%"); typeToken = typeToken->tokAt(2)) strtype += "::" + typeToken->strAt(2); setFlag(fIsClass, !lib->podtype(strtype) && !mTypeStartToken->isStandardType() && !isEnumType() && !isPointer() && !isReference() && strtype != "..."); setFlag(fIsStlType, Token::simpleMatch(mTypeStartToken, "std ::")); setFlag(fIsStlString, isStlType() && (Token::Match(mTypeStartToken->tokAt(2), "string|wstring|u16string|u32string !!::") || (Token::simpleMatch(mTypeStartToken->tokAt(2), "basic_string <") && !Token::simpleMatch(mTypeStartToken->linkAt(3), "> ::")))); setFlag(fIsSmartPointer, lib->isSmartPointer(mTypeStartToken)); } if (mAccess == AccessControl::Argument) { tok = mNameToken; if (!tok) { // Argument without name tok = mTypeEndToken; // back up to start of array dimensions while (tok && tok->str() == "]") tok = tok->link()->previous(); // add array dimensions if present if (tok && tok->next()->str() == "[") setFlag(fIsArray, arrayDimensions(settings, &isContainer)); } if (!tok) return; tok = tok->next(); while (tok->str() == "[") tok = tok->link(); setFlag(fHasDefault, tok->str() == "="); } // check for C++11 member initialization if (mScope && mScope->isClassOrStruct()) { // type var = x or // type var = {x} // type var = x; gets simplified to: type var ; var = x ; Token const * declEnd = declEndToken(); if ((Token::Match(declEnd, "; %name% =") && declEnd->strAt(1) == mNameToken->str()) || Token::Match(declEnd, "=|{")) setFlag(fHasDefault, true); } if (mTypeStartToken) { if (Token::Match(mTypeStartToken, "float|double")) setFlag(fIsFloatType, true); } } void Variable::setValueType(const ValueType &valueType) { if (valueType.type == ValueType::Type::UNKNOWN_TYPE) { const Token *declType = Token::findsimplematch(mTypeStartToken, "decltype (", mTypeEndToken); if (declType && !declType->next()->valueType()) return; } delete mValueType; mValueType = new ValueType(valueType); if ((mValueType->pointer > 0) && (!isArray() || Token::Match(mNameToken->previous(), "( * %name% )"))) setFlag(fIsPointer, true); setFlag(fIsConst, mValueType->constness & (1U << mValueType->pointer)); if (mValueType->smartPointerType) setFlag(fIsSmartPointer, true); } const Type *Variable::smartPointerType() const { if (!isSmartPointer()) return nullptr; if (mValueType->smartPointerType) return mValueType->smartPointerType; // TODO: Cache result const Token *ptrType = typeStartToken(); while (Token::Match(ptrType, "%name%|::")) ptrType = ptrType->next(); if (Token::Match(ptrType, "< %name% >")) return ptrType->scope()->findType(ptrType->next()->str()); return nullptr; } std::string Variable::getTypeName() const { std::string ret; // TODO: For known types, generate the full type name for (const Token *typeTok = mTypeStartToken; Token::Match(typeTok, "%name%|::") && typeTok->varId() == 0; typeTok = typeTok->next()) ret += typeTok->str(); return ret; } static bool isOperator(const Token *tokenDef) { if (!tokenDef) return false; if (tokenDef->isOperatorKeyword()) return true; const std::string &name = tokenDef->str(); return name.size() > 8 && name.compare(0,8,"operator")==0 && std::strchr("+-*/%&|~^<>!=[(", name[8]); } Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef) : tokenDef(tokDef), argDef(tokArgDef), token(nullptr), arg(nullptr), retDef(nullptr), retType(nullptr), functionScope(nullptr), nestedIn(scope), initArgCount(0), type(eFunction), access(AccessControl::Public), noexceptArg(nullptr), throwArg(nullptr), templateDef(nullptr), functionPointerUsage(nullptr), mFlags(0) { // operator function if (::isOperator(tokenDef)) { isOperator(true); // 'operator =' is special if (tokenDef->str() == "operator=") type = Function::eOperatorEqual; } else if (tokenDef->str() == "[") { type = Function::eLambda; } // class constructor/destructor else if (((tokenDef->str() == scope->className) || (tokenDef->str().substr(0, scope->className.size()) == scope->className && tokenDef->str().size() > scope->className.size() + 1 && tokenDef->str()[scope->className.size() + 1] == '<')) && scope->type != Scope::ScopeType::eNamespace) { // destructor if (tokenDef->previous()->str() == "~") type = Function::eDestructor; // constructor of any kind else type = Function::eConstructor; isExplicit(tokenDef->strAt(-1) == "explicit" || tokenDef->strAt(-2) == "explicit"); } const Token *tok1 = setFlags(tok, scope); // find the return type if (!isConstructor() && !isDestructor() && !isLambda()) { // @todo auto type deduction should be checked // @todo attributes and exception specification can also precede trailing return type if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type hasTrailingReturnType(true); if (argDef->link()->strAt(1) == ".") retDef = argDef->link()->tokAt(2); else if (argDef->link()->strAt(2) == ".") retDef = argDef->link()->tokAt(3); else if (argDef->link()->strAt(3) == ".") retDef = argDef->link()->tokAt(4); } else { if (tok1->str() == ">") tok1 = tok1->next(); while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum")) tok1 = tok1->next(); retDef = tok1; } } const Token *end = argDef->link(); // parse function attributes.. tok = end->next(); while (tok) { if (tok->str() == "const") isConst(true); else if (tok->str() == "&") hasLvalRefQualifier(true); else if (tok->str() == "&&") hasRvalRefQualifier(true); else if (tok->str() == "override") setFlag(fHasOverrideSpecifier, true); else if (tok->str() == "final") setFlag(fHasFinalSpecifier, true); else if (tok->str() == "volatile") isVolatile(true); else if (tok->str() == "noexcept") { isNoExcept(!Token::simpleMatch(tok->next(), "( false )")); if (tok->next()->str() == "(") tok = tok->linkAt(1); } else if (Token::simpleMatch(tok, "throw (")) { isThrow(true); if (tok->strAt(2) != ")") throwArg = tok->next(); tok = tok->linkAt(1); } else if (Token::Match(tok, "= 0|default|delete ;")) { const std::string& modifier = tok->strAt(1); isPure(modifier == "0"); isDefault(modifier == "default"); isDelete(modifier == "delete"); } else if (tok->str() == ".") { // trailing return type // skip over return type while (tok && !Token::Match(tok->next(), ";|{|override|final")) tok = tok->next(); } else break; if (tok) tok = tok->next(); } if (mTokenizer->isFunctionHead(end, ":{")) { // assume implementation is inline (definition and implementation same) token = tokenDef; arg = argDef; isInline(true); hasBody(true); } } Function::Function(const Token *tokenDef, const std::string &clangType) : tokenDef(tokenDef), argDef(nullptr), token(nullptr), arg(nullptr), retDef(nullptr), retType(nullptr), functionScope(nullptr), nestedIn(nullptr), initArgCount(0), type(eFunction), access(AccessControl::Public), noexceptArg(nullptr), throwArg(nullptr), templateDef(nullptr), functionPointerUsage(nullptr), mFlags(0) { // operator function if (::isOperator(tokenDef)) { isOperator(true); // 'operator =' is special if (tokenDef->str() == "operator=") type = Function::eOperatorEqual; } setFlags(tokenDef, tokenDef->scope()); if (endsWith(clangType, " const")) isConst(true); } const Token *Function::setFlags(const Token *tok1, const Scope *scope) { if (tok1->isInline()) isInlineKeyword(true); // look for end of previous statement while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{|public:|protected:|private:")) { tok1 = tok1->previous(); if (tok1->isInline()) isInlineKeyword(true); // extern function if (tok1->str() == "extern") { isExtern(true); } // virtual function else if (tok1->str() == "virtual") { hasVirtualSpecifier(true); } // static function else if (tok1->str() == "static") { isStatic(true); if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) isStaticLocal(true); } // friend function else if (tok1->str() == "friend") { isFriend(true); } // constexpr function else if (tok1->str() == "constexpr") { isConstexpr(true); } // decltype else if (tok1->str() == ")" && Token::simpleMatch(tok1->link()->previous(), "decltype (")) { tok1 = tok1->link()->previous(); } // Function template else if (tok1->link() && tok1->str() == ">" && Token::simpleMatch(tok1->link()->previous(), "template <")) { templateDef = tok1->link()->previous(); break; } } return tok1; } std::string Function::fullName() const { std::string ret = name(); for (const Scope *s = nestedIn; s; s = s->nestedIn) { if (!s->className.empty()) ret = s->className + "::" + ret; } ret += "("; for (const Variable &a : argumentList) ret += (a.index() == 0 ? "" : ",") + a.name(); return ret + ")"; } static std::string qualifiedName(const Scope *scope) { std::string name = scope->className; while (scope->nestedIn) { if (!scope->nestedIn->className.empty()) name = (scope->nestedIn->className + " :: ") + name; scope = scope->nestedIn; } return name; } static bool usingNamespace(const Scope *scope, const Token *first, const Token *second, int &offset) { // check if qualifications match first before checking if using is needed const Token *tok1 = first; const Token *tok2 = second; bool match = false; while (Token::Match(tok1, "%type% :: %type%") && Token::Match(tok2, "%type% :: %type%")) { if (tok1->str() == tok2->str()) { tok1 = tok1->tokAt(2); tok2 = tok2->tokAt(2); match = true; } else { match = false; break; } } if (match) return false; offset = 0; std::string name = first->str(); while (Token::Match(first, "%type% :: %type%")) { if (offset) name += (" :: " + first->str()); offset += 2; first = first->tokAt(2); if (first->str() == second->str()) { break; } } if (offset) { while (scope) { for (const auto & info : scope->usingList) { if (info.scope) { if (name == qualifiedName(info.scope)) return true; } // no scope so get name from using else { const Token *start = info.start->tokAt(2); std::string nsName; while (start && start->str() != ";") { if (!nsName.empty()) nsName += " "; nsName += start->str(); start = start->next(); } if (nsName == name) return true; } } scope = scope->nestedIn; } } return false; } static bool typesMatch( const Scope *first_scope, const Token *first_token, const Scope *second_scope, const Token *second_token, const Token **new_first, const Token **new_second) { // get first type const Type * first_type = first_scope->check->findType(first_token, first_scope); if (first_type) { // get second type const Type * second_type = second_scope->check->findType(second_token, second_scope); // check if types match if (first_type == second_type) { const Token* tok1 = first_token; while (tok1 && tok1->str() != first_type->name()) tok1 = tok1->next(); const Token *tok2 = second_token; while (tok2 && tok2->str() != second_type->name()) tok2 = tok2->next(); // update parser token positions if (tok1 && tok2) { *new_first = tok1->previous(); *new_second = tok2->previous(); return true; } } } return false; } bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const { const bool isCPP = scope->check->isCPP(); if (!isCPP) // C does not support overloads return true; int arg_path_length = path_length; int offset = 0; int openParen = 0; // check for () == (void) and (void) == () if ((Token::simpleMatch(first, "( )") && Token::simpleMatch(second, "( void )")) || (Token::simpleMatch(first, "( void )") && Token::simpleMatch(second, "( )"))) return true; while (first->str() == second->str() && first->isLong() == second->isLong() && first->isUnsigned() == second->isUnsigned()) { if (first->str() == "(") openParen++; // at end of argument list else if (first->str() == ")") { if (openParen == 1) return true; else --openParen; } // skip optional type information if (Token::Match(first->next(), "struct|enum|union|class")) first = first->next(); if (Token::Match(second->next(), "struct|enum|union|class")) second = second->next(); // skip const on type passed by value if (Token::Match(first->next(), "const %type% %name%|,|)") && !Token::Match(first->next(), "const %type% %name%| [")) first = first->next(); if (Token::Match(second->next(), "const %type% %name%|,|)") && !Token::Match(second->next(), "const %type% %name%| [")) second = second->next(); // skip default value assignment else if (first->next()->str() == "=") { first = first->nextArgument(); if (first) first = first->tokAt(-2); if (second->next()->str() == "=") { second = second->nextArgument(); if (second) second = second->tokAt(-2); if (!first || !second) { // End of argument list (first or second) return !first && !second; } } else if (!first) { // End of argument list (first) return !second->nextArgument(); // End of argument list (second) } } else if (second->next()->str() == "=") { second = second->nextArgument(); if (second) second = second->tokAt(-2); if (!second) { // End of argument list (second) return false; } } // definition missing variable name else if ((first->next()->str() == "," && second->next()->str() != ",") || (Token::Match(first, "!!( )") && second->next()->str() != ")")) { second = second->next(); // skip default value assignment if (second->next()->str() == "=") { do { second = second->next(); } while (!Token::Match(second->next(), ",|)")); } } else if (first->next()->str() == "[" && second->next()->str() != "[") second = second->next(); // function missing variable name else if ((second->next()->str() == "," && first->next()->str() != ",") || (Token::Match(second, "!!( )") && first->next()->str() != ")")) { first = first->next(); // skip default value assignment if (first->next()->str() == "=") { do { first = first->next(); } while (!Token::Match(first->next(), ",|)")); } } else if (second->next()->str() == "[" && first->next()->str() != "[") first = first->next(); // argument list has different number of arguments else if (openParen == 1 && second->str() == ")" && first->str() != ")") break; // ckeck for type * x == type x[] else if (Token::Match(first->next(), "* %name%| ,|)|=") && Token::Match(second->next(), "%name%| [ ] ,|)")) { do { first = first->next(); } while (!Token::Match(first->next(), ",|)")); do { second = second->next(); } while (!Token::Match(second->next(), ",|)")); } // const after * else if (first->next()->str() == "*" && second->next()->str() == "*" && ((first->strAt(2) != "const" && second->strAt(2) == "const") || (first->strAt(2) == "const" && second->strAt(2) != "const"))) { if (first->strAt(2) != "const") { first = first->next(); second = second->tokAt(2); } else { first = first->tokAt(2); second = second->next(); } } // variable names are different else if ((Token::Match(first->next(), "%name% ,|)|=|[") && Token::Match(second->next(), "%name% ,|)|[")) && (first->next()->str() != second->next()->str())) { // skip variable names first = first->next(); second = second->next(); // skip default value assignment if (first->next()->str() == "=") { do { first = first->next(); } while (!Token::Match(first->next(), ",|)")); } } // using namespace else if (usingNamespace(scope, first->next(), second->next(), offset)) first = first->tokAt(offset); // same type with different qualification else if (typesMatch(scope, first->next(), nestedIn, second->next(), &first, &second)) ; // variable with class path else if (arg_path_length && Token::Match(first->next(), "%name%") && first->strAt(1) != "const") { std::string param = path; if (Token::simpleMatch(second->next(), param.c_str(), param.size())) { // check for redundant qualification before skipping it if (!Token::simpleMatch(first->next(), param.c_str(), param.size())) { second = second->tokAt(int(arg_path_length)); arg_path_length = 0; } } // nested or base class variable else if (arg_path_length <= 2 && Token::Match(first->next(), "%name%") && (Token::Match(second->next(), "%name% :: %name%") || (Token::Match(second->next(), "%name% <") && Token::Match(second->linkAt(1), "> :: %name%"))) && ((second->next()->str() == scope->className) || (scope->definedType && scope->definedType->isDerivedFrom(second->next()->str()))) && (first->next()->str() == second->strAt(3))) { if (Token::Match(second->next(), "%name% <")) second = second->linkAt(1)->next(); else second = second->tokAt(2); } // remove class name else if (arg_path_length > 2 && first->strAt(1) != second->strAt(1)) { std::string short_path = path; unsigned int short_path_length = arg_path_length; // remove last " :: " short_path.resize(short_path.size() - 4); short_path_length--; // remove last name std::string::size_type lastSpace = short_path.find_last_of(' '); if (lastSpace != std::string::npos) { short_path.resize(lastSpace+1); short_path_length--; if (short_path[short_path.size() - 1] == '>') { short_path.resize(short_path.size() - 3); while (short_path[short_path.size() - 1] == '<') { lastSpace = short_path.find_last_of(' '); short_path.resize(lastSpace+1); short_path_length--; } } } param = short_path; if (Token::simpleMatch(second->next(), param.c_str(), param.size())) { second = second->tokAt(int(short_path_length)); arg_path_length = 0; } } } first = first->next(); second = second->next(); // reset path length if (first->str() == "," || second->str() == ",") arg_path_length = path_length; } return false; } static bool isUnknownType(const Token* start, const Token* end) { while (Token::Match(start, "const|volatile")) start = start->next(); start = skipScopeIdentifiers(start); if (start->tokAt(1) == end && !start->type() && !start->isStandardType()) return true; // TODO: Try to deduce the type of the expression if (Token::Match(start, "decltype|typeof")) return true; return false; } bool Function::returnsConst(const Function* function, bool unknown) { if (!function) return false; if (function->type != Function::eFunction) return false; const Token* defEnd = function->returnDefEnd(); if (Token::findsimplematch(function->retDef, "const", defEnd)) return true; // Check for unknown types, which could be a const if (isUnknownType(function->retDef, defEnd)) return unknown; return false; } bool Function::returnsReference(const Function* function, bool unknown) { if (!function) return false; if (function->type != Function::eFunction) return false; const Token* defEnd = function->returnDefEnd(); if (defEnd->strAt(-1) == "&") return true; // Check for unknown types, which could be a reference if (isUnknownType(function->retDef, defEnd)) return unknown; return false; } bool Function::returnsVoid(const Function* function, bool unknown) { if (!function) return false; if (function->type != Function::eFunction && function->type != Function::eOperatorEqual) return false; const Token* defEnd = function->returnDefEnd(); if (defEnd->strAt(-1) == "void") return true; // Check for unknown types, which could be a void type if (isUnknownType(function->retDef, defEnd)) return unknown; if (unknown) { // void STDCALL foo() const Token *def; bool isVoid = false; for (def = function->retDef; def && def->isName(); def = def->next()) isVoid |= (def->str() == "void"); if (isVoid && def && !Token::Match(def, "*|&|&&")) return true; } return false; } std::vector Function::findReturns(const Function* f) { std::vector result; if (!f) return result; const Scope* scope = f->functionScope; if (!scope) return result; for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "{" && tok->scope() && (tok->scope()->type == Scope::eLambda || tok->scope()->type == Scope::eClass)) { tok = tok->link(); continue; } if (Token::simpleMatch(tok->astParent(), "return")) { result.push_back(tok); } // Skip lambda functions since the scope may not be set correctly const Token* lambdaEndToken = findLambdaEndToken(tok); if (lambdaEndToken) { tok = lambdaEndToken; } } return result; } const Token * Function::constructorMemberInitialization() const { if (!isConstructor() || !arg) return nullptr; if (Token::simpleMatch(arg->link(), ") :")) return arg->link()->next(); if (Token::simpleMatch(arg->link(), ") noexcept (") && arg->link()->linkAt(2)->strAt(1) == ":") return arg->link()->linkAt(2)->next(); return nullptr; } bool Function::isSafe(const Settings *settings) const { if (settings->safeChecks.externalFunctions) { if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() != 0) return true; if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() != 0 || !isStatic())) return true; } if (settings->safeChecks.internalFunctions) { if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() == 0) return true; if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() == 0 || isStatic())) return true; } if (settings->safeChecks.classes && access == AccessControl::Public && (nestedIn->type == Scope::ScopeType::eClass || nestedIn->type == Scope::ScopeType::eStruct)) return true; return false; } Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart) { Function* function = nullptr; // Lambda functions are always unique if (tok->str() != "[") { for (std::multimap::iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) { const Function *f = i->second; if (f->hasBody()) continue; if (f->argsMatch(scope, f->argDef, argStart, emptyString, 0)) { function = const_cast(i->second); break; } } } if (!function) function = addGlobalFunctionDecl(scope, tok, argStart, funcStart); function->arg = argStart; function->token = funcStart; function->hasBody(true); addNewFunction(&scope, &tok); if (scope) { scope->function = function; function->functionScope = scope; return function; } return nullptr; } Function* SymbolDatabase::addGlobalFunctionDecl(Scope*& scope, const Token *tok, const Token *argStart, const Token* funcStart) { Function function(mTokenizer, tok, scope, funcStart, argStart); scope->addFunction(function); return &scope->functionList.back(); } void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const Token *argStart) { const bool destructor((*tok)->previous()->str() == "~"); const bool has_const(argStart->link()->strAt(1) == "const"); const bool lval(argStart->link()->strAt(has_const ? 2 : 1) == "&"); const bool rval(argStart->link()->strAt(has_const ? 2 : 1) == "&&"); int count = 0; std::string path; unsigned int path_length = 0; const Token *tok1 = (*tok); if (destructor) tok1 = tok1->previous(); // back up to head of path while (tok1 && tok1->previous() && tok1->previous()->str() == "::" && tok1->tokAt(-2) && ((tok1->tokAt(-2)->isName() && !tok1->tokAt(-2)->isStandardType()) || (tok1->strAt(-2) == ">" && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->previous(), "%name%")))) { count++; const Token * tok2 = tok1->tokAt(-2); if (tok2->str() == ">") tok2 = tok2->link()->previous(); if (tok2) { do { path = tok1->previous()->str() + " " + path; tok1 = tok1->previous(); path_length++; } while (tok1 != tok2); } else return; // syntax error ? } // syntax error? if (!tok1) return; // add global namespace if present if (tok1->strAt(-1) == "::") { path_length++; path.insert(0, ":: "); } std::list::iterator it1; // search for match for (it1 = scopeList.begin(); it1 != scopeList.end(); ++it1) { Scope *scope1 = &(*it1); bool match = false; // check in namespace if using found if (*scope == scope1 && !scope1->usingList.empty()) { std::list::const_iterator it2; for (it2 = scope1->usingList.begin(); it2 != scope1->usingList.end(); ++it2) { if (it2->scope) { Function * func = findFunctionInScope(tok1, it2->scope, path, path_length); if (func) { if (!func->hasBody()) { const Token *closeParen = (*tok)->next()->link(); if (closeParen) { const Token *eq = mTokenizer->isFunctionHead(closeParen, ";"); if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { func->isDefault(true); return; } } func->hasBody(true); func->token = *tok; func->arg = argStart; addNewFunction(scope, tok); if (*scope) { (*scope)->functionOf = func->nestedIn; (*scope)->function = func; (*scope)->function->functionScope = *scope; } return; } } } } } if (scope1->className == tok1->str() && (scope1->type != Scope::eFunction)) { // do the scopes match (same scope) or do their names match (multiple namespaces) if ((*scope == scope1->nestedIn) || (*scope && (*scope)->className == scope1->nestedIn->className && !(*scope)->className.empty() && (*scope)->type == scope1->nestedIn->type)) { // nested scopes => check that they match { const Scope *s1 = *scope; const Scope *s2 = scope1->nestedIn; while (s1 && s2) { if (s1->className != s2->className) break; s1 = s1->nestedIn; s2 = s2->nestedIn; } // Not matching scopes if (s1 || s2) continue; } Scope *scope2 = scope1; while (scope2 && count > 1) { count--; if (tok1->strAt(1) == "<") tok1 = tok1->linkAt(1)->tokAt(2); else tok1 = tok1->tokAt(2); scope2 = scope2->findRecordInNestedList(tok1->str()); } if (count == 1 && scope2) { match = true; scope1 = scope2; } } } if (match) { for (std::multimap::iterator it = scope1->functionMap.find((*tok)->str()); it != scope1->functionMap.end() && it->first == (*tok)->str(); ++it) { Function * func = const_cast(it->second); if (!func->hasBody()) { if (func->argsMatch(scope1, func->argDef, (*tok)->next(), path, path_length)) { if (func->type == Function::eDestructor && destructor) { func->hasBody(true); } else if (func->type != Function::eDestructor && !destructor) { // normal function? const Token *closeParen = (*tok)->next()->link(); if (closeParen) { const Token *eq = mTokenizer->isFunctionHead(closeParen, ";"); if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { func->isDefault(true); return; } const bool hasConstKeyword = closeParen->next()->str() == "const"; if ((func->isConst() == hasConstKeyword) && (func->hasLvalRefQualifier() == lval) && (func->hasRvalRefQualifier() == rval)) { func->hasBody(true); } } } if (func->hasBody()) { func->token = *tok; func->arg = argStart; addNewFunction(scope, tok); if (*scope) { (*scope)->functionOf = scope1; (*scope)->function = func; (*scope)->function->functionScope = *scope; } return; } } } } } } // class function of unknown class addNewFunction(scope, tok); } void SymbolDatabase::addNewFunction(Scope **scope, const Token **tok) { const Token *tok1 = *tok; scopeList.emplace_back(this, tok1, *scope); Scope *newScope = &scopeList.back(); // find start of function '{' bool foundInitList = false; while (tok1 && tok1->str() != "{" && tok1->str() != ";") { if (tok1->link() && Token::Match(tok1, "(|[|<")) { tok1 = tok1->link(); } else if (foundInitList && Token::Match(tok1, "%name%|> {") && Token::Match(tok1->linkAt(1), "} ,|{")) { tok1 = tok1->linkAt(1); } else { if (tok1->str() == ":") foundInitList = true; tok1 = tok1->next(); } } if (tok1 && tok1->str() == "{") { newScope->setBodyStartEnd(tok1); // syntax error? if (!newScope->bodyEnd) { scopeList.pop_back(); while (tok1->next()) tok1 = tok1->next(); *scope = nullptr; *tok = tok1; return; } (*scope)->nestedList.push_back(newScope); *scope = newScope; *tok = tok1; } else { scopeList.pop_back(); *scope = nullptr; *tok = nullptr; } } bool Type::isClassType() const { return classScope && classScope->type == Scope::ScopeType::eClass; } bool Type::isEnumType() const { //We explicitly check for "enum" because a forward declared enum doesn't get its own scope return (classDef && classDef->str() == "enum") || (classScope && classScope->type == Scope::ScopeType::eEnum); } bool Type::isStructType() const { return classScope && classScope->type == Scope::ScopeType::eStruct; } bool Type::isUnionType() const { return classScope && classScope->type == Scope::ScopeType::eUnion; } const Token *Type::initBaseInfo(const Token *tok, const Token *tok1) { // goto initial '{' const Token *tok2 = tok1; while (tok2 && tok2->str() != "{") { // skip unsupported templates if (tok2->str() == "<") tok2 = tok2->link(); // check for base classes else if (Token::Match(tok2, ":|,")) { tok2 = tok2->next(); // check for invalid code if (!tok2 || !tok2->next()) return nullptr; Type::BaseInfo base; if (tok2->str() == "virtual") { base.isVirtual = true; tok2 = tok2->next(); } if (tok2->str() == "public") { base.access = AccessControl::Public; tok2 = tok2->next(); } else if (tok2->str() == "protected") { base.access = AccessControl::Protected; tok2 = tok2->next(); } else if (tok2->str() == "private") { base.access = AccessControl::Private; tok2 = tok2->next(); } else { if (tok->str() == "class") base.access = AccessControl::Private; else if (tok->str() == "struct") base.access = AccessControl::Public; } if (!tok2) return nullptr; if (tok2->str() == "virtual") { base.isVirtual = true; tok2 = tok2->next(); } if (!tok2) return nullptr; base.nameTok = tok2; // handle global namespace if (tok2->str() == "::") { tok2 = tok2->next(); } // handle derived base classes while (Token::Match(tok2, "%name% ::")) { tok2 = tok2->tokAt(2); } if (!tok2) return nullptr; base.name = tok2->str(); tok2 = tok2->next(); // add unhandled templates if (tok2 && tok2->link() && tok2->str() == "<") { for (const Token* const end = tok2->link()->next(); tok2 != end; tok2 = tok2->next()) { base.name += tok2->str(); } } const Type * baseType = classScope->check->findType(base.nameTok, enclosingScope); if (baseType && !baseType->findDependency(this)) base.type = baseType; // save pattern for base class name derivedFrom.push_back(base); } else tok2 = tok2->next(); } return tok2; } const std::string& Type::name() const { const Token* next = classDef->next(); if (classScope && classScope->enumClass && isEnumType()) return next->strAt(1); else if (next->str() == "class") return next->strAt(1); else if (next->isName()) return next->str(); return emptyString; } void SymbolDatabase::debugMessage(const Token *tok, const std::string &type, const std::string &msg) const { if (tok && mSettings->debugwarnings) { const std::list locationList(1, tok); const ErrorMessage errmsg(locationList, &mTokenizer->list, Severity::debug, type, msg, Certainty::normal); if (mErrorLogger) mErrorLogger->reportErr(errmsg); } } const Function* Type::getFunction(const std::string& funcName) const { if (classScope) { const std::multimap::const_iterator it = classScope->functionMap.find(funcName); if (it != classScope->functionMap.end()) return it->second; } for (const Type::BaseInfo & i : derivedFrom) { if (i.type) { const Function* const func = i.type->getFunction(funcName); if (func) return func; } } return nullptr; } bool Type::hasCircularDependencies(std::set* ancestors) const { std::set knownAncestors; if (!ancestors) { ancestors=&knownAncestors; } for (std::vector::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) { if (!parent->type) continue; else if (this==parent->type) return true; else if (ancestors->find(*parent)!=ancestors->end()) return true; else { ancestors->insert(*parent); if (parent->type->hasCircularDependencies(ancestors)) return true; } } return false; } bool Type::findDependency(const Type* ancestor) const { if (this==ancestor) return true; for (std::vector::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) { if (parent->type && (parent->type == this || parent->type->findDependency(ancestor))) return true; } return false; } bool Type::isDerivedFrom(const std::string & ancestor) const { for (std::vector::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) { if (parent->name == ancestor) return true; if (parent->type && parent->type->isDerivedFrom(ancestor)) return true; } return false; } bool Variable::arrayDimensions(const Settings* settings, bool* isContainer) { *isContainer = false; const Library::Container* container = settings->library.detectContainer(mTypeStartToken); if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) { const Token* tok = Token::findsimplematch(mTypeStartToken, "<"); if (tok) { *isContainer = true; Dimension dimension_; tok = tok->next(); for (int i = 0; i < container->size_templateArgNo && tok; i++) { tok = tok->nextTemplateArgument(); } if (Token::Match(tok, "%num% [,>]")) { dimension_.tok = tok; dimension_.known = true; dimension_.num = MathLib::toLongNumber(tok->str()); } else if (tok) { dimension_.tok = tok; dimension_.known = false; } mDimensions.push_back(dimension_); return true; } } const Token *dim = mNameToken; if (!dim) { // Argument without name dim = mTypeEndToken; // back up to start of array dimensions while (dim && dim->str() == "]") dim = dim->link()->previous(); } if (dim) dim = dim->next(); if (dim && dim->str() == ")") dim = dim->next(); bool arr = false; while (dim && dim->next() && dim->str() == "[") { Dimension dimension_; dimension_.known = false; // check for empty array dimension [] if (dim->next()->str() != "]") { dimension_.tok = dim->astOperand2(); ValueFlow::valueFlowConstantFoldAST(const_cast(dimension_.tok), settings); if (dimension_.tok && dimension_.tok->hasKnownIntValue()) { dimension_.num = dimension_.tok->getKnownIntValue(); dimension_.known = true; } } mDimensions.push_back(dimension_); dim = dim->link()->next(); arr = true; } return arr; } static std::ostream & operator << (std::ostream & s, Scope::ScopeType type) { s << (type == Scope::eGlobal ? "Global" : type == Scope::eClass ? "Class" : type == Scope::eStruct ? "Struct" : type == Scope::eUnion ? "Union" : type == Scope::eNamespace ? "Namespace" : type == Scope::eFunction ? "Function" : type == Scope::eIf ? "If" : type == Scope::eElse ? "Else" : type == Scope::eFor ? "For" : type == Scope::eWhile ? "While" : type == Scope::eDo ? "Do" : type == Scope::eSwitch ? "Switch" : type == Scope::eTry ? "Try" : type == Scope::eCatch ? "Catch" : type == Scope::eUnconditional ? "Unconditional" : type == Scope::eLambda ? "Lambda" : type == Scope::eEnum ? "Enum" : "Unknown"); return s; } static std::string accessControlToString(const AccessControl& access) { switch (access) { case AccessControl::Public: return "Public"; case AccessControl::Protected: return "Protected"; case AccessControl::Private: return "Private"; case AccessControl::Global: return "Global"; case AccessControl::Namespace: return "Namespace"; case AccessControl::Argument: return "Argument"; case AccessControl::Local: return "Local"; case AccessControl::Throw: return "Throw"; } return "Unknown"; } static std::string tokenToString(const Token* tok, const Tokenizer* tokenizer) { std::ostringstream oss; if (tok) { oss << tok->str() << " "; oss << tokenizer->list.fileLine(tok) << " "; } oss << tok; return oss.str(); } static std::string scopeToString(const Scope* scope, const Tokenizer* tokenizer) { std::ostringstream oss; if (scope) { oss << scope->type << " "; if (!scope->className.empty()) oss << scope->className << " "; if (scope->classDef) oss << tokenizer->list.fileLine(scope->classDef) << " "; } oss << scope; return oss.str(); } static std::string tokenType(const Token * tok) { std::ostringstream oss; if (tok) { if (tok->isUnsigned()) oss << "unsigned "; else if (tok->isSigned()) oss << "signed "; if (tok->isComplex()) oss << "_Complex "; if (tok->isLong()) oss << "long "; oss << tok->str(); } return oss.str(); } void SymbolDatabase::printVariable(const Variable *var, const char *indent) const { std::cout << indent << "mNameToken: " << tokenToString(var->nameToken(), mTokenizer) << std::endl; if (var->nameToken()) { std::cout << indent << " declarationId: " << var->declarationId() << std::endl; } std::cout << indent << "mTypeStartToken: " << tokenToString(var->typeStartToken(), mTokenizer) << std::endl; std::cout << indent << "mTypeEndToken: " << tokenToString(var->typeEndToken(), mTokenizer) << std::endl; if (var->typeStartToken()) { const Token * autoTok = nullptr; std::cout << indent << " "; for (const Token * tok = var->typeStartToken(); tok != var->typeEndToken()->next(); tok = tok->next()) { std::cout << " " << tokenType(tok); if (tok->str() == "auto") autoTok = tok; } std::cout << std::endl; if (autoTok) { const ValueType * valueType = autoTok->valueType(); std::cout << indent << " auto valueType: " << valueType << std::endl; if (var->typeStartToken()->valueType()) { std::cout << indent << " " << valueType->str() << std::endl; } } } else if (var->valueType()) { std::cout << indent << " " << var->valueType()->str() << std::endl; } std::cout << indent << "mIndex: " << var->index() << std::endl; std::cout << indent << "mAccess: " << accessControlToString(var->accessControl()) << std::endl; std::cout << indent << "mFlags: " << std::endl; std::cout << indent << " isMutable: " << var->isMutable() << std::endl; std::cout << indent << " isStatic: " << var->isStatic() << std::endl; std::cout << indent << " isExtern: " << var->isExtern() << std::endl; std::cout << indent << " isLocal: " << var->isLocal() << std::endl; std::cout << indent << " isConst: " << var->isConst() << std::endl; std::cout << indent << " isClass: " << var->isClass() << std::endl; std::cout << indent << " isArray: " << var->isArray() << std::endl; std::cout << indent << " isPointer: " << var->isPointer() << std::endl; std::cout << indent << " isReference: " << var->isReference() << std::endl; std::cout << indent << " isRValueRef: " << var->isRValueReference() << std::endl; std::cout << indent << " hasDefault: " << var->hasDefault() << std::endl; std::cout << indent << " isStlType: " << var->isStlType() << std::endl; std::cout << indent << "mType: "; if (var->type()) { std::cout << var->type()->type() << " " << var->type()->name(); std::cout << " " << mTokenizer->list.fileLine(var->type()->classDef); std::cout << " " << var->type() << std::endl; } else std::cout << "none" << std::endl; if (var->nameToken()) { const ValueType * valueType = var->nameToken()->valueType(); std::cout << indent << "valueType: " << valueType << std::endl; if (valueType) { std::cout << indent << " " << valueType->str() << std::endl; } } std::cout << indent << "mScope: " << scopeToString(var->scope(), mTokenizer) << std::endl; std::cout << indent << "mDimensions:"; for (std::size_t i = 0; i < var->dimensions().size(); i++) { std::cout << " " << var->dimension(i); if (!var->dimensions()[i].known) std::cout << "?"; } std::cout << std::endl; } void SymbolDatabase::printOut(const char *title) const { std::cout << std::setiosflags(std::ios::boolalpha); if (title) std::cout << "\n### " << title << " ###\n"; for (std::list::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) { std::cout << "Scope: " << &*scope << " " << scope->type << std::endl; std::cout << " className: " << scope->className << std::endl; std::cout << " classDef: " << tokenToString(scope->classDef, mTokenizer) << std::endl; std::cout << " bodyStart: " << tokenToString(scope->bodyStart, mTokenizer) << std::endl; std::cout << " bodyEnd: " << tokenToString(scope->bodyEnd, mTokenizer) << std::endl; std::list::const_iterator func; // find the function body if not implemented inline for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { std::cout << " Function: " << &*func << std::endl; std::cout << " name: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; std::cout << " type: " << (func->type == Function::eConstructor? "Constructor" : func->type == Function::eCopyConstructor ? "CopyConstructor" : func->type == Function::eMoveConstructor ? "MoveConstructor" : func->type == Function::eOperatorEqual ? "OperatorEqual" : func->type == Function::eDestructor ? "Destructor" : func->type == Function::eFunction ? "Function" : func->type == Function::eLambda ? "Lambda" : "Unknown") << std::endl; std::cout << " access: " << accessControlToString(func->access) << std::endl; std::cout << " hasBody: " << func->hasBody() << std::endl; std::cout << " isInline: " << func->isInline() << std::endl; std::cout << " isConst: " << func->isConst() << std::endl; std::cout << " hasVirtualSpecifier: " << func->hasVirtualSpecifier() << std::endl; std::cout << " isPure: " << func->isPure() << std::endl; std::cout << " isStatic: " << func->isStatic() << std::endl; std::cout << " isStaticLocal: " << func->isStaticLocal() << std::endl; std::cout << " isExtern: " << func->isExtern() << std::endl; std::cout << " isFriend: " << func->isFriend() << std::endl; std::cout << " isExplicit: " << func->isExplicit() << std::endl; std::cout << " isDefault: " << func->isDefault() << std::endl; std::cout << " isDelete: " << func->isDelete() << std::endl; std::cout << " hasOverrideSpecifier: " << func->hasOverrideSpecifier() << std::endl; std::cout << " hasFinalSpecifier: " << func->hasFinalSpecifier() << std::endl; std::cout << " isNoExcept: " << func->isNoExcept() << std::endl; std::cout << " isThrow: " << func->isThrow() << std::endl; std::cout << " isOperator: " << func->isOperator() << std::endl; std::cout << " hasLvalRefQual: " << func->hasLvalRefQualifier() << std::endl; std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl; std::cout << " isVariadic: " << func->isVariadic() << std::endl; std::cout << " isVolatile: " << func->isVolatile() << std::endl; std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl; std::cout << " attributes:"; if (func->isAttributeConst()) std::cout << " const "; if (func->isAttributePure()) std::cout << " pure "; if (func->isAttributeNoreturn()) std::cout << " noreturn "; if (func->isAttributeNothrow()) std::cout << " nothrow "; if (func->isAttributeConstructor()) std::cout << " constructor "; if (func->isAttributeDestructor()) std::cout << " destructor "; if (func->isAttributeNodiscard()) std::cout << " nodiscard "; std::cout << std::endl; std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl; std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl; std::cout << " tokenDef: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; std::cout << " argDef: " << tokenToString(func->argDef, mTokenizer) << std::endl; if (!func->isConstructor() && !func->isDestructor()) std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl; if (func->retDef) { std::cout << " "; for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;|override|final"); tok = tok->next()) std::cout << " " << tokenType(tok); std::cout << std::endl; } std::cout << " retType: " << func->retType << std::endl; if (func->tokenDef->next()->valueType()) { const ValueType * valueType = func->tokenDef->next()->valueType(); std::cout << " valueType: " << valueType << std::endl; if (valueType) { std::cout << " " << valueType->str() << std::endl; } } if (func->hasBody()) { std::cout << " token: " << tokenToString(func->token, mTokenizer) << std::endl; std::cout << " arg: " << tokenToString(func->arg, mTokenizer) << std::endl; } std::cout << " nestedIn: " << scopeToString(func->nestedIn, mTokenizer) << std::endl; std::cout << " functionScope: " << scopeToString(func->functionScope, mTokenizer) << std::endl; std::list::const_iterator var; for (var = func->argumentList.begin(); var != func->argumentList.end(); ++var) { std::cout << " Variable: " << &*var << std::endl; printVariable(&*var, " "); } } std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { std::cout << " Variable: " << &*var << std::endl; printVariable(&*var, " "); } if (scope->type == Scope::eEnum) { std::cout << " enumType: "; if (scope->enumType) { std::cout << scope->enumType->stringify(false, true, false); } else std::cout << "int"; std::cout << std::endl; std::cout << " enumClass: " << scope->enumClass << std::endl; for (const Enumerator &enumerator : scope->enumeratorList) { std::cout << " Enumerator: " << enumerator.name->str() << " = "; if (enumerator.value_known) std::cout << enumerator.value; if (enumerator.start) { const Token * tok = enumerator.start; std::cout << (enumerator.value_known ? " " : "") << "[" << tok->str(); while (tok && tok != enumerator.end) { if (tok->next()) std::cout << " " << tok->next()->str(); tok = tok->next(); } std::cout << "]"; } std::cout << std::endl; } } std::cout << " nestedIn: " << scope->nestedIn; if (scope->nestedIn) { std::cout << " " << scope->nestedIn->type << " " << scope->nestedIn->className; } std::cout << std::endl; std::cout << " definedType: " << scope->definedType << std::endl; std::cout << " nestedList[" << scope->nestedList.size() << "] = ("; std::list::const_iterator nsi; std::size_t count = scope->nestedList.size(); for (nsi = scope->nestedList.begin(); nsi != scope->nestedList.end(); ++nsi) { std::cout << " " << (*nsi) << " " << (*nsi)->type << " " << (*nsi)->className; if (count-- > 1) std::cout << ","; } std::cout << " )" << std::endl; std::list::const_iterator use; for (use = scope->usingList.begin(); use != scope->usingList.end(); ++use) { std::cout << " using: " << use->scope << " " << use->start->strAt(2); const Token *tok1 = use->start->tokAt(3); while (tok1 && tok1->str() == "::") { std::cout << "::" << tok1->strAt(1); tok1 = tok1->tokAt(2); } std::cout << " " << mTokenizer->list.fileLine(use->start) << std::endl; } std::cout << " functionOf: " << scopeToString(scope->functionOf, mTokenizer) << std::endl; std::cout << " function: " << scope->function; if (scope->function) std::cout << " " << scope->function->name(); std::cout << std::endl; } for (std::list::const_iterator type = typeList.begin(); type != typeList.end(); ++type) { std::cout << "Type: " << &(*type) << std::endl; std::cout << " name: " << type->name() << std::endl; std::cout << " classDef: " << tokenToString(type->classDef, mTokenizer) << std::endl; std::cout << " classScope: " << type->classScope << std::endl; std::cout << " enclosingScope: " << type->enclosingScope; if (type->enclosingScope) { std::cout << " " << type->enclosingScope->type << " " << type->enclosingScope->className; } std::cout << std::endl; std::cout << " needInitialization: " << (type->needInitialization == Type::NeedInitialization::Unknown ? "Unknown" : type->needInitialization == Type::NeedInitialization::True ? "True" : type->needInitialization == Type::NeedInitialization::False ? "False" : "Invalid") << std::endl; std::cout << " derivedFrom[" << type->derivedFrom.size() << "] = ("; std::size_t count = type->derivedFrom.size(); for (const Type::BaseInfo & i : type->derivedFrom) { if (i.isVirtual) std::cout << "Virtual "; std::cout << (i.access == AccessControl::Public ? " Public" : i.access == AccessControl::Protected ? " Protected" : i.access == AccessControl::Private ? " Private" : " Unknown"); if (i.type) std::cout << " " << i.type; else std::cout << " Unknown"; std::cout << " " << i.name; if (count-- > 1) std::cout << ","; } std::cout << " )" << std::endl; std::cout << " friendList[" << type->friendList.size() << "] = ("; for (size_t i = 0; i < type->friendList.size(); i++) { if (type->friendList[i].type) std::cout << type->friendList[i].type; else std::cout << " Unknown"; std::cout << ' '; if (type->friendList[i].nameEnd) std::cout << type->friendList[i].nameEnd->str(); if (i+1 < type->friendList.size()) std::cout << ','; } std::cout << " )" << std::endl; } for (std::size_t i = 1; i < mVariableList.size(); i++) { std::cout << "mVariableList[" << i << "]: " << mVariableList[i]; if (mVariableList[i]) { std::cout << " " << mVariableList[i]->name() << " " << mTokenizer->list.fileLine(mVariableList[i]->nameToken()); } std::cout << std::endl; } std::cout << std::resetiosflags(std::ios::boolalpha); } void SymbolDatabase::printXml(std::ostream &out) const { out << std::setiosflags(std::ios::boolalpha); std::set variables; // Scopes.. out << " " << std::endl; for (std::list::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) { out << " type << "\""; if (!scope->className.empty()) out << " className=\"" << ErrorLogger::toxml(scope->className) << "\""; if (scope->bodyStart) out << " bodyStart=\"" << scope->bodyStart << '\"'; if (scope->bodyEnd) out << " bodyEnd=\"" << scope->bodyEnd << '\"'; if (scope->nestedIn) out << " nestedIn=\"" << scope->nestedIn << "\""; if (scope->function) out << " function=\"" << scope->function << "\""; if (scope->functionList.empty() && scope->varlist.empty()) out << "/>" << std::endl; else { out << '>' << std::endl; if (!scope->functionList.empty()) { out << " " << std::endl; for (std::list::const_iterator function = scope->functionList.begin(); function != scope->functionList.end(); ++function) { out << " token << "\" tokenDef=\"" << function->tokenDef << "\" name=\"" << ErrorLogger::toxml(function->name()) << '\"'; out << " type=\"" << (function->type == Function::eConstructor? "Constructor" : function->type == Function::eCopyConstructor ? "CopyConstructor" : function->type == Function::eMoveConstructor ? "MoveConstructor" : function->type == Function::eOperatorEqual ? "OperatorEqual" : function->type == Function::eDestructor ? "Destructor" : function->type == Function::eFunction ? "Function" : function->type == Function::eLambda ? "Lambda" : "Unknown") << '\"'; if (function->nestedIn->definedType) { if (function->hasVirtualSpecifier()) out << " hasVirtualSpecifier=\"true\""; else if (function->isImplicitlyVirtual()) out << " isImplicitlyVirtual=\"true\""; } if (function->isInlineKeyword()) out << " isInlineKeyword=\"true\""; if (function->isStatic()) out << " isStatic=\"true\""; if (function->argCount() == 0U) out << "/>" << std::endl; else { out << ">" << std::endl; for (unsigned int argnr = 0; argnr < function->argCount(); ++argnr) { const Variable *arg = function->getArgumentVar(argnr); out << " " << std::endl; variables.insert(arg); } out << " " << std::endl; } } out << " " << std::endl; } if (!scope->varlist.empty()) { out << " " << std::endl; for (std::list::const_iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var) out << " " << std::endl; out << " " << std::endl; } out << " " << std::endl; } } out << " " << std::endl; // Variables.. for (const Variable *var : mVariableList) variables.insert(var); out << " " << std::endl; for (const Variable *var : variables) { if (!var) continue; out << " nameToken() << '\"'; out << " typeStartToken=\"" << var->typeStartToken() << '\"'; out << " typeEndToken=\"" << var->typeEndToken() << '\"'; out << " access=\"" << accessControlToString(var->mAccess) << '\"'; out << " scope=\"" << var->scope() << '\"'; if (var->valueType()) out << " constness=\"" << var->valueType()->constness << '\"'; out << " isArray=\"" << var->isArray() << '\"'; out << " isClass=\"" << var->isClass() << '\"'; out << " isConst=\"" << var->isConst() << '\"'; out << " isExtern=\"" << var->isExtern() << '\"'; out << " isPointer=\"" << var->isPointer() << '\"'; out << " isReference=\"" << var->isReference() << '\"'; out << " isStatic=\"" << var->isStatic() << '\"'; out << " isVolatile=\"" << var->isVolatile() << '\"'; out << "/>" << std::endl; } out << " " << std::endl; out << std::resetiosflags(std::ios::boolalpha); } //--------------------------------------------------------------------------- static const Type* findVariableTypeIncludingUsedNamespaces(const SymbolDatabase* symbolDatabase, const Scope* scope, const Token* typeTok) { const Type* argType = symbolDatabase->findVariableType(scope, typeTok); if (argType) return argType; // look for variable type in any using namespace in this scope or above while (scope) { for (const Scope::UsingInfo &ui : scope->usingList) { if (ui.scope) { argType = symbolDatabase->findVariableType(ui.scope, typeTok); if (argType) return argType; } } scope = scope->nestedIn; } return nullptr; } //--------------------------------------------------------------------------- void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope) { // check for non-empty argument list "( ... )" const Token * start = arg ? arg : argDef; if (!Token::simpleMatch(start, "(")) return; if (!(start && start->link() != start->next() && !Token::simpleMatch(start, "( void )"))) return; unsigned int count = 0; for (const Token* tok = start->next(); tok; tok = tok->next()) { if (Token::Match(tok, ",|)")) return; // Syntax error const Token* startTok = tok; const Token* endTok = nullptr; const Token* nameTok = nullptr; do { if (Token::simpleMatch(tok, "decltype (")) { tok = tok->linkAt(1)->next(); continue; } if (tok != startTok && !nameTok && Token::Match(tok, "( & %var% ) [")) { nameTok = tok->tokAt(2); endTok = nameTok->previous(); tok = tok->link(); } else if (tok != startTok && !nameTok && Token::Match(tok, "( * %var% ) ( ) [,)]")) { nameTok = tok->tokAt(2); endTok = nameTok->previous(); tok = tok->link()->tokAt(2); } else if (tok != startTok && !nameTok && Token::Match(tok, "( * %var% ) [")) { nameTok = tok->tokAt(2); endTok = nameTok->previous(); tok = tok->link(); } else if (tok->varId() != 0) { nameTok = tok; endTok = tok->previous(); } else if (tok->str() == "[") { // skip array dimension(s) tok = tok->link(); while (tok->next()->str() == "[") tok = tok->next()->link(); } else if (tok->str() == "<") { tok = tok->link(); if (!tok) // something is wrong so just bail out return; } tok = tok->next(); if (!tok) // something is wrong so just bail return; } while (tok->str() != "," && tok->str() != ")" && tok->str() != "="); const Token *typeTok = startTok; // skip over stuff to get to type while (Token::Match(typeTok, "const|volatile|enum|struct|::")) typeTok = typeTok->next(); if (Token::Match(typeTok, ",|)")) { // #8333 symbolDatabase->mTokenizer->syntaxError(typeTok); } // skip over qualification while (Token::Match(typeTok, "%type% ::")) typeTok = typeTok->tokAt(2); // check for argument with no name or missing varid if (!endTok) { if (tok->previous()->isName() && !Token::Match(tok->tokAt(-1), "const|volatile")) { if (tok->previous() != typeTok) { nameTok = tok->previous(); endTok = nameTok->previous(); if (hasBody()) symbolDatabase->debugMessage(nameTok, "varid0", "Function::addArguments found argument \'" + nameTok->str() + "\' with varid 0."); } else endTok = typeTok; } else endTok = tok->previous(); } const ::Type *argType = nullptr; if (!typeTok->isStandardType()) { argType = findVariableTypeIncludingUsedNamespaces(symbolDatabase, scope, typeTok); // save type const_cast(typeTok)->type(argType); } // skip default values if (tok->str() == "=") { do { if (tok->link() && Token::Match(tok, "[{[(<]")) tok = tok->link(); tok = tok->next(); } while (tok->str() != "," && tok->str() != ")"); } // skip over stuff before type while (Token::Match(startTok, "enum|struct|const|volatile")) startTok = startTok->next(); if (startTok == nameTok) break; argumentList.emplace_back(nameTok, startTok, endTok, count++, AccessControl::Argument, argType, functionScope, symbolDatabase->mSettings); if (tok->str() == ")") { // check for a variadic function or a variadic template function if (Token::simpleMatch(endTok, "...")) isVariadic(true); break; } } // count default arguments for (const Token* tok = argDef->next(); tok && tok != argDef->link(); tok = tok->next()) { if (tok->str() == "=") { initArgCount++; if (tok->strAt(1) == "[") { const Token* lambdaStart = tok->next(); tok = findLambdaEndToken(lambdaStart); if (!tok) throw InternalError(lambdaStart, "Analysis failed (lambda not recognized). If the code is valid then please report this failure.", InternalError::INTERNAL); } } } } bool Function::isImplicitlyVirtual(bool defaultVal) const { if (hasVirtualSpecifier()) //If it has the virtual specifier it's definitely virtual return true; if (hasOverrideSpecifier()) //If it has the override specifier then it's either virtual or not going to compile return true; bool foundAllBaseClasses = true; if (getOverriddenFunction(&foundAllBaseClasses)) //If it overrides a base class's method then it's virtual return true; if (foundAllBaseClasses) //If we've seen all the base classes and none of the above were true then it must not be virtual return false; return defaultVal; //If we can't see all the bases classes then we can't say conclusively } std::vector Function::getOverloadedFunctions() const { std::vector result; const Scope* scope = nestedIn; while (scope) { const bool isMemberFunction = scope->isClassOrStruct() && !isStatic(); for (std::multimap::const_iterator it = scope->functionMap.find(tokenDef->str()); it != scope->functionMap.end() && it->first == tokenDef->str(); ++it) { const Function* func = it->second; if (isMemberFunction == func->isStatic()) continue; result.push_back(func); } if (isMemberFunction) break; scope = scope->nestedIn; } return result; } const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const { if (foundAllBaseClasses) *foundAllBaseClasses = true; if (!nestedIn->isClassOrStruct()) return nullptr; return getOverriddenFunctionRecursive(nestedIn->definedType, foundAllBaseClasses); } const Function * Function::getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const { // check each base class for (const ::Type::BaseInfo & i : baseType->derivedFrom) { const ::Type* derivedFromType = i.type; // check if base class exists in database if (!derivedFromType || !derivedFromType->classScope) { if (foundAllBaseClasses) *foundAllBaseClasses = false; continue; } const Scope *parent = derivedFromType->classScope; // check if function defined in base class for (std::multimap::const_iterator it = parent->functionMap.find(tokenDef->str()); it != parent->functionMap.end() && it->first == tokenDef->str(); ++it) { const Function * func = it->second; if (func->hasVirtualSpecifier()) { // Base is virtual and of same name const Token *temp1 = func->tokenDef->previous(); const Token *temp2 = tokenDef->previous(); bool match = true; // check for matching return parameters while (temp1->str() != "virtual") { if (temp1->str() != temp2->str() && !(temp1->str() == derivedFromType->name() && temp2->str() == baseType->name())) { match = false; break; } temp1 = temp1->previous(); temp2 = temp2->previous(); } // check for matching function parameters match = match && argsMatch(baseType->classScope, func->argDef, argDef, emptyString, 0); // check for matching cv-ref qualifiers match = match && isConst() == func->isConst() && isVolatile() == func->isVolatile() && hasRvalRefQualifier() == func->hasRvalRefQualifier() && hasLvalRefQualifier() == func->hasLvalRefQualifier(); // it's a match if (match) { return func; } } } if (!derivedFromType->derivedFrom.empty() && !derivedFromType->hasCircularDependencies()) { // avoid endless recursion, see #5289 Crash: Stack overflow in isImplicitlyVirtual_rec when checking SVN and // #5590 with a loop within the class hierarchy. const Function *func = getOverriddenFunctionRecursive(derivedFromType, foundAllBaseClasses); if (func) { return func; } } } return nullptr; } const Variable* Function::getArgumentVar(nonneg int num) const { for (std::list::const_iterator i = argumentList.begin(); i != argumentList.end(); ++i) { if (i->index() == num) return (&*i); else if (i->index() > num) return nullptr; } return nullptr; } //--------------------------------------------------------------------------- Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_) : check(check_), classDef(classDef_), nestedIn(nestedIn_), numConstructors(0), numCopyOrMoveConstructors(0), type(type_), definedType(nullptr), functionOf(nullptr), function(nullptr), enumType(nullptr), enumClass(false) { setBodyStartEnd(start_); } Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_) : check(check_), classDef(classDef_), bodyStart(nullptr), bodyEnd(nullptr), nestedIn(nestedIn_), numConstructors(0), numCopyOrMoveConstructors(0), definedType(nullptr), functionOf(nullptr), function(nullptr), enumType(nullptr), enumClass(false) { const Token *nameTok = classDef; if (!classDef) { type = Scope::eGlobal; } else if (classDef->str() == "class" && check && check->isCPP()) { type = Scope::eClass; nameTok = nameTok->next(); } else if (classDef->str() == "struct") { type = Scope::eStruct; nameTok = nameTok->next(); } else if (classDef->str() == "union") { type = Scope::eUnion; nameTok = nameTok->next(); } else if (classDef->str() == "namespace") { type = Scope::eNamespace; nameTok = nameTok->next(); } else if (classDef->str() == "enum") { type = Scope::eEnum; nameTok = nameTok->next(); if (nameTok->str() == "class") { enumClass = true; nameTok = nameTok->next(); } } else if (classDef->str() == "[") { type = Scope::eLambda; } else { type = Scope::eFunction; } // skip over qualification if present nameTok = skipScopeIdentifiers(nameTok); if (nameTok && ((type == Scope::eEnum && Token::Match(nameTok, ":|{")) || nameTok->str() != "{")) // anonymous and unnamed structs/unions don't have a name className = nameTok->str(); } bool Scope::hasDefaultConstructor() const { if (numConstructors) { std::list::const_iterator func; for (func = functionList.begin(); func != functionList.end(); ++func) { if (func->type == Function::eConstructor && func->argCount() == 0) return true; } } return false; } AccessControl Scope::defaultAccess() const { switch (type) { case eGlobal: return AccessControl::Global; case eClass: return AccessControl::Private; case eStruct: return AccessControl::Public; case eUnion: return AccessControl::Public; case eNamespace: return AccessControl::Namespace; default: return AccessControl::Local; } } void Scope::addVariable(const Token *token_, const Token *start_, const Token *end_, AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings) { // keep possible size_t -> int truncation outside emplace_back() to have a single line // C4267 VC++ warning instead of several dozens lines const int varIndex = varlist.size(); varlist.emplace_back(token_, start_, end_, varIndex, access_, type_, scope_, settings); } // Get variable list.. void Scope::getVariableList(const Settings* settings) { if (!bodyStartList.empty()) { for (const Token *bs: bodyStartList) getVariableList(settings, bs->next(), bs->link()); } // global scope else if (type == Scope::eGlobal) getVariableList(settings, check->mTokenizer->tokens(), nullptr); // forward declaration else return; } void Scope::getVariableList(const Settings* settings, const Token* start, const Token* end) { // Variable declared in condition: if (auto x = bar()) if (Token::Match(classDef, "if|while ( %type%") && Token::simpleMatch(classDef->next()->astOperand2(), "=")) { checkVariable(classDef->tokAt(2), defaultAccess(), settings); } AccessControl varaccess = defaultAccess(); for (const Token *tok = start; tok && tok != end; tok = tok->next()) { // syntax error? if (tok->next() == nullptr) break; // Is it a function? else if (tok->str() == "{") { tok = tok->link(); continue; } // Is it a nested class or structure? else if (tok->isKeyword() && Token::Match(tok, "class|struct|union|namespace %type% :|{")) { tok = tok->tokAt(2); while (tok && tok->str() != "{") tok = tok->next(); if (tok) { // skip implementation tok = tok->link(); continue; } else break; } else if (tok->isKeyword() && Token::Match(tok, "struct|union {")) { if (Token::Match(tok->next()->link(), "} %name% ;|[")) { tok = tok->next()->link()->tokAt(2); continue; } else if (Token::simpleMatch(tok->next()->link(), "} ;")) { tok = tok->next(); continue; } } // Borland C++: Skip all variables in the __published section. // These are automatically initialized. else if (tok->str() == "__published:") { for (; tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); if (Token::Match(tok->next(), "private:|protected:|public:")) break; } if (tok) continue; else break; } // "private:" "public:" "protected:" etc else if (tok->str() == "public:") { varaccess = AccessControl::Public; continue; } else if (tok->str() == "protected:") { varaccess = AccessControl::Protected; continue; } else if (tok->str() == "private:") { varaccess = AccessControl::Private; continue; } // Is it a forward declaration? else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;")) { tok = tok->tokAt(2); continue; } // Borland C++: Ignore properties.. else if (tok->str() == "__property") continue; // skip return, goto and delete else if (tok->isKeyword() && Token::Match(tok, "return|delete|goto")) { while (tok->next() && tok->next()->str() != ";" && tok->next()->str() != "}" /* ticket #4994 */) { tok = tok->next(); } continue; } // skip case/default if (tok->isKeyword() && Token::Match(tok, "case|default")) { while (tok->next() && !Token::Match(tok->next(), "[:;{}]")) tok = tok->next(); continue; } // Search for start of statement.. else if (tok->previous() && !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:")) continue; else if (tok->str() == ";") continue; tok = checkVariable(tok, varaccess, settings); if (!tok) break; } } const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, const Settings* settings) { // Is it a throw..? if (tok->isKeyword() && Token::Match(tok, "throw %any% (") && Token::simpleMatch(tok->linkAt(2), ") ;")) { return tok->linkAt(2); } if (tok->isKeyword() && Token::Match(tok, "throw %any% :: %any% (") && Token::simpleMatch(tok->linkAt(4), ") ;")) { return tok->linkAt(4); } // friend? if (tok->isKeyword() && Token::Match(tok, "friend %type%") && tok->next()->varId() == 0) { const Token *next = Token::findmatch(tok->tokAt(2), ";|{"); if (next && next->str() == "{") next = next->link(); return next; } // skip const|volatile|static|mutable|extern while (tok->isKeyword() && Token::Match(tok, "const|constexpr|volatile|static|mutable|extern")) { tok = tok->next(); } // the start of the type tokens does not include the above modifiers const Token *typestart = tok; // C++17 structured bindings if (settings->standards.cpp >= Standards::CPP17 && Token::Match(tok, "auto &|&&| [")) { const Token *typeend = Token::findsimplematch(typestart, "[")->previous(); for (tok = typeend->tokAt(2); Token::Match(tok, "%name%|,"); tok = tok->next()) { if (tok->varId()) addVariable(tok, typestart, typeend, varaccess, nullptr, this, settings); } return typeend->linkAt(1); } if (tok->isKeyword() && Token::Match(tok, "class|struct|union|enum")) { tok = tok->next(); } // This is the start of a statement const Token *vartok = nullptr; const Token *typetok = nullptr; if (tok && isVariableDeclaration(tok, vartok, typetok)) { // If the vartok was set in the if-blocks above, create a entry for this variable.. tok = vartok->next(); while (Token::Match(tok, "[|{")) tok = tok->link()->next(); if (vartok->varId() == 0) { if (!vartok->isBoolean()) check->debugMessage(vartok, "varid0", "Scope::checkVariable found variable \'" + vartok->str() + "\' with varid 0."); return tok; } const Type *vType = nullptr; if (typetok) { vType = findVariableTypeIncludingUsedNamespaces(check, this, typetok); const_cast(typetok)->type(vType); } // skip "enum" or "struct" if (Token::Match(typestart, "enum|struct")) typestart = typestart->next(); addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this, settings); } return tok; } const Variable *Scope::getVariable(const std::string &varname) const { std::list::const_iterator iter; for (iter = varlist.begin(); iter != varlist.end(); ++iter) { if (iter->name() == varname) return &*iter; } return nullptr; } static const Token* skipPointers(const Token* tok) { while (Token::Match(tok, "*|&|&&") || (Token::Match(tok, "( [*&]") && Token::Match(tok->link()->next(), "(|["))) { tok = tok->next(); if (tok->strAt(-1) == "(" && Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); } if (Token::simpleMatch(tok, "( *") && Token::simpleMatch(tok->link()->previous(), "] ) ;")) { const Token *tok2 = skipPointers(tok->next()); if (Token::Match(tok2, "%name% [") && Token::simpleMatch(tok2->linkAt(1), "] ) ;")) return tok2; } return tok; } static const Token* skipPointersAndQualifiers(const Token* tok) { tok = skipPointers(tok); while (Token::Match(tok, "const|volatile")) { tok = tok->next(); tok = skipPointers(tok); } return tok; } bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const { if (!tok) return false; const bool isCPP = check && check->mTokenizer->isCPP(); if (isCPP && Token::Match(tok, "throw|new")) return false; const bool isCPP11 = isCPP && check->mSettings->standards.cpp >= Standards::CPP11; if (isCPP11 && tok->str() == "using") return false; const Token* localTypeTok = skipScopeIdentifiers(tok); const Token* localVarTok = nullptr; if (Token::Match(localTypeTok, "%type% <")) { if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) return false; const Token* closeTok = localTypeTok->next()->link(); if (closeTok) { localVarTok = skipPointers(closeTok->next()); if (Token::Match(localVarTok, ":: %type% %name% [;=({]")) { if (localVarTok->strAt(3) != "(" || Token::Match(localVarTok->linkAt(3), "[)}] ;")) { localTypeTok = localVarTok->next(); localVarTok = localVarTok->tokAt(2); } } } } else if (Token::Match(localTypeTok, "%type%")) { if (isCPP11 && Token::simpleMatch(localTypeTok, "decltype (") && Token::Match(localTypeTok->linkAt(1), ") %name%|*|&|&&")) localVarTok = skipPointersAndQualifiers(localTypeTok->linkAt(1)->next()); else { localVarTok = skipPointersAndQualifiers(localTypeTok->next()); if (isCPP11 && Token::simpleMatch(localVarTok, "decltype (") && Token::Match(localVarTok->linkAt(1), ") %name%|*|&|&&")) localVarTok = skipPointersAndQualifiers(localVarTok->linkAt(1)->next()); } } if (!localVarTok) return false; if (localVarTok->str() == "const") localVarTok = localVarTok->next(); if (Token::Match(localVarTok, "%name% ;|=") || (localVarTok && localVarTok->varId() && localVarTok->strAt(1) == ":")) { vartok = localVarTok; typetok = localTypeTok; } else if (Token::Match(localVarTok, "%name% )|[") && localVarTok->str() != "operator") { vartok = localVarTok; typetok = localTypeTok; } else if (localVarTok && localVarTok->varId() && Token::Match(localVarTok, "%name% (|{") && Token::Match(localVarTok->next()->link(), ")|} ;")) { vartok = localVarTok; typetok = localTypeTok; } else if (type == eCatch && Token::Match(localVarTok, "%name% )")) { vartok = localVarTok; typetok = localTypeTok; } return nullptr != vartok; } const Token * Scope::addEnum(const Token * tok, bool isCpp) { const Token * tok2 = tok->next(); // skip over class if present if (isCpp && tok2->str() == "class") tok2 = tok2->next(); // skip over name tok2 = tok2->next(); // save type if present if (tok2->str() == ":") { tok2 = tok2->next(); enumType = tok2; tok2 = tok2->next(); } // add enumerators if (tok2->str() == "{") { const Token * end = tok2->link(); tok2 = tok2->next(); while (Token::Match(tok2, "%name% =|,|}") || (Token::Match(tok2, "%name% (") && Token::Match(tok2->linkAt(1), ") ,|}"))) { Enumerator enumerator(this); // save enumerator name enumerator.name = tok2; // skip over name tok2 = tok2->next(); if (tok2->str() == "=") { // skip over "=" tok2 = tok2->next(); if (tok2->str() == "}") return nullptr; enumerator.start = tok2; while (!Token::Match(tok2, ",|}")) { if (tok2->link()) tok2 = tok2->link(); enumerator.end = tok2; tok2 = tok2->next(); } } else if (tok2->str() == "(") { // skip over unknown macro tok2 = tok2->link()->next(); } if (tok2->str() == ",") { enumeratorList.push_back(enumerator); tok2 = tok2->next(); } else if (tok2->str() == "}") { enumeratorList.push_back(enumerator); break; } } if (tok2 == end) { tok2 = tok2->next(); if (tok2 && tok2->str() != ";") tok2 = nullptr; } else tok2 = nullptr; } else tok2 = nullptr; return tok2; } const Enumerator * SymbolDatabase::findEnumerator(const Token * tok) const { const Scope * scope = tok->scope(); const std::string &tokStr = tok->str(); if (mTokensThatAreNotEnumeratorValues.find(tokStr) != mTokensThatAreNotEnumeratorValues.end()) { return nullptr; } // check for qualified name if (tok->strAt(-1) == "::") { // find first scope const Token *tok1 = tok; while (Token::Match(tok1->tokAt(-2), "%name% ::")) tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) == "::") scope = &scopeList.front(); else { // FIXME search base class here // find first scope while (scope && scope->nestedIn) { const Scope * temp = scope->nestedIn->findRecordInNestedList(tok1->str()); if (temp) { scope = temp; break; } scope = scope->nestedIn; } } if (scope) { tok1 = tok1->tokAt(2); while (scope && Token::Match(tok1, "%name% ::")) { scope = scope->findRecordInNestedList(tok1->str()); tok1 = tok1->tokAt(2); } if (scope) { const Enumerator * enumerator = scope->findEnumerator(tokStr); if (enumerator) // enum class return enumerator; // enum else { for (std::list::const_iterator it = scope->nestedList.begin(), end = scope->nestedList.end(); it != end; ++it) { enumerator = (*it)->findEnumerator(tokStr); if (enumerator) return enumerator; } } } } } else { const Enumerator * enumerator = scope->findEnumerator(tokStr); if (enumerator) return enumerator; for (std::list::const_iterator s = scope->nestedList.begin(); s != scope->nestedList.end(); ++s) { enumerator = (*s)->findEnumerator(tokStr); if (enumerator) return enumerator; } if (scope->definedType) { const std::vector & derivedFrom = scope->definedType->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Type *derivedFromType = i.type; if (derivedFromType && derivedFromType->classScope) { enumerator = derivedFromType->classScope->findEnumerator(tokStr); if (enumerator) return enumerator; } } } while (scope->nestedIn) { if (scope->type == Scope::eFunction && scope->functionOf) scope = scope->functionOf; else scope = scope->nestedIn; enumerator = scope->findEnumerator(tokStr); if (enumerator) return enumerator; for (std::list::const_iterator s = scope->nestedList.begin(); s != scope->nestedList.end(); ++s) { enumerator = (*s)->findEnumerator(tokStr); if (enumerator) return enumerator; } } } mTokensThatAreNotEnumeratorValues.insert(tokStr); return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findVariableTypeInBase(const Scope* scope, const Token* typeTok) const { if (scope && scope->definedType && !scope->definedType->derivedFrom.empty()) { const std::vector &derivedFrom = scope->definedType->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Type *base = i.type; if (base && base->classScope) { if (base->classScope == scope) return nullptr; const Type * type = base->classScope->findType(typeTok->str()); if (type) return type; type = findVariableTypeInBase(base->classScope, typeTok); if (type) return type; } } } return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *typeTok) const { const Scope *scope = start; // check if type does not have a namespace if (typeTok->strAt(-1) != "::" && typeTok->strAt(1) != "::") { // check if type same as scope if (start->isClassOrStruct() && typeTok->str() == start->className) return start->definedType; while (scope) { // look for type in this scope const Type * type = scope->findType(typeTok->str()); if (type) return type; // look for type in base classes if possible if (scope->isClassOrStruct()) { type = findVariableTypeInBase(scope, typeTok); if (type) return type; } // check if in member function class to see if it's present in class if (scope->type == Scope::eFunction && scope->functionOf) { const Scope *scope1 = scope->functionOf; type = scope1->findType(typeTok->str()); if (type) return type; type = findVariableTypeInBase(scope1, typeTok); if (type) return type; } scope = scope->nestedIn; } } // check for a qualified name and use it when given else if (typeTok->strAt(-1) == "::") { // check if type is not part of qualification if (typeTok->strAt(1) == "::") return nullptr; // find start of qualified function name const Token *tok1 = typeTok; while (Token::Match(tok1->tokAt(-2), "%type% ::") || (Token::simpleMatch(tok1->tokAt(-2), "> ::") && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->tokAt(-1), "%type%"))) { if (tok1->strAt(-1) == "::") tok1 = tok1->tokAt(-2); else tok1 = tok1->linkAt(-2)->tokAt(-1); } // check for global scope if (tok1->strAt(-1) == "::") { scope = &scopeList.front(); scope = scope->findRecordInNestedList(tok1->str()); } // find start of qualification else { while (scope) { if (scope->className == tok1->str()) break; else { const Scope *scope1 = scope->findRecordInNestedList(tok1->str()); if (scope1) { scope = scope1; break; } else if (scope->type == Scope::eFunction && scope->functionOf) scope = scope->functionOf; else scope = scope->nestedIn; } } } if (scope) { // follow qualification while (scope && (Token::Match(tok1, "%type% ::") || (Token::Match(tok1, "%type% <") && Token::simpleMatch(tok1->linkAt(1), "> ::")))) { if (tok1->strAt(1) == "::") tok1 = tok1->tokAt(2); else tok1 = tok1->linkAt(1)->tokAt(2); const Scope * temp = scope->findRecordInNestedList(tok1->str()); if (!temp) { // look in base classes const Type * type = findVariableTypeInBase(scope, tok1); if (type) return type; } scope = temp; } if (scope && scope->definedType) return scope->definedType; } } return nullptr; } bool Scope::hasInlineOrLambdaFunction() const { for (const Scope *s : nestedList) { // Inline function if (s->type == Scope::eUnconditional && Token::simpleMatch(s->bodyStart->previous(), ") {")) return true; // Lambda function if (s->type == Scope::eLambda) return true; if (s->hasInlineOrLambdaFunction()) return true; } return false; } void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const { if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { const std::vector &derivedFrom = definedType->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Type *base = i.type; if (base && base->classScope) { if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already continue; for (std::multimap::const_iterator it = base->classScope->functionMap.find(name); it != base->classScope->functionMap.end() && it->first == name; ++it) { const Function *func = it->second; if ((func->isVariadic() && args >= (func->argCount() - 1)) || (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount()))) { matches.push_back(func); } } base->classScope->findFunctionInBase(name, args, matches); } } } } const Scope *Scope::findRecordInBase(const std::string & name) const { if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { const std::vector &derivedFrom = definedType->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Type *base = i.type; if (base && base->classScope) { if (base->classScope == this) // Recursive class; tok should have been found already continue; if (base->name() == name) { return base->classScope; } const ::Type * t = base->classScope->findType(name); if (t) return t->classScope; } } } return nullptr; } std::vector Scope::findAssociatedScopes() const { std::vector result = {this}; if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { const std::vector& derivedFrom = definedType->derivedFrom; for (const Type::BaseInfo& i : derivedFrom) { const Type* base = i.type; if (base && base->classScope) { if (contains(result, base->classScope)) continue; std::vector baseScopes = base->classScope->findAssociatedScopes(); result.insert(result.end(), baseScopes.begin(), baseScopes.end()); } } } return result; } //--------------------------------------------------------------------------- static void checkVariableCallMatch(const Variable* callarg, const Variable* funcarg, size_t& same, size_t& fallback1, size_t& fallback2) { if (callarg) { ValueType::MatchResult res = ValueType::matchParameter(callarg->valueType(), callarg, funcarg); if (res == ValueType::MatchResult::SAME) { same++; return; } if (res == ValueType::MatchResult::FALLBACK1) { fallback1++; return; } if (res == ValueType::MatchResult::FALLBACK2) { fallback2++; return; } if (res == ValueType::MatchResult::NOMATCH) return; bool ptrequals = callarg->isArrayOrPointer() == funcarg->isArrayOrPointer(); bool constEquals = !callarg->isArrayOrPointer() || ((callarg->typeStartToken()->strAt(-1) == "const") == (funcarg->typeStartToken()->strAt(-1) == "const")); if (ptrequals && constEquals && callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() && callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() && callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong()) { same++; } else if (callarg->isArrayOrPointer()) { if (ptrequals && constEquals && funcarg->typeStartToken()->str() == "void") fallback1++; else if (constEquals && funcarg->isStlStringType() && Token::Match(callarg->typeStartToken(), "char|wchar_t")) fallback2++; } else if (ptrequals) { const bool takesInt = Token::Match(funcarg->typeStartToken(), "char|short|int|long"); const bool takesFloat = Token::Match(funcarg->typeStartToken(), "float|double"); const bool passesInt = Token::Match(callarg->typeStartToken(), "char|short|int|long"); const bool passesFloat = Token::Match(callarg->typeStartToken(), "float|double"); if ((takesInt && passesInt) || (takesFloat && passesFloat)) fallback1++; else if ((takesInt && passesFloat) || (takesFloat && passesInt)) fallback2++; } } } static std::string getTypeString(const Token *typeToken) { if (!typeToken) return ""; while (Token::Match(typeToken, "%name%|*|&|::")) { if (typeToken->str() == "::") { std::string ret; while (Token::Match(typeToken, ":: %name%")) { ret += "::" + typeToken->strAt(1); typeToken = typeToken->tokAt(2); if (typeToken->str() == "<") { for (const Token *tok = typeToken; tok != typeToken->link(); tok = tok->next()) ret += tok->str(); ret += ">"; typeToken = typeToken->link()->next(); } } return ret; } if (Token::Match(typeToken, "%name% const| %var%|*|&")) { return typeToken->str(); } typeToken = typeToken->next(); } return ""; } const Function* Scope::findFunction(const Token *tok, bool requireConst) const { const bool isCall = Token::Match(tok->next(), "(|{"); const std::vector arguments = getArguments(tok); std::vector matches; // find all the possible functions that could match const std::size_t args = arguments.size(); auto addMatchingFunctions = [&](const Scope *scope) { for (std::multimap::const_iterator it = scope->functionMap.find(tok->str()); it != scope->functionMap.cend() && it->first == tok->str(); ++it) { const Function *func = it->second; if (!isCall || args == func->argCount() || (func->isVariadic() && args >= (func->argCount() - 1)) || (args < func->argCount() && args >= func->minArgCount())) { matches.push_back(func); } } }; addMatchingFunctions(this); // check in anonumous namespaces for (const Scope *nestedScope : nestedList) { if (nestedScope->type == eNamespace && nestedScope->className.empty()) addMatchingFunctions(nestedScope); } // check in base classes findFunctionInBase(tok->str(), args, matches); // Non-call => Do not match parameters if (!isCall) { return matches.empty() ? nullptr : matches[0]; } const Function* fallback1Func = nullptr; const Function* fallback2Func = nullptr; // check each function against the arguments in the function call for a match for (std::size_t i = 0; i < matches.size();) { bool constFallback = false; const Function * func = matches[i]; size_t same = 0; if (requireConst && !func->isConst()) { i++; continue; } if (!requireConst || !func->isConst()) { // get the function this call is in const Scope * scope = tok->scope(); // check if this function is a member function if (scope && scope->functionOf && scope->functionOf->isClassOrStruct() && scope->function) { // check if isConst mismatches if (scope->function->isConst() != func->isConst()) { if (scope->function->isConst()) { ++i; continue; } constFallback = true; } } } size_t fallback1 = 0; size_t fallback2 = 0; bool erased = false; for (std::size_t j = 0; j < args; ++j) { // don't check variadic arguments if (func->isVariadic() && j > (func->argCount() - 1)) { break; } const Variable *funcarg = func->getArgumentVar(j); if (!arguments[j]->valueType()) { const Token *vartok = arguments[j]; int pointer = 0; while (vartok && (vartok->isUnaryOp("&") || vartok->isUnaryOp("*"))) { pointer += vartok->isUnaryOp("&") ? 1 : -1; vartok = vartok->astOperand1(); } if (vartok && vartok->variable()) { const Token *callArgTypeToken = vartok->variable()->typeStartToken(); const Token *funcArgTypeToken = funcarg->typeStartToken(); auto parseDecl = [](const Token *typeToken) -> ValueType { ValueType ret; while (Token::Match(typeToken->previous(), "%name%")) typeToken = typeToken->previous(); while (Token::Match(typeToken, "%name%|*|&|::|<")) { if (typeToken->str() == "const") ret.constness |= (1 << ret.pointer); else if (typeToken->str() == "*") ret.pointer++; else if (typeToken->str() == "<") { if (!typeToken->link()) break; typeToken = typeToken->link(); } typeToken = typeToken->next(); } return ret; }; const std::string type1 = getTypeString(callArgTypeToken); const std::string type2 = getTypeString(funcArgTypeToken); if (!type1.empty() && type1 == type2) { ValueType callArgType = parseDecl(callArgTypeToken); callArgType.pointer += pointer; ValueType funcArgType = parseDecl(funcArgTypeToken); callArgType.sign = funcArgType.sign = ValueType::Sign::SIGNED; callArgType.type = funcArgType.type = ValueType::Type::INT; ValueType::MatchResult res = ValueType::matchParameter(&callArgType, &funcArgType); if (res == ValueType::MatchResult::SAME) ++same; else if (res == ValueType::MatchResult::FALLBACK1) ++fallback1; else if (res == ValueType::MatchResult::FALLBACK2) ++fallback2; continue; } } } // check for a match with a variable if (Token::Match(arguments[j], "%var% ,|)")) { const Variable * callarg = arguments[j]->variable(); checkVariableCallMatch(callarg, funcarg, same, fallback1, fallback2); } else if (funcarg->isStlStringType() && arguments[j]->valueType() && arguments[j]->valueType()->pointer == 1 && arguments[j]->valueType()->type == ValueType::Type::CHAR) fallback2++; // check for a match with nullptr else if (funcarg->isPointer() && Token::Match(arguments[j], "nullptr|NULL ,|)")) same++; else if (funcarg->isPointer() && MathLib::isNullValue(arguments[j]->str())) fallback1++; // Try to evaluate the apparently more complex expression else if (check->isCPP()) { const Token *vartok = arguments[j]; if (vartok->str() == ".") { const Token* rml = nextAfterAstRightmostLeaf(vartok); if (rml) vartok = rml->previous(); } while (vartok->isUnaryOp("&") || vartok->isUnaryOp("*")) vartok = vartok->astOperand1(); const Variable* var = vartok->variable(); // smart pointer deref? if (var && vartok->astParent() && vartok->astParent()->str() == "*" && var->isSmartPointer() && var->valueType() && var->valueType()->smartPointerTypeToken) var = var->valueType()->smartPointerTypeToken->variable(); ValueType::MatchResult res = ValueType::matchParameter(arguments[j]->valueType(), var, funcarg); if (res == ValueType::MatchResult::SAME) ++same; else if (res == ValueType::MatchResult::FALLBACK1) ++fallback1; else if (res == ValueType::MatchResult::FALLBACK2) ++fallback2; else if (res == ValueType::MatchResult::NOMATCH) { // can't match so remove this function from possible matches matches.erase(matches.begin() + i); erased = true; break; } } else // C code: if number of arguments match then do not match types fallback1++; } const size_t hasToBe = func->isVariadic() ? (func->argCount() - 1) : args; // check if all arguments matched if (same == hasToBe) { if (constFallback || (!requireConst && func->isConst())) fallback1Func = func; else return func; } else if (!fallback1Func) { if (same + fallback1 == hasToBe) fallback1Func = func; else if (!fallback2Func && same + fallback2 + fallback1 == hasToBe) fallback2Func = func; } if (!erased) ++i; } // Fallback cases if (fallback1Func) return fallback1Func; if (fallback2Func) return fallback2Func; // Only one candidate left if (matches.size() == 1) return matches[0]; return nullptr; } //--------------------------------------------------------------------------- const Function* SymbolDatabase::findFunction(const Token *tok) const { // find the scope this function is in const Scope *currScope = tok->scope(); while (currScope && currScope->isExecutable()) { if (currScope->functionOf) currScope = currScope->functionOf; else currScope = currScope->nestedIn; } // check for a qualified name and use it when given if (tok->strAt(-1) == "::") { // find start of qualified function name const Token *tok1 = tok; while (Token::Match(tok1->tokAt(-2), ">|%type% ::")) { if (tok1->strAt(-2) == ">") { if (tok1->linkAt(-2)) tok1 = tok1->linkAt(-2)->tokAt(-1); else { if (mSettings->debugwarnings) debugMessage(tok1->tokAt(-2), "debug", "SymbolDatabase::findFunction found '>' without link."); return nullptr; } } else tok1 = tok1->tokAt(-2); } // check for global scope if (tok1->strAt(-1) == "::") { currScope = &scopeList.front(); if (Token::Match(tok1, "%name% (")) return currScope->findFunction(tok); currScope = currScope->findRecordInNestedList(tok1->str()); } // find start of qualification else { while (currScope) { if (currScope->className == tok1->str()) break; else { const Scope *scope = currScope->findRecordInNestedList(tok1->str()); if (scope) { currScope = scope; break; } else currScope = currScope->nestedIn; } } } if (currScope) { while (currScope && tok1 && !(Token::Match(tok1, "%type% :: %name% [(),>]") || (Token::Match(tok1, "%type% <") && Token::Match(tok1->linkAt(1), "> :: %name% (")))) { if (tok1->strAt(1) == "::") tok1 = tok1->tokAt(2); else if (tok1->strAt(1) == "<") tok1 = tok1->linkAt(1)->tokAt(2); else tok1 = nullptr; if (tok1) currScope = currScope->findRecordInNestedList(tok1->str()); } if (tok1) tok1 = tok1->tokAt(2); if (currScope && tok1) return currScope->findFunction(tok1); } } // check for member function else if (Token::Match(tok->tokAt(-2), "!!this .")) { const Token* tok1 = tok->previous()->astOperand1(); if (tok1 && tok1->valueType() && tok1->valueType()->typeScope) { return tok1->valueType()->typeScope->findFunction(tok, tok1->valueType()->constness == 1); } else if (tok1 && Token::Match(tok1->previous(), "%name% (") && tok1->previous()->function() && tok1->previous()->function()->retDef) { ValueType vt = ValueType::parseDecl(tok1->previous()->function()->retDef, mSettings); if (vt.typeScope) return vt.typeScope->findFunction(tok, vt.constness == 1); } else if (Token::Match(tok1, "%var% .")) { const Variable *var = getVariableFromVarId(tok1->varId()); if (var && var->typeScope()) return var->typeScope()->findFunction(tok, var->valueType()->constness == 1); if (var && var->smartPointerType() && var->smartPointerType()->classScope && tok1->next()->originalName() == "->") return var->smartPointerType()->classScope->findFunction(tok, var->valueType()->constness == 1); } else if (Token::simpleMatch(tok->previous()->astOperand1(), "(")) { const Token *castTok = tok->previous()->astOperand1(); if (castTok->isCast()) { ValueType vt = ValueType::parseDecl(castTok->next(),mSettings); if (vt.typeScope) return vt.typeScope->findFunction(tok, vt.constness == 1); } } } // check in enclosing scopes else { while (currScope) { const Function *func = currScope->findFunction(tok); if (func) return func; currScope = currScope->nestedIn; } } // Check for constructor if (Token::Match(tok, "%name% (|{")) { ValueType vt = ValueType::parseDecl(tok, mSettings); if (vt.typeScope) return vt.typeScope->findFunction(tok, false); } return nullptr; } //--------------------------------------------------------------------------- const Scope *SymbolDatabase::findScopeByName(const std::string& name) const { for (const Scope &scope: scopeList) { if (scope.className == name) return &scope; } return nullptr; } //--------------------------------------------------------------------------- const Scope *Scope::findRecordInNestedList(const std::string & name) const { for (const Scope* scope: nestedList) { if (scope->className == name && scope->type != eFunction) return scope; } const Type * nested_type = findType(name); if (nested_type) { if (nested_type->isTypeAlias()) { if (nested_type->typeStart == nested_type->typeEnd) return findRecordInNestedList(nested_type->typeStart->str()); } else return nested_type->classScope; } return nullptr; } //--------------------------------------------------------------------------- const Type* Scope::findType(const std::string & name) const { auto it = definedTypesMap.find(name); // Type was found if (definedTypesMap.end() != it) return (*it).second; // is type defined in anonymous namespace.. it = definedTypesMap.find(""); if (it != definedTypesMap.end()) { for (const Scope *scope : nestedList) { if (scope->className.empty() && (scope->type == eNamespace || scope->isClassOrStructOrUnion())) { const Type *t = scope->findType(name); if (t) return t; } } } // Type was not found return nullptr; } //--------------------------------------------------------------------------- Scope *Scope::findInNestedListRecursive(const std::string & name) { for (Scope *scope: nestedList) { if (scope->className == name) return scope; } for (Scope* scope: nestedList) { Scope *child = scope->findInNestedListRecursive(name); if (child) return child; } return nullptr; } //--------------------------------------------------------------------------- const Function *Scope::getDestructor() const { for (const Function &f: functionList) { if (f.type == Function::eDestructor) return &f; } return nullptr; } //--------------------------------------------------------------------------- bool SymbolDatabase::isCPP() const { return mTokenizer->isCPP(); } //--------------------------------------------------------------------------- const Scope *SymbolDatabase::findScope(const Token *tok, const Scope *startScope) const { const Scope *scope = nullptr; // absolute path if (tok->str() == "::") { tok = tok->next(); scope = &scopeList.front(); } // relative path else if (tok->isName()) { scope = startScope; } while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::") { scope = scope->findRecordInNestedList(tok->str()); tok = tok->tokAt(2); } else if (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::")) { scope = scope->findRecordInNestedList(tok->str()); tok = tok->linkAt(1)->tokAt(2); } else return scope->findRecordInNestedList(tok->str()); } // not a valid path return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startScope) const { // skip over struct or union if (Token::Match(startTok, "struct|union")) startTok = startTok->next(); // type same as scope if (startTok->str() == startScope->className && startScope->isClassOrStruct() && startTok->strAt(1) != "::") return startScope->definedType; const Scope* start_scope = startScope; // absolute path - directly start in global scope if (startTok->str() == "::") { startTok = startTok->next(); start_scope = &scopeList.front(); } const Token* tok = startTok; const Scope* scope = start_scope; while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { scope = scope->findRecordInNestedList(tok->str()); if (scope) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } else { start_scope = start_scope->nestedIn; if (!start_scope) break; scope = start_scope; tok = startTok; } } else { const Type * type = scope->findType(tok->str()); const Scope *scope1; if (type) return type; else if ((scope1 = scope->findRecordInBase(tok->str()))) { type = scope1->definedType; if (type) return type; } else break; } } // check using namespaces while (startScope) { for (std::list::const_iterator it = startScope->usingList.begin(); it != startScope->usingList.end(); ++it) { tok = startTok; scope = it->scope; start_scope = startScope; while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { scope = scope->findRecordInNestedList(tok->str()); if (scope) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } else { start_scope = start_scope->nestedIn; if (!start_scope) break; scope = start_scope; tok = startTok; } } else { const Type * type = scope->findType(tok->str()); const Scope *scope1; if (type) return type; else if ((scope1 = scope->findRecordInBase(tok->str()))) { type = scope1->definedType; if (type) return type; } else break; } } } startScope = startScope->nestedIn; } // not a valid path return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findTypeInNested(const Token *startTok, const Scope *startScope) const { // skip over struct or union if (Token::Match(startTok, "struct|union|enum")) startTok = startTok->next(); // type same as scope if (startTok->str() == startScope->className && startScope->isClassOrStruct()) return startScope->definedType; bool hasPath = false; // absolute path - directly start in global scope if (startTok->str() == "::") { hasPath = true; startTok = startTok->next(); startScope = &scopeList.front(); } const Token* tok = startTok; const Scope* scope = startScope; while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { hasPath = true; scope = scope->findRecordInNestedList(tok->str()); if (scope) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } else { startScope = startScope->nestedIn; if (!startScope) break; scope = startScope; tok = startTok; } } else { const Type * type = scope->findType(tok->str()); if (hasPath || type) return type; else { scope = scope->nestedIn; if (!scope) break; } } } // not a valid path return nullptr; } //--------------------------------------------------------------------------- const Scope * SymbolDatabase::findNamespace(const Token * tok, const Scope * scope) const { const Scope * s = findScope(tok, scope); if (s) return s; else if (scope->nestedIn) return findNamespace(tok, scope->nestedIn); return nullptr; } //--------------------------------------------------------------------------- Function * SymbolDatabase::findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length) { const Function * function = nullptr; const bool destructor = func->strAt(-1) == "~"; for (std::multimap::const_iterator it = ns->functionMap.find(func->str()); it != ns->functionMap.end() && it->first == func->str(); ++it) { if (it->second->argsMatch(ns, it->second->argDef, func->next(), path, path_length) && it->second->isDestructor() == destructor) { function = it->second; break; } } if (!function) { const Scope * scope = ns->findRecordInNestedList(func->str()); if (scope && Token::Match(func->tokAt(1), "::|<")) { if (func->strAt(1) == "::") func = func->tokAt(2); else if (func->linkAt(1)) func = func->linkAt(1)->tokAt(2); else return nullptr; if (func->str() == "~") func = func->next(); function = findFunctionInScope(func, scope, path, path_length); } } return const_cast(function); } //--------------------------------------------------------------------------- namespace { #define C_KEYWORDS \ "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", \ "_Static_assert", "_Thread_local", "auto", "break", "case", "char", "const", "continue", "default", \ "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", \ "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", \ "union", "unsigned", "void", "volatile", "while" const std::unordered_set c_keywords = { C_KEYWORDS, "restrict" }; const std::unordered_set cpp_keywords = { C_KEYWORDS, "alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "bool", "catch", "char8_t", "char16_t", "char32_t", "class", "compl", "concept", "consteval", "constexpr", "constinit", "const_cast", "co_await", "co_return", "co_yield", "decltype", "delete", "dynamic_cast", "explicit", "export", "false", "friend", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "reinterpret_cast", "requires", "static_assert", "static_cast", "template", "this", "thread_local", "throw", "true", "try", "typeid", "typename", "using", "virtual", "wchar_t", "xor", "xor_eq" }; } bool SymbolDatabase::isReservedName(const std::string& iName) const { if (isCPP()) return cpp_keywords.find(iName) != cpp_keywords.cend(); else return c_keywords.find(iName) != c_keywords.cend(); } nonneg int SymbolDatabase::sizeOfType(const Token *type) const { int size = mTokenizer->sizeOfType(type); if (size == 0 && type->type() && type->type()->isEnumType() && type->type()->classScope) { size = mSettings->sizeof_int; const Token * enum_type = type->type()->classScope->enumType; if (enum_type) size = mTokenizer->sizeOfType(enum_type); } return size; } static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings); void SymbolDatabase::setValueType(Token *tok, const Variable &var) { ValueType valuetype; if (var.nameToken()) valuetype.bits = var.nameToken()->bits(); valuetype.pointer = var.dimensions().size(); valuetype.typeScope = var.typeScope(); if (var.valueType()) { valuetype.container = var.valueType()->container; valuetype.containerTypeToken = var.valueType()->containerTypeToken; } valuetype.smartPointerType = var.smartPointerType(); if (parsedecl(var.typeStartToken(), &valuetype, mDefaultSignedness, mSettings)) { if (tok->str() == "." && tok->astOperand1()) { const ValueType * const vt = tok->astOperand1()->valueType(); if (vt && (vt->constness & 1) != 0) valuetype.constness |= 1; } setValueType(tok, valuetype); } } void SymbolDatabase::setValueType(Token *tok, const Enumerator &enumerator) { ValueType valuetype; valuetype.typeScope = enumerator.scope; const Token * type = enumerator.scope->enumType; if (type) { valuetype.type = ValueType::typeFromString(type->str(), type->isLong()); if (valuetype.type == ValueType::Type::UNKNOWN_TYPE && type->isStandardType()) valuetype.fromLibraryType(type->str(), mSettings); if (valuetype.isIntegral()) { if (type->isSigned()) valuetype.sign = ValueType::Sign::SIGNED; else if (type->isUnsigned()) valuetype.sign = ValueType::Sign::UNSIGNED; else if (valuetype.type == ValueType::Type::CHAR) valuetype.sign = mDefaultSignedness; else valuetype.sign = ValueType::Sign::SIGNED; } setValueType(tok, valuetype); } else { valuetype.sign = ValueType::SIGNED; valuetype.type = ValueType::INT; setValueType(tok, valuetype); } } static void setAutoTokenProperties(Token * const autoTok) { const ValueType *valuetype = autoTok->valueType(); if (valuetype->isIntegral() || valuetype->isFloat()) autoTok->isStandardType(true); } static bool isContainerYieldElement(Library::Container::Yield yield) { return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX || yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; } static bool isContainerYieldPointer(Library::Container::Yield yield) { return yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; } void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype) { tok->setValueType(new ValueType(valuetype)); Token *parent = tok->astParent(); if (!parent || parent->valueType()) return; if (!parent->astOperand1()) return; const ValueType *vt1 = parent->astOperand1() ? parent->astOperand1()->valueType() : nullptr; const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr; if (vt1 && Token::Match(parent, "<<|>>")) { if (!mIsCpp || (vt2 && vt2->isIntegral())) { if (vt1->type < ValueType::Type::BOOL || vt1->type >= ValueType::Type::INT) { ValueType vt(*vt1); vt.reference = Reference::None; setValueType(parent, vt); } else { ValueType vt(*vt1); vt.type = ValueType::Type::INT; // Integer promotion vt.sign = ValueType::Sign::SIGNED; vt.reference = Reference::None; setValueType(parent, vt); } } return; } if (vt1 && vt1->container && vt1->containerTypeToken && Token::Match(parent, ". %name% (") && isContainerYieldElement(vt1->container->getYield(parent->next()->str()))) { ValueType item; if (parsedecl(vt1->containerTypeToken, &item, mDefaultSignedness, mSettings)) { if (item.constness == 0) item.constness = vt1->constness; if (isContainerYieldPointer(vt1->container->getYield(parent->next()->str()))) item.pointer += 1; else item.reference = Reference::LValue; setValueType(parent->tokAt(2), item); } } if (vt1 && vt1->smartPointerType && Token::Match(parent, ". %name% (") && parent->originalName() == "->" && !parent->next()->function()) { const Scope *scope = vt1->smartPointerType->classScope; const Function *f = scope ? scope->findFunction(parent->next(), false) : nullptr; if (f) parent->next()->function(f); } if (parent->isAssignmentOp()) { if (vt1) { auto vt = *vt1; vt.reference = Reference::None; setValueType(parent, vt); } else if (mIsCpp && ((Token::Match(parent->tokAt(-3), "%var% ; %var% =") && parent->strAt(-3) == parent->strAt(-1)) || Token::Match(parent->tokAt(-1), "%var% ="))) { Token *var1Tok = parent->strAt(-2) == ";" ? parent->tokAt(-3) : parent->tokAt(-1); Token *autoTok = nullptr; if (Token::Match(var1Tok->tokAt(-2), ";|{|}|(|const|constexpr auto")) autoTok = var1Tok->previous(); else if (Token::Match(var1Tok->tokAt(-3), ";|{|}|(|const|constexpr auto *|&|&&")) autoTok = var1Tok->tokAt(-2); if (autoTok) { ValueType vt(*vt2); if (vt.constness & (1 << vt.pointer)) vt.constness &= ~(1 << vt.pointer); if (autoTok->strAt(1) == "*" && vt.pointer) vt.pointer--; if (Token::Match(autoTok->tokAt(-1), "const|constexpr")) vt.constness |= 1; setValueType(autoTok, vt); setAutoTokenProperties(autoTok); if (vt2->pointer > vt.pointer) vt.pointer++; setValueType(var1Tok, vt); if (var1Tok != parent->previous()) setValueType(parent->previous(), vt); Variable *var = const_cast(parent->previous()->variable()); if (var) { ValueType vt2_(*vt2); if (vt2_.pointer == 0 && autoTok->strAt(1) == "*") vt2_.pointer = 1; if ((vt.constness & (1 << vt2->pointer)) != 0) vt2_.constness |= (1 << vt2->pointer); if (!Token::Match(autoTok->tokAt(1), "*|&")) vt2_.constness = vt.constness; var->setValueType(vt2_); if (vt2->typeScope && vt2->typeScope->definedType) { var->type(vt2->typeScope->definedType); if (autoTok->valueType()->pointer == 0) autoTok->type(vt2->typeScope->definedType); } } } } return; } if (parent->str() == "[" && (!mIsCpp || parent->astOperand1() == tok) && valuetype.pointer > 0U && !Token::Match(parent->previous(), "[{,]")) { const Token *op1 = parent->astOperand1(); while (op1 && op1->str() == "[") op1 = op1->astOperand1(); ValueType vt(valuetype); // the "[" is a dereference unless this is a variable declaration if (!(op1 && op1->variable() && op1->variable()->nameToken() == op1)) vt.pointer -= 1U; setValueType(parent, vt); return; } if (Token::Match(parent->previous(), "%name% (") && parent->astOperand1() == tok && valuetype.pointer > 0U) { ValueType vt(valuetype); vt.pointer -= 1U; setValueType(parent, vt); return; } // std::move if (vt2 && parent->str() == "(" && Token::simpleMatch(parent->tokAt(-3), "std :: move (")) { ValueType vt = valuetype; vt.reference = Reference::RValue; setValueType(parent, vt); return; } if (parent->str() == "*" && !parent->astOperand2() && valuetype.pointer > 0U) { ValueType vt(valuetype); vt.pointer -= 1U; setValueType(parent, vt); return; } // Dereference iterator if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::ITERATOR && valuetype.containerTypeToken) { ValueType vt; if (parsedecl(valuetype.containerTypeToken, &vt, mDefaultSignedness, mSettings)) { if (vt.constness == 0) vt.constness = valuetype.constness; vt.reference = Reference::LValue; setValueType(parent, vt); return; } } // Dereference smart pointer if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::SMART_POINTER && valuetype.smartPointerTypeToken) { ValueType vt; if (parsedecl(valuetype.smartPointerTypeToken, &vt, mDefaultSignedness, mSettings)) { if (vt.constness == 0) vt.constness = valuetype.constness; setValueType(parent, vt); return; } } if (parent->str() == "*" && Token::simpleMatch(parent->astOperand2(), "[") && valuetype.pointer > 0U) { const Token *op1 = parent->astOperand2()->astOperand1(); while (op1 && op1->str() == "[") op1 = op1->astOperand1(); const ValueType& vt(valuetype); if (op1 && op1->variable() && op1->variable()->nameToken() == op1) { setValueType(parent, vt); return; } } if (parent->str() == "&" && !parent->astOperand2()) { ValueType vt(valuetype); vt.reference = Reference::None; //Given int& x; the type of &x is int* not int&* vt.pointer += 1U; setValueType(parent, vt); return; } if ((parent->str() == "." || parent->str() == "::") && parent->astOperand2() && parent->astOperand2()->isName()) { const Variable* var = parent->astOperand2()->variable(); if (!var && valuetype.typeScope && vt1) { const std::string &name = parent->astOperand2()->str(); const Scope *typeScope = vt1->typeScope; if (!typeScope) return; for (std::list::const_iterator it = typeScope->varlist.begin(); it != typeScope->varlist.end(); ++it) { if (it->nameToken()->str() == name) { var = &*it; break; } } } if (var) setValueType(parent, *var); return; } // range for loop, auto if (vt2 && parent->str() == ":" && Token::Match(parent->astParent(), "( const| auto *|&| %var% :") && !parent->previous()->valueType() && Token::simpleMatch(parent->astParent()->astOperand1(), "for")) { const bool isconst = Token::simpleMatch(parent->astParent()->next(), "const"); Token * const autoToken = parent->astParent()->tokAt(isconst ? 2 : 1); if (vt2->pointer) { ValueType autovt(*vt2); autovt.pointer--; autovt.constness = 0; setValueType(autoToken, autovt); setAutoTokenProperties(autoToken); ValueType varvt(*vt2); varvt.pointer--; if (isconst) varvt.constness |= 1; setValueType(parent->previous(), varvt); Variable *var = const_cast(parent->previous()->variable()); if (var) { var->setValueType(varvt); if (vt2->typeScope && vt2->typeScope->definedType) { var->type(vt2->typeScope->definedType); autoToken->type(vt2->typeScope->definedType); } } } else if (vt2->container) { // TODO: Determine exact type of RHS const Token *typeStart = parent->astOperand2(); while (typeStart) { if (typeStart->variable()) typeStart = typeStart->variable()->typeStartToken(); else if (typeStart->str() == "(" && typeStart->previous() && typeStart->previous()->function()) typeStart = typeStart->previous()->function()->retDef; else break; } // Try to determine type of "auto" token. // TODO: Get type better bool setType = false; ValueType autovt; const Type *templateArgType = nullptr; // container element type / smart pointer type if (!vt2->container->rangeItemRecordType.empty()) { setType = true; autovt.type = ValueType::Type::RECORD; } else if (vt2->containerTypeToken) { if (mSettings->library.isSmartPointer(vt2->containerTypeToken)) { const Token *smartPointerTypeTok = vt2->containerTypeToken; while (Token::Match(smartPointerTypeTok, "%name%|::")) smartPointerTypeTok = smartPointerTypeTok->next(); if (Token::Match(smartPointerTypeTok, "< %name% > >") && smartPointerTypeTok->next()->type()) { setType = true; templateArgType = smartPointerTypeTok->next()->type(); autovt.smartPointerType = templateArgType; autovt.type = ValueType::Type::NONSTD; } } else if (parsedecl(vt2->containerTypeToken, &autovt, mDefaultSignedness, mSettings)) { setType = true; templateArgType = vt2->containerTypeToken->type(); } } if (setType) { // Type of "auto" has been determined.. set type information for "auto" and variable tokens setValueType(autoToken, autovt); setAutoTokenProperties(autoToken); ValueType varvt(autovt); if (isconst) varvt.constness |= 1; setValueType(parent->previous(), varvt); Variable * var = const_cast(parent->previous()->variable()); if (var) { var->setValueType(varvt); if (templateArgType && templateArgType->classScope && templateArgType->classScope->definedType) { autoToken->type(templateArgType->classScope->definedType); var->type(templateArgType->classScope->definedType); } } } } } if (vt1 && vt1->containerTypeToken && parent->str() == "[") { ValueType vtParent; if (parsedecl(vt1->containerTypeToken, &vtParent, mDefaultSignedness, mSettings)) { setValueType(parent, vtParent); return; } } if (mIsCpp && vt2 && Token::simpleMatch(parent->previous(), "decltype (")) { setValueType(parent, *vt2); return; } // c++17 auto type deduction of braced init list if (mIsCpp && mSettings->standards.cpp >= Standards::CPP17 && vt2 && Token::Match(parent->tokAt(-2), "auto %var% {")) { Token *autoTok = parent->tokAt(-2); setValueType(autoTok, *vt2); setAutoTokenProperties(autoTok); if (parent->previous()->variable()) const_cast(parent->previous()->variable())->setValueType(*vt2); else debugMessage(parent->previous(), "debug", "Missing variable class for variable with varid"); return; } if (!vt1) return; if (parent->astOperand2() && !vt2) return; const bool ternary = parent->str() == ":" && parent->astParent() && parent->astParent()->str() == "?"; if (ternary) { if (vt2 && vt1->pointer == vt2->pointer && vt1->type == vt2->type && vt1->sign == vt2->sign) setValueType(parent, *vt2); parent = parent->astParent(); } if (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eIncDecOp) { // CONTAINER + x => CONTAINER if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->isIntegral()) { setValueType(parent, *vt1); return; } // x + CONTAINER => CONTAINER if (parent->str() == "+" && vt1->isIntegral() && vt2 && vt2->type == ValueType::Type::CONTAINER) { setValueType(parent, *vt2); return; } if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) { setValueType(parent, *vt1); return; } if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) { setValueType(parent, *vt2); return; } if (vt1->pointer != 0U) { if (ternary || parent->tokType() == Token::eIncDecOp) // result is pointer setValueType(parent, *vt1); else // result is pointer diff setValueType(parent, ValueType(ValueType::Sign::SIGNED, ValueType::Type::INT, 0U, 0U, "ptrdiff_t")); return; } if (vt1->type == ValueType::Type::LONGDOUBLE || (vt2 && vt2->type == ValueType::Type::LONGDOUBLE)) { setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::LONGDOUBLE, 0U)); return; } if (vt1->type == ValueType::Type::DOUBLE || (vt2 && vt2->type == ValueType::Type::DOUBLE)) { setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U)); return; } if (vt1->type == ValueType::Type::FLOAT || (vt2 && vt2->type == ValueType::Type::FLOAT)) { setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U)); return; } // iterator +/- integral = iterator if (vt1->type == ValueType::Type::ITERATOR && vt2 && vt2->isIntegral() && (parent->str() == "+" || parent->str() == "-")) { setValueType(parent, *vt1); return; } if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->type == ValueType::Type::CONTAINER && vt1->container == vt2->container) { setValueType(parent, *vt1); return; } } if (vt1->isIntegral() && vt1->pointer == 0U && (!vt2 || (vt2->isIntegral() && vt2->pointer == 0U)) && (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eBitOp || parent->tokType() == Token::eIncDecOp || parent->isAssignmentOp())) { ValueType vt; if (!vt2 || vt1->type > vt2->type) { vt.type = vt1->type; vt.sign = vt1->sign; vt.originalTypeName = vt1->originalTypeName; } else if (vt1->type == vt2->type) { vt.type = vt1->type; if (vt1->sign == ValueType::Sign::UNSIGNED || vt2->sign == ValueType::Sign::UNSIGNED) vt.sign = ValueType::Sign::UNSIGNED; else if (vt1->sign == ValueType::Sign::UNKNOWN_SIGN || vt2->sign == ValueType::Sign::UNKNOWN_SIGN) vt.sign = ValueType::Sign::UNKNOWN_SIGN; else vt.sign = ValueType::Sign::SIGNED; vt.originalTypeName = (vt1->originalTypeName.empty() ? vt2 : vt1)->originalTypeName; } else { vt.type = vt2->type; vt.sign = vt2->sign; vt.originalTypeName = vt2->originalTypeName; } if (vt.type < ValueType::Type::INT && !(ternary && vt.type==ValueType::Type::BOOL)) { vt.type = ValueType::Type::INT; vt.sign = ValueType::Sign::SIGNED; vt.originalTypeName.clear(); } setValueType(parent, vt); return; } } static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings) { const Token * const previousType = type; const unsigned int pointer0 = valuetype->pointer; while (Token::Match(type->previous(), "%name%") && !endsWith(type->previous()->str(), ':')) type = type->previous(); valuetype->sign = ValueType::Sign::UNKNOWN_SIGN; if (!valuetype->typeScope && !valuetype->smartPointerType) valuetype->type = ValueType::Type::UNKNOWN_TYPE; else if (valuetype->smartPointerType) valuetype->type = ValueType::Type::SMART_POINTER; else if (valuetype->typeScope->type == Scope::eEnum) { const Token * enum_type = valuetype->typeScope->enumType; if (enum_type) { if (enum_type->isSigned()) valuetype->sign = ValueType::Sign::SIGNED; else if (enum_type->isUnsigned()) valuetype->sign = ValueType::Sign::UNSIGNED; else valuetype->sign = defaultSignedness; const ValueType::Type t = ValueType::typeFromString(enum_type->str(), enum_type->isLong()); if (t != ValueType::Type::UNKNOWN_TYPE) valuetype->type = t; else if (enum_type->isStandardType()) valuetype->fromLibraryType(enum_type->str(), settings); } else valuetype->type = ValueType::Type::INT; } else valuetype->type = ValueType::Type::RECORD; bool par = false; while (Token::Match(type, "%name%|*|&|&&|::|(") && !Token::Match(type, "typename|template") && type->varId() == 0 && !type->variable() && !type->function()) { if (type->str() == "(") { if (Token::Match(type->link(), ") const| {")) break; if (par) break; par = true; } if (Token::simpleMatch(type, "decltype (") && type->next()->valueType()) { const ValueType *vt2 = type->next()->valueType(); if (valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) valuetype->sign = vt2->sign; if (valuetype->type == ValueType::Type::UNKNOWN_TYPE) valuetype->type = vt2->type; valuetype->constness += vt2->constness; valuetype->pointer += vt2->pointer; type = type->linkAt(1)->next(); continue; } else if (type->isSigned()) valuetype->sign = ValueType::Sign::SIGNED; else if (type->isUnsigned()) valuetype->sign = ValueType::Sign::UNSIGNED; if (valuetype->type == ValueType::Type::UNKNOWN_TYPE && type->type() && type->type()->isTypeAlias() && type->type()->typeStart && type->type()->typeStart->str() != type->str() && type->type()->typeStart != previousType) parsedecl(type->type()->typeStart, valuetype, defaultSignedness, settings); else if (Token::Match(type, "const|constexpr")) valuetype->constness |= (1 << (valuetype->pointer - pointer0)); else if (settings->clang && type->str().size() > 2 && type->str().find("::") < type->str().find("<")) { TokenList typeTokens(settings); std::string::size_type pos1 = 0; do { std::string::size_type pos2 = type->str().find("::", pos1); if (pos2 == std::string::npos) { typeTokens.addtoken(type->str().substr(pos1), 0, 0, 0, false); break; } typeTokens.addtoken(type->str().substr(pos1, pos2 - pos1), 0, 0, 0, false); typeTokens.addtoken("::", 0, 0, 0, false); pos1 = pos2 + 2; } while (pos1 < type->str().size()); const Library::Container *container = settings->library.detectContainer(typeTokens.front()); if (container) { valuetype->type = ValueType::Type::CONTAINER; valuetype->container = container; } else { const Scope *scope = type->scope(); valuetype->typeScope = scope->check->findScope(typeTokens.front(), scope); if (valuetype->typeScope) valuetype->type = (scope->type == Scope::ScopeType::eClass) ? ValueType::Type::RECORD : ValueType::Type::NONSTD; } } else if (const Library::Container *container = settings->library.detectContainer(type)) { valuetype->type = ValueType::Type::CONTAINER; valuetype->container = container; while (Token::Match(type, "%type%|::|<")) { if (type->str() == "<" && type->link()) { if (container->type_templateArgNo >= 0) { const Token *templateType = type->next(); for (int j = 0; templateType && j < container->type_templateArgNo; j++) templateType = templateType->nextTemplateArgument(); valuetype->containerTypeToken = templateType; } type = type->link(); } type = type->next(); } if (type && type->str() == "(" && type->previous()->function()) // we are past the end of the type type = type->previous(); continue; } else if (const Library::SmartPointer* smartPointer = settings->library.detectSmartPointer(type)) { const Token* argTok = Token::findsimplematch(type, "<"); if (!argTok) continue; valuetype->smartPointer = smartPointer; valuetype->smartPointerTypeToken = argTok->next(); valuetype->smartPointerType = argTok->next()->type(); valuetype->type = ValueType::Type::SMART_POINTER; type = argTok->link(); if (type) type = type->next(); continue; } else if (Token::Match(type, "%name% :: %name%")) { std::string typestr; const Token *end = type; while (Token::Match(end, "%name% :: %name%")) { typestr += end->str() + "::"; end = end->tokAt(2); } typestr += end->str(); if (valuetype->fromLibraryType(typestr, settings)) type = end; } else if (ValueType::Type::UNKNOWN_TYPE != ValueType::typeFromString(type->str(), type->isLong())) { ValueType::Type t0 = valuetype->type; valuetype->type = ValueType::typeFromString(type->str(), type->isLong()); if (t0 == ValueType::Type::LONG) { if (valuetype->type == ValueType::Type::LONG) valuetype->type = ValueType::Type::LONGLONG; else if (valuetype->type == ValueType::Type::DOUBLE) valuetype->type = ValueType::Type::LONGDOUBLE; } } else if (type->str() == "auto") { const ValueType *vt = type->valueType(); if (!vt) return nullptr; valuetype->type = vt->type; valuetype->pointer = vt->pointer; if (vt->sign != ValueType::Sign::UNKNOWN_SIGN) valuetype->sign = vt->sign; valuetype->constness = vt->constness; valuetype->originalTypeName = vt->originalTypeName; while (Token::Match(type, "%name%|*|&|::") && !type->variable()) { if (type->str() == "*") valuetype->pointer++; if (type->str() == "const") valuetype->constness |= (1 << valuetype->pointer); type = type->next(); } break; } else if (!valuetype->typeScope && (type->str() == "struct" || type->str() == "enum")) valuetype->type = type->str() == "struct" ? ValueType::Type::RECORD : ValueType::Type::NONSTD; else if (!valuetype->typeScope && type->type() && type->type()->classScope) { if (type->type()->classScope->type == Scope::ScopeType::eEnum) { valuetype->type = ValueType::Type::INT; valuetype->sign = ValueType::Sign::SIGNED; } else { valuetype->type = ValueType::Type::RECORD; } valuetype->typeScope = type->type()->classScope; } else if (type->isName() && valuetype->sign != ValueType::Sign::UNKNOWN_SIGN && valuetype->pointer == 0U) return nullptr; else if (type->str() == "*") valuetype->pointer++; else if (type->str() == "&") valuetype->reference = Reference::LValue; else if (type->str() == "&&") valuetype->reference = Reference::RValue; else if (type->isStandardType()) valuetype->fromLibraryType(type->str(), settings); else if (Token::Match(type->previous(), "!!:: %name% !!::")) valuetype->fromLibraryType(type->str(), settings); if (!type->originalName().empty()) valuetype->originalTypeName = type->originalName(); type = type->next(); } // Set signedness for integral types.. if (valuetype->isIntegral() && valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) { if (valuetype->type == ValueType::Type::CHAR) valuetype->sign = defaultSignedness; else if (valuetype->type >= ValueType::Type::SHORT) valuetype->sign = ValueType::Sign::SIGNED; } return (type && (valuetype->type != ValueType::Type::UNKNOWN_TYPE || valuetype->pointer > 0 || valuetype->reference != Reference::None)) ? type : nullptr; } static const Scope *getClassScope(const Token *tok) { return tok && tok->valueType() && tok->valueType()->typeScope && tok->valueType()->typeScope->isClassOrStruct() ? tok->valueType()->typeScope : nullptr; } static const Function *getOperatorFunction(const Token * const tok) { const std::string functionName("operator" + tok->str()); std::multimap::const_iterator it; const Scope *classScope = getClassScope(tok->astOperand1()); if (classScope) { it = classScope->functionMap.find(functionName); if (it != classScope->functionMap.end()) return it->second; } classScope = getClassScope(tok->astOperand2()); if (classScope) { it = classScope->functionMap.find(functionName); if (it != classScope->functionMap.end()) return it->second; } return nullptr; } void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens) { if (!tokens) tokens = const_cast(mTokenizer)->list.front(); for (Token *tok = tokens; tok; tok = tok->next()) tok->setValueType(nullptr); for (Token *tok = tokens; tok; tok = tok->next()) { if (tok->isNumber()) { if (MathLib::isFloat(tok->str())) { ValueType::Type type = ValueType::Type::DOUBLE; const char suffix = tok->str()[tok->str().size() - 1]; if (suffix == 'f' || suffix == 'F') type = ValueType::Type::FLOAT; else if (suffix == 'L' || suffix == 'l') type = ValueType::Type::LONGDOUBLE; setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, type, 0U)); } else if (MathLib::isInt(tok->str())) { const std::string tokStr = MathLib::abs(tok->str()); const bool unsignedSuffix = (tokStr.find_last_of("uU") != std::string::npos); ValueType::Sign sign = unsignedSuffix ? ValueType::Sign::UNSIGNED : ValueType::Sign::SIGNED; ValueType::Type type = ValueType::Type::INT; const MathLib::biguint value = MathLib::toULongNumber(tokStr); for (std::size_t pos = tokStr.size() - 1U; pos > 0U; --pos) { const char suffix = tokStr[pos]; if (suffix == 'u' || suffix == 'U') sign = ValueType::Sign::UNSIGNED; else if (suffix == 'l' || suffix == 'L') type = (type == ValueType::Type::INT) ? ValueType::Type::LONG : ValueType::Type::LONGLONG; else if (pos > 2U && suffix == '4' && tokStr[pos - 1] == '6' && tokStr[pos - 2] == 'i') { type = ValueType::Type::LONGLONG; pos -= 2; } else break; } if (mSettings->platformType != cppcheck::Platform::Unspecified) { if (type <= ValueType::Type::INT && mSettings->isIntValue(unsignedSuffix ? (value >> 1) : value)) type = ValueType::Type::INT; else if (type <= ValueType::Type::INT && !MathLib::isDec(tokStr) && mSettings->isIntValue(value >> 2)) { type = ValueType::Type::INT; sign = ValueType::Sign::UNSIGNED; } else if (type <= ValueType::Type::LONG && mSettings->isLongValue(unsignedSuffix ? (value >> 1) : value)) type = ValueType::Type::LONG; else if (type <= ValueType::Type::LONG && !MathLib::isDec(tokStr) && mSettings->isLongValue(value >> 2)) { type = ValueType::Type::LONG; sign = ValueType::Sign::UNSIGNED; } else if (mSettings->isLongLongValue(unsignedSuffix ? (value >> 1) : value)) type = ValueType::Type::LONGLONG; else { type = ValueType::Type::LONGLONG; sign = ValueType::Sign::UNSIGNED; } } setValueType(tok, ValueType(sign, type, 0U)); } } else if (tok->isComparisonOp() || tok->tokType() == Token::eLogicalOp) { if (mIsCpp && tok->isComparisonOp() && (getClassScope(tok->astOperand1()) || getClassScope(tok->astOperand2()))) { const Function *function = getOperatorFunction(tok); if (function) { ValueType vt; parsedecl(function->retDef, &vt, mDefaultSignedness, mSettings); setValueType(tok, vt); continue; } } setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); } else if (tok->isBoolean()) { setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); } else if (tok->tokType() == Token::eChar || tok->tokType() == Token::eString) { nonneg int pointer = tok->tokType() == Token::eChar ? 0U : 1U; nonneg int constness = tok->tokType() == Token::eChar ? 0U : 1U; ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, pointer, constness); if (mIsCpp && mSettings->standards.cpp >= Standards::CPP20 && tok->isUtf8()) { valuetype.originalTypeName = "char8_t"; valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); } else if (tok->isUtf16()) { valuetype.originalTypeName = "char16_t"; valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); } else if (tok->isUtf32()) { valuetype.originalTypeName = "char32_t"; valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); } else if (tok->isLong()) { valuetype.originalTypeName = "wchar_t"; valuetype.type = ValueType::Type::WCHAR_T; } else if ((tok->tokType() == Token::eChar) && ((tok->isCChar() && !mIsCpp) || (tok->isCMultiChar()))) { valuetype.type = ValueType::Type::INT; valuetype.sign = ValueType::Sign::SIGNED; } setValueType(tok, valuetype); } else if (tok->link() && Token::Match(tok, "(|{")) { const Token* start = tok->astOperand1() ? tok->astOperand1()->findExpressionStartEndTokens().first : nullptr; // cast if (tok->isCast() && !tok->astOperand2() && Token::Match(tok, "( %name%")) { ValueType valuetype; if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, mDefaultSignedness, mSettings), ")")) setValueType(tok, valuetype); } // C++ cast else if (tok->astOperand2() && Token::Match(tok->astOperand1(), "static_cast|const_cast|dynamic_cast|reinterpret_cast < %name%") && tok->astOperand1()->linkAt(1)) { ValueType valuetype; if (Token::simpleMatch(parsedecl(tok->astOperand1()->tokAt(2), &valuetype, mDefaultSignedness, mSettings), ">")) setValueType(tok, valuetype); } // Construct smart pointer else if (mSettings->library.isSmartPointer(start)) { ValueType valuetype; if (parsedecl(start, &valuetype, mDefaultSignedness, mSettings)) { setValueType(tok, valuetype); setValueType(tok->astOperand1(), valuetype); } } // function else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) { ValueType valuetype; if (parsedecl(tok->previous()->function()->retDef, &valuetype, mDefaultSignedness, mSettings)) setValueType(tok, valuetype); } else if (Token::simpleMatch(tok->previous(), "sizeof (")) { ValueType valuetype(ValueType::Sign::UNSIGNED, ValueType::Type::LONG, 0U); if (mSettings->platformType == cppcheck::Platform::Win64) valuetype.type = ValueType::Type::LONGLONG; valuetype.originalTypeName = "size_t"; setValueType(tok, valuetype); if (Token::Match(tok, "( %type% %type%| *| *| )")) { ValueType vt; if (parsedecl(tok->next(), &vt, mDefaultSignedness, mSettings)) { setValueType(tok->next(), vt); } } } // function style cast else if (tok->previous() && tok->previous()->isStandardType()) { ValueType valuetype; if (tok->astOperand1() && valuetype.fromLibraryType(tok->astOperand1()->expressionString(), mSettings)) { setValueType(tok, valuetype); continue; } valuetype.type = ValueType::typeFromString(tok->previous()->str(), tok->previous()->isLong()); if (tok->previous()->isUnsigned()) valuetype.sign = ValueType::Sign::UNSIGNED; else if (tok->previous()->isSigned()) valuetype.sign = ValueType::Sign::SIGNED; else if (valuetype.isIntegral() && valuetype.type != ValueType::UNKNOWN_INT) valuetype.sign = mDefaultSignedness; setValueType(tok, valuetype); } // constructor call else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->isConstructor()) { ValueType valuetype; valuetype.type = ValueType::RECORD; valuetype.typeScope = tok->previous()->function()->token->scope(); setValueType(tok, valuetype); } else if (Token::simpleMatch(tok->previous(), "= {") && tok->tokAt(-2) && tok->tokAt(-2)->valueType()) { ValueType vt = *tok->tokAt(-2)->valueType(); setValueType(tok, vt); } // library type/function else if (tok->previous()) { // Aggregate constructor if (Token::Match(tok->previous(), "%name%")) { ValueType valuetype; if (parsedecl(tok->previous(), &valuetype, mDefaultSignedness, mSettings)) { if (valuetype.typeScope) { setValueType(tok, valuetype); continue; } } } if (tok->astParent() && Token::Match(tok->astOperand1(), "%name%|::")) { const Token *typeStartToken = tok->astOperand1(); while (typeStartToken && typeStartToken->str() == "::") typeStartToken = typeStartToken->astOperand1(); if (mSettings->library.detectContainer(typeStartToken) || mSettings->library.detectSmartPointer(typeStartToken)) { ValueType vt; if (parsedecl(typeStartToken, &vt, mDefaultSignedness, mSettings)) { setValueType(tok, vt); continue; } } const std::string e = tok->astOperand1()->expressionString(); if ((e == "std::make_shared" || e == "std::make_unique") && Token::Match(tok->astOperand1(), ":: %name% < %name%")) { ValueType vt; parsedecl(tok->astOperand1()->tokAt(3), &vt, mDefaultSignedness, mSettings); if (vt.typeScope) { vt.smartPointerType = vt.typeScope->definedType; vt.typeScope = nullptr; } if (e == "std::make_shared" && mSettings->library.smartPointers.count("std::shared_ptr") > 0) vt.smartPointer = &mSettings->library.smartPointers.at("std::shared_ptr"); if (e == "std::make_unique" && mSettings->library.smartPointers.count("std::unique_ptr") > 0) vt.smartPointer = &mSettings->library.smartPointers.at("std::unique_ptr"); vt.type = ValueType::Type::SMART_POINTER; vt.smartPointerTypeToken = tok->astOperand1()->tokAt(3); setValueType(tok, vt); continue; } ValueType podtype; if (podtype.fromLibraryType(e, mSettings)) { setValueType(tok, podtype); continue; } } const std::string& typestr(mSettings->library.returnValueType(tok->previous())); if (!typestr.empty()) { ValueType valuetype; TokenList tokenList(mSettings); std::istringstream istr(typestr+";"); tokenList.createTokens(istr); tokenList.simplifyStdType(); if (parsedecl(tokenList.front(), &valuetype, mDefaultSignedness, mSettings)) { valuetype.originalTypeName = typestr; setValueType(tok, valuetype); } } if (typestr.empty() || typestr == "iterator") { if (Token::simpleMatch(tok->astOperand1(), ".") && tok->astOperand1()->astOperand1() && tok->astOperand1()->astOperand2() && tok->astOperand1()->astOperand1()->valueType() && tok->astOperand1()->astOperand1()->valueType()->container) { const Library::Container *cont = tok->astOperand1()->astOperand1()->valueType()->container; const std::map::const_iterator it = cont->functions.find(tok->astOperand1()->astOperand2()->str()); if (it != cont->functions.end()) { if (it->second.yield == Library::Container::Yield::START_ITERATOR || it->second.yield == Library::Container::Yield::END_ITERATOR || it->second.yield == Library::Container::Yield::ITERATOR) { ValueType vt; vt.type = ValueType::Type::ITERATOR; vt.container = cont; vt.containerTypeToken = tok->astOperand1()->astOperand1()->valueType()->containerTypeToken; setValueType(tok, vt); } } } continue; } TokenList tokenList(mSettings); std::istringstream istr(typestr+";"); if (tokenList.createTokens(istr)) { ValueType vt; tokenList.simplifyPlatformTypes(); tokenList.simplifyStdType(); if (parsedecl(tokenList.front(), &vt, mDefaultSignedness, mSettings)) { vt.originalTypeName = typestr; setValueType(tok, vt); } } } } else if (tok->str() == "return") { const Scope *functionScope = tok->scope(); while (functionScope && functionScope->isExecutable() && functionScope->type != Scope::eLambda && functionScope->type != Scope::eFunction) functionScope = functionScope->nestedIn; if (functionScope && functionScope->type == Scope::eFunction && functionScope->function && functionScope->function->retDef) { ValueType vt = ValueType::parseDecl(functionScope->function->retDef, mSettings); setValueType(tok, vt); if (Token::simpleMatch(tok, "return {")) setValueType(tok->next(), vt); } } else if (tok->variable()) { setValueType(tok, *tok->variable()); if (!tok->variable()->valueType() && tok->valueType()) const_cast(tok->variable())->setValueType(*tok->valueType()); } else if (tok->enumerator()) { setValueType(tok, *tok->enumerator()); } else if (tok->isKeyword() && tok->str() == "new") { const Token *typeTok = tok->next(); if (Token::Match(typeTok, "( std| ::| nothrow )")) typeTok = typeTok->link()->next(); if (const Library::Container *c = mSettings->library.detectContainer(typeTok)) { ValueType vt; vt.pointer = 1; vt.container = c; vt.type = ValueType::Type::CONTAINER; setValueType(tok, vt); continue; } std::string typestr; while (Token::Match(typeTok, "%name% :: %name%")) { typestr += typeTok->str() + "::"; typeTok = typeTok->tokAt(2); } if (!Token::Match(typeTok, "%type% ;|[|(")) continue; typestr += typeTok->str(); ValueType vt; vt.pointer = 1; if (typeTok->type() && typeTok->type()->classScope) { vt.type = ValueType::Type::RECORD; vt.typeScope = typeTok->type()->classScope; } else { vt.type = ValueType::typeFromString(typestr, typeTok->isLong()); if (vt.type == ValueType::Type::UNKNOWN_TYPE) vt.fromLibraryType(typestr, mSettings); if (vt.type == ValueType::Type::UNKNOWN_TYPE) continue; if (typeTok->isUnsigned()) vt.sign = ValueType::Sign::UNSIGNED; else if (typeTok->isSigned()) vt.sign = ValueType::Sign::SIGNED; if (vt.sign == ValueType::Sign::UNKNOWN_SIGN && vt.isIntegral()) vt.sign = (vt.type == ValueType::Type::CHAR) ? mDefaultSignedness : ValueType::Sign::SIGNED; } setValueType(tok, vt); if (Token::simpleMatch(tok->astOperand1(), "(")) { vt.pointer--; setValueType(tok->astOperand1(), vt); } } else if (tok->isKeyword() && tok->str() == "return" && tok->scope()) { const Scope* fscope = tok->scope(); while (fscope && !fscope->function) fscope = fscope->nestedIn; if (fscope && fscope->function && fscope->function->retDef) { ValueType vt; parsedecl(fscope->function->retDef, &vt, mDefaultSignedness, mSettings); setValueType(tok, vt); } } } if (reportDebugWarnings && mSettings->debugwarnings) { for (Token *tok = tokens; tok; tok = tok->next()) { if (tok->str() == "auto" && !tok->valueType()) debugMessage(tok, "autoNoType", "auto token with no type."); } } // Update functions with new type information. createSymbolDatabaseSetFunctionPointers(false); // Update auto variables with new type information. createSymbolDatabaseSetVariablePointers(); } ValueType ValueType::parseDecl(const Token *type, const Settings *settings) { ValueType vt; parsedecl(type, &vt, settings->defaultSign == 'u' ? Sign::UNSIGNED : Sign::SIGNED, settings); return vt; } ValueType::Type ValueType::typeFromString(const std::string &typestr, bool longType) { if (typestr == "void") return ValueType::Type::VOID; if (typestr == "bool" || typestr == "_Bool") return ValueType::Type::BOOL; if (typestr== "char") return ValueType::Type::CHAR; if (typestr == "short") return ValueType::Type::SHORT; if (typestr == "wchar_t") return ValueType::Type::WCHAR_T; if (typestr == "int") return ValueType::Type::INT; if (typestr == "long") return longType ? ValueType::Type::LONGLONG : ValueType::Type::LONG; if (typestr == "float") return ValueType::Type::FLOAT; if (typestr == "double") return longType ? ValueType::Type::LONGDOUBLE : ValueType::Type::DOUBLE; return ValueType::Type::UNKNOWN_TYPE; } bool ValueType::fromLibraryType(const std::string &typestr, const Settings *settings) { const Library::PodType* podtype = settings->library.podtype(typestr); if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) { if (podtype->size == 1) type = ValueType::Type::CHAR; else if (podtype->size == settings->sizeof_int) type = ValueType::Type::INT; else if (podtype->size == settings->sizeof_short) type = ValueType::Type::SHORT; else if (podtype->size == settings->sizeof_long) type = ValueType::Type::LONG; else if (podtype->size == settings->sizeof_long_long) type = ValueType::Type::LONGLONG; else if (podtype->stdtype == Library::PodType::Type::BOOL) type = ValueType::Type::BOOL; else if (podtype->stdtype == Library::PodType::Type::CHAR) type = ValueType::Type::CHAR; else if (podtype->stdtype == Library::PodType::Type::SHORT) type = ValueType::Type::SHORT; else if (podtype->stdtype == Library::PodType::Type::INT) type = ValueType::Type::INT; else if (podtype->stdtype == Library::PodType::Type::LONG) type = ValueType::Type::LONG; else if (podtype->stdtype == Library::PodType::Type::LONGLONG) type = ValueType::Type::LONGLONG; else type = ValueType::Type::UNKNOWN_INT; sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED; return true; } const Library::PlatformType *platformType = settings->library.platform_type(typestr, settings->platformString()); if (platformType) { if (platformType->mType == "char") type = ValueType::Type::CHAR; else if (platformType->mType == "short") type = ValueType::Type::SHORT; else if (platformType->mType == "wchar_t") type = ValueType::Type::WCHAR_T; else if (platformType->mType == "int") type = platformType->mLong ? ValueType::Type::LONG : ValueType::Type::INT; else if (platformType->mType == "long") type = platformType->mLong ? ValueType::Type::LONGLONG : ValueType::Type::LONG; if (platformType->mSigned) sign = ValueType::SIGNED; else if (platformType->mUnsigned) sign = ValueType::UNSIGNED; if (platformType->mPointer) pointer = 1; if (platformType->mPtrPtr) pointer = 2; if (platformType->mConstPtr) constness = 1; return true; } else if (!podtype && (typestr == "size_t" || typestr == "std::size_t")) { originalTypeName = "size_t"; sign = ValueType::UNSIGNED; if (settings->sizeof_size_t == settings->sizeof_long) type = ValueType::Type::LONG; else if (settings->sizeof_size_t == settings->sizeof_long_long) type = ValueType::Type::LONGLONG; else if (settings->sizeof_size_t == settings->sizeof_int) type = ValueType::Type::INT; else type = ValueType::Type::UNKNOWN_INT; return true; } return false; } std::string ValueType::dump() const { std::ostringstream ret; switch (type) { case UNKNOWN_TYPE: return ""; case NONSTD: ret << "valueType-type=\"nonstd\""; break; case RECORD: ret << "valueType-type=\"record\""; break; case SMART_POINTER: ret << "valueType-type=\"smart-pointer\""; break; case CONTAINER: ret << "valueType-type=\"container\""; break; case ITERATOR: ret << "valueType-type=\"iterator\""; break; case VOID: ret << "valueType-type=\"void\""; break; case BOOL: ret << "valueType-type=\"bool\""; break; case CHAR: ret << "valueType-type=\"char\""; break; case SHORT: ret << "valueType-type=\"short\""; break; case WCHAR_T: ret << "valueType-type=\"wchar_t\""; break; case INT: ret << "valueType-type=\"int\""; break; case LONG: ret << "valueType-type=\"long\""; break; case LONGLONG: ret << "valueType-type=\"long long\""; break; case UNKNOWN_INT: ret << "valueType-type=\"unknown int\""; break; case FLOAT: ret << "valueType-type=\"float\""; break; case DOUBLE: ret << "valueType-type=\"double\""; break; case LONGDOUBLE: ret << "valueType-type=\"long double\""; break; } switch (sign) { case Sign::UNKNOWN_SIGN: break; case Sign::SIGNED: ret << " valueType-sign=\"signed\""; break; case Sign::UNSIGNED: ret << " valueType-sign=\"unsigned\""; break; } if (bits > 0) ret << " valueType-bits=\"" << bits << '\"'; if (pointer > 0) ret << " valueType-pointer=\"" << pointer << '\"'; if (constness > 0) ret << " valueType-constness=\"" << constness << '\"'; if (reference == Reference::None) ret << " valueType-reference=\"None\""; else if (reference == Reference::LValue) ret << " valueType-reference=\"LValue\""; else if (reference == Reference::RValue) ret << " valueType-reference=\"RValue\""; if (typeScope) ret << " valueType-typeScope=\"" << typeScope << '\"'; if (!originalTypeName.empty()) ret << " valueType-originalTypeName=\"" << ErrorLogger::toxml(originalTypeName) << '\"'; return ret.str(); } MathLib::bigint ValueType::typeSize(const cppcheck::Platform &platform, bool p) const { if (p && pointer) return platform.sizeof_pointer; 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: return 1; case ValueType::Type::SHORT: return platform.sizeof_short; case ValueType::Type::WCHAR_T: return platform.sizeof_wchar_t; case ValueType::Type::INT: return platform.sizeof_int; case ValueType::Type::LONG: return platform.sizeof_long; case ValueType::Type::LONGLONG: return platform.sizeof_long_long; case ValueType::Type::FLOAT: return platform.sizeof_float; case ValueType::Type::DOUBLE: return platform.sizeof_double; case ValueType::Type::LONGDOUBLE: return platform.sizeof_long_double; default: break; } // Unknown invalid size return 0; } std::string ValueType::str() const { std::string ret; if (constness & 1) ret = " const"; if (type == VOID) ret += " void"; else if (isIntegral()) { if (sign == SIGNED) ret += " signed"; else if (sign == UNSIGNED) ret += " unsigned"; if (type == BOOL) ret += " bool"; else if (type == CHAR) ret += " char"; else if (type == SHORT) ret += " short"; else if (type == WCHAR_T) ret += " wchar_t"; else if (type == INT) ret += " int"; else if (type == LONG) ret += " long"; else if (type == LONGLONG) ret += " long long"; else if (type == UNKNOWN_INT) ret += " unknown_int"; } else if (type == FLOAT) ret += " float"; else if (type == DOUBLE) ret += " double"; else if (type == LONGDOUBLE) ret += " long double"; else if ((type == ValueType::Type::NONSTD || type == ValueType::Type::RECORD) && typeScope) { std::string className(typeScope->className); const Scope *scope = typeScope->definedType ? typeScope->definedType->enclosingScope : typeScope->nestedIn; while (scope && scope->type != Scope::eGlobal) { if (scope->type == Scope::eClass || scope->type == Scope::eStruct || scope->type == Scope::eNamespace) className = scope->className + "::" + className; scope = (scope->definedType && scope->definedType->enclosingScope) ? scope->definedType->enclosingScope : scope->nestedIn; } ret += ' ' + className; } else if (type == ValueType::Type::CONTAINER && container) { ret += " container(" + container->startPattern + ')'; } else if (type == ValueType::Type::ITERATOR && container) { ret += " iterator(" + container->startPattern + ')'; } else if (type == ValueType::Type::SMART_POINTER && smartPointer) { ret += " smart-pointer(" + smartPointer->name + ")"; } for (unsigned int p = 0; p < pointer; p++) { ret += " *"; if (constness & (2 << p)) ret += " const"; } if (reference == Reference::LValue) ret += " &"; else if (reference == Reference::RValue) ret += " &&"; return ret.empty() ? ret : ret.substr(1); } ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const ValueType *func) { if (!call || !func) return ValueType::MatchResult::UNKNOWN; if (call->pointer != func->pointer) { if (call->pointer > 1 && func->pointer == 1 && func->type == ValueType::Type::VOID) return ValueType::MatchResult::FALLBACK1; if (call->pointer == 1 && func->pointer == 0 && func->isIntegral() && func->sign != ValueType::Sign::SIGNED) return ValueType::MatchResult::FALLBACK1; if (call->pointer == 1 && call->type == ValueType::Type::CHAR && func->pointer == 0 && func->container && func->container->stdStringLike) return ValueType::MatchResult::FALLBACK2; return ValueType::MatchResult::NOMATCH; // TODO } if (call->pointer > 0) { if ((call->constness | func->constness) != func->constness) return ValueType::MatchResult::NOMATCH; if (call->constness == 0 && func->constness != 0 && func->reference != Reference::None) return ValueType::MatchResult::NOMATCH; } if (call->type != func->type) { if (call->type == ValueType::Type::VOID || func->type == ValueType::Type::VOID) return ValueType::MatchResult::FALLBACK1; if (call->pointer > 0) return func->type == ValueType::UNKNOWN_TYPE ? ValueType::MatchResult::UNKNOWN : ValueType::MatchResult::NOMATCH; if (call->isIntegral() && func->isIntegral()) return call->type < func->type ? ValueType::MatchResult::FALLBACK1 : ValueType::MatchResult::FALLBACK2; else if (call->isFloat() && func->isFloat()) return ValueType::MatchResult::FALLBACK1; else if (call->isIntegral() && func->isFloat()) return ValueType::MatchResult::FALLBACK2; else if (call->isFloat() && func->isIntegral()) return ValueType::MatchResult::FALLBACK2; return ValueType::MatchResult::UNKNOWN; // TODO } if (call->typeScope != nullptr || func->typeScope != nullptr) { if (call->typeScope != func->typeScope) return ValueType::MatchResult::NOMATCH; } if (call->container != nullptr || func->container != nullptr) { if (call->container != func->container) return ValueType::MatchResult::NOMATCH; } if (func->typeScope != nullptr && func->container != nullptr) { if (func->type < ValueType::Type::VOID || func->type == ValueType::Type::UNKNOWN_INT) return ValueType::MatchResult::UNKNOWN; } if (call->isIntegral() && func->isIntegral() && call->sign != ValueType::Sign::UNKNOWN_SIGN && func->sign != ValueType::Sign::UNKNOWN_SIGN && call->sign != func->sign) return ValueType::MatchResult::FALLBACK1; if (func->reference != Reference::None && func->constness > call->constness) return ValueType::MatchResult::FALLBACK1; return ValueType::MatchResult::SAME; } ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar) { ValueType vt; const ValueType* pvt = funcVar->valueType(); if (pvt && funcVar->isArray() && !(funcVar->isStlType() && Token::simpleMatch(funcVar->typeStartToken(), "std :: array"))) { // std::array doesn't decay to a pointer vt = *pvt; if (vt.pointer == 0) // don't bump array of pointers ++vt.pointer; pvt = &vt; } ValueType::MatchResult res = ValueType::matchParameter(call, pvt); if (callVar && ((res == ValueType::MatchResult::SAME && call->container) || res == ValueType::MatchResult::UNKNOWN)) { const std::string type1 = getTypeString(callVar->typeStartToken()); const std::string type2 = getTypeString(funcVar->typeStartToken()); const bool templateVar = funcVar->scope() && funcVar->scope()->function && funcVar->scope()->function->templateDef; if (type1 == type2) return ValueType::MatchResult::SAME; if (!templateVar && type1.find("auto") == std::string::npos && type2.find("auto") == std::string::npos) return ValueType::MatchResult::NOMATCH; } return res; } cppcheck-2.7/lib/symboldatabase.h000066400000000000000000001337401417746362400171010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef symboldatabaseH #define symboldatabaseH //--------------------------------------------------------------------------- #include "config.h" #include "library.h" #include "mathlib.h" #include "token.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include namespace cppcheck { class Platform; } class ErrorLogger; class Function; class Scope; class Settings; class SymbolDatabase; class Tokenizer; class ValueType; /** * @brief Access control enumerations. */ enum class AccessControl { Public, Protected, Private, Global, Namespace, Argument, Local, Throw }; /** * @brief Array dimension information. */ struct Dimension { Dimension() : tok(nullptr), num(0), known(true) {} const Token *tok; ///< size token MathLib::bigint num; ///< (assumed) dimension length when size is a number, 0 if not known bool known; ///< Known size }; /** @brief Information about a class type. */ class CPPCHECKLIB Type { public: const Token* classDef; ///< Points to "class" token const Scope* classScope; const Scope* enclosingScope; enum class NeedInitialization { Unknown, True, False } needInitialization; class BaseInfo { public: BaseInfo() : type(nullptr), nameTok(nullptr), access(AccessControl::Public), isVirtual(false) {} std::string name; const Type* type; const Token* nameTok; AccessControl access; // public/protected/private bool isVirtual; // allow ordering within containers bool operator<(const BaseInfo& rhs) const { return this->type < rhs.type; } }; struct FriendInfo { FriendInfo() : nameStart(nullptr), nameEnd(nullptr), type(nullptr) {} const Token* nameStart; const Token* nameEnd; const Type* type; }; std::vector derivedFrom; std::vector friendList; const Token * typeStart; const Token * typeEnd; MathLib::bigint sizeOf; Type(const Token* classDef_ = nullptr, const Scope* classScope_ = nullptr, const Scope* enclosingScope_ = nullptr) : classDef(classDef_), classScope(classScope_), enclosingScope(enclosingScope_), needInitialization(NeedInitialization::Unknown), typeStart(nullptr), typeEnd(nullptr), sizeOf(0) { if (classDef_ && classDef_->str() == "enum") needInitialization = NeedInitialization::True; else if (classDef_ && classDef_->str() == "using") { typeStart = classDef->tokAt(3); typeEnd = typeStart; while (typeEnd->next() && typeEnd->next()->str() != ";") typeEnd = typeEnd->next(); } } const std::string& name() const; const std::string& type() const { return classDef ? classDef->str() : emptyString; } bool isClassType() const; bool isEnumType() const; bool isStructType() const; bool isUnionType() const; bool isTypeAlias() const { return classDef && classDef->str() == "using"; } const Token *initBaseInfo(const Token *tok, const Token *tok1); const Function* getFunction(const std::string& funcName) const; /** * Check for circulare dependencies, i.e. loops within the class hierarchy * @param ancestors list of ancestors. For internal usage only, clients should not supply this argument. * @return true if there is a circular dependency */ bool hasCircularDependencies(std::set* ancestors = nullptr) const; /** * Check for dependency * @param ancestor potential ancestor * @return true if there is a dependency */ bool findDependency(const Type* ancestor) const; bool isDerivedFrom(const std::string & ancestor) const; }; class CPPCHECKLIB Enumerator { public: explicit Enumerator(const Scope * scope_) : scope(scope_), name(nullptr), value(0), start(nullptr), end(nullptr), value_known(false) {} const Scope * scope; const Token * name; MathLib::bigint value; const Token * start; const Token * end; bool value_known; }; /** @brief Information about a member variable. */ class CPPCHECKLIB Variable { /** @brief flags mask used to access specific bit. */ enum { fIsMutable = (1 << 0), /** @brief mutable variable */ fIsStatic = (1 << 1), /** @brief static variable */ fIsConst = (1 << 2), /** @brief const variable */ fIsExtern = (1 << 3), /** @brief extern variable */ fIsClass = (1 << 4), /** @brief user defined type */ fIsArray = (1 << 5), /** @brief array variable */ fIsPointer = (1 << 6), /** @brief pointer variable */ fIsReference = (1 << 7), /** @brief reference variable */ fIsRValueRef = (1 << 8), /** @brief rvalue reference variable */ fHasDefault = (1 << 9), /** @brief function argument with default value */ fIsStlType = (1 << 10), /** @brief STL type ('std::') */ fIsStlString = (1 << 11), /** @brief std::string|wstring|basic_string<T>|u16string|u32string */ fIsFloatType = (1 << 12), /** @brief Floating point type */ fIsVolatile = (1 << 13), /** @brief volatile */ fIsSmartPointer = (1 << 14),/** @brief std::shared_ptr|unique_ptr */ fIsMaybeUnused = (1 << 15), /** @brief marked [[maybe_unused]] */ fIsInit = (1 << 16), /** @brief Is variable initialized in declaration */ }; /** * Get specified flag state. * @param flag_ flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(unsigned int flag_) const { return ((mFlags & flag_) != 0); } /** * Set specified flag state. * @param flag_ flag to set state * @param state_ new state of flag */ void setFlag(unsigned int flag_, bool state_) { mFlags = state_ ? mFlags | flag_ : mFlags & ~flag_; } /** * @brief parse and save array dimension information * @param settings Platform settings and library * @param isContainer Is the array container-like? * @return true if array, false if not */ bool arrayDimensions(const Settings* settings, bool* isContainer); public: Variable(const Token *name_, const Token *start_, const Token *end_, nonneg int index_, AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings) : mNameToken(name_), mTypeStartToken(start_), mTypeEndToken(end_), mIndex(index_), mAccess(access_), mFlags(0), mType(type_), mScope(scope_), mValueType(nullptr) { evaluate(settings); } Variable(const Token *name_, const std::string &clangType, const Token *typeStart, const Token *typeEnd, nonneg int index_, AccessControl access_, const Type *type_, const Scope *scope_); Variable(const Variable &var, const Scope *scope); Variable(const Variable &var); ~Variable(); Variable &operator=(const Variable &var); /** * Get name token. * @return name token */ const Token *nameToken() const { return mNameToken; } /** * Get type start token. * The type start token doesn't account 'static' and 'const' qualifiers * E.g.: * static const int * const p = ...; * type start token ^ * @return type start token */ const Token *typeStartToken() const { return mTypeStartToken; } /** * Get type end token. * The type end token doesn't account the forward 'const' qualifier * E.g.: * static const int * const p = ...; * type end token ^ * @return type end token */ const Token *typeEndToken() const { return mTypeEndToken; } /** * Get end token of variable declaration * E.g. * int i[2][3] = ... * end token ^ * @return variable declaration end token */ const Token *declEndToken() const; /** * Get name string. * @return name string */ const std::string &name() const { // name may not exist for function arguments if (mNameToken) return mNameToken->str(); return emptyString; } /** * Get declaration ID (varId used for variable in its declaration). * @return declaration ID */ nonneg int declarationId() const { // name may not exist for function arguments if (mNameToken) return mNameToken->varId(); return 0; } /** * Get index of variable in declared order. * @return variable index */ nonneg int index() const { return mIndex; } /** * Is variable public. * @return true if public, false if not */ bool isPublic() const { return mAccess == AccessControl::Public; } /** * Is variable protected. * @return true if protected, false if not */ bool isProtected() const { return mAccess == AccessControl::Protected; } /** * Is variable private. * @return true if private, false if not */ bool isPrivate() const { return mAccess == AccessControl::Private; } /** * Is variable global. * @return true if global, false if not */ bool isGlobal() const { return mAccess == AccessControl::Global; } /** * Is variable in a namespace. * @return true if in a namespace, false if not */ bool isNamespace() const { return mAccess == AccessControl::Namespace; } /** * Is variable a function argument. * @return true if a function argument, false if not */ bool isArgument() const { return mAccess == AccessControl::Argument; } /** * Is variable local. * @return true if local, false if not */ bool isLocal() const { return (mAccess == AccessControl::Local) && !isExtern(); } /** * Is variable mutable. * @return true if mutable, false if not */ bool isMutable() const { return getFlag(fIsMutable); } /** * Is variable volatile. * @return true if volatile, false if not */ bool isVolatile() const { return getFlag(fIsVolatile); } /** * Is variable static. * @return true if static, false if not */ bool isStatic() const { return getFlag(fIsStatic); } /** * Is variable extern. * @return true if extern, false if not */ bool isExtern() const { return getFlag(fIsExtern); } /** * Is variable const. * @return true if const, false if not */ bool isConst() const { return getFlag(fIsConst); } /** * Is variable a throw type. * @return true if throw type, false if not */ bool isThrow() const { return mAccess == AccessControl::Throw; } /** * Is variable a user defined (or unknown) type. * @return true if user defined type, false if not */ bool isClass() const { return getFlag(fIsClass); } /** * Is variable an array. * @return true if array, false if not */ bool isArray() const { return getFlag(fIsArray) && !getFlag(fIsPointer); } /** * Is pointer variable. * @return true if pointer, false otherwise */ bool isPointer() const { return getFlag(fIsPointer); } /** * Is variable a pointer to an array * @return true if pointer to array, false otherwise */ bool isPointerToArray() const { return isPointer() && getFlag(fIsArray); } /** * Is variable an array of pointers * @return true if array or pointers, false otherwise */ bool isPointerArray() const; /** * Is array or pointer variable. * @return true if pointer or array, false otherwise */ bool isArrayOrPointer() const { return getFlag(fIsArray) || getFlag(fIsPointer); } /** * Is reference variable. * @return true if reference, false otherwise */ bool isReference() const { return getFlag(fIsReference); } /** * Is reference variable. * @return true if reference, false otherwise */ bool isRValueReference() const { return getFlag(fIsRValueRef); } /** * Is variable unsigned. * @return true only if variable _is_ unsigned. if the sign is unknown, false is returned. */ bool isUnsigned() const; /** * Does variable have a default value. * @return true if has a default falue, false if not */ bool hasDefault() const { return getFlag(fHasDefault); } /** * Is variable initialized in its declaration * @return true if variable declaration contains initialization */ bool isInit() const { return getFlag(fIsInit); } /** * Get Type pointer of known type. * @return pointer to type if known, NULL if not known */ const Type *type() const { return mType; } /** * Get Scope pointer of known type. * @return pointer to type scope if known, NULL if not known */ const Scope *typeScope() const { return mType ? mType->classScope : nullptr; } /** * Get Scope pointer of enclosing scope. * @return pointer to enclosing scope */ const Scope *scope() const { return mScope; } /** * Get array dimensions. * @return array dimensions vector */ const std::vector &dimensions() const { return mDimensions; } /** * Get array dimension length. * @return length of dimension */ MathLib::bigint dimension(nonneg int index_) const { return mDimensions[index_].num; } /** * Get array dimension known. * @return length of dimension known */ bool dimensionKnown(nonneg int index_) const { return mDimensions[index_].known; } /** * Checks if the variable is an STL type ('std::') * E.g.: * std::string s; * ... * sVar->isStlType() == true * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlType() const { return getFlag(fIsStlType); } /** * Checks if the variable is an STL type ('std::') * E.g.: * std::string s; * ... * sVar->isStlType() == true * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlStringType() const { return getFlag(fIsStlString); } bool isSmartPointer() const { return getFlag(fIsSmartPointer); } const Type *smartPointerType() const; /** * Checks if the variable is of any of the STL types passed as arguments ('std::') * E.g.: * std::string s; * ... * const char *str[] = {"string", "wstring"}; * sVar->isStlType(str) == true * @param stlType stl type * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlType(const std::string& stlType) const { return isStlType() && stlType==mTypeStartToken->strAt(2); } /** * Checks if the variable is of any of the STL types passed as arguments ('std::') * E.g.: * std::string s; * ... * const std::set str = make_container< std::set >() << "string" << "wstring"; * sVar->isStlType(str) == true * @param stlTypes set of stl types * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlType(const std::set& stlTypes) const { return isStlType() && stlTypes.find(mTypeStartToken->strAt(2))!=stlTypes.end(); } /** * Determine whether it's a floating number type * @return true if the type is known and it's a floating type (float, double and long double) or a pointer/array to it */ bool isFloatingType() const { return getFlag(fIsFloatType); } /** * Determine whether it's an enumeration type * @return true if the type is known and it's an enumeration type */ bool isEnumType() const { return type() && type()->isEnumType(); } bool isMaybeUnused() const { return getFlag(fIsMaybeUnused); } const ValueType *valueType() const { return mValueType; } void setValueType(const ValueType &valueType); AccessControl accessControl() const { return mAccess; } std::string getTypeName() const; private: // only symbol database can change the type friend class SymbolDatabase; /** * Set Type pointer to known type. * @param t type */ void type(const Type * t) { mType = t; } /** @brief variable name token */ const Token *mNameToken; /** @brief variable type start token */ const Token *mTypeStartToken; /** @brief variable type end token */ const Token *mTypeEndToken; /** @brief order declared */ nonneg int mIndex; /** @brief what section is this variable declared in? */ AccessControl mAccess; // public/protected/private /** @brief flags */ unsigned int mFlags; /** @brief pointer to user defined type info (for known types) */ const Type *mType; /** @brief pointer to scope this variable is in */ const Scope *mScope; ValueType *mValueType; /** @brief array dimensions */ std::vector mDimensions; /** @brief fill in information, depending on Tokens given at instantiation */ void evaluate(const Settings* settings); }; class CPPCHECKLIB Function { // only symbol database can change this friend class SymbolDatabase; /** @brief flags mask used to access specific bit. */ enum { fHasBody = (1 << 0), ///< @brief has implementation fIsInline = (1 << 1), ///< @brief implementation in class definition fIsConst = (1 << 2), ///< @brief is const fHasVirtualSpecifier = (1 << 3), ///< @brief does declaration contain 'virtual' specifier fIsPure = (1 << 4), ///< @brief is pure virtual fIsStatic = (1 << 5), ///< @brief is static fIsStaticLocal = (1 << 6), ///< @brief is static local fIsExtern = (1 << 7), ///< @brief is extern fIsFriend = (1 << 8), ///< @brief is friend fIsExplicit = (1 << 9), ///< @brief is explicit fIsDefault = (1 << 10), ///< @brief is default fIsDelete = (1 << 11), ///< @brief is delete fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier? fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier? fIsNoExcept = (1 << 14), ///< @brief is noexcept fIsThrow = (1 << 15), ///< @brief is throw fIsOperator = (1 << 16), ///< @brief is operator fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier fIsVariadic = (1 << 19), ///< @brief is variadic fIsVolatile = (1 << 20), ///< @brief is volatile fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type fIsEscapeFunction = (1 << 22), ///< @brief Function throws or exits fIsInlineKeyword = (1 << 23), ///< @brief Function has "inline" keyword fIsConstexpr = (1 << 24), ///< @brief is constexpr }; /** * Get specified flag state. * @param flag flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(unsigned int flag) const { return ((mFlags & flag) != 0); } /** * Set specified flag state. * @param flag flag to set state * @param state new state of flag */ void setFlag(unsigned int flag, bool state) { mFlags = state ? mFlags | flag : mFlags & ~flag; } public: enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction, eLambda }; Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef); Function(const Token *tokenDef, const std::string &clangType); const std::string &name() const { return tokenDef->str(); } std::string fullName() const; nonneg int argCount() const { return argumentList.size(); } nonneg int minArgCount() const { return argumentList.size() - initArgCount; } const Variable* getArgumentVar(nonneg int num) const; nonneg int initializedArgCount() const { return initArgCount; } void addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope); /** @brief check if this function is virtual in the base classes */ bool isImplicitlyVirtual(bool defaultVal = false) const; std::vector getOverloadedFunctions() const; /** @brief get function in base class that is overridden */ const Function *getOverriddenFunction(bool *foundAllBaseClasses = nullptr) const; bool isLambda() const { return type==eLambda; } bool isConstructor() const { return type==eConstructor || type==eCopyConstructor || type==eMoveConstructor; } bool isDestructor() const { return type==eDestructor; } bool isAttributeConstructor() const { return tokenDef->isAttributeConstructor(); } bool isAttributeDestructor() const { return tokenDef->isAttributeDestructor(); } bool isAttributePure() const { return tokenDef->isAttributePure(); } bool isAttributeConst() const { return tokenDef->isAttributeConst(); } bool isAttributeNoreturn() const { return tokenDef->isAttributeNoreturn(); } bool isAttributeNothrow() const { return tokenDef->isAttributeNothrow(); } bool isAttributeNodiscard() const { return tokenDef->isAttributeNodiscard(); } bool hasBody() const { return getFlag(fHasBody); } bool isInline() const { return getFlag(fIsInline); } bool isConst() const { return getFlag(fIsConst); } bool hasVirtualSpecifier() const { return getFlag(fHasVirtualSpecifier); } bool isPure() const { return getFlag(fIsPure); } bool isStatic() const { return getFlag(fIsStatic); } bool isStaticLocal() const { return getFlag(fIsStaticLocal); } bool isExtern() const { return getFlag(fIsExtern); } bool isFriend() const { return getFlag(fIsFriend); } bool isExplicit() const { return getFlag(fIsExplicit); } bool isDefault() const { return getFlag(fIsDefault); } bool isDelete() const { return getFlag(fIsDelete); } bool isNoExcept() const { return getFlag(fIsNoExcept); } bool isThrow() const { return getFlag(fIsThrow); } bool hasOverrideSpecifier() const { return getFlag(fHasOverrideSpecifier); } bool hasFinalSpecifier() const { return getFlag(fHasFinalSpecifier); } bool isOperator() const { return getFlag(fIsOperator); } bool hasLvalRefQualifier() const { return getFlag(fHasLvalRefQual); } bool hasRvalRefQualifier() const { return getFlag(fHasRvalRefQual); } bool isVariadic() const { return getFlag(fIsVariadic); } bool isVolatile() const { return getFlag(fIsVolatile); } bool hasTrailingReturnType() const { return getFlag(fHasTrailingReturnType); } void hasBody(bool state) { setFlag(fHasBody, state); } bool isInlineKeyword() const { return getFlag(fIsInlineKeyword); } bool isEscapeFunction() const { return getFlag(fIsEscapeFunction); } void isEscapeFunction(bool state) { setFlag(fIsEscapeFunction, state); } bool isConstexpr() const { return getFlag(fIsConstexpr); } void isConstexpr(bool state) { setFlag(fIsConstexpr, state); } bool isSafe(const Settings *settings) const; const Token *tokenDef; ///< function name token in class definition const Token *argDef; ///< function argument start '(' in class definition const Token *token; ///< function name token in implementation const Token *arg; ///< function argument start '(' const Token *retDef; ///< function return type token const ::Type *retType; ///< function return type const Scope *functionScope; ///< scope of function body const Scope* nestedIn; ///< Scope the function is declared in std::list argumentList; ///< argument list nonneg int initArgCount; ///< number of args with default values Type type; ///< constructor, destructor, ... AccessControl access; ///< public/protected/private const Token *noexceptArg; ///< noexcept token const Token *throwArg; ///< throw token const Token *templateDef; ///< points to 'template <' before function const Token *functionPointerUsage; ///< function pointer usage bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const; static bool returnsConst(const Function* function, bool unknown = false); static bool returnsReference(const Function* function, bool unknown = false); static bool returnsVoid(const Function* function, bool unknown = false); static std::vector findReturns(const Function* f); const Token* returnDefEnd() const { if (this->hasTrailingReturnType()) { return Token::findmatch(retDef, "{|;"); } else { return tokenDef; } } /** * @return token to ":" if the function is a constructor * and it contains member initialization otherwise a nullptr is returned */ const Token * constructorMemberInitialization() const; private: /** Recursively determine if this function overrides a virtual function in a base class */ const Function * getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const; unsigned int mFlags; void isInline(bool state) { setFlag(fIsInline, state); } void isConst(bool state) { setFlag(fIsConst, state); } void hasVirtualSpecifier(bool state) { setFlag(fHasVirtualSpecifier, state); } void isPure(bool state) { setFlag(fIsPure, state); } void isStatic(bool state) { setFlag(fIsStatic, state); } void isStaticLocal(bool state) { setFlag(fIsStaticLocal, state); } void isExtern(bool state) { setFlag(fIsExtern, state); } void isFriend(bool state) { setFlag(fIsFriend, state); } void isExplicit(bool state) { setFlag(fIsExplicit, state); } void isDefault(bool state) { setFlag(fIsDefault, state); } void isDelete(bool state) { setFlag(fIsDelete, state); } void isNoExcept(bool state) { setFlag(fIsNoExcept, state); } void isThrow(bool state) { setFlag(fIsThrow, state); } void isOperator(bool state) { setFlag(fIsOperator, state); } void hasLvalRefQualifier(bool state) { setFlag(fHasLvalRefQual, state); } void hasRvalRefQualifier(bool state) { setFlag(fHasRvalRefQual, state); } void isVariadic(bool state) { setFlag(fIsVariadic, state); } void isVolatile(bool state) { setFlag(fIsVolatile, state); } void hasTrailingReturnType(bool state) { return setFlag(fHasTrailingReturnType, state); } void isInlineKeyword(bool state) { setFlag(fIsInlineKeyword, state); } const Token *setFlags(const Token *tok1, const Scope *scope); }; class CPPCHECKLIB Scope { // let tests access private function for testing friend class TestSymbolDatabase; public: struct UsingInfo { const Token *start; const Scope *scope; }; enum ScopeType { eGlobal, eClass, eStruct, eUnion, eNamespace, eFunction, eIf, eElse, eFor, eWhile, eDo, eSwitch, eUnconditional, eTry, eCatch, eLambda, eEnum }; Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_); Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_); const SymbolDatabase *check; std::string className; const Token *classDef; ///< class/struct/union/namespace token const Token *bodyStart; ///< '{' token const Token *bodyEnd; ///< '}' token std::list functionList; std::multimap functionMap; std::list varlist; const Scope *nestedIn; std::list nestedList; nonneg int numConstructors; nonneg int numCopyOrMoveConstructors; std::list usingList; ScopeType type; Type* definedType; std::map definedTypesMap; std::vector bodyStartList; // function specific fields const Scope *functionOf; ///< scope this function belongs to Function *function; ///< function info for this function // enum specific fields const Token * enumType; bool enumClass; std::vector enumeratorList; void setBodyStartEnd(const Token *start) { bodyStart = start; bodyEnd = start ? start->link() : nullptr; if (start) bodyStartList.push_back(start); } bool isAnonymous() const { // TODO: Check if class/struct is anonymous return className.size() > 9 && className.compare(0,9,"Anonymous") == 0 && std::isdigit(className[9]); } const Enumerator * findEnumerator(const std::string & name) const { for (const Enumerator & i : enumeratorList) { if (i.name->str() == name) return &i; } return nullptr; } bool isNestedIn(const Scope * outer) const { if (!outer) return false; if (outer == this) return true; const Scope * parent = nestedIn; while (outer != parent && parent) parent = parent->nestedIn; if (parent && parent == outer) return true; return false; } static Function* nestedInFunction(const Scope* scope) { while (scope) { if (scope->type == Scope::eFunction) break; scope = scope->nestedIn; } if (!scope) return nullptr; return scope->function; } bool isClassOrStruct() const { return (type == eClass || type == eStruct); } bool isClassOrStructOrUnion() const { return (type == eClass || type == eStruct || type == eUnion); } bool isExecutable() const { return type != eClass && type != eStruct && type != eUnion && type != eGlobal && type != eNamespace && type != eEnum; } bool isLoopScope() const { return type == Scope::ScopeType::eFor || type == Scope::ScopeType::eWhile || type == Scope::ScopeType::eDo; } bool isLocal() const { return (type == eIf || type == eElse || type == eFor || type == eWhile || type == eDo || type == eSwitch || type == eUnconditional || type == eTry || type == eCatch); } // Is there lambda/inline function(s) in this scope? bool hasInlineOrLambdaFunction() const; /** * @brief find a function * @param tok token of function call * @param requireConst if const refers to a const variable only const methods should be matched * @return pointer to function if found or NULL if not found */ const Function *findFunction(const Token *tok, bool requireConst=false) const; const Scope *findRecordInNestedList(const std::string & name) const; Scope *findRecordInNestedList(const std::string & name) { return const_cast(const_cast(this)->findRecordInNestedList(name)); } const Type* findType(const std::string& name) const; Type* findType(const std::string& name) { return const_cast(const_cast(this)->findType(name)); } /** * @brief find if name is in nested list * @param name name of nested scope */ Scope *findInNestedListRecursive(const std::string & name); void addVariable(const Token *token_, const Token *start_, const Token *end_, AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings); /** @brief initialize varlist */ void getVariableList(const Settings* settings); const Function *getDestructor() const; void addFunction(const Function & func) { functionList.push_back(func); const Function * back = &functionList.back(); functionMap.insert(make_pair(back->tokenDef->str(), back)); } bool hasDefaultConstructor() const; AccessControl defaultAccess() const; /** * @brief check if statement is variable declaration and add it if it is * @param tok pointer to start of statement * @param varaccess access control of statement * @param settings Settings * @return pointer to last token */ const Token *checkVariable(const Token *tok, AccessControl varaccess, const Settings* settings); /** * @brief get variable from name * @param varname name of variable * @return pointer to variable */ const Variable *getVariable(const std::string &varname) const; const Token * addEnum(const Token * tok, bool isCpp); const Scope *findRecordInBase(const std::string &name) const; std::vector findAssociatedScopes() const; private: /** * @brief helper function for getVariableList() * @param tok pointer to token to check * @param vartok populated with pointer to the variable token, if found * @param typetok populated with pointer to the type token, if found * @return true if tok points to a variable declaration, false otherwise */ bool isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const; void findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const; /** @brief initialize varlist */ void getVariableList(const Settings* settings, const Token *start, const Token *end); }; enum class Reference { None, LValue, RValue }; /** Value type */ class CPPCHECKLIB ValueType { public: enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign; enum Type { UNKNOWN_TYPE, NONSTD, RECORD, SMART_POINTER, CONTAINER, ITERATOR, VOID, BOOL, CHAR, SHORT, WCHAR_T, INT, LONG, LONGLONG, UNKNOWN_INT, FLOAT, DOUBLE, LONGDOUBLE } type; nonneg int bits; ///< bitfield bitcount nonneg int pointer; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc nonneg int constness; ///< bit 0=data, bit 1=*, bit 2=** Reference reference = Reference::None; ///< Is the outermost indirection of this type a reference or rvalue ///< reference or not? pointer=2, Reference=LValue would be a T**& const Scope* typeScope; ///< if the type definition is seen this point out the type scope const ::Type* smartPointerType; ///< Smart pointer type const Token* smartPointerTypeToken; ///< Smart pointer type token const Library::SmartPointer* smartPointer; ///< Smart pointer const Library::Container* container; ///< If the type is a container defined in a cfg file, this is the used ///< container const Token* containerTypeToken; ///< The container type token. the template argument token that defines the ///< container element type. std::string originalTypeName; ///< original type name as written in the source code. eg. this might be "uint8_t" ///< when type is CHAR. ValueType() : sign(UNKNOWN_SIGN), type(UNKNOWN_TYPE), bits(0), pointer(0U), constness(0U), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), smartPointer(nullptr), container(nullptr), containerTypeToken(nullptr) {} ValueType(enum Sign s, enum Type t, nonneg int p) : sign(s), type(t), bits(0), pointer(p), constness(0U), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), smartPointer(nullptr), container(nullptr), containerTypeToken(nullptr) {} ValueType(enum Sign s, enum Type t, nonneg int p, nonneg int c) : sign(s), type(t), bits(0), pointer(p), constness(c), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), smartPointer(nullptr), container(nullptr), containerTypeToken(nullptr) {} ValueType(enum Sign s, enum Type t, nonneg int p, nonneg int c, const std::string& otn) : sign(s), type(t), bits(0), pointer(p), constness(c), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), smartPointer(nullptr), container(nullptr), containerTypeToken(nullptr), originalTypeName(otn) {} static ValueType parseDecl(const Token *type, const Settings *settings); static Type typeFromString(const std::string &typestr, bool longType); enum class MatchResult { UNKNOWN, SAME, FALLBACK1, FALLBACK2, NOMATCH }; static MatchResult matchParameter(const ValueType *call, const ValueType *func); static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar); bool isPrimitive() const { return (type >= ValueType::Type::BOOL); } bool isIntegral() const { return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT); } bool isFloat() const { return (type >= ValueType::Type::FLOAT && type <= ValueType::Type::LONGDOUBLE); } bool fromLibraryType(const std::string &typestr, const Settings *settings); bool isEnum() const { return typeScope && typeScope->type == Scope::eEnum; } MathLib::bigint typeSize(const cppcheck::Platform &platform, bool p=false) const; std::string str() const; std::string dump() const; }; class CPPCHECKLIB SymbolDatabase { friend class TestSymbolDatabase; public: SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger); ~SymbolDatabase(); /** @brief Information about all namespaces/classes/structrues */ std::list scopeList; /** @brief Fast access to function scopes */ std::vector functionScopes; /** @brief Fast access to class and struct scopes */ std::vector classAndStructScopes; /** @brief Fast access to types */ std::list typeList; /** * @brief find a variable type if it's a user defined type * @param start scope to start looking in * @param typeTok token containing variable type * @return pointer to type if found or NULL if not found */ const Type *findVariableType(const Scope *start, const Token *typeTok) const; /** * @brief find a function * @param tok token of function call * @return pointer to function if found or NULL if not found */ const Function *findFunction(const Token *tok) const; /** For unit testing only */ const Scope *findScopeByName(const std::string& name) const; const Type* findType(const Token *startTok, const Scope *startScope) const; Type* findType(const Token *startTok, Scope *startScope) const { return const_cast(this->findType(startTok, const_cast(startScope))); } const Scope *findScope(const Token *tok, const Scope *startScope) const; Scope *findScope(const Token *tok, Scope *startScope) const { return const_cast(this->findScope(tok, const_cast(startScope))); } bool isVarId(nonneg int varid) const { return varid < mVariableList.size(); } const Variable *getVariableFromVarId(nonneg int varId) const { return mVariableList.at(varId); } const std::vector & variableList() const { return mVariableList; } /** * @brief output a debug message */ void debugMessage(const Token *tok, const std::string &type, const std::string &msg) const; void printOut(const char * title = nullptr) const; void printVariable(const Variable *var, const char *indent) const; void printXml(std::ostream &out) const; bool isCPP() const; /* * @brief Do a sanity check */ void validate() const; void validateExecutableScopes() const; /** * @brief Check variable list, e.g. variables w/o scope */ void validateVariables() const; /** Set valuetype in provided tokenlist */ void setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens=nullptr); /** * Calculates sizeof value for given type. * @param type Token which will contain e.g. "int", "*", or string. * @return sizeof for given type, or 0 if it can't be calculated. */ nonneg int sizeOfType(const Token *type) const; /** Set array dimensions when valueflow analysis is completed */ void setArrayDimensionsUsingValueFlow(); void clangSetVariables(const std::vector &variableList); void createSymbolDatabaseExprIds(); private: friend class Scope; friend class Function; // Create symboldatabase... void createSymbolDatabaseFindAllScopes(); void createSymbolDatabaseClassInfo(); void createSymbolDatabaseVariableInfo(); void createSymbolDatabaseCopyAndMoveConstructors(); void createSymbolDatabaseFunctionScopes(); void createSymbolDatabaseClassAndStructScopes(); void createSymbolDatabaseFunctionReturnTypes(); void createSymbolDatabaseNeedInitialization(); void createSymbolDatabaseVariableSymbolTable(); void createSymbolDatabaseSetScopePointers(); void createSymbolDatabaseSetFunctionPointers(bool firstPass); void createSymbolDatabaseSetVariablePointers(); void createSymbolDatabaseSetTypePointers(); void createSymbolDatabaseSetSmartPointerType(); void createSymbolDatabaseEnums(); void createSymbolDatabaseEscapeFunctions(); void createSymbolDatabaseIncompleteVars(); void addClassFunction(Scope **scope, const Token **tok, const Token *argStart); Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart); Function *addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart); void addNewFunction(Scope **scope, const Token **tok); bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart, const Token** declEnd) const; const Type *findTypeInNested(const Token *startTok, const Scope *startScope) const; const Scope *findNamespace(const Token * tok, const Scope * scope) const; Function *findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length); const Type *findVariableTypeInBase(const Scope *scope, const Token *typeTok) const; typedef std::map MemberIdMap; typedef std::map VarIdMap; void fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar); /** Whether iName is a keyword as defined in http://en.cppreference.com/w/c/keyword and http://en.cppreference.com/w/cpp/keyword*/ bool isReservedName(const std::string& iName) const; const Enumerator * findEnumerator(const Token * tok) const; void setValueType(Token *tok, const ValueType &valuetype); void setValueType(Token *tok, const Variable &var); void setValueType(Token *tok, const Enumerator &enumerator); const Tokenizer *mTokenizer; const Settings *mSettings; ErrorLogger *mErrorLogger; /** variable symbol table */ std::vector mVariableList; /** list for missing types */ std::list mBlankTypes; bool mIsCpp; ValueType::Sign mDefaultSignedness; /** "negative cache" list of tokens that we find are not enumeration values */ mutable std::set mTokensThatAreNotEnumeratorValues; }; //--------------------------------------------------------------------------- #endif // symboldatabaseH cppcheck-2.7/lib/templatesimplifier.cpp000066400000000000000000005061701417746362400203420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "templatesimplifier.h" #include "errorlogger.h" #include "errortypes.h" #include "mathlib.h" #include "settings.h" #include "standards.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include #include #include #include static Token *skipRequires(Token *tok) { if (!Token::simpleMatch(tok, "requires")) return tok; while (Token::Match(tok, "%oror%|&&|requires %name%|(")) { Token *after = tok->next(); if (after->str() == "(") { tok = after->link()->next(); continue; } if (Token::simpleMatch(after, "requires (") && Token::simpleMatch(after->linkAt(1), ") {")) { tok = after->linkAt(1)->linkAt(1)->next(); continue; } while (Token::Match(after, "%name% :: %name%")) after = after->tokAt(2); if (Token::Match(after, "%name% <")) { after = after->next()->findClosingBracket(); tok = after ? after->next() : nullptr; } else break; } return tok; } namespace { class FindToken { public: explicit FindToken(const Token *token) : mToken(token) {} bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { return tokenAndName.token() == mToken; } private: const Token * const mToken; }; class FindName { public: explicit FindName(const std::string &name) : mName(name) {} bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { return tokenAndName.name() == mName; } private: const std::string mName; }; class FindFullName { public: explicit FindFullName(const std::string &fullName) : mFullName(fullName) {} bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { return tokenAndName.fullName() == mFullName; } private: const std::string mFullName; }; } TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &scope) : mToken(token), mScope(scope), mName(mToken ? mToken->str() : ""), mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)), mNameToken(nullptr), mParamEnd(nullptr), mFlags(0) { if (mToken) { if (mToken->strAt(1) == "<") { const Token *end = mToken->next()->findClosingBracket(); if (end && end->strAt(1) == "(") { isFunction(true); } } mToken->templateSimplifierPointer(this); } } TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &scope, const Token *nameToken, const Token *paramEnd) : mToken(token), mScope(scope), mName(nameToken->str()), mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)), mNameToken(nameToken), mParamEnd(paramEnd), mFlags(0) { // only set flags for declaration if (mToken && mNameToken && mParamEnd) { isSpecialization(Token::simpleMatch(mToken, "template < >")); if (!isSpecialization()) { if (Token::simpleMatch(mToken->next()->findClosingBracket(), "> template <")) { const Token * temp = mNameToken->tokAt(-2); while (Token::Match(temp, ">|%name% ::")) { if (temp->str() == ">") temp = temp->findOpeningBracket()->previous(); else temp = temp->tokAt(-2); } isPartialSpecialization(temp->strAt(1) == "<"); } else isPartialSpecialization(mNameToken->strAt(1) == "<"); } isAlias(mParamEnd->strAt(1) == "using"); if (isAlias() && isPartialSpecialization()) { throw InternalError(mToken, "partial specialization of alias templates is not permitted", InternalError::SYNTAX); } if (isAlias() && isSpecialization()) { throw InternalError(mToken, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX); } isFriend(mParamEnd->strAt(1) == "friend"); const Token *next = mParamEnd->next(); if (isFriend()) next = next->next(); isClass(Token::Match(next, "class|struct|union %name% <|{|:|;|::")); if (mToken->strAt(1) == "<" && !isSpecialization()) { const Token *end = mToken->next()->findClosingBracket(); isVariadic(end && Token::findmatch(mToken->tokAt(2), "%name% ...", end)); } const Token *tok1 = mNameToken->next(); if (tok1->str() == "<") { const Token *closing = tok1->findClosingBracket(); if (closing) tok1 = closing->next(); else throw InternalError(mToken, "unsupported syntax", InternalError::SYNTAX); } isFunction(tok1->str() == "("); isVariable(!isClass() && !isAlias() && !isFriend() && Token::Match(tok1, "=|;")); if (!isFriend()) { if (isVariable()) isForwardDeclaration(tok1->str() == ";"); else if (!isAlias()) { if (isFunction()) tok1 = tok1->link()->next(); while (tok1 && !Token::Match(tok1, ";|{")) { if (tok1->str() == "<") tok1 = tok1->findClosingBracket(); else if (Token::Match(tok1, "(|[") && tok1->link()) tok1 = tok1->link(); if (tok1) tok1 = tok1->next(); } if (tok1) isForwardDeclaration(tok1->str() == ";"); } } // check for member class or function and adjust scope if ((isFunction() || isClass()) && (mNameToken->strAt(-1) == "::" || Token::simpleMatch(mNameToken->tokAt(-2), ":: ~"))) { const Token * start = mNameToken; if (start->strAt(-1) == "~") start = start->previous(); const Token *end = start; while (start && (Token::Match(start->tokAt(-2), "%name% ::") || (Token::simpleMatch(start->tokAt(-2), "> ::") && start->tokAt(-2)->findOpeningBracket() && Token::Match(start->tokAt(-2)->findOpeningBracket()->previous(), "%name% <")))) { if (start->strAt(-2) == ">") start = start->tokAt(-2)->findOpeningBracket()->previous(); else start = start->tokAt(-2); } if (start && start != end) { if (!mScope.empty()) mScope += " ::"; while (start && start->next() != end) { if (start->str() == "<") start = start->findClosingBracket(); else { if (!mScope.empty()) mScope += " "; mScope += start->str(); } start = start->next(); } if (start) mFullName = mScope.empty() ? mName : (mScope + " :: " + mName); } } } // make sure at most only one family flag is set assert(isClass() ? !(isFunction() || isVariable()) : true); assert(isFunction() ? !(isClass() || isVariable()) : true); assert(isVariable() ? !(isClass() || isFunction()) : true); if (mToken) mToken->templateSimplifierPointer(this); } TemplateSimplifier::TokenAndName::TokenAndName(const TokenAndName& other) : mToken(other.mToken), mScope(other.mScope), mName(other.mName), mFullName(other.mFullName), mNameToken(other.mNameToken), mParamEnd(other.mParamEnd), mFlags(other.mFlags) { if (mToken) mToken->templateSimplifierPointer(this); } TemplateSimplifier::TokenAndName::~TokenAndName() { if (mToken && mToken->templateSimplifierPointers()) mToken->templateSimplifierPointers()->erase(this); } const Token * TemplateSimplifier::TokenAndName::aliasStartToken() const { if (mParamEnd) return mParamEnd->tokAt(4); return nullptr; } const Token * TemplateSimplifier::TokenAndName::aliasEndToken() const { if (aliasStartToken()) return Token::findsimplematch(aliasStartToken(), ";"); return nullptr; } bool TemplateSimplifier::TokenAndName::isAliasToken(const Token *tok) const { const Token *end = aliasEndToken(); for (const Token *tok1 = aliasStartToken(); tok1 != end; tok1 = tok1->next()) { if (tok1 == tok) return true; } return false; } TemplateSimplifier::TemplateSimplifier(Tokenizer *tokenizer) : mTokenizer(tokenizer), mTokenList(tokenizer->list), mSettings(tokenizer->mSettings), mErrorLogger(tokenizer->mErrorLogger), mChanged(false) {} TemplateSimplifier::~TemplateSimplifier() {} void TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates() { // check for more complicated syntax errors when using templates.. for (const Token *tok = mTokenList.front(); tok; tok = tok->next()) { // skip executing scopes (ticket #3183).. if (Token::simpleMatch(tok, "( {")) { tok = tok->link(); if (!tok) syntaxError(nullptr); } // skip executing scopes.. const Token *start = Tokenizer::startOfExecutableScope(tok); if (start) { tok = start->link(); } // skip executing scopes (ticket #1985).. else if (Token::simpleMatch(tok, "try {")) { tok = tok->next()->link(); while (Token::simpleMatch(tok, "} catch (")) { tok = tok->linkAt(2); if (Token::simpleMatch(tok, ") {")) tok = tok->next()->link(); } } if (!tok) syntaxError(nullptr); // not start of statement? if (tok->previous() && !Token::Match(tok, "[;{}]")) continue; // skip starting tokens.. ;;; typedef typename foo::bar::.. while (Token::Match(tok, ";|{")) tok = tok->next(); while (Token::Match(tok, "typedef|typename")) tok = tok->next(); while (Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); if (!tok) break; // template variable or type.. if (Token::Match(tok, "%type% <") && !Token::simpleMatch(tok, "template")) { // these are used types.. std::set usedtypes; // parse this statement and see if the '<' and '>' are matching unsigned int level = 0; for (const Token *tok2 = tok; tok2 && !Token::simpleMatch(tok2, ";"); tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "{") && (!Token::Match(tok2->previous(), ">|%type%") || Token::simpleMatch(tok2->link(), "} ;"))) break; if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == "<") { bool inclevel = false; if (Token::simpleMatch(tok2->previous(), "operator <")) ; else if (level == 0 && Token::Match(tok2->previous(), "%type%")) { // @todo add better expression detection if (!(Token::Match(tok2->next(), "*| %type%|%num% ;") || Token::Match(tok2->next(), "*| %type% . %type% ;"))) { inclevel = true; } } else if (tok2->next() && tok2->next()->isStandardType() && !Token::Match(tok2->tokAt(2), "(|{")) inclevel = true; else if (Token::simpleMatch(tok2, "< typename")) inclevel = true; else if (Token::Match(tok2->tokAt(-2), "<|, %type% <") && usedtypes.find(tok2->previous()->str()) != usedtypes.end()) inclevel = true; else if (Token::Match(tok2, "< %type%") && usedtypes.find(tok2->next()->str()) != usedtypes.end()) inclevel = true; else if (Token::Match(tok2, "< %type%")) { // is the next token a type and not a variable/constant? // assume it's a type if there comes another "<" const Token *tok3 = tok2->next(); while (Token::Match(tok3, "%type% ::")) tok3 = tok3->tokAt(2); if (Token::Match(tok3, "%type% <")) inclevel = true; } else if (tok2->strAt(-1) == ">") syntaxError(tok); if (inclevel) { ++level; if (Token::Match(tok2->tokAt(-2), "<|, %type% <")) usedtypes.insert(tok2->previous()->str()); } } else if (tok2->str() == ">") { if (level > 0) --level; } else if (tok2->str() == ">>") { if (level > 0) --level; if (level > 0) --level; } } if (level > 0) syntaxError(tok); } } } unsigned int TemplateSimplifier::templateParameters(const Token *tok) { unsigned int numberOfParameters = 1; if (!tok) return 0; if (tok->str() != "<") return 0; if (Token::Match(tok->previous(), "%var% <")) return 0; tok = tok->next(); if (!tok || tok->str() == ">") return 0; unsigned int level = 0; while (tok) { // skip template template if (level == 0 && Token::simpleMatch(tok, "template <")) { const Token *closing = tok->next()->findClosingBracket(); if (closing) { if (closing->str() == ">>") return numberOfParameters; tok = closing->next(); if (Token::Match(tok, ">|>>|>>=")) return numberOfParameters; else if (tok->str() == ",") { ++numberOfParameters; tok = tok->next(); continue; } } else return 0; } // skip const/volatile if (Token::Match(tok, "const|volatile")) tok = tok->next(); // skip struct/union if (Token::Match(tok, "struct|union")) tok = tok->next(); // Skip '&' if (Token::Match(tok, "& ::| %name%")) tok = tok->next(); // Skip variadic types (Ticket #5774, #6059, #6172) if (Token::simpleMatch(tok, "...")) { if ((tok->previous()->isName() && !Token::Match(tok->tokAt(-2), "<|,|::")) || (!tok->previous()->isName() && tok->strAt(-1) != ">")) return 0; // syntax error tok = tok->next(); if (!tok) return 0; if (tok->str() == ">") { if (level == 0) return numberOfParameters; --level; } else if (tok->str() == ">>" || tok->str() == ">>=") { if (level == 1) return numberOfParameters; level -= 2; } else if (tok->str() == "," && level == 0) { ++numberOfParameters; tok = tok->next(); continue; } } // Skip '=', '?', ':' if (Token::Match(tok, "=|?|:")) tok = tok->next(); if (!tok) return 0; // Skip links if (Token::Match(tok, "(|{")) { tok = tok->link(); if (tok) tok = tok->next(); if (!tok) return 0; if (tok->str() == ">" && level == 0) return numberOfParameters; else if ((tok->str() == ">>" || tok->str() == ">>=") && level == 1) return numberOfParameters; else if (tok->str() == ",") { if (level == 0) ++numberOfParameters; tok = tok->next(); } continue; } // skip std:: if (tok->str() == "::") tok = tok->next(); while (Token::Match(tok, "%name% ::")) { tok = tok->tokAt(2); if (tok && tok->str() == "*") // Ticket #5759: Class member pointer as a template argument; skip '*' tok = tok->next(); } if (!tok) return 0; // num/type .. if (!tok->isNumber() && tok->tokType() != Token::eChar && !tok->isName() && !tok->isOp()) return 0; tok = tok->next(); if (!tok) return 0; // * / const while (Token::Match(tok, "*|&|&&|const")) tok = tok->next(); if (!tok) return 0; // Function pointer or prototype.. while (Token::Match(tok, "(|[")) { if (!tok->link()) syntaxError(tok); tok = tok->link()->next(); while (Token::Match(tok, "const|volatile")) // Ticket #5786: Skip function cv-qualifiers tok = tok->next(); } if (!tok) return 0; // inner template if (tok->str() == "<") { ++level; tok = tok->next(); } if (!tok) return 0; // ,/> while (Token::Match(tok, ">|>>|>>=")) { if (level == 0) return tok->str() == ">" && !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0; --level; if (tok->str() == ">>" || tok->str() == ">>=") { if (level == 0) return !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0; --level; } tok = tok->next(); if (Token::Match(tok, "(|[")) tok = tok->link()->next(); if (!tok) return 0; } if (tok->str() != ",") continue; if (level == 0) ++numberOfParameters; tok = tok->next(); } return 0; } const Token *TemplateSimplifier::findTemplateDeclarationEnd(const Token *tok) { return const_cast(findTemplateDeclarationEnd(const_cast(tok))); } Token *TemplateSimplifier::findTemplateDeclarationEnd(Token *tok) { if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (tok) tok = tok->next(); } if (!tok) return nullptr; Token * tok2 = tok; bool in_init = false; while (tok2 && !Token::Match(tok2, ";|{")) { if (tok2->str() == "<") tok2 = tok2->findClosingBracket(); else if (Token::Match(tok2, "(|[") && tok2->link()) tok2 = tok2->link(); else if (tok2->str() == ":") in_init = true; else if (in_init && Token::Match(tok2, "%name% (|{")) { tok2 = tok2->linkAt(1); if (tok2->strAt(1) == "{") in_init = false; } if (tok2) tok2 = tok2->next(); } if (tok2 && tok2->str() == "{") { tok = tok2->link(); if (tok && tok->strAt(1) == ";") tok = tok->next(); } else if (tok2 && tok2->str() == ";") tok = tok2; else tok = nullptr; return tok; } void TemplateSimplifier::eraseTokens(Token *begin, const Token *end) { if (!begin || begin == end) return; while (begin->next() && begin->next() != end) { begin->deleteNext(); } } void TemplateSimplifier::deleteToken(Token *tok) { if (tok->next()) tok->next()->deletePrevious(); else tok->deleteThis(); } bool TemplateSimplifier::removeTemplate(Token *tok) { if (!Token::simpleMatch(tok, "template <")) return false; Token *end = findTemplateDeclarationEnd(tok); if (end && end->next()) { eraseTokens(tok, end->next()); deleteToken(tok); return true; } return false; } bool TemplateSimplifier::getTemplateDeclarations() { bool codeWithTemplates = false; for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "template <")) continue; // ignore template template parameter if (tok->strAt(-1) == "<" || tok->strAt(-1) == ",") continue; // ignore nested template if (tok->strAt(-1) == ">") continue; // skip to last nested template parameter const Token *tok1 = tok; while (tok1 && tok1->next()) { const Token *closing = tok1->next()->findClosingBracket(); if (!Token::simpleMatch(closing, "> template <")) break; tok1 = closing->next(); } if (!Token::Match(tok, "%any% %any%")) syntaxError(tok); if (tok->strAt(2)=="typename" && !Token::Match(tok->tokAt(3), "%name%|...|,|=|>")) syntaxError(tok->next()); codeWithTemplates = true; const Token * const parmEnd = tok1->next()->findClosingBracket(); for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) { if (tok2->str() == "(" && tok2->link()) tok2 = tok2->link(); else if (tok2->str() == ")") break; // skip decltype(...) else if (Token::simpleMatch(tok2, "decltype (")) tok2 = tok2->linkAt(1); else if (Token::Match(tok2, "{|=|;")) { const int namepos = getTemplateNamePosition(parmEnd); if (namepos > 0) { TokenAndName decl(tok, tok->scopeInfo()->name, parmEnd->tokAt(namepos), parmEnd); if (decl.isForwardDeclaration()) { // Declaration => add to mTemplateForwardDeclarations mTemplateForwardDeclarations.emplace_back(std::move(decl)); } else { // Implementation => add to mTemplateDeclarations mTemplateDeclarations.emplace_back(std::move(decl)); } Token *end = findTemplateDeclarationEnd(tok); if (end) tok = end; break; } } } } return codeWithTemplates; } void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope) { simplifyTemplateArgs(token->tokAt(2), token->next()->findClosingBracket()); TokenAndName instantiation(token, scope); // check if instantiation already exists before adding it std::list::iterator it = std::find(mTemplateInstantiations.begin(), mTemplateInstantiations.end(), instantiation); if (it == mTemplateInstantiations.end()) mTemplateInstantiations.emplace_back(instantiation); } static void getFunctionArguments(const Token *nameToken, std::vector &args) { const Token *argToken; if (nameToken->strAt(1) == "(") argToken = nameToken->tokAt(2); else if (nameToken->strAt(1) == "<") { const Token *end = nameToken->next()->findClosingBracket(); if (end) argToken = end->tokAt(2); else return; } else return; if (argToken->str() == ")") return; args.push_back(argToken); while ((argToken = argToken->nextArgumentBeforeCreateLinks2())) args.push_back(argToken); } static bool areAllParamsTypes(const std::vector ¶ms) { if (params.empty()) return false; for (const auto *param : params) { if (!Token::Match(param->previous(), "typename|class %name% ,|>")) return false; } return true; } void TemplateSimplifier::getTemplateInstantiations() { std::multimap functionNameMap; for (const auto & decl : mTemplateDeclarations) { if (decl.isFunction()) functionNameMap.insert(std::make_pair(decl.name(), &decl)); } for (const auto & decl : mTemplateForwardDeclarations) { if (decl.isFunction()) functionNameMap.insert(std::make_pair(decl.name(), &decl)); } const Token *skip = nullptr; for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { // template definition.. skip it if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (!tok) break; const bool isUsing = tok->strAt(1) == "using"; if (isUsing && Token::Match(tok->tokAt(2), "%name% <")) { // Can't have specialized type alias so ignore it Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";"); if (tok2) tok = tok2; } else if (tok->strAt(-1) == "<") { // Don't ignore user specialization but don't consider it an instantiation. // Instantiations in return type, function parameters, and executable code // are not ignored. unsigned int pos = getTemplateNamePosition(tok); if (pos > 0) skip = tok->tokAt(pos); } else { // #7914 // Ignore template instantiations within template definitions: they will only be // handled if the definition is actually instantiated Token * tok2 = findTemplateDeclarationEnd(tok->next()); if (tok2) tok = tok2; } } else if (Token::Match(tok, "template using %name% <")) { // Can't have specialized type alias so ignore it Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";"); if (tok2) tok = tok2; } else if (Token::Match(tok, "using %name% <")) { // Can't have specialized type alias so ignore it Token *tok2 = Token::findsimplematch(tok->tokAt(2), ";"); if (tok2) tok = tok2; } else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|&|return|<|,|!|[ %name% ::|<|(") || Token::Match(tok->previous(), "%type% %name% ::|<") || Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) { std::string scopeName = tok->scopeInfo()->name; std::string qualification; Token * qualificationTok = tok; while (Token::Match(tok, "%name% :: %name%")) { qualification += (qualification.empty() ? "" : " :: ") + tok->str(); tok = tok->tokAt(2); } // skip specialization if (tok == skip) { skip = nullptr; continue; } // look for function instantiation with type deduction if (tok->strAt(1) == "(") { std::vector instantiationArgs; getFunctionArguments(tok, instantiationArgs); std::string fullName; if (!qualification.empty()) fullName = qualification + " :: " + tok->str(); else if (!scopeName.empty()) fullName = scopeName + " :: " + tok->str(); else fullName = tok->str(); // get all declarations with this name for (auto pos = functionNameMap.lower_bound(tok->str()); pos != functionNameMap.upper_bound(tok->str()); ++pos) { // look for declaration with same qualification or constructor with same qualification if (pos->second->fullName() == fullName || (pos->second->scope() == fullName && tok->str() == pos->second->name())) { std::vector templateParams; getTemplateParametersInDeclaration(pos->second->token()->tokAt(2), templateParams); // todo: handle more than one template parameter if (templateParams.size() != 1 || !areAllParamsTypes(templateParams)) continue; std::vector declarationParams; getFunctionArguments(pos->second->nameToken(), declarationParams); // function argument counts must match if (instantiationArgs.empty() || instantiationArgs.size() != declarationParams.size()) continue; size_t match = 0; size_t argMatch = 0; for (size_t i = 0; i < declarationParams.size(); ++i) { // fixme: only type deducton from literals is supported bool isArgLiteral = Token::Match(instantiationArgs[i], "%num%|%str%|%char%|%bool% ,|)"); if (isArgLiteral && Token::Match(declarationParams[i], "const| %type% &| %name%| ,|)")) { match++; // check if parameter types match if (templateParams[0]->str() == declarationParams[i]->str()) argMatch = i; else { // todo: check if non-template args match for function overloads } } } if (match == declarationParams.size()) { const Token *arg = instantiationArgs[argMatch]; tok->insertToken(">"); switch (arg->tokType()) { case Token::eBoolean: tok->insertToken("bool"); break; case Token::eChar: if (arg->isLong()) tok->insertToken("wchar_t"); else tok->insertToken("char"); break; case Token::eString: tok->insertToken("*"); if (arg->isLong()) tok->insertToken("wchar_t"); else tok->insertToken("char"); tok->insertToken("const"); break; case Token::eNumber: { MathLib::value num(arg->str()); if (num.isFloat()) { // MathLib::getSuffix doesn't work for floating point numbers char suffix = arg->str().back(); if (suffix == 'f' || suffix == 'F') tok->insertToken("float"); else if (suffix == 'l' || suffix == 'L') { tok->insertToken("double"); tok->next()->isLong(true); } else tok->insertToken("double"); } else if (num.isInt()) { std::string suffix = MathLib::getSuffix(tok->strAt(3)); if (suffix.find("LL") != std::string::npos) { tok->insertToken("long"); tok->next()->isLong(true); } else if (suffix.find('L') != std::string::npos) tok->insertToken("long"); else tok->insertToken("int"); if (suffix.find('U') != std::string::npos) tok->next()->isUnsigned(true); } break; } default: break; } tok->insertToken("<"); break; } } } } if (!Token::Match(tok, "%name% <") || Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast")) continue; if (tok == skip) { skip = nullptr; continue; } // Add inner template instantiations first => go to the ">" // and then parse backwards, adding all seen instantiations Token *tok2 = tok->next()->findClosingBracket(); // parse backwards and add template instantiations // TODO for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { if (Token::Match(tok2, ",|< %name% <") && (tok2->strAt(3) == ">" || templateParameters(tok2->tokAt(2)))) { addInstantiation(tok2->next(), tok->scopeInfo()->name); } else if (Token::Match(tok2->next(), "class|struct")) tok2->deleteNext(); } // Add outer template.. if (templateParameters(tok->next()) || tok->strAt(2) == ">") { const std::string scopeName1(scopeName); while (true) { const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") + qualification + (qualification.empty()?"":" :: ") + tok->str(); const std::list::const_iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindFullName(fullName)); if (it != mTemplateDeclarations.end()) { // full name matches addInstantiation(tok, it->scope()); break; } else { // full name doesn't match so try with using namespaces if available bool found = false; for (const auto & nameSpace : tok->scopeInfo()->usingNamespaces) { std::string fullNameSpace = scopeName + (scopeName.empty()?"":" :: ") + nameSpace + (qualification.empty()?"":" :: ") + qualification; std::string newFullName = fullNameSpace + " :: " + tok->str(); const std::list::const_iterator it1 = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindFullName(newFullName)); if (it1 != mTemplateDeclarations.end()) { // insert using namespace into token stream std::string::size_type offset = 0; std::string::size_type pos = 0; while ((pos = nameSpace.substr(offset).find(' ')) != std::string::npos) { qualificationTok->insertToken(nameSpace.substr(offset, pos), "", true); offset = offset + pos + 1; } qualificationTok->insertToken(nameSpace.substr(offset), "", true); qualificationTok->insertToken("::", "", true); addInstantiation(tok, it1->scope()); found = true; break; } } if (found) break; if (scopeName.empty()) { if (!qualification.empty()) addInstantiation(tok, qualification); else addInstantiation(tok, tok->scopeInfo()->name); break; } const std::string::size_type pos = scopeName.rfind(" :: "); scopeName = (pos == std::string::npos) ? std::string() : scopeName.substr(0,pos); } } } } } } void TemplateSimplifier::useDefaultArgumentValues() { for (TokenAndName &declaration : mTemplateDeclarations) useDefaultArgumentValues(declaration); for (TokenAndName &declaration : mTemplateForwardDeclarations) useDefaultArgumentValues(declaration); } void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration) { // Ticket #5762: Skip specialization tokens if (declaration.isSpecialization() || declaration.isAlias() || declaration.isFriend()) return; // template parameters with default value has syntax such as: // x = y // this list will contain all the '=' tokens for such arguments struct Default { Token *eq; Token *end; }; std::list eq; // and this set the position of parameters with a default value std::set defaultedArgPos; // parameter number. 1,2,3,.. std::size_t templatepar = 1; // parameter depth std::size_t templateParmDepth = 0; // map type parameter name to index std::map typeParameterNames; // Scan template declaration.. for (Token *tok = declaration.token()->next(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "template <")) { Token* end = tok->next()->findClosingBracket(); if (end) tok = end; continue; } if (tok->link() && Token::Match(tok, "{|(|[")) { // Ticket #6835 tok = tok->link(); continue; } if (tok->str() == "<" && (tok->strAt(1) == ">" || (tok->previous()->isName() && typeParameterNames.find(tok->strAt(-1)) == typeParameterNames.end()))) ++templateParmDepth; // end of template parameters? if (tok->str() == ">") { if (templateParmDepth<2) { if (!eq.empty()) eq.back().end = tok; break; } else --templateParmDepth; } // map type parameter name to index if (Token::Match(tok, "typename|class|%type% %name% ,|>")) typeParameterNames[tok->strAt(1)] = templatepar - 1; // next template parameter if (tok->str() == "," && (1 == templateParmDepth)) { // Ticket #5823: Properly count parameters if (!eq.empty()) eq.back().end = tok; ++templatepar; } // default parameter value? else if (Token::Match(tok, "= !!>")) { if (defaultedArgPos.insert(templatepar).second) { eq.push_back(Default{tok, nullptr}); } else { // Ticket #5605: Syntax error (two equal signs for the same parameter), bail out eq.clear(); break; } } } if (eq.empty()) return; // iterate through all template instantiations for (const TokenAndName &instantiation : mTemplateInstantiations) { if (declaration.fullName() != instantiation.fullName()) continue; // instantiation arguments.. std::vector> instantiationArgs; std::size_t index = 0; const Token *end = instantiation.token()->next()->findClosingBracket(); if (!end) continue; if (end != instantiation.token()->tokAt(2)) instantiationArgs.resize(1); for (const Token *tok1 = instantiation.token()->tokAt(2); tok1 && tok1 != end; tok1 = tok1->next()) { if (tok1->link() && Token::Match(tok1, "{|(|[")) { const Token *endLink = tok1->link(); do { instantiationArgs[index].push_back(tok1); tok1 = tok1->next(); } while (tok1 && tok1 != endLink); instantiationArgs[index].push_back(tok1); } else if (tok1->str() == "<" && (tok1->strAt(1) == ">" || (tok1->previous()->isName() && typeParameterNames.find(tok1->strAt(-1)) == typeParameterNames.end()))) { const Token *endLink = tok1->findClosingBracket(); do { instantiationArgs[index].push_back(tok1); tok1 = tok1->next(); } while (tok1 && tok1 != endLink); instantiationArgs[index].push_back(tok1); } else if (tok1->str() == ",") { ++index; instantiationArgs.resize(index + 1); } else instantiationArgs[index].push_back(tok1); } // count the parameters.. Token *tok = instantiation.token()->next(); unsigned int usedpar = templateParameters(tok); Token *instantiationEnd = tok->findClosingBracket(); tok = instantiationEnd; if (tok && tok->str() == ">") { tok = tok->previous(); std::list::const_iterator it = eq.begin(); for (std::size_t i = (templatepar - eq.size()); it != eq.end() && i < usedpar; ++i) ++it; int count = 0; while (it != eq.end()) { // check for end if (!it->end) { if (mSettings->debugwarnings) { const std::list locationList(1, it->eq); const ErrorMessage errmsg(locationList, &mTokenizer->list, Severity::debug, "noparamend", "TemplateSimplifier couldn't find end of template parameter.", Certainty::normal); } break; } if ((usedpar + count) && usedpar <= (instantiationArgs.size() + count)) { tok->insertToken(","); tok = tok->next(); } std::stack links; for (const Token* from = it->eq->next(); from && from != it->end; from = from->next()) { auto entry = typeParameterNames.find(from->str()); if (entry != typeParameterNames.end() && entry->second < instantiationArgs.size()) { for (const Token *tok1 : instantiationArgs[entry->second]) { tok->insertToken(tok1->str(), tok1->originalName()); tok = tok->next(); if (Token::Match(tok, "(|[|{")) links.push(tok); else if (!links.empty() && Token::Match(tok, ")|]|}")) { Token::createMutualLinks(links.top(), tok); links.pop(); } } } else { tok->insertToken(from->str(), from->originalName()); tok = tok->next(); if (Token::Match(tok, "(|[|{")) links.push(tok); else if (!links.empty() && Token::Match(tok, ")|]|}")) { Token::createMutualLinks(links.top(), tok); links.pop(); } } } ++it; count++; usedpar++; } } simplifyTemplateArgs(instantiation.token()->next(), instantiationEnd); } for (const auto & entry : eq) { Token *const eqtok = entry.eq; Token *tok2; int indentlevel = 0; for (tok2 = eqtok->next(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, ";|)|}|]")) { // bail out #6607 tok2 = nullptr; break; } if (Token::Match(tok2, "(|{|[")) tok2 = tok2->link(); else if (Token::Match(tok2, "%type% <") && (tok2->strAt(2) == ">" || templateParameters(tok2->next()))) { std::list::iterator ti = std::find_if(mTemplateInstantiations.begin(), mTemplateInstantiations.end(), FindToken(tok2)); if (ti != mTemplateInstantiations.end()) mTemplateInstantiations.erase(ti); ++indentlevel; } else if (indentlevel > 0 && tok2->str() == ">") --indentlevel; else if (indentlevel == 0 && Token::Match(tok2, ",|>")) break; if (indentlevel < 0) break; } // something went wrong, don't call eraseTokens() // with a nullptr "end" parameter (=all remaining tokens). if (!tok2) continue; // don't strip args from uninstantiated templates std::list::iterator ti2 = std::find_if(mTemplateInstantiations.begin(), mTemplateInstantiations.end(), FindName(declaration.name())); if (ti2 == mTemplateInstantiations.end()) continue; eraseTokens(eqtok, tok2); eqtok->deleteThis(); // update parameter end pointer declaration.paramEnd(declaration.token()->next()->findClosingBracket()); } } void TemplateSimplifier::simplifyTemplateAliases() { for (std::list::iterator it1 = mTemplateDeclarations.begin(); it1 != mTemplateDeclarations.end();) { TokenAndName &aliasDeclaration = *it1; if (!aliasDeclaration.isAlias()) { ++it1; continue; } // alias parameters.. std::vector aliasParameters; getTemplateParametersInDeclaration(aliasDeclaration.token()->tokAt(2), aliasParameters); std::map aliasParameterNames; for (unsigned int argnr = 0; argnr < aliasParameters.size(); ++argnr) aliasParameterNames[aliasParameters[argnr]->str()] = argnr; // Look for alias usages.. bool found = false; for (std::list::iterator it2 = mTemplateInstantiations.begin(); it2 != mTemplateInstantiations.end();) { TokenAndName &aliasUsage = *it2; if (!aliasUsage.token() || aliasUsage.fullName() != aliasDeclaration.fullName()) { ++it2; continue; } // don't recurse if (aliasDeclaration.isAliasToken(aliasUsage.token())) { ++it2; continue; } std::vector> args; Token *tok2 = aliasUsage.token()->tokAt(2); while (tok2) { Token * const start = tok2; while (tok2 && !Token::Match(tok2, "[,>;{}]")) { if (tok2->link() && Token::Match(tok2, "(|[")) tok2 = tok2->link(); else if (tok2->str() == "<") { tok2 = tok2->findClosingBracket(); if (!tok2) break; } tok2 = tok2->next(); } args.emplace_back(start, tok2); if (tok2 && tok2->str() == ",") { tok2 = tok2->next(); } else { break; } } if (!tok2 || tok2->str() != ">" || (!aliasDeclaration.isVariadic() && (args.size() != aliasParameters.size())) || (aliasDeclaration.isVariadic() && (args.size() < aliasParameters.size()))) { ++it2; continue; } mChanged = true; // copy template-id from declaration to after instantiation Token * dst = aliasUsage.token()->next()->findClosingBracket(); Token * end = TokenList::copyTokens(dst, aliasDeclaration.aliasStartToken(), aliasDeclaration.aliasEndToken()->previous(), false)->next(); // replace parameters for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { if (!tok1->isName()) continue; if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) { const unsigned int argnr = aliasParameterNames[tok1->str()]; const Token * const fromStart = args[argnr].first; const Token * const fromEnd = args[argnr].second->previous(); Token *temp = TokenList::copyTokens(tok1, fromStart, fromEnd, true); const bool tempOK(temp && temp != tok1->next()); tok1->deleteThis(); if (tempOK) tok1 = temp; // skip over inserted parameters } else if (tok1->str() == "typename") tok1->deleteThis(); } // add new instantiations for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { if (!tok1->isName()) continue; if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) { // Create template instance.. if (Token::Match(tok1, "%name% <")) { const std::list::iterator it = std::find_if(mTemplateInstantiations.begin(), mTemplateInstantiations.end(), FindToken(tok1)); if (it != mTemplateInstantiations.end()) addInstantiation(tok2, it->scope()); } } } // erase the instantiation tokens eraseTokens(aliasUsage.token()->previous(), dst->next()); found = true; // erase this instantiation it2 = mTemplateInstantiations.erase(it2); } if (found) { Token *end = const_cast(aliasDeclaration.aliasEndToken()); // remove declaration tokens if (aliasDeclaration.token()->previous()) eraseTokens(aliasDeclaration.token()->previous(), end->next() ? end->next() : end); else { eraseTokens(mTokenList.front(), end->next() ? end->next() : end); deleteToken(mTokenList.front()); } // remove declaration it1 = mTemplateDeclarations.erase(it1); } else ++it1; } } bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, bool variadic, const char patternAfter[]) { assert(instance->strAt(1) == "<"); auto n = templateParameters(instance->next()); if (variadic ? (n + 1 < numberOfArguments) : (numberOfArguments != n)) return false; if (patternAfter) { const Token *tok = instance->next()->findClosingBracket(); if (!tok || !Token::Match(tok->next(), patternAfter)) return false; } // nothing mismatching was found.. return true; } // Utility function for TemplateSimplifier::getTemplateNamePosition, that works on template functions bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos) { namepos = 1; while (tok && tok->next()) { if (Token::Match(tok->next(), ";|{")) return false; // skip decltype(...) else if (Token::simpleMatch(tok->next(), "decltype (")) { const Token * end = tok->linkAt(2)->previous(); while (tok->next() && tok != end) { tok = tok->next(); namepos++; } } else if (Token::Match(tok->next(), "%type% <")) { const Token *closing = tok->tokAt(2)->findClosingBracket(); if (closing) { if (closing->strAt(1) == "(" && Tokenizer::isFunctionHead(closing->next(), ";|{|:", true)) return true; while (tok->next() && tok->next() != closing) { tok = tok->next(); namepos++; } } } else if (Token::Match(tok->next(), "%type% (") && Tokenizer::isFunctionHead(tok->tokAt(2), ";|{|:", true)) { return true; } tok = tok->next(); namepos++; } return false; } bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos) { namepos = 1; while (tok && tok->next()) { if (Token::Match(tok->next(), ";|{|(|using")) return false; // skip decltype(...) else if (Token::simpleMatch(tok->next(), "decltype (")) { const Token * end = tok->linkAt(2); while (tok->next() && tok != end) { tok = tok->next(); namepos++; } } else if (Token::Match(tok->next(), "%type% <")) { const Token *closing = tok->tokAt(2)->findClosingBracket(); if (closing) { if (Token::Match(closing->next(), "=|;")) return true; while (tok->next() && tok->next() != closing) { tok = tok->next(); namepos++; } } } else if (Token::Match(tok->next(), "%type% =|;")) { return true; } tok = tok->next(); namepos++; } return false; } bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos) { if (Token::Match(tok, "> friend| class|struct|union %type% :|<|;|{|::")) { namepos = tok->strAt(1) == "friend" ? 3 : 2; tok = tok->tokAt(namepos); while (Token::Match(tok, "%type% :: %type%") || (Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) { if (tok->strAt(1) == "::") { tok = tok->tokAt(2); namepos += 2; } else { const Token *end = tok->next()->findClosingBracket(); if (!end || !end->tokAt(2)) { // syntax error namepos = -1; return true; } end = end->tokAt(2); do { tok = tok->next(); namepos += 1; } while (tok && tok != end); } } return true; } return false; } int TemplateSimplifier::getTemplateNamePosition(const Token *tok) { assert(tok && tok->str() == ">"); auto it = mTemplateNamePos.find(tok); if (!mSettings->debugtemplate && it != mTemplateNamePos.end()) { return it->second; } // get the position of the template name int namepos = 0; if (getTemplateNamePositionTemplateClass(tok, namepos)) ; else if (Token::Match(tok, "> using %name% =")) { // types may not be defined in alias template declarations if (!Token::Match(tok->tokAt(4), "class|struct|union|enum %name%| {")) namepos = 2; } else if (getTemplateNamePositionTemplateVariable(tok, namepos)) ; else if (!getTemplateNamePositionTemplateFunction(tok, namepos)) namepos = -1; // Name not found mTemplateNamePos[tok] = namepos; return namepos; } void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, const Token *tok) { // find start of qualification const Token * tokStart = tok; int offset = 0; while (Token::Match(tokStart->tokAt(-2), "%name% ::")) { tokStart = tokStart->tokAt(-2); offset -= 2; } // decide if namespace needs to be inserted in or appended to token list const bool insert = tokStart != tok; std::string::size_type start = 0; std::string::size_type end = 0; bool inTemplate = false; int level = 0; while ((end = templateDeclaration.scope().find(" ", start)) != std::string::npos) { std::string token = templateDeclaration.scope().substr(start, end - start); // done if scopes overlap if (token == tokStart->str() && tok->strAt(-1) != "::") break; if (token == "<") { inTemplate = true; ++level; } if (inTemplate) { if (insert) mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + token); else mTokenList.back()->str(mTokenList.back()->str() + token); if (token == ">") { --level; if (level == 0) inTemplate = false; } } else { if (insert) mTokenList.back()->tokAt(offset)->insertToken(token, ""); else mTokenList.addtoken(token, tok->linenr(), tok->column(), tok->fileIndex()); } start = end + 1; } // don't add if it already exists std::string token = templateDeclaration.scope().substr(start, end - start); if (token != tokStart->str() || tok->strAt(-1) != "::") { if (insert) { if (!inTemplate) mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope().substr(start), ""); else mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + templateDeclaration.scope().substr(start)); mTokenList.back()->tokAt(offset)->insertToken("::", ""); } else { if (!inTemplate) mTokenList.addtoken(templateDeclaration.scope().substr(start), tok->linenr(), tok->column(), tok->fileIndex()); else mTokenList.back()->str(mTokenList.back()->str() + templateDeclaration.scope().substr(start)); mTokenList.addtoken("::", tok->linenr(), tok->column(), tok->fileIndex()); } } } bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) { const std::string& scope = templateDeclaration.scope(); // get the length in tokens of the namespace std::string::size_type pos = 0; int offset = -2; while ((pos = scope.find("::", pos)) != std::string::npos) { offset -= 2; pos += 2; } return Token::simpleMatch(tok->tokAt(offset), scope.c_str(), scope.size()); } void TemplateSimplifier::expandTemplate( const TokenAndName &templateDeclaration, const TokenAndName &templateInstantiation, const std::vector &typeParametersInDeclaration, const std::string &newName, bool copy) { bool inTemplateDefinition = false; const Token *startOfTemplateDeclaration = nullptr; const Token *endOfTemplateDefinition = nullptr; const Token * const templateDeclarationNameToken = templateDeclaration.nameToken(); const Token * const templateDeclarationToken = templateDeclaration.paramEnd(); const bool isClass = templateDeclaration.isClass(); const bool isFunction = templateDeclaration.isFunction(); const bool isSpecialization = templateDeclaration.isSpecialization(); const bool isVariable = templateDeclaration.isVariable(); struct newInstantiation { newInstantiation(Token *t, const std::string &s) : token(t), scope(s) {} Token *token; std::string scope; }; std::vector newInstantiations; // add forward declarations if (copy && isClass) { templateDeclaration.token()->insertToken(templateDeclarationToken->strAt(1), "", true); templateDeclaration.token()->insertToken(newName, "", true); templateDeclaration.token()->insertToken(";", "", true); } else if ((isFunction && (copy || isSpecialization)) || (isVariable && !isSpecialization) || (isClass && isSpecialization && mTemplateSpecializationMap.find(templateDeclaration.token()) != mTemplateSpecializationMap.end())) { Token * dst = templateDeclaration.token(); Token * dstStart = dst->previous(); bool isStatic = false; std::string scope; Token * start; Token * end; auto it = mTemplateForwardDeclarationsMap.find(dst); if (!isSpecialization && it != mTemplateForwardDeclarationsMap.end()) { dst = it->second; dstStart = dst->previous(); const Token * temp1 = dst->tokAt(1)->findClosingBracket(); const Token * temp2 = temp1->tokAt(getTemplateNamePosition(temp1)); start = temp1->next(); end = temp2->linkAt(1)->next(); } else { if (it != mTemplateForwardDeclarationsMap.end()) { std::list::iterator it1 = std::find_if(mTemplateForwardDeclarations.begin(), mTemplateForwardDeclarations.end(), FindToken(it->second)); if (it1 != mTemplateForwardDeclarations.end()) mMemberFunctionsToDelete.push_back(*it1); } auto it2 = mTemplateSpecializationMap.find(dst); if (it2 != mTemplateSpecializationMap.end()) { dst = it2->second; dstStart = dst->previous(); isStatic = dst->next()->findClosingBracket()->strAt(1) == "static"; const Token * temp = templateDeclarationNameToken; while (Token::Match(temp->tokAt(-2), "%name% ::")) { scope.insert(0, temp->strAt(-2) + " :: "); temp = temp->tokAt(-2); } } start = templateDeclarationToken->next(); end = templateDeclarationNameToken->next(); if (end->str() == "<") end = end->findClosingBracket()->next(); if (end->str() == "(") end = end->link()->next(); else if (isVariable && end->str() == "=") { Token *temp = end->next(); while (temp && temp->str() != ";") { if (temp->link() && Token::Match(temp, "{|[|(")) temp = temp->link(); temp = temp->next(); } end = temp; } } unsigned int typeindentlevel = 0; while (end && !(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) { if (Token::Match(end, "<|(|{")) ++typeindentlevel; else if (Token::Match(end, ">|)|}")) --typeindentlevel; end = end->next(); } if (isStatic) { dst->insertToken("static", "", true); if (start) { dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); } } std::map links; bool inAssignment = false; while (start && start != end) { if (isVariable && start->str() == "=") inAssignment = true; unsigned int itype = 0; while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != start->str()) ++itype; if (itype < typeParametersInDeclaration.size() && itype < mTypesUsedInTemplateInstantiation.size() && (!isVariable || !Token::Match(typeParametersInDeclaration[itype]->previous(), "<|, %type% >|,"))) { typeindentlevel = 0; std::stack brackets1; // holds "(" and "{" tokens bool pointerType = false; Token * const dst1 = dst->previous(); const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size(); if (isVariadicTemplateArg && Token::Match(start, "%name% ... %name%")) start = start->tokAt(2); const std::string endStr(isVariadicTemplateArg ? ">" : ",>"); for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); typetok && (typeindentlevel > 0 || endStr.find(typetok->str()[0]) == std::string::npos); typetok = typetok->next()) { if (typeindentlevel == 0 && typetok->str() == "*") pointerType = true; if (Token::simpleMatch(typetok, "...")) continue; if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; else if (typetok->str() == "(") ++typeindentlevel; else if (typetok->str() == ")") --typeindentlevel; dst->insertToken(typetok->str(), typetok->originalName(), true); dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); Token *previous = dst->previous(); previous->isTemplateArg(true); previous->isSigned(typetok->isSigned()); previous->isUnsigned(typetok->isUnsigned()); previous->isLong(typetok->isLong()); if (Token::Match(previous, "{|(|[")) { brackets1.push(previous); } else if (previous->str() == "}") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "{"); Token::createMutualLinks(brackets1.top(), previous); brackets1.pop(); } else if (previous->str() == ")") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "("); Token::createMutualLinks(brackets1.top(), previous); brackets1.pop(); } else if (previous->str() == "]") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "["); Token::createMutualLinks(brackets1.top(), previous); brackets1.pop(); } } if (pointerType && Token::simpleMatch(dst1, "const")) { dst->insertToken("const", dst1->originalName(), true); dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); dst1->deleteThis(); } } else { if (isSpecialization && !copy && !scope.empty() && Token::Match(start, (scope + templateDeclarationNameToken->str()).c_str())) { // skip scope while (start->strAt(1) != templateDeclarationNameToken->str()) start = start->next(); } else if (start->str() == templateDeclarationNameToken->str() && !(templateDeclaration.isFunction() && templateDeclaration.scope().empty() && (start->strAt(-1) == "." || Token::simpleMatch(start->tokAt(-2), ". template")))) { if (start->strAt(1) != "<" || Token::Match(start, newName.c_str()) || !inAssignment) { dst->insertToken(newName, "", true); dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); if (start->strAt(1) == "<") start = start->next()->findClosingBracket(); } else { dst->insertToken(start->str(), "", true); dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); newInstantiations.emplace_back(dst->previous(), templateDeclaration.scope()); } } else { // check if type is a template if (start->strAt(1) == "<") { // get the instantiated name Token * closing = start->next()->findClosingBracket(); if (closing) { std::string name; const Token * type = start; while (type && type != closing->next()) { if (!name.empty()) name += " "; name += type->str(); type = type->next(); } // check if type is instantiated for (const auto & inst : mTemplateInstantiations) { if (Token::simpleMatch(inst.token(), name.c_str(), name.size())) { // use the instantiated name dst->insertToken(name, "", true); dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); start = closing; break; } } } // just copy the token if it wasn't instantiated if (start != closing) { dst->insertToken(start->str(), start->originalName(), true); dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); dst->previous()->isSigned(start->isSigned()); dst->previous()->isUnsigned(start->isUnsigned()); dst->previous()->isLong(start->isLong()); } } else { dst->insertToken(start->str(), start->originalName(), true); dst->previous()->linenr(start->linenr()); dst->previous()->column(start->column()); dst->previous()->isSigned(start->isSigned()); dst->previous()->isUnsigned(start->isUnsigned()); dst->previous()->isLong(start->isLong()); } } if (!start) continue; if (start->link()) { if (Token::Match(start, "[|{|(")) { links[start->link()] = dst->previous(); } else if (Token::Match(start, "]|}|)")) { std::map::iterator link = links.find(start); // make sure link is valid if (link != links.end()) { Token::createMutualLinks(link->second, dst->previous()); links.erase(start); } } } } start = start->next(); } dst->insertToken(";", "", true); dst->previous()->linenr(dst->tokAt(-2)->linenr()); dst->previous()->column(dst->tokAt(-2)->column() + 1); if (isVariable || isFunction) simplifyTemplateArgs(dstStart, dst); } if (copy && (isClass || isFunction)) { // check if this is an explicit instantiation Token * start = templateInstantiation.token(); while (start && !Token::Match(start->previous(), "}|;|extern")) start = start->previous(); if (Token::Match(start, "template !!<")) { if (start->strAt(-1) == "extern") start = start->previous(); mExplicitInstantiationsToDelete.emplace_back(start, ""); } } for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { if (inTemplateDefinition) { if (!endOfTemplateDefinition) { if (isVariable) { Token *temp = tok3->findClosingBracket(); if (temp) { while (temp && temp->str() != ";") { if (temp->link() && Token::Match(temp, "{|[|(")) temp = temp->link(); temp = temp->next(); } endOfTemplateDefinition = temp; } } else if (tok3->str() == "{") endOfTemplateDefinition = tok3->link(); } if (tok3 == endOfTemplateDefinition) { inTemplateDefinition = false; startOfTemplateDeclaration = nullptr; } } if (tok3->str()=="template") { if (tok3->next() && tok3->next()->str()=="<") { std::vector localTypeParametersInDeclaration; getTemplateParametersInDeclaration(tok3->tokAt(2), localTypeParametersInDeclaration); if (localTypeParametersInDeclaration.size() != typeParametersInDeclaration.size()) inTemplateDefinition = false; // Partial specialization else inTemplateDefinition = true; } else { inTemplateDefinition = false; // Only template instantiation } startOfTemplateDeclaration = tok3; } if (Token::Match(tok3, "(|[")) tok3 = tok3->link(); // Start of template.. if (tok3 == templateDeclarationToken) { tok3 = tok3->next(); if (tok3->str() == "static") tok3 = tok3->next(); } // member function implemented outside class definition else if (inTemplateDefinition && Token::Match(tok3, "%name% <") && templateInstantiation.name() == tok3->str() && instantiateMatch(tok3, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), ":: ~| %name% (")) { // there must be template.. bool istemplate = false; Token * tok5 = nullptr; // start of function return type for (Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { if (prev->str() == "template") { istemplate = true; tok5 = prev; break; } } if (!istemplate) continue; const Token *tok4 = tok3->next()->findClosingBracket(); while (tok4 && tok4->str() != "(") tok4 = tok4->next(); if (!Tokenizer::isFunctionHead(tok4, ":{", true)) continue; // find function return type start tok5 = tok5->next()->findClosingBracket(); if (tok5) tok5 = tok5->next(); // copy return type std::stack brackets2; // holds "(" and "{" tokens while (tok5 && tok5 != tok3) { // replace name if found if (Token::Match(tok5, "%name% <") && tok5->str() == templateInstantiation.name()) { if (copy) { if (!templateDeclaration.scope().empty() && tok5->strAt(-1) != "::") addNamespace(templateDeclaration, tok5); mTokenList.addtoken(newName, tok5->linenr(), tok5->column(), tok5->fileIndex()); tok5 = tok5->next()->findClosingBracket(); } else { tok5->str(newName); eraseTokens(tok5, tok5->next()->findClosingBracket()->next()); } } else if (copy) { bool added = false; if (tok5->isName() && !Token::Match(tok5, "class|typename|struct") && !tok5->isStandardType()) { // search for this token in the type vector unsigned int itype = 0; while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok5->str()) ++itype; // replace type with given type.. if (itype < typeParametersInDeclaration.size() && itype < mTypesUsedInTemplateInstantiation.size()) { unsigned int typeindentlevel = 0; std::stack brackets1; // holds "(" and "{" tokens for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>")); typetok = typetok->next()) { if (!Token::simpleMatch(typetok, "...")) { if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; else if (typetok->str() == "(") ++typeindentlevel; else if (typetok->str() == ")") --typeindentlevel; mTokenList.addtoken(typetok, tok5); Token *back = mTokenList.back(); if (Token::Match(back, "{|(|[")) { brackets1.push(back); } else if (back->str() == "}") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "{"); Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == ")") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "("); Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == "]") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "["); Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } back->isTemplateArg(true); back->isUnsigned(typetok->isUnsigned()); back->isSigned(typetok->isSigned()); back->isLong(typetok->isLong()); added = true; break; } } } } if (!added) { mTokenList.addtoken(tok5); Token *back = mTokenList.back(); if (Token::Match(back, "{|(|[")) { brackets2.push(back); } else if (back->str() == "}") { assert(brackets2.empty() == false); assert(brackets2.top()->str() == "{"); Token::createMutualLinks(brackets2.top(), back); brackets2.pop(); } else if (back->str() == ")") { assert(brackets2.empty() == false); assert(brackets2.top()->str() == "("); Token::createMutualLinks(brackets2.top(), back); brackets2.pop(); } else if (back->str() == "]") { assert(brackets2.empty() == false); assert(brackets2.top()->str() == "["); Token::createMutualLinks(brackets2.top(), back); brackets2.pop(); } } } tok5 = tok5->next(); } if (copy) { if (!templateDeclaration.scope().empty() && tok3->strAt(-1) != "::") addNamespace(templateDeclaration, tok3); mTokenList.addtoken(newName, tok3->linenr(), tok3->column(), tok3->fileIndex()); } while (tok3 && tok3->str() != "::") tok3 = tok3->next(); std::list::iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindToken(startOfTemplateDeclaration)); if (it != mTemplateDeclarations.end()) mMemberFunctionsToDelete.push_back(*it); } // not part of template.. go on to next token else continue; std::stack brackets; // holds "(", "[" and "{" tokens // FIXME use full name matching somehow const std::string lastName = (templateInstantiation.name().find(' ') != std::string::npos) ? templateInstantiation.name().substr(templateInstantiation.name().rfind(' ')+1) : templateInstantiation.name(); std::stack templates; for (; tok3; tok3 = tok3->next()) { if (tok3->isName() && !Token::Match(tok3, "class|typename|struct") && !tok3->isStandardType()) { // search for this token in the type vector unsigned int itype = 0; while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok3->str()) ++itype; // replace type with given type.. if (itype < typeParametersInDeclaration.size() && itype < mTypesUsedInTemplateInstantiation.size()) { unsigned int typeindentlevel = 0; std::stack brackets1; // holds "(" and "{" tokens Token * const beforeTypeToken = mTokenList.back(); bool pointerType = false; const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size(); if (isVariadicTemplateArg && Token::Match(tok3, "%name% ... %name%")) tok3 = tok3->tokAt(2); const std::string endStr(isVariadicTemplateArg ? ">" : ",>"); for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); typetok && (typeindentlevel > 0 || endStr.find(typetok->str()[0]) == std::string::npos); typetok = typetok->next()) { if (typeindentlevel == 0 && typetok->str() == "*") pointerType = true; if (Token::simpleMatch(typetok, "...")) continue; if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) { brackets1.push(typetok->next()); ++typeindentlevel; } else if (typeindentlevel > 0 && typetok->str() == ">" && brackets1.top()->str() == "<") { --typeindentlevel; brackets1.pop(); } else if (Token::Match(typetok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) { brackets1.push(typetok->next()); ++typeindentlevel; } else if (typetok->str() == "(") ++typeindentlevel; else if (typetok->str() == ")") --typeindentlevel; Token *back; if (copy) { mTokenList.addtoken(typetok, tok3); back = mTokenList.back(); } else back = const_cast(typetok); if (Token::Match(back, "{|(|[")) brackets1.push(back); else if (back->str() == "}") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "{"); if (copy) Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == ")") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "("); if (copy) Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == "]") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "["); if (copy) Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } if (copy) back->isTemplateArg(true); } if (pointerType && Token::simpleMatch(beforeTypeToken, "const")) { mTokenList.addtoken(beforeTypeToken); beforeTypeToken->deleteThis(); } continue; } } // replace name.. if (tok3->str() == lastName) { if (Token::simpleMatch(tok3->next(), "<")) { Token *closingBracket = tok3->next()->findClosingBracket(); if (closingBracket) { // replace multi token name with single token name if (tok3 == templateDeclarationNameToken || Token::Match(tok3, newName.c_str())) { if (copy) { mTokenList.addtoken(newName, tok3); tok3 = closingBracket; } else { tok3->str(newName); eraseTokens(tok3, closingBracket->next()); } continue; } else if (!templateDeclaration.scope().empty() && !alreadyHasNamespace(templateDeclaration, tok3) && !Token::Match(closingBracket->next(), "(|::")) { if (copy) addNamespace(templateDeclaration, tok3); } } } else { // don't modify friend if (Token::Match(tok3->tokAt(-3), "> friend class|struct|union")) { if (copy) mTokenList.addtoken(tok3); } else if (copy) { // add namespace if necessary if (!templateDeclaration.scope().empty() && (isClass ? tok3->strAt(1) != "(" : true)) { addNamespace(templateDeclaration, tok3); } mTokenList.addtoken(newName, tok3); } else if (!Token::Match(tok3->next(), ":|{|=|;|[")) tok3->str(newName); continue; } } // copy if (copy) mTokenList.addtoken(tok3); // look for template definitions if (Token::simpleMatch(tok3, "template <")) { Token * tok2 = findTemplateDeclarationEnd(tok3); if (tok2) templates.push(tok2); } else if (!templates.empty() && templates.top() == tok3) templates.pop(); if (Token::Match(tok3, "%type% <") && !Token::Match(tok3, "template|static_cast|const_cast|reinterpret_cast|dynamic_cast") && Token::Match(tok3->next()->findClosingBracket(), ">|>>")) { const Token *closingBracket = tok3->next()->findClosingBracket(); if (Token::simpleMatch(closingBracket->next(), "&")) { int num = 0; const Token *par = tok3->next(); while (num < typeParametersInDeclaration.size() && par != closingBracket) { const std::string pattern("[<,] " + typeParametersInDeclaration[num]->str() + " [,>]"); if (!Token::Match(par, pattern.c_str())) break; ++num; par = par->tokAt(2); } if (num < typeParametersInDeclaration.size() || par != closingBracket) continue; } std::string scope; const Token *prev = tok3; for (; Token::Match(prev->tokAt(-2), "%name% ::"); prev = prev->tokAt(-2)) { if (scope.empty()) scope = prev->strAt(-2); else scope = prev->strAt(-2) + " :: " + scope; } // check for global scope if (prev->strAt(-1) != "::") { // adjust for current scope std::string token_scope = tok3->scopeInfo()->name; std::string::size_type end = token_scope.find_last_of(" :: "); if (end != std::string::npos) { token_scope.resize(end); if (scope.empty()) scope = token_scope; else scope = token_scope + " :: " + scope; } } // don't add instantiations in template definitions if (templates.empty()) { if (copy) newInstantiations.emplace_back(mTokenList.back(), scope); else if (!inTemplateDefinition) newInstantiations.emplace_back(tok3, scope); } } // link() newly tokens manually else if (copy) { if (tok3->str() == "{") { brackets.push(mTokenList.back()); } else if (tok3->str() == "(") { brackets.push(mTokenList.back()); } else if (tok3->str() == "[") { brackets.push(mTokenList.back()); } else if (tok3->str() == "}") { assert(brackets.empty() == false); assert(brackets.top()->str() == "{"); Token::createMutualLinks(brackets.top(), mTokenList.back()); if (tok3->strAt(1) == ";") { const Token * tokSemicolon = tok3->next(); mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->column(), tokSemicolon->fileIndex()); } brackets.pop(); if (brackets.empty() && !Token::Match(tok3, "} >|,|{")) { inTemplateDefinition = false; break; } } else if (tok3->str() == ")") { assert(brackets.empty() == false); assert(brackets.top()->str() == "("); Token::createMutualLinks(brackets.top(), mTokenList.back()); brackets.pop(); } else if (tok3->str() == "]") { assert(brackets.empty() == false); assert(brackets.top()->str() == "["); Token::createMutualLinks(brackets.top(), mTokenList.back()); brackets.pop(); } } } assert(brackets.empty()); } // add new instantiations for (const auto & inst : newInstantiations) { simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket()); // only add recursive instantiation if its arguments are a constant expression if (templateDeclaration.name() != inst.token->str() || (inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType())) mTemplateInstantiations.emplace_back(inst.token, inst.scope); } } static bool isLowerThanLogicalAnd(const Token *lower) { return lower->isAssignmentOp() || Token::Match(lower, "}|;|(|[|]|)|,|?|:|%oror%|return|throw|case"); } static bool isLowerThanOr(const Token* lower) { return isLowerThanLogicalAnd(lower) || lower->str() == "&&"; } static bool isLowerThanXor(const Token* lower) { return isLowerThanOr(lower) || lower->str() == "|"; } static bool isLowerThanAnd(const Token* lower) { return isLowerThanXor(lower) || lower->str() == "^"; } static bool isLowerThanShift(const Token* lower) { return isLowerThanAnd(lower) || lower->str() == "&"; } static bool isLowerThanPlusMinus(const Token* lower) { return isLowerThanShift(lower) || Token::Match(lower, "%comp%|<<|>>"); } static bool isLowerThanMulDiv(const Token* lower) { return isLowerThanPlusMinus(lower) || Token::Match(lower, "+|-"); } static bool isLowerEqualThanMulDiv(const Token* lower) { return isLowerThanMulDiv(lower) || Token::Match(lower, "[*/%]"); } bool TemplateSimplifier::simplifyNumericCalculations(Token *tok, bool isTemplate) { bool ret = false; // (1-2) while (tok->tokAt(3) && tok->isNumber() && tok->tokAt(2)->isNumber()) { // %any% %num% %any% %num% %any% const Token *before = tok->previous(); if (!before) break; const Token* op = tok->next(); const Token* after = tok->tokAt(3); const std::string &num1 = op->previous()->str(); const std::string &num2 = op->next()->str(); if (Token::Match(before, "* %num% /") && (num2 != "0") && num1 == MathLib::multiply(num2, MathLib::divide(num1, num2))) { // Division where result is a whole number } else if (!((op->str() == "*" && (isLowerThanMulDiv(before) || before->str() == "*") && isLowerEqualThanMulDiv(after)) || // associative (Token::Match(op, "[/%]") && isLowerThanMulDiv(before) && isLowerEqualThanMulDiv(after)) || // NOT associative (Token::Match(op, "[+-]") && isLowerThanMulDiv(before) && isLowerThanMulDiv(after)) || // Only partially (+) associative, but handled later (Token::Match(op, ">>|<<") && isLowerThanShift(before) && isLowerThanPlusMinus(after)) || // NOT associative (op->str() == "&" && isLowerThanShift(before) && isLowerThanShift(after)) || // associative (op->str() == "^" && isLowerThanAnd(before) && isLowerThanAnd(after)) || // associative (op->str() == "|" && isLowerThanXor(before) && isLowerThanXor(after)) || // associative (op->str() == "&&" && isLowerThanOr(before) && isLowerThanOr(after)) || (op->str() == "||" && isLowerThanLogicalAnd(before) && isLowerThanLogicalAnd(after)))) break; // Don't simplify "%num% / 0" if (Token::Match(op, "[/%] 0")) { if (isTemplate) throw InternalError(op, "Instantiation error: Divide by zero in template instantiation.", InternalError::INSTANTIATION); else return ret; } // Integer operations if (Token::Match(op, ">>|<<|&|^|%or%")) { // Don't simplify if operand is negative, shifting with negative // operand is UB. Bitmasking with negative operand is implementation // defined behaviour. if (MathLib::isNegative(num1) || MathLib::isNegative(num2)) break; const MathLib::value v1(num1); const MathLib::value v2(num2); if (!v1.isInt() || !v2.isInt()) break; switch (op->str()[0]) { case '<': tok->str((v1 << v2).str()); break; case '>': tok->str((v1 >> v2).str()); break; case '&': tok->str((v1 & v2).str()); break; case '|': tok->str((v1 | v2).str()); break; case '^': tok->str((v1 ^ v2).str()); break; } } // Logical operations else if (Token::Match(op, "%oror%|&&")) { const bool op1 = !MathLib::isNullValue(num1); const bool op2 = !MathLib::isNullValue(num2); const bool result = (op->str() == "||") ? (op1 || op2) : (op1 && op2); tok->str(result ? "1" : "0"); } else if (Token::Match(tok->previous(), "- %num% - %num%")) tok->str(MathLib::add(num1, num2)); else if (Token::Match(tok->previous(), "- %num% + %num%")) tok->str(MathLib::subtract(num1, num2)); else { try { tok->str(MathLib::calculate(num1, num2, op->str()[0])); } catch (InternalError &e) { e.token = tok; throw; } } tok->deleteNext(2); ret = true; } return ret; } static Token *skipTernaryOp(Token *tok, const Token *backToken) { unsigned int colonLevel = 1; while (nullptr != (tok = tok->next())) { if (tok->str() == "?") { ++colonLevel; } else if (tok->str() == ":") { --colonLevel; if (colonLevel == 0) { tok = tok->next(); break; } } if (tok->link() && tok->str() == "(") tok = tok->link(); else if (Token::Match(tok->next(), "[{};)]") || tok->next() == backToken) break; } if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?' return nullptr; return tok; } void TemplateSimplifier::simplifyTemplateArgs(Token *start, Token *end) { // start could be erased so use the token before start if available Token * first = (start && start->previous()) ? start->previous() : mTokenList.front(); bool again = true; while (again) { again = false; for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { if (tok->str() == "sizeof") { // sizeof('x') if (Token::Match(tok->next(), "( %char% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream sz; sz << 1; tok->str(sz.str()); again = true; } // sizeof ("text") else if (Token::Match(tok->next(), "( %str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream ostr; ostr << (Token::getStrLength(tok) + 1); tok->str(ostr.str()); again = true; } else if (Token::Match(tok->next(), "( %type% * )")) { tok->str(MathLib::toString(mTokenizer->sizeOfType(tok->tokAt(3)))); tok->deleteNext(4); again = true; } else if (Token::simpleMatch(tok->next(), "( * )")) { tok->str(MathLib::toString(mTokenizer->sizeOfType(tok->tokAt(2)))); tok->deleteNext(3); again = true; } else if (Token::Match(tok->next(), "( %type% )")) { const unsigned int size = mTokenizer->sizeOfType(tok->tokAt(2)); if (size > 0) { tok->str(MathLib::toString(size)); tok->deleteNext(3); again = true; } } else if (tok->strAt(1) == "(") { tok = tok->linkAt(1); } } else if (Token::Match(tok, "%num% %comp% %num%") && MathLib::isInt(tok->str()) && MathLib::isInt(tok->strAt(2))) { if ((Token::Match(tok->previous(), "(|&&|%oror%|,") || tok == start) && (Token::Match(tok->tokAt(3), ")|&&|%oror%|?") || tok->tokAt(3) == end)) { const MathLib::bigint op1(MathLib::toLongNumber(tok->str())); const std::string &cmp(tok->next()->str()); const MathLib::bigint op2(MathLib::toLongNumber(tok->strAt(2))); std::string result; if (cmp == "==") result = (op1 == op2) ? "true" : "false"; else if (cmp == "!=") result = (op1 != op2) ? "true" : "false"; else if (cmp == "<=") result = (op1 <= op2) ? "true" : "false"; else if (cmp == ">=") result = (op1 >= op2) ? "true" : "false"; else if (cmp == "<") result = (op1 < op2) ? "true" : "false"; else result = (op1 > op2) ? "true" : "false"; tok->str(result); tok->deleteNext(2); again = true; tok = tok->previous(); } } } if (simplifyCalculations(first->next(), end)) again = true; for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { if (tok->str() == "?" && ((tok->previous()->isNumber() || tok->previous()->isBoolean()) || Token::Match(tok->tokAt(-3), "( %bool%|%num% )"))) { const int offset = (tok->previous()->str() == ")") ? 2 : 1; // Find the token ":" then go to the next token Token *colon = skipTernaryOp(tok, end); if (!colon || colon->previous()->str() != ":" || !colon->next()) continue; //handle the GNU extension: "x ? : y" <-> "x ? x : y" if (colon->previous() == tok->next()) tok->insertToken(tok->strAt(-offset)); // go back before the condition, if possible tok = tok->tokAt(-2); if (offset == 2) { // go further back before the "(" tok = tok->tokAt(-2); //simplify the parentheses tok->deleteNext(); tok->next()->deleteNext(); } if (Token::Match(tok->next(), "false|0")) { // Use code after colon, remove code before it. Token::eraseTokens(tok, colon); tok = tok->next(); again = true; } // The condition is true. Delete the operator after the ":".. else { // delete the condition token and the "?" tok->deleteNext(2); unsigned int ternaryOplevel = 0; for (const Token *endTok = colon; endTok; endTok = endTok->next()) { if (Token::Match(endTok, "(|[|{")) endTok = endTok->link(); else if (endTok->str() == "<" && (endTok->strAt(1) == ">" || templateParameters(endTok))) endTok = endTok->findClosingBracket(); else if (endTok->str() == "?") ++ternaryOplevel; else if (Token::Match(endTok, ")|}|]|;|,|:|>")) { if (endTok->str() == ":" && ternaryOplevel) --ternaryOplevel; else if (endTok->str() == ">" && !end) ; else { Token::eraseTokens(colon->tokAt(-2), endTok); again = true; break; } } } } } } for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { if (Token::Match(tok, "( %num%|%bool% )") && (tok->previous() && !Token::Match(tok->previous(), "%name%"))) { tok->deleteThis(); tok->deleteNext(); again = true; } } } } static bool validTokenStart(bool bounded, const Token *tok, const Token *frontToken, int offset) { if (!bounded) return true; if (frontToken) frontToken = frontToken->previous(); while (tok && offset <= 0) { if (tok == frontToken) return false; ++offset; tok = tok->previous(); } return tok && offset > 0; } static bool validTokenEnd(bool bounded, const Token *tok, const Token *backToken, int offset) { if (!bounded) return true; while (tok && offset >= 0) { if (tok == backToken) return false; --offset; tok = tok->next(); } return tok && offset < 0; } // TODO: This is not the correct class for simplifyCalculations(), so it // should be moved away. bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToken, bool isTemplate) { bool ret = false; const bool bounded = frontToken || backToken; if (!frontToken) { frontToken = mTokenList.front(); } for (Token *tok = frontToken; tok && tok != backToken; tok = tok->next()) { // Remove parentheses around variable.. // keep parentheses here: dynamic_cast(p); // keep parentheses here: A operator * (int); // keep parentheses here: int ( * ( * f ) ( ... ) ) (int) ; // keep parentheses here: int ( * * ( * compilerHookVector ) (void) ) ( ) ; // keep parentheses here: operator new [] (size_t); // keep parentheses here: Functor()(a ... ) // keep parentheses here: ) ( var ) ; if (validTokenEnd(bounded, tok, backToken, 4) && (Token::Match(tok->next(), "( %name% ) ;|)|,|]") || (Token::Match(tok->next(), "( %name% ) %cop%") && (tok->tokAt(2)->varId()>0 || !Token::Match(tok->tokAt(4), "[*&+-~]")))) && !tok->isName() && tok->str() != ">" && tok->str() != ")" && tok->str() != "]") { tok->deleteNext(); tok = tok->next(); tok->deleteNext(); ret = true; } if (validTokenEnd(bounded, tok, backToken, 3) && Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) { tok->str(MathLib::toString(MathLib::toLongNumber(tok->str()))); } if (validTokenEnd(bounded, tok, backToken, 5) && Token::Match(tok, "decltype ( %type% { } )")) { tok->deleteThis(); tok->deleteThis(); tok->deleteNext(); tok->deleteNext(); tok->deleteNext(); ret = true; } if (validTokenEnd(bounded, tok, backToken, 3) && Token::Match(tok, "decltype ( %bool%|%num% )")) { tok->deleteThis(); tok->deleteThis(); if (tok->isBoolean()) tok->str("bool"); else if (MathLib::isFloat(tok->str())) { // MathLib::getSuffix doesn't work for floating point numbers char suffix = tok->str().back(); if (suffix == 'f' || suffix == 'F') tok->str("float"); else if (suffix == 'l' || suffix == 'L') { tok->str("double"); tok->isLong(true); } else tok->str("double"); } else if (MathLib::isInt(tok->str())) { std::string suffix = MathLib::getSuffix(tok->str()); if (suffix.find("LL") != std::string::npos) { tok->str("long"); tok->isLong(true); } else if (suffix.find('L') != std::string::npos) tok->str("long"); else tok->str("int"); tok->isUnsigned(suffix.find('U') != std::string::npos); } tok->deleteNext(); ret = true; } if (validTokenEnd(bounded, tok, backToken, 2) && (Token::Match(tok, "char|short|int|long { }") || Token::Match(tok, "char|short|int|long ( )"))) { tok->str("0"); // FIXME add type suffix tok->isSigned(false); tok->isUnsigned(false); tok->isLong(false); tok->deleteNext(); tok->deleteNext(); ret = true; } if (tok && tok->isNumber()) { if (validTokenEnd(bounded, tok, backToken, 2) && simplifyNumericCalculations(tok, isTemplate)) { ret = true; Token *prev = tok->tokAt(-2); while (validTokenStart(bounded, tok, frontToken, -2) && prev && simplifyNumericCalculations(prev, isTemplate)) { tok = prev; prev = prev->tokAt(-2); } } // Remove redundant conditions (0&&x) (1||x) if (validTokenStart(bounded, tok, frontToken, -1) && validTokenEnd(bounded, tok, backToken, 1) && (Token::Match(tok->previous(), "[(=,] 0 &&") || Token::Match(tok->previous(), "[(=,] 1 %oror%"))) { unsigned int par = 0; const Token *tok2 = tok; const bool andAnd = (tok->next()->str() == "&&"); for (; tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") ++par; else if (tok2->str() == ")" || tok2->str() == "]") { if (par == 0) break; --par; } else if (par == 0 && isLowerThanLogicalAnd(tok2) && (andAnd || tok2->str() != "||")) break; } if (tok2) { eraseTokens(tok, tok2); ret = true; } continue; } if (tok->str() == "0" && validTokenStart(bounded, tok, frontToken, -1)) { if (validTokenEnd(bounded, tok, backToken, 1) && ((Token::Match(tok->previous(), "[+-] 0 %cop%|;") && isLowerThanMulDiv(tok->next())) || (Token::Match(tok->previous(), "%or% 0 %cop%|;") && isLowerThanXor(tok->next())))) { tok = tok->previous(); if (Token::Match(tok->tokAt(-4), "[;{}] %name% = %name% [+-|] 0 ;") && tok->strAt(-3) == tok->previous()->str()) { tok = tok->tokAt(-4); tok->deleteNext(5); } else { tok = tok->previous(); tok->deleteNext(2); } ret = true; } else if (validTokenEnd(bounded, tok, backToken, 1) && (Token::Match(tok->previous(), "[=([,] 0 [+|]") || Token::Match(tok->previous(), "return|case 0 [+|]"))) { tok = tok->previous(); tok->deleteNext(2); ret = true; } else if ((((Token::Match(tok->previous(), "[=[(,] 0 * %name%|%num% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%")) && validTokenEnd(bounded, tok, backToken, 3)) || (((Token::Match(tok->previous(), "[=[(,] 0 * (") || Token::Match(tok->previous(), "return|case 0 *|&& (")) && validTokenEnd(bounded, tok, backToken, 2))))) { tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } else if (validTokenEnd(bounded, tok, backToken, 4) && (Token::Match(tok->previous(), "[=[(,] 0 && *|& %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 0 && *|& %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } } if (tok->str() == "1" && validTokenStart(bounded, tok, frontToken, -1)) { if (validTokenEnd(bounded, tok, backToken, 3) && (Token::Match(tok->previous(), "[=[(,] 1 %oror% %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 1 %oror% %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } else if (validTokenEnd(bounded, tok, backToken, 4) && (Token::Match(tok->previous(), "[=[(,] 1 %oror% *|& %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 1 %oror% *|& %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } } if ((Token::Match(tok->tokAt(-2), "%any% * 1") && validTokenStart(bounded, tok, frontToken, -2)) || (Token::Match(tok->previous(), "%any% 1 *") && validTokenStart(bounded, tok, frontToken, -1))) { tok = tok->previous(); if (tok->str() == "*") tok = tok->previous(); tok->deleteNext(2); ret = true; } // Remove parentheses around number.. if (validTokenStart(bounded, tok, frontToken, -2) && Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") && tok->strAt(-2) != ">") { tok = tok->previous(); tok->deleteThis(); tok->deleteNext(); ret = true; } if (validTokenStart(bounded, tok, frontToken, -1) && validTokenEnd(bounded, tok, backToken, 1) && (Token::Match(tok->previous(), "( 0 [|+]") || Token::Match(tok->previous(), "[|+-] 0 )"))) { tok = tok->previous(); if (Token::Match(tok, "[|+-]")) tok = tok->previous(); tok->deleteNext(2); ret = true; } if (validTokenEnd(bounded, tok, backToken, 2) && Token::Match(tok, "%num% %comp% %num%") && MathLib::isInt(tok->str()) && MathLib::isInt(tok->strAt(2))) { if (validTokenStart(bounded, tok, frontToken, -1) && Token::Match(tok->previous(), "(|&&|%oror%") && Token::Match(tok->tokAt(3), ")|&&|%oror%|?")) { const MathLib::bigint op1(MathLib::toLongNumber(tok->str())); const std::string &cmp(tok->next()->str()); const MathLib::bigint op2(MathLib::toLongNumber(tok->strAt(2))); std::string result; if (cmp == "==") result = (op1 == op2) ? "1" : "0"; else if (cmp == "!=") result = (op1 != op2) ? "1" : "0"; else if (cmp == "<=") result = (op1 <= op2) ? "1" : "0"; else if (cmp == ">=") result = (op1 >= op2) ? "1" : "0"; else if (cmp == "<") result = (op1 < op2) ? "1" : "0"; else result = (op1 > op2) ? "1" : "0"; tok->str(result); tok->deleteNext(2); ret = true; tok = tok->previous(); } } } } return ret; } void TemplateSimplifier::getTemplateParametersInDeclaration( const Token * tok, std::vector & typeParametersInDeclaration) { assert(tok->strAt(-1) == "<"); typeParametersInDeclaration.clear(); const Token *end = tok->previous()->findClosingBracket(); bool inDefaultValue = false; for (; tok && tok!= end; tok = tok->next()) { if (Token::simpleMatch(tok, "template <")) { const Token *closing = tok->next()->findClosingBracket(); if (closing) tok = closing->next(); } else if (tok->link() && Token::Match(tok, "{|(|[")) tok = tok->link(); else if (Token::Match(tok, "%name% ,|>|=")) { if (!inDefaultValue) { typeParametersInDeclaration.push_back(tok); if (tok->strAt(1) == "=") inDefaultValue = true; } } else if (inDefaultValue) { if (tok->str() == ",") inDefaultValue = false; else if (tok->str() == "<") { const Token *closing = tok->findClosingBracket(); if (closing) tok = closing; } } } } bool TemplateSimplifier::matchSpecialization( const Token *templateDeclarationNameToken, const Token *templateInstantiationNameToken, const std::list & specializations) { // Is there a matching specialization? for (std::list::const_iterator it = specializations.begin(); it != specializations.end(); ++it) { if (!Token::Match(*it, "%name% <")) continue; const Token *startToken = (*it); while (startToken->previous() && !Token::Match(startToken->previous(), "[;{}]")) startToken = startToken->previous(); if (!Token::simpleMatch(startToken, "template <")) continue; std::vector templateParameters; getTemplateParametersInDeclaration(startToken->tokAt(2), templateParameters); const Token *instToken = templateInstantiationNameToken->tokAt(2); const Token *declToken = (*it)->tokAt(2); const Token * const endToken = (*it)->next()->findClosingBracket(); if (!endToken) continue; while (declToken != endToken) { if (declToken->str() != instToken->str() || declToken->isSigned() != instToken->isSigned() || declToken->isUnsigned() != instToken->isUnsigned() || declToken->isLong() != instToken->isLong()) { int nr = 0; while (nr < templateParameters.size() && templateParameters[nr]->str() != declToken->str()) ++nr; if (nr == templateParameters.size()) break; } declToken = declToken->next(); instToken = instToken->next(); } if (declToken && instToken && declToken == endToken && instToken->str() == ">") { // specialization matches. return templateDeclarationNameToken == *it; } } // No specialization matches. Return true if the declaration is not a specialization. return Token::Match(templateDeclarationNameToken, "%name% !!<") && (templateDeclarationNameToken->str().find('<') == std::string::npos); } std::string TemplateSimplifier::getNewName( Token *tok2, std::list &typeStringsUsedInTemplateInstantiation) { std::string typeForNewName; unsigned int indentlevel = 0; const Token * endToken = tok2->next()->findClosingBracket(); for (Token *tok3 = tok2->tokAt(2); tok3 != endToken && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { // #2721 - unhandled [ => bail out if (tok3->str() == "[" && !Token::Match(tok3->next(), "%num%| ]")) { typeForNewName.clear(); break; } if (!tok3->next()) { typeForNewName.clear(); break; } if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && (tok3->strAt(1) == ">" || templateParameters(tok3))) ++indentlevel; else if (indentlevel > 0 && Token::Match(tok3, "> ,|>|::")) --indentlevel; if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) { mTypesUsedInTemplateInstantiation.emplace_back(tok3, ""); } if (Token::Match(tok3, "(|[")) ++indentlevel; else if (Token::Match(tok3, ")|]")) --indentlevel; const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; if (!constconst) { if (tok3->isUnsigned()) typeStringsUsedInTemplateInstantiation.push_back("unsigned"); else if (tok3->isSigned()) typeStringsUsedInTemplateInstantiation.push_back("signed"); if (tok3->isLong()) typeStringsUsedInTemplateInstantiation.push_back("long"); typeStringsUsedInTemplateInstantiation.push_back(tok3->str()); } // add additional type information if (!constconst && !Token::Match(tok3, "class|struct|enum")) { if (!typeForNewName.empty()) typeForNewName += ' '; if (tok3->isUnsigned()) typeForNewName += "unsigned "; else if (tok3->isSigned()) typeForNewName += "signed "; if (tok3->isLong()) { typeForNewName += "long "; } typeForNewName += tok3->str(); } } return typeForNewName; } bool TemplateSimplifier::simplifyTemplateInstantiations( const TokenAndName &templateDeclaration, const std::list &specializations, const std::time_t maxtime, std::set &expandedtemplates) { // this variable is not used at the moment. The intention was to // allow continuous instantiations until all templates has been expanded //bool done = false; // Contains tokens such as "T" std::vector typeParametersInDeclaration; getTemplateParametersInDeclaration(templateDeclaration.token()->tokAt(2), typeParametersInDeclaration); const bool printDebug = mSettings->debugwarnings; const bool specialized = templateDeclaration.isSpecialization(); const bool isfunc = templateDeclaration.isFunction(); const bool isVar = templateDeclaration.isVariable(); // locate template usage.. std::string::size_type numberOfTemplateInstantiations = mTemplateInstantiations.size(); unsigned int recursiveCount = 0; bool instantiated = false; for (const TokenAndName &instantiation : mTemplateInstantiations) { // skip deleted instantiations if (!instantiation.token()) continue; if (numberOfTemplateInstantiations != mTemplateInstantiations.size()) { numberOfTemplateInstantiations = mTemplateInstantiations.size(); ++recursiveCount; if (recursiveCount > mSettings->maxTemplateRecursion) { std::list typeStringsUsedInTemplateInstantiation; const std::string typeForNewName = templateDeclaration.name() + "<" + getNewName(instantiation.token(), typeStringsUsedInTemplateInstantiation) + ">"; const std::list callstack(1, instantiation.token()); const ErrorMessage errmsg(callstack, &mTokenizer->list, Severity::information, "templateRecursion", "TemplateSimplifier: max template recursion (" + MathLib::toString(mSettings->maxTemplateRecursion) + ") reached for template '"+typeForNewName+"'. You might want to limit Cppcheck recursion.", Certainty::normal); if (mErrorLogger && mSettings->severity.isEnabled(Severity::information)) mErrorLogger->reportErr(errmsg); // bail out.. break; } } // already simplified if (!Token::Match(instantiation.token(), "%name% <")) continue; if (!((instantiation.fullName() == templateDeclaration.fullName()) || (instantiation.name() == templateDeclaration.name() && instantiation.fullName() == templateDeclaration.scope()))) { // FIXME: fallback to not matching scopes until type deduction works // names must match if (instantiation.name() != templateDeclaration.name()) continue; // scopes must match when present if (!instantiation.scope().empty() && !templateDeclaration.scope().empty()) continue; } // make sure constructors and destructors don't match each other if (templateDeclaration.nameToken()->strAt(-1) == "~" && instantiation.token()->strAt(-1) != "~") continue; // template families should match if (!instantiation.isFunction() && templateDeclaration.isFunction()) { // there are exceptions if (!Token::simpleMatch(instantiation.token()->tokAt(-2), "decltype (")) continue; } if (templateDeclaration.isFunction() && instantiation.isFunction()) { std::vector declFuncArgs; getFunctionArguments(templateDeclaration.nameToken(), declFuncArgs); std::vector instFuncParams; getFunctionArguments(instantiation.token(), instFuncParams); if (declFuncArgs.size() != instFuncParams.size()) { // check for default arguments const Token* tok = templateDeclaration.nameToken()->tokAt(2); const Token* end = templateDeclaration.nameToken()->linkAt(1); size_t count = 0; for (; tok != end; tok = tok->next()) { if (tok->str() == "=") count++; } if (instFuncParams.size() < (declFuncArgs.size() - count) || instFuncParams.size() > declFuncArgs.size()) continue; } } // A global function can't be called through a pointer. if (templateDeclaration.isFunction() && templateDeclaration.scope().empty() && (instantiation.token()->strAt(-1) == "." || Token::simpleMatch(instantiation.token()->tokAt(-2), ". template"))) continue; if (!matchSpecialization(templateDeclaration.nameToken(), instantiation.token(), specializations)) continue; Token * const tok2 = instantiation.token(); if (mErrorLogger && !mTokenList.getFiles().empty()) mErrorLogger->reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); #ifdef MAXTIME if (std::time(0) > maxtime) return false; #else (void)maxtime; #endif assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 const Token *startToken = tok2; while (Token::Match(startToken->tokAt(-2), ">|%name% :: %name%")) { if (startToken->strAt(-2) == ">") { const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); if (tok3) startToken = tok3->previous(); else break; } else startToken = startToken->tokAt(-2); } if (Token::Match(startToken->previous(), ";|{|}|=|const") && (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) continue; // New type.. mTypesUsedInTemplateInstantiation.clear(); std::list typeStringsUsedInTemplateInstantiation; std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); if ((typeForNewName.empty() && !templateDeclaration.isVariadic()) || (!typeParametersInDeclaration.empty() && !instantiateMatch(tok2, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), nullptr))) { if (printDebug && mErrorLogger) { std::list callstack(1, tok2); mErrorLogger->reportErr(ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", "Failed to instantiate template \"" + instantiation.name() + "\". The checking continues anyway.", Certainty::normal)); } if (typeForNewName.empty()) continue; break; } // New classname/funcname.. const std::string newName(templateDeclaration.name() + " < " + typeForNewName + " >"); const std::string newFullName(templateDeclaration.scope() + (templateDeclaration.scope().empty() ? "" : " :: ") + newName); if (expandedtemplates.insert(newFullName).second) { expandTemplate(templateDeclaration, instantiation, typeParametersInDeclaration, newName, !specialized && !isVar); instantiated = true; mChanged = true; } // Replace all these template usages.. replaceTemplateUsage(instantiation, typeStringsUsedInTemplateInstantiation, newName); } // process uninstantiated templates // TODO: remove the specialized check and handle all uninstantiated templates someday. if (!instantiated && specialized) { Token * tok2 = const_cast(templateDeclaration.nameToken()); if (mErrorLogger && !mTokenList.getFiles().empty()) mErrorLogger->reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); #ifdef MAXTIME if (std::time(0) > maxtime) return false; #else (void)maxtime; #endif assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 Token *startToken = tok2; while (Token::Match(startToken->tokAt(-2), ">|%name% :: %name%")) { if (startToken->strAt(-2) == ">") { const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); if (tok3) startToken = tok3->previous(); else break; } else startToken = startToken->tokAt(-2); } // TODO: re-enable when specialized check is removed // if (Token::Match(startToken->previous(), ";|{|}|=|const") && // (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) // return false; // already simplified if (!Token::Match(tok2, "%name% <")) return false; if (!matchSpecialization(templateDeclaration.nameToken(), tok2, specializations)) return false; // New type.. mTypesUsedInTemplateInstantiation.clear(); std::list typeStringsUsedInTemplateInstantiation; std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); if (typeForNewName.empty()) { if (printDebug && mErrorLogger) { std::list callstack(1, tok2); mErrorLogger->reportErr(ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", "Failed to instantiate template \"" + templateDeclaration.name() + "\". The checking continues anyway.", Certainty::normal)); } return false; } // New classname/funcname.. const std::string newName(templateDeclaration.name() + " < " + typeForNewName + " >"); const std::string newFullName(templateDeclaration.scope() + (templateDeclaration.scope().empty() ? "" : " :: ") + newName); if (expandedtemplates.insert(newFullName).second) { expandTemplate(templateDeclaration, templateDeclaration, typeParametersInDeclaration, newName, !specialized && !isVar); instantiated = true; mChanged = true; } // Replace all these template usages.. replaceTemplateUsage(templateDeclaration, typeStringsUsedInTemplateInstantiation, newName); } // Template has been instantiated .. then remove the template declaration return instantiated; } static bool matchTemplateParameters(const Token *nameTok, const std::list &strings) { std::list::const_iterator it = strings.begin(); const Token *tok = nameTok->tokAt(2); const Token *end = nameTok->next()->findClosingBracket(); if (!end) return false; while (tok && tok != end && it != strings.end()) { if (tok->isUnsigned()) { if (*it != "unsigned") return false; else { ++it; if (it == strings.end()) return false; } } else if (tok->isSigned()) { if (*it != "signed") return false; else { ++it; if (it == strings.end()) return false; } } if (tok->isLong()) { if (*it != "long") return false; else { ++it; if (it == strings.end()) return false; } } if (*it != tok->str()) return false; tok = tok->next(); ++it; } return it == strings.end() && tok && tok->str() == ">"; } void TemplateSimplifier::replaceTemplateUsage( const TokenAndName &instantiation, const std::list &typeStringsUsedInTemplateInstantiation, const std::string &newName) { std::list> removeTokens; for (Token *nameTok = mTokenList.front(); nameTok; nameTok = nameTok->next()) { if (!Token::Match(nameTok, "%name% <") || Token::Match(nameTok, "template|const_cast|dynamic_cast|reinterpret_cast|static_cast")) continue; std::set* pointers = nameTok->templateSimplifierPointers(); // check if instantiation matches token instantiation from pointer if (pointers && pointers->size()) { // check full name if (instantiation.fullName() != (*pointers->begin())->fullName()) { // FIXME: fallback to just matching name if (nameTok->str() != instantiation.name()) continue; } } // no pointer available look at tokens directly else { // FIXME: fallback to just matching name if (nameTok->str() != instantiation.name()) continue; } if (!matchTemplateParameters(nameTok, typeStringsUsedInTemplateInstantiation)) continue; Token *tok2 = nameTok->next()->findClosingBracket(); if (!tok2) break; const Token * const nameTok1 = nameTok; nameTok->str(newName); // matching template usage => replace tokens.. // Foo < int > => Foo for (Token *tok = nameTok1->next(); tok != tok2; tok = tok->next()) { if (tok->isName() && tok->templateSimplifierPointers() && !tok->templateSimplifierPointers()->empty()) { std::list::iterator ti; for (ti = mTemplateInstantiations.begin(); ti != mTemplateInstantiations.end();) { if (ti->token() == tok) { mTemplateInstantiations.erase(ti++); break; } else { ++ti; } } } } // Fix crash in #9007 if (Token::simpleMatch(nameTok->previous(), ">")) mTemplateNamePos.erase(nameTok->previous()); removeTokens.emplace_back(nameTok, tok2->next()); nameTok = tok2; } while (!removeTokens.empty()) { eraseTokens(removeTokens.back().first, removeTokens.back().second); removeTokens.pop_back(); } } static bool specMatch( const TemplateSimplifier::TokenAndName &spec, const TemplateSimplifier::TokenAndName &decl) { // make sure decl is really a declaration if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias() || decl.isFriend()) return false; if (!spec.isSameFamily(decl)) return false; // make sure the scopes and names match if (spec.fullName() == decl.fullName()) { if (spec.isFunction()) { std::vector specArgs; std::vector declArgs; getFunctionArguments(spec.nameToken(), specArgs); getFunctionArguments(decl.nameToken(), declArgs); if (specArgs.size() == declArgs.size()) { // @todo make sure function parameters also match return true; } } else return true; } return false; } void TemplateSimplifier::getSpecializations() { // try to locate a matching declaration for each user defined specialization for (auto & spec : mTemplateDeclarations) { if (spec.isSpecialization()) { bool found = false; for (auto & decl : mTemplateDeclarations) { if (specMatch(spec, decl)) { mTemplateSpecializationMap[spec.token()] = decl.token(); found = true; break; } } if (!found) { for (auto & decl : mTemplateForwardDeclarations) { if (specMatch(spec, decl)) { mTemplateSpecializationMap[spec.token()] = decl.token(); break; } } } } } } void TemplateSimplifier::getPartialSpecializations() { // try to locate a matching declaration for each user defined partial specialization for (auto & spec : mTemplateDeclarations) { if (spec.isPartialSpecialization()) { bool found = false; for (auto & decl : mTemplateDeclarations) { if (specMatch(spec, decl)) { mTemplatePartialSpecializationMap[spec.token()] = decl.token(); found = true; break; } } if (!found) { for (auto & decl : mTemplateForwardDeclarations) { if (specMatch(spec, decl)) { mTemplatePartialSpecializationMap[spec.token()] = decl.token(); break; } } } } } } void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() { // try to locate a matching declaration for each forward declaration for (const auto & forwardDecl : mTemplateForwardDeclarations) { std::vector params1; getTemplateParametersInDeclaration(forwardDecl.token()->tokAt(2), params1); for (auto & decl : mTemplateDeclarations) { // skip partializations, type aliases and friends if (decl.isPartialSpecialization() || decl.isAlias() || decl.isFriend()) continue; std::vector params2; getTemplateParametersInDeclaration(decl.token()->tokAt(2), params2); // make sure the number of arguments match if (params1.size() == params2.size()) { // make sure the scopes and names match if (forwardDecl.fullName() == decl.fullName()) { // save forward declaration for lookup later if ((decl.nameToken()->strAt(1) == "(" && forwardDecl.nameToken()->strAt(1) == "(") || (decl.nameToken()->strAt(1) == "{" && forwardDecl.nameToken()->strAt(1) == ";")) { mTemplateForwardDeclarationsMap[decl.token()] = forwardDecl.token(); } for (size_t k = 0; k < params1.size(); k++) { // copy default value to declaration if not present if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") { int level = 0; const Token *end = params1[k]->next(); while (end && !(level == 0 && Token::Match(end, ",|>"))) { if (Token::Match(end, "{|(|<")) level++; else if (Token::Match(end, "}|)|>")) level--; end = end->next(); } if (end) TokenList::copyTokens(const_cast(params2[k]), params1[k]->next(), end->previous()); } } // update parameter end pointer decl.paramEnd(decl.token()->next()->findClosingBracket()); } } } } } void TemplateSimplifier::printOut(const TokenAndName &tokenAndName, const std::string &indent) const { std::cout << indent << "token: "; if (tokenAndName.token()) std::cout << "\"" << tokenAndName.token()->str() << "\" " << mTokenList.fileLine(tokenAndName.token()); else std::cout << "nullptr"; std::cout << std::endl; std::cout << indent << "scope: \"" << tokenAndName.scope() << "\"" << std::endl; std::cout << indent << "name: \"" << tokenAndName.name() << "\"" << std::endl; std::cout << indent << "fullName: \"" << tokenAndName.fullName() << "\"" << std::endl; std::cout << indent << "nameToken: "; if (tokenAndName.nameToken()) std::cout << "\"" << tokenAndName.nameToken()->str() << "\" " << mTokenList.fileLine(tokenAndName.nameToken()); else std::cout << "nullptr"; std::cout << std::endl; std::cout << indent << "paramEnd: "; if (tokenAndName.paramEnd()) std::cout << "\"" << tokenAndName.paramEnd()->str() << "\" " << mTokenList.fileLine(tokenAndName.paramEnd()); else std::cout << "nullptr"; std::cout << std::endl; std::cout << indent << "flags: "; if (tokenAndName.isClass()) std::cout << " isClass"; if (tokenAndName.isFunction()) std::cout << " isFunction"; if (tokenAndName.isVariable()) std::cout << " isVariable"; if (tokenAndName.isAlias()) std::cout << " isAlias"; if (tokenAndName.isSpecialization()) std::cout << " isSpecialization"; if (tokenAndName.isPartialSpecialization()) std::cout << " isPartialSpecialization"; if (tokenAndName.isForwardDeclaration()) std::cout << " isForwardDeclaration"; if (tokenAndName.isVariadic()) std::cout << " isVariadic"; if (tokenAndName.isFriend()) std::cout << " isFriend"; std::cout << std::endl; if (tokenAndName.token() && !tokenAndName.paramEnd() && tokenAndName.token()->strAt(1) == "<") { const Token *end = tokenAndName.token()->next()->findClosingBracket(); if (end) { const Token *start = tokenAndName.token()->next(); std::cout << indent << "type: "; while (start && start != end) { if (start->isUnsigned()) std::cout << "unsigned"; else if (start->isSigned()) std::cout << "signed"; if (start->isLong()) std::cout << "long"; std::cout << start->str(); start = start->next(); } std::cout << end->str() << std::endl; } } else if (tokenAndName.isAlias() && tokenAndName.paramEnd()) { if (tokenAndName.aliasStartToken()) { std::cout << indent << "aliasStartToken: \"" << tokenAndName.aliasStartToken()->str() << "\" " << mTokenList.fileLine(tokenAndName.aliasStartToken()) << std::endl; } if (tokenAndName.aliasEndToken()) { std::cout << indent << "aliasEndToken: \"" << tokenAndName.aliasEndToken()->str() << "\" " << mTokenList.fileLine(tokenAndName.aliasEndToken()) << std::endl; } } } void TemplateSimplifier::printOut(const std::string & text) const { std::cout << std::endl; std::cout << text << std::endl; std::cout << std::endl; std::cout << "mTemplateDeclarations: " << mTemplateDeclarations.size() << std::endl; int count = 0; for (const auto & decl : mTemplateDeclarations) { std::cout << "mTemplateDeclarations[" << count++ << "]:" << std::endl; printOut(decl); } std::cout << "mTemplateForwardDeclarations: " << mTemplateForwardDeclarations.size() << std::endl; count = 0; for (const auto & decl : mTemplateForwardDeclarations) { std::cout << "mTemplateForwardDeclarations[" << count++ << "]:" << std::endl; printOut(decl); } std::cout << "mTemplateForwardDeclarationsMap: " << mTemplateForwardDeclarationsMap.size() << std::endl; unsigned int mapIndex = 0; for (const auto & mapItem : mTemplateForwardDeclarationsMap) { unsigned int declIndex = 0; for (const auto & decl : mTemplateDeclarations) { if (mapItem.first == decl.token()) { unsigned int forwardIndex = 0; for (const auto & forwardDecl : mTemplateForwardDeclarations) { if (mapItem.second == forwardDecl.token()) { std::cout << "mTemplateForwardDeclarationsMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << declIndex << "] => mTemplateForwardDeclarations[" << forwardIndex << "]" << std::endl; break; } forwardIndex++; } break; } declIndex++; } mapIndex++; } std::cout << "mTemplateSpecializationMap: " << mTemplateSpecializationMap.size() << std::endl; for (const auto & mapItem : mTemplateSpecializationMap) { unsigned int decl1Index = 0; for (const auto & decl1 : mTemplateDeclarations) { if (decl1.isSpecialization() && mapItem.first == decl1.token()) { bool found = false; unsigned int decl2Index = 0; for (const auto & decl2 : mTemplateDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl; found = true; break; } decl2Index++; } if (!found) { decl2Index = 0; for (const auto & decl2 : mTemplateForwardDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl; break; } decl2Index++; } } break; } decl1Index++; } mapIndex++; } std::cout << "mTemplatePartialSpecializationMap: " << mTemplatePartialSpecializationMap.size() << std::endl; for (const auto & mapItem : mTemplatePartialSpecializationMap) { unsigned int decl1Index = 0; for (const auto & decl1 : mTemplateDeclarations) { if (mapItem.first == decl1.token()) { bool found = false; unsigned int decl2Index = 0; for (const auto & decl2 : mTemplateDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl; found = true; break; } decl2Index++; } if (!found) { decl2Index = 0; for (const auto & decl2 : mTemplateForwardDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl; break; } decl2Index++; } } break; } decl1Index++; } mapIndex++; } std::cout << "mTemplateInstantiations: " << mTemplateInstantiations.size() << std::endl; count = 0; for (const auto & decl : mTemplateInstantiations) { std::cout << "mTemplateInstantiations[" << count++ << "]:" << std::endl; printOut(decl); } } void TemplateSimplifier::simplifyTemplates( const std::time_t maxtime, bool &codeWithTemplates) { // convert "sizeof ..." to "sizeof..." for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof ...")) { tok->str("sizeof..."); tok->deleteNext(); } } // Remove "typename" unless used in template arguments or using type alias.. for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok, "typename %name%") && !Token::Match(tok->tokAt(-3), "using %name% =")) tok->deleteThis(); if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (!tok) break; } } if (mSettings->standards.cpp >= Standards::CPP20) { // Remove concepts/requires // TODO concepts are not removed yet for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (!Token::Match(tok, ")|>|>> requires %name%|(")) continue; Token *end = skipRequires(tok->next()); if (end) Token::eraseTokens(tok, end); } // explicit(bool) for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "explicit (")) { bool isFalse = Token::simpleMatch(tok->tokAt(2), "false )"); Token::eraseTokens(tok, tok->linkAt(1)->next()); if (isFalse) tok->deleteThis(); } } } mTokenizer->calculateScopes(); unsigned int passCount = 0; const unsigned int passCountMax = 10; for (; passCount < passCountMax; ++passCount) { if (passCount) { // it may take more than one pass to simplify type aliases bool usingChanged = false; while (mTokenizer->simplifyUsing()) usingChanged = true; if (!usingChanged && !mChanged) break; mChanged = usingChanged; mTemplateDeclarations.clear(); mTemplateForwardDeclarations.clear(); mTemplateForwardDeclarationsMap.clear(); mTemplateSpecializationMap.clear(); mTemplatePartialSpecializationMap.clear(); mTemplateInstantiations.clear(); mInstantiatedTemplates.clear(); mExplicitInstantiationsToDelete.clear(); mTemplateNamePos.clear(); } bool hasTemplates = getTemplateDeclarations(); if (passCount == 0) codeWithTemplates = hasTemplates; // Make sure there is something to simplify. if (mTemplateDeclarations.empty() && mTemplateForwardDeclarations.empty()) return; if (mSettings->debugtemplate && mSettings->debugnormal) { std::string title("Template Simplifier pass " + std::to_string(passCount + 1)); mTokenList.front()->printOut(title.c_str(), mTokenList.getFiles()); } // Copy default argument values from forward declaration to declaration fixForwardDeclaredDefaultArgumentValues(); // Locate user defined specializations. getSpecializations(); // Locate user defined partial specializations. getPartialSpecializations(); // Locate possible instantiations of templates.. getTemplateInstantiations(); // Template arguments with default values useDefaultArgumentValues(); simplifyTemplateAliases(); if (mSettings->debugtemplate) printOut("### Template Simplifier pass " + std::to_string(passCount + 1) + " ###"); std::set expandedtemplates; for (std::list::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) { if (iter1->isAlias() || iter1->isFriend()) continue; // get specializations.. std::list specializations; for (std::list::const_iterator iter2 = mTemplateDeclarations.begin(); iter2 != mTemplateDeclarations.end(); ++iter2) { if (iter2->isAlias() || iter2->isFriend()) continue; if (iter1->fullName() == iter2->fullName()) specializations.push_back(iter2->nameToken()); } const bool instantiated = simplifyTemplateInstantiations( *iter1, specializations, maxtime, expandedtemplates); if (instantiated) mInstantiatedTemplates.push_back(*iter1); } for (std::list::const_iterator it = mInstantiatedTemplates.begin(); it != mInstantiatedTemplates.end(); ++it) { std::list::iterator decl; for (decl = mTemplateDeclarations.begin(); decl != mTemplateDeclarations.end(); ++decl) { if (decl->token() == it->token()) break; } if (decl != mTemplateDeclarations.end()) { if (it->isSpecialization()) { // delete the "template < >" Token * tok = it->token(); tok->deleteNext(2); tok->deleteThis(); } else { // remove forward declaration if found auto it1 = mTemplateForwardDeclarationsMap.find(it->token()); if (it1 != mTemplateForwardDeclarationsMap.end()) removeTemplate(it1->second); removeTemplate(it->token()); } mTemplateDeclarations.erase(decl); } } // remove out of line member functions while (!mMemberFunctionsToDelete.empty()) { const std::list::iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindToken(mMemberFunctionsToDelete.begin()->token())); // multiple functions can share the same declaration so make sure it hasn't already been deleted if (it != mTemplateDeclarations.end()) { removeTemplate(it->token()); mTemplateDeclarations.erase(it); } else { const std::list::iterator it1 = std::find_if(mTemplateForwardDeclarations.begin(), mTemplateForwardDeclarations.end(), FindToken(mMemberFunctionsToDelete.begin()->token())); // multiple functions can share the same declaration so make sure it hasn't already been deleted if (it1 != mTemplateForwardDeclarations.end()) { removeTemplate(it1->token()); mTemplateForwardDeclarations.erase(it1); } } mMemberFunctionsToDelete.erase(mMemberFunctionsToDelete.begin()); } // remove explicit instantiations for (TokenAndName & j : mExplicitInstantiationsToDelete) { Token * start = j.token(); if (start) { Token * end = start->next(); while (end && end->str() != ";") end = end->next(); if (start->previous()) start = start->previous(); if (end && end->next()) end = end->next(); eraseTokens(start, end); } } } if (passCount == passCountMax) { if (mSettings->debugwarnings) { const std::list locationList(1, mTokenList.front()); const ErrorMessage errmsg(locationList, &mTokenizer->list, Severity::debug, "debug", "TemplateSimplifier: pass count limit hit before simplifications were finished.", Certainty::normal); if (mErrorLogger) mErrorLogger->reportErr(errmsg); } } // Tweak uninstantiated C++17 fold expressions (... && args) if (mSettings->standards.cpp >= Standards::CPP17) { bool simplify = false; for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (tok->str() == "template") simplify = false; if (tok->str() == "{") simplify = true; if (!simplify || tok->str() != "(") continue; const Token *op = nullptr; const Token *args = nullptr; if (Token::Match(tok, "( ... %op%")) { op = tok->tokAt(2); args = tok->link()->previous(); } else if (Token::Match(tok, "( %name% %op% ...")) { op = tok->tokAt(2); args = tok->link()->previous()->isName() ? nullptr : tok->next(); } else if (Token::Match(tok->link()->tokAt(-3), "%op% ... )")) { op = tok->link()->tokAt(-2); args = tok->next(); } else if (Token::Match(tok->link()->tokAt(-3), "... %op% %name% )")) { op = tok->link()->tokAt(-2); args = tok->next()->isName() ? nullptr : tok->link()->previous(); } else { continue; } // cppcheck-suppress redundantCopyLocalConst ; False positive const std::string strop = op->str(); const std::string strargs = (args && args->isName()) ? args->str() : ""; Token::eraseTokens(tok, tok->link()); tok->insertToken(")"); if (!strargs.empty()) { tok->insertToken("..."); tok->insertToken(strargs); } tok->insertToken("("); Token::createMutualLinks(tok->next(), tok->link()->previous()); tok->insertToken("__cppcheck_fold_" + strop + "__"); } } } void TemplateSimplifier::syntaxError(const Token *tok) { throw InternalError(tok, "syntax error", InternalError::SYNTAX); } cppcheck-2.7/lib/templatesimplifier.h000066400000000000000000000427271417746362400200120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef templatesimplifierH #define templatesimplifierH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include #include #include #include class ErrorLogger; class Settings; class Token; class Tokenizer; class TokenList; /// @addtogroup Core /// @{ /** @brief Simplify templates from the preprocessed and partially simplified code. */ class CPPCHECKLIB TemplateSimplifier { friend class TestSimplifyTemplate; public: explicit TemplateSimplifier(Tokenizer *tokenizer); ~TemplateSimplifier(); /** */ void checkComplicatedSyntaxErrorsInTemplates(); /** * is the token pointing at a template parameters block * < int , 3 > => yes * \param tok start token that must point at "<" * \return number of parameters (invalid parameters => 0) */ static unsigned int templateParameters(const Token *tok); /** * Token and its full scopename */ class TokenAndName { Token *mToken; std::string mScope; std::string mName; std::string mFullName; const Token *mNameToken; const Token *mParamEnd; unsigned int mFlags; enum { fIsClass = (1 << 0), // class template fIsFunction = (1 << 1), // function template fIsVariable = (1 << 2), // variable template fIsAlias = (1 << 3), // alias template fIsSpecialization = (1 << 4), // user specialized template fIsPartialSpecialization = (1 << 5), // user partial specialized template fIsForwardDeclaration = (1 << 6), // forward declaration fIsVariadic = (1 << 7), // variadic template fIsFriend = (1 << 8), // friend template fFamilyMask = (fIsClass | fIsFunction | fIsVariable) }; void isClass(bool state) { setFlag(fIsClass, state); } void isFunction(bool state) { setFlag(fIsFunction, state); } void isVariable(bool state) { setFlag(fIsVariable, state); } void isAlias(bool state) { setFlag(fIsAlias, state); } void isSpecialization(bool state) { setFlag(fIsSpecialization, state); } void isPartialSpecialization(bool state) { setFlag(fIsPartialSpecialization, state); } void isForwardDeclaration(bool state) { setFlag(fIsForwardDeclaration, state); } void isVariadic(bool state) { setFlag(fIsVariadic, state); } void isFriend(bool state) { setFlag(fIsFriend, state); } /** * Get specified flag state. * @param flag flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(unsigned int flag) const { return ((mFlags & flag) != 0); } /** * Set specified flag state. * @param flag flag to set state * @param state new state of flag */ void setFlag(unsigned int flag, bool state) { mFlags = state ? mFlags | flag : mFlags & ~flag; } public: /** * Constructor used for instantiations. * \param token template instantiation name token "name<...>" * \param scope full qualification of template(scope) */ TokenAndName(Token *token, const std::string &scope); /** * Constructor used for declarations. * \param token template declaration token "template < ... >" * \param scope full qualification of template(scope) * \param nameToken template name token "template < ... > class name" * \param paramEnd template parameter end token ">" */ TokenAndName(Token *token, const std::string &scope, const Token *nameToken, const Token *paramEnd); TokenAndName(const TokenAndName& other); ~TokenAndName(); bool operator == (const TokenAndName & rhs) const { return mToken == rhs.mToken && mScope == rhs.mScope && mName == rhs.mName && mFullName == rhs.mFullName && mNameToken == rhs.mNameToken && mParamEnd == rhs.mParamEnd && mFlags == rhs.mFlags; } Token * token() const { return mToken; } void token(Token * token) { mToken = token; } const std::string & scope() const { return mScope; } const std::string & name() const { return mName; } const std::string & fullName() const { return mFullName; } const Token * nameToken() const { return mNameToken; } const Token * paramEnd() const { return mParamEnd; } void paramEnd(const Token *end) { mParamEnd = end; } bool isClass() const { return getFlag(fIsClass); } bool isFunction() const { return getFlag(fIsFunction); } bool isVariable() const { return getFlag(fIsVariable); } bool isAlias() const { return getFlag(fIsAlias); } bool isSpecialization() const { return getFlag(fIsSpecialization); } bool isPartialSpecialization() const { return getFlag(fIsPartialSpecialization); } bool isForwardDeclaration() const { return getFlag(fIsForwardDeclaration); } bool isVariadic() const { return getFlag(fIsVariadic); } bool isFriend() const { return getFlag(fIsFriend); } /** * Get alias start token. * template < ... > using X = foo < ... >; * ^ * @return alias start token */ const Token * aliasStartToken() const; /** * Get alias end token. * template < ... > using X = foo < ... >; * ^ * @return alias end token */ const Token * aliasEndToken() const; /** * Is token an alias token? * template < ... > using X = foo < ... >; * ^ * @param tok token to check * @return true if alias token, false if not */ bool isAliasToken(const Token *tok) const; /** * Is declaration the same family (class, function or variable). * * @param decl declaration to compare to * @return true if same family, false if different family */ bool isSameFamily(const TemplateSimplifier::TokenAndName &decl) const { // Make sure a family flag is set and matches. // This works because at most only one flag will be set. return ((mFlags & fFamilyMask) & (decl.mFlags & fFamilyMask)) != 0; } }; /** * Find last token of a template declaration. * @param tok start token of declaration "template" or token after "template < ... >" * @return last token of declaration or nullptr if syntax error */ static Token *findTemplateDeclarationEnd(Token *tok); static const Token *findTemplateDeclarationEnd(const Token *tok); /** * Match template declaration/instantiation * @param instance template instantiation * @param numberOfArguments number of template arguments * @param variadic last template argument is variadic * @param patternAfter pattern that must match the tokens after the ">" * @return match => true */ static bool instantiateMatch(const Token *instance, const std::size_t numberOfArguments, bool variadic, const char patternAfter[]); /** * Match template declaration/instantiation * @param tok The ">" token e.g. before "class" * @return -1 to bail out or positive integer to identity the position * of the template name. */ int getTemplateNamePosition(const Token *tok); /** * Get class template name position * @param tok The ">" token e.g. before "class" * @param namepos return offset to name * @return true if name found, false if not * */ static bool getTemplateNamePositionTemplateClass(const Token *tok, int &namepos); /** * Get function template name position * @param tok The ">" token * @param namepos return offset to name * @return true if name found, false if not * */ static bool getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos); /** * Get variable template name position * @param tok The ">" token * @param namepos return offset to name * @return true if name found, false if not * */ static bool getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos); /** * Simplify templates * @param maxtime time when the simplification should be stopped * @param codeWithTemplates output parameter that is set if code contains templates */ void simplifyTemplates( const std::time_t maxtime, bool &codeWithTemplates); /** * Simplify constant calculations such as "1+2" => "3" * @param tok start token * @return true if modifications to token-list are done. * false if no modifications are done. */ static bool simplifyNumericCalculations(Token *tok, bool isTemplate = true); /** * Simplify constant calculations such as "1+2" => "3". * This also performs simple cleanup of parentheses etc. * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyCalculations(Token* frontToken = nullptr, Token *backToken = nullptr, bool isTemplate = true); /** Simplify template instantiation arguments. * @param start first token of arguments * @param end token following last argument token */ void simplifyTemplateArgs(Token *start, Token *end); private: /** * Get template declarations * @return true if code has templates. */ bool getTemplateDeclarations(); /** Add template instantiation. * @param token first token of instantiation * @param scope scope of instantiation */ void addInstantiation(Token *token, const std::string &scope); /** * Get template instantiations */ void getTemplateInstantiations(); /** * Fix forward declared default argument values by copying them * when they are not present in the declaration. */ void fixForwardDeclaredDefaultArgumentValues(); /** * simplify template instantiations (use default argument values) */ void useDefaultArgumentValues(); /** * simplify template instantiations (use default argument values) * @param declaration template declaration or forward declaration */ void useDefaultArgumentValues(TokenAndName &declaration); /** * Try to locate a matching declaration for each user defined * specialization. */ void getSpecializations(); /** * Try to locate a matching declaration for each user defined * partial specialization. */ void getPartialSpecializations(); /** * simplify template aliases */ void simplifyTemplateAliases(); /** * Simplify templates : expand all instantiations for a template * @todo It seems that inner templates should be instantiated recursively * @param templateDeclaration template declaration * @param specializations template specializations (list each template name token) * @param maxtime time when the simplification will stop * @param expandedtemplates all templates that has been expanded so far. The full names are stored. * @return true if the template was instantiated */ bool simplifyTemplateInstantiations( const TokenAndName &templateDeclaration, const std::list &specializations, const std::time_t maxtime, std::set &expandedtemplates); /** * Simplify templates : add namespace to template name * @param templateDeclaration template declaration * @param tok place to insert namespace */ void addNamespace(const TokenAndName &templateDeclaration, const Token *tok); /** * Simplify templates : check if namespace already present * @param templateDeclaration template declaration * @param tok place to start looking for namespace * @return true if namespace already present */ static bool alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok); /** * Expand a template. Create "expanded" class/function at end of tokenlist. * @param templateDeclaration Template declaration information * @param templateInstantiation Full name of template * @param typeParametersInDeclaration The type parameters of the template * @param newName New name of class/function. * @param copy copy or expand in place */ void expandTemplate( const TokenAndName &templateDeclaration, const TokenAndName &templateInstantiation, const std::vector &typeParametersInDeclaration, const std::string &newName, bool copy); /** * Replace all matching template usages 'Foo < int >' => 'Foo' * @param instantiation Template instantiation information. * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. * @param newName The new type name */ void replaceTemplateUsage(const TokenAndName &instantiation, const std::list &typeStringsUsedInTemplateInstantiation, const std::string &newName); /** * @brief TemplateParametersInDeclaration * @param tok template < typename T, typename S > * ^ tok * @param typeParametersInDeclaration template < typename T, typename S > * ^ [0] ^ [1] */ static void getTemplateParametersInDeclaration( const Token * tok, std::vector & typeParametersInDeclaration); /** * Remove a specific "template < ..." template class/function */ static bool removeTemplate(Token *tok); /** Syntax error */ NORETURN static void syntaxError(const Token *tok); static bool matchSpecialization( const Token *templateDeclarationNameToken, const Token *templateInstantiationNameToken, const std::list & specializations); /* * Same as Token::eraseTokens() but tries to fix up lists with pointers to the deleted tokens. * @param begin Tokens after this will be erased. * @param end Tokens before this will be erased. */ static void eraseTokens(Token *begin, const Token *end); /** * Delete specified token without invalidating pointer to following token. * tok will be invalidated. * @param tok token to delete */ static void deleteToken(Token *tok); /** * Get the new token name. * @param tok2 name token * @param typeStringsUsedInTemplateInstantiation type strings use in template instantiation * @return new token name */ std::string getNewName( Token *tok2, std::list &typeStringsUsedInTemplateInstantiation); void printOut( const TokenAndName &tokenAndName, const std::string &indent = " ") const; void printOut(const std::string &text = "") const; Tokenizer *mTokenizer; TokenList &mTokenList; const Settings *mSettings; ErrorLogger *mErrorLogger; bool mChanged; std::list mTemplateDeclarations; std::list mTemplateForwardDeclarations; std::map mTemplateForwardDeclarationsMap; std::map mTemplateSpecializationMap; std::map mTemplatePartialSpecializationMap; std::list mTemplateInstantiations; std::list mInstantiatedTemplates; std::list mMemberFunctionsToDelete; std::vector mExplicitInstantiationsToDelete; std::vector mTypesUsedInTemplateInstantiation; std::unordered_map mTemplateNamePos; }; /// @} //--------------------------------------------------------------------------- #endif // templatesimplifierH cppcheck-2.7/lib/timer.cpp000066400000000000000000000065171417746362400155630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "timer.h" #include #include #include #include /* TODO: - rename "file" to "single" - synchronise map access in multithreaded mode or disable timing - add unit tests - for --showtime (needs input file) - for Timer* classes */ namespace { using dataElementType = std::pair; bool more_second_sec(const dataElementType& lhs, const dataElementType& rhs) { return lhs.second.seconds() > rhs.second.seconds(); } } void TimerResults::showResults(SHOWTIME_MODES mode) const { if (mode == SHOWTIME_MODES::SHOWTIME_NONE) return; std::cout << std::endl; TimerResultsData overallData; std::vector data(mResults.begin(), mResults.end()); std::sort(data.begin(), data.end(), more_second_sec); size_t ordinal = 1; // maybe it would be nice to have an ordinal in output later! for (std::vector::const_iterator iter=data.begin(); iter!=data.end(); ++iter) { const double sec = iter->second.seconds(); const double secAverage = sec / (double)(iter->second.mNumberOfResults); overallData.mClocks += iter->second.mClocks; if ((mode != SHOWTIME_MODES::SHOWTIME_TOP5) || (ordinal<=5)) { std::cout << iter->first << ": " << sec << "s (avg. " << secAverage << "s - " << iter->second.mNumberOfResults << " result(s))" << std::endl; } ++ordinal; } const double secOverall = overallData.seconds(); std::cout << "Overall time: " << secOverall << "s" << std::endl; } void TimerResults::addResults(const std::string& str, std::clock_t clocks) { mResults[str].mClocks += clocks; mResults[str].mNumberOfResults++; } Timer::Timer(const std::string& str, SHOWTIME_MODES showtimeMode, TimerResultsIntf* timerResults) : mStr(str) , mTimerResults(timerResults) , mStart(0) , mShowTimeMode(showtimeMode) , mStopped(false) { if (showtimeMode != SHOWTIME_MODES::SHOWTIME_NONE) mStart = std::clock(); } Timer::~Timer() { stop(); } void Timer::stop() { if ((mShowTimeMode != SHOWTIME_MODES::SHOWTIME_NONE) && !mStopped) { const std::clock_t end = std::clock(); const std::clock_t diff = end - mStart; if (mShowTimeMode == SHOWTIME_MODES::SHOWTIME_FILE) { const double sec = (double)diff / CLOCKS_PER_SEC; std::cout << mStr << ": " << sec << "s" << std::endl; } else { if (mTimerResults) mTimerResults->addResults(mStr, diff); } } mStopped = true; } cppcheck-2.7/lib/timer.h000066400000000000000000000045621417746362400152260ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef timerH #define timerH //--------------------------------------------------------------------------- #include "config.h" #include #include #include enum class SHOWTIME_MODES { SHOWTIME_NONE = 0, SHOWTIME_FILE, SHOWTIME_SUMMARY, SHOWTIME_TOP5 }; class CPPCHECKLIB TimerResultsIntf { public: virtual ~TimerResultsIntf() {} virtual void addResults(const std::string& str, std::clock_t clocks) = 0; }; struct TimerResultsData { std::clock_t mClocks; long mNumberOfResults; TimerResultsData() : mClocks(0) , mNumberOfResults(0) {} double seconds() const { const double ret = (double)((unsigned long)mClocks) / (double)CLOCKS_PER_SEC; return ret; } }; class CPPCHECKLIB TimerResults : public TimerResultsIntf { public: TimerResults() {} void showResults(SHOWTIME_MODES mode) const; void addResults(const std::string& str, std::clock_t clocks) OVERRIDE; private: std::map mResults; }; class CPPCHECKLIB Timer { public: Timer(const std::string& str, SHOWTIME_MODES showtimeMode, TimerResultsIntf* timerResults = nullptr); ~Timer(); void stop(); private: Timer(const Timer& other); // disallow copying Timer& operator=(const Timer&); // disallow assignments const std::string mStr; TimerResultsIntf* mTimerResults; std::clock_t mStart; const SHOWTIME_MODES mShowTimeMode; bool mStopped; }; //--------------------------------------------------------------------------- #endif // timerH cppcheck-2.7/lib/token.cpp000066400000000000000000002425471417746362400155700ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "token.h" #include "astutils.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "tokenlist.h" #include "utils.h" #include "tokenrange.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::list TokenImpl::mEmptyValueList; Token::Token(TokensFrontBack *tokensFrontBack) : mTokensFrontBack(tokensFrontBack), mNext(nullptr), mPrevious(nullptr), mLink(nullptr), mTokType(eNone), mFlags(0) { mImpl = new TokenImpl(); } Token::~Token() { delete mImpl; } /* * Get a TokenRange which starts at this token and contains every token following it in order up to but not including 't' * e.g. for the sequence of tokens A B C D E, C.until(E) would yield the Range C D * note t can be nullptr to iterate all the way to the end. */ ConstTokenRange Token::until(const Token* t) const { return ConstTokenRange(this, t); } static const std::unordered_set controlFlowKeywords = { "goto", "do", "if", "else", "for", "while", "switch", "case", "break", "continue", "return" }; void Token::update_property_info() { setFlag(fIsControlFlowKeyword, controlFlowKeywords.find(mStr) != controlFlowKeywords.end()); if (!mStr.empty()) { if (mStr == "true" || mStr == "false") tokType(eBoolean); else if (isStringLiteral(mStr)) tokType(eString); else if (isCharLiteral(mStr)) tokType(eChar); else if (std::isalpha((unsigned char)mStr[0]) || mStr[0] == '_' || mStr[0] == '$') { // Name if (mImpl->mVarId) tokType(eVariable); else if (mTokensFrontBack && mTokensFrontBack->list && mTokensFrontBack->list->isKeyword(mStr)) tokType(eKeyword); else if (mTokType != eVariable && mTokType != eFunction && mTokType != eType && mTokType != eKeyword) tokType(eName); } else if (std::isdigit((unsigned char)mStr[0]) || (mStr.length() > 1 && mStr[0] == '-' && std::isdigit((unsigned char)mStr[1]))) tokType(eNumber); else if (mStr == "=" || mStr == "<<=" || mStr == ">>=" || (mStr.size() == 2U && mStr[1] == '=' && std::strchr("+-*/%&^|", mStr[0]))) tokType(eAssignmentOp); else if (mStr.size() == 1 && mStr.find_first_of(",[]()?:") != std::string::npos) tokType(eExtendedOp); else if (mStr=="<<" || mStr==">>" || (mStr.size()==1 && mStr.find_first_of("+-*/%") != std::string::npos)) tokType(eArithmeticalOp); else if (mStr.size() == 1 && mStr.find_first_of("&|^~") != std::string::npos) tokType(eBitOp); else if (mStr.size() <= 2 && (mStr == "&&" || mStr == "||" || mStr == "!")) tokType(eLogicalOp); else if (mStr.size() <= 2 && !mLink && (mStr == "==" || mStr == "!=" || mStr == "<" || mStr == "<=" || mStr == ">" || mStr == ">=")) tokType(eComparisonOp); else if (mStr == "<=>") tokType(eComparisonOp); else if (mStr.size() == 2 && (mStr == "++" || mStr == "--")) tokType(eIncDecOp); else if (mStr.size() == 1 && (mStr.find_first_of("{}") != std::string::npos || (mLink && mStr.find_first_of("<>") != std::string::npos))) tokType(eBracket); else if (mStr == "...") tokType(eEllipsis); else tokType(eOther); } else { tokType(eNone); } update_property_char_string_literal(); update_property_isStandardType(); } static const std::unordered_set stdTypes = { "bool" , "_Bool" , "char" , "double" , "float" , "int" , "long" , "short" , "size_t" , "void" , "wchar_t" }; void Token::update_property_isStandardType() { isStandardType(false); if (mStr.size() < 3) return; if (stdTypes.find(mStr)!=stdTypes.end()) { isStandardType(true); tokType(eType); } } void Token::update_property_char_string_literal() { if (mTokType != Token::eString && mTokType != Token::eChar) return; isLong(((mTokType == Token::eString) && isPrefixStringCharLiteral(mStr, '"', "L")) || ((mTokType == Token::eChar) && isPrefixStringCharLiteral(mStr, '\'', "L"))); } bool Token::isUpperCaseName() const { if (!isName()) return false; for (char i : mStr) { if (std::islower(i)) return false; } return true; } void Token::concatStr(std::string const& b) { mStr.pop_back(); mStr.append(getStringLiteral(b) + "\""); if (isCChar() && isStringLiteral(b) && b[0] != '"') { mStr.insert(0, b.substr(0, b.find('"'))); } update_property_info(); } std::string Token::strValue() const { assert(mTokType == eString); std::string ret(getStringLiteral(mStr)); std::string::size_type pos = 0U; while ((pos = ret.find('\\', pos)) != std::string::npos) { ret.erase(pos,1U); if (ret[pos] >= 'a') { if (ret[pos] == 'n') ret[pos] = '\n'; else if (ret[pos] == 'r') ret[pos] = '\r'; else if (ret[pos] == 't') ret[pos] = '\t'; } if (ret[pos] == '0') return ret.substr(0,pos); pos++; } return ret; } void Token::deleteNext(nonneg int count) { while (mNext && count > 0) { Token *n = mNext; // #8154 we are about to be unknown -> destroy the link to us if (n->mLink && n->mLink->mLink == n) n->mLink->link(nullptr); mNext = n->next(); delete n; --count; } if (mNext) mNext->previous(this); else if (mTokensFrontBack) mTokensFrontBack->back = this; } void Token::deletePrevious(nonneg int count) { while (mPrevious && count > 0) { Token *p = mPrevious; // #8154 we are about to be unknown -> destroy the link to us if (p->mLink && p->mLink->mLink == p) p->mLink->link(nullptr); mPrevious = p->previous(); delete p; --count; } if (mPrevious) mPrevious->next(this); else if (mTokensFrontBack) mTokensFrontBack->front = this; } void Token::swapWithNext() { if (mNext) { std::swap(mStr, mNext->mStr); std::swap(mTokType, mNext->mTokType); std::swap(mFlags, mNext->mFlags); std::swap(mImpl, mNext->mImpl); if (mImpl->mTemplateSimplifierPointers) for (auto *templateSimplifierPointer : *mImpl->mTemplateSimplifierPointers) { templateSimplifierPointer->token(this); } if (mNext->mImpl->mTemplateSimplifierPointers) for (auto *templateSimplifierPointer : *mNext->mImpl->mTemplateSimplifierPointers) { templateSimplifierPointer->token(mNext); } if (mNext->mLink) mNext->mLink->mLink = this; if (this->mLink) this->mLink->mLink = mNext; std::swap(mLink, mNext->mLink); } } void Token::takeData(Token *fromToken) { mStr = fromToken->mStr; tokType(fromToken->mTokType); mFlags = fromToken->mFlags; delete mImpl; mImpl = fromToken->mImpl; fromToken->mImpl = nullptr; if (mImpl->mTemplateSimplifierPointers) for (auto *templateSimplifierPointer : *mImpl->mTemplateSimplifierPointers) { templateSimplifierPointer->token(this); } mLink = fromToken->mLink; if (mLink) mLink->link(this); } void Token::deleteThis() { if (mNext) { // Copy next to this and delete next takeData(mNext); mNext->link(nullptr); // mark as unlinked deleteNext(); } else if (mPrevious) { // Copy previous to this and delete previous takeData(mPrevious); mPrevious->link(nullptr); deletePrevious(); } else { // We are the last token in the list, we can't delete // ourselves, so just make us empty str(";"); } } void Token::replace(Token *replaceThis, Token *start, Token *end) { // Fix the whole in the old location of start and end if (start->previous()) start->previous()->next(end->next()); if (end->next()) end->next()->previous(start->previous()); // Move start and end to their new location if (replaceThis->previous()) replaceThis->previous()->next(start); if (replaceThis->next()) replaceThis->next()->previous(end); start->previous(replaceThis->previous()); end->next(replaceThis->next()); if (end->mTokensFrontBack && end->mTokensFrontBack->back == end) { while (end->next()) end = end->next(); end->mTokensFrontBack->back = end; } // Update mProgressValue, fileIndex and linenr for (Token *tok = start; tok != end->next(); tok = tok->next()) tok->mImpl->mProgressValue = replaceThis->mImpl->mProgressValue; // Delete old token, which is replaced delete replaceThis; } const Token *Token::tokAt(int index) const { const Token *tok = this; while (index > 0 && tok) { tok = tok->next(); --index; } while (index < 0 && tok) { tok = tok->previous(); ++index; } return tok; } const Token *Token::linkAt(int index) const { const Token *tok = this->tokAt(index); if (!tok) { throw InternalError(this, "Internal error. Token::linkAt called with index outside the tokens range."); } return tok->link(); } const std::string &Token::strAt(int index) const { const Token *tok = this->tokAt(index); return tok ? tok->mStr : emptyString; } static int multiComparePercent(const Token *tok, const char*& haystack, nonneg int varid) { ++haystack; // Compare only the first character of the string for optimization reasons switch (haystack[0]) { case '\0': case ' ': case '|': //simple '%' character haystack += 1; if (tok->isArithmeticalOp() && tok->str() == "%") return 1; break; case 'v': if (haystack[3] == '%') { // %var% haystack += 4; if (tok->varId() != 0) return 1; } else { // %varid% if (varid == 0) { throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); } haystack += 6; if (tok->varId() == varid) return 1; } break; case 't': // Type (%type%) { haystack += 5; if (tok->isName() && tok->varId() == 0 && (tok->str() != "delete" || !tok->isKeyword())) // HACK: this is legacy behaviour, it should return false for all keywords, except types return 1; } break; case 'a': // Accept any token (%any%) or assign (%assign%) { if (haystack[3] == '%') { // %any% haystack += 4; return 1; } else { // %assign% haystack += 7; if (tok->isAssignmentOp()) return 1; } } break; case 'n': // Number (%num%) or name (%name%) { if (haystack[4] == '%') { // %name% haystack += 5; if (tok->isName()) return 1; } else { haystack += 4; if (tok->isNumber()) return 1; } } break; case 'c': { haystack += 1; // Character (%char%) if (haystack[0] == 'h') { haystack += 4; if (tok->tokType() == Token::eChar) return 1; } // Const operator (%cop%) else if (haystack[1] == 'p') { haystack += 3; if (tok->isConstOp()) return 1; } // Comparison operator (%comp%) else { haystack += 4; if (tok->isComparisonOp()) return 1; } } break; case 's': // String (%str%) { haystack += 4; if (tok->tokType() == Token::eString) return 1; } break; case 'b': // Bool (%bool%) { haystack += 5; if (tok->isBoolean()) return 1; } break; case 'o': { ++haystack; if (haystack[1] == '%') { // Op (%op%) if (haystack[0] == 'p') { haystack += 2; if (tok->isOp()) return 1; } // Or (%or%) else { haystack += 2; if (tok->tokType() == Token::eBitOp && tok->str() == "|") return 1; } } // Oror (%oror%) else { haystack += 4; if (tok->tokType() == Token::eLogicalOp && tok->str() == "||") return 1; } } break; default: //unknown %cmd%, abort throw InternalError(tok, "Unexpected command"); } if (*haystack == '|') haystack += 1; else return -1; return 0xFFFF; } int Token::multiCompare(const Token *tok, const char *haystack, nonneg int varid) { const char *needle = tok->str().c_str(); const char *needlePointer = needle; for (;;) { if (needlePointer == needle && haystack[0] == '%' && haystack[1] != '|' && haystack[1] != '\0' && haystack[1] != ' ') { const int ret = multiComparePercent(tok, haystack, varid); if (ret < 2) return ret; } else if (*haystack == '|') { if (*needlePointer == 0) { // If needle is at the end, we have a match. return 1; } needlePointer = needle; ++haystack; } else if (*needlePointer == *haystack) { if (*needlePointer == '\0') return 1; ++needlePointer; ++haystack; } else if (*haystack == ' ' || *haystack == '\0') { if (needlePointer == needle) return 0; break; } // If haystack and needle don't share the same character, // find next '|' character. else { needlePointer = needle; do { ++haystack; if (*haystack == ' ' || *haystack == '\0') { return -1; } if (*haystack == '|') { break; } } while (true); ++haystack; } } if (*needlePointer == '\0') return 1; return -1; } bool Token::simpleMatch(const Token *tok, const char pattern[], size_t pattern_len) { if (!tok) return false; // shortcut const char *current = pattern; const char *end = pattern + pattern_len; const char *next = (const char*)std::memchr(pattern, ' ', pattern_len); if (!next) next = end; while (*current) { const std::size_t length = next - current; if (!tok || length != tok->mStr.length() || std::strncmp(current, tok->mStr.c_str(), length)) return false; current = next; if (*next) { next = std::strchr(++current, ' '); if (!next) next = end; } tok = tok->next(); } return true; } bool Token::firstWordEquals(const char *str, const char *word) { for (;;) { if (*str != *word) { return (*str == ' ' && *word == 0); } else if (*str == 0) break; ++str; ++word; } return true; } const char *Token::chrInFirstWord(const char *str, char c) { for (;;) { if (*str == ' ' || *str == 0) return nullptr; if (*str == c) return str; ++str; } } bool Token::Match(const Token *tok, const char pattern[], nonneg int varid) { const char *p = pattern; while (*p) { // Skip spaces in pattern.. while (*p == ' ') ++p; // No token => Success! if (*p == '\0') break; if (!tok) { // If we have no tokens, pattern "!!else" should return true if (p[0] == '!' && p[1] == '!' && p[2] != '\0') { while (*p && *p != ' ') ++p; continue; } else return false; } // [.. => search for a one-character token.. if (p[0] == '[' && chrInFirstWord(p, ']')) { if (tok->str().length() != 1) return false; const char *temp = p+1; bool chrFound = false; int count = 0; while (*temp && *temp != ' ') { if (*temp == ']') { ++count; } else if (*temp == tok->str()[0]) { chrFound = true; break; } ++temp; } if (count > 1 && tok->str()[0] == ']') chrFound = true; if (!chrFound) return false; p = temp; while (*p && *p != ' ') ++p; } // Parse "not" options. Token can be anything except the given one else if (p[0] == '!' && p[1] == '!' && p[2] != '\0') { p += 2; if (firstWordEquals(p, tok->str().c_str())) return false; while (*p && *p != ' ') ++p; } // Parse multi options, such as void|int|char (accept token which is one of these 3) else { const int res = multiCompare(tok, p, varid); if (res == 0) { // Empty alternative matches, use the same token on next round while (*p && *p != ' ') ++p; continue; } else if (res == -1) { // No match return false; } } while (*p && *p != ' ') ++p; tok = tok->next(); } // The end of the pattern has been reached and nothing wrong has been found return true; } nonneg int Token::getStrLength(const Token *tok) { assert(tok != nullptr); assert(tok->mTokType == eString); int len = 0; const std::string str(getStringLiteral(tok->str())); std::string::const_iterator it = str.begin(); const std::string::const_iterator end = str.end(); while (it != end) { if (*it == '\\') { ++it; // string ends at '\0' if (*it == '0') return len; } if (*it == '\0') return len; ++it; ++len; } return len; } nonneg int Token::getStrArraySize(const Token *tok) { assert(tok != nullptr); assert(tok->tokType() == eString); const std::string str(getStringLiteral(tok->str())); int sizeofstring = 1; for (int i = 0; i < (int)str.size(); i++) { if (str[i] == '\\') ++i; ++sizeofstring; } return sizeofstring; } nonneg int Token::getStrSize(const Token *tok, const Settings *settings) { assert(tok != nullptr && tok->tokType() == eString); nonneg int sizeofType = 1; if (tok->valueType()) { ValueType vt(*tok->valueType()); vt.pointer = 0; sizeofType = ValueFlow::getSizeOf(vt, settings); } return getStrArraySize(tok) * sizeofType; } std::string Token::getCharAt(const Token *tok, MathLib::bigint index) { assert(tok != nullptr); std::string str(getStringLiteral(tok->str())); std::string::const_iterator it = str.begin(); const std::string::const_iterator end = str.end(); while (it != end) { if (index == 0) { if (*it == '\0') return "\\0"; std::string ret(1, *it); if (*it == '\\') { ++it; ret += *it; } return ret; } if (*it == '\\') ++it; ++it; --index; } assert(index == 0); return "\\0"; } void Token::move(Token *srcStart, Token *srcEnd, Token *newLocation) { /**[newLocation] -> b -> c -> [srcStart] -> [srcEnd] -> f */ // Fix the gap, which tokens to be moved will leave srcStart->previous()->next(srcEnd->next()); srcEnd->next()->previous(srcStart->previous()); // Fix the tokens to be moved srcEnd->next(newLocation->next()); srcStart->previous(newLocation); // Fix the tokens at newLocation newLocation->next()->previous(srcEnd); newLocation->next(srcStart); // Update _progressValue for (Token *tok = srcStart; tok != srcEnd->next(); tok = tok->next()) tok->mImpl->mProgressValue = newLocation->mImpl->mProgressValue; } Token* Token::nextArgument() const { for (const Token* tok = this; tok; tok = tok->next()) { if (tok->str() == ",") return tok->next(); else if (tok->link() && Token::Match(tok, "(|{|[|<")) tok = tok->link(); else if (Token::Match(tok, ")|;")) return nullptr; } return nullptr; } Token* Token::nextArgumentBeforeCreateLinks2() const { for (const Token* tok = this; tok; tok = tok->next()) { if (tok->str() == ",") return tok->next(); else if (tok->link() && Token::Match(tok, "(|{|[")) tok = tok->link(); else if (tok->str() == "<") { const Token* temp = tok->findClosingBracket(); if (temp) tok = temp; } else if (Token::Match(tok, ")|;")) return nullptr; } return nullptr; } Token* Token::nextTemplateArgument() const { for (const Token* tok = this; tok; tok = tok->next()) { if (tok->str() == ",") return tok->next(); else if (tok->link() && Token::Match(tok, "(|{|[|<")) tok = tok->link(); else if (Token::Match(tok, ">|;")) return nullptr; } return nullptr; } static bool isOperator(const Token *tok) { if (tok->link()) tok = tok->link(); // TODO handle multi token operators return tok->strAt(-1) == "operator"; } const Token * Token::findClosingBracket() const { if (mStr != "<") return nullptr; if (!mPrevious) return nullptr; if (!(mPrevious->isName() || Token::Match(mPrevious->previous(), "operator %op% <") || Token::Match(mPrevious->tokAt(-2), "operator [([] [)]] <"))) return nullptr; const Token *closing = nullptr; const bool templateParameter(strAt(-1) == "template"); std::set templateParameters; bool isDecl = true; for (const Token *prev = previous(); prev; prev = prev->previous()) { if (prev->str() == "=") isDecl = false; if (Token::simpleMatch(prev, "template <")) isDecl = true; if (Token::Match(prev, "[;{}]")) break; } unsigned int depth = 0; for (closing = this; closing != nullptr; closing = closing->next()) { if (Token::Match(closing, "{|[|(")) { closing = closing->link(); if (!closing) return nullptr; // #6803 } else if (Token::Match(closing, "}|]|)|;")) return nullptr; // we can make some guesses for template parameters else if (closing->str() == "<" && closing->previous() && (closing->previous()->isName() || isOperator(closing->previous())) && (templateParameter ? templateParameters.find(closing->strAt(-1)) == templateParameters.end() : true)) ++depth; else if (closing->str() == ">") { if (--depth == 0) return closing; } else if (closing->str() == ">>" || closing->str() == ">>=") { if (!isDecl && depth == 1) continue; if (depth <= 2) return closing; depth -= 2; } // save named template parameter else if (templateParameter && depth == 1 && closing->str() == "," && closing->previous()->isName() && !Match(closing->previous(), "class|typename|.")) templateParameters.insert(closing->strAt(-1)); } return closing; } Token * Token::findClosingBracket() { // return value of const function return const_cast(const_cast(this)->findClosingBracket()); } const Token * Token::findOpeningBracket() const { if (mStr != ">") return nullptr; const Token *opening = nullptr; unsigned int depth = 0; for (opening = this; opening != nullptr; opening = opening->previous()) { if (Token::Match(opening, "}|]|)")) { opening = opening->link(); if (!opening) return nullptr; } else if (Token::Match(opening, "{|{|(|;")) return nullptr; else if (opening->str() == ">") ++depth; else if (opening->str() == "<") { if (--depth == 0) return opening; } } return opening; } Token * Token::findOpeningBracket() { // return value of const function return const_cast(const_cast(this)->findOpeningBracket()); } //--------------------------------------------------------------------------- const Token *Token::findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len) { for (const Token* tok = startTok; tok; tok = tok->next()) { if (Token::simpleMatch(tok, pattern, pattern_len)) return tok; } return nullptr; } const Token *Token::findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len, const Token * const end) { for (const Token* tok = startTok; tok && tok != end; tok = tok->next()) { if (Token::simpleMatch(tok, pattern, pattern_len)) return tok; } return nullptr; } const Token *Token::findmatch(const Token * const startTok, const char pattern[], const nonneg int varId) { for (const Token* tok = startTok; tok; tok = tok->next()) { if (Token::Match(tok, pattern, varId)) return tok; } return nullptr; } const Token *Token::findmatch(const Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId) { for (const Token* tok = startTok; tok && tok != end; tok = tok->next()) { if (Token::Match(tok, pattern, varId)) return tok; } return nullptr; } void Token::function(const Function *f) { mImpl->mFunction = f; if (f) { if (f->isLambda()) tokType(eLambda); else tokType(eFunction); } else if (mTokType == eFunction) tokType(eName); } Token* Token::insertToken(const std::string& tokenStr, const std::string& originalNameStr, bool prepend) { Token *newToken; if (mStr.empty()) newToken = this; else newToken = new Token(mTokensFrontBack); newToken->str(tokenStr); if (!originalNameStr.empty()) newToken->originalName(originalNameStr); if (newToken != this) { newToken->mImpl->mLineNumber = mImpl->mLineNumber; newToken->mImpl->mFileIndex = mImpl->mFileIndex; newToken->mImpl->mProgressValue = mImpl->mProgressValue; if (prepend) { if (this->previous()) { newToken->previous(this->previous()); newToken->previous()->next(newToken); } else if (mTokensFrontBack) { mTokensFrontBack->front = newToken; } this->previous(newToken); newToken->next(this); } else { if (this->next()) { newToken->next(this->next()); newToken->next()->previous(newToken); } else if (mTokensFrontBack) { mTokensFrontBack->back = newToken; } this->next(newToken); newToken->previous(this); } if (mImpl->mScopeInfo) { // If the brace is immediately closed there is no point opening a new scope for it if (newToken->str() == "{") { std::string nextScopeNameAddition; // This might be the opening of a member function Token *tok1 = newToken; while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->previous() && tok1->strAt(-1) == ")") { tok1 = tok1->linkAt(-1); if (Token::Match(tok1->previous(), "throw|noexcept")) { tok1 = tok1->previous(); while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->strAt(-1) != ")") return newToken; } else if (Token::Match(newToken->tokAt(-2), ":|, %name%")) { tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) != ")") return newToken; } if (tok1->strAt(-1) == ">") tok1 = tok1->previous()->findOpeningBracket(); if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) { tok1 = tok1->tokAt(-2); std::string scope = tok1->strAt(-1); while (Token::Match(tok1->tokAt(-2), ":: %name%")) { scope = tok1->strAt(-3) + " :: " + scope; tok1 = tok1->tokAt(-2); } nextScopeNameAddition += scope; } } // Or it might be a namespace/class/struct if (Token::Match(newToken->previous(), "%name%|>")) { Token* nameTok = newToken->previous(); while (nameTok && !Token::Match(nameTok, "namespace|class|struct|union %name% {|::|:|<")) { nameTok = nameTok->previous(); } if (nameTok) { for (nameTok = nameTok->next(); nameTok && !Token::Match(nameTok, "{|:|<"); nameTok = nameTok->next()) { nextScopeNameAddition.append(nameTok->str()); nextScopeNameAddition.append(" "); } if (nextScopeNameAddition.length() > 0) nextScopeNameAddition = nextScopeNameAddition.substr(0, nextScopeNameAddition.length() - 1); } } // New scope is opening, record it here std::shared_ptr newScopeInfo = std::make_shared(mImpl->mScopeInfo->name, nullptr, mImpl->mScopeInfo->usingNamespaces); if (!newScopeInfo->name.empty() && !nextScopeNameAddition.empty()) newScopeInfo->name.append(" :: "); newScopeInfo->name.append(nextScopeNameAddition); nextScopeNameAddition = ""; newToken->scopeInfo(newScopeInfo); } else if (newToken->str() == "}") { Token* matchingTok = newToken->previous(); int depth = 0; while (matchingTok && (depth != 0 || !Token::simpleMatch(matchingTok, "{"))) { if (Token::simpleMatch(matchingTok, "}")) depth++; if (Token::simpleMatch(matchingTok, "{")) depth--; matchingTok = matchingTok->previous(); } if (matchingTok && matchingTok->previous()) { newToken->mImpl->mScopeInfo = matchingTok->previous()->scopeInfo(); } } else { if (prepend && newToken->previous()) { newToken->mImpl->mScopeInfo = newToken->previous()->scopeInfo(); } else { newToken->mImpl->mScopeInfo = mImpl->mScopeInfo; } if (newToken->str() == ";") { const Token* statementStart; for (statementStart = newToken; statementStart->previous() && !Token::Match(statementStart->previous(), ";|{"); statementStart = statementStart->previous()); if (Token::Match(statementStart, "using namespace %name% ::|;")) { const Token * tok1 = statementStart->tokAt(2); std::string nameSpace; while (tok1 && tok1->str() != ";") { if (!nameSpace.empty()) nameSpace += " "; nameSpace += tok1->str(); tok1 = tok1->next(); } mImpl->mScopeInfo->usingNamespaces.insert(nameSpace); } } } } } return newToken; } void Token::eraseTokens(Token *begin, const Token *end) { if (!begin || begin == end) return; while (begin->next() && begin->next() != end) { begin->deleteNext(); } } void Token::createMutualLinks(Token *begin, Token *end) { assert(begin != nullptr); assert(end != nullptr); assert(begin != end); begin->link(end); end->link(begin); } void Token::printOut(const char *title) const { if (title && title[0]) std::cout << "\n### " << title << " ###\n"; std::cout << stringifyList(stringifyOptions::forPrintOut(), nullptr, nullptr) << std::endl; } void Token::printOut(const char *title, const std::vector &fileNames) const { if (title && title[0]) std::cout << "\n### " << title << " ###\n"; std::cout << stringifyList(stringifyOptions::forPrintOut(), &fileNames, nullptr) << std::endl; } // cppcheck-suppress unusedFunction - used for debugging void Token::printLines(int lines) const { const Token *end = this; while (end && end->linenr() < lines + linenr()) end = end->next(); std::cout << stringifyList(stringifyOptions::forDebugExprId(), nullptr, end) << std::endl; } std::string Token::stringify(const stringifyOptions& options) const { std::string ret; if (options.attributes) { if (isUnsigned()) ret += "unsigned "; else if (isSigned()) ret += "signed "; if (isComplex()) ret += "_Complex "; if (isLong()) { if (!(mTokType == eString || mTokType == eChar)) ret += "long "; } } if (options.macro && isExpandedMacro()) ret += '$'; if (isName() && mStr.find(' ') != std::string::npos) { for (char i : mStr) { if (i != ' ') ret += i; } } else if (mStr[0] != '\"' || mStr.find('\0') == std::string::npos) ret += mStr; else { for (char i : mStr) { if (i == '\0') ret += "\\0"; else ret += i; } } if (options.varid && mImpl->mVarId != 0) { ret += '@'; ret += (options.idtype ? "var" : ""); ret += std::to_string(mImpl->mVarId); } else if (options.exprid && mImpl->mExprId != 0) { ret += '@'; ret += (options.idtype ? "expr" : ""); ret += std::to_string(mImpl->mExprId); } return ret; } std::string Token::stringify(bool varid, bool attributes, bool macro) const { stringifyOptions options; options.varid = varid; options.attributes = attributes; options.macro = macro; return stringify(options); } std::string Token::stringifyList(const stringifyOptions& options, const std::vector* fileNames, const Token* end) const { if (this == end) return ""; std::string ret; unsigned int lineNumber = mImpl->mLineNumber - (options.linenumbers ? 1U : 0U); unsigned int fileIndex = options.files ? ~0U : mImpl->mFileIndex; std::map lineNumbers; for (const Token *tok = this; tok != end; tok = tok->next()) { assert(tok && "end precedes token"); if (!tok) return ret; bool fileChange = false; if (tok->mImpl->mFileIndex != fileIndex) { if (fileIndex != ~0U) { lineNumbers[fileIndex] = tok->mImpl->mFileIndex; } fileIndex = tok->mImpl->mFileIndex; if (options.files) { ret += "\n\n##file "; if (fileNames && fileNames->size() > tok->mImpl->mFileIndex) ret += fileNames->at(tok->mImpl->mFileIndex); else ret += std::to_string(fileIndex); ret += '\n'; } lineNumber = lineNumbers[fileIndex]; fileChange = true; } if (options.linebreaks && (lineNumber != tok->linenr() || fileChange)) { if (lineNumber+4 < tok->linenr() && fileIndex == tok->mImpl->mFileIndex) { ret += '\n'; ret += std::to_string(lineNumber+1); ret += ":\n|\n"; ret += std::to_string(tok->linenr()-1); ret += ":\n"; ret += std::to_string(tok->linenr()); ret += ": "; } else if (this == tok && options.linenumbers) { ret += std::to_string(tok->linenr()); ret += ": "; } else if (lineNumber > tok->linenr()) { lineNumber = tok->linenr(); ret += '\n'; if (options.linenumbers) { ret += std::to_string(lineNumber); ret += ':'; ret += ' '; } } else { while (lineNumber < tok->linenr()) { ++lineNumber; ret += '\n'; if (options.linenumbers) { ret += std::to_string(lineNumber); ret += ':'; if (lineNumber == tok->linenr()) ret += ' '; } } } lineNumber = tok->linenr(); } ret += tok->stringify(options); // print token if (tok->next() != end && (!options.linebreaks || (tok->next()->linenr() == tok->linenr() && tok->next()->fileIndex() == tok->fileIndex()))) ret += ' '; } if (options.linebreaks && (options.files || options.linenumbers)) ret += '\n'; return ret; } std::string Token::stringifyList(bool varid, bool attributes, bool linenumbers, bool linebreaks, bool files, const std::vector* fileNames, const Token* end) const { stringifyOptions options; options.varid = varid; options.attributes = attributes; options.macro = attributes; options.linenumbers = linenumbers; options.linebreaks = linebreaks; options.files = files; return stringifyList(options, fileNames, end); } std::string Token::stringifyList(const Token* end, bool attributes) const { return stringifyList(false, attributes, false, false, false, nullptr, end); } std::string Token::stringifyList(bool varid) const { return stringifyList(varid, false, true, true, true, nullptr, nullptr); } void Token::astParent(Token* tok) { const Token* tok2 = tok; while (tok2) { if (this == tok2) throw InternalError(this, "Internal error. AST cyclic dependency."); tok2 = tok2->astParent(); } // Clear children to avoid nodes referenced twice if (this->astParent()) { Token* parent = this->astParent(); if (parent->astOperand1() == this) parent->mImpl->mAstOperand1 = nullptr; if (parent->astOperand2() == this) parent->mImpl->mAstOperand2 = nullptr; } mImpl->mAstParent = tok; } void Token::astOperand1(Token *tok) { if (mImpl->mAstOperand1) mImpl->mAstOperand1->astParent(nullptr); // goto parent operator if (tok) { tok = tok->astTop(); tok->astParent(this); } mImpl->mAstOperand1 = tok; } void Token::astOperand2(Token *tok) { if (mImpl->mAstOperand2) mImpl->mAstOperand2->astParent(nullptr); // goto parent operator if (tok) { tok = tok->astTop(); tok->astParent(this); } mImpl->mAstOperand2 = tok; } static const Token* goToLeftParenthesis(const Token* start, const Token* end) { // move start to lpar in such expression: '(*it).x' int par = 0; for (const Token *tok = start; tok && tok != end; tok = tok->next()) { if (tok->str() == "(") ++par; else if (tok->str() == ")") { if (par == 0) start = tok->link(); else --par; } } return start; } static const Token* goToRightParenthesis(const Token* start, const Token* end) { // move end to rpar in such expression: '2>(x+1)' int par = 0; for (const Token *tok = end; tok && tok != start; tok = tok->previous()) { if (tok->str() == ")") ++par; else if (tok->str() == "(") { if (par == 0) end = tok->link(); else --par; } } return end; } std::pair Token::findExpressionStartEndTokens() const { const Token * const top = this; // find start node in AST tree const Token *start = top; while (start->astOperand1() && precedes(start->astOperand1(), start)) start = start->astOperand1(); // find end node in AST tree const Token *end = top; while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) { // lambda.. if (end->str() == "[") { const Token *lambdaEnd = findLambdaEndToken(end); if (lambdaEnd) { end = lambdaEnd; break; } } if (Token::Match(end,"(|[|{") && !(Token::Match(end, "( %type%") && !end->astOperand2())) { end = end->link(); break; } end = end->astOperand2() ? end->astOperand2() : end->astOperand1(); } // skip parentheses start = goToLeftParenthesis(start, end); end = goToRightParenthesis(start, end); if (Token::simpleMatch(end, "{")) end = end->link(); return std::pair(start,end); } bool Token::isCalculation() const { if (!Token::Match(this, "%cop%|++|--")) return false; if (Token::Match(this, "*|&")) { // dereference or address-of? if (!this->astOperand2()) return false; if (this->astOperand2()->str() == "[") return false; // type specification? std::stack operands; operands.push(this); while (!operands.empty()) { const Token *op = operands.top(); operands.pop(); if (op->isNumber() || op->varId() > 0) return true; if (op->astOperand1()) operands.push(op->astOperand1()); if (op->astOperand2()) operands.push(op->astOperand2()); else if (Token::Match(op, "*|&")) return false; } // type specification => return false return false; } return true; } bool Token::isUnaryPreOp() const { if (!astOperand1() || astOperand2()) return false; if (!Token::Match(this, "++|--")) return true; const Token *tokbefore = mPrevious; const Token *tokafter = mNext; for (int distance = 1; distance < 10 && tokbefore; distance++) { if (tokbefore == mImpl->mAstOperand1) return false; if (tokafter == mImpl->mAstOperand1) return true; tokbefore = tokbefore->mPrevious; tokafter = tokafter->mPrevious; } return false; // <- guess } static std::string stringFromTokenRange(const Token* start, const Token* end) { std::string ret; if (end) end = end->next(); for (const Token *tok = start; tok && tok != end; tok = tok->next()) { if (tok->isUnsigned()) ret += "unsigned "; if (tok->isLong() && !tok->isLiteral()) ret += "long "; if (tok->tokType() == Token::eString) { for (unsigned char c: tok->str()) { if (c == '\n') ret += "\\n"; else if (c == '\r') ret += "\\r"; else if (c == '\t') ret += "\\t"; else if (c >= ' ' && c <= 126) ret += c; else { char str[10]; sprintf(str, "\\x%02x", c); ret += str; } } } else if (tok->originalName().empty() || tok->isUnsigned() || tok->isLong()) { ret += tok->str(); } else ret += tok->originalName(); if (Token::Match(tok, "%name%|%num% %name%|%num%")) ret += ' '; } return ret; } std::string Token::expressionString() const { const auto tokens = findExpressionStartEndTokens(); return stringFromTokenRange(tokens.first, tokens.second); } static void astStringXml(const Token *tok, nonneg int indent, std::ostream &out) { const std::string strindent(indent, ' '); out << strindent << "str() << '\"'; if (tok->varId()) out << " varId=\"" << MathLib::toString(tok->varId()) << '\"'; if (tok->variable()) out << " variable=\"" << tok->variable() << '\"'; if (tok->function()) out << " function=\"" << tok->function() << '\"'; if (!tok->values().empty()) out << " values=\"" << &tok->values() << '\"'; if (!tok->astOperand1() && !tok->astOperand2()) { out << "/>" << std::endl; } else { out << '>' << std::endl; if (tok->astOperand1()) astStringXml(tok->astOperand1(), indent+2U, out); if (tok->astOperand2()) astStringXml(tok->astOperand2(), indent+2U, out); out << strindent << "" << std::endl; } } void Token::printAst(bool verbose, bool xml, const std::vector &fileNames, std::ostream &out) const { if (!xml) out << "\n\n##AST" << std::endl; std::set printed; for (const Token *tok = this; tok; tok = tok->next()) { if (!tok->mImpl->mAstParent && tok->mImpl->mAstOperand1) { if (printed.find(tok) != printed.end()) continue; printed.insert(tok); if (xml) { out << "scope() << "\" fileIndex=\"" << tok->fileIndex() << "\" linenr=\"" << tok->linenr() << "\" column=\"" << tok->column() << "\">" << std::endl; astStringXml(tok, 2U, out); out << "" << std::endl; } else if (verbose) out << "[" << fileNames[tok->fileIndex()] << ":" << tok->linenr() << "]" << std::endl << tok->astStringVerbose() << std::endl; else out << tok->astString(" ") << std::endl; if (tok->str() == "(") tok = tok->link(); } } } static void indent(std::string &str, const nonneg int indent1, const nonneg int indent2) { for (int i = 0; i < indent1; ++i) str += ' '; for (int i = indent1; i < indent2; i += 2) str += "| "; } void Token::astStringVerboseRecursive(std::string& ret, const nonneg int indent1, const nonneg int indent2) const { if (isExpandedMacro()) ret += '$'; ret += mStr; if (mImpl->mValueType) ret += " \'" + mImpl->mValueType->str() + '\''; if (function()) { std::ostringstream ostr; ostr << std::hex << function(); ret += " f:" + ostr.str(); } ret += '\n'; if (mImpl->mAstOperand1) { int i1 = indent1, i2 = indent2 + 2; if (indent1 == indent2 && !mImpl->mAstOperand2) i1 += 2; indent(ret, indent1, indent2); ret += mImpl->mAstOperand2 ? "|-" : "`-"; mImpl->mAstOperand1->astStringVerboseRecursive(ret, i1, i2); } if (mImpl->mAstOperand2) { int i1 = indent1, i2 = indent2 + 2; if (indent1 == indent2) i1 += 2; indent(ret, indent1, indent2); ret += "`-"; mImpl->mAstOperand2->astStringVerboseRecursive(ret, i1, i2); } } std::string Token::astStringVerbose() const { std::string ret; astStringVerboseRecursive(ret); return ret; } std::string Token::astStringZ3() const { if (!astOperand1()) return str(); if (!astOperand2()) return "(" + str() + " " + astOperand1()->astStringZ3() + ")"; return "(" + str() + " " + astOperand1()->astStringZ3() + " " + astOperand2()->astStringZ3() + ")"; } void Token::printValueFlow(bool xml, std::ostream &out) const { int line = 0; if (xml) out << " " << std::endl; else out << "\n\n##Value flow" << std::endl; for (const Token *tok = this; tok; tok = tok->next()) { if (!tok->mImpl->mValues) continue; if (tok->mImpl->mValues->empty()) // Values might be removed by removeContradictions continue; if (xml) out << " mImpl->mValues << "\">" << std::endl; else if (line != tok->linenr()) out << "Line " << tok->linenr() << std::endl; line = tok->linenr(); if (!xml) { ValueFlow::Value::ValueKind valueKind = tok->mImpl->mValues->front().valueKind; bool same = true; for (const ValueFlow::Value &value : *tok->mImpl->mValues) { if (value.valueKind != valueKind) { same = false; break; } } out << " " << tok->str() << " "; if (same) { switch (valueKind) { case ValueFlow::Value::ValueKind::Impossible: case ValueFlow::Value::ValueKind::Known: out << "always "; break; case ValueFlow::Value::ValueKind::Inconclusive: out << "inconclusive "; break; case ValueFlow::Value::ValueKind::Possible: out << "possible "; break; } } if (tok->mImpl->mValues->size() > 1U) out << '{'; } for (const ValueFlow::Value &value : *tok->mImpl->mValues) { if (xml) { out << " valueType() && tok->valueType()->sign == ValueType::UNSIGNED) out << "intvalue=\"" << (MathLib::biguint)value.intvalue << '\"'; else out << "intvalue=\"" << value.intvalue << '\"'; break; case ValueFlow::Value::ValueType::TOK: out << "tokvalue=\"" << value.tokvalue << '\"'; break; case ValueFlow::Value::ValueType::FLOAT: out << "floatvalue=\"" << value.floatValue << '\"'; break; case ValueFlow::Value::ValueType::MOVED: out << "movedvalue=\"" << ValueFlow::Value::toString(value.moveKind) << '\"'; break; case ValueFlow::Value::ValueType::UNINIT: out << "uninit=\"1\""; break; case ValueFlow::Value::ValueType::BUFFER_SIZE: out << "buffer-size=\"" << value.intvalue << "\""; break; case ValueFlow::Value::ValueType::CONTAINER_SIZE: out << "container-size=\"" << value.intvalue << '\"'; break; case ValueFlow::Value::ValueType::ITERATOR_START: out << "iterator-start=\"" << value.intvalue << '\"'; break; case ValueFlow::Value::ValueType::ITERATOR_END: out << "iterator-end=\"" << value.intvalue << '\"'; break; case ValueFlow::Value::ValueType::LIFETIME: out << "lifetime=\"" << value.tokvalue << '\"'; out << " lifetime-scope=\"" << ValueFlow::Value::toString(value.lifetimeScope) << "\""; out << " lifetime-kind=\"" << ValueFlow::Value::toString(value.lifetimeKind) << "\""; break; case ValueFlow::Value::ValueType::SYMBOLIC: out << "symbolic=\"" << value.tokvalue << '\"'; out << " symbolic-delta=\"" << value.intvalue << '\"'; break; } out << " bound=\"" << ValueFlow::Value::toString(value.bound) << "\""; if (value.condition) out << " condition-line=\"" << value.condition->linenr() << '\"'; if (value.isKnown()) out << " known=\"true\""; else if (value.isPossible()) out << " possible=\"true\""; else if (value.isImpossible()) out << " impossible=\"true\""; else if (value.isInconclusive()) out << " inconclusive=\"true\""; out << " path=\"" << value.path << "\""; out << "/>" << std::endl; } else { if (&value != &tok->mImpl->mValues->front()) out << ","; if (value.isImpossible()) out << "!"; if (value.bound == ValueFlow::Value::Bound::Lower) out << ">="; if (value.bound == ValueFlow::Value::Bound::Upper) out << "<="; switch (value.valueType) { case ValueFlow::Value::ValueType::INT: out << value.intvalue; break; case ValueFlow::Value::ValueType::TOK: out << value.tokvalue->str(); break; case ValueFlow::Value::ValueType::FLOAT: out << value.floatValue; break; case ValueFlow::Value::ValueType::MOVED: out << ValueFlow::Value::toString(value.moveKind); break; case ValueFlow::Value::ValueType::UNINIT: out << "Uninit"; break; case ValueFlow::Value::ValueType::BUFFER_SIZE: case ValueFlow::Value::ValueType::CONTAINER_SIZE: out << "size=" << value.intvalue; break; case ValueFlow::Value::ValueType::ITERATOR_START: out << "start=" << value.intvalue; break; case ValueFlow::Value::ValueType::ITERATOR_END: out << "end=" << value.intvalue; break; case ValueFlow::Value::ValueType::LIFETIME: out << "lifetime[" << ValueFlow::Value::toString(value.lifetimeKind) << "]=(" << value.tokvalue->expressionString() << ")"; break; case ValueFlow::Value::ValueType::SYMBOLIC: out << "symbolic=(" << value.tokvalue->expressionString(); if (value.intvalue > 0) out << "+" << value.intvalue; else if (value.intvalue < 0) out << "-" << -value.intvalue; out << ")"; break; } if (value.indirect > 0) for (int i=0; i 0) out << "@" << value.path; } } if (xml) out << " " << std::endl; else if (tok->mImpl->mValues->size() > 1U) out << '}' << std::endl; else out << std::endl; } if (xml) out << " " << std::endl; } const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Settings *settings) const { if (!mImpl->mValues) return nullptr; return ValueFlow::findValue(*mImpl->mValues, settings, [&](const ValueFlow::Value& v) { return !v.isImpossible() && v.isIntValue() && v.intvalue <= val; }); } const ValueFlow::Value * Token::getValueGE(const MathLib::bigint val, const Settings *settings) const { if (!mImpl->mValues) return nullptr; return ValueFlow::findValue(*mImpl->mValues, settings, [&](const ValueFlow::Value& v) { return !v.isImpossible() && v.isIntValue() && v.intvalue >= val; }); } const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const { if (!mImpl->mValues || !settings) return nullptr; const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isImpossible()) continue; if ((it->isIntValue() && !settings->library.isIntArgValid(ftok, argnr, it->intvalue)) || (it->isFloatValue() && !settings->library.isFloatArgValid(ftok, argnr, it->floatValue))) { if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive())) ret = &(*it); if (!ret->isInconclusive() && !ret->condition) break; } } if (ret) { if (ret->isInconclusive() && !settings->certainty.isEnabled(Certainty::inconclusive)) return nullptr; if (ret->condition && !settings->severity.isEnabled(Severity::warning)) return nullptr; } return ret; } const Token *Token::getValueTokenMinStrSize(const Settings *settings) const { if (!mImpl->mValues) return nullptr; const Token *ret = nullptr; int minsize = INT_MAX; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { const int size = getStrSize(it->tokvalue, settings); if (!ret || size < minsize) { minsize = size; ret = it->tokvalue; } } } return ret; } const Token *Token::getValueTokenMaxStrLength() const { if (!mImpl->mValues) return nullptr; const Token *ret = nullptr; int maxlength = 0; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { const int length = getStrLength(it->tokvalue); if (!ret || length > maxlength) { maxlength = length; ret = it->tokvalue; } } } return ret; } static bool isAdjacent(const ValueFlow::Value& x, const ValueFlow::Value& y) { if (x.bound != ValueFlow::Value::Bound::Point && x.bound == y.bound) return true; if (x.valueType == ValueFlow::Value::ValueType::FLOAT) return false; return std::abs(x.intvalue - y.intvalue) == 1; } static bool removePointValue(std::list& values, ValueFlow::Value& x) { const bool isPoint = x.bound == ValueFlow::Value::Bound::Point; if (!isPoint) x.decreaseRange(); else values.remove(x); return isPoint; } static bool removeContradiction(std::list& values) { bool result = false; for (ValueFlow::Value& x : values) { if (x.isNonValue()) continue; for (ValueFlow::Value& y : values) { if (y.isNonValue()) continue; if (x == y) continue; if (x.valueType != y.valueType) continue; if (x.isImpossible() == y.isImpossible()) continue; if (x.isSymbolicValue() && !ValueFlow::Value::sameToken(x.tokvalue, y.tokvalue)) continue; if (!x.equalValue(y)) { auto compare = [](const ValueFlow::Value& x, const ValueFlow::Value& y) { return x.compareValue(y, ValueFlow::less{}); }; const ValueFlow::Value& maxValue = std::max(x, y, compare); const ValueFlow::Value& minValue = std::min(x, y, compare); // TODO: Adjust non-points instead of removing them if (maxValue.isImpossible() && maxValue.bound == ValueFlow::Value::Bound::Upper) { values.remove(minValue); return true; } if (minValue.isImpossible() && minValue.bound == ValueFlow::Value::Bound::Lower) { values.remove(maxValue); return true; } continue; } const bool removex = !x.isImpossible() || y.isKnown(); const bool removey = !y.isImpossible() || x.isKnown(); if (x.bound == y.bound) { if (removex) values.remove(x); if (removey) values.remove(y); return true; } else { result = removex || removey; bool bail = false; if (removex && removePointValue(values, x)) bail = true; if (removey && removePointValue(values, y)) bail = true; if (bail) return true; } } } return result; } using ValueIterator = std::list::iterator; template static ValueIterator removeAdjacentValues(std::list& values, ValueIterator x, Iterator start, Iterator last) { if (!isAdjacent(*x, **start)) return std::next(x); auto it = std::adjacent_find(start, last, [](ValueIterator x, ValueIterator y) { return !isAdjacent(*x, *y); }); if (it == last) it--; (*it)->bound = x->bound; std::for_each(start, it, [&](ValueIterator y) { values.erase(y); }); return values.erase(x); } static void mergeAdjacent(std::list& values) { for (auto x = values.begin(); x != values.end();) { if (x->isNonValue()) { x++; continue; } if (x->bound == ValueFlow::Value::Bound::Point) { x++; continue; } std::vector adjValues; for (auto y = values.begin(); y != values.end(); y++) { if (x == y) continue; if (y->isNonValue()) continue; if (x->valueType != y->valueType) continue; if (x->valueKind != y->valueKind) continue; if (x->isSymbolicValue() && !ValueFlow::Value::sameToken(x->tokvalue, y->tokvalue)) continue; if (x->bound != y->bound) { if (y->bound != ValueFlow::Value::Bound::Point && isAdjacent(*x, *y)) { adjValues.clear(); break; } // No adjacent points for floating points if (x->valueType == ValueFlow::Value::ValueType::FLOAT) continue; if (y->bound != ValueFlow::Value::Bound::Point) continue; } if (x->bound == ValueFlow::Value::Bound::Lower && !y->compareValue(*x, ValueFlow::less{})) continue; if (x->bound == ValueFlow::Value::Bound::Upper && !x->compareValue(*y, ValueFlow::less{})) continue; adjValues.push_back(y); } if (adjValues.empty()) { x++; continue; } std::sort(adjValues.begin(), adjValues.end(), [&values](ValueIterator xx, ValueIterator yy) { assert(xx != values.end() && yy != values.end()); return xx->compareValue(*yy, ValueFlow::less{}); }); if (x->bound == ValueFlow::Value::Bound::Lower) x = removeAdjacentValues(values, x, adjValues.rbegin(), adjValues.rend()); else if (x->bound == ValueFlow::Value::Bound::Upper) x = removeAdjacentValues(values, x, adjValues.begin(), adjValues.end()); } } static void removeOverlaps(std::list& values) { for (ValueFlow::Value& x : values) { if (x.isNonValue()) continue; values.remove_if([&](ValueFlow::Value& y) { if (y.isNonValue()) return false; if (&x == &y) return false; if (x.valueType != y.valueType) return false; if (x.valueKind != y.valueKind) return false; // TODO: Remove points coverd in a lower or upper bound // TODO: Remove lower or upper bound already covered by a lower and upper bound if (!x.equalValue(y)) return false; if (x.bound != y.bound) return false; return true; }); } mergeAdjacent(values); } // Removing contradictions is an NP-hard problem. Instead we run multiple // passes to try to catch most contradictions static void removeContradictions(std::list& values) { removeOverlaps(values); for (int i = 0; i < 4; i++) { if (!removeContradiction(values)) return; removeOverlaps(values); } } static bool sameValueType(const ValueFlow::Value& x, const ValueFlow::Value& y) { if (x.valueType != y.valueType) return false; // Symbolic are the same type if they share the same tokvalue if (x.isSymbolicValue()) return x.tokvalue->exprId() == 0 || x.tokvalue->exprId() == y.tokvalue->exprId(); return true; } bool Token::addValue(const ValueFlow::Value &value) { if (value.isKnown() && mImpl->mValues) { // Clear all other values of the same type since value is known mImpl->mValues->remove_if([&](const ValueFlow::Value& x) { return sameValueType(x, value); }); } // Dont add a value if its already known if (!value.isKnown() && mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& x) { return x.isKnown() && sameValueType(x, value) && !x.equalValue(value); })) return false; // assert(value.isKnown() || !mImpl->mValues || std::none_of(mImpl->mValues->begin(), mImpl->mValues->end(), // [&](const ValueFlow::Value& x) { // return x.isKnown() && sameValueType(x, value); // })); if (mImpl->mValues) { // Don't handle more than 10 values for performance reasons // TODO: add setting? if (mImpl->mValues->size() >= 10U) return false; // if value already exists, don't add it again std::list::iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { // different types => continue if (it->valueType != value.valueType) continue; if (it->isImpossible() != value.isImpossible()) continue; // different value => continue if (!it->equalValue(value)) continue; if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str())) continue; // same value, but old value is inconclusive so replace it if (it->isInconclusive() && !value.isInconclusive() && !value.isImpossible()) { *it = value; if (it->varId == 0) it->varId = mImpl->mVarId; break; } // Same value already exists, don't add new value return false; } // Add value if (it == mImpl->mValues->end()) { ValueFlow::Value v(value); if (v.varId == 0) v.varId = mImpl->mVarId; if (v.isKnown() && v.isIntValue()) mImpl->mValues->push_front(v); else mImpl->mValues->push_back(v); } } else { ValueFlow::Value v(value); if (v.varId == 0) v.varId = mImpl->mVarId; mImpl->mValues = new std::list(1, v); } removeContradictions(*mImpl->mValues); return true; } void Token::assignProgressValues(Token *tok) { int total_count = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) ++total_count; int count = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) tok2->mImpl->mProgressValue = count++ *100 / total_count; } void Token::assignIndexes() { int index = (mPrevious ? mPrevious->mImpl->mIndex : 0) + 1; for (Token *tok = this; tok; tok = tok->next()) tok->mImpl->mIndex = index++; } void Token::setValueType(ValueType *vt) { if (vt != mImpl->mValueType) { delete mImpl->mValueType; mImpl->mValueType = vt; } } void Token::type(const ::Type *t) { mImpl->mType = t; if (t) { tokType(eType); isEnumType(mImpl->mType->isEnumType()); } else if (mTokType == eType) tokType(eName); } const ::Type* Token::typeOf(const Token* tok, const Token** typeTok) { if (!tok) return nullptr; if (typeTok != nullptr) *typeTok = tok; if (Token::simpleMatch(tok, "return")) { const Scope *scope = tok->scope(); if (!scope) return nullptr; const Function *function = scope->function; if (!function) return nullptr; return function->retType; } else if (Token::Match(tok, "%type%")) { return tok->type(); } else if (Token::Match(tok, "%var%")) { const Variable *var = tok->variable(); if (!var) return nullptr; return var->type(); } else if (Token::Match(tok, "%name%")) { const Function *function = tok->function(); if (!function) return nullptr; return function->retType; } else if (Token::Match(tok->previous(), "%type%|= (|{")) { return typeOf(tok->previous(), typeTok); } else if (Token::simpleMatch(tok, "=")) { return Token::typeOf(getLHSVariableToken(tok), typeTok); } else if (Token::simpleMatch(tok, ".")) { return Token::typeOf(tok->astOperand2(), typeTok); } else if (Token::simpleMatch(tok, "[")) { return Token::typeOf(tok->astOperand1(), typeTok); } else if (Token::simpleMatch(tok, "{")) { int argnr; const Token* ftok = getTokenArgumentFunction(tok, argnr); if (argnr < 0) return nullptr; if (!ftok) return nullptr; if (ftok == tok) return nullptr; std::vector vars = getArgumentVars(ftok, argnr); if (vars.empty()) return nullptr; if (std::all_of( vars.begin(), vars.end(), [&](const Variable* var) { return var->type() == vars.front()->type(); })) return vars.front()->type(); } return nullptr; } std::pair Token::typeDecl(const Token * tok) { if (!tok) return {}; if (Token::simpleMatch(tok, "return")) { const Scope *scope = tok->scope(); if (!scope) return {}; const Function *function = scope->function; if (!function) return {}; return {function->retDef, function->returnDefEnd()}; } else if (Token::Match(tok, "%type%")) { return {tok, tok->next()}; } else if (Token::Match(tok, "%var%")) { const Variable *var = tok->variable(); if (!var) return {}; if (!var->typeStartToken() || !var->typeEndToken()) return {}; if (Token::simpleMatch(var->typeStartToken(), "auto")) { const Token * tok2 = var->declEndToken(); if (Token::Match(tok2, "; %varid% =", var->declarationId())) tok2 = tok2->tokAt(2); if (Token::simpleMatch(tok2, "=") && Token::Match(tok2->astOperand2(), "!!=") && tok != tok2->astOperand2()) { std::pair r = typeDecl(tok2->astOperand2()); if (r.first) return r; } } return {var->typeStartToken(), var->typeEndToken()->next()}; } else if (Token::Match(tok->previous(), "%name% (")) { const Function *function = tok->previous()->function(); if (!function) return {}; return {function->retDef, function->returnDefEnd()}; } else if (Token::simpleMatch(tok, "=")) { return Token::typeDecl(tok->astOperand1()); } else if (Token::simpleMatch(tok, ".")) { return Token::typeDecl(tok->astOperand2()); } else { const ::Type * t = typeOf(tok); if (!t || !t->classDef) return {}; return {t->classDef->next(), t->classDef->tokAt(2)}; } } std::string Token::typeStr(const Token* tok) { if (tok->valueType()) { const ValueType * vt = tok->valueType(); std::string ret = vt->str(); if (!ret.empty()) return ret; } std::pair r = Token::typeDecl(tok); if (!r.first || !r.second) return ""; return r.first->stringifyList(r.second, false); } void Token::scopeInfo(std::shared_ptr newScopeInfo) { mImpl->mScopeInfo = newScopeInfo; } std::shared_ptr Token::scopeInfo() const { return mImpl->mScopeInfo; } bool Token::hasKnownIntValue() const { if (!mImpl->mValues) return false; return std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value& value) { return value.isKnown() && value.isIntValue(); }); } bool Token::hasKnownValue() const { return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown)); } bool Token::hasKnownValue(ValueFlow::Value::ValueType t) const { return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) { return value.isKnown() && value.valueType == t; }); } bool Token::hasKnownSymbolicValue(const Token* tok) const { if (tok->exprId() == 0) return false; return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) { return value.isKnown() && value.isSymbolicValue() && value.tokvalue && value.tokvalue->exprId() == tok->exprId(); }); } const ValueFlow::Value* Token::getKnownValue(ValueFlow::Value::ValueType t) const { if (!mImpl->mValues) return nullptr; auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) { return value.isKnown() && value.valueType == t; }); return it == mImpl->mValues->end() ? nullptr : &*it; } bool Token::isImpossibleIntValue(const MathLib::bigint val) const { if (!mImpl->mValues) return false; for (const auto& v : *mImpl->mValues) { if (v.isIntValue() && v.isImpossible() && v.intvalue == val) return true; if (v.isIntValue() && v.bound == ValueFlow::Value::Bound::Lower && val > v.intvalue) return true; if (v.isIntValue() && v.bound == ValueFlow::Value::Bound::Upper && val < v.intvalue) return true; } return false; } const ValueFlow::Value* Token::getValue(const MathLib::bigint val) const { if (!mImpl->mValues) return nullptr; const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [=](const ValueFlow::Value& value) { return value.isIntValue() && !value.isImpossible() && value.intvalue == val; }); return it == mImpl->mValues->end() ? nullptr : &*it; } const ValueFlow::Value* Token::getMaxValue(bool condition, MathLib::bigint path) const { if (!mImpl->mValues) return nullptr; const ValueFlow::Value* ret = nullptr; for (const ValueFlow::Value& value : *mImpl->mValues) { if (!value.isIntValue()) continue; if (value.isImpossible()) continue; if (path > -0 && value.path != 0 && value.path != path) continue; if ((!ret || value.intvalue > ret->intvalue) && ((value.condition != nullptr) == condition)) ret = &value; } return ret; } const ValueFlow::Value* Token::getMovedValue() const { if (!mImpl->mValues) return nullptr; const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value& value) { return value.isMovedValue() && !value.isImpossible() && value.moveKind != ValueFlow::Value::MoveKind::NonMovedVariable; }); return it == mImpl->mValues->end() ? nullptr : &*it; } // cppcheck-suppress unusedFunction const ValueFlow::Value* Token::getContainerSizeValue(const MathLib::bigint val) const { if (!mImpl->mValues) return nullptr; const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [=](const ValueFlow::Value& value) { return value.isContainerSizeValue() && !value.isImpossible() && value.intvalue == val; }); return it == mImpl->mValues->end() ? nullptr : &*it; } TokenImpl::~TokenImpl() { delete mOriginalName; delete mValueType; delete mValues; if (mTemplateSimplifierPointers) for (auto *templateSimplifierPointer : *mTemplateSimplifierPointers) { templateSimplifierPointer->token(nullptr); } delete mTemplateSimplifierPointers; while (mCppcheckAttributes) { struct CppcheckAttributes *c = mCppcheckAttributes; mCppcheckAttributes = mCppcheckAttributes->next; delete c; } } void TokenImpl::setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) { struct CppcheckAttributes *attr = mCppcheckAttributes; while (attr && attr->type != type) attr = attr->next; if (attr) attr->value = value; else { attr = new CppcheckAttributes; attr->type = type; attr->value = value; attr->next = mCppcheckAttributes; mCppcheckAttributes = attr; } } bool TokenImpl::getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const { struct CppcheckAttributes *attr = mCppcheckAttributes; while (attr && attr->type != type) attr = attr->next; if (attr) *value = attr->value; return attr != nullptr; } Token* findTypeEnd(Token* tok) { while (Token::Match(tok, "%name%|.|::|*|&|&&|<|(|template|decltype|sizeof")) { if (Token::Match(tok, "(|<")) tok = tok->link(); if (!tok) return nullptr; tok = tok->next(); } return tok; } const Token* findTypeEnd(const Token* tok) { return findTypeEnd(const_cast(tok)); } Token* findLambdaEndScope(Token* tok) { if (!Token::simpleMatch(tok, "[")) return nullptr; tok = tok->link(); if (!Token::Match(tok, "] (|{")) return nullptr; tok = tok->linkAt(1); if (Token::simpleMatch(tok, "}")) return tok; if (Token::simpleMatch(tok, ") {")) return tok->linkAt(1); if (!Token::simpleMatch(tok, ")")) return nullptr; tok = tok->next(); while (Token::Match(tok, "mutable|constexpr|constval|noexcept|.")) { if (Token::simpleMatch(tok, "noexcept (")) tok = tok->linkAt(1); if (Token::simpleMatch(tok, ".")) { tok = findTypeEnd(tok); break; } tok = tok->next(); } if (Token::simpleMatch(tok, "{")) return tok->link(); return nullptr; } const Token* findLambdaEndScope(const Token* tok) { return findLambdaEndScope(const_cast(tok)); } cppcheck-2.7/lib/token.h000066400000000000000000001331641417746362400152270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef tokenH #define tokenH //--------------------------------------------------------------------------- #include "config.h" #include "mathlib.h" #include "valueflow.h" #include "templatesimplifier.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include class Enumerator; class Function; class Scope; class Settings; class Type; class ValueType; class Variable; class TokenList; class ConstTokenRange; class Token; /** * @brief This struct stores pointers to the front and back tokens of the list this token is in. */ struct TokensFrontBack { Token *front; Token *back; const TokenList* list; }; struct ScopeInfo2 { ScopeInfo2(const std::string &name_, const Token *bodyEnd_, const std::set &usingNamespaces_ = std::set()) : name(name_), bodyEnd(bodyEnd_), usingNamespaces(usingNamespaces_) {} std::string name; const Token * const bodyEnd; std::set usingNamespaces; }; struct TokenImpl { nonneg int mVarId; nonneg int mFileIndex; nonneg int mLineNumber; nonneg int mColumn; nonneg int mExprId; // AST.. Token *mAstOperand1; Token *mAstOperand2; Token *mAstParent; // symbol database information const Scope *mScope; union { const Function *mFunction; const Variable *mVariable; const ::Type* mType; const Enumerator *mEnumerator; }; /** * A value from 0-100 that provides a rough idea about where in the token * list this token is located. */ nonneg int mProgressValue; /** * Token index. Position in token list */ nonneg int mIndex; // original name like size_t std::string* mOriginalName; // ValueType ValueType *mValueType; // ValueFlow std::list* mValues; static const std::list mEmptyValueList; // Pointer to a template in the template simplifier std::set* mTemplateSimplifierPointers; // Pointer to the object representing this token's scope std::shared_ptr mScopeInfo; // __cppcheck_in_range__ struct CppcheckAttributes { enum Type {LOW,HIGH} type; MathLib::bigint value; struct CppcheckAttributes *next; }; struct CppcheckAttributes *mCppcheckAttributes; // For memoization, to speed up parsing of huge arrays #8897 enum class Cpp11init {UNKNOWN, CPP11INIT, NOINIT} mCpp11init; /** Bitfield bit count. */ unsigned char mBits; void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value); bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint *value) const; TokenImpl() : mVarId(0) , mFileIndex(0) , mLineNumber(0) , mColumn(0) , mExprId(0) , mAstOperand1(nullptr) , mAstOperand2(nullptr) , mAstParent(nullptr) , mScope(nullptr) , mFunction(nullptr) // Initialize whole union , mProgressValue(0) , mIndex(0) , mOriginalName(nullptr) , mValueType(nullptr) , mValues(nullptr) , mTemplateSimplifierPointers(nullptr) , mScopeInfo(nullptr) , mCppcheckAttributes(nullptr) , mCpp11init(Cpp11init::UNKNOWN) , mBits(0) {} ~TokenImpl(); }; /// @addtogroup Core /// @{ /** * @brief The token list that the TokenList generates is a linked-list of this class. * * Tokens are stored as strings. The "if", "while", etc are stored in plain text. * The reason the Token class is needed (instead of using the string class) is that some extra functionality is also needed for tokens: * - location of the token is stored (fileIndex, linenr, column) * - functions for classifying the token (isName, isNumber, isBoolean, isStandardType) * * The Token class also has other functions for management of token list, matching tokens, etc. */ class CPPCHECKLIB Token { private: TokensFrontBack* mTokensFrontBack; // Not implemented.. Token(const Token &); Token operator=(const Token &); public: enum Type { eVariable, eType, eFunction, eKeyword, eName, // Names: Variable (varId), Type (typeId, later), Function (FuncId, later), Language keyword, Name (unknown identifier) eNumber, eString, eChar, eBoolean, eLiteral, eEnumerator, // Literals: Number, String, Character, Boolean, User defined literal (C++11), Enumerator eArithmeticalOp, eComparisonOp, eAssignmentOp, eLogicalOp, eBitOp, eIncDecOp, eExtendedOp, // Operators: Arithmetical, Comparison, Assignment, Logical, Bitwise, ++/--, Extended eBracket, // {, }, <, >: < and > only if link() is set. Otherwise they are comparison operators. eLambda, // A function without a name eEllipsis, // "..." eOther, eNone }; explicit Token(TokensFrontBack *tokensFrontBack = nullptr); ~Token(); ConstTokenRange until(const Token * t) const; template void str(T&& s) { mStr = s; mImpl->mVarId = 0; update_property_info(); } /** * Concatenate two (quoted) strings. Automatically cuts of the last/first character. * Example: "hello ""world" -> "hello world". Used by the token simplifier. */ void concatStr(std::string const& b); const std::string &str() const { return mStr; } /** * Unlink and delete the next 'count' tokens. */ void deleteNext(nonneg int count = 1); /** * Unlink and delete the previous 'count' tokens. */ void deletePrevious(nonneg int count = 1); /** * Swap the contents of this token with the next token. */ void swapWithNext(); /** * @return token in given index, related to this token. * For example index 1 would return next token, and 2 * would return next from that one. */ const Token *tokAt(int index) const; Token *tokAt(int index) { return const_cast(const_cast(this)->tokAt(index)); } /** * @return the link to the token in given index, related to this token. * For example index 1 would return the link to next token. */ const Token *linkAt(int index) const; Token *linkAt(int index) { return const_cast(const_cast(this)->linkAt(index)); } /** * @return String of the token in given index, related to this token. * If that token does not exist, an empty string is being returned. */ const std::string &strAt(int index) const; /** * Match given token (or list of tokens) to a pattern list. * * Possible patterns * "someRandomText" If token contains "someRandomText". * @note Use Match() if you want to use flags in patterns * * The patterns can be also combined to compare to multiple tokens at once * by separating tokens with a space, e.g. * ") void {" will return true if first token is ')' next token * is "void" and token after that is '{'. If even one of the tokens does * not match its pattern, false is returned. * * @param tok List of tokens to be compared to the pattern * @param pattern The pattern against which the tokens are compared, * e.g. "const" or ") void {". * @return true if given token matches with given pattern * false if given token does not match with given pattern */ template static bool simpleMatch(const Token *tok, const char (&pattern)[count]) { return simpleMatch(tok, pattern, count-1); } static bool simpleMatch(const Token *tok, const char pattern[], size_t pattern_len); /** * Match given token (or list of tokens) to a pattern list. * * Possible patterns * - "%any%" any token * - "%assign%" a assignment operand * - "%bool%" true or false * - "%char%" Any token enclosed in '-character. * - "%comp%" Any token such that isComparisonOp() returns true. * - "%cop%" Any token such that isConstOp() returns true. * - "%name%" any token which is a name, variable or type e.g. "hello" or "int" * - "%num%" Any numeric token, e.g. "23" * - "%op%" Any token such that isOp() returns true. * - "%or%" A bitwise-or operator '|' * - "%oror%" A logical-or operator '||' * - "%type%" Anything that can be a variable type, e.g. "int", but not "delete". * - "%str%" Any token starting with "-character (C-string). * - "%var%" Match with token with varId > 0 * - "%varid%" Match with parameter varid * - "[abc]" Any of the characters 'a' or 'b' or 'c' * - "int|void|char" Any of the strings, int, void or char * - "int|void|char|" Any of the strings, int, void or char or empty string * - "!!else" No tokens or any token that is not "else". * - "someRandomText" If token contains "someRandomText". * * multi-compare patterns such as "int|void|char" can contain %%or%, %%oror% and %%op% * it is recommended to put such an %%cmd% as the first pattern. * For example: "%var%|%num%|)" means yes to a variable, a number or ')'. * * The patterns can be also combined to compare to multiple tokens at once * by separating tokens with a space, e.g. * ") const|void {" will return true if first token is ')' next token is either * "const" or "void" and token after that is '{'. If even one of the tokens does not * match its pattern, false is returned. * * @param tok List of tokens to be compared to the pattern * @param pattern The pattern against which the tokens are compared, * e.g. "const" or ") const|volatile| {". * @param varid if %%varid% is given in the pattern the Token::varId * will be matched against this argument * @return true if given token matches with given pattern * false if given token does not match with given pattern */ static bool Match(const Token *tok, const char pattern[], nonneg int varid = 0); /** * @return length of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string **/ static nonneg int getStrLength(const Token *tok); /** * @return array length of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string **/ static nonneg int getStrArraySize(const Token *tok); /** * @return sizeof of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string * @param settings Settings **/ static nonneg int getStrSize(const Token *tok, const Settings *const settings); /** * @return char of C-string at index (possible escaped "\\n") * * Should be called for %%str%% tokens only. * * @param tok token with C-string * @param index position of character **/ static std::string getCharAt(const Token *tok, MathLib::bigint index); const ValueType *valueType() const { return mImpl->mValueType; } void setValueType(ValueType *vt); const ValueType *argumentType() const { const Token *top = this; while (top && !Token::Match(top->astParent(), ",|(")) top = top->astParent(); return top ? top->mImpl->mValueType : nullptr; } Token::Type tokType() const { return mTokType; } void tokType(Token::Type t) { mTokType = t; const bool memoizedIsName = (mTokType == eName || mTokType == eType || mTokType == eVariable || mTokType == eFunction || mTokType == eKeyword || mTokType == eBoolean || mTokType == eEnumerator); // TODO: "true"/"false" aren't really a name... setFlag(fIsName, memoizedIsName); const bool memoizedIsLiteral = (mTokType == eNumber || mTokType == eString || mTokType == eChar || mTokType == eBoolean || mTokType == eLiteral || mTokType == eEnumerator); setFlag(fIsLiteral, memoizedIsLiteral); } bool isKeyword() const { return mTokType == eKeyword; } bool isName() const { return getFlag(fIsName); } bool isNameOnly() const { return mFlags == fIsName && mTokType == eName; } bool isUpperCaseName() const; bool isLiteral() const { return getFlag(fIsLiteral); } bool isNumber() const { return mTokType == eNumber; } bool isEnumerator() const { return mTokType == eEnumerator; } bool isOp() const { return (isConstOp() || isAssignmentOp() || mTokType == eIncDecOp); } bool isConstOp() const { return (isArithmeticalOp() || mTokType == eLogicalOp || mTokType == eComparisonOp || mTokType == eBitOp); } bool isExtendedOp() const { return isConstOp() || mTokType == eExtendedOp; } bool isArithmeticalOp() const { return mTokType == eArithmeticalOp; } bool isComparisonOp() const { return mTokType == eComparisonOp; } bool isAssignmentOp() const { return mTokType == eAssignmentOp; } bool isBoolean() const { return mTokType == eBoolean; } bool isIncDecOp() const { return mTokType == eIncDecOp; } bool isBinaryOp() const { return astOperand1() != nullptr && astOperand2() != nullptr; } bool isUnaryOp(const std::string &s) const { return s == mStr && astOperand1() != nullptr && astOperand2() == nullptr; } bool isUnaryPreOp() const; unsigned int flags() const { return mFlags; } void flags(const unsigned int flags_) { mFlags = flags_; } bool isUnsigned() const { return getFlag(fIsUnsigned); } void isUnsigned(const bool sign) { setFlag(fIsUnsigned, sign); } bool isSigned() const { return getFlag(fIsSigned); } void isSigned(const bool sign) { setFlag(fIsSigned, sign); } bool isPointerCompare() const { return getFlag(fIsPointerCompare); } void isPointerCompare(const bool b) { setFlag(fIsPointerCompare, b); } bool isLong() const { return getFlag(fIsLong); } void isLong(bool size) { setFlag(fIsLong, size); } bool isStandardType() const { return getFlag(fIsStandardType); } void isStandardType(const bool b) { setFlag(fIsStandardType, b); } bool isExpandedMacro() const { return getFlag(fIsExpandedMacro); } void isExpandedMacro(const bool m) { setFlag(fIsExpandedMacro, m); } bool isCast() const { return getFlag(fIsCast); } void isCast(bool c) { setFlag(fIsCast, c); } bool isAttributeConstructor() const { return getFlag(fIsAttributeConstructor); } void isAttributeConstructor(const bool ac) { setFlag(fIsAttributeConstructor, ac); } bool isAttributeDestructor() const { return getFlag(fIsAttributeDestructor); } void isAttributeDestructor(const bool value) { setFlag(fIsAttributeDestructor, value); } bool isAttributeUnused() const { return getFlag(fIsAttributeUnused); } void isAttributeUnused(bool unused) { setFlag(fIsAttributeUnused, unused); } bool isAttributeUsed() const { return getFlag(fIsAttributeUsed); } void isAttributeUsed(const bool unused) { setFlag(fIsAttributeUsed, unused); } bool isAttributePure() const { return getFlag(fIsAttributePure); } void isAttributePure(const bool value) { setFlag(fIsAttributePure, value); } bool isAttributeConst() const { return getFlag(fIsAttributeConst); } void isAttributeConst(bool value) { setFlag(fIsAttributeConst, value); } bool isAttributeNoreturn() const { return getFlag(fIsAttributeNoreturn); } void isAttributeNoreturn(const bool value) { setFlag(fIsAttributeNoreturn, value); } bool isAttributeNothrow() const { return getFlag(fIsAttributeNothrow); } void isAttributeNothrow(const bool value) { setFlag(fIsAttributeNothrow, value); } bool isAttributePacked() const { return getFlag(fIsAttributePacked); } void isAttributePacked(const bool value) { setFlag(fIsAttributePacked, value); } bool isAttributeNodiscard() const { return getFlag(fIsAttributeNodiscard); } void isAttributeNodiscard(const bool value) { setFlag(fIsAttributeNodiscard, value); } bool isAttributeMaybeUnused() const { return getFlag(fIsAttributeMaybeUnused); } void isAttributeMaybeUnused(const bool value) { setFlag(fIsAttributeMaybeUnused, value); } void setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) { mImpl->setCppcheckAttribute(type, value); } bool getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const { return mImpl->getCppcheckAttribute(type, value); } bool hasCppcheckAttributes() const { return nullptr != mImpl->mCppcheckAttributes; } bool isControlFlowKeyword() const { return getFlag(fIsControlFlowKeyword); } bool isOperatorKeyword() const { return getFlag(fIsOperatorKeyword); } void isOperatorKeyword(const bool value) { setFlag(fIsOperatorKeyword, value); } bool isComplex() const { return getFlag(fIsComplex); } void isComplex(const bool value) { setFlag(fIsComplex, value); } bool isEnumType() const { return getFlag(fIsEnumType); } void isEnumType(const bool value) { setFlag(fIsEnumType, value); } bool isAtAddress() const { return getFlag(fAtAddress); } void isAtAddress(bool b) { setFlag(fAtAddress, b); } bool isIncompleteVar() const { return getFlag(fIncompleteVar); } void isIncompleteVar(bool b) { setFlag(fIncompleteVar, b); } bool isConstexpr() const { return getFlag(fConstexpr); } void isConstexpr(bool b) { setFlag(fConstexpr, b); } bool isExternC() const { return getFlag(fExternC); } void isExternC(bool b) { setFlag(fExternC, b); } bool isSplittedVarDeclComma() const { return getFlag(fIsSplitVarDeclComma); } void isSplittedVarDeclComma(bool b) { setFlag(fIsSplitVarDeclComma, b); } bool isSplittedVarDeclEq() const { return getFlag(fIsSplitVarDeclEq); } void isSplittedVarDeclEq(bool b) { setFlag(fIsSplitVarDeclEq, b); } bool isImplicitInt() const { return getFlag(fIsImplicitInt); } void isImplicitInt(bool b) { setFlag(fIsImplicitInt, b); } bool isInline() const { return getFlag(fIsInline); } void isInline(bool b) { setFlag(fIsInline, b); } bool isTemplate() const { return getFlag(fIsTemplate); } void isTemplate(bool b) { setFlag(fIsTemplate, b); } bool isBitfield() const { return mImpl->mBits > 0; } unsigned char bits() const { return mImpl->mBits; } std::set* templateSimplifierPointers() const { return mImpl->mTemplateSimplifierPointers; } void templateSimplifierPointer(TemplateSimplifier::TokenAndName* tokenAndName) { if (!mImpl->mTemplateSimplifierPointers) mImpl->mTemplateSimplifierPointers = new std::set; mImpl->mTemplateSimplifierPointers->insert(tokenAndName); } void setBits(const unsigned char b) { mImpl->mBits = b; } bool isUtf8() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u8")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u8"))); } bool isUtf16() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u"))); } bool isUtf32() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "U")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "U"))); } bool isCChar() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "") && mStr.length() == 3)); } bool isCMultiChar() const { return (((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "")) && (mStr.length() > 3)); } /** * @brief Is current token a template argument? * * Original code: * * template struct S { * C x; * }; * S s; * * Resulting code: * * struct S { * int x ; // <- "int" is a template argument * } * S s; */ bool isTemplateArg() const { return getFlag(fIsTemplateArg); } void isTemplateArg(const bool value) { setFlag(fIsTemplateArg, value); } template static const Token *findsimplematch(const Token * const startTok, const char (&pattern)[count]) { return findsimplematch(startTok, pattern, count-1); } static const Token *findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len); template static const Token *findsimplematch(const Token * const startTok, const char (&pattern)[count], const Token * const end) { return findsimplematch(startTok, pattern, count-1, end); } static const Token *findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len, const Token * const end); static const Token *findmatch(const Token * const startTok, const char pattern[], const nonneg int varId = 0); static const Token *findmatch(const Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId = 0); template static Token *findsimplematch(Token * const startTok, const char (&pattern)[count]) { return findsimplematch(startTok, pattern, count-1); } static Token *findsimplematch(Token * const startTok, const char pattern[], size_t pattern_len) { return const_cast(findsimplematch(const_cast(startTok), pattern, pattern_len)); } template static Token *findsimplematch(Token * const startTok, const char (&pattern)[count], const Token * const end) { return findsimplematch(startTok, pattern, count-1, end); } static Token *findsimplematch(Token * const startTok, const char pattern[], size_t pattern_len, const Token * const end) { return const_cast(findsimplematch(const_cast(startTok), pattern, pattern_len, end)); } static Token *findmatch(Token * const startTok, const char pattern[], const nonneg int varId = 0) { return const_cast(findmatch(const_cast(startTok), pattern, varId)); } static Token *findmatch(Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId = 0) { return const_cast(findmatch(const_cast(startTok), pattern, end, varId)); } /** * Needle is build from multiple alternatives. If one of * them is equal to haystack, return value is 1. If there * are no matches, but one alternative to needle is empty * string, return value is 0. If needle was not found, return * value is -1. * * @param tok Current token (needle) * @param haystack e.g. "one|two" or "|one|two" * @param varid optional varid of token * @return 1 if needle is found from the haystack * 0 if needle was empty string * -1 if needle was not found */ static int multiCompare(const Token *tok, const char *haystack, nonneg int varid); nonneg int fileIndex() const { return mImpl->mFileIndex; } void fileIndex(nonneg int indexOfFile) { mImpl->mFileIndex = indexOfFile; } nonneg int linenr() const { return mImpl->mLineNumber; } void linenr(nonneg int lineNumber) { mImpl->mLineNumber = lineNumber; } nonneg int column() const { return mImpl->mColumn; } void column(nonneg int c) { mImpl->mColumn = c; } Token *next() const { return mNext; } /** * Delete tokens between begin and end. E.g. if begin = 1 * and end = 5, tokens 2,3 and 4 would be erased. * * @param begin Tokens after this will be erased. * @param end Tokens before this will be erased. */ static void eraseTokens(Token *begin, const Token *end); /** * Insert new token after this token. This function will handle * relations between next and previous token also. * @param tokenStr String for the new token. * @param originalNameStr String used for Token::originalName(). * @param prepend Insert the new token before this token when it's not * the first one on the tokens list. */ Token* insertToken(const std::string& tokenStr, const std::string& originalNameStr = emptyString, bool prepend = false); Token* insertTokenBefore(const std::string& tokenStr, const std::string& originalNameStr = emptyString) { return insertToken(tokenStr, originalNameStr, true); } Token *previous() const { return mPrevious; } nonneg int varId() const { return mImpl->mVarId; } void varId(nonneg int id) { mImpl->mVarId = id; if (id != 0) { tokType(eVariable); isStandardType(false); } else { update_property_info(); } } nonneg int exprId() const { if (mImpl->mExprId) return mImpl->mExprId; return mImpl->mVarId; } void exprId(nonneg int id) { mImpl->mExprId = id; } /** * For debugging purposes, prints token and all tokens * followed by it. * @param title Title for the printout or use default parameter or 0 * for no title. */ void printOut(const char *title = nullptr) const; /** * For debugging purposes, prints token and all tokens * followed by it. * @param title Title for the printout or use default parameter or 0 * for no title. * @param fileNames Prints out file name instead of file index. * File index should match the index of the string in this vector. */ void printOut(const char *title, const std::vector &fileNames) const; /** * print out tokens - used for debugging */ void printLines(int lines=5) const; /** * Replace token replaceThis with tokens between start and end, * including start and end. The replaceThis token is deleted. * @param replaceThis This token will be deleted. * @param start This will be in the place of replaceThis * @param end This is also in the place of replaceThis */ static void replace(Token *replaceThis, Token *start, Token *end); struct stringifyOptions { bool varid = false; bool exprid = false; bool idtype = false; // distinguish varid / exprid bool attributes = false; bool macro = false; bool linenumbers = false; bool linebreaks = false; bool files = false; static stringifyOptions forDebug() { stringifyOptions options; options.attributes = true; options.macro = true; options.linenumbers = true; options.linebreaks = true; options.files = true; return options; } static stringifyOptions forDebugVarId() { stringifyOptions options = forDebug(); options.varid = true; return options; } static stringifyOptions forDebugExprId() { stringifyOptions options = forDebug(); options.exprid = true; return options; } static stringifyOptions forPrintOut() { stringifyOptions options = forDebug(); options.exprid = true; options.varid = true; options.idtype = true; return options; } }; std::string stringify(const stringifyOptions& options) const; /** * Stringify a token * @param varid Print varids. (Style: "varname\@id") * @param attributes Print attributes of tokens like "unsigned" in front of it. * @param macro Prints $ in front of the token if it was expanded from a macro. */ std::string stringify(bool varid, bool attributes, bool macro) const; std::string stringifyList(const stringifyOptions& options, const std::vector* fileNames = nullptr, const Token* end = nullptr) const; std::string stringifyList(const Token* end, bool attributes = true) const; std::string stringifyList(bool varid = false) const; /** * Stringify a list of token, from current instance on. * @param varid Print varids. (Style: "varname\@id") * @param attributes Print attributes of tokens like "unsigned" in front of it. * @param linenumbers Print line number in front of each line * @param linebreaks Insert "\\n" into string when line number changes * @param files print Files as numbers or as names (if fileNames is given) * @param fileNames Vector of filenames. Used (if given) to print filenames as strings instead of numbers. * @param end Stringification ends before this token is reached. 0 to stringify until end of list. * @return Stringified token list as a string */ std::string stringifyList(bool varid, bool attributes, bool linenumbers, bool linebreaks, bool files, const std::vector* fileNames = nullptr, const Token* end = nullptr) const; /** * Remove the contents for this token from the token list. * * The contents are replaced with the contents of the next token and * the next token is unlinked and deleted from the token list. * * So this token will still be valid after the 'deleteThis()'. */ void deleteThis(); /** * Create link to given token * @param linkToToken The token where this token should link * to. */ void link(Token *linkToToken) { mLink = linkToToken; if (mStr == "<" || mStr == ">") update_property_info(); } /** * Return token where this token links to. * Supported links are: * "{" <-> "}" * "(" <-> ")" * "[" <-> "]" * * @return The token where this token links to. */ Token *link() const { return mLink; } /** * Associate this token with given scope * @param s Scope to be associated */ void scope(const Scope *s) { mImpl->mScope = s; } /** * @return a pointer to the scope containing this token. */ const Scope *scope() const { return mImpl->mScope; } /** * Associate this token with given function * @param f Function to be associated */ void function(const Function *f); /** * @return a pointer to the Function associated with this token. */ const Function *function() const { return mTokType == eFunction || mTokType == eLambda ? mImpl->mFunction : nullptr; } /** * Associate this token with given variable * @param v Variable to be associated */ void variable(const Variable *v) { mImpl->mVariable = v; if (v || mImpl->mVarId) tokType(eVariable); else if (mTokType == eVariable) tokType(eName); } /** * @return a pointer to the variable associated with this token. */ const Variable *variable() const { return mTokType == eVariable ? mImpl->mVariable : nullptr; } /** * Associate this token with given type * @param t Type to be associated */ void type(const ::Type *t); /** * @return a pointer to the type associated with this token. */ const ::Type *type() const { return mTokType == eType ? mImpl->mType : nullptr; } static const ::Type* typeOf(const Token* tok, const Token** typeTok = nullptr); static std::pair typeDecl(const Token * tok); static std::string typeStr(const Token* tok); /** * @return a pointer to the Enumerator associated with this token. */ const Enumerator *enumerator() const { return mTokType == eEnumerator ? mImpl->mEnumerator : nullptr; } /** * Associate this token with given enumerator * @param e Enumerator to be associated */ void enumerator(const Enumerator *e) { mImpl->mEnumerator = e; if (e) tokType(eEnumerator); else if (mTokType == eEnumerator) tokType(eName); } /** * Links two elements against each other. **/ static void createMutualLinks(Token *begin, Token *end); /** * This can be called only for tokens that are strings, else * the assert() is called. If Token is e.g. '"hello"', this will return * 'hello' (removing the double quotes). * @return String value */ std::string strValue() const; /** * Move srcStart and srcEnd tokens and all tokens between them * into new a location. Only links between tokens are changed. * @param srcStart This is the first token to be moved * @param srcEnd The last token to be moved * @param newLocation srcStart will be placed after this token. */ static void move(Token *srcStart, Token *srcEnd, Token *newLocation); /** Get progressValue (0 - 100) */ nonneg int progressValue() const { return mImpl->mProgressValue; } /** Calculate progress values for all tokens */ static void assignProgressValues(Token *tok); /** * @return the first token of the next argument. Does only work on argument * lists. Requires that Tokenizer::createLinks2() has been called before. * Returns 0, if there is no next argument. */ Token* nextArgument() const; /** * @return the first token of the next argument. Does only work on argument * lists. Should be used only before Tokenizer::createLinks2() was called. * Returns 0, if there is no next argument. */ Token* nextArgumentBeforeCreateLinks2() const; /** * @return the first token of the next template argument. Does only work on template argument * lists. Requires that Tokenizer::createLinks2() has been called before. * Returns 0, if there is no next argument. */ Token* nextTemplateArgument() const; /** * Returns the closing bracket of opening '<'. Should only be used if link() * is unavailable. * @return closing '>', ')', ']' or '}'. if no closing bracket is found, NULL is returned */ const Token* findClosingBracket() const; Token* findClosingBracket(); const Token* findOpeningBracket() const; Token* findOpeningBracket(); /** * @return the original name. */ const std::string & originalName() const { return mImpl->mOriginalName ? *mImpl->mOriginalName : emptyString; } const std::list& values() const { return mImpl->mValues ? *mImpl->mValues : TokenImpl::mEmptyValueList; } /** * Sets the original name. */ template void originalName(T&& name) { if (!mImpl->mOriginalName) mImpl->mOriginalName = new std::string(name); else *mImpl->mOriginalName = name; } bool hasKnownIntValue() const; bool hasKnownValue() const; bool hasKnownValue(ValueFlow::Value::ValueType t) const; bool hasKnownSymbolicValue(const Token* tok) const; const ValueFlow::Value* getKnownValue(ValueFlow::Value::ValueType t) const; MathLib::bigint getKnownIntValue() const { return mImpl->mValues->front().intvalue; } bool isImpossibleIntValue(const MathLib::bigint val) const; const ValueFlow::Value* getValue(const MathLib::bigint val) const; const ValueFlow::Value* getMaxValue(bool condition, MathLib::bigint path = 0) const; const ValueFlow::Value* getMovedValue() const; const ValueFlow::Value * getValueLE(const MathLib::bigint val, const Settings *settings) const; const ValueFlow::Value * getValueGE(const MathLib::bigint val, const Settings *settings) const; const ValueFlow::Value * getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const; const ValueFlow::Value* getContainerSizeValue(const MathLib::bigint val) const; const Token *getValueTokenMaxStrLength() const; const Token *getValueTokenMinStrSize(const Settings *settings) const; /** Add token value. Return true if value is added. */ bool addValue(const ValueFlow::Value &value); void removeValues(std::function pred) { if (mImpl->mValues) mImpl->mValues->remove_if(pred); } nonneg int index() const { return mImpl->mIndex; } void assignIndexes(); private: void next(Token *nextToken) { mNext = nextToken; } void previous(Token *previousToken) { mPrevious = previousToken; } /** used by deleteThis() to take data from token to delete */ void takeData(Token *fromToken); /** * Works almost like strcmp() except returns only true or false and * if str has empty space ' ' character, that character is handled * as if it were '\\0' */ static bool firstWordEquals(const char *str, const char *word); /** * Works almost like strchr() except * if str has empty space ' ' character, that character is handled * as if it were '\\0' */ static const char *chrInFirstWord(const char *str, char c); std::string mStr; Token *mNext; Token *mPrevious; Token *mLink; enum : uint64_t { fIsUnsigned = (1 << 0), fIsSigned = (1 << 1), fIsPointerCompare = (1 << 2), fIsLong = (1 << 3), fIsStandardType = (1 << 4), fIsExpandedMacro = (1 << 5), fIsCast = (1 << 6), fIsAttributeConstructor = (1 << 7), // __attribute__((constructor)) __attribute__((constructor(priority))) fIsAttributeDestructor = (1 << 8), // __attribute__((destructor)) __attribute__((destructor(priority))) fIsAttributeUnused = (1 << 9), // __attribute__((unused)) fIsAttributePure = (1 << 10), // __attribute__((pure)) fIsAttributeConst = (1 << 11), // __attribute__((const)) fIsAttributeNoreturn = (1 << 12), // __attribute__((noreturn)), __declspec(noreturn) fIsAttributeNothrow = (1 << 13), // __attribute__((nothrow)), __declspec(nothrow) fIsAttributeUsed = (1 << 14), // __attribute__((used)) fIsAttributePacked = (1 << 15), // __attribute__((packed)) fIsAttributeMaybeUnused = (1 << 16), // [[maybe_unsed]] fIsControlFlowKeyword = (1 << 17), // if/switch/while/... fIsOperatorKeyword = (1 << 18), // operator=, etc fIsComplex = (1 << 19), // complex/_Complex type fIsEnumType = (1 << 20), // enumeration type fIsName = (1 << 21), fIsLiteral = (1 << 22), fIsTemplateArg = (1 << 23), fIsAttributeNodiscard = (1 << 24), // __attribute__ ((warn_unused_result)), [[nodiscard]] fAtAddress = (1 << 25), // @ 0x4000 fIncompleteVar = (1 << 26), fConstexpr = (1 << 27), fExternC = (1 << 28), fIsSplitVarDeclComma = (1 << 29), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;') fIsSplitVarDeclEq = (1 << 30), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;') fIsImplicitInt = (1U << 31), // Is "int" token implicitly added? fIsInline = (1ULL << 32), // Is this a inline type fIsTemplate = (1ULL << 33) }; Token::Type mTokType; uint64_t mFlags; TokenImpl *mImpl; /** * Get specified flag state. * @param flag_ flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(uint64_t flag_) const { return ((mFlags & flag_) != 0); } /** * Set specified flag state. * @param flag_ flag to set state * @param state_ new state of flag */ void setFlag(uint64_t flag_, bool state_) { mFlags = state_ ? mFlags | flag_ : mFlags & ~flag_; } /** Updates internal property cache like _isName or _isBoolean. Called after any mStr() modification. */ void update_property_info(); /** Update internal property cache about isStandardType() */ void update_property_isStandardType(); /** Update internal property cache about string and char literals */ void update_property_char_string_literal(); /** Internal helper function to avoid excessive string allocations */ void astStringVerboseRecursive(std::string& ret, const nonneg int indent1 = 0, const nonneg int indent2 = 0) const; public: void astOperand1(Token *tok); void astOperand2(Token *tok); void astParent(Token* tok); Token * astOperand1() { return mImpl->mAstOperand1; } const Token * astOperand1() const { return mImpl->mAstOperand1; } Token * astOperand2() { return mImpl->mAstOperand2; } const Token * astOperand2() const { return mImpl->mAstOperand2; } Token * astParent() { return mImpl->mAstParent; } const Token * astParent() const { return mImpl->mAstParent; } Token * astSibling() { if (!astParent()) return nullptr; if (this == astParent()->astOperand1()) return astParent()->astOperand2(); else if (this == astParent()->astOperand2()) return astParent()->astOperand1(); return nullptr; } const Token * astSibling() const { if (!astParent()) return nullptr; if (this == astParent()->astOperand1()) return astParent()->astOperand2(); else if (this == astParent()->astOperand2()) return astParent()->astOperand1(); return nullptr; } Token *astTop() { Token *ret = this; while (ret->mImpl->mAstParent) ret = ret->mImpl->mAstParent; return ret; } const Token *astTop() const { const Token *ret = this; while (ret->mImpl->mAstParent) ret = ret->mImpl->mAstParent; return ret; } std::pair findExpressionStartEndTokens() const; /** * Is current token a calculation? Only true for operands. * For '*' and '&' tokens it is looked up if this is a * dereference or address-of. A dereference or address-of is not * counted as a calculation. * @return returns true if current token is a calculation */ bool isCalculation() const; void clearAst() { mImpl->mAstOperand1 = mImpl->mAstOperand2 = mImpl->mAstParent = nullptr; } void clearValueFlow() { delete mImpl->mValues; mImpl->mValues = nullptr; } std::string astString(const char *sep = "") const { std::string ret; if (mImpl->mAstOperand1) ret = mImpl->mAstOperand1->astString(sep); if (mImpl->mAstOperand2) ret += mImpl->mAstOperand2->astString(sep); return ret + sep + mStr; } std::string astStringVerbose() const; std::string astStringZ3() const; std::string expressionString() const; void printAst(bool verbose, bool xml, const std::vector &fileNames, std::ostream &out) const; void printValueFlow(bool xml, std::ostream &out) const; void scopeInfo(std::shared_ptr newScopeInfo); std::shared_ptr scopeInfo() const; void setCpp11init(bool cpp11init) const { mImpl->mCpp11init=cpp11init ? TokenImpl::Cpp11init::CPP11INIT : TokenImpl::Cpp11init::NOINIT; } TokenImpl::Cpp11init isCpp11init() const { return mImpl->mCpp11init; } }; Token* findTypeEnd(Token* tok); const Token* findTypeEnd(const Token* tok); Token* findLambdaEndScope(Token* tok); const Token* findLambdaEndScope(const Token* tok); /// @} //--------------------------------------------------------------------------- #endif // tokenH cppcheck-2.7/lib/tokenize.cpp000066400000000000000000016755171417746362400163100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "tokenize.h" #include "check.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "platform.h" #include "preprocessor.h" #include "settings.h" #include "standards.h" #include "summaries.h" #include "symboldatabase.h" #include "templatesimplifier.h" #include "timer.h" #include "token.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- namespace { // local struct used in setVarId // in order to store information about the scope struct VarIdScopeInfo { VarIdScopeInfo() : isExecutable(false), isStructInit(false), isEnum(false), startVarid(0) {} VarIdScopeInfo(bool isExecutable, bool isStructInit, bool isEnum, nonneg int startVarid) : isExecutable(isExecutable), isStructInit(isStructInit), isEnum(isEnum), startVarid(startVarid) {} const bool isExecutable; const bool isStructInit; const bool isEnum; const nonneg int startVarid; }; } /** Return whether tok is the "{" that starts an enumerator list */ static bool isEnumStart(const Token* tok) { if (!tok || tok->str() != "{") return false; return (tok->strAt(-1) == "enum") || (tok->strAt(-2) == "enum"); } template static void skipEnumBody(T **tok) { T *defStart = *tok; while (Token::Match(defStart, "%name%|::|:")) defStart = defStart->next(); if (defStart && defStart->str() == "{") *tok = defStart->link()->next(); } const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &endsWith) const { return Tokenizer::isFunctionHead(tok, endsWith, isCPP()); } const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &endsWith, bool cpp) { if (!tok) return nullptr; if (tok->str() == "(") tok = tok->link(); if (Token::Match(tok, ") ;|{|[")) { tok = tok->next(); while (tok && tok->str() == "[" && tok->link()) { if (endsWith.find(tok->str()) != std::string::npos) return tok; tok = tok->link()->next(); } return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; } if (cpp && tok->str() == ")") { tok = tok->next(); while (Token::Match(tok, "const|noexcept|override|final|volatile|&|&& !!(") || (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) tok = tok->next(); if (tok && tok->str() == ")") tok = tok->next(); while (tok && tok->str() == "[") tok = tok->link()->next(); if (Token::Match(tok, "throw|noexcept (")) tok = tok->linkAt(1)->next(); if (Token::Match(tok, "%name% (") && tok->isUpperCaseName()) tok = tok->linkAt(1)->next(); if (tok && tok->originalName() == "->") { // trailing return type for (tok = tok->next(); tok && !Token::Match(tok, ";|{|override|final"); tok = tok->next()) if (tok->link() && Token::Match(tok, "<|[|(")) tok = tok->link(); } while (Token::Match(tok, "override|final !!(") || (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) tok = tok->next(); if (Token::Match(tok, "= 0|default|delete ;")) tok = tok->tokAt(2); return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; } return nullptr; } /** * is tok the start brace { of a class, struct, union, or enum */ static bool isClassStructUnionEnumStart(const Token * tok) { if (!Token::Match(tok->previous(), "class|struct|union|enum|%name%|>|>> {")) return false; const Token * tok2 = tok->previous(); while (tok2 && !Token::Match(tok2, "class|struct|union|enum|{|}|;")) tok2 = tok2->previous(); return Token::Match(tok2, "class|struct|union|enum"); } //--------------------------------------------------------------------------- Tokenizer::Tokenizer() : list(nullptr), mSettings(nullptr), mErrorLogger(nullptr), mSymbolDatabase(nullptr), mTemplateSimplifier(nullptr), mVarId(0), mUnnamedCount(0), mCodeWithTemplates(false), //is there any templates? mTimerResults(nullptr) #ifdef MAXTIME , mMaxTime(std::time(0) + MAXTIME) #endif , mPreprocessor(nullptr) {} Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger) : list(settings), mSettings(settings), mErrorLogger(errorLogger), mSymbolDatabase(nullptr), mTemplateSimplifier(nullptr), mVarId(0), mUnnamedCount(0), mCodeWithTemplates(false), //is there any templates? mTimerResults(nullptr) #ifdef MAXTIME ,mMaxTime(std::time(0) + MAXTIME) #endif , mPreprocessor(nullptr) { // make sure settings are specified assert(mSettings); mTemplateSimplifier = new TemplateSimplifier(this); } Tokenizer::~Tokenizer() { delete mSymbolDatabase; delete mTemplateSimplifier; } //--------------------------------------------------------------------------- // SizeOfType - gives the size of a type //--------------------------------------------------------------------------- nonneg int Tokenizer::sizeOfType(const Token *type) const { if (!type || type->str().empty()) return 0; if (type->tokType() == Token::eString) return Token::getStrLength(type) + 1U; const std::map::const_iterator it = mTypeSize.find(type->str()); if (it == mTypeSize.end()) { const Library::PodType* podtype = mSettings->library.podtype(type->str()); if (!podtype) return 0; return podtype->size; } else if (type->isLong()) { if (type->str() == "double") return mSettings->sizeof_long_double; else if (type->str() == "long") return mSettings->sizeof_long_long; } return it->second; } //--------------------------------------------------------------------------- // check if this statement is a duplicate definition bool Tokenizer::duplicateTypedef(Token **tokPtr, const Token *name, const Token *typeDef) const { // check for an end of definition const Token * tok = *tokPtr; if (tok && Token::Match(tok->next(), ";|,|[|=|)|>|(|{")) { const Token * end = tok->next(); if (end->str() == "[") { if (!end->link()) syntaxError(end); // invalid code end = end->link()->next(); } else if (end->str() == ",") { // check for derived class if (Token::Match(tok->previous(), "public|private|protected")) return false; // find end of definition while (end && end->next() && !Token::Match(end->next(), ";|)|>")) { if (end->next()->str() == "(") end = end->linkAt(1); end = (end)?end->next():nullptr; } if (end) end = end->next(); } else if (end->str() == "(") { if (tok->previous()->str().compare(0, 8, "operator") == 0) { // conversion operator return false; } else if (tok->previous()->str() == "typedef") { // typedef of function returning this type return false; } else if (Token::Match(tok->previous(), "public:|private:|protected:")) { return false; } else if (tok->previous()->str() == ">") { if (!Token::Match(tok->tokAt(-2), "%type%")) return false; if (!Token::Match(tok->tokAt(-3), ",|<")) return false; *tokPtr = end->link(); return true; } } if (end) { if (Token::simpleMatch(end, ") {")) { // function parameter ? // look backwards if (Token::Match(tok->previous(), "%type%") && !Token::Match(tok->previous(), "return|new|const|struct")) { // duplicate definition so skip entire function *tokPtr = end->next()->link(); return true; } } else if (end->str() == ">") { // template parameter ? // look backwards if (Token::Match(tok->previous(), "%type%") && !Token::Match(tok->previous(), "return|new|const|volatile")) { // duplicate definition so skip entire template while (end && end->str() != "{") end = end->next(); if (end) { *tokPtr = end->link(); return true; } } } else { // look backwards if (Token::Match(tok->previous(), "typedef|}|>") || (end->str() == ";" && tok->previous()->str() == ",") || (tok->previous()->str() == "*" && tok->next()->str() != "(") || (Token::Match(tok->previous(), "%type%") && (!Token::Match(tok->previous(), "return|new|const|friend|public|private|protected|throw|extern") && !Token::simpleMatch(tok->tokAt(-2), "friend class")))) { // scan backwards for the end of the previous statement while (tok && tok->previous() && !Token::Match(tok->previous(), ";|{")) { if (tok->previous()->str() == "}") { tok = tok->previous()->link(); } else if (tok->previous()->str() == "typedef") { return true; } else if (tok->previous()->str() == "enum") { return true; } else if (tok->previous()->str() == "struct") { if (tok->strAt(-2) == "typedef" && tok->next()->str() == "{" && typeDef->strAt(3) != "{") { // declaration after forward declaration return true; } else if (tok->next()->str() == "{") { return true; } else if (Token::Match(tok->next(), ")|*")) { return true; } else if (tok->next()->str() == name->str()) { return true; } else if (tok->next()->str() != ";") { return true; } else { return false; } } else if (tok->previous()->str() == "union") { if (tok->next()->str() != ";") { return true; } else { return false; } } else if (isCPP() && tok->previous()->str() == "class") { if (tok->next()->str() != ";") { return true; } else { return false; } } if (tok) tok = tok->previous(); } if ((*tokPtr)->strAt(1) != "(" || !Token::Match((*tokPtr)->linkAt(1), ") .|(|[")) return true; } } } } return false; } void Tokenizer::unsupportedTypedef(const Token *tok) const { if (!mSettings->debugwarnings) return; std::ostringstream str; const Token *tok1 = tok; int level = 0; while (tok) { if (level == 0 && tok->str() == ";") break; else if (tok->str() == "{") ++level; else if (tok->str() == "}") { if (level == 0) break; --level; } if (tok != tok1) str << " "; str << tok->str(); tok = tok->next(); } if (tok) str << " ;"; reportError(tok1, Severity::debug, "simplifyTypedef", "Failed to parse \'" + str.str() + "\'. The checking continues anyway."); } Token * Tokenizer::deleteInvalidTypedef(Token *typeDef) { Token *tok = nullptr; // remove typedef but leave ; while (typeDef->next()) { if (typeDef->next()->str() == ";") { typeDef->deleteNext(); break; } else if (typeDef->next()->str() == "{") Token::eraseTokens(typeDef, typeDef->linkAt(1)); else if (typeDef->next()->str() == "}") break; typeDef->deleteNext(); } if (typeDef != list.front()) { tok = typeDef->previous(); tok->deleteNext(); } else { list.front()->deleteThis(); tok = list.front(); } return tok; } namespace { struct Space { Space() : bodyEnd(nullptr), bodyEnd2(nullptr), isNamespace(false) {} std::string className; const Token * bodyEnd; // for body contains typedef define const Token * bodyEnd2; // for body contains typedef using bool isNamespace; std::set recordTypes; }; } static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount) { std::string name; bool isConst = false; Token *tok1 = tok->next(); // skip const if present if (tok1->str() == "const") { tok1->deleteThis(); isConst = true; } // skip "class|struct|union|enum" tok1 = tok1->next(); const bool hasName = Token::Match(tok1, "%name%"); // skip name if (hasName) { name = tok1->str(); tok1 = tok1->next(); } // skip base classes if present if (tok1->str() == ":") { tok1 = tok1->next(); while (tok1 && tok1->str() != "{") tok1 = tok1->next(); if (!tok1) return nullptr; } // skip to end tok1 = tok1->link(); if (!hasName) { // unnamed if (tok1->next()) { // use typedef name if available if (Token::Match(tok1->next(), "%type%")) name = tok1->next()->str(); else // create a unique name name = "Unnamed" + MathLib::toString((*unnamedCount)++); tok->next()->insertToken(name); } else return nullptr; } tok1->insertToken(";"); tok1 = tok1->next(); if (tok1->next() && tok1->next()->str() == ";" && tok1->previous()->str() == "}") { tok->deleteThis(); tok1->deleteThis(); return nullptr; } else { tok1->insertToken("typedef"); tok1 = tok1->next(); Token * tok3 = tok1; if (isConst) { tok1->insertToken("const"); tok1 = tok1->next(); } tok1->insertToken(tok->next()->str()); // struct, union or enum tok1 = tok1->next(); tok1->insertToken(name); tok->deleteThis(); tok = tok3; } return tok; } /* This function is called when processing function related typedefs. * If simplifyTypedef generates an "Internal Error" message and the * code that generated it deals in some way with functions, then this * function will probably need to be extended to handle a new function * related pattern */ Token *Tokenizer::processFunc(Token *tok2, bool inOperator) const { if (tok2->next() && tok2->next()->str() != ")" && tok2->next()->str() != ",") { // skip over tokens for some types of canonicalization if (Token::Match(tok2->next(), "( * %type% ) (")) tok2 = tok2->linkAt(5); else if (Token::Match(tok2->next(), "* ( * %type% ) (")) tok2 = tok2->linkAt(6); else if (Token::Match(tok2->next(), "* ( * %type% ) ;")) tok2 = tok2->tokAt(5); else if (Token::Match(tok2->next(), "* ( %type% [") && Token::Match(tok2->linkAt(4), "] ) ;|=")) tok2 = tok2->linkAt(4)->next(); else if (Token::Match(tok2->next(), "* ( * %type% (")) tok2 = tok2->linkAt(5)->next(); else if (Token::simpleMatch(tok2->next(), "* [") && Token::simpleMatch(tok2->linkAt(2), "] ;")) tok2 = tok2->next(); else { if (tok2->next()->str() == "(") tok2 = tok2->next()->link(); else if (!inOperator && !Token::Match(tok2->next(), "[|>|;")) { tok2 = tok2->next(); while (Token::Match(tok2, "*|&") && !Token::Match(tok2->next(), ")|>")) tok2 = tok2->next(); // skip over namespace while (Token::Match(tok2, "%name% ::")) tok2 = tok2->tokAt(2); if (!tok2) return nullptr; if (tok2->str() == "(" && tok2->link()->next() && tok2->link()->next()->str() == "(") { tok2 = tok2->link(); if (tok2->next()->str() == "(") tok2 = tok2->next()->link(); } // skip over typedef parameter if (tok2->next() && tok2->next()->str() == "(") { tok2 = tok2->next()->link(); if (!tok2->next()) syntaxError(tok2); if (tok2->next()->str() == "(") tok2 = tok2->next()->link(); } } } } return tok2; } void Tokenizer::simplifyUsingToTypedef() { if (!isCPP() || mSettings->standards.cpp < Standards::CPP11) return; for (Token *tok = list.front(); tok; tok = tok->next()) { // using a::b; => typedef a::b b; if ((Token::Match(tok, "[;{}] using %name% :: %name% ::|;") && !tok->tokAt(2)->isKeyword()) || (Token::Match(tok, "[;{}] using :: %name% :: %name% ::|;") && !tok->tokAt(3)->isKeyword())) { Token *endtok = tok->tokAt(5); if (Token::Match(endtok, "%name%")) endtok = endtok->next(); while (Token::Match(endtok, ":: %name%")) endtok = endtok->tokAt(2); if (endtok && endtok->str() == ";") { tok->next()->str("typedef"); endtok = endtok->previous(); endtok->insertToken(endtok->str()); } } } } void Tokenizer::simplifyTypedef() { std::vector spaceInfo; bool isNamespace = false; std::string className; std::string fullClassName; bool hasClass = false; bool goback = false; // add global namespace spaceInfo.emplace_back(Space{}); // Convert "using a::b;" to corresponding typedef statements simplifyUsingToTypedef(); for (Token *tok = list.front(); tok; tok = tok->next()) { if (mErrorLogger && !list.getFiles().empty()) mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (typedef)", tok->progressValue()); if (Settings::terminated()) return; if (isMaxTime()) return; if (goback) { //jump back once, see the comment at the end of the function goback = false; tok = tok->previous(); } if (tok->str() != "typedef") { if (Token::simpleMatch(tok, "( typedef")) { // Skip typedefs inside parentheses (#2453 and #4002) tok = tok->next(); } else if (Token::Match(tok, "class|struct|namespace %any%") && (!tok->previous() || tok->previous()->str() != "enum")) { isNamespace = (tok->str() == "namespace"); hasClass = true; className = tok->next()->str(); const Token *tok1 = tok->next(); fullClassName = className; while (Token::Match(tok1, "%name% :: %name%")) { tok1 = tok1->tokAt(2); fullClassName += " :: " + tok1->str(); } } else if (hasClass && tok->str() == ";") { hasClass = false; } else if (hasClass && tok->str() == "{") { if (!isNamespace) spaceInfo.back().recordTypes.insert(fullClassName); Space info; info.isNamespace = isNamespace; info.className = className; info.bodyEnd = tok->link(); info.bodyEnd2 = tok->link(); spaceInfo.push_back(info); hasClass = false; } else if (spaceInfo.size() > 1 && tok->str() == "}" && spaceInfo.back().bodyEnd == tok) { spaceInfo.pop_back(); } continue; } // pull struct, union, enum or class definition out of typedef // use typedef name for unnamed struct, union, enum or class if (Token::Match(tok->next(), "const| struct|enum|union|class %type%| {|:")) { Token *tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount); if (!tok1) continue; tok = tok1; } /** @todo add support for union */ if (Token::Match(tok->next(), "enum %type% %type% ;") && tok->strAt(2) == tok->strAt(3)) { tok->deleteNext(3); tok->deleteThis(); if (tok->next()) tok->deleteThis(); //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } Token *typeName; Token *typeStart = nullptr; Token *typeEnd = nullptr; Token *argStart = nullptr; Token *argEnd = nullptr; Token *arrayStart = nullptr; Token *arrayEnd = nullptr; Token *specStart = nullptr; Token *specEnd = nullptr; Token *typeDef = tok; Token *argFuncRetStart = nullptr; Token *argFuncRetEnd = nullptr; Token *funcStart = nullptr; Token *funcEnd = nullptr; Token *tokOffset = tok->next(); bool function = false; bool functionPtr = false; bool functionRetFuncPtr = false; bool functionPtrRetFuncPtr = false; bool ptrToArray = false; bool refToArray = false; bool ptrMember = false; bool typeOf = false; Token *namespaceStart = nullptr; Token *namespaceEnd = nullptr; // check for invalid input if (!tokOffset) syntaxError(tok); if (tokOffset->str() == "::") { typeStart = tokOffset; tokOffset = tokOffset->next(); while (Token::Match(tokOffset, "%type% ::")) tokOffset = tokOffset->tokAt(2); typeEnd = tokOffset; if (Token::Match(tokOffset, "%type%")) tokOffset = tokOffset->next(); } else if (Token::Match(tokOffset, "%type% ::")) { typeStart = tokOffset; do { tokOffset = tokOffset->tokAt(2); } while (Token::Match(tokOffset, "%type% ::")); typeEnd = tokOffset; if (Token::Match(tokOffset, "%type%")) tokOffset = tokOffset->next(); } else if (Token::Match(tokOffset, "%type%")) { typeStart = tokOffset; while (Token::Match(tokOffset, "const|struct|enum %type%") || (tokOffset->next() && tokOffset->next()->isStandardType())) tokOffset = tokOffset->next(); typeEnd = tokOffset; tokOffset = tokOffset->next(); while (Token::Match(tokOffset, "%type%") && (tokOffset->isStandardType() || Token::Match(tokOffset, "unsigned|signed"))) { typeEnd = tokOffset; tokOffset = tokOffset->next(); } bool atEnd = false; while (!atEnd) { if (tokOffset && tokOffset->str() == "::") { typeEnd = tokOffset; tokOffset = tokOffset->next(); } if (Token::Match(tokOffset, "%type%") && tokOffset->next() && !Token::Match(tokOffset->next(), "[|;|,|(")) { typeEnd = tokOffset; tokOffset = tokOffset->next(); } else if (Token::simpleMatch(tokOffset, "const (")) { typeEnd = tokOffset; tokOffset = tokOffset->next(); atEnd = true; } else atEnd = true; } } else continue; // invalid input // check for invalid input if (!tokOffset) syntaxError(tok); // check for template if (!isC() && tokOffset->str() == "<") { typeEnd = tokOffset->findClosingBracket(); while (typeEnd && Token::Match(typeEnd->next(), ":: %type%")) typeEnd = typeEnd->tokAt(2); if (!typeEnd) { // internal error return; } while (Token::Match(typeEnd->next(), "const|volatile")) typeEnd = typeEnd->next(); tok = typeEnd; tokOffset = tok->next(); } std::list pointers; // check for pointers and references while (Token::Match(tokOffset, "*|&|&&|const")) { pointers.push_back(tokOffset->str()); tokOffset = tokOffset->next(); } // check for invalid input if (!tokOffset) syntaxError(tok); if (tokOffset->isName() && !tokOffset->isKeyword()) { // found the type name typeName = tokOffset; tokOffset = tokOffset->next(); // check for array while (tokOffset && tokOffset->str() == "[") { if (!arrayStart) arrayStart = tokOffset; arrayEnd = tokOffset->link(); tokOffset = arrayEnd->next(); } // check for end or another if (Token::Match(tokOffset, ";|,")) tok = tokOffset; // or a function typedef else if (tokOffset && tokOffset->str() == "(") { Token *tokOffset2 = nullptr; if (Token::Match(tokOffset, "( *|%name%")) { tokOffset2 = tokOffset->next(); if (tokOffset2->str() == "typename") tokOffset2 = tokOffset2->next(); while (Token::Match(tokOffset2, "%type% ::")) tokOffset2 = tokOffset2->tokAt(2); } // unhandled typedef, skip it and continue if (typeName->str() == "void") { unsupportedTypedef(typeDef); tok = deleteInvalidTypedef(typeDef); if (tok == list.front()) //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } // function pointer else if (Token::Match(tokOffset2, "* %name% ) (")) { // name token wasn't a name, it was part of the type typeEnd = typeEnd->next(); functionPtr = true; funcStart = funcEnd = tokOffset2; // * tokOffset = tokOffset2->tokAt(3); // ( typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); tok = argEnd->next(); } // function else if (isFunctionHead(tokOffset->link(), ";,")) { function = true; if (tokOffset->link()->next()->str() == "const") { specStart = tokOffset->link()->next(); specEnd = specStart; } argStart = tokOffset; argEnd = tokOffset->link(); tok = argEnd->next(); if (specStart) tok = tok->next(); } // syntax error else syntaxError(tok); } // unhandled typedef, skip it and continue else { unsupportedTypedef(typeDef); tok = deleteInvalidTypedef(typeDef); if (tok == list.front()) //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } } // typeof: typedef typeof ( ... ) type; else if (Token::simpleMatch(tokOffset->previous(), "typeof (") && Token::Match(tokOffset->link(), ") %type% ;")) { argStart = tokOffset; argEnd = tokOffset->link(); typeName = tokOffset->link()->next(); tok = typeName->next(); typeOf = true; } // function: typedef ... ( ... type )( ... ); // typedef ... (( ... type )( ... )); // typedef ... ( * ( ... type )( ... )); else if (tokOffset->str() == "(" && ( (tokOffset->link() && Token::Match(tokOffset->link()->previous(), "%type% ) (") && Token::Match(tokOffset->link()->next()->link(), ") const|volatile|;")) || (Token::simpleMatch(tokOffset, "( (") && tokOffset->next() && Token::Match(tokOffset->next()->link()->previous(), "%type% ) (") && Token::Match(tokOffset->next()->link()->next()->link(), ") const|volatile| ) ;|,")) || (Token::simpleMatch(tokOffset, "( * (") && tokOffset->linkAt(2) && Token::Match(tokOffset->linkAt(2)->previous(), "%type% ) (") && Token::Match(tokOffset->linkAt(2)->next()->link(), ") const|volatile| ) ;|,")))) { if (tokOffset->next()->str() == "(") tokOffset = tokOffset->next(); else if (Token::simpleMatch(tokOffset, "( * (")) { pointers.emplace_back("*"); tokOffset = tokOffset->tokAt(2); } if (tokOffset->link()->strAt(-2) == "*") functionPtr = true; else function = true; funcStart = tokOffset->next(); tokOffset = tokOffset->link(); funcEnd = tokOffset->tokAt(-2); typeName = tokOffset->previous(); argStart = tokOffset->next(); argEnd = tokOffset->next()->link(); if (!argEnd) syntaxError(argStart); tok = argEnd->next(); Token *spec = tok; if (Token::Match(spec, "const|volatile")) { specStart = spec; specEnd = spec; while (Token::Match(spec->next(), "const|volatile")) { specEnd = spec->next(); spec = specEnd; } tok = specEnd->next(); } if (!tok) syntaxError(specEnd); if (tok->str() == ")") tok = tok->next(); } else if (Token::Match(tokOffset, "( %type% (")) { function = true; if (tokOffset->link()->next()) { tok = tokOffset->link()->next(); tokOffset = tokOffset->tokAt(2); typeName = tokOffset->previous(); argStart = tokOffset; argEnd = tokOffset->link(); } else { // internal error continue; } } // pointer to function returning pointer to function else if (Token::Match(tokOffset, "( * ( * %type% ) (") && Token::simpleMatch(tokOffset->linkAt(6), ") ) (") && Token::Match(tokOffset->linkAt(6)->linkAt(2), ") ;|,")) { functionPtrRetFuncPtr = true; tokOffset = tokOffset->tokAt(6); typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); if (!argEnd) syntaxError(arrayStart); argFuncRetStart = argEnd->tokAt(2); argFuncRetEnd = argFuncRetStart->link(); if (!argFuncRetEnd) syntaxError(argFuncRetStart); tok = argFuncRetEnd->next(); } // function returning pointer to function else if (Token::Match(tokOffset, "( * %type% (") && Token::simpleMatch(tokOffset->linkAt(3), ") ) (") && Token::Match(tokOffset->linkAt(3)->linkAt(2), ") ;|,")) { functionRetFuncPtr = true; tokOffset = tokOffset->tokAt(3); typeName = tokOffset->previous(); argStart = tokOffset; argEnd = tokOffset->link(); argFuncRetStart = argEnd->tokAt(2); if (!argFuncRetStart) syntaxError(tokOffset); argFuncRetEnd = argFuncRetStart->link(); if (!argFuncRetEnd) syntaxError(tokOffset); tok = argFuncRetEnd->next(); } else if (Token::Match(tokOffset, "( * ( %type% ) (")) { functionRetFuncPtr = true; tokOffset = tokOffset->tokAt(5); typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); if (!argEnd) syntaxError(arrayStart); argFuncRetStart = argEnd->tokAt(2); if (!argFuncRetStart) syntaxError(tokOffset); argFuncRetEnd = argFuncRetStart->link(); if (!argFuncRetEnd) syntaxError(tokOffset); tok = argFuncRetEnd->next(); } // pointer/reference to array else if (Token::Match(tokOffset, "( *|& %type% ) [")) { ptrToArray = (tokOffset->next()->str() == "*"); refToArray = !ptrToArray; tokOffset = tokOffset->tokAt(2); typeName = tokOffset; arrayStart = tokOffset->tokAt(2); arrayEnd = arrayStart->link(); if (!arrayEnd) syntaxError(arrayStart); tok = arrayEnd->next(); } // pointer to class member else if (Token::Match(tokOffset, "( %type% :: * %type% ) ;")) { tokOffset = tokOffset->tokAt(2); namespaceStart = tokOffset->previous(); namespaceEnd = tokOffset; ptrMember = true; tokOffset = tokOffset->tokAt(2); typeName = tokOffset; tok = tokOffset->tokAt(2); } // unhandled typedef, skip it and continue else { unsupportedTypedef(typeDef); tok = deleteInvalidTypedef(typeDef); if (tok == list.front()) //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } bool done = false; bool ok = true; TypedefInfo typedefInfo; typedefInfo.name = typeName->str(); typedefInfo.filename = list.file(typeName); typedefInfo.lineNumber = typeName->linenr(); typedefInfo.column = typeName->column(); typedefInfo.used = false; mTypedefInfo.push_back(typedefInfo); while (!done) { std::string pattern = typeName->str(); int scope = 0; bool simplifyType = false; bool inMemberFunc = false; int memberScope = 0; bool globalScope = false; int classLevel = spaceInfo.size(); bool inTypeDef = false; std::string removed; std::string classPath; for (size_t i = 1; i < spaceInfo.size(); ++i) { if (!classPath.empty()) classPath += " :: "; classPath += spaceInfo[i].className; } for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (Settings::terminated()) return; removed.clear(); if (Token::simpleMatch(tok2, "typedef")) inTypeDef = true; if (inTypeDef && Token::simpleMatch(tok2, ";")) inTypeDef = false; // Check for variable declared with the same name if (!inTypeDef && spaceInfo.size() == 1 && Token::Match(tok2->previous(), "%name%") && !tok2->previous()->isKeyword()) { Token* varDecl = tok2; while (Token::Match(varDecl, "*|&|&&|const")) varDecl = varDecl->next(); if (Token::Match(varDecl, "%name% ;|,|)|=") && varDecl->str() == typeName->str()) { // Skip to the next closing brace if (Token::Match(varDecl, "%name% ) {")) { // is argument variable tok2 = varDecl->linkAt(2)->next(); } else { tok2 = varDecl; while (tok2 && !Token::simpleMatch(tok2, "}")) { if (Token::Match(tok2, "(|{|[")) tok2 = tok2->link(); tok2 = tok2->next(); } } if (!tok2) break; continue; } } if (tok2->link()) { // Pre-check for performance // check for end of scope if (tok2->str() == "}") { // check for end of member function if (inMemberFunc) { --memberScope; if (memberScope == 0) inMemberFunc = false; } if (classLevel > 1 && tok2 == spaceInfo[classLevel - 1].bodyEnd2) { --classLevel; pattern.clear(); for (int i = classLevel; i < spaceInfo.size(); ++i) pattern += (spaceInfo[i].className + " :: "); pattern += typeName->str(); } else { if (scope == 0) break; --scope; } } // check for member functions else if (isCPP() && tok2->str() == "(" && isFunctionHead(tok2, "{")) { const Token *func = tok2->previous(); /** @todo add support for multi-token operators */ if (func->previous()->str() == "operator") func = func->previous(); if (!func->previous()) syntaxError(func); // check for qualifier if (Token::Match(func->tokAt(-2), "%name% ::")) { int offset = -2; while (Token::Match(func->tokAt(offset - 2), "%name% ::")) offset -= 2; // check for available and matching class name if (spaceInfo.size() > 1 && classLevel < spaceInfo.size() && func->strAt(offset) == spaceInfo[classLevel].className) { memberScope = 0; inMemberFunc = true; } } } // check for entering a new scope else if (tok2->str() == "{") { // check for entering a new namespace if (isCPP() && tok2->strAt(-2) == "namespace") { if (classLevel < spaceInfo.size() && spaceInfo[classLevel].isNamespace && spaceInfo[classLevel].className == tok2->previous()->str()) { spaceInfo[classLevel].bodyEnd2 = tok2->link(); ++classLevel; pattern.clear(); for (int i = classLevel; i < spaceInfo.size(); ++i) pattern += spaceInfo[i].className + " :: "; pattern += typeName->str(); } ++scope; } // keep track of scopes within member function if (inMemberFunc) ++memberScope; ++scope; } } // check for operator typedef /** @todo add support for multi-token operators */ else if (isCPP() && tok2->str() == "operator" && tok2->next() && tok2->next()->str() == typeName->str() && tok2->linkAt(2) && tok2->strAt(2) == "(" && Token::Match(tok2->linkAt(2), ") const| {")) { // check for qualifier if (tok2->previous()->str() == "::") { // check for available and matching class name if (spaceInfo.size() > 1 && classLevel < spaceInfo.size() && tok2->strAt(-2) == spaceInfo[classLevel].className) { tok2 = tok2->next(); simplifyType = true; } } } else if (Token::Match(tok2->previous(), "class|struct %name% [:{]")) { // don't replace names in struct/class definition } // check for typedef that can be substituted else if ((tok2->isNameOnly() || (tok2->isName() && tok2->isExpandedMacro())) && (Token::simpleMatch(tok2, pattern.c_str(), pattern.size()) || (inMemberFunc && tok2->str() == typeName->str()))) { // member function class variables don't need qualification if (!(inMemberFunc && tok2->str() == typeName->str()) && pattern.find("::") != std::string::npos) { // has a "something ::" Token *start = tok2; int count = 0; int back = classLevel - 1; bool good = true; // check for extra qualification while (back >= 1) { Token *qualificationTok = start->tokAt(-2); if (!Token::Match(qualificationTok, "%type% ::")) break; if (qualificationTok->str() == spaceInfo[back].className) { start = qualificationTok; back--; count++; } else { good = false; break; } } // check global namespace if (good && back == 1 && start->strAt(-1) == "::") good = false; if (good) { // remove any extra qualification if present while (count) { if (!removed.empty()) removed.insert(0, " "); removed.insert(0, tok2->strAt(-2) + " " + tok2->strAt(-1)); tok2->tokAt(-3)->deleteNext(2); --count; } // remove global namespace if present if (tok2->strAt(-1) == "::") { removed.insert(0, ":: "); tok2->tokAt(-2)->deleteNext(); globalScope = true; } // remove qualification if present for (int i = classLevel; i < spaceInfo.size(); ++i) { if (!removed.empty()) removed += " "; removed += (tok2->str() + " " + tok2->strAt(1)); tok2->deleteThis(); tok2->deleteThis(); } simplifyType = true; } } else { if (tok2->strAt(-1) == "::") { int relativeSpaceInfoSize = spaceInfo.size(); Token * tokBeforeType = tok2->previous(); while (relativeSpaceInfoSize > 1 && tokBeforeType && tokBeforeType->str() == "::" && tokBeforeType->strAt(-1) == spaceInfo[relativeSpaceInfoSize-1].className) { tokBeforeType = tokBeforeType->tokAt(-2); --relativeSpaceInfoSize; } if (tokBeforeType && tokBeforeType->str() != "::") { Token::eraseTokens(tokBeforeType, tok2); simplifyType = true; } } else if (Token::Match(tok2->previous(), "case|;|{|} %type% :")) { tok2 = tok2->next(); } else if (duplicateTypedef(&tok2, typeName, typeDef)) { // skip to end of scope if not already there if (tok2->str() != "}") { while (tok2->next()) { if (tok2->next()->str() == "{") tok2 = tok2->linkAt(1)->previous(); else if (tok2->next()->str() == "}") break; tok2 = tok2->next(); } } } else if (Token::Match(tok2->tokAt(-2), "%type% *|&")) { // Ticket #5868: Don't substitute variable names } else if (tok2->previous()->str() != ".") { simplifyType = true; } } } if (simplifyType) { mTypedefInfo.back().used = true; // can't simplify 'operator functionPtr ()' and 'functionPtr operator ... ()' if (functionPtr && (tok2->previous()->str() == "operator" || (tok2->next() && tok2->next()->str() == "operator"))) { simplifyType = false; tok2 = tok2->next(); continue; } // There are 2 categories of typedef substitutions: // 1. variable declarations that preserve the variable name like // global, local, and function parameters // 2. not variable declarations that have no name like derived // classes, casts, operators, and template parameters // try to determine which category this substitution is bool inCast = false; bool inTemplate = false; bool inOperator = false; bool inSizeof = false; const bool sameStartEnd = (typeStart == typeEnd); // check for derived class: class A : some_typedef { const bool isDerived = Token::Match(tok2->previous(), "public|protected|private %type% {|,"); // check for cast: (some_typedef) A or static_cast(A) // todo: check for more complicated casts like: (const some_typedef *)A if ((tok2->previous()->str() == "(" && tok2->next()->str() == ")" && tok2->strAt(-2) != "sizeof") || (tok2->previous()->str() == "<" && Token::simpleMatch(tok2->next(), "> (")) || Token::Match(tok2->tokAt(-2), "( const %name% )")) inCast = true; // check for template parameters: t t1 else if (Token::Match(tok2->previous(), "<|,") && Token::Match(tok2->next(), "&|*| &|*| >|,")) inTemplate = true; else if (Token::Match(tok2->tokAt(-2), "sizeof ( %type% )")) inSizeof = true; // check for operator if (tok2->strAt(-1) == "operator" || Token::simpleMatch(tok2->tokAt(-2), "operator const")) inOperator = true; if (typeStart->str() == "typename" && tok2->strAt(-1)=="typename") { // Remove one typename if it is already contained in the goal typeStart = typeStart->next(); } // skip over class or struct in derived class declaration bool structRemoved = false; if (isDerived && Token::Match(typeStart, "class|struct")) { if (typeStart->str() == "struct") structRemoved = true; typeStart = typeStart->next(); } if (Token::Match(typeStart, "struct|class") && Token::Match(tok2, "%name% ::")) typeStart = typeStart->next(); if (sameStartEnd) typeEnd = typeStart; // start substituting at the typedef name by replacing it with the type tok2->str(typeStart->str()); // restore qualification if it was removed if (typeStart->str() == "struct" || structRemoved) { if (structRemoved) tok2 = tok2->previous(); if (globalScope) { tok2->insertToken("::"); tok2 = tok2->next(); } for (int i = classLevel; i < spaceInfo.size(); ++i) { tok2->insertToken(spaceInfo[i].className); tok2 = tok2->next(); tok2->insertToken("::"); tok2 = tok2->next(); } } // add some qualification back if needed Token *start = tok2; std::string removed1 = removed; std::string::size_type idx = removed1.rfind(" ::"); if (idx != std::string::npos) removed1.resize(idx); if (removed1 == classPath && !removed1.empty()) { for (std::vector::const_reverse_iterator it = spaceInfo.crbegin(); it != spaceInfo.crend(); ++it) { if (it->recordTypes.find(start->str()) != it->recordTypes.end()) { std::string::size_type spaceIdx = 0; std::string::size_type startIdx = 0; while ((spaceIdx = removed1.find(" ", startIdx)) != std::string::npos) { tok2->previous()->insertToken(removed1.substr(startIdx, spaceIdx - startIdx)); startIdx = spaceIdx + 1; } tok2->previous()->insertToken(removed1.substr(startIdx)); tok2->previous()->insertToken("::"); break; } idx = removed1.rfind(" ::"); if (idx == std::string::npos) break; removed1.resize(idx); } } // add remainder of type tok2 = TokenList::copyTokens(tok2, typeStart->next(), typeEnd); if (!pointers.empty()) { for (const std::string &p : pointers) { tok2->insertToken(p); tok2 = tok2->next(); } } if (funcStart && funcEnd) { tok2->insertToken("("); tok2 = tok2->next(); Token *paren = tok2; tok2 = TokenList::copyTokens(tok2, funcStart, funcEnd); if (!inCast) tok2 = processFunc(tok2, inOperator); if (!tok2) break; while (Token::Match(tok2, "%name%|] [")) tok2 = tok2->linkAt(1); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, paren); tok2 = TokenList::copyTokens(tok2, argStart, argEnd); if (specStart) { Token *spec = specStart; tok2->insertToken(spec->str()); tok2 = tok2->next(); while (spec != specEnd) { spec = spec->next(); tok2->insertToken(spec->str()); tok2 = tok2->next(); } } } else if (functionPtr || function) { // don't add parentheses around function names because it // confuses other simplifications bool needParen = true; if (!inTemplate && function && tok2->next() && tok2->next()->str() != "*") needParen = false; if (needParen) { tok2->insertToken("("); tok2 = tok2->next(); } Token *tok3 = tok2; if (namespaceStart) { const Token *tok4 = namespaceStart; while (tok4 != namespaceEnd) { tok2->insertToken(tok4->str()); tok2 = tok2->next(); tok4 = tok4->next(); } tok2->insertToken(namespaceEnd->str()); tok2 = tok2->next(); } if (functionPtr) { tok2->insertToken("*"); tok2 = tok2->next(); } if (!inCast) tok2 = processFunc(tok2, inOperator); if (needParen) { if (!tok2) syntaxError(nullptr); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } if (!tok2) syntaxError(nullptr); tok2 = TokenList::copyTokens(tok2, argStart, argEnd); if (inTemplate) { if (!tok2) syntaxError(nullptr); tok2 = tok2->next(); } if (specStart) { Token *spec = specStart; tok2->insertToken(spec->str()); tok2 = tok2->next(); while (spec != specEnd) { spec = spec->next(); tok2->insertToken(spec->str()); tok2 = tok2->next(); } } } else if (functionRetFuncPtr || functionPtrRetFuncPtr) { tok2->insertToken("("); tok2 = tok2->next(); Token *tok3 = tok2; tok2->insertToken("*"); tok2 = tok2->next(); Token * tok4 = nullptr; if (functionPtrRetFuncPtr) { tok2->insertToken("("); tok2 = tok2->next(); tok4 = tok2; tok2->insertToken("*"); tok2 = tok2->next(); } // skip over variable name if there if (!inCast) { if (!tok2 || !tok2->next()) syntaxError(nullptr); if (tok2->next()->str() != ")") tok2 = tok2->next(); } if (tok4 && functionPtrRetFuncPtr) { tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok4); } tok2 = TokenList::copyTokens(tok2, argStart, argEnd); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); tok2 = TokenList::copyTokens(tok2, argFuncRetStart, argFuncRetEnd); } else if (ptrToArray || refToArray) { tok2->insertToken("("); tok2 = tok2->next(); Token *tok3 = tok2; if (ptrToArray) tok2->insertToken("*"); else tok2->insertToken("&"); tok2 = tok2->next(); bool hasName = false; // skip over name if (tok2->next() && tok2->next()->str() != ")" && tok2->next()->str() != "," && tok2->next()->str() != ">") { hasName = true; if (tok2->next()->str() != "(") tok2 = tok2->next(); // check for function and skip over args if (tok2 && tok2->next() && tok2->next()->str() == "(") tok2 = tok2->next()->link(); // check for array if (tok2 && tok2->next() && tok2->next()->str() == "[") tok2 = tok2->next()->link(); } tok2->insertToken(")"); Token::createMutualLinks(tok2->next(), tok3); if (!hasName) tok2 = tok2->next(); } else if (ptrMember) { if (Token::simpleMatch(tok2, "* (")) { tok2->insertToken("*"); tok2 = tok2->next(); } else { // This is the case of casting operator. // Name is not available, and () should not be // inserted const bool castOperator = inOperator && Token::Match(tok2, "%type% ("); Token *openParenthesis = nullptr; if (!castOperator) { tok2->insertToken("("); tok2 = tok2->next(); openParenthesis = tok2; } const Token *tok4 = namespaceStart; while (tok4 != namespaceEnd) { tok2->insertToken(tok4->str()); tok2 = tok2->next(); tok4 = tok4->next(); } tok2->insertToken(namespaceEnd->str()); tok2 = tok2->next(); tok2->insertToken("*"); tok2 = tok2->next(); if (openParenthesis) { // Skip over name, if any if (Token::Match(tok2->next(), "%name%")) tok2 = tok2->next(); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, openParenthesis); } } } else if (typeOf) { tok2 = TokenList::copyTokens(tok2, argStart, argEnd); } else if (Token::Match(tok2, "%name% [")) { while (Token::Match(tok2, "%name%|] [")) { tok2 = tok2->linkAt(1); } tok2 = tok2->previous(); } if (arrayStart && arrayEnd) { do { if (!tok2->next()) syntaxError(tok2); // can't recover so quit if (!inCast && !inSizeof && !inTemplate) tok2 = tok2->next(); if (tok2->str() == "const") tok2 = tok2->next(); // reference or pointer to array? if (Token::Match(tok2, "&|*|&&")) { tok2 = tok2->previous(); tok2->insertToken("("); Token *tok3 = tok2->next(); // handle missing variable name if (Token::Match(tok3, "( *|&|&& *|&|&& %name%")) tok2 = tok3->tokAt(3); else if (Token::Match(tok2->tokAt(3), "[(),;]")) tok2 = tok2->tokAt(2); else tok2 = tok2->tokAt(3); if (!tok2) syntaxError(nullptr); while (tok2->strAt(1) == "::") tok2 = tok2->tokAt(2); // skip over function parameters if (tok2->str() == "(") tok2 = tok2->link(); if (tok2->strAt(1) == "(") tok2 = tok2->linkAt(1); // skip over const/noexcept while (Token::Match(tok2->next(), "const|noexcept")) tok2 = tok2->next(); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } if (!tok2->next()) syntaxError(tok2); // can't recover so quit // skip over array dimensions while (tok2->next()->str() == "[") tok2 = tok2->linkAt(1); tok2 = TokenList::copyTokens(tok2, arrayStart, arrayEnd); if (!tok2->next()) syntaxError(tok2); if (tok2->str() == "=") { if (!tok2->next()) syntaxError(tok2); if (tok2->next()->str() == "{") tok2 = tok2->next()->link()->next(); else if (tok2->next()->str().at(0) == '\"') tok2 = tok2->tokAt(2); } } while (Token::Match(tok2, ", %name% ;|=|,")); } simplifyType = false; } if (!tok2) break; } if (!tok) syntaxError(nullptr); if (tok->str() == ";") done = true; else if (tok->str() == ",") { arrayStart = nullptr; arrayEnd = nullptr; tokOffset = tok->next(); pointers.clear(); while (Token::Match(tokOffset, "*|&")) { pointers.push_back(tokOffset->str()); tokOffset = tokOffset->next(); } if (Token::Match(tokOffset, "%type%")) { typeName = tokOffset; tokOffset = tokOffset->next(); if (tokOffset && tokOffset->str() == "[") { arrayStart = tokOffset; for (;;) { while (tokOffset->next() && !Token::Match(tokOffset->next(), ";|,")) tokOffset = tokOffset->next(); if (!tokOffset->next()) return; // invalid input else if (tokOffset->next()->str() == ";") break; else if (tokOffset->str() == "]") break; else tokOffset = tokOffset->next(); } arrayEnd = tokOffset; tokOffset = tokOffset->next(); } if (Token::Match(tokOffset, ";|,")) tok = tokOffset; else { // we encountered a typedef we don't support yet so just continue done = true; ok = false; } } else { // we encountered a typedef we don't support yet so just continue done = true; ok = false; } } else { // something is really wrong (internal error) done = true; ok = false; } } if (ok) { // remove typedef Token::eraseTokens(typeDef, tok); if (typeDef != list.front()) { tok = typeDef->previous(); tok->deleteNext(); //no need to remove last token in the list if (tok->tokAt(2)) tok->deleteNext(); } else { list.front()->deleteThis(); //no need to remove last token in the list if (list.front()->next()) list.front()->deleteThis(); tok = list.front(); //now the next token to process is 'tok', not 'tok->next()'; goback = true; } } } } namespace { struct ScopeInfo3 { enum Type { Global, Namespace, Record, MemberFunction, Other }; ScopeInfo3() : parent(nullptr), type(Global), bodyStart(nullptr), bodyEnd(nullptr) {} ScopeInfo3(ScopeInfo3 *parent_, Type type_, const std::string &name_, const Token *bodyStart_, const Token *bodyEnd_) : parent(parent_), type(type_), name(name_), bodyStart(bodyStart_), bodyEnd(bodyEnd_) { if (name.empty()) return; fullName = name; ScopeInfo3 *scope = parent; while (scope && scope->parent) { if (scope->name.empty()) break; fullName = scope->name + " :: " + fullName; scope = scope->parent; } } ScopeInfo3 *parent; std::list children; Type type; std::string fullName; std::string name; const Token * bodyStart; const Token * bodyEnd; std::set usingNamespaces; std::set recordTypes; std::set baseTypes; ScopeInfo3 *addChild(Type scopeType, const std::string &scopeName, const Token *bodyStartToken, const Token *bodyEndToken) { children.emplace_back(this, scopeType, scopeName, bodyStartToken, bodyEndToken); return &children.back(); } bool hasChild(const std::string &childName) const { for (const auto & child : children) { if (child.name == childName) return true; } return false; } const ScopeInfo3 * findInChildren(const std::string & scope) const { for (const auto & child : children) { if (child.type == Record && (child.name == scope || child.fullName == scope)) return &child; else { const ScopeInfo3 * temp = child.findInChildren(scope); if (temp) return temp; } } return nullptr; } const ScopeInfo3 * findScope(const std::string & scope) const { const ScopeInfo3 * tempScope = this; while (tempScope) { // check children for (const auto & child : tempScope->children) { if (&child != this && child.type == Record && (child.name == scope || child.fullName == scope)) return &child; } // check siblings for same name if (tempScope->parent) { for (const auto &sibling : tempScope->parent->children) { if (sibling.name == tempScope->name && &sibling != this) { const ScopeInfo3 * temp = sibling.findInChildren(scope); if (temp) return temp; } } } tempScope = tempScope->parent; } return nullptr; } bool findTypeInBase(const std::string &scope) const { // check in base types first if (baseTypes.find(scope) != baseTypes.end()) return true; // check in base types base types for (const std::string & base : baseTypes) { const ScopeInfo3 * baseScope = findScope(base); // bail on uninstantiated recursive template if (baseScope == this) return false; if (baseScope && baseScope->fullName == scope) return true; if (baseScope && baseScope->findTypeInBase(scope)) return true; } return false; } ScopeInfo3 * findScope(const ScopeInfo3 * scope) { if (scope->bodyStart == bodyStart) return this; for (auto & child : children) { ScopeInfo3 * temp = child.findScope(scope); if (temp) return temp; } return nullptr; } }; void setScopeInfo(Token *tok, ScopeInfo3 **scopeInfo, bool debug=false) { if (!tok) return; if (tok->str() == "{" && (*scopeInfo)->parent && tok == (*scopeInfo)->bodyStart) return; if (tok->str() == "}") { if ((*scopeInfo)->parent && tok == (*scopeInfo)->bodyEnd) *scopeInfo = (*scopeInfo)->parent; else { // Try to find parent scope ScopeInfo3 *parent = (*scopeInfo)->parent; while (parent && parent->bodyEnd != tok) parent = parent->parent; if (parent) { *scopeInfo = parent; if (debug) throw std::runtime_error("Internal error: unmatched }"); } } return; } if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::|<")) { // check for using namespace if (Token::Match(tok, "using namespace %name% ;|::")) { const Token * tok1 = tok->tokAt(2); std::string nameSpace; while (tok1 && tok1->str() != ";") { if (!nameSpace.empty()) nameSpace += " "; nameSpace += tok1->str(); tok1 = tok1->next(); } (*scopeInfo)->usingNamespaces.insert(nameSpace); } // check for member function else if (tok->str() == "{") { bool added = false; Token *tok1 = tok; while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->previous() && (tok1->strAt(-1) == ")" || tok->strAt(-1) == "}")) { tok1 = tok1->linkAt(-1); if (Token::Match(tok1->previous(), "throw|noexcept (")) { tok1 = tok1->previous(); while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->strAt(-1) != ")") return; tok1 = tok1->linkAt(-1); } else { while (Token::Match(tok1->tokAt(-2), ":|, %name%")) { tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) != ")" && tok1->strAt(-1) != "}") return; tok1 = tok1->linkAt(-1); } } if (tok1->strAt(-1) == ">") tok1 = tok1->previous()->findOpeningBracket(); if (tok1 && (Token::Match(tok1->tokAt(-3), "%name% :: %name%") || Token::Match(tok1->tokAt(-4), "%name% :: ~ %name%"))) { tok1 = tok1->tokAt(-2); if (tok1->str() == "~") tok1 = tok1->previous(); std::string scope = tok1->strAt(-1); while (Token::Match(tok1->tokAt(-2), ":: %name%")) { scope = tok1->strAt(-3) + " :: " + scope; tok1 = tok1->tokAt(-2); } *scopeInfo = (*scopeInfo)->addChild(ScopeInfo3::MemberFunction, scope, tok, tok->link()); added = true; } } if (!added) *scopeInfo = (*scopeInfo)->addChild(ScopeInfo3::Other, "", tok, tok->link()); } return; } const bool record = Token::Match(tok, "class|struct|union %name%"); tok = tok->next(); std::string classname = tok->str(); while (Token::Match(tok, "%name% :: %name%")) { tok = tok->tokAt(2); classname += " :: " + tok->str(); } // add record type to scope info if (record) (*scopeInfo)->recordTypes.insert(classname); tok = tok->next(); // skip template parameters if (tok && tok->str() == "<") { tok = tok->findClosingBracket(); if (tok) tok = tok->next(); } // get base class types std::set baseTypes; if (tok && tok->str() == ":") { do { tok = tok->next(); while (Token::Match(tok, "public|protected|private|virtual")) tok = tok->next(); std::string base; while (tok && !Token::Match(tok, ";|,|{")) { if (!base.empty()) base += ' '; base += tok->str(); tok = tok->next(); // skip template parameters if (tok && tok->str() == "<") { tok = tok->findClosingBracket(); if (tok) tok = tok->next(); } } baseTypes.insert(base); } while (tok && !Token::Match(tok, ";|{")); } if (tok && tok->str() == "{") { *scopeInfo = (*scopeInfo)->addChild(record ? ScopeInfo3::Record : ScopeInfo3::Namespace, classname, tok, tok->link()); (*scopeInfo)->baseTypes = baseTypes; } } Token *findSemicolon(Token *tok) { int level = 0; for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) { if (tok->str() == "{") ++level; else if (level > 0 && tok->str() == "}") --level; } return tok; } bool usingMatch( const Token *nameToken, const std::string &scope, Token **tok, const std::string &scope1, const ScopeInfo3 *currentScope, const ScopeInfo3 *memberClassScope) { Token *tok1 = *tok; if (tok1 && tok1->str() != nameToken->str()) return false; // skip this using if (tok1 == nameToken) { *tok = findSemicolon(tok1); return false; } // skip other using with this name if (tok1->strAt(-1) == "using") { // fixme: this is wrong // skip to end of scope if (currentScope->bodyEnd) *tok = currentScope->bodyEnd->previous(); return false; } if (Token::Match(tok1->tokAt(-1), "class|struct|union|enum|namespace")) { // fixme return false; } // get qualification std::string qualification; const Token* tok2 = tok1; std::string::size_type index = scope.size(); std::string::size_type new_index = std::string::npos; bool match = true; while (Token::Match(tok2->tokAt(-2), "%name% ::") && !tok2->tokAt(-2)->isKeyword()) { std::string last; if (match && !scope1.empty()) { new_index = scope1.rfind(' ', index - 1); if (new_index != std::string::npos) last = scope1.substr(new_index, index - new_index); else if (!qualification.empty()) last.clear(); else last = scope1; } else match = false; if (match && tok2->strAt(-2) == last) index = new_index; else { if (!qualification.empty()) qualification = " :: " + qualification; qualification = tok2->strAt(-2) + qualification; } tok2 = tok2->tokAt(-2); } std::string fullScope1 = scope1; if (!scope1.empty() && !qualification.empty()) fullScope1 += " :: "; fullScope1 += qualification; if (scope == fullScope1) return true; const ScopeInfo3 *scopeInfo = memberClassScope ? memberClassScope : currentScope; // check in base types if (scopeInfo->findTypeInBase(scope)) return true; // check using namespace const ScopeInfo3 * tempScope = scopeInfo; while (tempScope) { //if (!tempScope->parent->usingNamespaces.empty()) { if (!tempScope->usingNamespaces.empty()) { if (qualification.empty()) { if (tempScope->usingNamespaces.find(scope) != tempScope->usingNamespaces.end()) return true; } else { for (auto ns : tempScope->usingNamespaces) { if (scope == ns + " :: " + qualification) return true; } } } tempScope = tempScope->parent; } std::string newScope1 = scope1; // scopes didn't match so try higher scopes index = newScope1.size(); while (!newScope1.empty()) { std::string::size_type separator = newScope1.rfind(" :: ", index - 1); if (separator != std::string::npos) newScope1 = newScope1.substr(0, separator); else newScope1.clear(); std::string newFullScope1 = newScope1; if (!newScope1.empty() && !qualification.empty()) newFullScope1 += " :: "; newFullScope1 += qualification; if (scope == newFullScope1) return true; } return false; } std::string memberFunctionScope(const Token *tok) { std::string qualification; const Token *qualTok = tok->strAt(-2) == "~" ? tok->tokAt(-4) : tok->tokAt(-3); while (Token::Match(qualTok, "%type% ::")) { if (!qualification.empty()) qualification = " :: " + qualification; qualification = qualTok->str() + qualification; qualTok = qualTok->tokAt(-2); } return qualification; } const Token * memberFunctionEnd(const Token *tok) { if (tok->str() != "(") return nullptr; const Token *end = tok->link()->next(); while (end) { if (end->str() == "{" && !Token::Match(end->tokAt(-2), ":|, %name%")) return end; else if (end->str() == ";") break; end = end->next(); } return nullptr; } } // namespace bool Tokenizer::isMemberFunction(const Token *openParen) const { return (Token::Match(openParen->tokAt(-2), ":: %name% (") || Token::Match(openParen->tokAt(-3), ":: ~ %name% (")) && isFunctionHead(openParen, "{|:"); } static bool scopesMatch(const std::string &scope1, const std::string &scope2, const ScopeInfo3 *globalScope) { if (scope1.empty() || scope2.empty()) return false; // check if scopes match if (scope1 == scope2) return true; // check if scopes only differ by global qualification if (scope1 == (":: " + scope2)) { std::string::size_type end = scope2.find_first_of(' '); if (end == std::string::npos) end = scope2.size(); if (globalScope->hasChild(scope2.substr(0, end))) return true; } else if (scope2 == (":: " + scope1)) { std::string::size_type end = scope1.find_first_of(' '); if (end == std::string::npos) end = scope1.size(); if (globalScope->hasChild(scope1.substr(0, end))) return true; } return false; } bool Tokenizer::simplifyUsing() { if (!isCPP() || mSettings->standards.cpp < Standards::CPP11) return false; bool substitute = false; ScopeInfo3 scopeInfo; ScopeInfo3 *currentScope = &scopeInfo; struct Using { Using(Token *start, Token *end) : startTok(start), endTok(end) {} Token *startTok; Token *endTok; }; std::list usingList; for (Token *tok = list.front(); tok; tok = tok->next()) { if (mErrorLogger && !list.getFiles().empty()) mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (using)", tok->progressValue()); if (Settings::terminated()) return substitute; if (Token::Match(tok, "enum class|struct")) { Token *bodyStart = tok; while (Token::Match(bodyStart, "%name%|:|::|<")) { if (bodyStart->str() == "<") bodyStart = bodyStart->findClosingBracket(); bodyStart = bodyStart ? bodyStart->next() : nullptr; } if (Token::simpleMatch(bodyStart, "{")) tok = bodyStart->link(); continue; } if (Token::Match(tok, "{|}|namespace|class|struct|union") || Token::Match(tok, "using namespace %name% ;|::")) { try { setScopeInfo(tok, ¤tScope, mSettings->debugwarnings); } catch (const std::runtime_error &) { reportError(tok, Severity::debug, "simplifyUsingUnmatchedBodyEnd", "simplifyUsing: unmatched body end"); } continue; } // skip template declarations if (Token::Match(tok, "template < !!>")) { // add template record type to scope info const Token *end = tok->next()->findClosingBracket(); if (end && Token::Match(end->next(), "class|struct|union %name%")) currentScope->recordTypes.insert(end->strAt(2)); Token *declEndToken = TemplateSimplifier::findTemplateDeclarationEnd(tok); if (declEndToken) tok = declEndToken; continue; } // look for non-template type aliases if (!(tok->strAt(-1) != ">" && (Token::Match(tok, "using %name% = ::| %name%") || (Token::Match(tok, "using %name% [ [") && Token::Match(tok->linkAt(2), "] ] = ::| %name%"))))) continue; std::string name = tok->strAt(1); const Token *nameToken = tok->next(); std::string scope = currentScope->fullName; Token *usingStart = tok; Token *start; if (tok->strAt(2) == "=") start = tok->tokAt(3); else start = tok->linkAt(2)->tokAt(3); Token *usingEnd = findSemicolon(start); if (!usingEnd) continue; // Move struct defined in using out of using. // using T = struct t { }; => struct t { }; using T = struct t; // fixme: this doesn't handle attributes if (Token::Match(start, "class|struct|union|enum %name%| {|:")) { Token *structEnd = start->tokAt(1); const bool hasName = Token::Match(structEnd, "%name%"); // skip over name if present if (hasName) structEnd = structEnd->next(); // skip over base class information if (structEnd->str() == ":") { structEnd = structEnd->next(); // skip over ":" while (structEnd && structEnd->str() != "{") structEnd = structEnd->next(); if (!structEnd) continue; } // use link to go to end structEnd = structEnd->link(); // add ';' after end of struct structEnd->insertToken(";", ""); // add name for anonymous struct if (!hasName) { std::string newName; if (structEnd->strAt(2) == ";") newName = name; else newName = "Unnamed" + MathLib::toString(mUnnamedCount++); TokenList::copyTokens(structEnd->next(), tok, start); structEnd->tokAt(5)->insertToken(newName, ""); start->insertToken(newName, ""); } else TokenList::copyTokens(structEnd->next(), tok, start->next()); // add using after end of struct usingStart = structEnd->tokAt(2); nameToken = usingStart->next(); if (usingStart->strAt(2) == "=") start = usingStart->tokAt(3); else start = usingStart->linkAt(2)->tokAt(3); usingEnd = findSemicolon(start); // delete original using before struct tok->deleteThis(); tok->deleteThis(); tok->deleteThis(); tok = usingStart; } // remove 'typename' and 'template' else if (start->str() == "typename") { start->deleteThis(); Token *temp = start; while (Token::Match(temp, "%name% ::")) temp = temp->tokAt(2); if (Token::Match(temp, "template %name%")) temp->deleteThis(); } if (usingEnd) tok = usingEnd; // Unfortunately we have to start searching from the beginning // of the token stream because templates are instantiated at // the end of the token stream and it may be used before then. ScopeInfo3 scopeInfo1; ScopeInfo3 *currentScope1 = &scopeInfo1; Token *startToken = list.front(); Token *endToken = nullptr; bool inMemberFunc = false; const ScopeInfo3 * memberFuncScope = nullptr; const Token * memberFuncEnd = nullptr; // We can limit the search to the current function when the type alias // is defined in that function. if (currentScope->type == ScopeInfo3::Other || currentScope->type == ScopeInfo3::MemberFunction) { scopeInfo1 = scopeInfo; currentScope1 = scopeInfo1.findScope(currentScope); if (!currentScope1) return substitute; // something bad happened startToken = usingEnd->next(); endToken = currentScope->bodyEnd->next(); if (currentScope->type == ScopeInfo3::MemberFunction) { const ScopeInfo3 * temp = currentScope->findScope(currentScope->fullName); if (temp) { inMemberFunc = true; memberFuncScope = temp; memberFuncEnd = endToken; } } } std::string scope1 = currentScope1->fullName; bool skip = false; // don't erase type aliases we can't parse Token *enumOpenBrace = nullptr; for (Token* tok1 = startToken; !skip && tok1 && tok1 != endToken; tok1 = tok1->next()) { // skip enum body if (tok1 && tok1 == enumOpenBrace) { tok1 = tok1->link(); enumOpenBrace = nullptr; continue; } if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") || Token::Match(tok1, "using namespace %name% ;|::")) { try { setScopeInfo(tok1, ¤tScope1, mSettings->debugwarnings); } catch (const std::runtime_error &) { reportError(tok1, Severity::debug, "simplifyUsingUnmatchedBodyEnd", "simplifyUsing: unmatched body end"); } scope1 = currentScope1->fullName; if (inMemberFunc && memberFuncEnd && tok1 == memberFuncEnd) { inMemberFunc = false; memberFuncScope = nullptr; memberFuncEnd = nullptr; } continue; } // skip template definitions if (Token::Match(tok1, "template < !!>")) { Token *declEndToken = TemplateSimplifier::findTemplateDeclarationEnd(tok1); if (declEndToken) tok1 = declEndToken; continue; } // check for enum with body if (tok1->str() == "enum") { if (Token::Match(tok1, "enum class|struct")) tok1 = tok1->next(); Token *defStart = tok1; while (Token::Match(defStart, "%name%|::|:")) defStart = defStart->next(); if (Token::simpleMatch(defStart, "{")) enumOpenBrace = defStart; continue; } // check for member function and adjust scope if (isMemberFunction(tok1)) { if (!scope1.empty()) scope1 += " :: "; scope1 += memberFunctionScope(tok1); const ScopeInfo3 * temp = currentScope1->findScope(scope1); if (temp) { const Token *end = memberFunctionEnd(tok1); if (end) { inMemberFunc = true; memberFuncScope = temp; memberFuncEnd = end; } } continue; } else if (inMemberFunc && memberFuncScope) { if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1, memberFuncScope)) continue; } else if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1, nullptr)) continue; // remove the qualification std::string fullScope = scope; std::string removed; while (Token::Match(tok1->tokAt(-2), "%name% ::") && !tok1->tokAt(-2)->isKeyword()) { removed = (tok1->strAt(-2) + " :: ") + removed; if (fullScope == tok1->strAt(-2)) { tok1->deletePrevious(); tok1->deletePrevious(); break; } else { const std::string::size_type idx = fullScope.rfind(" "); if (idx == std::string::npos) break; if (tok1->strAt(-2) == fullScope.substr(idx + 1)) { tok1->deletePrevious(); tok1->deletePrevious(); fullScope.resize(idx - 3); } else break; } } // remove global namespace if present if (tok1->strAt(-1) == "::") { removed.insert(0, ":: "); tok1->deletePrevious(); } Token * arrayStart = nullptr; // parse the type Token *type = start; if (type->str() == "::") { type = type->next(); while (Token::Match(type, "%type% ::")) type = type->tokAt(2); if (Token::Match(type, "%type%")) type = type->next(); } else if (Token::Match(type, "%type% ::")) { do { type = type->tokAt(2); } while (Token::Match(type, "%type% ::")); if (Token::Match(type, "%type%")) type = type->next(); } else if (Token::Match(type, "%type%")) { while (Token::Match(type, "const|class|struct|union|enum %type%") || (type->next() && type->next()->isStandardType())) type = type->next(); type = type->next(); while (Token::Match(type, "%type%") && (type->isStandardType() || Token::Match(type, "unsigned|signed"))) { type = type->next(); } bool atEnd = false; while (!atEnd) { if (type && type->str() == "::") { type = type->next(); } if (Token::Match(type, "%type%") && type->next() && !Token::Match(type->next(), "[|,|(")) { type = type->next(); } else if (Token::simpleMatch(type, "const (")) { type = type->next(); atEnd = true; } else atEnd = true; } } else syntaxError(type); // check for invalid input if (!type) syntaxError(tok1); // check for template if (type->str() == "<") { type = type->findClosingBracket(); while (type && Token::Match(type->next(), ":: %type%")) type = type->tokAt(2); if (!type) { syntaxError(tok1); } while (Token::Match(type->next(), "const|volatile")) type = type->next(); type = type->next(); } // check for pointers and references std::list pointers; while (Token::Match(type, "*|&|&&|const")) { pointers.push_back(type->str()); type = type->next(); } // check for array if (type && type->str() == "[") { do { if (!arrayStart) arrayStart = type; bool atEnd = false; while (!atEnd) { while (type->next() && !Token::Match(type->next(), ";|,")) { type = type->next(); } if (!type->next()) syntaxError(type); // invalid input else if (type->next()->str() == ";") atEnd = true; else if (type->str() == "]") atEnd = true; else type = type->next(); } type = type->next(); } while (type && type->str() == "["); } // make sure we are in a good state if (!tok1 || !tok1->next()) break; // bail Token* after = tok1->next(); // check if type was parsed if (type && type == usingEnd) { // check for array syntax and add type around variable if (arrayStart) { if (Token::Match(tok1->next(), "%name%")) { TokenList::copyTokens(tok1->next(), arrayStart, usingEnd->previous()); TokenList::copyTokens(tok1, start, arrayStart->previous()); tok1->deleteThis(); substitute = true; } } else { // add some qualification back if needed std::string removed1 = removed; std::string::size_type idx = removed1.rfind(" ::"); if (idx != std::string::npos) removed1.resize(idx); if (scopesMatch(removed1, scope, &scopeInfo1)) { ScopeInfo3 * tempScope = currentScope; while (tempScope->parent) { if (tempScope->recordTypes.find(start->str()) != tempScope->recordTypes.end()) { std::string::size_type spaceIdx = 0; std::string::size_type startIdx = 0; while ((spaceIdx = removed1.find(" ", startIdx)) != std::string::npos) { tok1->previous()->insertToken(removed1.substr(startIdx, spaceIdx - startIdx)); startIdx = spaceIdx + 1; } tok1->previous()->insertToken(removed1.substr(startIdx)); tok1->previous()->insertToken("::"); break; } idx = removed1.rfind(" ::"); if (idx == std::string::npos) break; removed1.resize(idx); tempScope = tempScope->parent; } } // just replace simple type aliases TokenList::copyTokens(tok1, start, usingEnd->previous()); tok1->deleteThis(); substitute = true; } } else { skip = true; if (mSettings->debugwarnings && mErrorLogger) { std::string str; for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) { if (!str.empty()) str += ' '; str += tok3->str(); } str += " ;"; std::list callstack(1, usingStart); mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing", "Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal)); } } tok1 = after; } if (!skip) usingList.emplace_back(usingStart, usingEnd); } // delete all used type alias definitions for (std::list::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) { Token *usingStart = it->startTok; Token *usingEnd = it->endTok; if (usingStart->previous()) { if (usingEnd->next()) Token::eraseTokens(usingStart->previous(), usingEnd->next()); else { Token::eraseTokens(usingStart->previous(), usingEnd); usingEnd->deleteThis(); } } else { if (usingEnd->next()) { Token::eraseTokens(usingStart, usingEnd->next()); usingStart->deleteThis(); } else { // this is the only code being checked so leave ';' Token::eraseTokens(usingStart, usingEnd); usingStart->deleteThis(); } } } return substitute; } void Tokenizer::simplifyMulAndParens() { if (!list.front()) return; for (Token *tok = list.front()->tokAt(3); tok; tok = tok->next()) { if (!tok->isName()) continue; //fix ticket #2784 - improved by ticket #3184 int closedPars = 0; Token *tokend = tok->next(); Token *tokbegin = tok->previous(); while (tokend && tokend->str() == ")") { ++closedPars; tokend = tokend->next(); } if (!tokend || !(tokend->isAssignmentOp())) continue; while (Token::Match(tokbegin, "&|(")) { if (tokbegin->str() == "&") { if (Token::Match(tokbegin->tokAt(-2), "[;{}&(] *")) { //remove '* &' tokbegin = tokbegin->tokAt(-2); tokbegin->deleteNext(2); } else if (Token::Match(tokbegin->tokAt(-3), "[;{}&(] * (")) { if (closedPars == 0) break; --closedPars; //remove ')' tok->deleteNext(); //remove '* ( &' tokbegin = tokbegin->tokAt(-3); tokbegin->deleteNext(3); } else break; } else if (tokbegin->str() == "(") { if (closedPars == 0) break; //find consecutive opening parentheses int openPars = 0; while (tokbegin && tokbegin->str() == "(" && openPars <= closedPars) { ++openPars; tokbegin = tokbegin->previous(); } if (!tokbegin || openPars > closedPars) break; if ((openPars == closedPars && Token::Match(tokbegin, "[;{}]")) || Token::Match(tokbegin->tokAt(-2), "[;{}&(] * &") || Token::Match(tokbegin->tokAt(-3), "[;{}&(] * ( &")) { //remove the excessive parentheses around the variable while (openPars > 0) { tok->deleteNext(); tokbegin->deleteNext(); --closedPars; --openPars; } } else break; } } } } bool Tokenizer::createTokens(std::istream &code, const std::string& FileName) { // make sure settings specified assert(mSettings); return list.createTokens(code, FileName); } void Tokenizer::createTokens(simplecpp::TokenList&& tokenList) { // make sure settings specified assert(mSettings); list.createTokens(std::move(tokenList)); } bool Tokenizer::simplifyTokens1(const std::string &configuration) { // Fill the map mTypeSize.. fillTypeSizes(); mConfiguration = configuration; if (!simplifyTokenList1(list.getFiles().front().c_str())) return false; if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::createAst", mSettings->showtime, mTimerResults); list.createAst(); list.validateAst(); } else { list.createAst(); list.validateAst(); } if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::createSymbolDatabase", mSettings->showtime, mTimerResults); createSymbolDatabase(); } else { createSymbolDatabase(); } if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::setValueType", mSettings->showtime, mTimerResults); mSymbolDatabase->setValueTypeInTokenList(true); } else { mSymbolDatabase->setValueTypeInTokenList(true); } if (!mSettings->buildDir.empty()) Summaries::create(this, configuration); // TODO: do not run valueflow if no checks are being performed at all - e.g. unusedFunctions only const char* disableValueflowEnv = std::getenv("DISABLE_VALUEFLOW"); const bool doValueFlow = !disableValueflowEnv || (std::strcmp(disableValueflowEnv, "1") != 0); if (doValueFlow) { if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::ValueFlow", mSettings->showtime, mTimerResults); ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); } else { ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); } } // Warn about unhandled character literals if (mSettings->severity.isEnabled(Severity::portability)) { for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->tokType() == Token::eChar && tok->values().empty()) { try { simplecpp::characterLiteralToLL(tok->str()); } catch (const std::exception &e) { unhandledCharLiteral(tok, e.what()); } } } } if (doValueFlow) { mSymbolDatabase->setArrayDimensionsUsingValueFlow(); } printDebugOutput(1); return true; } bool Tokenizer::tokenize(std::istream &code, const char FileName[], const std::string &configuration) { if (!createTokens(code, FileName)) return false; return simplifyTokens1(configuration); } //--------------------------------------------------------------------------- void Tokenizer::findComplicatedSyntaxErrorsInTemplates() { validate(); mTemplateSimplifier->checkComplicatedSyntaxErrorsInTemplates(); } void Tokenizer::checkForEnumsWithTypedef() { for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "enum %name% {")) { tok = tok->tokAt(2); const Token *tok2 = Token::findsimplematch(tok, "typedef", tok->link()); if (tok2) syntaxError(tok2); tok = tok->link(); } } } void Tokenizer::fillTypeSizes() { mTypeSize.clear(); mTypeSize["char"] = 1; mTypeSize["_Bool"] = mSettings->sizeof_bool; mTypeSize["bool"] = mSettings->sizeof_bool; mTypeSize["short"] = mSettings->sizeof_short; mTypeSize["int"] = mSettings->sizeof_int; mTypeSize["long"] = mSettings->sizeof_long; mTypeSize["float"] = mSettings->sizeof_float; mTypeSize["double"] = mSettings->sizeof_double; mTypeSize["wchar_t"] = mSettings->sizeof_wchar_t; mTypeSize["size_t"] = mSettings->sizeof_size_t; mTypeSize["*"] = mSettings->sizeof_pointer; } void Tokenizer::combineOperators() { const bool cpp = isCPP(); // Combine tokens.. for (Token *tok = list.front(); tok && tok->next(); tok = tok->next()) { const char c1 = tok->str()[0]; if (tok->str().length() == 1 && tok->next()->str().length() == 1) { const char c2 = tok->next()->str()[0]; // combine +-*/ and = if (c2 == '=' && (std::strchr("+-*/%|^=!<>", c1))) { // skip templates if (cpp && (tok->str() == ">" || Token::simpleMatch(tok->previous(), "> *"))) { const Token* opening = tok->str() == ">" ? tok->findOpeningBracket() : tok->previous()->findOpeningBracket(); if (opening && Token::Match(opening->previous(), "%name%")) continue; } tok->str(tok->str() + c2); tok->deleteNext(); continue; } } else if (tok->next()->str() == "=") { if (tok->str() == ">>") { tok->str(">>="); tok->deleteNext(); } else if (tok->str() == "<<") { tok->str("<<="); tok->deleteNext(); } } else if (cpp && (c1 == 'p' || c1 == '_') && Token::Match(tok, "private|protected|public|__published : !!:")) { bool simplify = false; int par = 0; for (const Token *prev = tok->previous(); prev; prev = prev->previous()) { if (prev->str() == ")") { ++par; } else if (prev->str() == "(") { if (par == 0U) break; --par; } if (par != 0U || prev->str() == "(") continue; if (Token::Match(prev, "[;{}]")) { simplify = true; break; } if (prev->isName() && prev->isUpperCaseName()) continue; if (prev->isName() && endsWith(prev->str(), ':')) simplify = true; break; } if (simplify) { tok->str(tok->str() + ":"); tok->deleteNext(); } } else if (tok->str() == "->") { // If the preceding sequence is "( & %name% )", replace it by "%name%" Token *t = tok->tokAt(-4); if (Token::Match(t, "( & %name% )") && !Token::simpleMatch(t->previous(), ">")) { t->deleteThis(); t->deleteThis(); t->deleteNext(); tok->str("."); } else { tok->str("."); tok->originalName("->"); } } } } void Tokenizer::combineStringAndCharLiterals() { // Combine strings for (Token *tok = list.front(); tok; tok = tok->next()) { if (!isStringLiteral(tok->str())) continue; tok->str(simplifyString(tok->str())); while (Token::Match(tok->next(), "%str%") || Token::Match(tok->next(), "_T|_TEXT|TEXT ( %str% )")) { if (tok->next()->isName()) { if (!mSettings->isWindowsPlatform()) break; tok->deleteNext(2); tok->next()->deleteNext(); } // Two strings after each other, combine them tok->concatStr(simplifyString(tok->next()->str())); tok->deleteNext(); } } } void Tokenizer::concatenateNegativeNumberAndAnyPositive() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "?|:|,|(|[|{|return|case|sizeof|%op% +|-") || tok->tokType() == Token::eIncDecOp) continue; while (tok->str() != ">" && tok->next() && tok->next()->str() == "+") tok->deleteNext(); if (Token::Match(tok->next(), "- %num%")) { tok->deleteNext(); tok->next()->str("-" + tok->next()->str()); } } } void Tokenizer::simplifyExternC() { if (isC()) return; // Add attributes to all tokens within `extern "C"` inlines and blocks, and remove the `extern "C"` tokens. for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "extern \"C\"")) { Token *tok2 = tok->next(); if (tok->strAt(2) == "{") { tok2 = tok2->next(); // skip { while ((tok2 = tok2->next()) && tok2 != tok->linkAt(2)) tok2->isExternC(true); tok->linkAt(2)->deleteThis(); // } tok->deleteNext(2); // "C" { } else { while ((tok2 = tok2->next()) && !Token::simpleMatch(tok2, ";")) tok2->isExternC(true); tok->deleteNext(); // "C" } tok->deleteThis(); // extern } } } void Tokenizer::simplifyRoundCurlyParentheses() { for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "[;{}:] ( {") && Token::simpleMatch(tok->linkAt(2), "} ) ;")) { if (tok->str() == ":" && !Token::Match(tok->tokAt(-2),"[;{}] %type% :")) break; Token *end = tok->linkAt(2)->tokAt(-3); if (Token::Match(end, "[;{}] %num%|%str% ;")) end->deleteNext(2); tok->linkAt(2)->previous()->deleteNext(3); tok->deleteNext(2); } if (Token::Match(tok, "( { %bool%|%char%|%num%|%str%|%name% ; } )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(3); } } } void Tokenizer::simplifySQL() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL")) continue; const Token *end = findSQLBlockEnd(tok); if (end == nullptr) syntaxError(nullptr); const std::string instruction = tok->stringifyList(end); // delete all tokens until the embedded SQL block end Token::eraseTokens(tok, end); // insert "asm ( "instruction" ) ;" tok->str("asm"); // it can happen that 'end' is NULL when wrong code is inserted if (!tok->next()) tok->insertToken(";"); tok->insertToken(")"); tok->insertToken("\"" + instruction + "\""); tok->insertToken("("); // jump to ';' and continue tok = tok->tokAt(3); } } void Tokenizer::simplifyArrayAccessSyntax() { // 0[a] -> a[0] for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isNumber() && Token::Match(tok, "%num% [ %name% ]")) { const std::string number(tok->str()); Token* indexTok = tok->tokAt(2); tok->str(indexTok->str()); tok->varId(indexTok->varId()); indexTok->str(number); } } } void Tokenizer::simplifyRedundantConsecutiveBraces() { // Remove redundant consecutive braces, i.e. '.. { { .. } } ..' -> '.. { .. } ..'. for (Token *tok = list.front(); tok;) { if (Token::simpleMatch(tok, "= {")) { tok = tok->linkAt(1); } else if (Token::simpleMatch(tok, "{ {") && Token::simpleMatch(tok->next()->link(), "} }")) { //remove internal parentheses tok->next()->link()->deleteThis(); tok->deleteNext(); } else tok = tok->next(); } } void Tokenizer::simplifyDoublePlusAndDoubleMinus() { // Convert - - into + and + - into - for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->next()) { if (tok->str() == "+") { if (tok->next()->str()[0] == '-') { tok = tok->next(); if (tok->str().size() == 1) { tok = tok->previous(); tok->str("-"); tok->deleteNext(); } else if (tok->isNumber()) { tok->str(tok->str().substr(1)); tok = tok->previous(); tok->str("-"); } continue; } } else if (tok->str() == "-") { if (tok->next()->str()[0] == '-') { tok = tok->next(); if (tok->str().size() == 1) { tok = tok->previous(); tok->str("+"); tok->deleteNext(); } else if (tok->isNumber()) { tok->str(tok->str().substr(1)); tok = tok->previous(); tok->str("+"); } continue; } } break; } } } /** Specify array size if it hasn't been given */ void Tokenizer::arraySize() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName() || !Token::Match(tok, "%var% [ ] =")) continue; bool addlength = false; if (Token::Match(tok, "%var% [ ] = { %str% } ;")) { Token *t = tok->tokAt(3); t->deleteNext(); t->next()->deleteNext(); addlength = true; } if (addlength || Token::Match(tok, "%var% [ ] = %str% ;")) { tok = tok->next(); const int sz = Token::getStrArraySize(tok->tokAt(3)); tok->insertToken(MathLib::toString(sz)); tok = tok->tokAt(5); } else if (Token::Match(tok, "%var% [ ] = {")) { MathLib::biguint sz = 1; tok = tok->next(); Token *end = tok->linkAt(3); for (Token *tok2 = tok->tokAt(4); tok2 && tok2 != end; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "{|(|[|<")) { if (tok2->str() == "[" && tok2->link()->strAt(1) == "=") { // designated initializer if (Token::Match(tok2, "[ %num% ]")) sz = std::max(sz, MathLib::toULongNumber(tok2->strAt(1)) + 1U); else { sz = 0; break; } } tok2 = tok2->link(); } else if (tok2->str() == ",") { if (!Token::Match(tok2->next(), "[},]")) ++sz; else { tok2 = tok2->previous(); tok2->deleteNext(); } } } if (sz != 0) tok->insertToken(MathLib::toString(sz)); tok = end->next() ? end->next() : end; } } } static Token *skipTernaryOp(Token *tok) { int colonLevel = 1; while (nullptr != (tok = tok->next())) { if (tok->str() == "?") { ++colonLevel; } else if (tok->str() == ":") { --colonLevel; if (colonLevel == 0) { tok = tok->next(); break; } } if (tok->link() && Token::Match(tok, "[(<]")) tok = tok->link(); else if (Token::Match(tok->next(), "[{};)]")) break; } if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?' return nullptr; return tok; } // Skips until the colon at the end of the case label, the argument must point to the "case" token. // In case of success returns the colon token. // In case of failure returns the token that caused the error. static Token *skipCaseLabel(Token *tok) { assert(tok->str() == "case"); while (nullptr != (tok = tok->next())) { if (Token::Match(tok, "(|[")) tok = tok->link(); else if (tok->str() == "?") { Token * tok1 = skipTernaryOp(tok); if (!tok1) return tok; tok = tok1; } if (Token::Match(tok, "[:{};]")) return tok; } return nullptr; } const Token * Tokenizer::startOfExecutableScope(const Token * tok) { if (tok->str() != ")") return nullptr; tok = isFunctionHead(tok, ":{", true); if (Token::Match(tok, ": %name% [({]")) { while (Token::Match(tok, "[:,] %name% [({]")) tok = tok->linkAt(2)->next(); } return (tok && tok->str() == "{") ? tok : nullptr; } /** simplify labels and case|default in the code: add a ";" if not already in.*/ void Tokenizer::simplifyLabelsCaseDefault() { const bool cpp = isCPP(); bool executablescope = false; int indentLevel = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { // Simplify labels in the executable scope.. Token *start = const_cast(startOfExecutableScope(tok)); if (start) { tok = start; executablescope = true; } if (!executablescope) continue; if (tok->str() == "{") { if (tok->previous()->str() == "=") tok = tok->link(); else ++indentLevel; } else if (tok->str() == "}") { --indentLevel; if (indentLevel == 0) { executablescope = false; continue; } } else if (Token::Match(tok, "(|[")) tok = tok->link(); if (Token::Match(tok, "[;{}:] case")) { tok = skipCaseLabel(tok->next()); if (!tok) break; if (tok->str() != ":" || tok->strAt(-1) == "case" || !tok->next()) syntaxError(tok); if (tok->next()->str() != ";" && tok->next()->str() != "case") tok->insertToken(";"); else tok = tok->previous(); } else if (Token::Match(tok, "[;{}] %name% : !!;")) { if (!cpp || !Token::Match(tok->next(), "class|struct|enum")) { tok = tok->tokAt(2); tok->insertToken(";"); } } } } void Tokenizer::simplifyCaseRange() { for (Token* tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "case %num%|%char% ... %num%|%char% :")) { const MathLib::bigint start = MathLib::toLongNumber(tok->strAt(1)); MathLib::bigint end = MathLib::toLongNumber(tok->strAt(3)); end = std::min(start + 50, end); // Simplify it 50 times at maximum if (start < end) { tok = tok->tokAt(2); tok->str(":"); tok->insertToken("case"); for (MathLib::bigint i = end-1; i > start; i--) { tok->insertToken(":"); tok->insertToken(MathLib::toString(i)); tok->insertToken("case"); } } } } } void Tokenizer::calculateScopes() { for (auto *tok = list.front(); tok; tok = tok->next()) tok->scopeInfo(nullptr); std::string nextScopeNameAddition; std::shared_ptr primaryScope = std::make_shared("", nullptr); list.front()->scopeInfo(primaryScope); for (Token* tok = list.front(); tok; tok = tok->next()) { if (tok == list.front() || !tok->scopeInfo()) { if (tok != list.front()) tok->scopeInfo(tok->previous()->scopeInfo()); if (Token::Match(tok, "using namespace %name% ::|<|;")) { std::string usingNamespaceName; for (const Token* namespaceNameToken = tok->tokAt(2); namespaceNameToken && namespaceNameToken->str() != ";"; namespaceNameToken = namespaceNameToken->next()) { usingNamespaceName += namespaceNameToken->str(); usingNamespaceName += " "; } if (usingNamespaceName.length() > 0) usingNamespaceName = usingNamespaceName.substr(0, usingNamespaceName.length() - 1); tok->scopeInfo()->usingNamespaces.insert(usingNamespaceName); } else if (Token::Match(tok, "namespace|class|struct|union %name% {|::|:|<")) { for (Token* nameTok = tok->next(); nameTok && !Token::Match(nameTok, "{|:"); nameTok = nameTok->next()) { if (Token::Match(nameTok, ";|<")) { nextScopeNameAddition = ""; break; } nextScopeNameAddition.append(nameTok->str()); nextScopeNameAddition.append(" "); } if (nextScopeNameAddition.length() > 0) nextScopeNameAddition = nextScopeNameAddition.substr(0, nextScopeNameAddition.length() - 1); } if (Token::simpleMatch(tok, "{")) { // This might be the opening of a member function Token *tok1 = tok; while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->previous() && tok1->strAt(-1) == ")") { bool member = true; tok1 = tok1->linkAt(-1); if (Token::Match(tok1->previous(), "throw|noexcept")) { tok1 = tok1->previous(); while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->strAt(-1) != ")") member = false; } else if (Token::Match(tok->tokAt(-2), ":|, %name%")) { tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) != ")") member = false; } if (member) { if (tok1->strAt(-1) == ">") tok1 = tok1->previous()->findOpeningBracket(); if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) { tok1 = tok1->tokAt(-2); std::string scope = tok1->strAt(-1); while (Token::Match(tok1->tokAt(-2), ":: %name%")) { scope = tok1->strAt(-3) + " :: " + scope; tok1 = tok1->tokAt(-2); } if (!nextScopeNameAddition.empty() && !scope.empty()) nextScopeNameAddition += " :: "; nextScopeNameAddition += scope; } } } // New scope is opening, record it here std::shared_ptr newScopeInfo = std::make_shared(tok->scopeInfo()->name, tok->link(), tok->scopeInfo()->usingNamespaces); if (newScopeInfo->name != "" && nextScopeNameAddition != "") newScopeInfo->name.append(" :: "); newScopeInfo->name.append(nextScopeNameAddition); nextScopeNameAddition = ""; if (tok->link()) tok->link()->scopeInfo(tok->scopeInfo()); tok->scopeInfo(newScopeInfo); } } } } void Tokenizer::simplifyTemplates() { if (isC()) return; mTemplateSimplifier->simplifyTemplates( #ifdef MAXTIME mMaxTime, #else 0, // ignored #endif mCodeWithTemplates); } //--------------------------------------------------------------------------- static bool setVarIdParseDeclaration(const Token **tok, const std::map &variableId, bool executableScope, bool cpp, bool c) { const Token *tok2 = *tok; if (!tok2->isName()) return false; int typeCount = 0; int singleNameCount = 0; bool hasstruct = false; // Is there a "struct" or "class"? bool bracket = false; bool ref = false; while (tok2) { if (tok2->isName()) { if (cpp && Token::Match(tok2, "namespace|public|private|protected")) return false; if (cpp && Token::simpleMatch(tok2, "decltype (")) { typeCount = 1; tok2 = tok2->linkAt(1)->next(); continue; } if (Token::Match(tok2, "struct|union|enum") || (!c && Token::Match(tok2, "class|typename"))) { hasstruct = true; typeCount = 0; singleNameCount = 0; } else if (tok2->str() == "const") { // just skip "const" } else if (!hasstruct && variableId.find(tok2->str()) != variableId.end() && tok2->previous()->str() != "::") { ++typeCount; tok2 = tok2->next(); if (!tok2 || tok2->str() != "::") break; } else { if (tok2->str() != "void" || Token::Match(tok2, "void const| *|(")) // just "void" cannot be a variable type ++typeCount; ++singleNameCount; } } else if (!c && ((TemplateSimplifier::templateParameters(tok2) > 0) || Token::simpleMatch(tok2, "< >") /* Ticket #4764 */)) { const Token *start = *tok; if (Token::Match(start->previous(), "%or%|%oror%|&&|&|^|+|-|*|/")) return false; const Token * const closingBracket = tok2->findClosingBracket(); if (closingBracket == nullptr) { /* Ticket #8151 */ throw tok2; } tok2 = closingBracket; if (tok2->str() != ">") break; singleNameCount = 1; if (Token::Match(tok2, "> %name% %or%|%oror%|&&|&|^|+|-|*|/") && !Token::Match(tok2, "> const [*&]")) return false; if (Token::Match(tok2, "> %name% )")) { if (Token::Match(tok2->linkAt(2)->previous(), "if|for|while (")) return false; if (!Token::Match(tok2->linkAt(2)->previous(), "%name% (")) return false; } } else if (Token::Match(tok2, "&|&&")) { ref = !bracket; } else if (singleNameCount >= 1 && Token::Match(tok2, "( [*&]") && Token::Match(tok2->link()->next(), "(|[")) { bracket = true; // Skip: Seems to be valid pointer to array or function pointer } else if (singleNameCount >= 1 && Token::Match(tok2, "( * %name% [") && Token::Match(tok2->linkAt(3), "] ) [;,]")) { bracket = true; } else if (tok2->str() == "::") { singleNameCount = 0; } else if (tok2->str() != "*" && tok2->str() != "::" && tok2->str() != "...") { break; } tok2 = tok2->next(); } if (tok2) { bool isLambdaArg = false; { const Token *tok3 = (*tok)->previous(); if (tok3 && tok3->str() == ",") { while (tok3 && !Token::Match(tok3,";|(|[|{")) { if (Token::Match(tok3, ")|]")) tok3 = tok3->link(); tok3 = tok3->previous(); } if (tok3 && executableScope && Token::Match(tok3->previous(), "%name% (")) { const Token *fdecl = tok3->previous(); int count = 0; while (Token::Match(fdecl, "%name%|*")) { fdecl = fdecl->previous(); count++; } if (!Token::Match(fdecl, "[;{}] %name%") || count <= 1) return false; } } if (cpp && tok3 && Token::simpleMatch(tok3->previous(), "] (") && Token::simpleMatch(tok3->link(), ") {")) isLambdaArg = true; } *tok = tok2; // In executable scopes, references must be assigned // Catching by reference is an exception if (executableScope && ref && !isLambdaArg) { if (Token::Match(tok2, "(|=|{|:")) ; // reference is assigned => ok else if (tok2->str() != ")" || tok2->link()->strAt(-1) != "catch") return false; // not catching by reference => not declaration } } // Check if array declaration is valid (#2638) // invalid declaration: AAA a[4] = 0; if (typeCount >= 2 && executableScope && tok2 && tok2->str() == "[") { const Token *tok3 = tok2->link()->next(); while (tok3 && tok3->str() == "[") { tok3 = tok3->link()->next(); } if (Token::Match(tok3, "= %num%")) return false; } return (typeCount >= 2 && tok2 && Token::Match(tok2->tokAt(-2), "!!:: %type%")); } void Tokenizer::setVarIdStructMembers(Token **tok1, std::map>& structMembers, nonneg int *varId) const { Token *tok = *tok1; if (Token::Match(tok, "%name% = { . %name% =|{")) { const int struct_varid = tok->varId(); if (struct_varid == 0) return; std::map& members = structMembers[struct_varid]; tok = tok->tokAt(3); while (tok->str() != "}") { if (Token::Match(tok, "{|[|(")) tok = tok->link(); if (Token::Match(tok->previous(), "[,{] . %name% =|{")) { tok = tok->next(); const std::map::iterator it = members.find(tok->str()); if (it == members.end()) { members[tok->str()] = ++(*varId); tok->varId(*varId); } else { tok->varId(it->second); } } tok = tok->next(); } return; } while (Token::Match(tok->next(), ")| . %name% !!(")) { // Don't set varid for trailing return type if (tok->strAt(1) == ")" && tok->linkAt(1)->previous()->isName() && isFunctionHead(tok->linkAt(1), "{|;")) { tok = tok->tokAt(3); continue; } const int struct_varid = tok->varId(); tok = tok->tokAt(2); if (struct_varid == 0) continue; if (tok->str() == ".") tok = tok->next(); // Don't set varid for template function if (TemplateSimplifier::templateParameters(tok->next()) > 0) break; std::map& members = structMembers[struct_varid]; const std::map::iterator it = members.find(tok->str()); if (it == members.end()) { members[tok->str()] = ++(*varId); tok->varId(*varId); } else { tok->varId(it->second); } } // tok can't be null *tok1 = tok; } void Tokenizer::setVarIdClassDeclaration(const Token * const startToken, const VariableMap &variableMap, const nonneg int scopeStartVarId, std::map>& structMembers) { // end of scope const Token * const endToken = startToken->link(); // determine class name std::string className; for (const Token *tok = startToken->previous(); tok; tok = tok->previous()) { if (!tok->isName() && tok->str() != ":") break; if (Token::Match(tok, "class|struct|enum %type% [:{]")) { className = tok->next()->str(); break; } } // replace varids.. int indentlevel = 0; bool initList = false; bool inEnum = false; const Token *initListArgLastToken = nullptr; for (Token *tok = startToken->next(); tok != endToken; tok = tok->next()) { if (!tok) syntaxError(nullptr); if (initList) { if (tok == initListArgLastToken) initListArgLastToken = nullptr; else if (!initListArgLastToken && Token::Match(tok->previous(), "%name%|>|>> {|(") && Token::Match(tok->link(), "}|) ,|{")) initListArgLastToken = tok->link(); } if (tok->str() == "{") { inEnum = isEnumStart(tok); if (initList && !initListArgLastToken) initList = false; ++indentlevel; } else if (tok->str() == "}") { --indentlevel; inEnum = false; } else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %name% [({]")) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) { tok->varId(it->second); } } else if (tok->isName() && tok->varId() <= scopeStartVarId) { if (indentlevel > 0 || initList) { if (Token::Match(tok->previous(), "::|.") && tok->strAt(-2) != "this" && !Token::simpleMatch(tok->tokAt(-5), "( * this ) .")) continue; if (!tok->next()) syntaxError(nullptr); if (tok->next()->str() == "::") { if (tok->str() == className) tok = tok->tokAt(2); else continue; } if (!inEnum) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); } } } } else if (indentlevel == 0 && tok->str() == ":" && !initListArgLastToken) initList = true; } } // Update the variable ids.. // Parse each function.. void Tokenizer::setVarIdClassFunction(const std::string &classname, Token * const startToken, const Token * const endToken, const std::map &varlist, std::map>& structMembers, nonneg int *varId_) { for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { if (tok2->varId() != 0 || !tok2->isName()) continue; if (Token::Match(tok2->tokAt(-2), ("!!" + classname + " ::").c_str())) continue; if (Token::Match(tok2->tokAt(-4), "%name% :: %name% ::")) // Currently unsupported continue; if (Token::Match(tok2->tokAt(-2), "!!this .") && !Token::simpleMatch(tok2->tokAt(-5), "( * this ) .")) continue; const std::map::const_iterator it = varlist.find(tok2->str()); if (it != varlist.end()) { tok2->varId(it->second); setVarIdStructMembers(&tok2, structMembers, varId_); } } } void Tokenizer::setVarId() { // Clear all variable ids for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isName()) tok->varId(0); } setPodTypes(); setVarIdPass1(); setVarIdPass2(); } // Variable declarations can't start with "return" etc. #define NOTSTART_C "NOT", "case", "default", "goto", "not", "return", "sizeof", "typedef" static const std::unordered_set notstart_c = { NOTSTART_C }; static const std::unordered_set notstart_cpp = { NOTSTART_C, "delete", "friend", "new", "throw", "using", "virtual", "explicit", "const_cast", "dynamic_cast", "reinterpret_cast", "static_cast", "template" }; void Tokenizer::setVarIdPass1() { // Variable declarations can't start with "return" etc. const std::unordered_set& notstart = (isC()) ? notstart_c : notstart_cpp; VariableMap variableMap; std::map> structMembers; std::stack scopeStack; scopeStack.push(VarIdScopeInfo()); std::stack functionDeclEndStack; const Token *functionDeclEndToken = nullptr; bool initlist = false; bool inlineFunction = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isOp()) continue; if (tok == functionDeclEndToken) { functionDeclEndStack.pop(); functionDeclEndToken = functionDeclEndStack.empty() ? nullptr : functionDeclEndStack.top(); if (tok->str() == ":") initlist = true; else if (tok->str() == ";") { if (!variableMap.leaveScope()) cppcheckError(tok); } else if (tok->str() == "{") { scopeStack.push(VarIdScopeInfo(true, scopeStack.top().isStructInit || tok->strAt(-1) == "=", /*isEnum=*/ false, *variableMap.getVarId())); // check if this '{' is a start of an "if" body const Token * ifToken = tok->previous(); if (ifToken && ifToken->str() == ")") ifToken = ifToken->link(); else ifToken = nullptr; if (ifToken) ifToken = ifToken->previous(); if (ifToken && ifToken->str() == "if") { // open another scope to differentiate between variables declared in the "if" condition and in the "if" body variableMap.enterScope(); } } } else if (!initlist && tok->str()=="(") { const Token * newFunctionDeclEnd = nullptr; if (!scopeStack.top().isExecutable) newFunctionDeclEnd = isFunctionHead(tok, "{:;"); else { Token const * const tokenLinkNext = tok->link()->next(); if (tokenLinkNext && tokenLinkNext->str() == "{") // might be for- or while-loop or if-statement newFunctionDeclEnd = tokenLinkNext; } if (newFunctionDeclEnd && newFunctionDeclEnd != functionDeclEndToken) { functionDeclEndStack.push(newFunctionDeclEnd); functionDeclEndToken = newFunctionDeclEnd; variableMap.enterScope(); } } else if (Token::Match(tok, "{|}")) { inlineFunction = false; const Token * const startToken = (tok->str() == "{") ? tok : tok->link(); // parse anonymous unions as part of the current scope if (!Token::Match(startToken->previous(), "union|struct|enum {") && !(initlist && Token::Match(startToken->previous(), "%name%|>|>>") && Token::Match(startToken->link(), "} ,|{"))) { if (tok->str() == "{") { bool isExecutable; const Token *prev = tok->previous(); while (Token::Match(prev, "%name%|.")) prev = prev->previous(); const bool isLambda = prev && prev->str() == ")" && Token::simpleMatch(prev->link()->previous(), "] ("); if ((!isLambda && (tok->strAt(-1) == ")" || Token::Match(tok->tokAt(-2), ") %type%"))) || (initlist && tok->strAt(-1) == "}")) { isExecutable = true; } else { isExecutable = ((scopeStack.top().isExecutable || initlist || tok->strAt(-1) == "else") && !isClassStructUnionEnumStart(tok)); if (!(scopeStack.top().isStructInit || tok->strAt(-1) == "=")) variableMap.enterScope(); } initlist = false; scopeStack.push(VarIdScopeInfo(isExecutable, scopeStack.top().isStructInit || tok->strAt(-1) == "=", isEnumStart(tok), *variableMap.getVarId())); } else { /* if (tok->str() == "}") */ bool isNamespace = false; for (const Token *tok1 = tok->link()->previous(); tok1 && tok1->isName(); tok1 = tok1->previous()) { if (tok1->str() == "namespace") { isNamespace = true; break; } } // Set variable ids in class declaration.. if (!initlist && !isC() && !scopeStack.top().isExecutable && tok->link() && !isNamespace) { setVarIdClassDeclaration(tok->link(), variableMap, scopeStack.top().startVarid, structMembers); } if (!scopeStack.top().isStructInit) { variableMap.leaveScope(); // check if this '}' is an end of an "else" body or an "if" body without an "else" part const Token * ifToken = startToken->previous(); if (ifToken && ifToken->str() == ")") ifToken = ifToken->link()->previous(); else ifToken = nullptr; if (startToken->strAt(-1) == "else" || (ifToken && ifToken->str() == "if" && tok->strAt(1) != "else")) { // leave the extra scope used to differentiate between variables declared in the "if" condition and in the "if" body variableMap.leaveScope(); } } scopeStack.pop(); if (scopeStack.empty()) { // should be impossible scopeStack.push(VarIdScopeInfo()); } } } } if (!scopeStack.top().isStructInit && (tok == list.front() || Token::Match(tok, "[;{}]") || (tok->str() == "(" && isFunctionHead(tok,"{")) || (tok->str() == "(" && !scopeStack.top().isExecutable && isFunctionHead(tok,";:")) || (tok->str() == "," && (!scopeStack.top().isExecutable || inlineFunction)) || (tok->isName() && endsWith(tok->str(), ':')))) { // No variable declarations in sizeof if (Token::simpleMatch(tok->previous(), "sizeof (")) { continue; } if (Settings::terminated()) return; // locate the variable name.. const Token *tok2 = (tok->isName()) ? tok : tok->next(); // private: protected: public: etc while (tok2 && endsWith(tok2->str(), ':')) { tok2 = tok2->next(); } if (!tok2) break; // Variable declaration can't start with "return", etc if (notstart.find(tok2->str()) != notstart.end()) continue; if (!isC() && Token::simpleMatch(tok2, "const new")) continue; bool decl; if (isCPP() && mSettings->standards.cpp >= Standards::CPP17 && Token::Match(tok, "[(;{}] const| auto &|&&| [")) { // Structured bindings tok2 = Token::findsimplematch(tok, "["); if ((Token::simpleMatch(tok->previous(), "for (") && Token::simpleMatch(tok2->link(), "] :")) || Token::simpleMatch(tok2->link(), "] =")) { while (tok2 && tok2->str() != "]") { if (Token::Match(tok2, "%name% [,]]")) variableMap.addVariable(tok2->str()); tok2 = tok2->next(); } continue; } } try { /* Ticket #8151 */ decl = setVarIdParseDeclaration(&tok2, variableMap.map(), scopeStack.top().isExecutable, isCPP(), isC()); } catch (const Token * errTok) { syntaxError(errTok); } if (decl) { if (isCPP()) { if (Token *declTypeTok = Token::findsimplematch(tok, "decltype (", tok2)) { for (Token *declTok = declTypeTok->linkAt(1); declTok != declTypeTok; declTok = declTok->previous()) { if (declTok->isName() && !Token::Match(declTok->previous(), "::|.") && variableMap.hasVariable(declTok->str())) declTok->varId(variableMap.find(declTok->str())->second); } } } if (tok->str() == "(" && isFunctionHead(tok,"{") && scopeStack.top().isExecutable) inlineFunction = true; const Token* prev2 = tok2->previous(); if (Token::Match(prev2, "%type% [;[=,)]") && tok2->previous()->str() != "const") ; else if (Token::Match(prev2, "%type% :") && tok->strAt(-1) == "for") ; else if (Token::Match(prev2, "%type% ( !!)") && Token::simpleMatch(tok2->link(), ") ;")) { // In C++ , a variable can't be called operator+ or something like that. if (isCPP() && prev2->isOperatorKeyword()) continue; const Token *tok3 = tok2->next(); if (!tok3->isStandardType() && tok3->str() != "void" && !Token::Match(tok3, "struct|union|class %type%") && tok3->str() != "." && !Token::Match(tok2->link()->previous(), "[&*]")) { if (!scopeStack.top().isExecutable) { // Detecting initializations with () in non-executable scope is hard and often impossible to be done safely. Thus, only treat code as a variable that definitely is one. decl = false; bool rhs = false; for (; tok3; tok3 = tok3->nextArgumentBeforeCreateLinks2()) { if (tok3->str() == "=") { rhs = true; continue; } if (tok3->str() == ",") { rhs = false; continue; } if (rhs) continue; if (tok3->isLiteral() || (tok3->isName() && variableMap.hasVariable(tok3->str())) || tok3->isOp() || tok3->str() == "(" || notstart.find(tok3->str()) != notstart.end()) { decl = true; break; } } } } else decl = false; } else if (isCPP() && Token::Match(prev2, "%type% {") && Token::simpleMatch(tok2->link(), "} ;")) { // C++11 initialization style if (tok2->link() != tok2->next() && // add value-initialized variable T x{}; (Token::Match(prev2, "do|try|else") || Token::Match(prev2->tokAt(-2), "struct|class|:"))) continue; } else decl = false; if (decl) { variableMap.addVariable(prev2->str()); if (Token::simpleMatch(tok->previous(), "for (") && Token::Match(prev2, "%name% [=,]")) { for (const Token *tok3 = prev2->next(); tok3 && tok3->str() != ";"; tok3 = tok3->next()) { if (Token::Match(tok3, "[([]")) tok3 = tok3->link(); if (Token::Match(tok3, ", %name% [,=;]")) variableMap.addVariable(tok3->next()->str()); } } // set varid for template parameters.. tok = tok->next(); while (Token::Match(tok, "%name%|::")) tok = tok->next(); if (tok && tok->str() == "<") { const Token *end = tok->findClosingBracket(); while (tok != end) { if (tok->isName() && !(Token::simpleMatch(tok->next(), "<") && Token::Match(tok->tokAt(-2), "std :: %name%"))) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) tok->varId(it->second); } tok = tok->next(); } } tok = tok2->previous(); } } } if (tok->isName()) { // don't set variable id after a struct|enum|union if (Token::Match(tok->previous(), "struct|enum|union") || (isCPP() && tok->strAt(-1) == "class")) continue; if (!isC()) { if (tok->previous() && tok->previous()->str() == "::") continue; if (tok->next() && tok->next()->str() == "::") continue; if (Token::simpleMatch(tok->tokAt(-2), ":: template")) continue; } // function declaration inside executable scope? Function declaration is of form: type name "(" args ")" if (scopeStack.top().isExecutable && Token::Match(tok, "%name% [,)]")) { bool par = false; const Token *start, *end; // search begin of function declaration for (start = tok; Token::Match(start, "%name%|*|&|,|("); start = start->previous()) { if (start->str() == "(") { if (par) break; par = true; } if (Token::Match(start, "[(,]")) { if (!Token::Match(start, "[(,] %type% %name%|*|&")) break; } if (start->varId() > 0) break; } // search end of function declaration for (end = tok->next(); Token::Match(end, "%name%|*|&|,"); end = end->next()) {} // there are tokens which can't appear at the begin of a function declaration such as "return" const bool isNotstartKeyword = start->next() && notstart.find(start->next()->str()) != notstart.end(); // now check if it is a function declaration if (Token::Match(start, "[;{}] %type% %name%|*") && par && Token::simpleMatch(end, ") ;") && !isNotstartKeyword) // function declaration => don't set varid continue; } if (!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); } } } else if (Token::Match(tok, "::|. %name%")) { // Don't set varid after a :: or . token tok = tok->next(); } else if (tok->str() == ":" && Token::Match(tok->tokAt(-2), "class %type%")) { do { tok = tok->next(); } while (tok && (tok->isName() || tok->str() == ",")); if (!tok) break; tok = tok->previous(); } } mVarId = *variableMap.getVarId(); } namespace { struct Member { Member(const std::list &s, const std::list &ns, Token *t) : usingnamespaces(ns), scope(s), tok(t) {} std::list usingnamespaces; std::list scope; Token *tok; }; } static std::string getScopeName(const std::list &scopeInfo) { std::string ret; for (const ScopeInfo2 &si : scopeInfo) ret += (ret.empty() ? "" : " :: ") + (si.name); return ret; } static Token * matchMemberName(const std::list &scope, const Token *nsToken, Token *memberToken, const std::list &scopeInfo) { std::list::const_iterator scopeIt = scopeInfo.begin(); // Current scope.. for (std::list::const_iterator it = scope.begin(); it != scope.end(); ++it) { if (scopeIt == scopeInfo.end() || scopeIt->name != *it) return nullptr; ++scopeIt; } // using namespace.. if (nsToken) { while (Token::Match(nsToken, "%name% ::")) { if (scopeIt != scopeInfo.end() && nsToken->str() == scopeIt->name) { nsToken = nsToken->tokAt(2); ++scopeIt; } else { return nullptr; } } if (!Token::Match(nsToken, "%name% ;")) return nullptr; if (scopeIt == scopeInfo.end() || nsToken->str() != scopeIt->name) return nullptr; ++scopeIt; } // Parse member tokens.. while (scopeIt != scopeInfo.end()) { if (!Token::Match(memberToken, "%name% ::|<")) return nullptr; if (memberToken->str() != scopeIt->name) return nullptr; if (memberToken->next()->str() == "<") { memberToken = memberToken->next()->findClosingBracket(); if (!Token::simpleMatch(memberToken, "> ::")) return nullptr; } memberToken = memberToken->tokAt(2); ++scopeIt; } return Token::Match(memberToken, "~| %name%") ? memberToken : nullptr; } static Token * matchMemberName(const Member &member, const std::list &scopeInfo) { if (scopeInfo.empty()) return nullptr; // Does this member match without "using namespace".. Token *ret = matchMemberName(member.scope, nullptr, member.tok, scopeInfo); if (ret) return ret; // Try to match member using the "using namespace ..." namespaces.. for (const Token *ns : member.usingnamespaces) { ret = matchMemberName(member.scope, ns, member.tok, scopeInfo); if (ret) return ret; } return nullptr; } static Token * matchMemberVarName(const Member &var, const std::list &scopeInfo) { Token *tok = matchMemberName(var, scopeInfo); return Token::Match(tok, "%name% !!(") ? tok : nullptr; } static Token * matchMemberFunctionName(const Member &func, const std::list &scopeInfo) { Token *tok = matchMemberName(func, scopeInfo); return Token::Match(tok, "~| %name% (") ? tok : nullptr; } void Tokenizer::setVarIdPass2() { std::map> structMembers; // Member functions and variables in this source std::list allMemberFunctions; std::list allMemberVars; if (!isC()) { std::map endOfScope; std::list scope; std::list usingnamespaces; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) { if (Token::Match(tok, "using namespace %name% ::|;")) { Token *endtok = tok->tokAt(2); while (Token::Match(endtok, "%name% ::")) endtok = endtok->tokAt(2); if (Token::Match(endtok, "%name% ;")) usingnamespaces.push_back(tok->tokAt(2)); tok = endtok; continue; } else if (Token::Match(tok, "namespace %name% {")) { scope.push_back(tok->strAt(1)); endOfScope[tok->linkAt(2)] = tok->strAt(1); } } if (tok->str() == "}") { const std::map::iterator it = endOfScope.find(tok); if (it != endOfScope.end()) scope.remove(it->second); } Token* const tok1 = tok; if (Token::Match(tok->previous(), "!!:: %name% :: ~| %name%")) tok = tok->next(); else if (Token::Match(tok->previous(), "!!:: %name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) tok = tok->next()->findClosingBracket()->next(); else continue; while (Token::Match(tok, ":: ~| %name%")) { tok = tok->next(); if (tok->str() == "~") tok = tok->next(); else if (Token::Match(tok, "%name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) tok = tok->next()->findClosingBracket()->next(); else if (Token::Match(tok, "%name% ::")) tok = tok->next(); else break; } if (!tok->next()) syntaxError(tok); if (Token::Match(tok, "%name% (")) allMemberFunctions.emplace_back(scope, usingnamespaces, tok1); else allMemberVars.emplace_back(scope, usingnamespaces, tok1); } } std::list scopeInfo; // class members.. std::map> varsByClass; for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "}" && !scopeInfo.empty() && tok == scopeInfo.back().bodyEnd) scopeInfo.pop_back(); if (!Token::Match(tok, "namespace|class|struct %name% {|:|::|<")) continue; const std::string &scopeName(getScopeName(scopeInfo)); const std::string scopeName2(scopeName.empty() ? std::string() : (scopeName + " :: ")); std::list classnameTokens; classnameTokens.push_back(tok->next()); const Token* tokStart = tok->tokAt(2); while (Token::Match(tokStart, ":: %name%") || tokStart->str() == "<") { if (tokStart->str() == "<") { // skip the template part tokStart = tokStart->findClosingBracket()->next(); } else { classnameTokens.push_back(tokStart->next()); tokStart = tokStart->tokAt(2); } } std::string classname; for (const Token *it : classnameTokens) classname += (classname.empty() ? "" : " :: ") + it->str(); std::map &thisClassVars = varsByClass[scopeName2 + classname]; while (Token::Match(tokStart, ":|::|,|%name%")) { if (Token::Match(tokStart, "%name% <")) { tokStart = tokStart->next()->findClosingBracket(); if (tokStart) tokStart = tokStart->next(); continue; } if (Token::Match(tokStart, "%name% ,|{")) { std::string baseClassName = tokStart->str(); std::string scopeName3(scopeName2); while (!scopeName3.empty()) { const std::string name = scopeName3 + baseClassName; if (varsByClass.find(name) != varsByClass.end()) { baseClassName = name; break; } // Remove last scope name if (scopeName3.size() <= 8) break; scopeName3.erase(scopeName3.size() - 4); const std::string::size_type pos = scopeName3.rfind(" :: "); if (pos == std::string::npos) break; scopeName3.erase(pos + 4); } const std::map& baseClassVars = varsByClass[baseClassName]; thisClassVars.insert(baseClassVars.begin(), baseClassVars.end()); } tokStart = tokStart->next(); } if (!Token::simpleMatch(tokStart, "{")) continue; // What member variables are there in this class? for (const Token *it : classnameTokens) scopeInfo.emplace_back(it->str(), tokStart->link()); for (Token *tok2 = tokStart->next(); tok2 && tok2 != tokStart->link(); tok2 = tok2->next()) { // skip parentheses.. if (tok2->link()) { if (tok2->str() == "(") { Token *funcstart = const_cast(isFunctionHead(tok2, "{")); if (funcstart) { setVarIdClassFunction(scopeName2 + classname, funcstart, funcstart->link(), thisClassVars, structMembers, &mVarId); tok2 = funcstart->link(); continue; } } if (tok2->str() == "{") { if (tok2->strAt(-1) == ")") setVarIdClassFunction(scopeName2 + classname, tok2, tok2->link(), thisClassVars, structMembers, &mVarId); tok2 = tok2->link(); } else if (Token::Match(tok2, "( %name%|)") && !Token::Match(tok2->link(), "(|[")) { tok2 = tok2->link(); // Skip initialization list while (Token::Match(tok2, ") [:,] %name% (")) tok2 = tok2->linkAt(3); } } // Found a member variable.. else if (tok2->varId() > 0) thisClassVars[tok2->str()] = tok2->varId(); } // Are there any member variables in this class? if (thisClassVars.empty()) continue; // Member variables for (const Member &var : allMemberVars) { Token *tok2 = matchMemberVarName(var, scopeInfo); if (!tok2) continue; tok2->varId(thisClassVars[tok2->str()]); } if (isC() || tok->str() == "namespace") continue; // Set variable ids in member functions for this class.. for (const Member &func : allMemberFunctions) { Token *tok2 = matchMemberFunctionName(func, scopeInfo); if (!tok2) continue; if (tok2->str() == "~") tok2 = tok2->linkAt(2); else tok2 = tok2->linkAt(1); // If this is a function implementation.. add it to funclist Token * start = const_cast(isFunctionHead(tok2, "{")); if (start) { setVarIdClassFunction(classname, start, start->link(), thisClassVars, structMembers, &mVarId); } if (Token::Match(tok2, ") %name% (")) tok2 = tok2->linkAt(2); // constructor with initializer list if (!Token::Match(tok2, ") : ::| %name%")) continue; Token *tok3 = tok2; while (Token::Match(tok3, "[)}] [,:]")) { tok3 = tok3->tokAt(2); if (Token::Match(tok3, ":: %name%")) tok3 = tok3->next(); while (Token::Match(tok3, "%name% :: %name%")) tok3 = tok3->tokAt(2); if (!Token::Match(tok3, "%name% (|{|<")) break; // set varid const std::map::const_iterator varpos = thisClassVars.find(tok3->str()); if (varpos != thisClassVars.end()) tok3->varId(varpos->second); // goto end of var if (tok3->strAt(1) == "<") { tok3 = tok3->next()->findClosingBracket(); if (tok3 && tok3->next() && tok3->next()->link()) tok3 = tok3->next()->link(); } else tok3 = tok3->linkAt(1); } if (Token::Match(tok3, ")|} {")) { setVarIdClassFunction(classname, tok2, tok3->next()->link(), thisClassVars, structMembers, &mVarId); } } } } static void linkBrackets(const Tokenizer * const tokenizer, std::stack& type, std::stack& links, Token * const token, const char open, const char close) { if (token->str()[0] == open) { links.push(token); type.push(token); } else if (token->str()[0] == close) { if (links.empty()) { // Error, { and } don't match. tokenizer->unmatchedToken(token); } if (type.top()->str()[0] != open) { tokenizer->unmatchedToken(type.top()); } type.pop(); Token::createMutualLinks(links.top(), token); links.pop(); } } void Tokenizer::createLinks() { std::stack type; std::stack links1; std::stack links2; std::stack links3; for (Token *token = list.front(); token; token = token->next()) { if (token->link()) { token->link(nullptr); } linkBrackets(this, type, links1, token, '{', '}'); linkBrackets(this, type, links2, token, '(', ')'); linkBrackets(this, type, links3, token, '[', ']'); } if (!links1.empty()) { // Error, { and } don't match. unmatchedToken(links1.top()); } if (!links2.empty()) { // Error, ( and ) don't match. unmatchedToken(links2.top()); } if (!links3.empty()) { // Error, [ and ] don't match. unmatchedToken(links3.top()); } } void Tokenizer::createLinks2() { if (isC()) return; bool isStruct = false; std::stack type; std::stack templateTokens; for (Token *token = list.front(); token; token = token->next()) { if (Token::Match(token, "%name%|> %name% [:<]")) isStruct = true; else if (Token::Match(token, "[;{}]")) isStruct = false; if (token->link()) { if (Token::Match(token, "{|[|(")) type.push(token); else if (!type.empty() && Token::Match(token, "}|]|)")) { while (type.top()->str() == "<") { if (!templateTokens.empty() && templateTokens.top()->next() == type.top()) templateTokens.pop(); type.pop(); } type.pop(); } } else if (templateTokens.empty() && !isStruct && Token::Match(token, "%oror%|&&|;")) { if (Token::Match(token, "&& [,>]")) continue; // If there is some such code: A.. // Then this is probably a template instantiation if either "B" or "C" has comparisons if (token->tokType() == Token::eLogicalOp && !type.empty() && type.top()->str() == "<") { const Token *prev = token->previous(); bool foundComparison = false; while (Token::Match(prev, "%name%|%num%|%str%|%cop%|)|]") && prev != type.top()) { if (prev->str() == ")" || prev->str() == "]") prev = prev->link(); else if (prev->tokType() == Token::eLogicalOp) break; else if (prev->isComparisonOp()) foundComparison = true; prev = prev->previous(); } if (prev == type.top() && foundComparison) continue; const Token *next = token->next(); foundComparison = false; while (Token::Match(next, "%name%|%num%|%str%|%cop%|(|[") && next->str() != ">") { if (next->str() == "(" || next->str() == "[") next = next->link(); else if (next->tokType() == Token::eLogicalOp) break; else if (next->isComparisonOp()) foundComparison = true; next = next->next(); } if (next && next->str() == ">" && foundComparison) continue; } while (!type.empty() && type.top()->str() == "<") { const Token* end = type.top()->findClosingBracket(); if (Token::Match(end, "> %comp%|;|.|=|{|::")) break; // Variable declaration if (Token::Match(end, "> %var% ;") && (type.top()->tokAt(-2) == nullptr || Token::Match(type.top()->tokAt(-2), ";|}|{"))) break; type.pop(); } } else if (token->str() == "<" && ((token->previous() && (token->previous()->isTemplate() || (token->previous()->isName() && !token->previous()->varId()))) || Token::Match(token->next(), ">|>>"))) { type.push(token); if (token->previous()->str() == "template") templateTokens.push(token); } else if (token->str() == ">" || token->str() == ">>") { if (type.empty() || type.top()->str() != "<") // < and > don't match. continue; Token * const top1 = type.top(); type.pop(); Token * const top2 = type.empty() ? nullptr : type.top(); type.push(top1); if (!top2 || top2->str() != "<") { if (token->str() == ">>") continue; if (!Token::Match(token->next(), "%name%|%cop%|%assign%|::|,|(|)|{|}|;|[|:|.|=|...") && !Token::Match(token->next(), "&& %name% =")) continue; } // if > is followed by [ .. "new a[" is expected // unless this is from varidiac expansion if (token->strAt(1) == "[" && !Token::simpleMatch(token->tokAt(-1), "... >") && !Token::Match(token->tokAt(1), "[ ]")) { Token *prev = type.top()->previous(); while (prev && Token::Match(prev->previous(), ":: %name%")) prev = prev->tokAt(-2); if (prev && prev->str() != "new") prev = prev->previous(); if (!prev || prev->str() != "new") continue; } if (token->str() == ">>" && top1 && top2) { type.pop(); type.pop(); // Split the angle brackets token->str(">"); Token::createMutualLinks(top1, token->insertTokenBefore(">")); Token::createMutualLinks(top2, token); if (templateTokens.size() == 2 && (top1 == templateTokens.top() || top2 == templateTokens.top())) { templateTokens.pop(); templateTokens.pop(); } } else { type.pop(); if (Token::Match(token, "> %name%") && !token->next()->isKeyword() && Token::Match(top1->tokAt(-2), "%op% %name% <") && (templateTokens.empty() || top1 != templateTokens.top())) continue; Token::createMutualLinks(top1, token); if (!templateTokens.empty() && top1 == templateTokens.top()) templateTokens.pop(); } } } } void Tokenizer::sizeofAddParentheses() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "sizeof !!(")) continue; if (tok->next()->isLiteral() || Token::Match(tok->next(), "%name%|*|~|!|&")) { Token *endToken = tok->next(); while (Token::simpleMatch(endToken, "* *")) endToken = endToken->next(); while (Token::Match(endToken->next(), "%name%|%num%|%str%|[|(|.|::|++|--|!|~") || (Token::Match(endToken, "%type% * %op%|?|:|const|;|,"))) { if (Token::Match(endToken->next(), "(|[")) endToken = endToken->linkAt(1); else endToken = endToken->next(); } // Add ( after sizeof and ) behind endToken tok->insertToken("("); endToken->insertToken(")"); Token::createMutualLinks(tok->next(), endToken->next()); } } } bool Tokenizer::simplifySizeof() { // Locate variable declarations and calculate the size std::map sizeOfVar; std::map declTokOfVar; for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->varId() != 0 && sizeOfVar.find(tok->varId()) == sizeOfVar.end()) { const int varId = tok->varId(); if (Token::Match(tok->tokAt(-3), "[;{}(,] %type% * %name% [;,)]") || Token::Match(tok->tokAt(-4), "[;{}(,] const %type% * %name% [;),]") || Token::Match(tok->tokAt(-2), "[;{}(,] %type% %name% [;),]") || Token::Match(tok->tokAt(-3), "[;{}(,] const %type% %name% [;),]")) { const int size = sizeOfType(tok->previous()); if (size == 0) { continue; } sizeOfVar[varId] = size; declTokOfVar[varId] = tok; } else if (Token::Match(tok->previous(), "%type% %name% [ %num% ] [[;=]") || Token::Match(tok->tokAt(-2), "%type% * %name% [ %num% ] [[;=]")) { int size = sizeOfType(tok->previous()); if (size == 0) continue; const Token* tok2 = tok->next(); do { const MathLib::bigint num = MathLib::toLongNumber(tok2->strAt(1)); if (num<0) break; size *= num; tok2 = tok2->tokAt(3); } while (Token::Match(tok2, "[ %num% ]")); if (Token::Match(tok2, "[;=]")) { sizeOfVar[varId] = size; declTokOfVar[varId] = tok; } if (!tok2) { syntaxError(tok); } tok = tok2; } else if (Token::Match(tok->previous(), "%type% %name% [ %num% ] [,)]") || Token::Match(tok->tokAt(-2), "%type% * %name% [ %num% ] [,)]")) { Token tempTok; tempTok.str("*"); sizeOfVar[varId] = sizeOfType(&tempTok); declTokOfVar[varId] = tok; } } } bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "sizeof") continue; if (Token::simpleMatch(tok->next(), "...")) { //tok->deleteNext(3); tok->deleteNext(); } // sizeof('x') if (Token::Match(tok->next(), "( %char% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream sz; sz << ((isC()) ? mSettings->sizeof_int : 1); tok->str(sz.str()); ret = true; continue; } // sizeof ("text") if (Token::Match(tok->next(), "( %str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream ostr; ostr << (Token::getStrLength(tok) + 1); tok->str(ostr.str()); ret = true; continue; } // sizeof(type *) => sizeof(*) if (Token::Match(tok->next(), "( %type% * )")) { tok->next()->deleteNext(); } if (Token::simpleMatch(tok->next(), "( * )")) { tok->str(MathLib::toString(sizeOfType(tok->tokAt(2)))); tok->deleteNext(3); ret = true; } // sizeof( a ) else if (Token::Match(tok->next(), "( %var% )")) { const std::map::const_iterator sizeOfVarPos = sizeOfVar.find(tok->tokAt(2)->varId()); if (sizeOfVarPos != sizeOfVar.end()) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); tok->str(MathLib::toString(sizeOfVarPos->second)); ret = true; } else { // don't try to replace size of variable if variable has // similar name with type (#329) } } else if (Token::Match(tok->next(), "( %type% )")) { const int size = sizeOfType(tok->tokAt(2)); if (size > 0) { tok->str(MathLib::toString(size)); tok->deleteNext(3); ret = true; } } else if (Token::simpleMatch(tok->next(), "( *") || Token::Match(tok->next(), "( %name% [")) { int derefs = 0; const Token* nametok = tok->tokAt(2); if (nametok->str() == "*") { do { nametok = nametok->next(); derefs++; } while (nametok && nametok->str() == "*"); if (!Token::Match(nametok, "%name% )")) continue; } else { const Token* tok2 = nametok->next(); do { tok2 = tok2->link()->next(); derefs++; } while (tok2 && tok2->str() == "["); if (!tok2 || tok2->str() != ")") continue; } // Some default value MathLib::biguint size = 0; const int varid = nametok->varId(); if (derefs != 0 && varid != 0 && declTokOfVar.find(varid) != declTokOfVar.end()) { // Try to locate variable declaration.. const Token *decltok = declTokOfVar[varid]; if (Token::Match(decltok->previous(), "%type%|* %name% [")) { size = sizeOfType(decltok->previous()); } else if (Token::Match(decltok->tokAt(-2), "%type% * %name%")) { size = sizeOfType(decltok->tokAt(-2)); } // Multi-dimensional array.. if (Token::Match(decltok, "%name% [") && Token::simpleMatch(decltok->linkAt(1), "] [")) { const Token *tok2 = decltok; for (int i = 0; i < derefs; i++) tok2 = tok2->linkAt(1); // Skip all dimensions that are dereferenced before the sizeof call while (Token::Match(tok2, "] [ %num% ]")) { size *= MathLib::toULongNumber(tok2->strAt(2)); tok2 = tok2->linkAt(1); } if (Token::simpleMatch(tok2, "] [")) continue; } } else if (nametok->strAt(1) == "[" && nametok->isStandardType()) { size = sizeOfType(nametok); if (size == 0) continue; const Token *tok2 = nametok->next(); while (Token::Match(tok2, "[ %num% ]")) { size *= MathLib::toULongNumber(tok2->strAt(1)); tok2 = tok2->link()->next(); } if (!tok2 || tok2->str() != ")") continue; } if (size > 0) { tok->str(MathLib::toString(size)); Token::eraseTokens(tok, tok->next()->link()->next()); ret = true; } } } return ret; } bool Tokenizer::simplifyTokenList1(const char FileName[]) { if (Settings::terminated()) return false; // if MACRO for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "if|for|while|BOOST_FOREACH %name% (")) { if (Token::simpleMatch(tok, "for each")) { // 'for each ( )' -> 'asm ( )' tok->str("asm"); tok->deleteNext(); } else if (tok->strAt(1) == "constexpr") { tok->deleteNext(); tok->isConstexpr(true); } else { syntaxError(tok); } } } // Is there C++ code in C file? validateC(); // remove MACRO in variable declaration: MACRO int x; removeMacroInVarDecl(); // Combine strings and character literals, e.g. L"string", L'c', "string1" "string2" combineStringAndCharLiterals(); // replace inline SQL with "asm()" (Oracle PRO*C). Ticket: #1959 simplifySQL(); createLinks(); removePragma(); // Simplify the C alternative tokens (and, or, etc.) simplifyCAlternativeTokens(); simplifyFunctionTryCatch(); simplifyHeadersAndUnusedTemplates(); // Remove __asm.. simplifyAsm(); // foo < bar < >> => foo < bar < > > if (isCPP() || mSettings->daca) splitTemplateRightAngleBrackets(!isCPP()); // Remove extra "template" tokens that are not used by cppcheck removeExtraTemplateKeywords(); removeAlignas(); simplifySpaceshipOperator(); // Bail out if code is garbage if (mTimerResults) { Timer t("Tokenizer::tokenize::findGarbageCode", mSettings->showtime, mTimerResults); findGarbageCode(); } else { findGarbageCode(); } checkConfiguration(); // if (x) MACRO() .. for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "if (")) { tok = tok->next()->link(); if (Token::Match(tok, ") %name% (") && tok->next()->isUpperCaseName() && Token::Match(tok->linkAt(2), ") {|else")) { syntaxError(tok->next()); } } } if (Settings::terminated()) return false; // convert C++17 style nested namespaces to old style namespaces simplifyNestedNamespace(); // convert c++20 coroutines simplifyCoroutines(); // simplify namespace aliases simplifyNamespaceAliases(); // Remove [[attribute]] and alignas(?) simplifyCPPAttribute(); // remove __attribute__((?)) simplifyAttribute(); // simplify cppcheck attributes __cppcheck_?__(?) simplifyCppcheckAttribute(); // Combine tokens.. combineOperators(); // replace 'sin(0)' to '0' and other similar math expressions simplifyMathExpressions(); // combine "- %num%" concatenateNegativeNumberAndAnyPositive(); // remove extern "C" and extern "C" {} if (isCPP()) simplifyExternC(); // simplify weird but legal code: "[;{}] ( { code; } ) ;"->"[;{}] code;" simplifyRoundCurlyParentheses(); // check for simple syntax errors.. for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "> struct {") && Token::simpleMatch(tok->linkAt(2), "} ;")) { syntaxError(tok); } } if (!simplifyAddBraces()) return false; sizeofAddParentheses(); // Simplify: 0[foo] -> *(foo) for (Token* tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "0 [") && tok->linkAt(1)) { tok->str("*"); tok->next()->str("("); tok->linkAt(1)->str(")"); } } if (Settings::terminated()) return false; // Remove __declspec() simplifyDeclspec(); validate(); // Remove "inline", "register", and "restrict" simplifyKeyword(); // simplify simple calculations inside <..> if (isCPP()) { Token *lt = nullptr; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}]")) lt = nullptr; else if (Token::Match(tok, "%type% <")) lt = tok->next(); else if (lt && Token::Match(tok, ">|>> %name%|::|(")) { const Token * const end = tok; for (tok = lt; tok != end; tok = tok->next()) { if (tok->isNumber()) TemplateSimplifier::simplifyNumericCalculations(tok); } lt = tok->next(); } } } // Convert K&R function declarations to modern C simplifyVarDecl(true); simplifyFunctionParameters(); // simplify case ranges (gcc extension) simplifyCaseRange(); // simplify labels and 'case|default'-like syntaxes simplifyLabelsCaseDefault(); // simplify '[;{}] * & ( %any% ) =' to '%any% =' simplifyMulAndParens(); if (!isC() && !mSettings->library.markupFile(FileName)) { findComplicatedSyntaxErrorsInTemplates(); } if (Settings::terminated()) return false; // remove calling conventions __cdecl, __stdcall.. simplifyCallingConvention(); addSemicolonAfterUnknownMacro(); // remove some unhandled macros in global scope removeMacrosInGlobalScope(); // remove undefined macro in class definition: // class DLLEXPORT Fred { }; // class Fred FINAL : Base { }; removeMacroInClassDef(); // That call here fixes #7190 validate(); // remove unnecessary member qualification.. removeUnnecessaryQualification(); // convert Microsoft memory functions simplifyMicrosoftMemoryFunctions(); // convert Microsoft string functions simplifyMicrosoftStringFunctions(); if (Settings::terminated()) return false; // Remove Qt signals and slots simplifyQtSignalsSlots(); // remove Borland stuff.. simplifyBorland(); // syntax error: enum with typedef in it checkForEnumsWithTypedef(); // Add parentheses to ternary operator where necessary prepareTernaryOpForAST(); // Change initialisation of variable to assignment simplifyInitVar(); // Split up variable declarations. simplifyVarDecl(false); reportUnknownMacros(); // typedef.. if (mTimerResults) { Timer t("Tokenizer::tokenize::simplifyTypedef", mSettings->showtime, mTimerResults); simplifyTypedef(); } else { simplifyTypedef(); } // using A = B; while (simplifyUsing()) ; // Add parentheses to ternary operator where necessary // TODO: this is only necessary if one typedef simplification had a comma and was used within ?: // If typedef handling is refactored and moved to symboldatabase someday we can remove this prepareTernaryOpForAST(); for (Token* tok = list.front(); tok;) { if (Token::Match(tok, "union|struct|class union|struct|class")) tok->deleteNext(); else tok = tok->next(); } // class x y { if (isCPP() && mSettings->severity.isEnabled(Severity::information)) { for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "class %type% %type% [:{]")) { unhandled_macro_class_x_y(tok); } } } // catch bad typedef canonicalization // // to reproduce bad typedef, download upx-ucl from: // http://packages.debian.org/sid/upx-ucl // analyse the file src/stub/src/i386-linux.elf.interp-main.c validate(); // The simplify enum have inner loops if (Settings::terminated()) return false; // Put ^{} statements in asm() simplifyAsm2(); // @.. simplifyAt(); // When the assembly code has been cleaned up, no @ is allowed for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(") { const Token *tok1 = tok; tok = tok->link(); if (!tok) syntaxError(tok1); } else if (tok->str() == "@") { syntaxError(tok); } } // Order keywords "static" and "const" simplifyStaticConst(); // convert platform dependent types to standard types // 32 bits: size_t -> unsigned long // 64 bits: size_t -> unsigned long long list.simplifyPlatformTypes(); // collapse compound standard types into a single token // unsigned long long int => long (with _isUnsigned=true,_isLong=true) list.simplifyStdType(); if (Settings::terminated()) return false; // simplify bit fields.. simplifyBitfields(); if (Settings::terminated()) return false; // struct simplification "struct S {} s; => struct S { } ; S s ; simplifyStructDecl(); if (Settings::terminated()) return false; // x = ({ 123; }); => { x = 123; } simplifyAssignmentBlock(); if (Settings::terminated()) return false; simplifyVariableMultipleAssign(); // Collapse operator name tokens into single token // operator = => operator= simplifyOperatorName(); // Remove redundant parentheses simplifyRedundantParentheses(); if (isCPP()) simplifyTypeIntrinsics(); if (!isC()) { // Handle templates.. if (mTimerResults) { Timer t("Tokenizer::tokenize::simplifyTemplates", mSettings->showtime, mTimerResults); simplifyTemplates(); } else { simplifyTemplates(); } // The simplifyTemplates have inner loops if (Settings::terminated()) return false; validate(); // #6847 - invalid code } // Simplify pointer to standard types (C only) simplifyPointerToStandardType(); // simplify function pointers simplifyFunctionPointers(); // Change initialisation of variable to assignment simplifyInitVar(); // Split up variable declarations. simplifyVarDecl(false); elseif(); validate(); // #6772 "segmentation fault (invalid code) in Tokenizer::setVarId" if (mTimerResults) { Timer t("Tokenizer::tokenize::setVarId", mSettings->showtime, mTimerResults); setVarId(); } else { setVarId(); } // Link < with > createLinks2(); // Mark C++ casts for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <") && Token::simpleMatch(tok->linkAt(1), "> (")) { tok = tok->linkAt(1)->next(); tok->isCast(true); } } // specify array size arraySize(); // The simplify enum might have inner loops if (Settings::terminated()) return false; // Add std:: in front of std classes, when using namespace std; was given simplifyNamespaceStd(); // Change initialisation of variable to assignment simplifyInitVar(); simplifyDoublePlusAndDoubleMinus(); simplifyArrayAccessSyntax(); Token::assignProgressValues(list.front()); removeRedundantSemicolons(); simplifyRedundantConsecutiveBraces(); simplifyEmptyNamespaces(); simplifyIfSwitchForInit(); simplifyOverloadedOperators(); validate(); list.front()->assignIndexes(); return true; } bool Tokenizer::simplifyTokenList2() { // clear the _functionList so it can't contain dead pointers deleteSymbolDatabase(); // Clear AST,ValueFlow. These will be created again at the end of this function. for (Token *tok = list.front(); tok; tok = tok->next()) { tok->clearAst(); tok->clearValueFlow(); } // Convert e.g. atol("0") into 0 simplifyMathFunctions(); // f(x=g()) => x=g(); f(x) simplifyAssignmentInFunctionCall(); // ";a+=b;" => ";a=a+b;" simplifyCompoundAssignment(); simplifyCharAt(); // simplify references simplifyReference(); simplifyStd(); if (Settings::terminated()) return false; simplifySizeof(); simplifyUndefinedSizeArray(); simplifyCasts(); // Simplify simple calculations before replace constants, this allows the replacement of constants that are calculated // e.g. const static int value = sizeof(X)/sizeof(Y); simplifyCalculations(); if (Settings::terminated()) return false; // Replace "*(ptr + num)" => "ptr[num]" simplifyOffsetPointerDereference(); // Replace "&str[num]" => "(str + num)" simplifyOffsetPointerReference(); removeRedundantAssignment(); simplifyRealloc(); // Change initialisation of variable to assignment simplifyInitVar(); // Simplify variable declarations simplifyVarDecl(false); simplifyErrNoInWhile(); simplifyIfAndWhileAssign(); simplifyRedundantParentheses(); simplifyNestedStrcat(); simplifyFuncInWhile(); simplifyIfAndWhileAssign(); // replace strlen(str) for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "strlen ( %str% )")) { tok->str(MathLib::toString(Token::getStrLength(tok->tokAt(2)))); tok->deleteNext(3); } } bool modified = true; while (modified) { if (Settings::terminated()) return false; modified = false; modified |= simplifyConditions(); modified |= simplifyFunctionReturn(); modified |= simplifyKnownVariables(); modified |= simplifyStrlen(); modified |= removeRedundantConditions(); modified |= simplifyRedundantParentheses(); modified |= simplifyConstTernaryOp(); modified |= simplifyCalculations(); validate(); } // simplify redundant loops simplifyWhile0(); removeRedundantFor(); // Remove redundant parentheses in return.. for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::simpleMatch(tok, "return (")) { Token *tok2 = tok->next()->link(); if (Token::simpleMatch(tok2, ") ;")) { tok->deleteNext(); tok2->deleteThis(); } else { break; } } } simplifyReturnStrncat(); removeRedundantAssignment(); simplifyComma(); removeRedundantSemicolons(); simplifyFlowControl(); simplifyRedundantConsecutiveBraces(); simplifyEmptyNamespaces(); simplifyMathFunctions(); validate(); Token::assignProgressValues(list.front()); list.front()->assignIndexes(); list.createAst(); // needed for #7208 (garbage code) and #7724 (ast max depth limit) list.validateAst(); // Create symbol database and then remove const keywords createSymbolDatabase(); mSymbolDatabase->setValueTypeInTokenList(true); ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); if (Settings::terminated()) return false; printDebugOutput(2); return true; } //--------------------------------------------------------------------------- void Tokenizer::printDebugOutput(int simplification) const { const bool debug = (simplification != 1U && mSettings->debugSimplified) || (simplification != 2U && mSettings->debugnormal); if (debug && list.front()) { list.front()->printOut(nullptr, list.getFiles()); if (mSettings->xml) std::cout << "" << std::endl; if (mSymbolDatabase) { if (mSettings->xml) mSymbolDatabase->printXml(std::cout); else if (mSettings->verbose) { mSymbolDatabase->printOut("Symbol database"); } } if (mSettings->verbose) list.front()->printAst(mSettings->verbose, mSettings->xml, list.getFiles(), std::cout); list.front()->printValueFlow(mSettings->xml, std::cout); if (mSettings->xml) std::cout << "" << std::endl; } if (mSymbolDatabase && simplification == 2U && mSettings->debugwarnings) { printUnknownTypes(); // the typeStartToken() should come before typeEndToken() for (const Variable *var : mSymbolDatabase->variableList()) { if (!var) continue; const Token * typetok = var->typeStartToken(); while (typetok && typetok != var->typeEndToken()) typetok = typetok->next(); if (typetok != var->typeEndToken()) { reportError(var->typeStartToken(), Severity::debug, "debug", "Variable::typeStartToken() of variable '" + var->name() + "' is not located before Variable::typeEndToken(). The location of the typeStartToken() is '" + var->typeStartToken()->str() + "' at line " + MathLib::toString(var->typeStartToken()->linenr())); } } } } void Tokenizer::dump(std::ostream &out) const { // Create a xml data dump. // The idea is not that this will be readable for humans. It's a // data dump that 3rd party tools could load and get useful info from. // tokens.. out << " " << std::endl; for (const Token *tok = list.front(); tok; tok = tok->next()) { out << " linenr() << "\" column=\"" << tok->column() << "\""; out << " str=\"" << ErrorLogger::toxml(tok->str()) << '\"'; out << " scope=\"" << tok->scope() << '\"'; if (tok->isName()) { out << " type=\"name\""; if (tok->isUnsigned()) out << " isUnsigned=\"true\""; else if (tok->isSigned()) out << " isSigned=\"true\""; } else if (tok->isNumber()) { out << " type=\"number\""; if (MathLib::isInt(tok->str())) out << " isInt=\"true\""; if (MathLib::isFloat(tok->str())) out << " isFloat=\"true\""; } else if (tok->tokType() == Token::eString) out << " type=\"string\" strlen=\"" << Token::getStrLength(tok) << '\"'; else if (tok->tokType() == Token::eChar) out << " type=\"char\""; else if (tok->isBoolean()) out << " type=\"boolean\""; else if (tok->isOp()) { out << " type=\"op\""; if (tok->isArithmeticalOp()) out << " isArithmeticalOp=\"true\""; else if (tok->isAssignmentOp()) out << " isAssignmentOp=\"true\""; else if (tok->isComparisonOp()) out << " isComparisonOp=\"true\""; else if (tok->tokType() == Token::eLogicalOp) out << " isLogicalOp=\"true\""; } if (tok->isExpandedMacro()) out << " isExpandedMacro=\"true\""; if (tok->isSplittedVarDeclComma()) out << " isSplittedVarDeclComma=\"true\""; if (tok->isSplittedVarDeclEq()) out << " isSplittedVarDeclEq=\"true\""; if (tok->isImplicitInt()) out << " isImplicitInt=\"true\""; if (tok->link()) out << " link=\"" << tok->link() << '\"'; if (tok->varId() > 0) out << " varId=\"" << MathLib::toString(tok->varId()) << '\"'; if (tok->variable()) out << " variable=\"" << tok->variable() << '\"'; if (tok->function()) out << " function=\"" << tok->function() << '\"'; if (!tok->values().empty()) out << " values=\"" << &tok->values() << '\"'; if (tok->type()) out << " type-scope=\"" << tok->type()->classScope << '\"'; if (tok->astParent()) out << " astParent=\"" << tok->astParent() << '\"'; if (tok->astOperand1()) out << " astOperand1=\"" << tok->astOperand1() << '\"'; if (tok->astOperand2()) out << " astOperand2=\"" << tok->astOperand2() << '\"'; if (!tok->originalName().empty()) out << " originalName=\"" << tok->originalName() << '\"'; if (tok->valueType()) { const std::string vt = tok->valueType()->dump(); if (!vt.empty()) out << ' ' << vt; } if (!tok->varId() && tok->scope()->isExecutable() && Token::Match(tok, "%name% (")) { if (mSettings->library.isnoreturn(tok)) out << " noreturn=\"true\""; } out << "/>" << std::endl; } out << " " << std::endl; mSymbolDatabase->printXml(out); if (list.front()) list.front()->printValueFlow(true, out); if (!mTypedefInfo.empty()) { out << " " << std::endl; for (const TypedefInfo &typedefInfo: mTypedefInfo) { out << " " << std::endl; } out << " " << std::endl; } } void Tokenizer::simplifyHeadersAndUnusedTemplates() { if (mSettings->checkHeaders && mSettings->checkUnusedTemplates) // Full analysis. All information in the headers are kept. return; const bool checkHeaders = mSettings->checkHeaders; const bool removeUnusedIncludedFunctions = !mSettings->checkHeaders; const bool removeUnusedIncludedClasses = !mSettings->checkHeaders; const bool removeUnusedIncludedTemplates = !mSettings->checkUnusedTemplates || !mSettings->checkHeaders; const bool removeUnusedTemplates = !mSettings->checkUnusedTemplates; // checkHeaders: // // If it is true then keep all code in the headers. It's possible // to remove unused types/variables if false positives / false // negatives can be avoided. // // If it is false, then we want to remove selected stuff from the // headers but not *everything*. The intention here is to not damage // the analysis of the source file. You should get all warnings in // the source file. You should not get false positives. // functions and types to keep std::set keep; for (const Token *tok = list.front(); tok; tok = tok->next()) { if (isCPP() && Token::simpleMatch(tok, "template <")) { const Token *closingBracket = tok->next()->findClosingBracket(); if (Token::Match(closingBracket, "> class|struct %name% {")) tok = closingBracket->linkAt(3); } if (!tok->isName() || tok->isKeyword()) continue; if (!checkHeaders && tok->fileIndex() != 0) continue; if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { keep.insert(tok->str()); continue; } if (Token::Match(tok, "%name% %name%|::|*|&|<")) { keep.insert(tok->str()); } } const std::set functionStart{"static", "const", "unsigned", "signed", "void", "bool", "char", "short", "int", "long", "float", "*"}; for (Token *tok = list.front(); tok; tok = tok->next()) { const bool isIncluded = (tok->fileIndex() != 0); // Remove executable code if (isIncluded && !mSettings->checkHeaders && tok->str() == "{") { // TODO: We probably need to keep the executable code if this function is called from the source file. const Token *prev = tok->previous(); while (prev && prev->isName()) prev = prev->previous(); if (Token::simpleMatch(prev, ")")) { // Replace all tokens from { to } with a ";". Token::eraseTokens(tok,tok->link()->next()); tok->str(";"); tok->link(nullptr); } } if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) { // Remove unused function declarations if (isIncluded && removeUnusedIncludedFunctions) { while (true) { Token *start = tok; while (start && functionStart.find(start->str()) != functionStart.end()) start = start->next(); if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end()) { Token::eraseTokens(tok, start->linkAt(1)->tokAt(2)); tok->deleteThis(); } else break; } } if (isIncluded && removeUnusedIncludedClasses) { if (Token::Match(tok, "class|struct %name% [:{]") && keep.find(tok->strAt(1)) == keep.end()) { // Remove this class/struct const Token *endToken = tok->tokAt(2); if (endToken->str() == ":") { endToken = endToken->next(); while (Token::Match(endToken, "%name%|,")) endToken = endToken->next(); } if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;")) { Token::eraseTokens(tok, endToken->link()->next()); tok->deleteThis(); } } } if (removeUnusedTemplates || (isIncluded && removeUnusedIncludedTemplates)) { if (Token::Match(tok, "template < %name%")) { const Token *closingBracket = tok->next()->findClosingBracket(); if (Token::Match(closingBracket, "> class|struct %name% [;:{]") && keep.find(closingBracket->strAt(2)) == keep.end()) { const Token *endToken = closingBracket->tokAt(3); if (endToken->str() == ":") { endToken = endToken->next(); while (Token::Match(endToken, "%name%|,")) endToken = endToken->next(); } if (endToken && endToken->str() == "{") endToken = endToken->link()->next(); if (endToken && endToken->str() == ";") { Token::eraseTokens(tok, endToken); tok->deleteThis(); } } else if (Token::Match(closingBracket, "> %type% %name% (") && Token::simpleMatch(closingBracket->linkAt(3), ") {") && keep.find(closingBracket->strAt(2)) == keep.end()) { const Token *endToken = closingBracket->linkAt(3)->linkAt(1)->next(); Token::eraseTokens(tok, endToken); tok->deleteThis(); } } } } } } void Tokenizer::removeExtraTemplateKeywords() { if (isCPP()) { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%name%|>|) .|:: template %name%")) { tok->next()->deleteNext(); Token* templateName = tok->tokAt(2); while (Token::Match(templateName, "%name%|::")) { templateName->isTemplate(true); templateName = templateName->next(); } if (Token::Match(templateName->previous(), "operator %op%|(")) { templateName->isTemplate(true); if (templateName->str() == "(" && templateName->link()) templateName->link()->isTemplate(true); } } } } } static std::string getExpression(const Token *tok) { std::string line; for (const Token *prev = tok->previous(); prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) line = prev->str() + " " + line; line += "!!!" + tok->str() + "!!!"; for (const Token *next = tok->next(); next && !Token::Match(next, "[;{}]"); next = next->next()) line = line + " " + next->str(); return line; } void Tokenizer::splitTemplateRightAngleBrackets(bool check) { std::set vars; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %type% %type% [;,=]") && tok->next()->isStandardType()) vars.insert(tok->strAt(2)); // Ticket #6181: normalize C++11 template parameter list closing syntax if (tok->previous() && tok->str() == "<" && TemplateSimplifier::templateParameters(tok) && vars.find(tok->previous()->str()) == vars.end()) { Token *endTok = tok->findClosingBracket(); if (check) { if (Token::Match(endTok, ">>|>>=")) reportError(tok, Severity::debug, "dacaWrongSplitTemplateRightAngleBrackets", "bad closing bracket for !!!str() == ">>") { endTok->str(">"); endTok->insertToken(">"); } else if (endTok && endTok->str() == ">>=") { endTok->str(">"); endTok->insertToken("="); endTok->insertToken(">"); } } else if (Token::Match(tok, "class|struct|union|=|:|public|protected|private %name% <") && vars.find(tok->next()->str()) == vars.end()) { Token *endTok = tok->tokAt(2)->findClosingBracket(); if (check) { if (Token::simpleMatch(endTok, ">>")) reportError(tok, Severity::debug, "dacaWrongSplitTemplateRightAngleBrackets", "bad closing bracket for !!!> ;|{|%type%")) { endTok->str(">"); endTok->insertToken(">"); } } } } void Tokenizer::removeMacrosInGlobalScope() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(") { tok = tok->link(); if (Token::Match(tok, ") %type% {") && !Token::Match(tok->next(), "const|namespace|class|struct|union|noexcept|override|final|volatile")) tok->deleteNext(); } if (Token::Match(tok, "%type%") && tok->isUpperCaseName() && (!tok->previous() || Token::Match(tok->previous(), "[;{}]") || (tok->previous()->isName() && endsWith(tok->previous()->str(), ':')))) { const Token *tok2 = tok->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); // Several unknown macros... while (Token::Match(tok2, "%type% (") && tok2->isUpperCaseName()) tok2 = tok2->linkAt(1)->next(); if (Token::Match(tok, "%name% (") && Token::Match(tok2, "%name% *|&|::|<| %name%") && !Token::Match(tok2, "namespace|class|struct|union|private:|protected:|public:")) unknownMacroError(tok); if (Token::Match(tok, "%type% (") && Token::Match(tok2, "%type% (") && !Token::Match(tok2, "noexcept|throw") && isFunctionHead(tok2->next(), ":;{")) unknownMacroError(tok); // remove unknown macros before namespace|class|struct|union if (Token::Match(tok2, "namespace|class|struct|union")) { // is there a "{" for? const Token *tok3 = tok2; while (tok3 && !Token::Match(tok3,"[;{}()]")) tok3 = tok3->next(); if (tok3 && tok3->str() == "{") { Token::eraseTokens(tok, tok2); tok->deleteThis(); } continue; } // replace unknown macros before foo( /* if (Token::Match(tok2, "%type% (") && isFunctionHead(tok2->next(), "{")) { std::string typeName; for (const Token* tok3 = tok; tok3 != tok2; tok3 = tok3->next()) typeName += tok3->str(); Token::eraseTokens(tok, tok2); tok->str(typeName); } */ // remove unknown macros before foo::foo( if (Token::Match(tok2, "%type% :: %type%")) { const Token *tok3 = tok2; while (Token::Match(tok3, "%type% :: %type% ::")) tok3 = tok3->tokAt(2); if (Token::Match(tok3, "%type% :: %type% (") && tok3->str() == tok3->strAt(2)) { Token::eraseTokens(tok, tok2); tok->deleteThis(); } continue; } } // Skip executable scopes if (tok->str() == "{") { const Token *prev = tok->previous(); while (prev && prev->isName()) prev = prev->previous(); if (prev && prev->str() == ")") tok = tok->link(); } } } //--------------------------------------------------------------------------- void Tokenizer::removePragma() { if (isC() && mSettings->standards.c == Standards::C89) return; if (isCPP() && mSettings->standards.cpp == Standards::CPP03) return; for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::simpleMatch(tok, "_Pragma (")) { Token::eraseTokens(tok, tok->linkAt(1)->next()); tok->deleteThis(); } } } //--------------------------------------------------------------------------- void Tokenizer::removeMacroInClassDef() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "class|struct %name% %name% {|:")) continue; const bool nextIsUppercase = tok->next()->isUpperCaseName(); const bool afterNextIsUppercase = tok->tokAt(2)->isUpperCaseName(); if (nextIsUppercase && !afterNextIsUppercase) tok->deleteNext(); else if (!nextIsUppercase && afterNextIsUppercase) tok->next()->deleteNext(); } } //--------------------------------------------------------------------------- void Tokenizer::removeMacroInVarDecl() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %name% (") && tok->next()->isUpperCaseName()) { // goto ')' parentheses const Token *tok2 = tok; int parlevel = 0; while (tok2) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { if (--parlevel <= 0) break; } tok2 = tok2->next(); } tok2 = tok2 ? tok2->next() : nullptr; // check if this is a variable declaration.. const Token *tok3 = tok2; while (tok3 && tok3->isUpperCaseName()) tok3 = tok3->next(); if (tok3 && (tok3->isStandardType() || Token::Match(tok3,"const|static|struct|union|class"))) Token::eraseTokens(tok,tok2); } } } //--------------------------------------------------------------------------- void Tokenizer::addSemicolonAfterUnknownMacro() { if (!isCPP()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != ")") continue; const Token *macro = tok->link() ? tok->link()->previous() : nullptr; if (!macro || !macro->isName()) continue; if (Token::simpleMatch(tok, ") try") && !Token::Match(macro, "if|for|while")) tok->insertToken(";"); else if (Token::simpleMatch(tok, ") using")) tok->insertToken(";"); } } //--------------------------------------------------------------------------- void Tokenizer::removeRedundantAssignment() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); const Token * const start = const_cast(startOfExecutableScope(tok)); if (start) { tok = start->previous(); // parse in this function.. std::set localvars; const Token * const end = tok->next()->link(); for (Token * tok2 = tok->next(); tok2 && tok2 != end; tok2 = tok2->next()) { // skip local class or struct if (Token::Match(tok2, "class|struct %type% {|:")) { // skip to '{' tok2 = tok2->tokAt(2); while (tok2 && tok2->str() != "{") tok2 = tok2->next(); if (tok2) tok2 = tok2->link(); // skip local class or struct else return; } else if (Token::Match(tok2, "[;{}] %type% * %name% ;") && tok2->next()->str() != "return") { tok2 = tok2->tokAt(3); localvars.insert(tok2->varId()); } else if (Token::Match(tok2, "[;{}] %type% %name% ;") && tok2->next()->isStandardType()) { tok2 = tok2->tokAt(2); localvars.insert(tok2->varId()); } else if (tok2->varId() && !Token::Match(tok2->previous(), "[;{}] %name% = %char%|%num%|%name% ;")) { localvars.erase(tok2->varId()); } } localvars.erase(0); if (!localvars.empty()) { for (Token *tok2 = tok->next(); tok2 && tok2 != end;) { if (Token::Match(tok2, "[;{}] %type% %name% ;") && localvars.find(tok2->tokAt(2)->varId()) != localvars.end()) { tok2->deleteNext(3); } else if ((Token::Match(tok2, "[;{}] %type% * %name% ;") && localvars.find(tok2->tokAt(3)->varId()) != localvars.end()) || (Token::Match(tok2, "[;{}] %name% = %any% ;") && localvars.find(tok2->next()->varId()) != localvars.end())) { tok2->deleteNext(4); } else tok2 = tok2->next(); } } } } } void Tokenizer::simplifyRealloc() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "(|[") || (tok->str() == "{" && tok->previous() && tok->previous()->str() == "=")) tok = tok->link(); else if (Token::Match(tok, "[;{}] %name% = realloc (")) { tok = tok->tokAt(3); if (Token::simpleMatch(tok->next(), "( 0 ,")) { //no "x = realloc(0,);" if (!Token::simpleMatch(tok->next()->link(), ") ;") || tok->next()->link()->previous() == tok->tokAt(3)) continue; // delete "0 ," tok->next()->deleteNext(2); // Change function name "realloc" to "malloc" tok->str("malloc"); tok = tok->next()->link(); } else { Token *tok2 = tok->next()->link()->tokAt(-2); //no "x = realloc(,0);" if (!Token::simpleMatch(tok2, ", 0 ) ;") || tok2 == tok->tokAt(2)) continue; //remove ", 0" tok2 = tok2->previous(); tok2->deleteNext(2); //change "realloc" to "free" tok->str("free"); //insert "0" after "var =" tok = tok->previous(); tok->insertToken("0"); //move "var = 0" between "free(...)" and ";" tok2 = tok2->next(); Token::move(tok->previous(), tok->next(), tok2); //add missing ";" after "free(...)" tok2->insertToken(";"); //goto before last ";" and continue tok = tok->next(); } } } } void Tokenizer::simplifyEmptyNamespaces() { if (isC()) return; bool goback = false; for (Token *tok = list.front(); tok; tok = tok ? tok->next() : nullptr) { if (goback) { tok = tok->previous(); goback = false; } if (Token::Match(tok, "(|[|{")) { tok = tok->link(); continue; } if (!Token::Match(tok, "namespace %name%| {")) continue; bool isAnonymousNS = tok->strAt(1) == "{"; if (tok->strAt(3 - isAnonymousNS) == "}") { tok->deleteNext(3 - isAnonymousNS); // remove '%name%| { }' if (!tok->previous()) { // remove 'namespace' or replace it with ';' if isolated tok->deleteThis(); goback = true; } else { // '%any% namespace %any%' tok = tok->previous(); // goto previous token tok->deleteNext(); // remove next token: 'namespace' if (tok->str() == "{") { // Go back in case we were within a namespace that's empty now tok = tok->tokAt(-2) ? tok->tokAt(-2) : tok->previous(); goback = true; } } } else { tok = tok->tokAt(2 - isAnonymousNS); } } } void Tokenizer::simplifyFlowControl() { for (Token *begin = list.front(); begin; begin = begin->next()) { if (Token::Match(begin, "(|[") || (begin->str() == "{" && begin->previous() && begin->strAt(-1) == "=")) begin = begin->link(); //function scope if (!Token::simpleMatch(begin, ") {") && !Token::Match(begin, ") %name% {")) continue; Token* end = begin->linkAt(1+(begin->next()->str() == "{" ? 0 : 1)); int indentLevel = 0; bool stilldead = false; for (Token *tok = begin; tok && tok != end; tok = tok->next()) { if (Token::Match(tok, "(|[")) { tok = tok->link(); continue; } if (tok->str() == "{") { if (tok->previous() && tok->previous()->str() == "=") { tok = tok->link(); continue; } ++indentLevel; } else if (tok->str() == "}") { if (indentLevel == 0) break; --indentLevel; if (stilldead) { eraseDeadCode(tok, nullptr); if (indentLevel == 1 || tok->next()->str() != "}" || !Token::Match(tok->next()->link()->previous(), ";|{|}|do {")) stilldead = false; continue; } } if (indentLevel == 0) continue; if (Token::Match(tok,"continue|break ;")) { tok = tok->next(); eraseDeadCode(tok, nullptr); } else if (Token::Match(tok,"return|goto") || (Token::Match(tok->previous(), "[;{}] %name% (") && mSettings->library.isnoreturn(tok)) || (isCPP() && tok->str() == "throw")) { if (tok->next()->str() == "}") syntaxError(tok->next()); // invalid code like in #6731 //TODO: ensure that we exclude user-defined 'exit|abort|throw', except for 'noreturn' //catch the first ';' for (Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "(|[")) { tok2 = tok2->link(); } else if (tok2->str() == ";") { tok = tok2; eraseDeadCode(tok, nullptr); break; } else if (Token::Match(tok2, "[{}]")) break; } //if everything is removed, then remove also the code after an inferior scope //only if the actual scope is not special if (indentLevel > 1 && tok->next()->str() == "}" && Token::Match(tok->next()->link()->previous(), ";|{|}|do {")) stilldead = true; } } begin = end; } } bool Tokenizer::removeRedundantConditions() { // Return value for function. Set to true if there are any simplifications bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "if ( %bool% ) {")) continue; // Find matching else Token *elseTag = tok->linkAt(4)->next(); const bool boolValue = (tok->strAt(2) == "true"); // Handle if with else if (Token::simpleMatch(elseTag, "else {")) { // Handle else if (!boolValue) { // Convert "if( false ) {aaa;} else {bbb;}" => "{bbb;}" //remove '(false)' tok->deleteNext(3); //delete dead code inside scope eraseDeadCode(tok, elseTag); //remove 'else' elseTag->deleteThis(); //remove 'if' tok->deleteThis(); } else { // Convert "if( true ) {aaa;} else {bbb;}" => "{aaa;}" const Token *end = elseTag->next()->link()->next(); // Remove "else { bbb; }" elseTag = elseTag->previous(); eraseDeadCode(elseTag, end); // Remove "if( true )" tok->deleteNext(3); tok->deleteThis(); } ret = true; } // Handle if without else else { if (!boolValue) { //remove '(false)' tok->deleteNext(3); //delete dead code inside scope eraseDeadCode(tok, elseTag); //remove 'if' tok->deleteThis(); } else { // convert "if( true ) {aaa;}" => "{aaa;}" tok->deleteNext(3); tok->deleteThis(); } ret = true; } } return ret; } void Tokenizer::removeRedundantFor() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] for ( %name% = %num% ; %name% < %num% ; ++| %name% ++| ) {") || Token::Match(tok, "[;{}] for ( %type% %name% = %num% ; %name% < %num% ; ++| %name% ++| ) {")) { // Same variable name.. const Token* varTok = tok->tokAt(3); const bool type = varTok->next()->isName(); if (type) varTok = varTok->next(); const std::string varname(varTok->str()); const int varid(varTok->varId()); if (varname != varTok->strAt(4)) continue; const Token *vartok2 = tok->linkAt(2)->previous(); if (vartok2->str() == "++") vartok2 = vartok2->previous(); else if (vartok2->strAt(-1) != "++") continue; if (varname != vartok2->str()) continue; // Check that the difference of the numeric values is 1 const MathLib::bigint num1(MathLib::toLongNumber(varTok->strAt(2))); const MathLib::bigint num2(MathLib::toLongNumber(varTok->strAt(6))); if (num1 + 1 != num2) continue; // check how loop variable is used in loop.. bool read = false; bool write = false; const Token* end = tok->linkAt(2)->next()->link(); for (const Token *tok2 = tok->linkAt(2); tok2 != end; tok2 = tok2->next()) { if (tok2->str() == varname) { if (tok2->previous()->isArithmeticalOp() && tok2->next() && (tok2->next()->isArithmeticalOp() || tok2->next()->str() == ";")) { read = true; } else { read = write = true; break; } } } // Simplify loop if loop variable isn't written if (!write) { Token* bodyBegin = tok->linkAt(2)->next(); // remove "for (" tok->deleteNext(2); // If loop variable is read then keep assignment before // loop body.. if (type) { tok->insertToken("{"); Token::createMutualLinks(tok->next(), bodyBegin->link()); bodyBegin->deleteThis(); tok = tok->tokAt(6); } else if (read) { // goto ";" tok = tok->tokAt(4); } else { // remove "x = 0 ;" tok->deleteNext(4); } // remove "x < 1 ; x ++ )" tok->deleteNext(7); if (!type) { // Add assignment after the loop body so the loop variable // get the correct end value Token *tok2 = tok->next()->link(); tok2->insertToken(";"); tok2->insertToken(MathLib::toString(num2)); tok2->insertToken("="); tok2->insertToken(varname); tok2->next()->varId(varid); } } } } } void Tokenizer::removeRedundantSemicolons() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->link() && tok->str() == "(") { tok = tok->link(); continue; } for (;;) { if (Token::simpleMatch(tok, "; ;")) { tok->deleteNext(); } else if (Token::simpleMatch(tok, "; { ; }")) { tok->deleteNext(3); } else { break; } } } } bool Tokenizer::simplifyAddBraces() { for (Token *tok = list.front(); tok; tok = tok->next()) { Token const * tokRet=simplifyAddBracesToCommand(tok); if (!tokRet) return false; } return true; } Token *Tokenizer::simplifyAddBracesToCommand(Token *tok) { Token * tokEnd=tok; if (Token::Match(tok,"for|switch|BOOST_FOREACH")) { tokEnd=simplifyAddBracesPair(tok,true); } else if (tok->str()=="while") { Token *tokPossibleDo=tok->previous(); if (Token::simpleMatch(tok->previous(), "{")) tokPossibleDo = nullptr; else if (Token::simpleMatch(tokPossibleDo,"}")) tokPossibleDo = tokPossibleDo->link(); if (!tokPossibleDo || tokPossibleDo->strAt(-1) != "do") tokEnd=simplifyAddBracesPair(tok,true); } else if (tok->str()=="do") { tokEnd=simplifyAddBracesPair(tok,false); if (tokEnd!=tok) { // walk on to next token, i.e. "while" // such that simplifyAddBracesPair does not close other braces // before the "while" if (tokEnd) { tokEnd=tokEnd->next(); if (!tokEnd || tokEnd->str()!="while") // no while syntaxError(tok); } } } else if (tok->str()=="if" && !Token::simpleMatch(tok->tokAt(-2), "operator \"\"")) { tokEnd=simplifyAddBracesPair(tok,true); if (!tokEnd) return nullptr; if (tokEnd->strAt(1) == "else") { Token * tokEndNextNext= tokEnd->tokAt(2); if (!tokEndNextNext || tokEndNextNext->str() == "}") syntaxError(tokEndNextNext); if (tokEndNextNext->str() == "if") // do not change "else if ..." to "else { if ... }" tokEnd=simplifyAddBracesToCommand(tokEndNextNext); else tokEnd=simplifyAddBracesPair(tokEnd->next(),false); } } return tokEnd; } Token *Tokenizer::simplifyAddBracesPair(Token *tok, bool commandWithCondition) { Token * tokCondition=tok->next(); if (!tokCondition) // Missing condition return tok; Token *tokAfterCondition=tokCondition; if (commandWithCondition) { if (tokCondition->str()=="(") tokAfterCondition=tokCondition->link(); else syntaxError(tok); // Bad condition if (!tokAfterCondition || tokAfterCondition->strAt(1) == "]") syntaxError(tok); // Bad condition tokAfterCondition=tokAfterCondition->next(); if (!tokAfterCondition || Token::Match(tokAfterCondition, ")|}|,")) { // No tokens left where to add braces around return tok; } } // Skip labels Token * tokStatement = tokAfterCondition; while (true) { if (Token::Match(tokStatement, "%name% :")) tokStatement = tokStatement->tokAt(2); else if (tokStatement->str() == "case") { tokStatement = skipCaseLabel(tokStatement); if (!tokStatement) return tok; if (tokStatement->str() != ":") syntaxError(tokStatement); tokStatement = tokStatement->next(); } else break; if (!tokStatement) return tok; } Token * tokBracesEnd=nullptr; if (tokStatement->str() == "{") { // already surrounded by braces if (tokStatement != tokAfterCondition) { // Move the opening brace before labels Token::move(tokStatement, tokStatement, tokAfterCondition->previous()); } tokBracesEnd = tokStatement->link(); } else if (Token::simpleMatch(tokStatement, "try {") && Token::simpleMatch(tokStatement->linkAt(1), "} catch (")) { tokAfterCondition->previous()->insertToken("{"); Token * tokOpenBrace = tokAfterCondition->previous(); Token * tokEnd = tokStatement->linkAt(1)->linkAt(2)->linkAt(1); if (!tokEnd) { syntaxError(tokStatement); } tokEnd->insertToken("}"); Token * tokCloseBrace = tokEnd->next(); Token::createMutualLinks(tokOpenBrace, tokCloseBrace); tokBracesEnd = tokCloseBrace; } else { Token * tokEnd = simplifyAddBracesToCommand(tokStatement); if (!tokEnd) // Ticket #4887 return tok; if (tokEnd->str()!="}") { // Token does not end with brace // Look for ; to add own closing brace after it while (tokEnd && !Token::Match(tokEnd, ";|)|}")) { if (tokEnd->tokType()==Token::eBracket || tokEnd->str() == "(") { tokEnd = tokEnd->link(); if (!tokEnd) { // Inner bracket does not close return tok; } } tokEnd=tokEnd->next(); } if (!tokEnd || tokEnd->str() != ";") { // No trailing ; return tok; } } tokAfterCondition->previous()->insertToken("{"); Token * tokOpenBrace=tokAfterCondition->previous(); tokEnd->insertToken("}"); Token * tokCloseBrace=tokEnd->next(); Token::createMutualLinks(tokOpenBrace,tokCloseBrace); tokBracesEnd=tokCloseBrace; } return tokBracesEnd; } void Tokenizer::simplifyCompoundAssignment() { // Simplify compound assignments: // "a+=b" => "a = a + b" for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "[;{}] (| *| (| %name%")) continue; if (tok->next()->str() == "return") continue; // backup current token.. Token * const tok1 = tok; if (tok->next()->str() == "*") tok = tok->next(); if (tok->next() && tok->next()->str() == "(") { tok = tok->next()->link()->next(); } else { // variable.. tok = tok->tokAt(2); while (Token::Match(tok, ". %name%") || Token::Match(tok, "[|(")) { if (tok->str() == ".") tok = tok->tokAt(2); else { // goto "]" or ")" tok = tok->link(); // goto next token.. tok = tok ? tok->next() : nullptr; } } } if (!tok) break; // Is current token at a compound assignment: +=|-=|.. ? const std::string &str = tok->str(); std::string op; // operator used in assignment if (tok->isAssignmentOp() && str.size() == 2) op = str.substr(0, 1); else if (tok->isAssignmentOp() && str.size() == 3) op = str.substr(0, 2); else { tok = tok1; continue; } // Remove the whole statement if it says: "+=0;", "-=0;", "*=1;" or "/=1;" if (Token::Match(tok, "+=|-= 0 ;") || Token::simpleMatch(tok, "|= 0 ;") || Token::Match(tok, "*=|/= 1 ;")) { tok = tok1; while (tok->next()->str() != ";") tok->deleteNext(); } else { // Enclose the rhs in parentheses.. if (!Token::Match(tok->tokAt(2), "[;)]")) { // Only enclose rhs in parentheses if there is some operator bool someOperator = false; for (Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "{|[|(")) tok2 = tok2->link(); if (Token::Match(tok2->next(), "[;)]")) { if (someOperator) { tok->insertToken("("); tok2->insertToken(")"); Token::createMutualLinks(tok->next(), tok2->next()); } break; } someOperator |= (tok2->isOp() || tok2->str() == "?"); } } // simplify the compound assignment.. tok->str("="); tok->insertToken(op); std::stack tokend; for (Token *tok2 = tok->previous(); tok2 && tok2 != tok1; tok2 = tok2->previous()) { // Don't duplicate ++ and --. Put preincrement in lhs. Put // postincrement in rhs. if (tok2->tokType() == Token::eIncDecOp) { // pre increment/decrement => don't copy if (tok2->next()->isName()) { continue; } // post increment/decrement => move from lhs to rhs tok->insertToken(tok2->str()); tok2->deleteThis(); continue; } // Copy token from lhs to rhs tok->insertToken(tok2->str()); tok->next()->varId(tok2->varId()); if (Token::Match(tok->next(), "]|)|}")) tokend.push(tok->next()); else if (Token::Match(tok->next(), "(|[|{")) { Token::createMutualLinks(tok->next(), tokend.top()); tokend.pop(); } } } } } bool Tokenizer::simplifyConditions() { bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "! %bool%|%num%")) { tok->deleteThis(); if (Token::Match(tok, "0|false")) tok->str("true"); else tok->str("false"); ret = true; } if (Token::simpleMatch(tok, "&& true &&")) { tok->deleteNext(2); ret = true; } else if (Token::simpleMatch(tok, "|| false ||")) { tok->deleteNext(2); ret = true; } else if (Token::Match(tok, "(|&& true && true &&|)")) { tok->deleteNext(2); ret = true; } else if (Token::Match(tok, "%oror%|( false %oror% false %oror%|)")) { tok->deleteNext(2); ret = true; } else if (Token::simpleMatch(tok, "( true ||") || Token::simpleMatch(tok, "( false &&")) { Token::eraseTokens(tok->next(), tok->link()); ret = true; } else if (Token::simpleMatch(tok, "|| true )") || Token::simpleMatch(tok, "&& false )")) { tok = tok->next(); Token::eraseTokens(tok->next()->link(), tok); ret = true; } else if (Token::simpleMatch(tok, "&& false &&") || Token::simpleMatch(tok, "|| true ||")) { //goto '(' Token *tok2 = tok; while (tok2 && tok2->previous()) { if (tok2->previous()->str() == ")") tok2 = tok2->previous()->link(); else { tok2 = tok2->previous(); if (tok2->str() == "(") break; } } if (!tok2) continue; //move tok to 'true|false' position tok = tok->next(); //remove everything before 'true|false' Token::eraseTokens(tok2, tok); //remove everything after 'true|false' Token::eraseTokens(tok, tok2->link()); ret = true; } // Change numeric constant in condition to "true" or "false" if (Token::Match(tok, "if|while ( %num% )|%oror%|&&")) { tok->tokAt(2)->str((tok->strAt(2) != "0") ? "true" : "false"); ret = true; } if (Token::Match(tok, "&&|%oror% %num% )|%oror%|&&")) { tok->next()->str((tok->next()->str() != "0") ? "true" : "false"); ret = true; } // Reduce "(%num% == %num%)" => "(true)"/"(false)" if (Token::Match(tok, "&&|%oror%|(") && (Token::Match(tok->next(), "%num% %any% %num%") || Token::Match(tok->next(), "%bool% %any% %bool%")) && Token::Match(tok->tokAt(4), "&&|%oror%|)|?")) { std::string cmp = tok->strAt(2); bool result = false; if (tok->next()->isNumber()) { // Compare numbers if (cmp == "==" || cmp == "!=") { const std::string& op1(tok->next()->str()); const std::string& op2(tok->strAt(3)); bool eq = false; if (MathLib::isInt(op1) && MathLib::isInt(op2)) eq = (MathLib::toLongNumber(op1) == MathLib::toLongNumber(op2)); else { eq = (op1 == op2); // It is inconclusive whether two unequal float representations are numerically equal if (!eq && MathLib::isFloat(op1)) cmp.clear(); } if (cmp == "==") result = eq; else result = !eq; } else { const double op1 = MathLib::toDoubleNumber(tok->next()->str()); const double op2 = MathLib::toDoubleNumber(tok->strAt(3)); if (cmp == ">=") result = (op1 >= op2); else if (cmp == ">") result = (op1 > op2); else if (cmp == "<=") result = (op1 <= op2); else if (cmp == "<") result = (op1 < op2); else cmp.clear(); } } else { // Compare boolean const bool op1 = (tok->next()->str() == std::string("true")); const bool op2 = (tok->strAt(3) == std::string("true")); if (cmp == "==") result = (op1 == op2); else if (cmp == "!=") result = (op1 != op2); else if (cmp == ">=") result = (op1 >= op2); else if (cmp == ">") result = (op1 > op2); else if (cmp == "<=") result = (op1 <= op2); else if (cmp == "<") result = (op1 < op2); else cmp.clear(); } if (!cmp.empty()) { tok = tok->next(); tok->deleteNext(2); tok->str(result ? "true" : "false"); ret = true; } } } return ret; } bool Tokenizer::simplifyConstTernaryOp() { bool ret = false; const Token *templateParameterEnd = nullptr; // The end of the current template parameter list, if any for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "<" && TemplateSimplifier::templateParameters(tok)) templateParameterEnd = tok->findClosingBracket(); if (tok == templateParameterEnd) templateParameterEnd = nullptr; // End of the current template parameter list if (tok->str() != "?") continue; if (!Token::Match(tok->tokAt(-2), "<|=|,|(|[|{|}|;|case|return %bool%|%num%") && !Token::Match(tok->tokAt(-4), "<|=|,|(|[|{|}|;|case|return ( %bool%|%num% )")) continue; const int offset = (tok->previous()->str() == ")") ? 2 : 1; if (tok->strAt(-2*offset) == "<") { if (isC() || !TemplateSimplifier::templateParameters(tok->tokAt(-2*offset))) continue; // '<' is less than; the condition is not a constant } // Find the token ":" then go to the next token Token *colon = skipTernaryOp(tok); if (!colon || colon->previous()->str() != ":" || !colon->next()) continue; //handle the GNU extension: "x ? : y" <-> "x ? x : y" if (colon->previous() == tok->next()) tok->insertToken(tok->strAt(-offset)); // go back before the condition, if possible tok = tok->tokAt(-2); if (offset == 2) { // go further back before the "(" tok = tok->tokAt(-2); //simplify the parentheses tok->deleteNext(); tok->next()->deleteNext(); } if (Token::Match(tok->next(), "false|0")) { // Use code after colon, remove code before it. Token::eraseTokens(tok, colon); tok = tok->next(); ret = true; } // The condition is true. Delete the operator after the ":".. else { // delete the condition token and the "?" tok->deleteNext(2); int ternaryOplevel = 0; for (const Token *endTok = colon; endTok; endTok = endTok->next()) { if (Token::Match(endTok, "(|[|{")) endTok = endTok->link(); else if (endTok->str() == "<" && (endTok->strAt(1) == ">" || TemplateSimplifier::templateParameters(endTok))) endTok = endTok->findClosingBracket(); else if (endTok->str() == "?") ++ternaryOplevel; else if (Token::Match(endTok, ")|}|]|;|,|:|>")) { if (endTok->str() == ":" && ternaryOplevel) --ternaryOplevel; else if (endTok->str() == ">" && !templateParameterEnd) ; else { Token::eraseTokens(colon->tokAt(-2), endTok); ret = true; break; } } } } } return ret; } void Tokenizer::simplifyUndefinedSizeArray() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%type%")) { Token *tok2 = tok->next(); while (tok2 && tok2->str() == "*") tok2 = tok2->next(); if (!Token::Match(tok2, "%name% [ ] ;|[")) continue; tok = tok2->previous(); Token *end = tok2->next(); int count = 0; do { end = end->tokAt(2); ++count; } while (Token::Match(end, "[ ] [;=[]")); if (Token::Match(end, "[;=]")) { do { tok2->deleteNext(2); tok->insertToken("*"); } while (--count); tok = end; } else tok = tok->tokAt(3); } } } void Tokenizer::simplifyCasts() { for (Token *tok = list.front(); tok; tok = tok->next()) { // Don't remove cast in such cases: // *((char *)a + 1) = 0; // Remove cast when casting a function pointer: // (*(void (*)(char *))fp)(x); if (!tok->isName() && Token::simpleMatch(tok->next(), "* (") && !Token::Match(tok->linkAt(2), ") %name%|&")) { tok = tok->linkAt(2); continue; } // #3935 : don't remove cast in such cases: // ((char *)a)[1] = 0; if (tok->str() == "(" && Token::simpleMatch(tok->link(), ") [")) { tok = tok->link(); continue; } // #4164 : ((unsigned char)1) => (1) if (Token::Match(tok->next(), "( %type% ) %num%") && tok->next()->link()->previous()->isStandardType()) { const MathLib::bigint value = MathLib::toLongNumber(tok->next()->link()->next()->str()); int bits = mSettings->char_bit * mTypeSize[tok->next()->link()->previous()->str()]; if (!tok->tokAt(2)->isUnsigned() && bits > 0) bits--; if (bits < 31 && value >= 0 && value < (1LL << bits)) { tok->linkAt(1)->next()->isCast(true); Token::eraseTokens(tok, tok->next()->link()->next()); } continue; } while ((Token::Match(tok->next(), "( %type% *| *| *|&| ) *|&| %name%") && (tok->str() != ")" || tok->tokAt(2)->isStandardType())) || Token::Match(tok->next(), "( const| %type% * *| *|&| ) *|&| %name%") || Token::Match(tok->next(), "( const| %type% %type% *| *| *|&| ) *|&| %name%") || (!tok->isName() && (Token::Match(tok->next(), "( %type% * *| *|&| ) (") || Token::Match(tok->next(), "( const| %type% %type% * *| *|&| ) (")))) { if (tok->isName() && tok->str() != "return") break; if (isCPP() && tok->strAt(-1) == "operator") break; // Remove cast.. Token::eraseTokens(tok, tok->next()->link()->next()); // Set isCasted flag. Token *tok2 = tok->next(); if (!Token::Match(tok2, "%name% [|.")) tok2->isCast(true); else { // TODO: handle more complex expressions tok2->next()->isCast(true); } // Remove '* &' if (Token::simpleMatch(tok, "* &")) { tok->deleteNext(); tok->deleteThis(); } if (tok->str() == ")" && tok->link()->previous()) { // If there was another cast before this, go back // there to check it also. e.g. "(int)(char)x" tok = tok->link()->previous(); } } // Replace pointer casts of 0.. "(char *)0" => "0" while (Token::Match(tok->next(), "( %type% %type%| * *| ) 0")) { tok->linkAt(1)->next()->isCast(true); Token::eraseTokens(tok, tok->next()->link()->next()); if (tok->str() == ")" && tok->link()->previous()) { // If there was another cast before this, go back // there to check it also. e.g. "(char*)(char*)0" tok = tok->link()->previous(); } } if (Token::Match(tok->next(), "dynamic_cast|reinterpret_cast|const_cast|static_cast <")) { Token *tok2 = tok->linkAt(2); if (!Token::simpleMatch(tok2, "> (")) break; tok2->tokAt(2)->isCast(true); Token::eraseTokens(tok, tok2->next()); } } } void Tokenizer::simplifyFunctionParameters() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->link() && Token::Match(tok, "{|[|(")) { tok = tok->link(); } // Find the function e.g. foo( x ) or foo( x, y ) else if (Token::Match(tok, "%name% ( %name% [,)]") && !(tok->strAt(-1) == ":" || tok->strAt(-1) == "," || tok->strAt(-1) == "::")) { // We have found old style function, now we need to change it // First step: Get list of argument names in parentheses std::map argumentNames; bool bailOut = false; Token * tokparam = nullptr; //take count of the function name.. const std::string& funcName(tok->str()); //floating token used to check for parameters Token *tok1 = tok; while (nullptr != (tok1 = tok1->tokAt(2))) { if (!Token::Match(tok1, "%name% [,)]")) { bailOut = true; break; } //same parameters: take note of the parameter if (argumentNames.find(tok1->str()) != argumentNames.end()) tokparam = tok1; else if (tok1->str() != funcName) argumentNames[tok1->str()] = tok1; else { if (tok1->next()->str() == ")") { if (tok1->previous()->str() == ",") { tok1 = tok1->tokAt(-2); tok1->deleteNext(2); } else { tok1 = tok1->previous(); tok1->deleteNext(); bailOut = true; break; } } else { tok1 = tok1->tokAt(-2); tok1->next()->deleteNext(2); } } if (tok1->next()->str() == ")") { tok1 = tok1->tokAt(2); //expect at least a type name after round brace.. if (!tok1 || !tok1->isName()) bailOut = true; break; } } //goto '(' tok = tok->next(); if (bailOut) { tok = tok->link(); continue; } tok1 = tok->link()->next(); // there should be the sequence '; {' after the round parentheses for (const Token* tok2 = tok1; tok2; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "; {")) break; else if (tok2->str() == "{") { bailOut = true; break; } } if (bailOut) { tok = tok->link(); continue; } // Last step: check out if the declarations between ')' and '{' match the parameters list std::map argumentNames2; while (tok1 && tok1->str() != "{") { if (Token::Match(tok1, "(|)")) { bailOut = true; break; } if (tok1->str() == ";") { if (tokparam) { syntaxError(tokparam); } Token *tok2 = tok1->previous(); while (tok2->str() == "]") tok2 = tok2->link()->previous(); //it should be a name.. if (!tok2->isName()) { bailOut = true; break; } if (argumentNames2.find(tok2->str()) != argumentNames2.end()) { //same parameter names... syntaxError(tok1); } else argumentNames2[tok2->str()] = tok2; if (argumentNames.find(tok2->str()) == argumentNames.end()) { //non-matching parameter... bailout bailOut = true; break; } } tok1 = tok1->next(); } if (bailOut || !tok1) { tok = tok->link(); continue; } //the two containers may not hold the same size... //in that case, the missing parameters are defined as 'int' if (argumentNames.size() != argumentNames2.size()) { //move back 'tok1' to the last ';' tok1 = tok1->previous(); for (std::pair& argumentName : argumentNames) { if (argumentNames2.find(argumentName.first) == argumentNames2.end()) { //add the missing parameter argument declaration tok1->insertToken(";"); tok1->insertToken(argumentName.first); //register the change inside argumentNames2 argumentNames2[argumentName.first] = tok1->next(); tok1->insertToken("int"); } } } while (tok->str() != ")") { //initialize start and end tokens to be moved Token *declStart = argumentNames2[tok->next()->str()]; Token *declEnd = declStart; while (declStart->previous()->str() != ";" && declStart->previous()->str() != ")") declStart = declStart->previous(); while (declEnd->next()->str() != ";" && declEnd->next()->str() != "{") declEnd = declEnd->next(); //remove ';' after declaration declEnd->deleteNext(); //replace the parameter name in the parentheses with all the declaration Token::replace(tok->next(), declStart, declEnd); //since there are changes to tokens, put tok where tok1 is tok = declEnd->next(); //fix up line number if (tok->str() == ",") tok->linenr(tok->previous()->linenr()); } //goto forward and continue tok = tok->next()->link(); } } } void Tokenizer::simplifyPointerToStandardType() { if (!isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "& %name% [ 0 ] !![")) continue; if (!Token::Match(tok->previous(), "[,(=]")) continue; // Remove '[ 0 ]' suffix Token::eraseTokens(tok->next(), tok->tokAt(5)); // Remove '&' prefix tok = tok->previous(); if (!tok) break; tok->deleteNext(); } } void Tokenizer::simplifyFunctionPointers() { for (Token *tok = list.front(); tok; tok = tok->next()) { // #2873 - do not simplify function pointer usage here: // (void)(xy(*p)(0)); if (Token::simpleMatch(tok, ") (")) { tok = tok->next()->link(); continue; } // check for function pointer cast if (Token::Match(tok, "( %type% %type%| *| *| ( * ) (") || Token::Match(tok, "static_cast < %type% %type%| *| *| ( * ) (")) { Token *tok1 = tok; if (isCPP() && tok1->str() == "static_cast") tok1 = tok1->next(); tok1 = tok1->next(); if (Token::Match(tok1->next(), "%type%")) tok1 = tok1->next(); while (tok1->next()->str() == "*") tok1 = tok1->next(); // check that the cast ends if (!Token::Match(tok1->linkAt(4), ") )|>")) continue; // ok simplify this function pointer cast to an ordinary pointer cast tok1->deleteNext(); tok1->next()->deleteNext(); Token::eraseTokens(tok1->next(), tok1->linkAt(2)->next()); continue; } // check for start of statement else if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|,|(|public:|protected:|private:")) continue; if (Token::Match(tok, "delete|else|return|throw|typedef")) continue; while (Token::Match(tok, "%type%|:: %type%|::")) tok = tok->next(); Token *tok2 = (tok && tok->isName()) ? tok->next() : nullptr; while (Token::Match(tok2, "*|&")) tok2 = tok2->next(); if (!tok2 || tok2->str() != "(") continue; while (Token::Match(tok2, "(|:: %type%")) tok2 = tok2->tokAt(2); if (!Token::Match(tok2, "(|:: * *| %name%")) continue; tok2 = tok2->tokAt(2); if (tok2->str() == "*") tok2 = tok2->next(); while (Token::Match(tok2, "%type%|:: %type%|::")) tok2 = tok2->next(); if (!Token::Match(tok2, "%name% ) (") && !Token::Match(tok2, "%name% [ ] ) (") && !(Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") ) ("))) continue; while (tok && tok->str() != "(") tok = tok->next(); // check that the declaration ends if (!tok || !tok->link() || !tok->link()->next()) { syntaxError(nullptr); } Token *endTok = tok->link()->next()->link(); if (Token::simpleMatch(endTok, ") throw (")) endTok = endTok->linkAt(2); if (!Token::Match(endTok, ") const|volatile| const|volatile| ;|,|)|=|[|{")) continue; while (Token::Match(endTok->next(), "const|volatile")) endTok->deleteNext(); // ok simplify this function pointer to an ordinary pointer Token::eraseTokens(tok->link(), endTok->next()); if (Token::simpleMatch(tok->link()->previous(), ") )")) { // Function returning function pointer // void (*dostuff(void))(void) {} tok->link()->deleteThis(); tok->deleteThis(); } else { // Function pointer variable // void (*p)(void) {} tok->link()->insertToken("("); Token *par1 = tok->link()->next(); par1->insertToken(")"); par1->link(par1->next()); par1->next()->link(par1); while (Token::Match(tok, "( %type% ::")) tok->deleteNext(2); } } } bool Tokenizer::simplifyFunctionReturn() { std::map functions; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); else if (Token::Match(tok, "%name% ( ) { return %bool%|%char%|%num%|%str% ; }") && tok->strAt(-1) != "::") { const Token* const any = tok->tokAt(5); functions[tok->str()] = any; tok = any; } } if (functions.empty()) return false; bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "(|[|=|return|%op% %name% ( ) ;|]|)|%cop%")) { tok = tok->next(); auto it = functions.find(tok->str()); if (it != functions.cend()) { tok->str(it->second->str()); tok->deleteNext(2); ret = true; } } } return ret; } void Tokenizer::simplifyVarDecl(const bool only_k_r_fpar) { simplifyVarDecl(list.front(), nullptr, only_k_r_fpar); } void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, const bool only_k_r_fpar) { const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11; // Split up variable declarations.. // "int a=4;" => "int a; a=4;" bool finishedwithkr = true; bool scopeDecl = false; for (Token *tok = tokBegin; tok != tokEnd; tok = tok->next()) { if (Token::Match(tok, "{|;")) scopeDecl = false; if (isCPP()) { if (Token::Match(tok, "class|struct|namespace|union")) scopeDecl = true; if (Token::Match(tok, "decltype|noexcept (")) { tok = tok->next()->link(); // skip decltype(...){...} if (tok && Token::simpleMatch(tok->previous(), ") {")) tok = tok->link(); } else if (Token::simpleMatch(tok, "= {") || (!scopeDecl && Token::Match(tok, "%name%|> {") && !Token::Match(tok, "else|try|do|const|constexpr|override|volatile|noexcept"))) { if (!tok->next()->link()) syntaxError(tokBegin); // Check for lambdas before skipping for (Token* tok2 = tok->next(); tok2 != tok->next()->link(); tok2 = tok2->next()) { Token* lambdaEnd = findLambdaEndScope(tok2); if (!lambdaEnd) continue; simplifyVarDecl(lambdaEnd->link()->next(), lambdaEnd, only_k_r_fpar); } tok = tok->next()->link(); } } else if (Token::simpleMatch(tok, "= {")) { tok = tok->next()->link(); } if (!tok) { syntaxError(tokBegin); } if (only_k_r_fpar && finishedwithkr) { if (Token::Match(tok, "(|[|{")) { tok = tok->link(); if (tok->next() && Token::Match(tok, ") !!{")) tok = tok->next(); else continue; } else continue; } else if (tok->str() == "(") { if (isCPP()) { for (Token * tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { if (Token::Match(tok2, "[(,] [")) { // lambda function at tok2->next() // find start of lambda body Token * lambdaBody = tok2; while (lambdaBody && lambdaBody != tok2->link() && lambdaBody->str() != "{") lambdaBody = lambdaBody->next(); if (lambdaBody && lambdaBody != tok2->link() && lambdaBody->link()) simplifyVarDecl(lambdaBody, lambdaBody->link()->next(), only_k_r_fpar); } } } tok = tok->link(); } if (!tok) syntaxError(nullptr); // #7043 invalid code if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:")) continue; if (Token::simpleMatch(tok, "template <")) continue; Token *type0 = tok; if (!Token::Match(type0, "::|extern| %type%")) continue; if (Token::Match(type0, "else|return|public:|protected:|private:")) continue; if (isCPP11 && type0->str() == "using") continue; if (isCPP() && type0->str() == "namespace") continue; bool isconst = false; bool isstatic = false; Token *tok2 = type0; int typelen = 1; if (Token::Match(tok2, "::|extern")) { tok2 = tok2->next(); typelen++; } //check if variable is declared 'const' or 'static' or both while (tok2) { if (!Token::Match(tok2, "const|static|constexpr") && Token::Match(tok2, "%type% const|static")) { tok2 = tok2->next(); ++typelen; } if (Token::Match(tok2, "const|constexpr")) isconst = true; else if (Token::Match(tok2, "static|constexpr")) isstatic = true; else if (Token::Match(tok2, "%type% :: %type%")) { tok2 = tok2->next(); ++typelen; } else break; if (tok2->strAt(1) == "*") break; if (Token::Match(tok2->next(), "& %name% ,")) break; tok2 = tok2->next(); ++typelen; } // strange looking variable declaration => don't split up. if (Token::Match(tok2, "%type% *|&| %name% , %type% *|&| %name%")) continue; if (Token::Match(tok2, "struct|union|class %type%")) { tok2 = tok2->next(); ++typelen; } // check for qualification.. if (Token::Match(tok2, ":: %type%")) { ++typelen; tok2 = tok2->next(); } //skip combinations of templates and namespaces while (!isC() && (Token::Match(tok2, "%type% <") || Token::Match(tok2, "%type% ::"))) { if (tok2->next()->str() == "<" && !TemplateSimplifier::templateParameters(tok2->next())) { tok2 = nullptr; break; } typelen += 2; tok2 = tok2->tokAt(2); if (tok2 && tok2->previous()->str() == "::") continue; int indentlevel = 0; int parens = 0; for (Token *tok3 = tok2; tok3; tok3 = tok3->next()) { ++typelen; if (!parens && tok3->str() == "<") { ++indentlevel; } else if (!parens && tok3->str() == ">") { if (indentlevel == 0) { tok2 = tok3->next(); break; } --indentlevel; } else if (!parens && tok3->str() == ">>") { if (indentlevel <= 1) { tok2 = tok3->next(); break; } indentlevel -= 2; } else if (tok3->str() == "(") { ++parens; } else if (tok3->str() == ")") { if (!parens) { tok2 = nullptr; break; } --parens; } else if (tok3->str() == ";") { break; } } if (Token::Match(tok2, ":: %type%")) { ++typelen; tok2 = tok2->next(); } } //pattern: "%type% *| ... *| const| %name% ,|=" if (Token::Match(tok2, "%type%") || (tok2 && tok2->previous() && tok2->previous()->str() == ">")) { Token *varName = tok2; if (!tok2->previous() || tok2->previous()->str() != ">") varName = varName->next(); else --typelen; //skip all the pointer part bool isPointerOrRef = false; while (Token::simpleMatch(varName, "*") || Token::Match(varName, "& %name% ,")) { isPointerOrRef = true; varName = varName->next(); } while (Token::Match(varName, "%type% %type%")) { if (varName->str() != "const") { ++typelen; } varName = varName->next(); } // Function pointer if (Token::simpleMatch(varName, "( *") && Token::Match(varName->link()->previous(), "%name% ) ( ) =")) { Token *endDecl = varName->link()->tokAt(2); varName = varName->link()->previous(); endDecl->insertToken(";"); endDecl = endDecl->next(); endDecl->insertToken(varName->str()); continue; } //non-VLA case else if (Token::Match(varName, "%name% ,|=")) { if (varName->str() != "operator") { tok2 = varName->next(); // The ',' or '=' token if (tok2->str() == "=" && (isstatic || (isconst && !isPointerOrRef))) { //do not split const non-pointer variables.. while (tok2 && tok2->str() != "," && tok2->str() != ";") { if (Token::Match(tok2, "{|(|[")) tok2 = tok2->link(); const Token *tok3 = tok2; if (!isC() && tok2->str() == "<" && TemplateSimplifier::templateParameters(tok2) > 0) { tok2 = tok2->findClosingBracket(); } if (!tok2) syntaxError(tok3); // #6881 invalid code tok2 = tok2->next(); } if (tok2 && tok2->str() == ";") tok2 = nullptr; } } else tok2 = nullptr; } //VLA case else if (Token::Match(varName, "%name% [")) { tok2 = varName->next(); while (Token::Match(tok2->link(), "] ,|=|[")) tok2 = tok2->link()->next(); if (!Token::Match(tok2, "=|,")) tok2 = nullptr; if (tok2 && tok2->str() == "=") { while (tok2 && tok2->str() != "," && tok2->str() != ";") { if (Token::Match(tok2, "{|(|[")) tok2 = tok2->link(); tok2 = tok2->next(); } if (tok2 && tok2->str() == ";") tok2 = nullptr; } } // brace initialization else if (Token::Match(varName, "%name% {")) { tok2 = varName->next(); tok2 = tok2->link(); if (tok2) tok2 = tok2->next(); if (tok2 && tok2->str() != ",") tok2 = nullptr; } // parenthesis, functions can't be declared like: // int f1(a,b), f2(c,d); // so if there is a comma assume this is a variable declaration else if (Token::Match(varName, "%name% (") && Token::simpleMatch(varName->linkAt(1), ") ,")) { tok2 = varName->linkAt(1)->next(); } else tok2 = nullptr; } else { tok2 = nullptr; } if (!tok2) { if (only_k_r_fpar) finishedwithkr = false; continue; } if (tok2->str() == ",") { tok2->str(";"); tok2->isSplittedVarDeclComma(true); //TODO: should we have to add also template '<>' links? TokenList::insertTokens(tok2, type0, typelen); } else { Token *eq = tok2; while (tok2) { if (Token::Match(tok2, "{|(|[")) tok2 = tok2->link(); else if (!isC() && tok2->str() == "<" && tok2->previous()->isName() && !tok2->previous()->varId()) tok2 = tok2->findClosingBracket(); else if (std::strchr(";,", tok2->str()[0])) { // "type var =" => "type var; var =" const Token *varTok = type0->tokAt(typelen); while (Token::Match(varTok, "%name%|*|& %name%|*|&")) varTok = varTok->next(); if (!varTok) syntaxError(tok2); // invalid code TokenList::insertTokens(eq, varTok, 2); eq->str(";"); eq->isSplittedVarDeclEq(true); // "= x, " => "= x; type " if (tok2->str() == ",") { tok2->str(";"); tok2->isSplittedVarDeclComma(true); TokenList::insertTokens(tok2, type0, typelen); } break; } if (tok2) tok2 = tok2->next(); } } finishedwithkr = (only_k_r_fpar && tok2 && tok2->strAt(1) == "{"); } } void Tokenizer::simplifyStaticConst() { // This function will simplify the token list so that the qualifiers "extern", "static" // and "const" appear in the same order as in the array below. const std::string qualifiers[] = {"extern", "static", "const"}; // Move 'const' before all other qualifiers and types and then // move 'static' before all other qualifiers and types, ... for (Token *tok = list.front(); tok; tok = tok->next()) { bool continue2 = false; for (int i = 0; i < sizeof(qualifiers)/sizeof(qualifiers[0]); i++) { // Keep searching for a qualifier if (!tok->next() || tok->next()->str() != qualifiers[i]) continue; // Look backwards to find the beginning of the declaration Token* leftTok = tok; bool behindOther = false; for (; leftTok; leftTok = leftTok->previous()) { for (int j = 0; j <= i; j++) { if (leftTok->str() == qualifiers[j]) { behindOther = true; break; } } if (behindOther) break; if (!Token::Match(leftTok, "%type%|struct|::") || (isCPP() && Token::Match(leftTok, "private:|protected:|public:|operator|template"))) { break; } } // The token preceding the declaration should indicate the start of a declaration if (leftTok == tok) continue; if (leftTok && !behindOther && !Token::Match(leftTok, ";|{|}|(|,|private:|protected:|public:")) { continue2 = true; break; } // Move the qualifier to the left-most position in the declaration tok->deleteNext(); if (!leftTok) { list.front()->insertToken(qualifiers[i], emptyString, false); list.front()->swapWithNext(); tok = list.front(); } else if (leftTok->next()) { leftTok->next()->insertToken(qualifiers[i], emptyString, true); tok = leftTok->next(); } else { leftTok->insertToken(qualifiers[i]); tok = leftTok; } } if (continue2) continue; } } void Tokenizer::simplifyIfAndWhileAssign() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok->next(), "if|while (")) continue; const Token* tokAt3 = tok->tokAt(3); if (!Token::Match(tokAt3, "!| (| %name% =") && !Token::Match(tokAt3, "!| (| %name% . %name% =") && !Token::Match(tokAt3, "0 == (| %name% =") && !Token::Match(tokAt3, "0 == (| %name% . %name% =")) continue; // simplifying a "while(cond) { }" condition ? const bool iswhile(tok->next()->str() == "while"); // simplifying a "do { } while(cond);" condition ? const bool isDoWhile = iswhile && Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), "do"); Token* openBraceTok = tok->link(); // delete the "if|while" tok->deleteNext(); // Remember if there is a "!" or not. And delete it if there are. const bool isNot(Token::Match(tok->tokAt(2), "!|0")); if (isNot) tok->next()->deleteNext((tok->strAt(2) == "0") ? 2 : 1); // Delete parentheses.. and remember how many there are with // their links. std::stack braces; while (tok->next()->str() == "(") { braces.push(tok->next()->link()); tok->deleteNext(); } // Skip the "%name% = ..." Token *tok2; for (tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")") break; } // Insert "; if|while ( .." tok2 = tok2->previous(); if (tok->strAt(2) == ".") { tok2->insertToken(tok->strAt(3)); tok2->next()->varId(tok->tokAt(3)->varId()); tok2->insertToken("."); } tok2->insertToken(tok->next()->str()); tok2->next()->varId(tok->next()->varId()); while (!braces.empty()) { tok2->insertToken("("); Token::createMutualLinks(tok2->next(), braces.top()); braces.pop(); } if (isNot) tok2->next()->insertToken("!"); tok2->insertToken(iswhile ? "while" : "if"); if (isDoWhile) { tok2->insertToken("}"); Token::createMutualLinks(openBraceTok, tok2->next()); } tok2->insertToken(";"); // delete the extra "}" if (isDoWhile) tok->deleteThis(); // If it's a while loop, insert the assignment in the loop if (iswhile && !isDoWhile) { int indentlevel = 0; Token *tok3 = tok2; for (; tok3; tok3 = tok3->next()) { if (tok3->str() == "{") ++indentlevel; else if (tok3->str() == "}") { if (indentlevel <= 1) break; --indentlevel; } } if (tok3 && indentlevel == 1) { tok3 = tok3->previous(); std::stack braces2; for (tok2 = tok2->next(); tok2 && tok2 != tok; tok2 = tok2->previous()) { tok3->insertToken(tok2->str()); Token *newTok = tok3->next(); newTok->varId(tok2->varId()); newTok->fileIndex(tok2->fileIndex()); newTok->linenr(tok2->linenr()); // link() new tokens manually if (tok2->link()) { if (Token::Match(newTok, "}|)|]|>")) { braces2.push(newTok); } else { Token::createMutualLinks(newTok, braces2.top()); braces2.pop(); } } } } } } } void Tokenizer::simplifyVariableMultipleAssign() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% = %name% = %num%|%name% ;")) { // skip intermediate assignments Token *tok2 = tok->previous(); while (tok2 && tok2->str() == "=" && Token::Match(tok2->previous(), "%name%")) { tok2 = tok2->tokAt(-2); } if (!tok2 || tok2->str() != ";") { continue; } Token *stopAt = tok->tokAt(2); const Token *valueTok = stopAt->tokAt(2); const std::string& value(valueTok->str()); tok2 = tok2->next(); while (tok2 != stopAt) { tok2->next()->insertToken(";"); tok2->next()->insertToken(value); tok2 = tok2->tokAt(4); } } } } // Binary operators simplification map static const std::unordered_map cAlternativeTokens = { std::make_pair("and", "&&") , std::make_pair("and_eq", "&=") , std::make_pair("bitand", "&") , std::make_pair("bitor", "|") , std::make_pair("not_eq", "!=") , std::make_pair("or", "||") , std::make_pair("or_eq", "|=") , std::make_pair("xor", "^") , std::make_pair("xor_eq", "^=") }; // Simplify the C alternative tokens: // and => && // and_eq => &= // bitand => & // bitor => | // compl => ~ // not => ! // not_eq => != // or => || // or_eq => |= // xor => ^ // xor_eq => ^= bool Tokenizer::simplifyCAlternativeTokens() { /* executable scope level */ int executableScopeLevel = 0; std::vector alt; bool replaceAll = false; // replace all or none for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == ")") { if (const Token *end = isFunctionHead(tok, "{")) { ++executableScopeLevel; tok = const_cast(end); continue; } } if (tok->str() == "{") { if (executableScopeLevel > 0) ++executableScopeLevel; continue; } if (tok->str() == "}") { if (executableScopeLevel > 0) --executableScopeLevel; continue; } if (!tok->isName()) continue; const std::unordered_map::const_iterator cOpIt = cAlternativeTokens.find(tok->str()); if (cOpIt != cAlternativeTokens.end()) { alt.push_back(tok); // Is this a variable declaration.. if (isC() && Token::Match(tok->previous(), "%type%|* %name% [;,=]")) return false; if (!Token::Match(tok->previous(), "%name%|%num%|%char%|)|]|> %name% %name%|%num%|%char%|%op%|(")) continue; if (Token::Match(tok->next(), "%assign%|%or%|%oror%|&&|*|/|%|^") && !Token::Match(tok->previous(), "%num%|%char%|) %name% *")) continue; if (executableScopeLevel == 0 && Token::Match(tok, "%name% (")) { const Token *start = tok; while (Token::Match(start, "%name%|*")) start = start->previous(); if (!start || Token::Match(start, "[;}]")) continue; } replaceAll = true; } else if (Token::Match(tok, "not|compl")) { alt.push_back(tok); if (Token::Match(tok->previous(), "%assign%") || Token::Match(tok->next(), "%num%")) { replaceAll = true; continue; } // Don't simplify 'not p;' (in case 'not' is a type) if (!Token::Match(tok->next(), "%name%|(") || Token::Match(tok->previous(), "[;{}]") || (executableScopeLevel == 0U && tok->strAt(-1) == "(")) continue; replaceAll = true; } } if (!replaceAll) return false; for (Token *tok: alt) { const std::unordered_map::const_iterator cOpIt = cAlternativeTokens.find(tok->str()); if (cOpIt != cAlternativeTokens.end()) tok->str(cOpIt->second); else if (tok->str() == "not") tok->str("!"); else tok->str("~"); } return !alt.empty(); } // int i(0); => int i; i = 0; // int i(0), j; => int i; i = 0; int j; void Tokenizer::simplifyInitVar() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName() || (tok->previous() && !Token::Match(tok->previous(), "[;{}]"))) continue; if (tok->str() == "return") continue; if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ;")) { tok = initVar(tok); } else if (Token::Match(tok, "%type% *| %name% ( %type% (")) { const Token* tok2 = tok->tokAt(2); if (!tok2->link()) tok2 = tok2->next(); if (!tok2->link() || (tok2->link()->strAt(1) == ";" && !Token::simpleMatch(tok2->linkAt(2), ") ("))) tok = initVar(tok); } else if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ,")) { Token *tok1 = tok->tokAt(5); while (tok1->str() != ",") tok1 = tok1->next(); tok1->str(";"); const int numTokens = (Token::Match(tok, "class|struct|union")) ? 2U : 1U; TokenList::insertTokens(tok1, tok, numTokens); tok = initVar(tok); } } } Token * Tokenizer::initVar(Token * tok) { // call constructor of class => no simplification if (Token::Match(tok, "class|struct|union")) { if (tok->strAt(2) != "*") return tok; tok = tok->next(); } else if (!tok->isStandardType() && tok->str() != "auto" && tok->next()->str() != "*") return tok; // goto variable name.. tok = tok->next(); if (tok->str() == "*") tok = tok->next(); // sizeof is not a variable name.. if (tok->str() == "sizeof") return tok; // check initializer.. if (tok->tokAt(2)->isStandardType() || tok->strAt(2) == "void") return tok; else if (!tok->tokAt(2)->isNumber() && !Token::Match(tok->tokAt(2), "%type% (") && tok->strAt(2) != "&" && tok->tokAt(2)->varId() == 0) return tok; // insert '; var =' tok->insertToken(";"); tok->next()->insertToken(tok->str()); tok->tokAt(2)->varId(tok->varId()); tok = tok->tokAt(2); tok->insertToken("="); // goto '('.. tok = tok->tokAt(2); // delete ')' tok->link()->deleteThis(); // delete this tok->deleteThis(); return tok; } bool Tokenizer::simplifyKnownVariables() { // return value for function. Set to true if any simplifications are made bool ret = false; // constants.. { std::unordered_map constantValues; std::map constantVars; std::unordered_map> constantValueUsages; for (Token *tok = list.front(); tok; tok = tok->next()) { // Reference to variable if (Token::Match(tok, "%type%|* & %name% = %name% ;")) { Token *start = tok->previous(); while (Token::Match(start,"%type%|*|&")) start = start->previous(); if (!Token::Match(start,"[;{}]")) continue; const Token *reftok = tok->tokAt(2); const Token *vartok = reftok->tokAt(2); int level = 0; for (Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { ++level; } else if (tok2->str() == "}") { if (level <= 0) break; --level; } else if (tok2->varId() == reftok->varId()) { tok2->str(vartok->str()); tok2->varId(vartok->varId()); } } Token::eraseTokens(start, tok->tokAt(6)); tok = start; } if (tok->isName() && (Token::Match(tok, "static| const| static| %type% const| %name% = %any% ;") || Token::Match(tok, "static| const| static| %type% const| %name% ( %any% ) ;"))) { bool isconst = false; for (const Token *tok2 = tok; (tok2->str() != "=") && (tok2->str() != "("); tok2 = tok2->next()) { if (tok2->str() == "const") { isconst = true; break; } } if (!isconst) continue; Token *tok1 = tok; // start of statement if (tok != list.front() && !Token::Match(tok->previous(),";|{|}|private:|protected:|public:")) continue; // skip "const" and "static" while (Token::Match(tok, "const|static")) tok = tok->next(); // pod type if (!tok->isStandardType()) continue; Token * const vartok = (tok->next() && tok->next()->str() == "const") ? tok->tokAt(2) : tok->next(); const Token * const valuetok = vartok->tokAt(2); if (Token::Match(valuetok, "%bool%|%char%|%num%|%str% )| ;")) { // record a constant value for this variable constantValues[vartok->varId()] = valuetok->str(); constantVars[vartok->varId()] = tok1; } } else if (tok->varId()) { // find the entry for the known variable, if any. Exclude the location where the variable is assigned with next == "=" if (constantValues.find(tok->varId()) != constantValues.end() && tok->next()->str() != "=") { constantValueUsages[tok->varId()].push_back(tok); } } } for (auto constantVar = constantVars.rbegin(); constantVar != constantVars.rend(); constantVar++) { bool referenceFound = false; std::list usageList = constantValueUsages[constantVar->first]; for (Token* usage : usageList) { // check if any usages of each known variable are a reference if (Token::Match(usage->tokAt(-2), "(|[|,|{|return|%op% & %varid%", constantVar->first)) { referenceFound = true; break; } } if (!referenceFound) { // replace all usages of non-referenced known variables with their value for (Token* usage : usageList) { usage->str(constantValues[constantVar->first]); } Token* startTok = constantVar->second; // remove variable assignment statement while (startTok->next()->str() != ";") startTok->deleteNext(); startTok->deleteNext(); // #8579 if we can we want another token to delete startTok. if we can't it doesn't matter if (startTok->previous()) { startTok->previous()->deleteNext(); } else if (startTok->next()) { startTok->next()->deletePrevious(); } else { startTok->deleteThis(); } startTok = nullptr; constantVar->second = nullptr; ret = true; } } } // variable id for local, float/double, array variables std::set localvars; std::set floatvars; std::set arrays; // auto variables.. for (Token *tok = list.front(); tok; tok = tok->next()) { // Search for a block of code Token * const start = const_cast(startOfExecutableScope(tok)); if (!start) continue; for (const Token *tok2 = start->previous(); tok2 && !Token::Match(tok2, "[;{}]"); tok2 = tok2->previous()) { if (tok2->varId() != 0) localvars.insert(tok2->varId()); } tok = start; // parse the block of code.. int indentlevel = 0; Token *tok2 = tok; for (; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}] %type% %name%|*")) { bool isfloat = false; bool ispointer = false; const Token *vartok = tok2->next(); while (Token::Match(vartok, "%name%|* %name%|*")) { if (Token::Match(vartok, "float|double")) isfloat = true; if (vartok->str() == "*") ispointer = true; vartok = vartok->next(); } if (Token::Match(vartok, "%var% ;|[")) localvars.insert(vartok->varId()); if (isfloat && !ispointer && Token::Match(vartok, "%var% ;")) floatvars.insert(vartok->varId()); if (Token::Match(vartok, "%var% [")) arrays.insert(vartok->varId()); } if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { --indentlevel; if (indentlevel <= 0) break; } else if (Token::simpleMatch(tok2, "for (")) tok2 = tok2->next()->link(); else if (tok2->previous()->str() != "*" && !Token::Match(tok2->tokAt(-2), "* --|++") && (Token::Match(tok2, "%name% = %bool%|%char%|%num%|%str%|%name% ;") || Token::Match(tok2, "%name% [ %num%| ] = %str% ;") || Token::Match(tok2, "%name% = & %name% ;") || (Token::Match(tok2, "%name% = & %name% [ 0 ] ;") && arrays.find(tok2->tokAt(3)->varId()) != arrays.end()))) { const int varid = tok2->varId(); if (varid == 0) continue; if (Token::Match(tok2->previous(), "[;{}]") && localvars.find(varid) == localvars.end()) continue; // initialization of static variable => the value is not *known* { bool isstatic = false; const Token *decl = tok2->previous(); while (decl && (decl->isName() || decl->str() == "*")) { if (decl->str() == "static") { isstatic = true; break; } decl = decl->previous(); } if (isstatic) continue; } // skip loop variable if (Token::Match(tok2->tokAt(-2), "(|:: %type%")) { const Token *tok3 = tok2->previous(); do { tok3 = tok3->tokAt(-2); } while (Token::Match(tok3->previous(), ":: %type%")); if (Token::Match(tok3->tokAt(-2), "for ( %type%")) continue; } // struct name.. if (Token::Match(tok2, "%varid% = &| %varid%", tok2->varId())) continue; const std::string structname = Token::Match(tok2->tokAt(-3), "[;{}] %name% .") ? std::string(tok2->strAt(-2) + " .") : std::string(); const Token * const valueToken = tok2->tokAt(2); std::string value; nonneg int valueVarId = 0; Token *tok3 = nullptr; bool valueIsPointer = false; // there could be a hang here if tok2 is moved back by the function calls below for some reason if (Settings::terminated()) return false; if (!simplifyKnownVariablesGetData(varid, &tok2, &tok3, value, valueVarId, valueIsPointer, floatvars.find(tok2->varId()) != floatvars.end())) continue; if (valueVarId > 0 && arrays.find(valueVarId) != arrays.end()) continue; ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, structname, value, valueVarId, valueIsPointer, valueToken, indentlevel); } else if (Token::Match(tok2, "strcpy|sprintf ( %name% , %str% ) ;")) { const int varid(tok2->tokAt(2)->varId()); if (varid == 0) continue; const Token * const valueToken = tok2->tokAt(4); std::string value(valueToken->str()); if (tok2->str() == "sprintf") { std::string::size_type n = 0; while ((n = value.find("%%", n)) != std::string::npos) { // Replace "%%" with "%" - erase the first '%' and continue past the second '%' value.erase(n, 1); ++n; } } const int valueVarId(0); const bool valueIsPointer(false); Token *tok3 = tok2->tokAt(6); ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, emptyString, value, valueVarId, valueIsPointer, valueToken, indentlevel); // there could be a hang here if tok2 was moved back by the function call above for some reason if (Settings::terminated()) return false; } } if (tok2) tok = tok2->previous(); } return ret; } bool Tokenizer::simplifyKnownVariablesGetData(nonneg int varid, Token **_tok2, Token **_tok3, std::string &value, nonneg int &valueVarId, bool &valueIsPointer, bool floatvar) { Token *tok2 = *_tok2; Token *tok3 = nullptr; if (Token::simpleMatch(tok2->tokAt(-2), "for (")) { // only specific for loops is handled if (!Token::Match(tok2, "%varid% = %num% ; %varid% <|<= %num% ; ++| %varid% ++| ) {", varid)) return false; // is there a "break" in the for loop? bool hasbreak = false; const Token* end4 = tok2->linkAt(-1)->linkAt(1); for (const Token *tok4 = tok2->previous()->link(); tok4 != end4; tok4 = tok4->next()) { if (tok4->str() == "break") { hasbreak = true; break; } } if (hasbreak) return false; // no break => the value of the counter value is known after the for loop.. const Token* compareTok = tok2->tokAt(5); if (compareTok->str() == "<") { value = compareTok->next()->str(); valueVarId = compareTok->next()->varId(); } else value = MathLib::toString(MathLib::toLongNumber(compareTok->next()->str()) + 1); // Skip for-body.. tok3 = tok2->previous()->link()->next()->link()->next(); } else { value = tok2->strAt(2); valueVarId = tok2->tokAt(2)->varId(); if (tok2->strAt(1) == "[") { value = tok2->next()->link()->strAt(2); valueVarId = 0; } else if (value == "&") { value = tok2->strAt(3); valueVarId = tok2->tokAt(3)->varId(); // *ptr = &var; *ptr = 5; // equals // var = 5; not *var = 5; if (tok2->strAt(4) == ";") valueIsPointer = true; } // Add a '.0' to a decimal value and therefore convert it to an floating point number. else if (MathLib::isDec(tok2->strAt(2)) && floatvar) { value += ".0"; } // float variable: convert true/false to 1.0 / 0.0 else if (tok2->tokAt(2)->isBoolean() && floatvar) { value = (value == "true") ? "1.0" : "0.0"; } if (Token::simpleMatch(tok2->next(), "= &")) tok2 = tok2->tokAt(3); tok3 = tok2->next(); } *_tok2 = tok2; *_tok3 = tok3; return true; } bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, nonneg int varid, const std::string &structname, std::string &value, nonneg int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) const { const bool pointeralias(valueToken->isName() || Token::Match(valueToken, "& %name% [")); const bool varIsGlobal = (indentlevel == 0); const bool printDebug = mSettings->debugwarnings; if (mErrorLogger && !list.getFiles().empty()) mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (simplifyKnownVariables)", tok3->progressValue()); if (isMaxTime()) return false; bool ret = false; Token* bailOutFromLoop = nullptr; int indentlevel3 = indentlevel; bool ret3 = false; for (; tok3; tok3 = tok3->next()) { if (tok3->str() == "{") { ++indentlevel3; } else if (tok3->str() == "}") { --indentlevel3; if (indentlevel3 < indentlevel) { if (Token::Match((*tok2)->tokAt(-7), "%type% * %name% ; %name% = & %name% ;") && (*tok2)->strAt(-5) == (*tok2)->strAt(-3)) { (*tok2) = (*tok2)->tokAt(-4); Token::eraseTokens((*tok2), (*tok2)->tokAt(6)); } break; } } // Stop if there is a pointer alias and a shadow variable is // declared in an inner scope (#3058) if (valueIsPointer && tok3->varId() > 0 && tok3->previous() && (tok3->previous()->isName() || tok3->previous()->str() == "*") && valueToken->str() == "&" && valueToken->next() && valueToken->next()->isName() && tok3->str() == valueToken->next()->str() && tok3->varId() > valueToken->next()->varId()) { // more checking if this is a variable declaration bool decl = true; for (const Token *tok4 = tok3->previous(); tok4; tok4 = tok4->previous()) { if (Token::Match(tok4, "[;{}]")) break; else if (tok4->isName()) { if (tok4->varId() > 0) { decl = false; break; } } else if (!Token::Match(tok4, "[&*]")) { decl = false; break; } } if (decl) break; } // Stop if label is found if (Token::Match(tok3, "; %type% : ;")) break; // Stop if break/continue is found .. if (Token::Match(tok3, "break|continue")) break; if ((indentlevel3 > 1 || !Token::simpleMatch(Token::findsimplematch(tok3,";"), "; }")) && tok3->str() == "return") ret3 = true; if (ret3 && tok3->str() == ";") break; if (pointeralias && Token::Match(tok3, ("!!= " + value).c_str())) break; // Stop if a loop is found if (pointeralias && Token::Match(tok3, "do|for|while")) break; // Stop if unknown function call is seen and the variable is global: it might be // changed by the function call if (varIsGlobal && tok3->str() == ")" && tok3->link() && Token::Match(tok3->link()->tokAt(-2), "[;{}] %name% (") && !Token::Match(tok3->link()->previous(), "if|for|while|switch|BOOST_FOREACH")) break; // Stop if something like 'while (--var)' is found if (Token::Match(tok3, "for|while|do")) { const Token *endpar = tok3->next()->link(); if (Token::simpleMatch(endpar, ") {")) endpar = endpar->next()->link(); bool bailout = false; for (const Token *tok4 = tok3; tok4 && tok4 != endpar; tok4 = tok4->next()) { if (Token::Match(tok4, "++|-- %varid%", varid) || Token::Match(tok4, "%varid% ++|--|=", varid)) { bailout = true; break; } } if (bailout) break; } if (bailOutFromLoop) { // This could be a loop, skip it, but only if it doesn't contain // the variable we are checking for. If it contains the variable // we will bail out. if (tok3->varId() == varid) { // Continue //tok2 = bailOutFromLoop; break; } else if (tok3 == bailOutFromLoop) { // We have skipped the loop bailOutFromLoop = nullptr; continue; } continue; } else if (tok3->str() == "{" && tok3->previous()->str() == ")") { // There is a possible loop after the assignment. Try to skip it. if (tok3->previous()->link() && tok3->previous()->link()->strAt(-1) != "if") bailOutFromLoop = tok3->link(); continue; } // Variable used in realloc (see Ticket #1649) if (Token::Match(tok3, "%name% = realloc ( %name% ,") && tok3->varId() == varid && tok3->tokAt(4)->varId() == varid) { tok3->tokAt(4)->str(value); ret = true; } // condition "(|&&|%OROR% %varid% )|&&|%OROR%|; if (!Token::Match(tok3->previous(), "( %name% )") && Token::Match(tok3->previous(), "&&|(|%oror% %varid% &&|%oror%|)|;", varid)) { tok3->str(value); tok3->varId(valueVarId); ret = true; } // parameter in function call.. if (tok3->varId() == varid && Token::Match(tok3->previous(), "[(,] %name% [,)]")) { // If the parameter is passed by value then simplify it if (isFunctionParameterPassedByValue(tok3)) { tok3->str(value); tok3->varId(valueVarId); ret = true; } } // Variable is used somehow in a non-defined pattern => bail out if (tok3->varId() == varid) { // This is a really generic bailout so let's try to avoid this. // There might be lots of false negatives. if (printDebug) { // FIXME: Fix all the debug warnings for values and then // remove this bailout if (pointeralias) break; // suppress debug-warning when calling member function if (Token::Match(tok3->next(), ". %name% (")) break; // suppress debug-warning when assignment if (tok3->strAt(1) == "=") break; // taking address of variable.. if (Token::Match(tok3->tokAt(-2), "return|= & %name% ;")) break; // parameter in function call.. if (Token::Match(tok3->tokAt(-2), "%name% ( %name% ,|)") || Token::Match(tok3->previous(), ", %name% ,|)")) break; // conditional increment if (Token::Match(tok3->tokAt(-3), ") { ++|--") || Token::Match(tok3->tokAt(-2), ") { %name% ++|--")) break; reportError(tok3, Severity::debug, "debug", "simplifyKnownVariables: bailing out (variable="+tok3->str()+", value="+value+")"); } break; } // Using the variable in condition.. if (Token::Match(tok3->previous(), ("if ( " + structname + " %varid% %cop%|)").c_str(), varid) || Token::Match(tok3, ("( " + structname + " %varid% %comp%").c_str(), varid) || Token::Match(tok3, ("%comp%|!|= " + structname + " %varid% %cop%|)|;").c_str(), varid) || Token::Match(tok3->previous(), "strlen|free ( %varid% )", varid)) { if (value[0] == '\"' && tok3->previous()->str() != "strlen") { // bail out if value is a string unless if it's just given // as parameter to strlen break; } if (!structname.empty()) { tok3->deleteNext(2); } if (Token::Match(valueToken, "& %name% ;")) { tok3->insertToken("&"); tok3 = tok3->next(); } tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // pointer alias used in condition.. if (Token::Match(valueToken,"& %name% ;") && Token::Match(tok3, ("( * " + structname + " %varid% %cop%").c_str(), varid)) { tok3->deleteNext(); if (!structname.empty()) tok3->deleteNext(2); tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // Delete pointer alias if (isCPP() && pointeralias && (tok3->str() == "delete") && tok3->next() && (Token::Match(tok3->next(), "%varid% ;", varid) || Token::Match(tok3->next(), "[ ] %varid%", varid))) { tok3 = (tok3->next()->str() == "[") ? tok3->tokAt(3) : tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // Variable is used in function call.. if (Token::Match(tok3, ("%name% ( " + structname + " %varid% ,").c_str(), varid)) { static const char * const functionName[] = { // always simplify "strcmp", "strdup", // don't simplify buffer value "memcmp","memcpy","memmove","memset","strcpy","strncmp","strncpy" }; for (int i = 0; i < (sizeof(functionName) / sizeof(*functionName)); ++i) { if (valueVarId == 0U && i >= 2) break; if (tok3->str() == functionName[i]) { Token *par1 = tok3->tokAt(2); if (!structname.empty()) { par1->deleteNext(); par1->deleteThis(); } par1->str(value); par1->varId(valueVarId); break; } } } // Variable is used as 2nd parameter in function call.. if (Token::Match(tok3, ("%name% ( %any% , " + structname + " %varid% ,|)").c_str(), varid)) { static const char * const functionName[] = { // always simplify "strcmp","strcpy","strncmp","strncpy", // don't simplify buffer value "memcmp","memcpy","memmove" }; for (int i = 0; i < (sizeof(functionName) / sizeof(*functionName)); ++i) { if (valueVarId == 0U && i >= 4) break; if (tok3->str() == functionName[i]) { Token *par = tok3->tokAt(4); if (!structname.empty()) { par->deleteNext(); par->deleteThis(); } par->str(value); par->varId(valueVarId); break; } } } // array usage if (value[0] != '\"' && Token::Match(tok3, ("[(,] " + structname + " %varid% [|%cop%").c_str(), varid)) { if (!structname.empty()) { tok3->deleteNext(2); } tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // The >> operator is sometimes used to assign a variable in C++ if (isCPP() && Token::Match(tok3, (">> " + structname + " %varid%").c_str(), varid)) { // bailout for such code: ; std :: cin >> i ; const Token *prev = tok3->previous(); while (prev && prev->str() != "return" && Token::Match(prev, "%name%|::|*")) prev = prev->previous(); if (Token::Match(prev, ";|{|}|>>")) break; } // Variable is used in calculation.. if (((tok3->previous()->varId() > 0) && Token::Match(tok3, ("& " + structname + " %varid%").c_str(), varid)) || (Token::Match(tok3, ("[=+-*/%^|[] " + structname + " %varid% [=?+-*/%^|;])]").c_str(), varid) && !Token::Match(tok3, ("= " + structname + " %name% =").c_str())) || Token::Match(tok3, ("[(=+-*/%^|[] " + structname + " %varid% <<|>>").c_str(), varid) || Token::Match(tok3, ("<<|>> " + structname + " %varid% %cop%|;|]|)").c_str(), varid) || Token::Match(tok3->previous(), ("[=+-*/%^|[] ( " + structname + " %varid% !!=").c_str(), varid)) { if (value[0] == '\"') break; if (!structname.empty()) { tok3->deleteNext(2); ret = true; } tok3 = tok3->next(); if (tok3->str() != value) ret = true; tok3->str(value); tok3->varId(valueVarId); if (tok3->previous()->str() == "*" && (valueIsPointer || Token::Match(valueToken, "& %name% ;"))) { tok3 = tok3->previous(); tok3->deleteThis(); ret = true; } else if (Token::Match(valueToken, "& %name% ;")) tok3->insertToken("&", emptyString, true); } if (Token::simpleMatch(tok3, "= {")) { const Token* const end4 = tok3->linkAt(1); for (const Token *tok4 = tok3; tok4 != end4; tok4 = tok4->next()) { if (Token::Match(tok4, "{|, %varid% ,|}", varid)) { tok4->next()->str(value); tok4->next()->varId(valueVarId); ret = true; } } } // Using the variable in for-condition.. if (Token::simpleMatch(tok3, "for (")) { for (Token *tok4 = tok3->tokAt(2); tok4; tok4 = tok4->next()) { if (Token::Match(tok4, "(|)")) break; // Replace variable used in condition.. if (Token::Match(tok4, "; %name% <|<=|!= %name% ; ++| %name% ++| )")) { const Token *inctok = tok4->tokAt(5); if (inctok->str() == "++") inctok = inctok->next(); if (inctok->varId() == varid) break; if (tok4->next()->varId() == varid) { tok4->next()->str(value); tok4->next()->varId(valueVarId); ret = true; } if (tok4->tokAt(3)->varId() == varid) { tok4->tokAt(3)->str(value); tok4->tokAt(3)->varId(valueVarId); ret = true; } } } } if (indentlevel == indentlevel3 && Token::Match(tok3->next(), "%varid% ++|--", varid) && MathLib::isInt(value)) { const std::string op(tok3->strAt(2)); if (Token::Match(tok3, "[{};] %any% %any% ;")) { tok3->deleteNext(3); } else { tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); tok3->deleteNext(); } value = MathLib::incdec(value, op); if (!Token::simpleMatch((*tok2)->tokAt(-2), "for (")) { (*tok2)->tokAt(2)->str(value); (*tok2)->tokAt(2)->varId(valueVarId); } ret = true; } if (indentlevel == indentlevel3 && Token::Match(tok3->next(), "++|-- %varid%", varid) && MathLib::isInt(value) && !Token::Match(tok3->tokAt(3), "[.[]")) { value = MathLib::incdec(value, tok3->next()->str()); (*tok2)->tokAt(2)->str(value); (*tok2)->tokAt(2)->varId(valueVarId); if (Token::Match(tok3, "[;{}] %any% %any% ;")) { tok3->deleteNext(3); } else { tok3->deleteNext(); tok3->next()->str(value); tok3->next()->varId(valueVarId); } tok3 = tok3->next(); ret = true; } // return variable.. if (Token::Match(tok3, "return %varid% %any%", varid) && valueToken->str() != "&" && (tok3->tokAt(2)->isExtendedOp() || tok3->strAt(2) == ";") && value[0] != '\"') { tok3->next()->str(value); tok3->next()->varId(valueVarId); } else if (pointeralias && Token::Match(tok3, "return * %varid% ;", varid) && value[0] != '\"') { tok3->deleteNext(); tok3->next()->str(value); tok3->next()->varId(valueVarId); } } return ret; } void Tokenizer::elseif() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "else if")) continue; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "(|{|[")) tok2 = tok2->link(); if (Token::Match(tok2, "}|;")) { if (tok2->next() && tok2->next()->str() != "else") { tok->insertToken("{"); tok2->insertToken("}"); Token::createMutualLinks(tok->next(), tok2->next()); break; } } } } } void Tokenizer::simplifyIfSwitchForInit() { if (!isCPP() || mSettings->standards.cpp < Standards::CPP17) return; const bool forInit = (mSettings->standards.cpp >= Standards::CPP20); for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "if|switch|for (")) continue; Token *semicolon = tok->tokAt(2); while (!Token::Match(semicolon, "[;)]")) { if (Token::Match(semicolon, "(|{|[") && semicolon->link()) semicolon = semicolon->link(); semicolon = semicolon->next(); } if (semicolon->str() != ";") continue; if (tok->str() == "for") { if (!forInit) continue; // Is it a for range.. const Token *tok2 = semicolon->next(); bool rangeFor = false; while (!Token::Match(tok2, "[;)]")) { if (tok2->str() == "(") tok2 = tok2->link(); else if (!rangeFor && tok2->str() == "?") break; else if (tok2->str() == ":") rangeFor = true; tok2 = tok2->next(); } if (!rangeFor || tok2->str() != ")") continue; } Token *endpar = tok->linkAt(1); if (!Token::simpleMatch(endpar, ") {")) continue; Token *endscope = endpar->linkAt(1); if (Token::simpleMatch(endscope, "} else {")) endscope = endscope->linkAt(2); // Simplify, the initialization expression is broken out.. semicolon->insertToken(tok->str()); semicolon->next()->insertToken("("); Token::createMutualLinks(semicolon->next()->next(), endpar); tok->deleteNext(); tok->str("{"); endscope->insertToken("}"); Token::createMutualLinks(tok, endscope->next()); } } bool Tokenizer::simplifyRedundantParentheses() { bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "(") continue; if (isCPP() && Token::simpleMatch(tok->previous(), "} (") && Token::Match(tok->previous()->link()->previous(), "%name%|> {")) continue; if (Token::simpleMatch(tok, "( {")) continue; if (Token::Match(tok->link(), ") %num%")) { tok = tok->link(); continue; } // Do not simplify if there is comma inside parentheses.. if (Token::Match(tok->previous(), "%op% (") || Token::Match(tok->link(), ") %op%")) { bool innerComma = false; for (const Token *inner = tok->link()->previous(); inner != tok; inner = inner->previous()) { if (inner->str() == ")") inner = inner->link(); if (inner->str() == ",") { innerComma = true; break; } } if (innerComma) continue; } // !!operator = ( x ) ; if (tok->strAt(-2) != "operator" && tok->previous() && tok->previous()->str() == "=" && tok->next() && tok->next()->str() != "{" && Token::simpleMatch(tok->link(), ") ;")) { tok->link()->deleteThis(); tok->deleteThis(); continue; } while (Token::simpleMatch(tok, "( (") && tok->link() && tok->link()->previous() == tok->next()->link()) { // We have "(( *something* ))", remove the inner // parentheses tok->deleteNext(); tok->link()->tokAt(-2)->deleteNext(); ret = true; } if (isCPP() && Token::Match(tok->tokAt(-2), "[;{}=(] new (") && Token::Match(tok->link(), ") [;,{}[]")) { // Remove the parentheses in "new (type)" constructs tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "! ( %name% )")) { // Remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "[(,;{}] ( %name% ) .")) { // Remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "[(,;{}] ( %name% (") && tok->link()->previous() == tok->linkAt(2)) { // We have "( func ( *something* ))", remove the outer // parentheses tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "[,;{}] ( delete [| ]| %name% ) ;")) { // We have "( delete [| ]| var )", remove the outer // parentheses tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (!Token::simpleMatch(tok->tokAt(-2), "operator delete") && Token::Match(tok->previous(), "delete|; (") && (tok->previous()->str() != "delete" || tok->next()->varId() > 0) && Token::Match(tok->link(), ") ;|,")) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "[(!*;{}] ( %name% )") && (tok->next()->varId() != 0 || Token::Match(tok->tokAt(3), "[+-/=]")) && !tok->next()->isStandardType()) { // We have "( var )", remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } while (Token::Match(tok->previous(), "[;{}[(,!*] ( %name% .")) { Token *tok2 = tok->tokAt(2); while (Token::Match(tok2, ". %name%")) { tok2 = tok2->tokAt(2); } if (tok2 != tok->link()) break; // We have "( var . var . ... . var )", remove the parentheses tok = tok->previous(); tok->deleteNext(); tok2->deleteThis(); ret = true; } if (Token::simpleMatch(tok->previous(), "? (") && Token::simpleMatch(tok->link(), ") :")) { const Token *tok2 = tok->next(); while (tok2 && (Token::Match(tok2,"%bool%|%num%|%name%") || tok2->isArithmeticalOp())) tok2 = tok2->next(); if (tok2 && tok2->str() == ")") { tok->link()->deleteThis(); tok->deleteThis(); ret = true; continue; } } while (Token::Match(tok->previous(), "[{([,] ( !!{") && Token::Match(tok->link(), ") [;,])]") && !Token::simpleMatch(tok->tokAt(-2), "operator ,") && // Ticket #5709 !Token::findsimplematch(tok, ",", tok->link())) { // We have "( ... )", remove the parentheses tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::simpleMatch(tok->previous(), ", (") && Token::simpleMatch(tok->link(), ") =")) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } // Simplify "!!operator !!%name%|)|]|>|>> ( %num%|%bool% ) %op%|;|,|)" if (Token::Match(tok, "( %bool%|%num% ) %cop%|;|,|)") && tok->strAt(-2) != "operator" && tok->previous() && !Token::Match(tok->previous(), "%name%|)|]") && (!(isCPP() && Token::Match(tok->previous(),">|>>")))) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "*|& ( %name% )")) { // We may have a variable declaration looking like "type_name *(var_name)" Token *tok2 = tok->tokAt(-2); while (Token::Match(tok2, "%type%|static|const|extern") && tok2->str() != "operator") { tok2 = tok2->previous(); } if (tok2 && !Token::Match(tok2, "[;,{]")) { // Not a variable declaration } else { tok->deleteThis(); tok->deleteNext(); } } } return ret; } void Tokenizer::simplifyTypeIntrinsics() { static const std::unordered_map intrinsics = { { "__has_nothrow_assign", "has_nothrow_assign" }, { "__has_nothrow_constructor", "has_nothrow_constructor" }, { "__has_nothrow_copy", "has_nothrow_copy" }, { "__has_trivial_assign", "has_trivial_assign" }, { "__has_trivial_constructor", "has_trivial_constructor" }, { "__has_trivial_copy", "has_trivial_copy" }, { "__has_trivial_destructor", "has_trivial_destructor" }, { "__has_virtual_destructor", "has_virtual_destructor" }, { "__is_abstract", "is_abstract" }, { "__is_aggregate", "is_aggregate" }, { "__is_assignable", "is_assignable" }, { "__is_base_of", "is_base_of" }, { "__is_class", "is_class" }, { "__is_constructible", "is_constructible" }, { "__is_convertible_to", "is_convertible_to" }, { "__is_destructible", "is_destructible" }, { "__is_empty", "is_empty" }, { "__is_enum", "is_enum" }, { "__is_final", "is_final" }, { "__is_nothrow_assignable", "is_nothrow_assignable" }, { "__is_nothrow_constructible", "is_nothrow_constructible" }, { "__is_nothrow_destructible", "is_nothrow_destructible" }, { "__is_pod", "is_pod" }, { "__is_polymorphic", "is_polymorphic" }, { "__is_trivially_assignable", "is_trivially_assignable" }, { "__is_trivially_constructible", "is_trivially_constructible" }, { "__is_union", "is_union" }, }; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; auto p = intrinsics.find(tok->str()); if (p == intrinsics.end()) continue; Token * end = tok->next()->link(); Token * prev = tok->previous(); tok->str(p->second); prev->insertToken("::"); prev->insertToken("std"); tok->next()->str("<"); end->str(">"); end->insertToken("}"); end->insertToken("{"); Token::createMutualLinks(end->tokAt(1), end->tokAt(2)); } } void Tokenizer::simplifyCharAt() { // Replace "string"[0] with 's' for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%str% [ %num% ]")) { const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(2)); // Check within range if (index >= 0 && index <= Token::getStrLength(tok)) { tok->str("'" + Token::getCharAt(tok, index) + "'"); tok->deleteNext(3); } } } } void Tokenizer::simplifyReference() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { // starting executable scope.. Token *start = const_cast(startOfExecutableScope(tok)); if (start) { tok = start; // replace references in this scope.. Token * const end = tok->link(); for (Token *tok2 = tok; tok2 && tok2 != end; tok2 = tok2->next()) { // found a reference.. if (Token::Match(tok2, "[;{}] %type% & %name% (|= %name% )| ;")) { const int refId = tok2->tokAt(3)->varId(); if (!refId) continue; // replace reference in the code.. for (Token *tok3 = tok2->tokAt(7); tok3 && tok3 != end; tok3 = tok3->next()) { if (tok3->varId() == refId) { tok3->str(tok2->strAt(5)); tok3->varId(tok2->tokAt(5)->varId()); } } tok2->deleteNext(6+(tok2->strAt(6)==")" ? 1 : 0)); } } tok = end; } } } bool Tokenizer::simplifyCalculations() { return mTemplateSimplifier->simplifyCalculations(nullptr, nullptr, false); } void Tokenizer::simplifyOffsetPointerDereference() { // Replace "*(str + num)" => "str[num]" and // Replace "*(str - num)" => "str[-num]" for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName() && !tok->isLiteral() && !Token::Match(tok, "]|)|++|--") && Token::Match(tok->next(), "* ( %name% +|- %num%|%name% )")) { // remove '* (' tok->deleteNext(2); // '+'->'[' tok = tok->tokAt(2); Token* const openBraceTok = tok; const bool isNegativeIndex = (tok->str() == "-"); tok->str("["); // Insert a "-" in front of the number or variable if (isNegativeIndex) { if (tok->next()->isName()) { tok->insertToken("-"); tok = tok->next(); } else tok->next()->str(std::string("-") + tok->next()->str()); } tok = tok->tokAt(2); tok->str("]"); Token::createMutualLinks(openBraceTok, tok); } } } void Tokenizer::simplifyOffsetPointerReference() { std::set pod; for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isStandardType()) { tok = tok->next(); while (tok && (tok->str() == "*" || tok->isName())) { if (tok->varId() > 0) { pod.insert(tok->varId()); break; } tok = tok->next(); } if (!tok) break; } } for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "%num%|%name%|]|)") && (Token::Match(tok->next(), "& %name% [ %num%|%name% ] !!["))) { tok = tok->next(); if (tok->next()->varId()) { if (pod.find(tok->next()->varId()) == pod.end()) { tok = tok->tokAt(5); if (!tok) syntaxError(tok); continue; } } // '&' => '(' tok->str("("); tok = tok->next(); // '[' => '+' tok->deleteNext(); tok->insertToken("+"); tok = tok->tokAt(3); //remove ']' tok->str(")"); Token::createMutualLinks(tok->tokAt(-4), tok); } } } void Tokenizer::simplifyNestedStrcat() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "[;{}] strcat ( strcat (")) { continue; } // find inner strcat call Token *tok2 = tok->tokAt(3); while (Token::simpleMatch(tok2, "strcat ( strcat")) tok2 = tok2->tokAt(2); if (tok2->strAt(3) != ",") continue; // If we have this code: // strcat(strcat(dst, foo), bar); // We move this part of code before all strcat() calls: strcat(dst, foo) // And place "dst" token where the code was. Token *prevTok = tok2->previous(); // Move tokens to new place Token::move(tok2, tok2->next()->link(), tok); tok = tok2->next()->link(); // Insert the "dst" token prevTok->insertToken(tok2->strAt(2)); prevTok->next()->varId(tok2->tokAt(2)->varId()); // Insert semicolon after the moved strcat() tok->insertToken(";"); } } static const std::set stdFunctionsPresentInC = { "strcat", "strcpy", "strncat", "strncpy", "free", "malloc", "strdup" }; void Tokenizer::simplifyStd() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "std") continue; if (Token::Match(tok->previous(), "[(,{};] std :: %name% (") && stdFunctionsPresentInC.find(tok->strAt(2)) != stdFunctionsPresentInC.end()) { tok->deleteNext(); tok->deleteThis(); } } } //--------------------------------------------------------------------------- // Helper functions for handling the tokens list //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const { std::string unknownFunc; const bool ret = mSettings->library.isScopeNoReturn(endScopeToken,&unknownFunc); if (!unknownFunc.empty() && mSettings->summaryReturn.find(unknownFunc) != mSettings->summaryReturn.end()) { return false; } if (unknown) *unknown = !unknownFunc.empty(); if (!unknownFunc.empty() && mSettings->checkLibrary && mSettings->severity.isEnabled(Severity::information)) { // Is function global? bool globalFunction = true; if (Token::simpleMatch(endScopeToken->tokAt(-2), ") ; }")) { const Token * const ftok = endScopeToken->linkAt(-2)->previous(); if (ftok && ftok->isName() && ftok->function() && ftok->function()->nestedIn && ftok->function()->nestedIn->type != Scope::eGlobal) { globalFunction = false; } } // don't warn for nonglobal functions (class methods, functions hidden in namespaces) since they can't be configured yet // FIXME: when methods and namespaces can be configured properly, remove the "globalFunction" check if (globalFunction) { reportError(endScopeToken->previous(), Severity::information, "checkLibraryNoReturn", "--check-library: Function " + unknownFunc + "() should have configuration"); } } return ret; } //--------------------------------------------------------------------------- bool Tokenizer::isFunctionParameterPassedByValue(const Token *fpar) const { // TODO: If symbol database is available, use it. const Token *ftok; // Look at function call, what parameter number is it? int parameter = 1; for (ftok = fpar->previous(); ftok; ftok = ftok->previous()) { if (ftok->str() == "(") break; else if (ftok->str() == ")") ftok = ftok->link(); else if (ftok->str() == ",") ++parameter; else if (Token::Match(ftok, "[;{}]")) break; } // Is this a function call? if (ftok && Token::Match(ftok->tokAt(-2), "[;{}=] %name% (")) { const std::string& functionName(ftok->previous()->str()); if (functionName == "return") return true; // Locate function declaration.. for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); else if (Token::Match(tok, "%type% (") && tok->str() == functionName) { // Goto parameter tok = tok->tokAt(2); int par = 1; while (tok && par < parameter) { if (tok->str() == ")") break; if (tok->str() == ",") ++par; tok = tok->next(); } if (!tok) return false; // If parameter was found, determine if it's passed by value if (par == parameter) { bool knowntype = false; while (tok && tok->isName()) { knowntype |= tok->isStandardType(); knowntype |= (tok->str() == "struct"); tok = tok->next(); } if (!tok || !knowntype) return false; if (tok->str() != "," && tok->str() != ")") return false; return true; } } } } return false; } //--------------------------------------------------------------------------- void Tokenizer::eraseDeadCode(Token *begin, const Token *end) { if (!begin) return; const bool isgoto = Token::Match(begin->tokAt(-2), "goto %name% ;"); int indentlevel = 1; int indentcase = 0; int indentswitch = 0; int indentlabel = 0; int roundbraces = 0; int indentcheck = 0; std::vector switchindents; bool checklabel = false; Token *tok = begin; Token *tokcheck = nullptr; while (tok->next() && tok->next() != end) { if (tok->next()->str() == "(") { ++roundbraces; tok->deleteNext(); continue; } else if (tok->next()->str() == ")") { if (!roundbraces) break; //too many ending round parentheses --roundbraces; tok->deleteNext(); continue; } if (roundbraces) { tok->deleteNext(); continue; } if (Token::Match(tok, "[{};] switch (")) { if (!checklabel) { if (!indentlabel) { //remove 'switch ( ... )' Token::eraseTokens(tok, tok->linkAt(2)->next()); } else { tok = tok->linkAt(2); } if (tok->next()->str() == "{") { ++indentswitch; indentcase = indentlevel + 1; switchindents.push_back(indentcase); } } else { tok = tok->linkAt(2); if (Token::simpleMatch(tok, ") {")) { ++indentswitch; indentcase = indentlevel + 1; switchindents.push_back(indentcase); } } } else if (tok->next()->str() == "{") { ++indentlevel; if (!checklabel) { checklabel = true; tokcheck = tok; indentcheck = indentlevel; indentlabel = 0; } tok = tok->next(); } else if (tok->next()->str() == "}") { --indentlevel; if (!indentlevel) break; if (!checklabel) { tok->deleteNext(); } else { if (indentswitch && indentlevel == indentcase) --indentlevel; if (indentlevel < indentcheck) { const Token *end2 = tok->next(); tok = end2->link()->previous(); //return to initial '{' if (indentswitch && Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->tokAt(-2), "[{};] switch (")) tok = tok->link()->tokAt(-2); //remove also 'switch ( ... )' Token::eraseTokens(tok, end2->next()); checklabel = false; tokcheck = nullptr; indentcheck = 0; } else { tok = tok->next(); } } if (indentswitch && indentlevel <= indentcase) { --indentswitch; switchindents.pop_back(); if (!indentswitch) indentcase = 0; else indentcase = switchindents[indentswitch-1]; } } else if (Token::Match(tok, "[{};:] case")) { const Token *tok2 = Token::findsimplematch(tok->next(), ": ;", end); if (!tok2) { tok->deleteNext(); continue; } if (indentlevel == 1) break; //it seems like the function was called inside a case-default block. if (indentlevel == indentcase) ++indentlevel; tok2 = tok2->next(); if (!checklabel || !indentswitch) { Token::eraseTokens(tok, tok2->next()); } else { tok = const_cast(tok2); } } else if (Token::Match(tok, "[{};] default : ;")) { if (indentlevel == 1) break; //it seems like the function was called inside a case-default block. if (indentlevel == indentcase) ++indentlevel; if (!checklabel || !indentswitch) { tok->deleteNext(3); } else { tok = tok->tokAt(3); } } else if (Token::Match(tok, "[{};] %name% : ;") && tok->next()->str() != "default") { if (checklabel) { indentlabel = indentlevel; tok = tokcheck->next(); checklabel = false; indentlevel = indentcheck; } else { if (indentswitch) { //Before stopping the function, since the 'switch()' //instruction is removed, there's no sense to keep the //case instructions. Remove them, if there are any. Token *tok2 = tok->tokAt(3); int indentlevel2 = indentlevel; while (tok2->next() && tok2->next() != end) { if (Token::Match(tok2->next(), "{|[|(")) { tok2 = tok2->next()->link(); } else if (Token::Match(tok2, "[{};:] case")) { const Token *tok3 = Token::findsimplematch(tok2->next(), ": ;", end); if (!tok3) { tok2 = tok2->next(); continue; } Token::eraseTokens(tok2, tok3->next()); } else if (Token::Match(tok2, "[{};] default : ;")) { tok2->deleteNext(3); } else if (tok2->next()->str() == "}") { --indentlevel2; if (indentlevel2 <= indentcase) break; tok2 = tok2->next(); } else { tok2 = tok2->next(); } } } break; //stop removing tokens, we arrived to the label. } } else if (isgoto && Token::Match(tok, "[{};] do|while|for|BOOST_FOREACH")) { //it's possible that code inside loop is not dead, //because of the possible presence of the label pointed by 'goto' const Token *start = tok->tokAt(2); if (start && start->str() == "(") start = start->link()->next(); if (start && start->str() == "{") { std::string labelpattern = "[{};] " + begin->previous()->str() + " : ;"; bool simplify = true; for (Token *tok2 = start->next(); tok2 != start->link(); tok2 = tok2->next()) { if (Token::Match(tok2, labelpattern.c_str())) { simplify = false; break; } } //bailout for now if (!simplify) break; } tok->deleteNext(); } else { // no need to keep the other strings, remove them. if (tok->strAt(1) == "while") { if (tok->str() == "}" && tok->link()->strAt(-1) == "do") tok->link()->previous()->deleteThis(); } tok->deleteNext(); } } } //--------------------------------------------------------------------------- void Tokenizer::syntaxError(const Token *tok, const std::string &code) const { printDebugOutput(0); throw InternalError(tok, code.empty() ? "syntax error" : "syntax error: " + code, InternalError::SYNTAX); } void Tokenizer::unmatchedToken(const Token *tok) const { printDebugOutput(0); throw InternalError(tok, "Unmatched '" + tok->str() + "'. Configuration: '" + mConfiguration + "'.", InternalError::SYNTAX); } void Tokenizer::syntaxErrorC(const Token *tok, const std::string &what) const { printDebugOutput(0); throw InternalError(tok, "Code '"+what+"' is invalid C code. Use --std or --language to configure the language.", InternalError::SYNTAX); } void Tokenizer::unknownMacroError(const Token *tok1) const { printDebugOutput(0); throw InternalError(tok1, "There is an unknown macro here somewhere. Configuration is required. If " + tok1->str() + " is a macro then please configure it.", InternalError::UNKNOWN_MACRO); } void Tokenizer::unhandled_macro_class_x_y(const Token *tok) const { reportError(tok, Severity::information, "class_X_Y", "The code '" + tok->str() + " " + tok->strAt(1) + " " + tok->strAt(2) + " " + tok->strAt(3) + "' is not handled. You can use -I or --include to add handling of this code."); } void Tokenizer::macroWithSemicolonError(const Token *tok, const std::string ¯oName) const { reportError(tok, Severity::information, "macroWithSemicolon", "Ensure that '" + macroName + "' is defined either using -I, --include or -D."); } void Tokenizer::cppcheckError(const Token *tok) const { printDebugOutput(0); throw InternalError(tok, "Analysis failed. If the code is valid then please report this failure.", InternalError::INTERNAL); } void Tokenizer::unhandledCharLiteral(const Token *tok, const std::string& msg) const { std::string s = tok ? (" " + tok->str()) : ""; for (int i = 0; i < s.size(); ++i) { if ((unsigned char)s[i] >= 0x80) s.clear(); } reportError(tok, Severity::portability, "nonStandardCharLiteral", "Non-standard character literal" + s + ". " + msg); } /** * Helper function to check whether number is equal to integer constant X * or floating point pattern X.0 * @param s the string to check * @param intConstant the integer constant to check against * @param floatConstant the string with stringified float constant to check against * @return true in case s is equal to X or X.0 and false otherwise. */ static bool isNumberOneOf(const std::string &s, const MathLib::bigint& intConstant, const char* floatConstant) { if (MathLib::isInt(s)) { if (MathLib::toLongNumber(s) == intConstant) return true; } else if (MathLib::isFloat(s)) { if (MathLib::toString(MathLib::toDoubleNumber(s)) == floatConstant) return true; } return false; } // ------------------------------------------------------------------------ // Helper function to check whether number is zero (0 or 0.0 or 0E+0) or not? // @param s the string to check // @return true in case s is zero and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isZeroNumber(const std::string &s) { return isNumberOneOf(s, 0L, "0.0"); } // ------------------------------------------------------------------------ // 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 s is one and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isOneNumber(const std::string &s) { if (!MathLib::isPositive(s)) return false; return isNumberOneOf(s, 1L, "1.0"); } // ------------------------------------------------------------------------ // Helper function to check whether number is two (2 or 0.2E+1 or 2E+0) or not? // @param s the string to check // @return true in case s is two and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isTwoNumber(const std::string &s) { if (!MathLib::isPositive(s)) return false; return isNumberOneOf(s, 2L, "2.0"); } // ------------------------------------------------------ // Simplify math functions. // It simplifies the following functions: atol(), fmin(), // fminl(), fminf(), fmax(), fmaxl(), fmaxf(), pow(), // powf(), powl(), cbrt(), cbrtl(), cbtrf(), sqrt(), // sqrtf(), sqrtl(), exp(), expf(), expl(), exp2(), // exp2f(), exp2l(), log2(), log2f(), log2l(), log1p(), // log1pf(), log1pl(), log10(), log10l(), log10f(), // log(), logf(), logl(), logb(), logbf(), logbl(), acosh() // acoshf(), acoshl(), acos(), acosf(), acosl(), cosh() // coshf(), coshf(), cos(), cosf(), cosl(), erfc(), // erfcf(), erfcl(), ilogb(), ilogbf(), ilogbf(), erf(), // erfl(), erff(), asin(), asinf(), asinf(), asinh(), // asinhf(), asinhl(), tan(), tanf(), tanl(), tanh(), // tanhf(), tanhl(), atan(), atanf(), atanl(), atanh(), // atanhf(), atanhl(), expm1(), expm1l(), expm1f(), sin(), // sinf(), sinl(), sinh(), sinhf(), sinhl() // in the tokenlist. // // Reference: // - http://www.cplusplus.com/reference/cmath/ // ------------------------------------------------------ void Tokenizer::simplifyMathFunctions() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isName() && !tok->varId() && tok->strAt(1) == "(") { // precondition for function bool simplifcationMade = false; if (Token::Match(tok, "atol ( %str% )")) { //@todo Add support for atoll() if (Token::simpleMatch(tok->tokAt(-2), "std ::")) { tok = tok->tokAt(-2);// set token index two steps back tok->deleteNext(2); // delete "std ::" } const std::string& strNumber = tok->tokAt(2)->strValue(); // get number const bool isNotAnInteger = (!MathLib::isInt(strNumber));// check: is not an integer if (strNumber.empty() || isNotAnInteger) { // Ignore strings which we can't convert continue; } // Convert string into a number and insert into token list tok->str(MathLib::toString(MathLib::toLongNumber(strNumber))); // remove ( %num% ) tok->deleteNext(3); simplifcationMade = true; } else if (Token::Match(tok, "sqrt|sqrtf|sqrtl|cbrt|cbrtf|cbrtl ( %num% )")) { // Simplify: sqrt(0) = 0 and cbrt(0) == 0 // sqrt(1) = 1 and cbrt(1) == 1 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 0 ? if (isZeroNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("0"); // insert result into token list simplifcationMade = true; } else if (isOneNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("1"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "exp|expf|expl|exp2|exp2f|exp2l|cos|cosf|cosl|cosh|coshf|coshl|erfc|erfcf|erfcl ( %num% )")) { // Simplify: exp[f|l](0) = 1 and exp2[f|l](0) = 1 // cosh[f|l](0) = 1 and cos[f|l](0) = 1 // erfc[f|l](0) = 1 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 0 ? if (isZeroNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("1"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "log1p|log1pf|log1pl|sin|sinf|sinl|sinh|sinhf|sinhl|erf|erff|erfl|asin|asinf|asinl|asinh|asinhf|asinhl|tan|tanf|tanl|tanh|tanhf|tanhl|atan|atanf|atanl|atanh|atanhf|atanhl|expm1|expm1f|expm1l ( %num% )")) { // Simplify: log1p[f|l](0) = 0 and sin[f|l](0) = 0 // sinh[f|l](0) = 0 and erf[f|l](0) = 0 // asin[f|l](0) = 0 and sinh[f|l](0) = 0 // tan[f|l](0) = 0 and tanh[f|l](0) = 0 // atan[f|l](0) = 0 and atanh[f|l](0)= 0 // expm1[f|l](0) = 0 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 0 ? if (isZeroNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("0"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "log2|log2f|log2l|log|logf|logl|log10|log10f|log10l|logb|logbf|logbl|acosh|acoshf|acoshl|acos|acosf|acosl|ilogb|ilogbf|ilogbl ( %num% )")) { // Simplify: log2[f|l](1) = 0 , log10[f|l](1) = 0 // log[f|l](1) = 0 , logb10[f|l](1) = 0 // acosh[f|l](1) = 0 , acos[f|l](1) = 0 // ilogb[f|l](1) = 0 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 1 ? if (isOneNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("0"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "fmin|fminl|fminf ( %num% , %num% )")) { // @todo if one of the parameters is NaN the other is returned // e.g. printf ("fmin (NaN, -1.0) = %f\n", fmin(NaN,-1.0)); // e.g. printf ("fmin (-1.0, NaN) = %f\n", fmin(-1.0,NaN)); const std::string& strLeftNumber(tok->strAt(2)); const std::string& strRightNumber(tok->strAt(4)); const bool isLessEqual = MathLib::isLessEqual(strLeftNumber, strRightNumber); // case: left <= right ==> insert left if (isLessEqual) { tok->str(strLeftNumber); // insert e.g. -1.0 tok->deleteNext(5); // delete e.g. fmin ( -1.0, 1.0 ) simplifcationMade = true; } else { // case left > right ==> insert right tok->str(strRightNumber); // insert e.g. 0.0 tok->deleteNext(5); // delete e.g. fmin ( 1.0, 0.0 ) simplifcationMade = true; } } else if (Token::Match(tok, "fmax|fmaxl|fmaxf ( %num% , %num% )")) { // @todo if one of the parameters is NaN the other is returned // e.g. printf ("fmax (NaN, -1.0) = %f\n", fmax(NaN,-1.0)); // e.g. printf ("fmax (-1.0, NaN) = %f\n", fmax(-1.0,NaN)); const std::string& strLeftNumber(tok->strAt(2)); const std::string& strRightNumber(tok->strAt(4)); const bool isLessEqual = MathLib::isLessEqual(strLeftNumber, strRightNumber); // case: left <= right ==> insert right if (isLessEqual) { tok->str(strRightNumber);// insert e.g. 1.0 tok->deleteNext(5); // delete e.g. fmax ( -1.0, 1.0 ) simplifcationMade = true; } else { // case left > right ==> insert left tok->str(strLeftNumber); // insert e.g. 1.0 tok->deleteNext(5); // delete e.g. fmax ( 1.0, 0.0 ) simplifcationMade = true; } } else if (Token::Match(tok, "pow|powf|powl (")) { if (Token::Match(tok->tokAt(2), "%num% , %num% )")) { // In case of pow ( 0 , anyNumber > 0): It can be simplified to 0 // In case of pow ( 0 , 0 ): It simplified to 1 // In case of pow ( 1 , anyNumber ): It simplified to 1 const std::string& leftNumber(tok->strAt(2)); // get the left parameter const std::string& rightNumber(tok->strAt(4)); // get the right parameter const bool isLeftNumberZero = isZeroNumber(leftNumber); const bool isLeftNumberOne = isOneNumber(leftNumber); const bool isRightNumberZero = isZeroNumber(rightNumber); if (isLeftNumberZero && !isRightNumberZero && MathLib::isPositive(rightNumber)) { // case: 0^(y) = 0 and y > 0 tok->deleteNext(5); // delete tokens tok->str("0"); // insert simplified result simplifcationMade = true; } else if (isLeftNumberZero && isRightNumberZero) { // case: 0^0 = 1 tok->deleteNext(5); // delete tokens tok->str("1"); // insert simplified result simplifcationMade = true; } else if (isLeftNumberOne) { // case 1^(y) = 1 tok->deleteNext(5); // delete tokens tok->str("1"); // insert simplified result simplifcationMade = true; } } if (Token::Match(tok->tokAt(2), "%any% , %num% )")) { // In case of pow( x , 1 ): It can be simplified to x. const std::string& leftParameter(tok->strAt(2)); // get the left parameter const std::string& rightNumber(tok->strAt(4)); // get right number if (isOneNumber(rightNumber)) { // case: x^(1) = x tok->str(leftParameter); // insert simplified result tok->deleteNext(5); // delete tokens simplifcationMade = true; } else if (isZeroNumber(rightNumber)) { // case: x^(0) = 1 tok->deleteNext(5); // delete tokens tok->str("1"); // insert simplified result simplifcationMade = true; } } } // Jump back to begin of statement if a simplification was performed if (simplifcationMade) { while (tok->previous() && tok->str() != ";") { tok = tok->previous(); } } } } } void Tokenizer::simplifyComma() { bool inReturn = false; for (Token *tok = list.front(); tok; tok = tok->next()) { // skip enums if (Token::Match(tok, "enum class|struct| %name%| :|{")) { skipEnumBody(&tok); } if (!tok) syntaxError(nullptr); // invalid code like in #4195 if (Token::Match(tok, "(|[") || Token::Match(tok->previous(), "%name%|= {")) { tok = tok->link(); continue; } if (Token::simpleMatch(tok, "= (") && Token::simpleMatch(tok->linkAt(1), ") {")) { tok = tok->linkAt(1)->linkAt(1); continue; } // Skip unhandled template specifiers.. if (tok->link() && tok->str() == "<") tok = tok->link(); if (tok->str() == "return" && Token::Match(tok->previous(), "[;{}]")) inReturn = true; if (inReturn && Token::Match(tok, "[;{}?:]")) inReturn = false; if (!tok->next() || tok->str() != ",") continue; // We must not accept just any keyword, e.g. accepting int // would cause function parameters to corrupt. if (isCPP() && tok->strAt(1) == "delete") { // Handle "delete a, delete b;" tok->str(";"); } if (isCPP() && Token::Match(tok->tokAt(-2), "delete %name% , %name% ;") && tok->next()->varId() != 0) { // Handle "delete a, b;" - convert to delete a; b; tok->str(";"); } else if (!inReturn && tok->tokAt(-2)) { bool replace = false; for (Token *tok2 = tok->previous(); tok2; tok2 = tok2->previous()) { if (tok2->str() == "=") { // Handle "a = 0, b = 0;" replace = true; } else if (isCPP() && (Token::Match(tok2, "delete %name%") || Token::Match(tok2, "delete [ ] %name%"))) { // Handle "delete a, a = 0;" replace = true; } else if (Token::Match(tok2, "[?:;,{}()]")) { if (replace && Token::Match(tok2, "[;{}]")) tok->str(";"); break; } } } // find token where return ends and also count commas if (inReturn) { Token *startFrom = nullptr; // "[;{}]" token before "return" Token *endAt = nullptr; // first ";" token after "[;{}] return" // find "; return" pattern before comma for (Token *tok2 = tok->previous(); tok2; tok2 = tok2->previous()) { if (tok2->str() == "return") { startFrom = tok2->previous(); break; } } if (!startFrom) // to be very sure... return; int commaCounter = 0; for (Token *tok2 = startFrom->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { endAt = tok2; break; } else if (Token::Match(tok2, "(|[") || (tok2->str() == "{" && tok2->previous() && tok2->previous()->str() == "=")) { tok2 = tok2->link(); } else if (tok2->str() == ",") { ++commaCounter; } } if (!endAt) //probably a syntax error return; if (commaCounter) { // change tokens: // "; return a ( ) , b ( ) , c ;" // to // "; a ( ) ; b ( ) ; return c ;" // remove "return" startFrom->deleteNext(); for (Token *tok2 = startFrom->next(); tok2 != endAt; tok2 = tok2->next()) { if (Token::Match(tok2, "(|[") || (tok2->str() == "{" && tok2->previous() && tok2->previous()->str() == "=")) { tok2 = tok2->link(); } else if (tok2->str() == ",") { tok2->str(";"); --commaCounter; if (commaCounter == 0) { tok2->insertToken("return"); } } } tok = endAt; } } } } void Tokenizer::checkConfiguration() const { if (!mSettings->checkConfiguration) return; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; if (tok->isControlFlowKeyword()) continue; for (const Token *tok2 = tok->tokAt(2); tok2 && tok2->str() != ")"; tok2 = tok2->next()) { if (tok2->str() == ";") { macroWithSemicolonError(tok, tok->str()); break; } if (Token::Match(tok2, "(|{")) tok2 = tok2->link(); } } } void Tokenizer::validateC() const { if (isCPP()) return; for (const Token *tok = tokens(); tok; tok = tok->next()) { // That might trigger false positives, but it's much faster to have this truncated pattern if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) syntaxErrorC(tok, "C++ cast <..."); // Template function.. if (Token::Match(tok, "%name% < %name% > (")) { const Token *tok2 = tok->tokAt(5); while (tok2 && !Token::Match(tok2, "[()]")) tok2 = tok2->next(); if (Token::simpleMatch(tok2, ") {")) syntaxErrorC(tok, tok->str() + '<' + tok->strAt(2) + ">() {}"); } if (tok->previous() && !Token::Match(tok->previous(), "[;{}]")) continue; if (Token::Match(tok, "using namespace %name% ;")) syntaxErrorC(tok, "using namespace " + tok->strAt(2)); if (Token::Match(tok, "template < class|typename %name% [,>]")) syntaxErrorC(tok, "template<..."); if (Token::Match(tok, "%name% :: %name%")) syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); if (Token::Match(tok, "class|namespace %name% [:{]")) syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); } } void Tokenizer::validate() const { std::stack linkTokens; const Token *lastTok = nullptr; for (const Token *tok = tokens(); tok; tok = tok->next()) { lastTok = tok; if (Token::Match(tok, "[{([]") || (tok->str() == "<" && tok->link())) { if (tok->link() == nullptr) cppcheckError(tok); linkTokens.push(tok); } else if (Token::Match(tok, "[})]]") || (Token::Match(tok, ">|>>") && tok->link())) { if (tok->link() == nullptr) cppcheckError(tok); if (linkTokens.empty() == true) cppcheckError(tok); if (tok->link() != linkTokens.top()) cppcheckError(tok); if (tok != tok->link()->link()) cppcheckError(tok); linkTokens.pop(); } else if (tok->link() != nullptr) cppcheckError(tok); } if (!linkTokens.empty()) cppcheckError(linkTokens.top()); // Validate that the Tokenizer::list.back() is updated correctly during simplifications if (lastTok != list.back()) cppcheckError(lastTok); } static const Token *findUnmatchedTernaryOp(const Token * const begin, const Token * const end, int depth = 0) { std::stack ternaryOp; for (const Token *tok = begin; tok != end && tok->str() != ";"; tok = tok->next()) { if (tok->str() == "?") ternaryOp.push(tok); else if (!ternaryOp.empty() && tok->str() == ":") ternaryOp.pop(); else if (depth < 100 && Token::Match(tok,"(|[")) { const Token *inner = findUnmatchedTernaryOp(tok->next(), tok->link(), depth+1); if (inner) return inner; tok = tok->link(); } } return ternaryOp.empty() ? nullptr : ternaryOp.top(); } static bool isCPPAttribute(const Token * tok) { return Token::simpleMatch(tok, "[ [") && tok->link() && tok->link()->previous() == tok->linkAt(1); } static bool isAlignAttribute(const Token * tok) { return Token::simpleMatch(tok, "alignas (") && tok->next()->link(); } static const Token* skipCPPOrAlignAttribute(const Token * tok) { if (isCPPAttribute(tok)) { return tok->link(); } else if (isAlignAttribute(tok)) { return tok->next()->link(); } return tok; } static bool isNonMacro(const Token* tok) { if (tok->isKeyword()) return true; if (cAlternativeTokens.count(tok->str()) > 0) return true; if (tok->str().compare(0, 2, "__") == 0) // attribute/annotation return true; return false; } void Tokenizer::reportUnknownMacros() const { // Report unknown macros used in expressions "%name% %num%" for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% %num%")) { // A keyword is not an unknown macro if (tok->isKeyword()) continue; if (Token::Match(tok->previous(), "%op%|(")) unknownMacroError(tok); } } // Report unknown macros that contain several statements "MACRO(a;b;c)" for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; if (!tok->isUpperCaseName()) continue; const Token *endTok = tok->linkAt(1); for (const Token *inner = tok->tokAt(2); inner != endTok; inner = inner->next()) { if (Token::Match(inner, "[[({]")) inner = inner->link(); else if (inner->str() == ";") unknownMacroError(inner); } } // Report unknown macros that contain struct initialization "MACRO(a, .b=3)" for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; const Token *endTok = tok->linkAt(1); for (const Token *inner = tok->tokAt(2); inner != endTok; inner = inner->next()) { if (Token::Match(inner, "[[({]")) inner = inner->link(); else if (Token::Match(inner->previous(), "[,(] . %name% =|{")) unknownMacroError(tok); } } // Report unknown macros in non-executable scopes.. std::set possible; for (const Token *tok = tokens(); tok; tok = tok->next()) { // Skip executable scopes.. if (tok->str() == "{") { const Token *prev = tok->previous(); while (prev && prev->isName()) prev = prev->previous(); if (prev && prev->str() == ")") tok = tok->link(); else possible.clear(); } else if (tok->str() == "}") possible.clear(); if (Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::simpleMatch(tok->linkAt(1), ") (") && Token::simpleMatch(tok->linkAt(1)->linkAt(1), ") {")) { // A keyword is not an unknown macro if (tok->isKeyword()) continue; const Token *bodyStart = tok->linkAt(1)->linkAt(1)->tokAt(2); const Token *bodyEnd = tok->link(); for (const Token *tok2 = bodyStart; tok2 && tok2 != bodyEnd; tok2 = tok2->next()) { if (Token::Match(tok2, "if|switch|for|while|return")) unknownMacroError(tok); } } else if (Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::Match(tok->linkAt(1), ") %name% (") && Token::Match(tok->linkAt(1)->linkAt(2), ") [;{]")) { if (possible.count(tok->str()) == 0) possible.insert(tok->str()); else unknownMacroError(tok); } } // String concatenation with unknown macros for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%str% %name% (") && Token::Match(tok->linkAt(2), ") %str%")) { if (tok->next()->isKeyword()) continue; unknownMacroError(tok->next()); } if (Token::Match(tok, "[(,] %name% (") && Token::Match(tok->linkAt(2), ") %name% %name%|,|)")) { if (tok->next()->isKeyword() || tok->linkAt(2)->next()->isKeyword()) continue; if (cAlternativeTokens.count(tok->linkAt(2)->next()->str()) > 0) continue; if (tok->next()->str().compare(0, 2, "__") == 0) // attribute/annotation continue; unknownMacroError(tok->next()); } } // Report unknown macros without commas or operators inbetween statements: MACRO1() MACRO2() for (const Token* tok = tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; if (isNonMacro(tok)) continue; const Token* endTok = tok->linkAt(1); if (!Token::Match(endTok, ") %name% (|.")) continue; const Token* tok2 = endTok->next(); if (isNonMacro(tok2)) continue; if (tok2->next()->str() == "(") { if (Token::Match(tok->previous(), "%name%|::|>")) continue; } unknownMacroError(tok); } } void Tokenizer::findGarbageCode() const { const bool isCPP11 = isCPP() && mSettings->standards.cpp >= Standards::CPP11; static const std::unordered_set nonConsecutiveKeywords{ "break", "continue", "for", "goto", "if", "return", "switch", "throw", "typedef", "while" }; for (const Token *tok = tokens(); tok; tok = tok->next()) { // initialization: = { if (Token::simpleMatch(tok, "= {") && Token::simpleMatch(tok->linkAt(1), "} (")) syntaxError(tok->linkAt(1)); // Inside [] there can't be ; or various keywords else if (tok->str() == "[") { for (const Token* inner = tok->next(); inner != tok->link(); inner = inner->next()) { if (Token::Match(inner, "(|[|{")) inner = inner->link(); else if (Token::Match(inner, ";|goto|return|typedef")) syntaxError(inner); } } // array assignment else if (Token::Match(tok, "%assign% [") && Token::simpleMatch(tok->linkAt(1), "] ;")) syntaxError(tok, tok->str() + "[...];"); // UNKNOWN_MACRO(return) if (tok->isKeyword() && Token::Match(tok, "throw|return )") && Token::Match(tok->linkAt(1)->previous(), "%name% (")) unknownMacroError(tok->linkAt(1)->previous()); // UNKNOWN_MACRO(return) else if (Token::Match(tok, "%name% throw|return") && std::isupper(tok->str()[0])) unknownMacroError(tok); // Assign/increment/decrement literal else if (Token::Match(tok, "!!) %num%|%str%|%char% %assign%|++|--")) syntaxError(tok, tok->next()->str() + " " + tok->strAt(2)); if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for|switch")) { // if|while|for|switch (EXPR) { ... } if (tok->previous() && !Token::Match(tok->previous(), "%name%|:|;|{|}|)")) { if (Token::Match(tok->previous(), "[,(]")) { const Token *prev = tok->previous(); while (prev && prev->str() != "(") { if (prev->str() == ")") prev = prev->link(); prev = prev->previous(); } if (prev && Token::Match(prev->previous(), "%name% (")) unknownMacroError(prev->previous()); } if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if")) syntaxError(tok); } if (!Token::Match(tok->next(), "( !!)")) syntaxError(tok); if (tok->str() != "for") { if (isGarbageExpr(tok->next(), tok->linkAt(1), mSettings->standards.cpp>=Standards::cppstd_t::CPP17)) syntaxError(tok); } } // keyword keyword if (tok->isKeyword() && nonConsecutiveKeywords.count(tok->str()) != 0) { if (Token::Match(tok, "%name% %name%") && nonConsecutiveKeywords.count(tok->next()->str()) == 1) syntaxError(tok); const Token* prev = tok; while (prev && prev->isName()) prev = prev->previous(); if (Token::Match(prev, "%op%|%num%|%str%|%char%")) { if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if") && !Token::simpleMatch(tok->tokAt(-2), "extern \"C\"")) syntaxError(tok, prev == tok->previous() ? (prev->str() + " " + tok->str()) : (prev->str() + " .. " + tok->str())); } } } // invalid struct declaration for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "struct|class|enum %name%| {") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { const Token *tok2 = tok->linkAt(tok->next()->isName() ? 2 : 1); if (Token::Match(tok2, "} %op%")) { tok2 = tok2->next(); if (!Token::Match(tok2, "*|&|&&")) syntaxError(tok2, "Unexpected token '" + tok2->str() + "'"); while (Token::Match(tok2, "*|&|&&")) tok2 = tok2->next(); if (!Token::Match(tok2, "%name%")) syntaxError(tok2, "Unexpected token '" + tok2->str() + "'"); } } } // Keywords in global scope static const std::unordered_set nonGlobalKeywords{"break", "continue", "for", "goto", "if", "return", "switch", "while", "try", "catch"}; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); else if (tok->isKeyword() && nonGlobalKeywords.count(tok->str()) && !Token::Match(tok->tokAt(-2), "operator %str%")) syntaxError(tok, "keyword '" + tok->str() + "' is not allowed in global scope"); } // case keyword must be inside switch for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "switch (")) { if (Token::simpleMatch(tok->linkAt(1), ") {")) { tok = tok->linkAt(1)->linkAt(1); continue; } const Token *switchToken = tok; tok = tok->linkAt(1); if (!tok) syntaxError(switchToken); // Look for the end of the switch statement, i.e. the first semi-colon or '}' for (; tok; tok = tok->next()) { if (tok->str() == "{") { tok = tok->link(); } if (Token::Match(tok, ";|}")) { // We're at the end of the switch block if (tok->str() == "}" && tok->strAt(-1) == ":") // Invalid case syntaxError(switchToken); break; } } if (!tok) break; } else if (tok->str() == "(") { tok = tok->link(); } else if (tok->str() == "case") { syntaxError(tok); } } for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "for (")) // find for loops continue; // count number of semicolons int semicolons = 0; const Token* const startTok = tok; tok = tok->next()->link()->previous(); // find ")" of the for-loop // walk backwards until we find the beginning (startTok) of the for() again for (; tok != startTok; tok = tok->previous()) { if (tok->str() == ";") { // do the counting semicolons++; } else if (tok->str() == ")") { // skip pairs of ( ) tok = tok->link(); } } // if we have an invalid number of semicolons inside for( ), assume syntax error if (semicolons > 2) syntaxError(tok); if (semicolons == 1 && !(isCPP() && mSettings->standards.cpp >= Standards::CPP20)) syntaxError(tok); } // Operators without operands.. const Token *templateEndToken = nullptr; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!templateEndToken) { if (tok->str() == "<" && isCPP()) templateEndToken = tok->findClosingBracket(); } else { if (templateEndToken == tok) templateEndToken = nullptr; if (Token::Match(tok, "> %cop%")) continue; } // skip C++ attributes [[...]] if (isCPP11 && (isCPPAttribute(tok) || isAlignAttribute(tok))) { tok = skipCPPOrAlignAttribute(tok); continue; } { bool match1 = Token::Match(tok, "%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|++|--|::|sizeof"); bool match2 = Token::Match(tok->next(), "{|if|else|while|do|for|return|switch|break"); if (isCPP()) { match1 = match1 || Token::Match(tok, "::|throw|decltype|typeof"); match2 = match2 || Token::Match(tok->next(), "try|catch|namespace"); } if (match1 && match2) syntaxError(tok); } if (Token::Match(tok, "%or%|%oror%|~|^|!|%comp%|+|-|/|%")) { std::string code; if (Token::Match(tok->next(), ")|]|}")) code = tok->str() + tok->next()->str(); if (Token::simpleMatch(tok->next(), "( )")) code = tok->str() + "()"; if (!code.empty()) { if (isC() || (tok->str() != ">" && !Token::simpleMatch(tok->previous(), "operator"))) syntaxError(tok, code); } } if (Token::Match(tok, "%num%|%bool%|%char%|%str% %num%|%bool%|%char%|%str%") && !Token::Match(tok, "%str% %str%")) syntaxError(tok); if (Token::Match(tok, "%assign% typename|class %assign%")) syntaxError(tok); if (Token::Match(tok, "%cop%|=|,|[ %or%|%oror%|/|%")) syntaxError(tok); if (Token::Match(tok, ";|(|[ %comp%")) syntaxError(tok); if (Token::Match(tok, "%cop%|= ]") && !(isCPP() && Token::Match(tok->previous(), "[|,|%num% &|=|> ]"))) syntaxError(tok); if (Token::Match(tok, "[+-] [;,)]}]") && !(isCPP() && Token::Match(tok->previous(), "operator [+-] ;"))) syntaxError(tok); if (Token::simpleMatch(tok, ",") && !Token::Match(tok->tokAt(-2), "[ = , &|%name%")) { if (Token::Match(tok->previous(), "(|[|{|<|%assign%|%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|::|sizeof")) syntaxError(tok); if (isCPP() && Token::Match(tok->previous(), "throw|decltype|typeof")) syntaxError(tok); if (Token::Match(tok->next(), ")|]|>|%assign%|%or%|%oror%|==|!=|/|>=|<=|&&")) syntaxError(tok); } if (Token::simpleMatch(tok, ".") && !Token::simpleMatch(tok->previous(), ".") && !Token::simpleMatch(tok->next(), ".") && !Token::Match(tok->previous(), "{|, . %name% =|.|[|{") && !Token::Match(tok->previous(), ", . %name%")) { if (!Token::Match(tok->previous(), "%name%|)|]|>|}")) syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); if (!Token::Match(tok->next(), "%name%|*|~")) syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); } if (Token::Match(tok, "[!|+-/%^~] )|]")) syntaxError(tok); if (Token::Match(tok, "==|!=|<=|>= %comp%") && tok->strAt(-1) != "operator") syntaxError(tok, tok->str() + " " + tok->strAt(1)); } // ternary operator without : if (const Token *ternaryOp = findUnmatchedTernaryOp(tokens(), nullptr)) syntaxError(ternaryOp); // Code must not start with an arithmetical operand if (Token::Match(list.front(), "%cop%")) syntaxError(list.front()); // Code must end with } ; ) NAME if (!Token::Match(list.back(), "%name%|;|}|)")) syntaxError(list.back()); if (list.back()->str() == ")" && !Token::Match(list.back()->link()->previous(), "%name%|> (")) syntaxError(list.back()); for (const Token *end = list.back(); end && end->isName(); end = end->previous()) { if (Token::Match(end, "void|char|short|int|long|float|double|const|volatile|static|inline|struct|class|enum|union|template|sizeof|case|break|continue|typedef")) syntaxError(list.back()); } if ((list.back()->str()==")" || list.back()->str()=="}") && list.back()->previous() && list.back()->previous()->isControlFlowKeyword()) syntaxError(list.back()->previous()); // Garbage templates.. if (isCPP()) { for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "template <")) continue; if (tok->previous() && !Token::Match(tok->previous(), ":|;|{|}|)|>|\"C++\"")) { if (tok->previous()->isUpperCaseName()) unknownMacroError(tok->previous()); else syntaxError(tok); } const Token * const tok1 = tok; tok = tok->next()->findClosingBracket(); if (!tok) syntaxError(tok1); if (!Token::Match(tok, ">|>> ::|...| %name%") && !Token::Match(tok, ">|>> [ [ %name%") && !Token::Match(tok, "> >|*")) syntaxError(tok->next() ? tok->next() : tok1); } } // Objective C/C++ for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] [ %name% %name% ] ;")) syntaxError(tok->next()); } } bool Tokenizer::isGarbageExpr(const Token *start, const Token *end, bool allowSemicolon) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->isControlFlowKeyword()) return true; if (!allowSemicolon && tok->str() == ";") return true; if (tok->str() == "{") tok = tok->link(); } return false; } std::string Tokenizer::simplifyString(const std::string &source) { std::string str = source; for (std::string::size_type i = 0; i + 1U < str.size(); ++i) { if (str[i] != '\\') continue; int c = 'a'; // char int sz = 0; // size of stringdata if (str[i+1] == 'x') { sz = 2; while (sz < 4 && std::isxdigit((unsigned char)str[i+sz])) sz++; if (sz > 2) { std::istringstream istr(str.substr(i+2, sz-2)); istr >> std::hex >> c; } } else if (MathLib::isOctalDigit(str[i+1])) { sz = 2; while (sz < 4 && MathLib::isOctalDigit(str[i+sz])) sz++; std::istringstream istr(str.substr(i+1, sz-1)); istr >> std::oct >> c; str = str.substr(0,i) + (char)c + str.substr(i+sz); continue; } if (sz <= 2) i++; else if (i+sz < str.size()) str.replace(i, sz, std::string(1U, (char)c)); else str.replace(i, str.size() - i - 1U, "a"); } return str; } void Tokenizer::simplifyWhile0() { for (Token *tok = list.front(); tok; tok = tok->next()) { // while (0) const bool while0(Token::Match(tok->previous(), "[{};] while ( 0|false )")); // for (0) - not banal, ticket #3140 const bool for0((Token::Match(tok->previous(), "[{};] for ( %name% = %num% ; %name% < %num% ;") && tok->strAt(2) == tok->strAt(6) && tok->strAt(4) == tok->strAt(8)) || (Token::Match(tok->previous(), "[{};] for ( %type% %name% = %num% ; %name% < %num% ;") && tok->strAt(3) == tok->strAt(7) && tok->strAt(5) == tok->strAt(9))); if (!while0 && !for0) continue; if (while0 && tok->previous()->str() == "}") { // find "do" Token *tok2 = tok->previous()->link(); tok2 = tok2->previous(); if (tok2 && tok2->str() == "do") { const bool flowmatch = Token::findmatch(tok2, "continue|break", tok) != nullptr; // delete "do ({)" tok2->deleteThis(); if (!flowmatch) tok2->deleteThis(); // delete "(}) while ( 0 ) (;)" tok = tok->previous(); tok->deleteNext(4); // while ( 0 ) if (tok->next() && tok->next()->str() == ";") tok->deleteNext(); // ; if (!flowmatch) tok->deleteThis(); // } continue; } } // remove "while (0) { .. }" if (Token::simpleMatch(tok->next()->link(), ") {")) { Token *end = tok->next()->link(), *old_prev = tok->previous(); end = end->next()->link(); if (Token::Match(tok, "for ( %name% =")) old_prev = end->link(); eraseDeadCode(old_prev, end->next()); if (old_prev && old_prev->next()) tok = old_prev->next(); else break; } } } void Tokenizer::simplifyFunctionTryCatch() { if (!isCPP()) return; for (Token * tok = list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "try {")) continue; if (!isFunctionHead(tok->previous(), "try")) continue; // find the end of the last catch block Token * const tryEndToken = tok->linkAt(1); Token * endToken = tryEndToken; while (Token::simpleMatch(endToken, "} catch (")) { endToken = endToken->linkAt(2)->next(); if (!endToken) break; if (endToken->str() != "{") { endToken = nullptr; break; } endToken = endToken->link(); } if (!endToken || endToken == tryEndToken) continue; tok->previous()->insertToken("{"); endToken->insertToken("}"); Token::createMutualLinks(tok->previous(), endToken->next()); } } void Tokenizer::simplifyErrNoInWhile() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "errno") continue; Token *endpar = nullptr; if (Token::Match(tok->previous(), "&& errno == EINTR ) { ;| }")) endpar = tok->tokAt(3); else if (Token::Match(tok->tokAt(-2), "&& ( errno == EINTR ) ) { ;| }")) endpar = tok->tokAt(4); else continue; if (Token::simpleMatch(endpar->link()->previous(), "while (")) { Token *tok1 = tok->previous(); if (tok1->str() == "(") tok1 = tok1->previous(); // erase "&& errno == EINTR" tok1 = tok1->previous(); Token::eraseTokens(tok1, endpar); // tok is invalid.. move to endpar tok = endpar; } } } void Tokenizer::simplifyFuncInWhile() { int count = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "while ( %name% ( %name% ) ) {")) continue; Token *func = tok->tokAt(2); const Token * const var = tok->tokAt(4); Token * const end = tok->next()->link()->next()->link(); const int varid = ++mVarId; // Create new variable const std::string varname("cppcheck:r" + MathLib::toString(++count)); tok->str("int"); tok->next()->insertToken(varname); tok->tokAt(2)->varId(varid); tok->insertToken("while"); tok->insertToken(";"); tok->insertToken(")"); tok->insertToken(var->str()); tok->next()->varId(var->varId()); tok->insertToken("("); tok->insertToken(func->str()); tok->insertToken("="); tok->insertToken(varname); tok->next()->varId(varid); Token::createMutualLinks(tok->tokAt(4), tok->tokAt(6)); end->previous()->insertToken(varname); end->previous()->varId(varid); end->previous()->insertToken("="); Token::move(func, func->tokAt(3), end->previous()); end->previous()->insertToken(";"); tok = end; } } void Tokenizer::simplifyStructDecl() { const bool cpp = isCPP(); // A counter that is used when giving unique names for anonymous structs. int count = 0; // Skip simplification of unions in class definition std::stack skip; // true = in function, false = not in function skip.push(false); // Add names for anonymous structs for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName()) continue; // check for anonymous struct/union if (Token::Match(tok, "struct|union {")) { if (Token::Match(tok->next()->link(), "} const| *|&| const| %type% ,|;|[|(|{|=")) { tok->insertToken("Anonymous" + MathLib::toString(count++)); } } // check for derived anonymous class/struct else if (cpp && Token::Match(tok, "class|struct :")) { const Token *tok1 = Token::findsimplematch(tok, "{"); if (tok1 && Token::Match(tok1->link(), "} const| *|&| const| %type% ,|;|[|(|{")) { tok->insertToken("Anonymous" + MathLib::toString(count++)); } } // check for anonymous enum else if ((Token::simpleMatch(tok, "enum {") && !Token::Match(tok->tokAt(-3), "using %name% =") && Token::Match(tok->next()->link(), "} (| %type%| )| ,|;|[|(|{")) || (Token::Match(tok, "enum : %type% {") && Token::Match(tok->linkAt(3), "} (| %type%| )| ,|;|[|(|{"))) { Token *start = tok->strAt(1) == ":" ? tok->linkAt(3) : tok->linkAt(1); if (start && Token::Match(start->next(), "( %type% )")) { start->next()->link()->deleteThis(); start->next()->deleteThis(); } tok->insertToken("Anonymous" + MathLib::toString(count++)); } } for (Token *tok = list.front(); tok; tok = tok->next()) { // check for start of scope and determine if it is in a function if (tok->str() == "{") skip.push(Token::Match(tok->previous(), "const|)")); // end of scope else if (tok->str() == "}" && !skip.empty()) skip.pop(); // check for named struct/union else if (Token::Match(tok, "class|struct|union|enum %type% :|{")) { Token *start = tok; while (Token::Match(start->previous(), "%type%")) start = start->previous(); const Token * const type = tok->next(); Token *next = tok->tokAt(2); while (next && next->str() != "{") next = next->next(); if (!next) continue; skip.push(false); tok = next->link(); if (!tok) break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) Token *restart = next; // check for named type if (Token::Match(tok->next(), "const| *|&| const| (| %type% )| ,|;|[|=|(|{")) { tok->insertToken(";"); tok = tok->next(); while (!Token::Match(start, "struct|class|union|enum")) { tok->insertToken(start->str()); tok = tok->next(); start->deleteThis(); } if (!tok) break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) tok->insertToken(type->str()); if (start->str() != "class") { tok->insertToken(start->str()); tok = tok->next(); } tok = tok->tokAt(2); if (Token::Match(tok, "( %type% )")) { tok->link()->deleteThis(); tok->deleteThis(); } // check for initialization if (tok && (tok->next()->str() == "(" || tok->next()->str() == "{")) { tok->insertToken("="); tok = tok->next(); if (start->str() == "enum") { if (tok->next()->str() == "{") { tok->next()->str("("); tok->linkAt(1)->str(")"); } } } } tok = restart; } // check for anonymous struct/union else if (Token::Match(tok, "struct|union {")) { const bool inFunction = skip.top(); skip.push(false); Token *tok1 = tok; Token *restart = tok->next(); tok = tok->next()->link(); // unnamed anonymous struct/union so possibly remove it if (tok && tok->next() && tok->next()->str() == ";") { if (inFunction && tok1->str() == "union") { // Try to create references in the union.. Token *tok2 = tok1->tokAt(2); while (tok2) { if (Token::Match(tok2, "%type% %name% ;")) tok2 = tok2->tokAt(3); else break; } if (!Token::simpleMatch(tok2, "} ;")) continue; Token *vartok = nullptr; tok2 = tok1->tokAt(2); while (Token::Match(tok2, "%type% %name% ;")) { if (!vartok) { vartok = tok2->next(); tok2 = tok2->tokAt(3); } else { tok2->insertToken("&"); tok2 = tok2->tokAt(2); tok2->insertToken(vartok->str()); tok2->next()->varId(vartok->varId()); tok2->insertToken("="); tok2 = tok2->tokAt(4); } } } // don't remove unnamed anonymous unions from a class, struct or union if (!(!inFunction && tok1->str() == "union") && !Token::Match(tok1->tokAt(-3), "using %name% =")) { skip.pop(); tok1->deleteThis(); if (tok1->next() == tok) { tok1->deleteThis(); tok = tok1; } else tok1->deleteThis(); restart = tok1->previous(); tok->deleteThis(); if (tok->next()) tok->deleteThis(); } } if (!restart) { simplifyStructDecl(); return; } else if (!restart->next()) return; tok = restart; } } } void Tokenizer::simplifyCallingConvention() { const bool windows = mSettings->isWindowsPlatform(); for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "__cdecl|__stdcall|__fastcall|__thiscall|__clrcall|__syscall|__pascal|__fortran|__far|__near") || (windows && Token::Match(tok, "WINAPI|APIENTRY|CALLBACK"))) { tok->deleteThis(); } } } void Tokenizer::simplifyDeclspec() { for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "__declspec|_declspec (") && tok->next()->link() && tok->next()->link()->next()) { if (Token::Match(tok->tokAt(2), "noreturn|nothrow")) { Token *tok1 = tok->next()->link()->next(); while (tok1 && !Token::Match(tok1, "%name%")) { tok1 = tok1->next(); } if (tok1) { if (tok->strAt(2) == "noreturn") tok1->isAttributeNoreturn(true); else tok1->isAttributeNothrow(true); } } else if (tok->strAt(2) == "property") tok->next()->link()->insertToken("__property"); Token::eraseTokens(tok, tok->next()->link()->next()); tok->deleteThis(); } } } void Tokenizer::simplifyAttribute() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%type% (") && !mSettings->library.isNotLibraryFunction(tok)) { if (mSettings->library.isFunctionConst(tok->str(), true)) tok->isAttributePure(true); if (mSettings->library.isFunctionConst(tok->str(), false)) tok->isAttributeConst(true); } while (Token::Match(tok, "__attribute__|__attribute (")) { Token *after = tok; while (Token::Match(after, "__attribute__|__attribute (")) after = after->linkAt(1)->next(); if (!after) syntaxError(tok); Token *functok = nullptr; if (Token::Match(after, "%name%|*")) { Token *ftok = after; while (Token::Match(ftok, "%name%|::|<|* !!(")) { if (ftok->str() == "<") { ftok = ftok->findClosingBracket(); if (!ftok) break; } ftok = ftok->next(); } if (Token::Match(ftok, "%name% (")) functok = ftok; } else if (Token::Match(after, "[;{=:]")) { Token *prev = tok->previous(); while (Token::Match(prev, "%name%")) prev = prev->previous(); if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->previous(), "%name% (")) functok = prev->link()->previous(); else if ((!prev || Token::Match(prev, "[;{}*]")) && Token::Match(tok->previous(), "%name%")) functok = tok->previous(); } for (Token *attr = tok->tokAt(2); attr->str() != ")"; attr = attr->next()) { if (Token::Match(attr, "%name% (")) attr = attr->linkAt(1); if (Token::Match(attr, "[(,] constructor|__constructor__ [,()]")) { if (!functok) syntaxError(tok); functok->isAttributeConstructor(true); } else if (Token::Match(attr, "[(,] destructor|__destructor__ [,()]")) { if (!functok) syntaxError(tok); functok->isAttributeDestructor(true); } else if (Token::Match(attr, "[(,] unused|__unused__|used|__used__ [,)]")) { Token *vartok = nullptr; // check if after variable name if (Token::Match(after, ";|=")) { if (Token::Match(tok->previous(), "%type%")) vartok = tok->previous(); } // check if before variable name else if (Token::Match(after, "%type%")) vartok = after; if (vartok) { const std::string &attribute(attr->next()->str()); if (attribute.find("unused") != std::string::npos) vartok->isAttributeUnused(true); else vartok->isAttributeUsed(true); } } else if (Token::Match(attr, "[(,] pure|__pure__|const|__const__|noreturn|__noreturn__|nothrow|__nothrow__|warn_unused_result [,)]")) { if (!functok) syntaxError(tok); const std::string &attribute(attr->next()->str()); if (attribute.find("pure") != std::string::npos) functok->isAttributePure(true); else if (attribute.find("const") != std::string::npos) functok->isAttributeConst(true); else if (attribute.find("noreturn") != std::string::npos) functok->isAttributeNoreturn(true); else if (attribute.find("nothrow") != std::string::npos) functok->isAttributeNothrow(true); else if (attribute.find("warn_unused_result") != std::string::npos) functok->isAttributeNodiscard(true); } else if (Token::Match(attr, "[(,] packed [,)]") && Token::simpleMatch(tok->previous(), "}")) tok->previous()->isAttributePacked(true); } Token::eraseTokens(tok, tok->linkAt(1)->next()); tok->deleteThis(); } } } void Tokenizer::simplifyCppcheckAttribute() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "(") continue; if (!tok->previous()) continue; const std::string &attr = tok->previous()->str(); if (attr.compare(0, 11, "__cppcheck_") != 0) // TODO: starts_with("__cppcheck_") continue; if (attr.compare(attr.size()-2, 2, "__") != 0) // TODO: ends_with("__") continue; Token *vartok = tok->link(); while (Token::Match(vartok->next(), "%name%|*|&|::")) { vartok = vartok->next(); if (Token::Match(vartok, "%name% (") && vartok->str().compare(0,11,"__cppcheck_") == 0) vartok = vartok->linkAt(1); } if (vartok->isName()) { if (Token::Match(tok->previous(), "__cppcheck_low__ ( %num% )")) vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(tok->next()->str())); else if (Token::Match(tok->previous(), "__cppcheck_high__ ( %num% )")) vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(tok->next()->str())); } // Delete cppcheck attribute.. if (tok->tokAt(-2)) { tok = tok->tokAt(-2); Token::eraseTokens(tok, tok->linkAt(2)->next()); } else { tok = tok->previous(); Token::eraseTokens(tok, tok->linkAt(1)->next()); tok->str(";"); } } } void Tokenizer::simplifyCPPAttribute() { if (mSettings->standards.cpp < Standards::CPP11 || isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!isCPPAttribute(tok) && !isAlignAttribute(tok)) { continue; } if (isCPPAttribute(tok)) { if (Token::findsimplematch(tok->tokAt(2), "noreturn", tok->link())) { const Token * head = skipCPPOrAlignAttribute(tok); while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head); head = head->next(); while (Token::Match(head, "%name%|::|*|&|<|>|,")) // skip return type head = head->next(); if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { head->previous()->isAttributeNoreturn(true); } } else if (Token::findsimplematch(tok->tokAt(2), "nodiscard", tok->link())) { const Token * head = skipCPPOrAlignAttribute(tok); while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head); head = head->next(); while (Token::Match(head, "%name%|::|*|&|<|>|,")) head = head->next(); if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { head->previous()->isAttributeNodiscard(true); } } else if (Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link())) { const Token* head = skipCPPOrAlignAttribute(tok); while (isCPPAttribute(head) || isAlignAttribute(head)) head = skipCPPOrAlignAttribute(head); head->next()->isAttributeMaybeUnused(true); } else if (Token::Match(tok->previous(), ") [ [ expects|ensures|assert default|audit|axiom| : %name% <|<=|>|>= %num% ] ]")) { const Token *vartok = tok->tokAt(4); if (vartok->str() == ":") vartok = vartok->next(); Token *argtok = tok->tokAt(-2); while (argtok && argtok->str() != "(") { if (argtok->str() == vartok->str()) break; if (argtok->str() == ")") argtok = argtok->link(); argtok = argtok->previous(); } if (argtok && argtok->str() == vartok->str()) { if (vartok->next()->str() == ">=") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))); else if (vartok->next()->str() == ">") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))+1); else if (vartok->next()->str() == "<=") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))); else if (vartok->next()->str() == "<") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))-1); } } } else { if (Token::simpleMatch(tok, "alignas (")) { // alignment requirements could be checked here } } Token::eraseTokens(tok, skipCPPOrAlignAttribute(tok)->next()); // fix iterator after removing if (tok->previous()) { tok = tok->previous(); tok->next()->deleteThis(); } else { tok->deleteThis(); tok = list.front(); } } } void Tokenizer::removeAlignas() { if (!isCPP() || mSettings->standards.cpp < Standards::CPP11) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] alignas (") && Token::Match(tok->linkAt(2), ") %name%")) Token::eraseTokens(tok, tok->linkAt(2)->next()); } } void Tokenizer::simplifySpaceshipOperator() { if (isCPP() && mSettings->standards.cpp >= Standards::CPP20) { for (Token *tok = list.front(); tok && tok->next(); tok = tok->next()) { if (Token::simpleMatch(tok, "<= >")) { tok->str("<=>"); tok->deleteNext(); } } } } static const std::unordered_set keywords = { "inline" , "_inline" , "__inline" , "__forceinline" , "register" , "__restrict" , "__restrict__" , "__thread" }; // Remove "inline", "register", "restrict", "override", "static" and "constexpr" // "restrict" keyword // - New to 1999 ANSI/ISO C standard // - Not in C++ standard yet void Tokenizer::simplifyKeyword() { // FIXME: There is a risk that "keywords" are removed by mistake. This // code should be fixed so it doesn't remove variables etc. Nonstandard // keywords should be defined with a library instead. For instance the // linux kernel code at least uses "_inline" as struct member name at some // places. const bool c99 = isC() && mSettings->standards.c >= Standards::C99; const bool cpp11 = isCPP() && mSettings->standards.cpp >= Standards::CPP11; for (Token *tok = list.front(); tok; tok = tok->next()) { if (keywords.find(tok->str()) != keywords.end()) { // Don't remove struct members if (!Token::simpleMatch(tok->previous(), ".")) { if (tok->str().find("inline") != std::string::npos && Token::Match(tok->next(), "%name%")) tok->next()->isInline(true); tok->deleteThis(); // Simplify.. } } if (isC() || mSettings->standards.cpp == Standards::CPP03) { if (tok->str() == "auto") tok->deleteThis(); } // simplify static keyword: // void foo( int [ static 5 ] ); ==> void foo( int [ 5 ] ); if (Token::Match(tok, "[ static %num%")) tok->deleteNext(); if (c99) { while (tok->str() == "restrict") tok->deleteThis(); if (mSettings->standards.c >= Standards::C11) { while (tok->str() == "_Atomic") tok->deleteThis(); } } else if (cpp11) { // final: // 1) struct name final { }; <- struct is final if (Token::Match(tok->previous(), "struct|class|union %type% final [:{]")) { tok->deleteNext(); } // noexcept -> noexcept(true) // 2) void f() noexcept; -> void f() noexcept(true); else if (Token::Match(tok, ") noexcept :|{|;|const|override|final")) { // Insertion is done in inverse order // The brackets are linked together accordingly afterwards Token * tokNoExcept = tok->next(); tokNoExcept->insertToken(")"); Token * braceEnd = tokNoExcept->next(); tokNoExcept->insertToken("true"); tokNoExcept->insertToken("("); Token * braceStart = tokNoExcept->next(); tok = tok->tokAt(3); Token::createMutualLinks(braceStart, braceEnd); } // 3) thread_local -> static // on single thread thread_local has the effect of static else if (tok->str() == "thread_local") { tok->originalName(tok->str()); tok->str("static"); } } } } void Tokenizer::simplifyAssignmentInFunctionCall() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(") tok = tok->link(); // Find 'foo(var='. Exclude 'assert(var=' to allow tests to check that assert(...) does not contain side-effects else if (Token::Match(tok, "[;{}] %name% ( %name% =") && Token::simpleMatch(tok->linkAt(2), ") ;") && !Token::Match(tok->next(), "assert|while")) { const std::string& funcname(tok->next()->str()); Token* const vartok = tok->tokAt(3); // Goto ',' or ')'.. for (Token *tok2 = vartok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "(|[|{")) tok2 = tok2->link(); else if (tok2->str() == ";") break; else if (Token::Match(tok2, ")|,")) { tok2 = tok2->previous(); tok2->insertToken(vartok->str()); tok2->next()->varId(vartok->varId()); tok2->insertToken("("); Token::createMutualLinks(tok2->next(), tok->linkAt(2)); tok2->insertToken(funcname); tok2->insertToken(";"); Token::eraseTokens(tok, vartok); break; } } } } } void Tokenizer::simplifyAssignmentBlock() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %name% = ( {")) { const std::string &varname = tok->next()->str(); // goto the "} )" int indentlevel = 0; Token *tok2 = tok; while (nullptr != (tok2 = tok2->next())) { if (Token::Match(tok2, "(|{")) ++indentlevel; else if (Token::Match(tok2, ")|}")) { if (indentlevel <= 2) break; --indentlevel; } else if (indentlevel == 2 && tok2->str() == varname && Token::Match(tok2->previous(), "%type%|*")) // declaring variable in inner scope with same name as lhs variable break; } if (indentlevel == 2 && Token::simpleMatch(tok2, "} )")) { tok2 = tok2->tokAt(-3); if (Token::Match(tok2, "[;{}] %num%|%name% ;")) { tok2->insertToken("="); tok2->insertToken(tok->next()->str()); tok2->next()->varId(tok->next()->varId()); tok->deleteNext(3); tok2->tokAt(5)->deleteNext(); } } } } } // Remove __asm.. void Tokenizer::simplifyAsm() { std::string instruction; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "__asm|_asm|asm {") && tok->next()->link()->next()) { instruction = tok->tokAt(2)->stringifyList(tok->next()->link()); Token::eraseTokens(tok, tok->next()->link()->next()); } else if (Token::Match(tok, "asm|__asm|__asm__ volatile|__volatile|__volatile__| (")) { // Goto "(" Token *partok = tok->next(); if (partok->str() != "(") partok = partok->next(); instruction = partok->next()->stringifyList(partok->link()); Token::eraseTokens(tok, partok->link()->next()); } else if (Token::Match(tok, "_asm|__asm")) { Token *endasm = tok->next(); const Token *firstSemiColon = nullptr; int comment = 0; while (Token::Match(endasm, "%num%|%name%|,|:|;") || (endasm && endasm->linenr() == comment)) { if (Token::Match(endasm, "_asm|__asm|__endasm")) break; if (endasm->str() == ";") { comment = endasm->linenr(); if (!firstSemiColon) firstSemiColon = endasm; } endasm = endasm->next(); } if (Token::simpleMatch(endasm, "__endasm")) { instruction = tok->next()->stringifyList(endasm); Token::eraseTokens(tok, endasm->next()); if (!Token::simpleMatch(tok->next(), ";")) tok->insertToken(";"); } else if (firstSemiColon) { instruction = tok->next()->stringifyList(firstSemiColon); Token::eraseTokens(tok, firstSemiColon); } else if (!endasm) { instruction = tok->next()->stringifyList(endasm); Token::eraseTokens(tok, endasm); tok->insertToken(";"); } else continue; } else continue; // insert "asm ( "instruction" )" tok->str("asm"); if (tok->strAt(1) != ";" && tok->strAt(1) != "{") tok->insertToken(";"); tok->insertToken(")"); tok->insertToken("\"" + instruction + "\""); tok->insertToken("("); tok = tok->next(); Token::createMutualLinks(tok, tok->tokAt(2)); //move the new tokens in the same line as ";" if available tok = tok->tokAt(2); if (tok->next() && tok->next()->str() == ";" && tok->next()->linenr() != tok->linenr()) { const int endposition = tok->next()->linenr(); tok = tok->tokAt(-3); for (int i = 0; i < 4; ++i) { tok = tok->next(); tok->linenr(endposition); } } } } void Tokenizer::simplifyAsm2() { // Block declarations: ^{} // A C extension used to create lambda like closures. // Put ^{} statements in asm() for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "^") continue; if (Token::simpleMatch(tok, "^ {") || (Token::simpleMatch(tok->linkAt(1), ") {") && tok->strAt(-1) != "operator")) { Token * start = tok; while (start && !Token::Match(start, "[,(;{}=]")) { if (start->link() && Token::Match(start, ")|]|>")) start = start->link(); start = start->previous(); } const Token *last = tok->next()->link(); if (Token::simpleMatch(last, ") {")) last = last->linkAt(1); last = last->next(); while (last && !Token::Match(last, "%cop%|,|;|{|}|)")) { if (Token::Match(last, "(|[")) last = last->link(); last = last->next(); } if (start && last) { std::string asmcode; while (start->next() != last) { asmcode += start->next()->str(); start->deleteNext(); } if (last->str() == "}") start->insertToken(";"); start->insertToken(")"); start->insertToken("\"" + asmcode + "\""); start->insertToken("("); start->insertToken("asm"); start->tokAt(2)->link(start->tokAt(4)); start->tokAt(4)->link(start->tokAt(2)); tok = start->tokAt(4); } } } } void Tokenizer::simplifyAt() { std::set var; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%name%|] @ %num%|%name%|(")) { const Token *end = tok->tokAt(2); if (end->isNumber()) end = end->next(); else if (end->str() == "(") { int par = 0; while ((end = end->next()) != nullptr) { if (end->str() == "(") par++; else if (end->str() == ")") { if (--par < 0) break; } } end = end ? end->next() : nullptr; } else if (var.find(end->str()) != var.end()) end = end->next(); else continue; if (Token::Match(end, ": %num% ;")) end = end->tokAt(2); if (end && end->str() == ";") { if (tok->isName()) var.insert(tok->str()); tok->isAtAddress(true); Token::eraseTokens(tok, end); } } // keywords in compiler from cosmic software for STM8 // TODO: Should use platform configuration. if (Token::Match(tok, "@ builtin|eeprom|far|inline|interrupt|near|noprd|nostack|nosvf|packed|stack|svlreg|tiny|vector")) { tok->str(tok->next()->str() + "@"); tok->deleteNext(); } } } // Simplify bitfields void Tokenizer::simplifyBitfields() { bool goback = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (goback) { goback = false; tok = tok->previous(); } Token *last = nullptr; if (Token::simpleMatch(tok, "for (")) tok = tok->linkAt(1); 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(); } } if (Token::Match(tok->next(), "const| %type% %name% :") && !Token::Match(tok->next(), "case|public|protected|private|class|struct") && !Token::simpleMatch(tok->tokAt(2), "default :")) { Token *tok1 = (tok->next()->str() == "const") ? tok->tokAt(3) : tok->tokAt(2); if (Token::Match(tok1, "%name% : %num% ;")) tok1->setBits(MathLib::toLongNumber(tok1->strAt(2))); if (tok1 && tok1->tokAt(2) && (Token::Match(tok1->tokAt(2), "%bool%|%num%") || !Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) { while (tok1->next() && !Token::Match(tok1->next(), "[;,)]{}]")) { if (Token::Match(tok1->next(), "[([]")) Token::eraseTokens(tok1, tok1->next()->link()); tok1->deleteNext(); } last = tok1->next(); } } else if (isEnum && Token::Match(tok, "} %name%| : %num% ;")) { if (tok->next()->str() == ":") { tok->deleteNext(2); tok->insertToken("Anonymous"); } else { tok->next()->deleteNext(2); } } else if (Token::Match(tok->next(), "const| %type% : %num%|%bool% ;") && tok->next()->str() != "default") { const int offset = (tok->next()->str() == "const") ? 1 : 0; if (!Token::Match(tok->tokAt(3 + offset), "[{};()]")) { tok->deleteNext(4 + offset); goback = true; } } if (last && last->str() == ",") { Token * tok1 = last; tok1->str(";"); const Token *const tok2 = tok->next(); tok1->insertToken(tok2->str()); tok1 = tok1->next(); tok1->isSigned(tok2->isSigned()); tok1->isUnsigned(tok2->isUnsigned()); tok1->isLong(tok2->isLong()); } } } // Types and objects in std namespace that are neither functions nor templates static const std::set stdTypes = { "string", "wstring", "u16string", "u32string", "iostream", "ostream", "ofstream", "ostringstream", "istream", "ifstream", "istringstream", "fstream", "stringstream", "wstringstream", "wistringstream", "wostringstream", "wstringbuf", "stringbuf", "streambuf", "ios", "filebuf", "ios_base", "exception", "bad_exception", "bad_alloc", "logic_error", "domain_error", "invalid_argument_", "length_error", "out_of_range", "runtime_error", "range_error", "overflow_error", "underflow_error", "locale", "cout", "cerr", "clog", "cin", "wcerr", "wcin", "wclog", "wcout", "endl", "ends", "flush", "boolalpha", "noboolalpha", "showbase", "noshowbase", "showpoint", "noshowpoint", "showpos", "noshowpos", "skipws", "noskipws", "unitbuf", "nounitbuf", "uppercase", "nouppercase", "dec", "hex", "oct", "fixed", "scientific", "internal", "left", "right", "fpos", "streamoff", "streampos", "streamsize" }; static const std::set stdTemplates = { "array", "basic_string", "bitset", "deque", "list", "map", "multimap", "priority_queue", "queue", "set", "multiset", "stack", "vector", "pair", "iterator", "iterator_traits", "unordered_map", "unordered_multimap", "unordered_set", "unordered_multiset", "tuple", "function" }; static const std::set stdFunctions = { "getline", "for_each", "find", "find_if", "find_end", "find_first_of", "adjacent_find", "count", "count_if", "mismatch", "equal", "search", "search_n", "copy", "copy_backward", "swap", "swap_ranges", "iter_swap", "transform", "replace", "replace_if", "replace_copy", "replace_copy_if", "fill", "fill_n", "generate", "generate_n", "remove", "remove_if", "remove_copy", "remove_copy_if", "unique", "unique_copy", "reverse", "reverse_copy", "rotate", "rotate_copy", "random_shuffle", "partition", "stable_partition", "sort", "stable_sort", "partial_sort", "partial_sort_copy", "nth_element", "lower_bound", "upper_bound", "equal_range", "binary_search", "merge", "inplace_merge", "includes", "set_union", "set_intersection", "set_difference", "set_symmetric_difference", "push_heap", "pop_heap", "make_heap", "sort_heap", "min", "max", "min_element", "max_element", "lexicographical_compare", "next_permutation", "prev_permutation", "advance", "back_inserter", "distance", "front_inserter", "inserter", "make_pair", "make_shared", "make_tuple" }; // Add std:: in front of std classes, when using namespace std; was given void Tokenizer::simplifyNamespaceStd() { if (!isCPP()) return; const bool isCPP11 = mSettings->standards.cpp == Standards::CPP11; std::set userFunctions; for (const Token* tok = Token::findsimplematch(list.front(), "using namespace std ;"); tok; tok = tok->next()) { bool insert = false; if (Token::Match(tok, "enum class|struct| %name%| :|{")) { // Don't replace within enum definitions skipEnumBody(&tok); } if (!Token::Match(tok->previous(), ".|::")) { if (Token::Match(tok, "%name% (")) { if (isFunctionHead(tok->next(), "{")) userFunctions.insert(tok->str()); else if (isFunctionHead(tok->next(), ";")) { const Token *start = tok; while (Token::Match(start->previous(), "%type%|*|&")) start = start->previous(); if (start != tok && start->isName() && (!start->previous() || Token::Match(start->previous(), "[;{}]"))) userFunctions.insert(tok->str()); } if (userFunctions.find(tok->str()) == userFunctions.end() && stdFunctions.find(tok->str()) != stdFunctions.end()) insert = true; } else if (Token::Match(tok, "%name% <") && stdTemplates.find(tok->str()) != stdTemplates.end()) insert = true; else if (tok->isName() && !tok->varId() && !Token::Match(tok->next(), "(|<") && stdTypes.find(tok->str()) != stdTypes.end()) insert = true; } if (insert) { tok->previous()->insertToken("std"); tok->previous()->linenr(tok->linenr()); // For stylistic reasons we put the std:: in the same line as the following token tok->previous()->fileIndex(tok->fileIndex()); tok->previous()->insertToken("::"); } else if (isCPP11 && Token::Match(tok, "!!:: tr1 ::")) tok->next()->str("std"); } for (Token* tok = list.front(); tok; tok = tok->next()) { if (isCPP11 && Token::simpleMatch(tok, "std :: tr1 ::")) Token::eraseTokens(tok, tok->tokAt(3)); else if (Token::simpleMatch(tok, "using namespace std ;")) { Token::eraseTokens(tok, tok->tokAt(4)); tok->deleteThis(); } } } void Tokenizer::simplifyMicrosoftMemoryFunctions() { // skip if not Windows if (!mSettings->isWindowsPlatform()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->strAt(1) != "(") continue; if (Token::Match(tok, "CopyMemory|RtlCopyMemory|RtlCopyBytes")) { tok->str("memcpy"); } else if (Token::Match(tok, "MoveMemory|RtlMoveMemory")) { tok->str("memmove"); } else if (Token::Match(tok, "FillMemory|RtlFillMemory|RtlFillBytes")) { // FillMemory(dst, len, val) -> memset(dst, val, len) tok->str("memset"); Token *tok1 = tok->tokAt(2); if (tok1) tok1 = tok1->nextArgument(); // Second argument if (tok1) { Token *tok2 = tok1->nextArgument(); // Third argument if (tok2) Token::move(tok1->previous(), tok2->tokAt(-2), tok->next()->link()->previous()); // Swap third with second argument } } else if (Token::Match(tok, "ZeroMemory|RtlZeroMemory|RtlZeroBytes|RtlSecureZeroMemory")) { // ZeroMemory(dst, len) -> memset(dst, 0, len) tok->str("memset"); Token *tok1 = tok->tokAt(2); if (tok1) tok1 = tok1->nextArgument(); // Second argument if (tok1) { tok1 = tok1->previous(); tok1->insertToken("0"); tok1 = tok1->next(); tok1->insertToken(","); } } else if (Token::simpleMatch(tok, "RtlCompareMemory")) { // RtlCompareMemory(src1, src2, len) -> memcmp(src1, src2, len) tok->str("memcmp"); // For the record, when memcmp returns 0, both strings are equal. // When RtlCompareMemory returns len, both strings are equal. // It might be needed to improve this replacement by something // like ((len - memcmp(src1, src2, len)) % (len + 1)) to // respect execution path (if required) } } } namespace { struct triplet { triplet(const char* m, const char* u) : mbcs(m), unicode(u) {} std::string mbcs, unicode; }; const std::map apis = { std::make_pair("_topen", triplet("open", "_wopen")), std::make_pair("_tsopen_s", triplet("_sopen_s", "_wsopen_s")), std::make_pair("_tfopen", triplet("fopen", "_wfopen")), std::make_pair("_tfopen_s", triplet("fopen_s", "_wfopen_s")), std::make_pair("_tfreopen", triplet("freopen", "_wfreopen")), std::make_pair("_tfreopen_s", triplet("freopen_s", "_wfreopen_s")), std::make_pair("_tcscat", triplet("strcat", "wcscat")), std::make_pair("_tcschr", triplet("strchr", "wcschr")), std::make_pair("_tcscmp", triplet("strcmp", "wcscmp")), std::make_pair("_tcsdup", triplet("strdup", "wcsdup")), std::make_pair("_tcscpy", triplet("strcpy", "wcscpy")), std::make_pair("_tcslen", triplet("strlen", "wcslen")), std::make_pair("_tcsncat", triplet("strncat", "wcsncat")), std::make_pair("_tcsncpy", triplet("strncpy", "wcsncpy")), std::make_pair("_tcsnlen", triplet("strnlen", "wcsnlen")), std::make_pair("_tcsrchr", triplet("strrchr", "wcsrchr")), std::make_pair("_tcsstr", triplet("strstr", "wcsstr")), std::make_pair("_tcstok", triplet("strtok", "wcstok")), std::make_pair("_ftprintf", triplet("fprintf", "fwprintf")), std::make_pair("_tprintf", triplet("printf", "wprintf")), std::make_pair("_stprintf", triplet("sprintf", "swprintf")), std::make_pair("_sntprintf", triplet("_snprintf", "_snwprintf")), std::make_pair("_ftscanf", triplet("fscanf", "fwscanf")), std::make_pair("_tscanf", triplet("scanf", "wscanf")), std::make_pair("_stscanf", triplet("sscanf", "swscanf")), std::make_pair("_ftprintf_s", triplet("fprintf_s", "fwprintf_s")), std::make_pair("_tprintf_s", triplet("printf_s", "wprintf_s")), std::make_pair("_stprintf_s", triplet("sprintf_s", "swprintf_s")), std::make_pair("_sntprintf_s", triplet("_snprintf_s", "_snwprintf_s")), std::make_pair("_ftscanf_s", triplet("fscanf_s", "fwscanf_s")), std::make_pair("_tscanf_s", triplet("scanf_s", "wscanf_s")), std::make_pair("_stscanf_s", triplet("sscanf_s", "swscanf_s")) }; } void Tokenizer::simplifyMicrosoftStringFunctions() { // skip if not Windows if (!mSettings->isWindowsPlatform()) return; const bool ansi = mSettings->platformType == Settings::Win32A; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->strAt(1) != "(") continue; const std::map::const_iterator match = apis.find(tok->str()); if (match!=apis.end()) { tok->str(ansi ? match->second.mbcs : match->second.unicode); tok->originalName(match->first); } else if (Token::Match(tok, "_T|_TEXT|TEXT ( %char%|%str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); if (!ansi) { tok->isLong(true); if (tok->str()[0] != 'L') tok->str("L" + tok->str()); } while (Token::Match(tok->next(), "_T|_TEXT|TEXT ( %char%|%str% )")) { tok->next()->deleteNext(); tok->next()->deleteThis(); tok->next()->deleteNext(); tok->concatStr(tok->next()->str()); tok->deleteNext(); } } } } // Remove Borland code void Tokenizer::simplifyBorland() { // skip if not Windows if (!mSettings->isWindowsPlatform()) return; if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "( __closure * %name% )")) { tok->deleteNext(); } } // I think that these classes are always declared at the outer scope // I save some time by ignoring inner classes. for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{" && !Token::Match(tok->tokAt(-2), "namespace %type%")) { tok = tok->link(); if (!tok) break; } else if (Token::Match(tok, "class %name% :|{")) { while (tok && tok->str() != "{" && tok->str() != ";") tok = tok->next(); if (!tok) break; if (tok->str() == ";") continue; const Token* end = tok->link()->next(); for (Token *tok2 = tok->next(); tok2 != end; tok2 = tok2->next()) { if (tok2->str() == "__property" && Token::Match(tok2->previous(), ";|{|}|protected:|public:|__published:")) { while (tok2->next() && !Token::Match(tok2->next(), "{|;")) tok2->deleteNext(); tok2->deleteThis(); if (tok2->str() == "{") { Token::eraseTokens(tok2, tok2->link()); tok2->deleteNext(); tok2->deleteThis(); // insert "; __property ;" tok2->previous()->insertToken(";"); tok2->previous()->insertToken("__property"); tok2->previous()->insertToken(";"); } } } } } } // Remove Qt signals and slots void Tokenizer::simplifyQtSignalsSlots() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { // check for emit which can be outside of class if (Token::Match(tok, "emit|Q_EMIT %name% (") && Token::simpleMatch(tok->linkAt(2), ") ;")) { tok->deleteThis(); } else if (!Token::Match(tok, "class %name% :|::|{")) continue; if (tok->previous() && tok->previous()->str() == "enum") { tok = tok->tokAt(2); continue; } // count { and } for tok2 int indentlevel = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { ++indentlevel; if (indentlevel == 1) tok = tok2; else tok2 = tok2->link(); } else if (tok2->str() == "}") { if (indentlevel<2) break; else --indentlevel; } else if (tok2->str() == ";" && indentlevel == 0) break; if (tok2->strAt(1) == "Q_OBJECT") tok2->deleteNext(); if (Token::Match(tok2->next(), "public|protected|private slots|Q_SLOTS :")) { tok2 = tok2->next(); tok2->str(tok2->str() + ":"); tok2->deleteNext(2); tok2 = tok2->previous(); } else if (Token::Match(tok2->next(), "signals|Q_SIGNALS :")) { tok2 = tok2->next(); tok2->str("protected:"); tok2->deleteNext(); } else if (Token::Match(tok2->next(), "emit|Q_EMIT %name% (") && Token::simpleMatch(tok2->linkAt(3), ") ;")) { tok2->deleteNext(); } } } } void Tokenizer::createSymbolDatabase() { if (!mSymbolDatabase) mSymbolDatabase = new SymbolDatabase(this, mSettings, mErrorLogger); mSymbolDatabase->validate(); } void Tokenizer::deleteSymbolDatabase() { delete mSymbolDatabase; mSymbolDatabase = nullptr; } bool Tokenizer::operatorEnd(const Token * tok) const { if (tok && tok->str() == ")") { if (isFunctionHead(tok, "{|;|?|:|[")) return true; tok = tok->next(); while (tok && !Token::Match(tok, "[=;{),]")) { if (Token::Match(tok, "const|volatile|override")) { tok = tok->next(); } else if (tok->str() == "noexcept") { tok = tok->next(); if (tok && tok->str() == "(") { tok = tok->link()->next(); } } else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") { tok = tok->next()->link()->next(); } // unknown macros ") MACRO {" and ") MACRO(...) {" else if (tok->isUpperCaseName()) { tok = tok->next(); if (tok && tok->str() == "(") { tok = tok->link()->next(); } } else if (Token::Match(tok, "%op% !!(") || (Token::Match(tok, "%op% (") && !isFunctionHead(tok->next(), "{"))) break; else return false; } return true; } return false; } void Tokenizer::simplifyOperatorName() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "using|:: operator %op%|%name% ;")) { tok->next()->str("operator" + tok->strAt(2)); tok->next()->deleteNext(); continue; } if (tok->str() != "operator") continue; // operator op if (Token::Match(tok, "operator %op% (") && !operatorEnd(tok->linkAt(2))) { tok->str(tok->str() + tok->next()->str()); tok->deleteNext(); continue; } std::string op; Token *par = tok->next(); bool done = false; while (!done && par) { done = true; if (par->isName()) { op += par->str(); par = par->next(); // merge namespaces eg. 'operator std :: string () const {' if (Token::Match(par, ":: %name%|%op%|.")) { op += par->str(); par = par->next(); } done = false; } else if (Token::Match(par, ".|%op%|,")) { // check for operator in template if (par->str() == "," && !op.empty()) break; if (!(Token::Match(par, "<|>") && !op.empty())) { op += par->str(); par = par->next(); done = false; } } else if (Token::simpleMatch(par, "[ ]")) { op += "[]"; par = par->tokAt(2); done = false; } else if (Token::Match(par, "( *| )")) { // break out and simplify.. if (operatorEnd(par->next())) break; while (par->str() != ")") { op += par->str(); par = par->next(); } op += ")"; par = par->next(); if (Token::simpleMatch(par, "...")) { op.clear(); par = nullptr; break; } done = false; } else if (Token::Match(par, "\"\" %name% (|;|<")) { op += "\"\""; op += par->strAt(1); par = par->tokAt(2); done = true; } else if (par->str() == "::") { op += par->str(); par = par->next(); done = false; } else if (par->str() == ";" || par->str() == ")") { done = true; } else if (par->str() != "(") { syntaxError(par, "operator"); } } if (par && !op.empty()) { tok->str("operator" + op); Token::eraseTokens(tok, par); } if (!op.empty()) tok->isOperatorKeyword(true); } for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%op% %str% %name%")) { std::string name = tok->strAt(2); Token * const str = tok->next(); str->deleteNext(); tok->insertToken("operator\"\"" + name); tok = tok->next(); tok->isOperatorKeyword(true); tok->insertToken("("); str->insertToken(")"); Token::createMutualLinks(tok->next(), str->next()); str->insertToken(MathLib::toString(Token::getStrLength(str))); str->insertToken(","); } } if (mSettings->debugwarnings) { const Token *tok = list.front(); while ((tok = Token::findsimplematch(tok, "operator")) != nullptr) { reportError(tok, Severity::debug, "debug", "simplifyOperatorName: found unsimplified operator name"); tok = tok->next(); } } } void Tokenizer::simplifyOverloadedOperators() { if (isC()) return; std::set classNames; std::set classVars; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName()) continue; if (Token::simpleMatch(tok, "this ) (") && Token::simpleMatch(tok->tokAt(-2), "( *")) { tok = tok->next(); tok->insertToken("operator()"); tok->insertToken("."); continue; } // Get classes that have operator() member if (Token::Match(tok, "class|struct %name% [:{]")) { int indent = 0; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "}") break; else if (indent == 0 && tok2->str() == ";") break; else if (tok2->str() == "{") { if (indent == 0) ++indent; else tok2 = tok2->link(); } else if (indent == 1 && Token::simpleMatch(tok2, "operator() (") && isFunctionHead(tok2->next(), ";{")) { classNames.insert(tok->strAt(1)); break; } } } // Get variables that have operator() member if (Token::Match(tok, "%type% &| %var%") && classNames.find(tok->str()) != classNames.end()) { tok = tok->next(); while (!tok->isName()) tok = tok->next(); classVars.insert(tok->varId()); } // Simplify operator() calls if (Token::Match(tok, "%var% (") && classVars.find(tok->varId()) != classVars.end()) { // constructor init list.. if (Token::Match(tok->previous(), "[:,]")) { const Token *start = tok->previous(); while (Token::simpleMatch(start, ",")) { if (Token::simpleMatch(start->previous(), ")")) start = start->linkAt(-1); else break; if (Token::Match(start->previous(), "%name%")) start = start->tokAt(-2); else break; } const Token *after = tok->linkAt(1); while (Token::Match(after, ")|} , %name% (|{")) after = after->linkAt(3); // Do not simplify initlist if (Token::simpleMatch(start, ":") && Token::simpleMatch(after, ") {")) continue; } tok->insertToken("operator()"); tok->insertToken("."); } } } // remove unnecessary member qualification.. void Tokenizer::removeUnnecessaryQualification() { if (isC()) return; std::vector classInfo; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "class|struct|namespace %type% :|{") && (!tok->previous() || tok->previous()->str() != "enum")) { Space info; info.isNamespace = tok->str() == "namespace"; tok = tok->next(); info.className = tok->str(); tok = tok->next(); while (tok && tok->str() != "{") tok = tok->next(); if (!tok) return; info.bodyEnd = tok->link(); classInfo.push_back(info); } else if (!classInfo.empty()) { if (tok == classInfo.back().bodyEnd) classInfo.pop_back(); else if (tok->str() == classInfo.back().className && !classInfo.back().isNamespace && tok->previous()->str() != ":" && (Token::Match(tok, "%type% :: ~| %type% (") || Token::Match(tok, "%type% :: operator"))) { const Token *tok1 = tok->tokAt(3); if (tok->strAt(2) == "operator") { // check for operator () if (tok1->str() == "(") tok1 = tok1->next(); while (tok1 && tok1->str() != "(") { if (tok1->str() == ";") break; tok1 = tok1->next(); } if (!tok1 || tok1->str() != "(") continue; } else if (tok->strAt(2) == "~") tok1 = tok1->next(); if (!tok1 || !Token::Match(tok1->link(), ") const| {|;|:")) { continue; } const bool isConstructorOrDestructor = Token::Match(tok, "%type% :: ~| %type%") && (tok->strAt(2) == tok->str() || (tok->strAt(2) == "~" && tok->strAt(3) == tok->str())); if (!isConstructorOrDestructor) { bool isPrependedByType = Token::Match(tok->previous(), "%type%"); if (!isPrependedByType) { const Token* tok2 = tok->tokAt(-2); isPrependedByType = Token::Match(tok2, "%type% *|&"); } if (!isPrependedByType) { const Token* tok3 = tok->tokAt(-3); isPrependedByType = Token::Match(tok3, "%type% * *|&"); } if (!isPrependedByType) { // It's not a constructor declaration and it's not a function declaration so // this is a function call which can have all the qualifiers just fine - skip. continue; } } } } } } void Tokenizer::simplifyReturnStrncat() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "return strncat (") && Token::simpleMatch(tok->linkAt(2), ") ;") && tok->strAt(3) != ")" && tok->strAt(3) != ",") { //first argument Token *tok2 = tok->tokAt(3); //check if there are at least three arguments for (int i = 0; i < 2; ++i) { tok2 = tok2->nextArgument(); if (!tok2) { tok = tok->linkAt(2)->next(); break; } } if (!tok2) continue; tok2 = tok2->nextArgument(); //we want only three arguments if (tok2) { tok = tok->linkAt(2)->next(); continue; } // Remove 'return' tok->deleteThis(); // Add 'return arg1 ;' after 'strncat(arg1, arg2, arg3);' tok = tok->next(); tok2 = tok->link()->next(); tok2->insertToken(";"); //the last token of the first argument before ',' const Token * const end = tok->next()->nextArgument()->tokAt(-2); //all the first argument is copied TokenList::copyTokens(tok2, tok->next(), end); tok2->insertToken("return"); } } } void Tokenizer::printUnknownTypes() const { if (!mSymbolDatabase) return; std::multimap unknowns; for (int i = 1; i <= mVarId; ++i) { const Variable *var = mSymbolDatabase->getVariableFromVarId(i); if (!var) continue; // is unknown type? if (var->type() || var->typeStartToken()->isStandardType()) continue; std::string name; const Token * nameTok; // single token type? if (var->typeStartToken() == var->typeEndToken()) { nameTok = var->typeStartToken(); name = nameTok->str(); } // complicated type else { const Token *tok = var->typeStartToken(); int level = 0; nameTok = tok; while (tok) { // skip pointer and reference part of type if (level == 0 && Token::Match(tok, "*|&")) break; name += tok->str(); if (Token::Match(tok, "struct|union|enum")) name += " "; // pointers and references are OK in template else if (tok->str() == "<") ++level; else if (tok->str() == ">") --level; if (tok == var->typeEndToken()) break; tok = tok->next(); } } unknowns.insert(std::pair(name, nameTok)); } if (!unknowns.empty()) { std::string last; int count = 0; for (std::multimap::const_iterator it = unknowns.begin(); it != unknowns.end(); ++it) { // skip types is std namespace because they are not interesting if (it->first.find("std::") != 0) { if (it->first != last) { last = it->first; count = 1; reportError(it->second, Severity::debug, "debug", "Unknown type \'" + it->first + "\'."); } else { if (count < 3) // limit same type to 3 reportError(it->second, Severity::debug, "debug", "Unknown type \'" + it->first + "\'."); count++; } } } } } void Tokenizer::simplifyMathExpressions() { for (Token *tok = list.front(); tok; tok = tok->next()) { //simplify Pythagorean trigonometric identity: pow(sin(x),2)+pow(cos(x),2) = 1 // pow(cos(x),2)+pow(sin(x),2) = 1 // @todo: sin(x) * sin(x) + cos(x) * cos(x) = 1 // cos(x) * cos(x) + sin(x) * sin(x) = 1 //simplify Hyperbolic identity: pow(sinh(x),2)-pow(cosh(x),2) = -1 // pow(cosh(x),2)-pow(sinh(x),2) = -1 // @todo: sinh(x) * sinh(x) - cosh(x) * cosh(x) = -1 // cosh(x) * cosh(x) - sinh(x) * sinh(x) = -1 if (Token::Match(tok, "pow|powf|powl (")) { if (Token::Match(tok->tokAt(2), "sin|sinf|sinl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) + pow|powf|powl ( cos|cosf|cosl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("1"); } } else if (Token::Match(tok->tokAt(2), "cos|cosf|cosl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) + pow|powf|powl ( sin|sinf|sinl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("1"); } } else if (Token::Match(tok->tokAt(2), "sinh|sinhf|sinhl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) - pow|powf|powl ( cosh|coshf|coshl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("-1"); } } else if (Token::Match(tok->tokAt(2), "cosh|coshf|coshl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) - pow|powf|powl ( sinh|sinhf|sinhl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("-1"); } } } } } bool Tokenizer::simplifyStrlen() { // replace strlen(str) bool modified=false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "strlen ( %str% )")) { tok->str(MathLib::toString(Token::getStrLength(tok->tokAt(2)))); tok->deleteNext(3); modified=true; } } return modified; } void Tokenizer::prepareTernaryOpForAST() { // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." // The AST parser relies on this function to add such parentheses where necessary. for (Token* tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "?") { bool parenthesesNeeded = false; int depth = 0; Token* tok2 = tok->next(); for (; tok2; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "[|(|<")) tok2 = tok2->link(); else if (tok2->str() == ":") { if (depth == 0) break; depth--; } else if (tok2->str() == ";" || (tok2->link() && tok2->str() != "{" && tok2->str() != "}")) break; else if (tok2->str() == ",") parenthesesNeeded = true; else if (tok2->str() == "<") parenthesesNeeded = true; else if (tok2->str() == "?") { depth++; parenthesesNeeded = true; } } if (parenthesesNeeded && tok2 && tok2->str() == ":") { tok->insertToken("("); tok2->insertToken(")", emptyString, true); Token::createMutualLinks(tok->next(), tok2->previous()); } } } } void Tokenizer::reportError(const Token* tok, const Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive) const { const std::list callstack(1, tok); reportError(callstack, severity, id, msg, inconclusive); } void Tokenizer::reportError(const std::list& callstack, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive) const { const ErrorMessage errmsg(callstack, &list, severity, id, msg, inconclusive ? Certainty::inconclusive : Certainty::normal); if (mErrorLogger) mErrorLogger->reportErr(errmsg); else Check::reportError(errmsg); } void Tokenizer::setPodTypes() { if (!mSettings) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName()) continue; // pod type const struct Library::PodType *podType = mSettings->library.podtype(tok->str()); if (podType) { const Token *prev = tok->previous(); while (prev && prev->isName()) prev = prev->previous(); if (prev && !Token::Match(prev, ";|{|}|,|(")) continue; tok->isStandardType(true); } } } const Token *Tokenizer::findSQLBlockEnd(const Token *tokSQLStart) { const Token *tokLastEnd = nullptr; for (const Token *tok = tokSQLStart->tokAt(2); tok != nullptr; tok = tok->next()) { if (tokLastEnd == nullptr && tok->str() == ";") tokLastEnd = tok; else if (tok->str() == "__CPPCHECK_EMBEDDED_SQL_EXEC__") { if (Token::simpleMatch(tok->tokAt(-2), "END - __CPPCHECK_EMBEDDED_SQL_EXEC__ ;")) return tok->next(); return tokLastEnd; } else if (Token::Match(tok, "{|}|==|&&|!|^|<<|>>|++|+=|-=|/=|*=|>>=|<<=|~")) break; // We are obviously outside the SQL block } return tokLastEnd; } void Tokenizer::simplifyNestedNamespace() { if (!isCPP()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "namespace %name% ::") && tok->strAt(-1) != "using") { Token * tok2 = tok->tokAt(2); // validate syntax while (Token::Match(tok2, ":: %name%")) tok2 = tok2->tokAt(2); if (!tok2 || tok2->str() != "{") return; // syntax error std::stack links; tok2 = tok->tokAt(2); while (tok2->str() == "::") { links.push(tok2); tok2->str("{"); tok2->insertToken("namespace"); tok2 = tok2->tokAt(3); } tok = tok2; if (!links.empty() && tok2->str() == "{") { tok2 = tok2->link(); while (!links.empty()) { tok2->insertToken("}"); tok2 = tok2->next(); Token::createMutualLinks(links.top(), tok2); links.pop(); } } } } } void Tokenizer::simplifyCoroutines() { if (!isCPP() || mSettings->standards.cpp < Standards::CPP20) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName() || !Token::Match(tok, "co_return|co_yield|co_await")) continue; Token *end = tok->next(); while (end && end->str() != ";") { if (Token::Match(end, "[({[]")) end = end->link(); else if (Token::Match(end, "[)]}]")) break; end = end->next(); } if (Token::simpleMatch(end, ";")) { tok->insertToken("("); end->previous()->insertToken(")"); Token::createMutualLinks(tok->next(), end->previous()); } } } static bool sameTokens(const Token *first, const Token *last, const Token *other) { while (other && first->str() == other->str()) { if (first == last) return true; first = first->next(); other = other->next(); } return false; } static bool alreadyHasNamespace(const Token *first, const Token *last, const Token *end) { while (end && last->str() == end->str()) { if (first == last) return true; last = last->previous(); end = end->previous(); } return false; } static Token * deleteAlias(Token * tok) { Token::eraseTokens(tok, Token::findsimplematch(tok, ";")); // delete first token tok->deleteThis(); // delete ';' if not last token tok->deleteThis(); return tok; } void Tokenizer::simplifyNamespaceAliases() { if (!isCPP()) return; int scope = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{") scope++; else if (tok->str() == "}") scope--; else if (Token::Match(tok, "namespace %name% =")) { const std::string name(tok->next()->str()); Token * tokNameStart = tok->tokAt(3); Token * tokNameEnd = tokNameStart; while (tokNameEnd && tokNameEnd->next() && tokNameEnd->next()->str() != ";") tokNameEnd = tokNameEnd->next(); if (!tokNameEnd) return; // syntax error int endScope = scope; Token * tokLast = tokNameEnd->next(); Token * tokNext = tokLast->next(); Token * tok2 = tokNext; while (tok2 && endScope >= scope) { if (Token::simpleMatch(tok2, "{")) endScope++; else if (Token::simpleMatch(tok2, "}")) endScope--; else if (tok2->str() == name) { if (Token::Match(tok2->previous(), "namespace %name% =")) { // check for possible duplicate aliases if (sameTokens(tokNameStart, tokNameEnd, tok2->tokAt(2))) { // delete duplicate tok2 = deleteAlias(tok2->previous()); continue; } else { // conflicting declaration (syntax error) // cppcheck-suppress duplicateBranch - remove when TODO below is addressed if (endScope == scope) { // delete conflicting declaration tok2 = deleteAlias(tok2->previous()); } // new declaration else { // TODO: use the new alias in this scope tok2 = deleteAlias(tok2->previous()); } continue; } } if (tok2->strAt(1) == "::" && !alreadyHasNamespace(tokNameStart, tokNameEnd, tok2)) { tok2->str(tokNameStart->str()); Token * tok3 = tokNameStart; while (tok3 != tokNameEnd) { tok2->insertToken(tok3->next()->str()); tok2 = tok2->next(); tok3 = tok3->next(); } } } tok2 = tok2->next(); } if (tok->previous() && tokNext) { Token::eraseTokens(tok->previous(), tokNext); tok = tokNext->previous(); } else if (tok->previous()) { Token::eraseTokens(tok->previous(), tokLast); tok = tokLast; } else if (tokNext) { Token::eraseTokens(tok, tokNext); tok->deleteThis(); } else { Token::eraseTokens(tok, tokLast); tok->deleteThis(); } } } } Tokenizer::VariableMap::VariableMap() : mVarId(0) {} void Tokenizer::VariableMap::enterScope() { mScopeInfo.push(std::list>()); } bool Tokenizer::VariableMap::leaveScope() { if (mScopeInfo.empty()) return false; for (const std::pair &outerVariable : mScopeInfo.top()) { if (outerVariable.second != 0) mVariableId[outerVariable.first] = outerVariable.second; else mVariableId.erase(outerVariable.first); } mScopeInfo.pop(); return true; } void Tokenizer::VariableMap::addVariable(const std::string &varname) { if (mScopeInfo.empty()) { mVariableId[varname] = ++mVarId; return; } std::map::iterator it = mVariableId.find(varname); if (it == mVariableId.end()) { mScopeInfo.top().push_back(std::pair(varname, 0)); mVariableId[varname] = ++mVarId; return; } mScopeInfo.top().push_back(std::pair(varname, it->second)); it->second = ++mVarId; } bool Tokenizer::VariableMap::hasVariable(const std::string &varname) const { return mVariableId.find(varname) != mVariableId.end(); } bool Tokenizer::hasIfdef(const Token *start, const Token *end) const { if (!mPreprocessor) return false; for (const Directive &d: mPreprocessor->getDirectives()) { if (d.str.compare(0,3,"#if") == 0 && d.linenr >= start->linenr() && d.linenr <= end->linenr() && start->fileIndex() < list.getFiles().size() && d.file == list.getFiles()[start->fileIndex()]) return true; } return false; } cppcheck-2.7/lib/tokenize.h000066400000000000000000000703311417746362400157330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef tokenizeH #define tokenizeH //--------------------------------------------------------------------------- #include "config.h" #include "errortypes.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include #include #include #include class Settings; class SymbolDatabase; class TimerResults; class Token; class TemplateSimplifier; class ErrorLogger; class Preprocessor; namespace simplecpp { class TokenList; } /// @addtogroup Core /// @{ /** @brief The main purpose is to tokenize the source code. It also has functions that simplify the token list */ class CPPCHECKLIB Tokenizer { friend class TestSimplifyTokens; friend class TestSimplifyTypedef; friend class TestSimplifyUsing; friend class TestTokenizer; friend class SymbolDatabase; friend class TestSimplifyTemplate; friend class TemplateSimplifier; /** Class used in Tokenizer::setVarIdPass1 */ class VariableMap { private: std::map mVariableId; std::stack>> mScopeInfo; mutable nonneg int mVarId; public: VariableMap(); void enterScope(); bool leaveScope(); void addVariable(const std::string &varname); bool hasVariable(const std::string &varname) const; std::map::const_iterator find(const std::string &varname) const { return mVariableId.find(varname); } std::map::const_iterator end() const { return mVariableId.end(); } const std::map &map() const { return mVariableId; } nonneg int *getVarId() const { return &mVarId; } }; public: Tokenizer(); Tokenizer(const Settings * settings, ErrorLogger *errorLogger); ~Tokenizer(); void setTimerResults(TimerResults *tr) { mTimerResults = tr; } /** Is the code C. Used for bailouts */ bool isC() const { return list.isC(); } /** Is the code CPP. Used for bailouts */ bool isCPP() const { return list.isCPP(); } /** * Check if inner scope ends with a call to a noreturn function * \param endScopeToken The '}' token * \param unknown set to true if it's unknown if the scope is noreturn * \return true if scope ends with a function call that might be 'noreturn' */ bool isScopeNoReturn(const Token *endScopeToken, bool *unknown = nullptr) const; bool createTokens(std::istream &code, const std::string& FileName); void createTokens(simplecpp::TokenList&& tokenList); bool simplifyTokens1(const std::string &configuration); /** * Tokenize code * @param code input stream for code, e.g. * \code * #file "p.h" * class Foo * { * private: * void Bar(); * }; * * #endfile * void Foo::Bar() * { * } * \endcode * * @param FileName The filename * @param configuration E.g. "A" for code where "#ifdef A" is true * @return false if source code contains syntax errors */ bool tokenize(std::istream &code, const char FileName[], const std::string &configuration = emptyString); /** Set variable id */ void setVarId(); void setVarIdPass1(); void setVarIdPass2(); /** * Basic simplification of tokenlist * * @param FileName The filename to run; used to do * markup checks. * * @return false if there is an error that requires aborting * the checking of this file. */ bool simplifyTokenList1(const char FileName[]); /** * Most aggressive simplification of tokenlist * * @return false if there is an error that requires aborting * the checking of this file. */ bool simplifyTokenList2(); /** * If --check-headers=no has been given; then remove unneeded code in headers. * - All executable code. * - Unused types/variables/etc */ void simplifyHeadersAndUnusedTemplates(); /** * Remove extra "template" keywords that are not used by Cppcheck */ void removeExtraTemplateKeywords(); /** Split up template right angle brackets. * foo < bar < >> => foo < bar < > > */ void splitTemplateRightAngleBrackets(bool check); /** * Deletes dead code between 'begin' and 'end'. * In general not everything can be erased, such as: * - code after labels; * - code outside the scope where the function is called; * - code after a change of scope caused by 'switch(...);' * instructions, like 'case %any%;' or 'default;' * Also, if the dead code contains a 'switch' block * and inside it there's a label, the function removes all * the 'switch(..)' tokens and every occurrence of 'case %any%; | default;' * expression, such as the 'switch' block is reduced to a simple block. * * @param begin Tokens after this have a possibility to be erased. * @param end Tokens before this have a possibility to be erased. */ static void eraseDeadCode(Token *begin, const Token *end); /** * Simplify '* & ( %name% ) =' or any combination of '* &' and '()' * parentheses around '%name%' to '%name% =' */ void simplifyMulAndParens(); /** * Calculates sizeof value for given type. * @param type Token which will contain e.g. "int", "*", or string. * @return sizeof for given type, or 0 if it can't be calculated. */ nonneg int sizeOfType(const Token *type) const; /** * Try to determine if function parameter is passed by value by looking * at the function declaration. * @param fpar token for function parameter in the function call * @return true if the parameter is passed by value. if unsure, false is returned */ bool isFunctionParameterPassedByValue(const Token *fpar) const; /** Simplify assignment in function call "f(x=g());" => "x=g();f(x);" */ void simplifyAssignmentInFunctionCall(); /** Simplify assignment where rhs is a block : "x=({123;});" => "{x=123;}" */ void simplifyAssignmentBlock(); /** * Simplify constant calculations such as "1+2" => "3" * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyCalculations(); /** * Simplify dereferencing a pointer offset by a number: * "*(ptr + num)" => "ptr[num]" * "*(ptr - num)" => "ptr[-num]" */ void simplifyOffsetPointerDereference(); /** * Simplify referencing a pointer offset: * "Replace "&str[num]" => "(str + num)" */ void simplifyOffsetPointerReference(); /** Insert array size where it isn't given */ void arraySize(); /** Simplify labels and 'case|default' syntaxes. */ void simplifyLabelsCaseDefault(); /** simplify case ranges (gcc extension) */ void simplifyCaseRange(); /** Remove macros in global scope */ void removeMacrosInGlobalScope(); void addSemicolonAfterUnknownMacro(); // Remove C99 and CPP11 _Pragma(str) void removePragma(); /** Remove undefined macro in class definition: * class DLLEXPORT Fred { }; * class Fred FINAL : Base { }; */ void removeMacroInClassDef(); /** Remove unknown macro in variable declarations: PROGMEM char x; */ void removeMacroInVarDecl(); /** Remove redundant assignment */ void removeRedundantAssignment(); /** Simplifies some realloc usage like * 'x = realloc (0, n);' => 'x = malloc(n);' * 'x = realloc (y, 0);' => 'x = 0; free(y);' */ void simplifyRealloc(); /** Add parentheses for sizeof: sizeof x => sizeof(x) */ void sizeofAddParentheses(); /** * Replace sizeof() to appropriate size. * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifySizeof(); /** * Simplify variable declarations (split up) * \param only_k_r_fpar Only simplify K&R function parameters */ void simplifyVarDecl(const bool only_k_r_fpar); void simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, const bool only_k_r_fpar); /** * Simplify variable initialization * '; int *p(0);' => '; int *p = 0;' */ void simplifyInitVar(); Token * initVar(Token * tok); /** * Simplify easy constant '?:' operation * Example: 0 ? (2/0) : 0 => 0 * @return true if something is modified * false if nothing is done. */ bool simplifyConstTernaryOp(); /** * Simplify compound assignments * Example: ";a+=b;" => ";a=a+b;" */ void simplifyCompoundAssignment(); /** * Simplify the location of "static" and "const" qualifiers in * a variable declaration or definition. * Example: "int static const a;" => "static const a;" * Example: "long long const static b;" => "static const long long b;" */ void simplifyStaticConst(); /** * Simplify assignments in "if" and "while" conditions * Example: "if(a=b);" => "a=b;if(a);" * Example: "while(a=b) { f(a); }" => "a = b; while(a){ f(a); a = b; }" * Example: "do { f(a); } while(a=b);" => "do { f(a); a = b; } while(a);" */ void simplifyIfAndWhileAssign(); /** * Simplify multiple assignments. * Example: "a = b = c = 0;" => "a = 0; b = 0; c = 0;" */ void simplifyVariableMultipleAssign(); /** * Simplify the 'C Alternative Tokens' * Examples: * "if(s and t)" => "if(s && t)" * "while((r bitand s) and not t)" => while((r & s) && !t)" * "a and_eq b;" => "a &= b;" */ bool simplifyCAlternativeTokens(); /** * Simplify comma into a semicolon when possible: * - "delete a, delete b" => "delete a; delete b;" * - "a = 0, b = 0;" => "a = 0; b = 0;" * - "return a(), b;" => "a(); return b;" */ void simplifyComma(); /** Add braces to an if-block, for-block, etc. * @return true if no syntax errors */ bool simplifyAddBraces(); /** Add braces to an if-block, for-block, etc. * for command starting at token including else-block * @return last token of command * or input token in case of an error where no braces are added * or NULL when syntaxError is called */ Token * simplifyAddBracesToCommand(Token * tok); /** Add pair of braces to an single if-block, else-block, for-block, etc. * for command starting at token * @return last token of command * or input token in case of an error where no braces are added * or NULL when syntaxError is called */ Token * simplifyAddBracesPair(Token *tok, bool commandWithCondition); // Convert "using ...;" to corresponding typedef void simplifyUsingToTypedef(); /** * typedef A mytype; * mytype c; * * Becomes: * typedef A mytype; * A c; */ void simplifyTypedef(); /** */ bool isMemberFunction(const Token *openParen) const; /** */ bool simplifyUsing(); /** * Simplify casts */ void simplifyCasts(); /** * Change (multiple) arrays to (multiple) pointers. */ void simplifyUndefinedSizeArray(); /** * A simplify function that replaces a variable with its value in cases * when the value is known. e.g. "x=10; if(x)" => "x=10;if(10)" * * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyKnownVariables(); /** * Utility function for simplifyKnownVariables. Get data about an * assigned variable. */ static bool simplifyKnownVariablesGetData(nonneg int varid, Token **_tok2, Token **_tok3, std::string &value, nonneg int &valueVarId, bool &valueIsPointer, bool floatvar); /** * utility function for simplifyKnownVariables. Perform simplification * of a given variable */ bool simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, nonneg int varid, const std::string &structname, std::string &value, nonneg int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) const; /** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/ void simplifyEmptyNamespaces(); /** Simplify redundant code placed after control flow statements : * 'return', 'throw', 'goto', 'break' and 'continue' */ void simplifyFlowControl(); /** Expand nested strcat() calls. */ void simplifyNestedStrcat(); /** Simplify "if else" */ void elseif(); /** Simplify C++17/C++20 if/switch/for initialization expression */ void simplifyIfSwitchForInit(); /** Simplify conditions * @return true if something is modified * false if nothing is done. */ bool simplifyConditions(); /** Remove redundant code, e.g. if( false ) { int a; } should be * removed, because it is never executed. * @return true if something is modified * false if nothing is done. */ bool removeRedundantConditions(); /** * Remove redundant for: * "for (x=0;x<1;x++) { }" => "{ x = 1; }" */ void removeRedundantFor(); /** * Reduces "; ;" to ";", except in "( ; ; )" */ void removeRedundantSemicolons(); /** Simplify function calls - constant return value * @return true if something is modified * false if nothing is done. */ bool simplifyFunctionReturn(); /** Struct simplification * "struct S { } s;" => "struct S { }; S s;" */ void simplifyStructDecl(); /** * Remove redundant parentheses: * - "((x))" => "(x)" * - "(function())" => "function()" * - "(delete x)" => "delete x" * - "(delete [] x)" => "delete [] x" * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyRedundantParentheses(); void simplifyCharAt(); /** Simplify references */ void simplifyReference(); /** * Simplify functions like "void f(x) int x; {" * into "void f(int x) {" */ void simplifyFunctionParameters(); /** Simplify function level try blocks: * Convert "void f() try {} catch (int) {}" * to "void f() { try {} catch (int) {} }" */ void simplifyFunctionTryCatch(); /** * Simplify templates */ void simplifyTemplates(); void simplifyDoublePlusAndDoubleMinus(); void simplifyRedundantConsecutiveBraces(); void simplifyArrayAccessSyntax(); void simplifyParameterVoid(); void fillTypeSizes(); void combineOperators(); void combineStringAndCharLiterals(); void concatenateNegativeNumberAndAnyPositive(); void simplifyExternC(); void simplifyRoundCurlyParentheses(); void simplifyTypeIntrinsics(); void simplifySQL(); void checkForEnumsWithTypedef(); void findComplicatedSyntaxErrorsInTemplates(); /** * Simplify e.g. 'atol("0")' into '0' */ void simplifyMathFunctions(); /** * Simplify e.g. 'sin(0)' into '0' */ void simplifyMathExpressions(); /** * Modify strings in the token list by replacing hex and oct * values. E.g. "\x61" -> "a" and "\000" -> "\0" * @param source The string to be modified, e.g. "\x61" * @return Modified string, e.g. "a" */ static std::string simplifyString(const std::string &source); /** * is token pointing at function head? * @param tok A '(' or ')' token in a possible function head * @param endsWith string after function head * @return token matching with endsWith if syntax seems to be a function head else nullptr */ const Token * isFunctionHead(const Token *tok, const std::string &endsWith) const; /** * is token pointing at function head? * @param tok A '(' or ')' token in a possible function head * @param endsWith string after function head * @param cpp c++ code * @return token matching with endsWith if syntax seems to be a function head else nullptr */ static const Token * isFunctionHead(const Token *tok, const std::string &endsWith, bool cpp); void setPreprocessor(const Preprocessor *preprocessor) { mPreprocessor = preprocessor; } const Preprocessor *getPreprocessor() const { return mPreprocessor; } bool hasIfdef(const Token *start, const Token *end) const; private: /** * simplify "while (0)" */ void simplifyWhile0(); /** * Simplify while(func() && errno==EINTR) */ void simplifyErrNoInWhile(); /** * Simplify while(func(f)) */ void simplifyFuncInWhile(); /** * Remove "std::" before some function names */ void simplifyStd(); /** Simplify pointer to standard type (C only) */ void simplifyPointerToStandardType(); /** Simplify function pointers */ void simplifyFunctionPointers(); /** * Send error message to error logger about internal bug. * @param tok the token that this bug concerns. */ NORETURN void cppcheckError(const Token *tok) const; /** * Setup links for tokens so that one can call Token::link(). */ void createLinks(); /** * Setup links between < and >. */ void createLinks2(); public: /** Syntax error */ NORETURN void syntaxError(const Token *tok, const std::string &code = "") const; /** Syntax error. Unmatched character. */ NORETURN void unmatchedToken(const Token *tok) const; /** Syntax error. C++ code in C file. */ NORETURN void syntaxErrorC(const Token *tok, const std::string &what) const; /** Warn about unknown macro(s), configuration is recommended */ NORETURN void unknownMacroError(const Token *tok1) const; void unhandledCharLiteral(const Token *tok, const std::string& msg) const; private: /** Report that there is an unhandled "class x y {" code */ void unhandled_macro_class_x_y(const Token *tok) const; /** Check configuration (unknown macros etc) */ void checkConfiguration() const; void macroWithSemicolonError(const Token *tok, const std::string ¯oName) const; /** * Is there C++ code in C file? */ void validateC() const; /** * assert that tokens are ok - used during debugging for example * to catch problems in simplifyTokenList1/2. */ void validate() const; /** Detect unknown macros and throw unknownMacro */ void reportUnknownMacros() const; /** Detect garbage code and call syntaxError() if found. */ void findGarbageCode() const; /** Detect garbage expression */ static bool isGarbageExpr(const Token *start, const Token *end, bool allowSemicolon); /** * Remove __declspec() */ void simplifyDeclspec(); /** * Remove calling convention */ void simplifyCallingConvention(); /** * Remove \__attribute\__ ((?)) */ void simplifyAttribute(); /** * Remove \__cppcheck\__ ((?)) */ void simplifyCppcheckAttribute(); /** Remove alignas */ void removeAlignas(); /** Simplify c++20 spaceship operator */ void simplifySpaceshipOperator(); /** * Remove keywords "volatile", "inline", "register", and "restrict" */ void simplifyKeyword(); /** * Remove __asm */ void simplifyAsm(); /** * asm heuristics, Put ^{} statements in asm() */ void simplifyAsm2(); /** * Simplify \@… (compiler extension) */ void simplifyAt(); /** * Simplify bitfields - the field width is removed as we don't use it. */ void simplifyBitfields(); /** * Remove unnecessary member qualification */ void removeUnnecessaryQualification(); /** * Add std:: in front of std classes, when using namespace std; was given */ void simplifyNamespaceStd(); /** * Convert Microsoft memory functions * CopyMemory(dst, src, len) -> memcpy(dst, src, len) * FillMemory(dst, len, val) -> memset(dst, val, len) * MoveMemory(dst, src, len) -> memmove(dst, src, len) * ZeroMemory(dst, len) -> memset(dst, 0, len) */ void simplifyMicrosoftMemoryFunctions(); /** * Convert Microsoft string functions * _tcscpy -> strcpy */ void simplifyMicrosoftStringFunctions(); /** * Remove Borland code */ void simplifyBorland(); /** * Remove Qt signals and slots */ void simplifyQtSignalsSlots(); /** * Collapse operator name tokens into single token * operator = => operator= */ void simplifyOperatorName(); /** simplify overloaded operators: 'obj(123)' => 'obj . operator() ( 123 )' */ void simplifyOverloadedOperators(); /** * Remove [[attribute]] (C++11 and later) from TokenList */ void simplifyCPPAttribute(); /** * Replace strlen(str) * @return true if any replacement took place, false else * */ bool simplifyStrlen(); /** * Convert namespace aliases */ void simplifyNamespaceAliases(); /** * Convert C++17 style nested namespace to older style */ void simplifyNestedNamespace(); /** * Simplify coroutines - just put parentheses around arguments for * co_* keywords so they can be handled like function calls in data * flow. */ void simplifyCoroutines(); /** * Prepare ternary operators with parentheses so that the AST can be created * */ void prepareTernaryOpForAST(); /** * report error message */ void reportError(const Token* tok, const Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive = false) const; void reportError(const std::list& callstack, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive = false) const; bool duplicateTypedef(Token **tokPtr, const Token *name, const Token *typeDef) const; void unsupportedTypedef(const Token *tok) const; void setVarIdClassDeclaration(const Token * const startToken, const VariableMap &variableMap, const nonneg int scopeStartVarId, std::map>& structMembers); void setVarIdStructMembers(Token **tok1, std::map>& structMembers, nonneg int *varId) const; void setVarIdClassFunction(const std::string &classname, Token * const startToken, const Token * const endToken, const std::map &varlist, std::map>& structMembers, nonneg int *varId_); /** * Simplify e.g. 'return(strncat(temp,"a",1));' into * strncat(temp,"a",1); return temp; */ void simplifyReturnStrncat(); /** * Output list of unknown types. */ void printUnknownTypes() const; /** Find end of SQL (or PL/SQL) block */ static const Token *findSQLBlockEnd(const Token *tokSQLStart); bool operatorEnd(const Token * tok) const; public: /** Was there templates in the code? */ bool codeWithTemplates() const { return mCodeWithTemplates; } void setSettings(const Settings *settings) { mSettings = settings; list.setSettings(settings); } const SymbolDatabase *getSymbolDatabase() const { return mSymbolDatabase; } void createSymbolDatabase(); void deleteSymbolDatabase(); /** print --debug output if debug flags match the simplification: * 0=unknown/both simplifications * 1=1st simplifications * 2=2nd simplifications */ void printDebugOutput(int simplification) const; void dump(std::ostream &out) const; Token *deleteInvalidTypedef(Token *typeDef); /** * Get variable count. * @return number of variables */ nonneg int varIdCount() const { return mVarId; } /** * Token list: stores all tokens. */ TokenList list; // Implement tokens() as a wrapper for convenience when using the TokenList const Token* tokens() const { return list.front(); } /** * Helper function to check whether number is zero (0 or 0.0 or 0E+0) or not? * @param s the string to check * @return true in case is is zero and false otherwise. */ static bool isZeroNumber(const std::string &s); /** * 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. */ static bool isOneNumber(const std::string &s); /** * Helper function to check whether number is two (2 or 0.2E+1 or 2E+0) or not? * @param s the string to check * @return true in case is is two and false otherwise. */ static bool isTwoNumber(const std::string &s); /** * Helper function to check for start of function execution scope. * Do not use this in checks. Use the symbol database. * @param tok pointer to end parentheses of parameter list * @return pointer to start brace of function scope or nullptr if not start. */ static const Token * startOfExecutableScope(const Token * tok); #ifdef MAXTIME bool isMaxTime() const { return (std::time(0) > mMaxTime); #else static bool isMaxTime() { return false; #endif } const Settings *getSettings() const { return mSettings; } void calculateScopes(); /** Disable copy constructor */ Tokenizer(const Tokenizer &) = delete; /** Disable assignment operator */ Tokenizer &operator=(const Tokenizer &) = delete; private: Token *processFunc(Token *tok2, bool inOperator) const; /** * Get new variable id. * @return new variable id */ nonneg int newVarId() { return ++mVarId; } /** Set pod types */ void setPodTypes(); /** settings */ const Settings * mSettings; /** errorlogger */ ErrorLogger* const mErrorLogger; /** Symbol database that all checks etc can use */ SymbolDatabase *mSymbolDatabase; TemplateSimplifier *mTemplateSimplifier; /** E.g. "A" for code where "#ifdef A" is true. This is used to print additional information in error situations. */ std::string mConfiguration; /** sizeof information for known types */ std::map mTypeSize; struct TypedefInfo { std::string name; std::string filename; int lineNumber; int column; bool used; }; std::vector mTypedefInfo; /** variable count */ nonneg int mVarId; /** unnamed count "Unnamed0", "Unnamed1", "Unnamed2", ... */ nonneg int mUnnamedCount; /** * was there any templates? templates that are "unused" are * removed from the token list */ bool mCodeWithTemplates; /** * TimerResults */ TimerResults *mTimerResults; #ifdef MAXTIME /** Tokenizer maxtime */ const std::time_t mMaxTime; #endif const Preprocessor *mPreprocessor; }; /// @} //--------------------------------------------------------------------------- #endif // tokenizeH cppcheck-2.7/lib/tokenlist.cpp000066400000000000000000002110371417746362400164520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "tokenlist.h" #include "astutils.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" #include "path.h" #include "settings.h" #include "standards.h" #include "token.h" #include #include #include #include #include #include #include #include #include // How many compileExpression recursions are allowed? // For practical code this could be endless. But in some special torture test // there needs to be a limit. static const int AST_MAX_DEPTH = 100; TokenList::TokenList(const Settings* settings) : mTokensFrontBack(), mSettings(settings), mIsC(false), mIsCpp(false) { mTokensFrontBack.list = this; mKeywords.insert("auto"); mKeywords.insert("break"); mKeywords.insert("case"); //mKeywords.insert("char"); // type mKeywords.insert("const"); mKeywords.insert("continue"); mKeywords.insert("default"); mKeywords.insert("do"); //mKeywords.insert("double"); // type mKeywords.insert("else"); mKeywords.insert("enum"); mKeywords.insert("extern"); //mKeywords.insert("float"); // type mKeywords.insert("for"); mKeywords.insert("goto"); mKeywords.insert("if"); mKeywords.insert("inline"); //mKeywords.insert("int"); // type //mKeywords.insert("long"); // type mKeywords.insert("register"); mKeywords.insert("restrict"); mKeywords.insert("return"); //mKeywords.insert("short"); // type mKeywords.insert("signed"); mKeywords.insert("sizeof"); mKeywords.insert("static"); mKeywords.insert("struct"); mKeywords.insert("switch"); mKeywords.insert("typedef"); mKeywords.insert("union"); mKeywords.insert("unsigned"); mKeywords.insert("void"); mKeywords.insert("volatile"); mKeywords.insert("while"); } TokenList::~TokenList() { deallocateTokens(); } //--------------------------------------------------------------------------- const std::string& TokenList::getSourceFilePath() const { if (getFiles().empty()) { return emptyString; } return getFiles()[0]; } //--------------------------------------------------------------------------- // Deallocate lists.. void TokenList::deallocateTokens() { deleteTokens(mTokensFrontBack.front); mTokensFrontBack.front = nullptr; mTokensFrontBack.back = nullptr; mFiles.clear(); } void TokenList::determineCppC() { if (!mSettings) { mIsC = Path::isC(getSourceFilePath()); mIsCpp = Path::isCPP(getSourceFilePath()); } else { mIsC = mSettings->enforcedLang == Settings::C || (mSettings->enforcedLang == Settings::None && Path::isC(getSourceFilePath())); mIsCpp = mSettings->enforcedLang == Settings::CPP || (mSettings->enforcedLang == Settings::None && Path::isCPP(getSourceFilePath())); } if (mIsCpp) { //mKeywords.insert("bool"); // type mKeywords.insert("catch"); mKeywords.insert("class"); mKeywords.insert("constexpr"); mKeywords.insert("const_cast"); mKeywords.insert("decltype"); mKeywords.insert("delete"); mKeywords.insert("dynamic_cast"); mKeywords.insert("explicit"); mKeywords.insert("export"); //mKeywords.insert("false"); // literal mKeywords.insert("friend"); mKeywords.insert("mutable"); mKeywords.insert("namespace"); mKeywords.insert("new"); mKeywords.insert("noexcept"); mKeywords.insert("operator"); mKeywords.insert("private"); mKeywords.insert("protected"); mKeywords.insert("public"); mKeywords.insert("reinterpret_cast"); mKeywords.insert("static_assert"); mKeywords.insert("static_cast"); mKeywords.insert("template"); mKeywords.insert("this"); mKeywords.insert("thread_local"); mKeywords.insert("throw"); //mKeywords.insert("true"); // literal mKeywords.insert("try"); mKeywords.insert("typeid"); mKeywords.insert("typename"); mKeywords.insert("typeof"); mKeywords.insert("using"); mKeywords.insert("virtual"); //mKeywords.insert("wchar_t"); // type if (!mSettings || mSettings->standards.cpp >= Standards::CPP20) { mKeywords.insert("alignas"); mKeywords.insert("alignof"); mKeywords.insert("axiom"); mKeywords.insert("co_await"); mKeywords.insert("co_return"); mKeywords.insert("co_yield"); mKeywords.insert("concept"); mKeywords.insert("synchronized"); mKeywords.insert("consteval"); mKeywords.insert("reflexpr"); mKeywords.insert("requires"); } } } int TokenList::appendFileIfNew(const std::string &fileName) { // Has this file been tokenized already? for (int i = 0; i < mFiles.size(); ++i) if (Path::sameFileName(mFiles[i], fileName)) return i; // The "mFiles" vector remembers what files have been tokenized.. mFiles.push_back(fileName); // Update mIsC and mIsCpp properties if (mFiles.size() == 1) { // Update only useful if first file added to _files determineCppC(); } return mFiles.size() - 1; } void TokenList::clangSetOrigFiles() { mOrigFiles = mFiles; } void TokenList::deleteTokens(Token *tok) { while (tok) { Token *next = tok->next(); delete tok; tok = next; } } //--------------------------------------------------------------------------- // add a token. //--------------------------------------------------------------------------- void TokenList::addtoken(std::string str, const nonneg int lineno, const nonneg int column, const nonneg int fileno, bool split) { if (str.empty()) return; // If token contains # characters, split it up if (split) { size_t begin = 0; size_t end = 0; while ((end = str.find("##", begin)) != std::string::npos) { addtoken(str.substr(begin, end - begin), lineno, fileno, false); addtoken("##", lineno, column, fileno, false); begin = end+2; } if (begin != 0) { addtoken(str.substr(begin), lineno, column, fileno, false); return; } } if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(str); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(str); } mTokensFrontBack.back->linenr(lineno); mTokensFrontBack.back->column(column); mTokensFrontBack.back->fileIndex(fileno); } void TokenList::addtoken(std::string str, const Token *locationTok) { if (str.empty()) return; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(str); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(str); } mTokensFrontBack.back->linenr(locationTok->linenr()); mTokensFrontBack.back->column(locationTok->column()); mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); } void TokenList::addtoken(const Token * tok, const nonneg int lineno, const nonneg int column, const nonneg int fileno) { if (tok == nullptr) return; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(tok->str()); if (!tok->originalName().empty()) mTokensFrontBack.back->originalName(tok->originalName()); } mTokensFrontBack.back->linenr(lineno); mTokensFrontBack.back->column(column); mTokensFrontBack.back->fileIndex(fileno); mTokensFrontBack.back->flags(tok->flags()); } void TokenList::addtoken(const Token *tok, const Token *locationTok) { if (tok == nullptr || locationTok == nullptr) return; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(tok->str()); if (!tok->originalName().empty()) mTokensFrontBack.back->originalName(tok->originalName()); } mTokensFrontBack.back->flags(tok->flags()); mTokensFrontBack.back->linenr(locationTok->linenr()); mTokensFrontBack.back->column(locationTok->column()); mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); } void TokenList::addtoken(const Token *tok) { if (tok == nullptr) return; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(tok->str()); if (!tok->originalName().empty()) mTokensFrontBack.back->originalName(tok->originalName()); } mTokensFrontBack.back->flags(tok->flags()); mTokensFrontBack.back->linenr(tok->linenr()); mTokensFrontBack.back->column(tok->column()); mTokensFrontBack.back->fileIndex(tok->fileIndex()); } //--------------------------------------------------------------------------- // copyTokens - Copy and insert tokens //--------------------------------------------------------------------------- Token *TokenList::copyTokens(Token *dest, const Token *first, const Token *last, bool one_line) { std::stack links; Token *tok2 = dest; int linenr = dest->linenr(); const int commonFileIndex = dest->fileIndex(); for (const Token *tok = first; tok != last->next(); tok = tok->next()) { tok2->insertToken(tok->str()); tok2 = tok2->next(); tok2->fileIndex(commonFileIndex); tok2->linenr(linenr); tok2->tokType(tok->tokType()); tok2->flags(tok->flags()); tok2->varId(tok->varId()); // Check for links and fix them up if (Token::Match(tok2, "(|[|{")) links.push(tok2); else if (Token::Match(tok2, ")|]|}")) { if (links.empty()) return tok2; Token * link = links.top(); tok2->link(link); link->link(tok2); links.pop(); } if (!one_line && tok->next()) linenr += tok->next()->linenr() - tok->linenr(); } return tok2; } //--------------------------------------------------------------------------- // InsertTokens - Copy and insert tokens //--------------------------------------------------------------------------- void TokenList::insertTokens(Token *dest, const Token *src, nonneg int n) { std::stack link; while (n > 0) { dest->insertToken(src->str(), src->originalName()); dest = dest->next(); // Set links if (Token::Match(dest, "(|[|{")) link.push(dest); else if (!link.empty() && Token::Match(dest, ")|]|}")) { Token::createMutualLinks(dest, link.top()); link.pop(); } dest->fileIndex(src->fileIndex()); dest->linenr(src->linenr()); dest->column(src->column()); dest->varId(src->varId()); dest->tokType(src->tokType()); dest->flags(src->flags()); src = src->next(); --n; } } //--------------------------------------------------------------------------- // Tokenize - tokenizes a given file. //--------------------------------------------------------------------------- bool TokenList::createTokens(std::istream &code, const std::string& file0) { appendFileIfNew(file0); simplecpp::OutputList outputList; simplecpp::TokenList tokens(code, mFiles, file0, &outputList); createTokens(std::move(tokens)); return outputList.empty(); } //--------------------------------------------------------------------------- void TokenList::createTokens(simplecpp::TokenList&& tokenList) { if (tokenList.cfront()) mOrigFiles = mFiles = tokenList.cfront()->location.files; else mFiles.clear(); determineCppC(); for (const simplecpp::Token *tok = tokenList.cfront(); tok;) { std::string str = tok->str(); // Float literal if (str.size() > 1 && str[0] == '.' && std::isdigit(str[1])) str = '0' + str; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(str); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(str); } mTokensFrontBack.back->fileIndex(tok->location.fileIndex); mTokensFrontBack.back->linenr(tok->location.line); mTokensFrontBack.back->column(tok->location.col); mTokensFrontBack.back->isExpandedMacro(!tok->macro.empty()); tok = tok->next; if (tok) tokenList.deleteToken(tok->previous); } if (mSettings && mSettings->relativePaths) { for (std::string & mFile : mFiles) mFile = Path::getRelativePath(mFile, mSettings->basePaths); } Token::assignProgressValues(mTokensFrontBack.front); } //--------------------------------------------------------------------------- unsigned long long TokenList::calculateChecksum() const { unsigned long long checksum = 0; for (const Token* tok = front(); tok; tok = tok->next()) { const unsigned int subchecksum1 = tok->flags() + tok->varId() + tok->tokType(); unsigned int subchecksum2 = 0; for (char i : tok->str()) subchecksum2 += (unsigned int)i; if (!tok->originalName().empty()) { for (char i : tok->originalName()) subchecksum2 += (unsigned int) i; } checksum ^= ((static_cast(subchecksum1) << 32) | subchecksum2); const bool bit1 = (checksum & 1) != 0; checksum >>= 1; if (bit1) checksum |= (1ULL << 63); } return checksum; } //--------------------------------------------------------------------------- struct AST_state { std::stack op; int depth; int inArrayAssignment; bool cpp; int assign; bool inCase; // true from case to : bool stopAtColon; // help to properly parse ternary operators const Token *functionCallEndPar; explicit AST_state(bool cpp) : depth(0), inArrayAssignment(0), cpp(cpp), assign(0), inCase(false),stopAtColon(false), functionCallEndPar(nullptr) {} }; static Token * skipDecl(Token *tok) { if (!Token::Match(tok->previous(), "( %name%")) return tok; Token *vartok = tok; while (Token::Match(vartok, "%name%|*|&|::|<")) { if (vartok->str() == "<") { if (vartok->link()) vartok = vartok->link(); else return tok; } else if (Token::Match(vartok, "%var% [:=(]")) { return vartok; } else if (Token::simpleMatch(vartok, "decltype (") && !Token::Match(tok->linkAt(1), ") [,)]")) { return vartok->linkAt(1)->next(); } vartok = vartok->next(); } return tok; } static bool iscast(const Token *tok, bool cpp) { if (!Token::Match(tok, "( ::| %name%")) return false; if (Token::simpleMatch(tok->link(), ") ( )")) return false; if (tok->previous() && tok->previous()->isName() && tok->previous()->str() != "return" && (!cpp || tok->previous()->str() != "throw")) return false; if (Token::simpleMatch(tok->previous(), ">") && tok->previous()->link()) return false; if (Token::Match(tok, "( (| typeof (") && Token::Match(tok->link(), ") %num%")) return true; if (Token::Match(tok->link(), ") }|)|]|;")) return false; if (Token::Match(tok->link(), ") %cop%") && !Token::Match(tok->link(), ") [&*+-~!]")) return false; if (Token::Match(tok->previous(), "= ( %name% ) {") && tok->next()->varId() == 0) return true; bool type = false; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId() != 0) return false; while (tok2->link() && Token::Match(tok2, "(|[|<")) tok2 = tok2->link()->next(); if (tok2->str() == ")") { if (Token::simpleMatch(tok2, ") (") && Token::simpleMatch(tok2->linkAt(1), ") .")) return true; if (Token::simpleMatch(tok2, ") {") && !type) { const Token *tok3 = tok2->linkAt(1); while (tok3 != tok2 && Token::Match(tok3, "[{}]")) tok3 = tok3->previous(); return tok3 != tok2 && tok3->str() != ";"; } return type || tok2->strAt(-1) == "*" || Token::simpleMatch(tok2, ") ~") || (Token::Match(tok2, ") %any%") && !tok2->next()->isOp() && !Token::Match(tok2->next(), "[[]);,?:.]")); } if (Token::Match(tok2, "&|&& )")) return true; if (!Token::Match(tok2, "%name%|*|::")) return false; if (tok2->isStandardType() && (tok2->next()->str() != "(" || Token::Match(tok2->next(), "( * *| )"))) type = true; } return false; } // int(1), int*(2), .. static Token * findCppTypeInitPar(Token *tok) { if (!tok || !Token::Match(tok->previous(), "[,()] %name%")) return nullptr; bool istype = false; while (Token::Match(tok, "%name%|::|<")) { if (tok->str() == "<") { tok = tok->link(); if (!tok) return nullptr; } istype |= tok->isStandardType(); tok = tok->next(); } if (!istype) return nullptr; if (!Token::Match(tok, "[*&]")) return nullptr; while (Token::Match(tok, "[*&]")) tok = tok->next(); return (tok && tok->str() == "(") ? tok : nullptr; } // X{} X{} etc static bool iscpp11init_impl(const Token * const tok); static bool iscpp11init(const Token * const tok) { if (tok->isCpp11init() == TokenImpl::Cpp11init::UNKNOWN) tok->setCpp11init(iscpp11init_impl(tok)); return tok->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; } static bool iscpp11init_impl(const Token * const tok) { if (Token::simpleMatch(tok, "{") && Token::simpleMatch(tok->link()->previous(), "; }")) return false; const Token *nameToken = tok; while (nameToken && nameToken->str() == "{") { if (nameToken->isCpp11init() != TokenImpl::Cpp11init::UNKNOWN) return nameToken->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; nameToken = nameToken->previous(); if (nameToken && nameToken->str() == "," && Token::simpleMatch(nameToken->previous(), "} ,")) nameToken = nameToken->linkAt(-1); } if (!nameToken) return false; if (nameToken->str() == ")" && Token::simpleMatch(nameToken->link()->previous(), "decltype (")) return true; if (nameToken->str() == ">" && nameToken->link()) nameToken = nameToken->link()->previous(); const Token *endtok = nullptr; if (Token::Match(nameToken, "%name%|return|: {") && (!Token::simpleMatch(nameToken->tokAt(2), "[") || findLambdaEndScope(nameToken->tokAt(2)))) endtok = nameToken->linkAt(1); else if (Token::Match(nameToken,"%name% <") && Token::simpleMatch(nameToken->linkAt(1),"> {")) endtok = nameToken->linkAt(1)->linkAt(1); else if (Token::Match(nameToken->previous(), "%name% ( {")) endtok = nameToken->linkAt(1); else return false; if (Token::Match(nameToken, "else|try|do|const|constexpr|override|volatile|&|&&")) return false; if (Token::simpleMatch(nameToken->previous(), "namespace")) return false; if (Token::Match(nameToken, "%any% {") && !Token::Match(nameToken, "return|:")) { // If there is semicolon between {..} this is not a initlist for (const Token *tok2 = nameToken->next(); tok2 != endtok; tok2 = tok2->next()) { if (tok2->str() == ";") return false; const Token * lambdaEnd = findLambdaEndScope(tok2); if (lambdaEnd) tok2 = lambdaEnd; } } // There is no initialisation for example here: 'class Fred {};' if (!Token::simpleMatch(endtok, "} ;")) return true; const Token *prev = nameToken; while (Token::Match(prev, "%name%|::|:|<|>")) { if (Token::Match(prev, "class|struct")) return false; prev = prev->previous(); } return true; } static bool isQualifier(const Token* tok) { while (Token::Match(tok, "&|&&|*")) tok = tok->next(); if (!Token::Match(tok, "{|;")) return false; return true; } static void compileUnaryOp(Token *&tok, AST_state& state, void (*f)(Token *&tok, AST_state& state)) { Token *unaryop = tok; if (f) { tok = tok->next(); state.depth++; if (state.depth > AST_MAX_DEPTH) throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); if (tok) f(tok, state); state.depth--; } if (!state.op.empty()) { unaryop->astOperand1(state.op.top()); state.op.pop(); } state.op.push(unaryop); } static void compileBinOp(Token *&tok, AST_state& state, void (*f)(Token *&tok, AST_state& state)) { Token *binop = tok; if (f) { tok = tok->next(); if (Token::Match(binop, "::|. ~")) tok = tok->next(); state.depth++; if (tok && state.depth <= AST_MAX_DEPTH) f(tok, state); state.depth--; } // TODO: Should we check if op is empty. // * Is it better to add assertion that it isn't? // * Write debug warning if it's empty? if (!state.op.empty()) { binop->astOperand2(state.op.top()); state.op.pop(); } if (!state.op.empty()) { binop->astOperand1(state.op.top()); state.op.pop(); } state.op.push(binop); } static void compileExpression(Token *&tok, AST_state& state); static void compileTerm(Token *&tok, AST_state& state) { if (!tok) return; if (Token::Match(tok, "L %str%|%char%")) tok = tok->next(); if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] . %name%")) { // Jump over . in C style struct initialization state.op.push(tok); tok->astOperand1(tok->next()); tok = tok->tokAt(2); } if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] [ %num%|%name% ]")) { state.op.push(tok); tok->astOperand1(tok->next()); tok = tok->tokAt(3); } if (tok->isLiteral()) { state.op.push(tok); do { tok = tok->next(); } while (Token::Match(tok, "%name%|%str%")); } else if (tok->isName()) { if (Token::Match(tok, "return|case") || (state.cpp && tok->str() == "throw")) { if (tok->str() == "case") state.inCase = true; const bool tokIsReturn = tok->str() == "return"; const bool stopAtColon = state.stopAtColon; state.stopAtColon=true; compileUnaryOp(tok, state, compileExpression); state.stopAtColon=stopAtColon; if (tokIsReturn) state.op.pop(); if (state.inCase && Token::simpleMatch(tok, ": ;")) { state.inCase = false; tok = tok->next(); } } else if (Token::Match(tok, "sizeof !!(")) { compileUnaryOp(tok, state, compileExpression); state.op.pop(); } else if (state.cpp && findCppTypeInitPar(tok)) { // int(0), int*(123), .. tok = findCppTypeInitPar(tok); state.op.push(tok); tok = tok->tokAt(2); } else if (state.cpp && iscpp11init(tok)) { // X{} X{} etc state.op.push(tok); tok = tok->next(); if (tok->str() == "<") tok = tok->link()->next(); if (Token::Match(tok, "{ . %name% =|{")) { const int inArrayAssignment = state.inArrayAssignment; state.inArrayAssignment = 1; compileBinOp(tok, state, compileExpression); state.inArrayAssignment = inArrayAssignment; } else if (Token::simpleMatch(tok, "{ }")) { tok->astOperand1(state.op.top()); state.op.pop(); state.op.push(tok); tok = tok->tokAt(2); } } else if (!state.cpp || !Token::Match(tok, "new|delete %name%|*|&|::|(|[")) { Token* tok2 = tok; tok = skipDecl(tok); if (Token::simpleMatch(tok2, "decltype (")) { Token* tok3 = tok2->next(); AST_state state1(state.cpp); compileExpression(tok3, state1); } bool repeat = true; while (repeat) { repeat = false; if (Token::Match(tok->next(), "%name%")) { tok = tok->next(); repeat = true; } if (Token::simpleMatch(tok->next(), "<") && Token::Match(tok->linkAt(1), "> %name%")) { tok = tok->next()->link()->next(); repeat = true; } } state.op.push(tok); if (Token::Match(tok, "%name% <") && tok->linkAt(1)) tok = tok->linkAt(1); else if (Token::Match(tok, "%name% ...")) tok = tok->next(); tok = tok->next(); if (Token::Match(tok, "%str%")) { while (Token::Match(tok, "%name%|%str%")) tok = tok->next(); } if (Token::Match(tok, "%name% %assign%")) tok = tok->next(); } } else if (tok->str() == "{") { const Token *prev = tok->previous(); if (Token::simpleMatch(prev, ") {") && iscast(prev->link(), state.cpp)) prev = prev->link()->previous(); if (Token::simpleMatch(tok->link(),"} [")) { tok = tok->next(); } else if (state.cpp && iscpp11init(tok)) { if (state.op.empty() || Token::Match(tok->previous(), "[{,]") || Token::Match(tok->tokAt(-2), "%name% (")) { if (Token::Match(tok, "{ !!}")) { Token *const end = tok->link(); if (Token::Match(tok, "{ . %name% =|{")) { const int inArrayAssignment = state.inArrayAssignment; state.inArrayAssignment = 1; compileBinOp(tok, state, compileExpression); state.inArrayAssignment = inArrayAssignment; } else { compileUnaryOp(tok, state, compileExpression); } if (precedes(tok,end)) tok = end; } else { state.op.push(tok); tok = tok->tokAt(2); } } else compileBinOp(tok, state, compileExpression); if (Token::Match(tok, "} ,|:|)")) tok = tok->next(); } else if (state.cpp && Token::Match(tok->tokAt(-2), "%name% ( {") && !Token::findsimplematch(tok, ";", tok->link())) { if (Token::simpleMatch(tok, "{ }")) tok = tok->tokAt(2); else { Token *tok1 = tok; state.inArrayAssignment++; compileUnaryOp(tok, state, compileExpression); state.inArrayAssignment--; tok = tok1->link()->next(); } } else if (!state.inArrayAssignment && !Token::simpleMatch(prev, "=")) { state.op.push(tok); tok = tok->link()->next(); } else { if (tok->link() != tok->next()) { state.inArrayAssignment++; compileUnaryOp(tok, state, compileExpression); if (Token::Match(tok, "} [,};]") && state.inArrayAssignment > 0) { tok = tok->next(); state.inArrayAssignment--; } } else { state.op.push(tok); tok = tok->tokAt(2); } } } } static void compileScope(Token *&tok, AST_state& state) { compileTerm(tok, state); while (tok) { if (tok->str() == "::") { const Token *lastOp = state.op.empty() ? nullptr : state.op.top(); if (Token::Match(lastOp, ":: %name%")) lastOp = lastOp->next(); if (Token::Match(lastOp, "%name%") && (lastOp->next() == tok || (Token::Match(lastOp, "%name% <") && lastOp->linkAt(1) && tok == lastOp->linkAt(1)->next()))) compileBinOp(tok, state, compileTerm); else compileUnaryOp(tok, state, compileTerm); } else break; } } static bool isPrefixUnary(const Token* tok, bool cpp) { if (!tok->previous() || ((Token::Match(tok->previous(), "(|[|{|%op%|;|}|?|:|,|.|return|::") || (cpp && tok->strAt(-1) == "throw")) && (tok->previous()->tokType() != Token::eIncDecOp || tok->tokType() == Token::eIncDecOp))) return true; if (tok->str() == "*" && tok->previous()->tokType() == Token::eIncDecOp && isPrefixUnary(tok->previous(), cpp)) return true; return tok->strAt(-1) == ")" && iscast(tok->linkAt(-1), cpp); } static void compilePrecedence2(Token *&tok, AST_state& state) { compileScope(tok, state); while (tok) { if (tok->tokType() == Token::eIncDecOp && !isPrefixUnary(tok, state.cpp)) { compileUnaryOp(tok, state, compileScope); } else if (tok->str() == "...") { state.op.push(tok); tok = tok->next(); break; } else if (tok->str() == "." && tok->strAt(1) != "*") { if (tok->strAt(1) == ".") { state.op.push(tok); tok = tok->tokAt(3); break; } compileBinOp(tok, state, compileScope); } else if (tok->str() == "[") { if (state.cpp && isPrefixUnary(tok, state.cpp) && Token::Match(tok->link(), "] (|{")) { // Lambda // What we do here: // - Nest the round bracket under the square bracket. // - Nest what follows the lambda (if anything) with the lambda opening [ // - Compile the content of the lambda function as separate tree (this is done later) // this must be consistent with isLambdaCaptureList Token* const squareBracket = tok; // Parse arguments in the capture list if (tok->strAt(1) != "]") { Token* tok2 = tok->next(); AST_state state2(state.cpp); compileExpression(tok2, state2); if (!state2.op.empty()) { squareBracket->astOperand2(state2.op.top()); } } if (Token::simpleMatch(squareBracket->link(), "] (")) { Token* const roundBracket = squareBracket->link()->next(); Token* curlyBracket = roundBracket->link()->next(); while (Token::Match(curlyBracket, "mutable|const|constexpr")) curlyBracket = curlyBracket->next(); if (Token::simpleMatch(curlyBracket, "noexcept (")) curlyBracket = curlyBracket->linkAt(1)->next(); if (curlyBracket && curlyBracket->originalName() == "->") curlyBracket = findTypeEnd(curlyBracket->next()); if (curlyBracket && curlyBracket->str() == "{") { squareBracket->astOperand1(roundBracket); roundBracket->astOperand1(curlyBracket); state.op.push(squareBracket); tok = curlyBracket->link()->next(); continue; } } else { Token* const curlyBracket = squareBracket->link()->next(); squareBracket->astOperand1(curlyBracket); state.op.push(squareBracket); tok = curlyBracket->link()->next(); continue; } } const Token* const tok2 = tok; if (tok->strAt(1) != "]") compileBinOp(tok, state, compileExpression); else compileUnaryOp(tok, state, compileExpression); tok = tok2->link()->next(); } else if (tok->str() == "(" && (!iscast(tok, state.cpp) || Token::Match(tok->previous(), "if|while|for|switch|catch"))) { Token* tok2 = tok; tok = tok->next(); const bool opPrevTopSquare = !state.op.empty() && state.op.top() && state.op.top()->str() == "["; const std::size_t oldOpSize = state.op.size(); compileExpression(tok, state); tok = tok2; if ((oldOpSize > 0 && Token::simpleMatch(tok->previous(), "} (")) || (tok->previous() && tok->previous()->isName() && !Token::Match(tok->previous(), "return|case") && (!state.cpp || !Token::Match(tok->previous(), "throw|delete"))) || (tok->strAt(-1) == "]" && (!state.cpp || !Token::Match(tok->linkAt(-1)->previous(), "new|delete"))) || (tok->strAt(-1) == ">" && tok->linkAt(-1)) || (tok->strAt(-1) == ")" && !iscast(tok->linkAt(-1), state.cpp)) // Don't treat brackets to clarify precedence as function calls || (tok->strAt(-1) == "}" && opPrevTopSquare)) { const bool operandInside = oldOpSize < state.op.size(); if (operandInside) compileBinOp(tok, state, nullptr); else compileUnaryOp(tok, state, nullptr); } tok = tok->link()->next(); } else if (iscast(tok, state.cpp) && Token::simpleMatch(tok->link(), ") {") && Token::simpleMatch(tok->link()->linkAt(1), "} [")) { Token *cast = tok; tok = tok->link()->next(); Token *tok1 = tok; compileUnaryOp(tok, state, compileExpression); cast->astOperand1(tok1); tok = tok1->link()->next(); } else if (state.cpp && tok->str() == "{" && iscpp11init(tok)) { if (Token::simpleMatch(tok, "{ }")) compileUnaryOp(tok, state, compileExpression); else compileBinOp(tok, state, compileExpression); while (Token::simpleMatch(tok, "}")) tok = tok->next(); } else break; } } static void compilePrecedence3(Token *&tok, AST_state& state) { compilePrecedence2(tok, state); while (tok) { if ((Token::Match(tok, "[+-!~*&]") || tok->tokType() == Token::eIncDecOp) && isPrefixUnary(tok, state.cpp)) { if (Token::Match(tok, "* [*,)]")) { Token* tok2 = tok->next(); while (tok2->next() && tok2->str() == "*") tok2 = tok2->next(); if (Token::Match(tok2, "[>),]")) { tok = tok2; continue; } } compileUnaryOp(tok, state, compilePrecedence3); } else if (tok->str() == "(" && iscast(tok, state.cpp)) { Token* castTok = tok; castTok->isCast(true); tok = tok->link()->next(); const int inArrayAssignment = state.inArrayAssignment; if (tok && tok->str() == "{") state.inArrayAssignment = 1; compilePrecedence3(tok, state); state.inArrayAssignment = inArrayAssignment; compileUnaryOp(castTok, state, nullptr); } else if (state.cpp && Token::Match(tok, "new %name%|::|(")) { Token* newtok = tok; tok = tok->next(); bool innertype = false; if (tok->str() == "(") { if (Token::Match(tok, "( &| %name%") && Token::Match(tok->link(), ") ( %type%") && Token::simpleMatch(tok->link()->linkAt(1), ") (")) tok = tok->link()->next(); if (Token::Match(tok->link(), ") ::| %type%")) { if (Token::Match(tok, "( !!)")) { Token *innerTok = tok->next(); AST_state innerState(true); compileExpression(innerTok, innerState); } tok = tok->link()->next(); } else if (Token::Match(tok, "( %type%") && Token::Match(tok->link(), ") [();,[]")) { tok = tok->next(); innertype = true; } else if (Token::Match(tok, "( &| %name%") && Token::simpleMatch(tok->link(), ") (")) { tok = tok->next(); innertype = true; } else { /* bad code */ continue; } } Token* leftToken = tok; while (Token::Match(tok->next(), ":: %name%")) { Token* scopeToken = tok->next(); //The :: scopeToken->astOperand1(leftToken); scopeToken->astOperand2(scopeToken->next()); leftToken = scopeToken; tok = scopeToken->next(); } state.op.push(tok); while (Token::Match(tok, "%name%|*|&|<|::")) { if (tok->link()) tok = tok->link(); tok = tok->next(); } if (Token::Match(tok, "( const| %type% ) (")) { state.op.push(tok->next()); tok = tok->link()->next(); compileBinOp(tok, state, compilePrecedence2); } else if (tok && (tok->str() == "[" || tok->str() == "(" || tok->str() == "{")) compilePrecedence2(tok, state); else if (innertype && Token::simpleMatch(tok, ") [")) { tok = tok->next(); compilePrecedence2(tok, state); } compileUnaryOp(newtok, state, nullptr); if (innertype && Token::simpleMatch(tok, ") ,")) tok = tok->next(); } else if (state.cpp && Token::Match(tok, "delete %name%|*|&|::|(|[")) { Token* tok2 = tok; tok = tok->next(); if (tok && tok->str() == "[") tok = tok->link()->next(); compilePrecedence3(tok, state); compileUnaryOp(tok2, state, nullptr); } // TODO: Handle sizeof else break; } } static void compilePointerToElem(Token *&tok, AST_state& state) { compilePrecedence3(tok, state); while (tok) { if (Token::simpleMatch(tok, ". *")) { compileBinOp(tok, state, compilePrecedence3); } else break; } } static void compileMulDiv(Token *&tok, AST_state& state) { compilePointerToElem(tok, state); while (tok) { if (Token::Match(tok, "[/%]") || (tok->str() == "*" && !tok->astOperand1() && !isQualifier(tok))) { if (Token::Match(tok, "* [*,)]")) { Token* tok2 = tok->next(); while (tok2->next() && tok2->str() == "*") tok2 = tok2->next(); if (Token::Match(tok2, "[>),]")) { tok = tok2; break; } } compileBinOp(tok, state, compilePointerToElem); } else break; } } static void compileAddSub(Token *&tok, AST_state& state) { compileMulDiv(tok, state); while (tok) { if (Token::Match(tok, "+|-") && !tok->astOperand1()) { compileBinOp(tok, state, compileMulDiv); } else break; } } static void compileShift(Token *&tok, AST_state& state) { compileAddSub(tok, state); while (tok) { if (Token::Match(tok, "<<|>>")) { compileBinOp(tok, state, compileAddSub); } else break; } } static void compileThreewayComp(Token *&tok, AST_state& state) { compileShift(tok, state); while (tok) { if (tok->str() == "<=>") { compileBinOp(tok, state, compileShift); } else break; } } static void compileRelComp(Token *&tok, AST_state& state) { compileThreewayComp(tok, state); while (tok) { if (Token::Match(tok, "<|<=|>=|>") && !tok->link()) { compileBinOp(tok, state, compileThreewayComp); } else break; } } static void compileEqComp(Token *&tok, AST_state& state) { compileRelComp(tok, state); while (tok) { if (Token::Match(tok, "==|!=")) { compileBinOp(tok, state, compileRelComp); } else break; } } static void compileAnd(Token *&tok, AST_state& state) { compileEqComp(tok, state); while (tok) { if (tok->str() == "&" && !tok->astOperand1() && !isQualifier(tok)) { Token* tok2 = tok->next(); if (!tok2) break; if (tok2->str() == "&") tok2 = tok2->next(); if (state.cpp && Token::Match(tok2, ",|)")) { tok = tok2; break; // rValue reference } compileBinOp(tok, state, compileEqComp); } else break; } } static void compileXor(Token *&tok, AST_state& state) { compileAnd(tok, state); while (tok) { if (tok->str() == "^") { compileBinOp(tok, state, compileAnd); } else break; } } static void compileOr(Token *&tok, AST_state& state) { compileXor(tok, state); while (tok) { if (tok->str() == "|") { compileBinOp(tok, state, compileXor); } else break; } } static void compileLogicAnd(Token *&tok, AST_state& state) { compileOr(tok, state); while (tok) { if (tok->str() == "&&" && !isQualifier(tok)) { if (!tok->astOperand1()) { Token* tok2 = tok->next(); if (!tok2) break; if (state.cpp && Token::Match(tok2, ",|)")) { tok = tok2; break; // rValue reference } } compileBinOp(tok, state, compileOr); } else break; } } static void compileLogicOr(Token *&tok, AST_state& state) { compileLogicAnd(tok, state); while (tok) { if (tok->str() == "||") { compileBinOp(tok, state, compileLogicAnd); } else break; } } static void compileAssignTernary(Token *&tok, AST_state& state) { compileLogicOr(tok, state); while (tok) { if (tok->isAssignmentOp()) { state.assign++; const Token *tok1 = tok->next(); compileBinOp(tok, state, compileAssignTernary); if (Token::simpleMatch(tok1, "{") && tok == tok1->link() && tok->next()) tok = tok->next(); if (state.assign > 0) state.assign--; } else if (tok->str() == "?") { // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." // Hence, we rely on Tokenizer::prepareTernaryOpForAST() to add such parentheses where necessary. const bool stopAtColon = state.stopAtColon; state.stopAtColon = false; if (tok->strAt(1) == ":") { state.op.push(nullptr); } const int assign = state.assign; state.assign = 0; compileBinOp(tok, state, compileAssignTernary); state.assign = assign; state.stopAtColon = stopAtColon; } else if (tok->str() == ":") { if (state.depth == 1U && state.inCase) { state.inCase = false; tok = tok->next(); break; } if (state.stopAtColon) break; if (state.assign > 0) break; compileBinOp(tok, state, compileAssignTernary); } else break; } } static void compileComma(Token *&tok, AST_state& state) { compileAssignTernary(tok, state); while (tok) { if (tok->str() == ",") { if (Token::simpleMatch(tok, ", }")) tok = tok->next(); else compileBinOp(tok, state, compileAssignTernary); } else if (tok->str() == ";" && state.functionCallEndPar && tok->index() < state.functionCallEndPar->index()) { compileBinOp(tok, state, compileAssignTernary); } else break; } } static void compileExpression(Token *&tok, AST_state& state) { if (state.depth > AST_MAX_DEPTH) throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); // ticket #5592 if (tok) compileComma(tok, state); } static bool isLambdaCaptureList(const Token * tok) { // a lambda expression '[x](y){}' is compiled as: // [ // `-( <<-- optional // `-{ // see compilePrecedence2 if (tok->str() != "[") return false; if (!Token::Match(tok->link(), "] (|{")) return false; if (Token::simpleMatch(tok->astOperand1(), "{") && tok->astOperand1() == tok->link()->next()) return true; if (!tok->astOperand1() || tok->astOperand1()->str() != "(") return false; const Token * params = tok->astOperand1(); if (!params->astOperand1() || params->astOperand1()->str() != "{") return false; return true; } static Token * createAstAtToken(Token *tok, bool cpp); // Compile inner expressions inside inner ({..}) and lambda bodies static void createAstAtTokenInner(Token * const tok1, const Token *endToken, bool cpp) { for (Token* tok = tok1; precedes(tok, endToken); tok = tok ? tok->next() : nullptr) { if (tok->str() == "{" && !iscpp11init(tok)) { const Token * const endToken2 = tok->link(); bool hasAst = false; for (const Token *inner = tok->next(); inner != endToken2; inner = inner->next()) { if (inner->astOperand1()) { hasAst = true; break; } if (tok->isConstOp()) break; if (inner->str() == "{") inner = inner->link(); } if (!hasAst) { for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) tok = createAstAtToken(tok, cpp); } } else if (cpp && tok->str() == "[") { if (isLambdaCaptureList(tok)) { tok = tok->astOperand1(); if (tok->str() == "(") tok = tok->astOperand1(); const Token * const endToken2 = tok->link(); tok = tok->next(); for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) tok = createAstAtToken(tok, cpp); } } } } static Token * findAstTop(Token *tok1, Token *tok2) { for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { if (tok->astParent() || tok->astOperand1() || tok->astOperand2()) { while (tok->astParent() && tok->astParent()->index() >= tok1->index() && tok->astParent()->index() <= tok2->index()) tok = tok->astParent(); return tok; } if (Token::simpleMatch(tok, "( {")) tok = tok->link(); } for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { if (tok->isName() || tok->isNumber()) return tok; if (Token::simpleMatch(tok, "( {")) tok = tok->link(); } return nullptr; } static Token * createAstAtToken(Token *tok, bool cpp) { if (Token::simpleMatch(tok, "for (")) { if (cpp && Token::Match(tok, "for ( const| auto &|&&| [")) { Token *decl = Token::findsimplematch(tok, "["); if (Token::simpleMatch(decl->link(), "] :")) { AST_state state1(cpp); while (decl->str() != "]") { if (Token::Match(decl, "%name% ,|]")) { state1.op.push(decl); } else if (decl->str() == ",") { if (!state1.op.empty()) { decl->astOperand1(state1.op.top()); state1.op.pop(); } if (!state1.op.empty()) { state1.op.top()->astOperand2(decl); state1.op.pop(); } state1.op.push(decl); } decl = decl->next(); } if (state1.op.size() > 1) { Token *lastName = state1.op.top(); state1.op.pop(); state1.op.top()->astOperand2(lastName); } decl = decl->next(); Token *colon = decl; compileExpression(decl, state1); tok->next()->astOperand1(tok); tok->next()->astOperand2(colon); return decl; } } Token *tok2 = skipDecl(tok->tokAt(2)); if (Token::simpleMatch(tok->tokAt(2), "decltype (")) { Token* tok3 = tok->tokAt(4); AST_state state1(cpp); compileExpression(tok3, state1); } Token *init1 = nullptr; Token * const endPar = tok->next()->link(); if (tok2 == tok->tokAt(2) && Token::Match(tok2, "%op%|(")) { init1 = tok2; AST_state state1(cpp); compileExpression(tok2, state1); if (Token::Match(init1, "( !!{")) { for (Token *tok3 = init1; tok3 != tok3->link(); tok3 = tok3->next()) { if (tok3->astParent()) { while (tok3->astParent()) tok3 = tok3->astParent(); init1 = tok3; break; } if (!Token::Match(tok3, "%op%|(|[")) init1 = tok3; } } } else { while (tok2 && tok2 != endPar && tok2->str() != ";") { if (tok2->str() == "<" && tok2->link()) { tok2 = tok2->link(); } else if (Token::Match(tok2, "%name% %op%|(|[|.|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) { init1 = tok2; AST_state state1(cpp); compileExpression(tok2, state1); if (Token::Match(tok2, ";|)")) break; init1 = nullptr; } if (!tok2) // #7109 invalid code return nullptr; tok2 = tok2->next(); } } if (!tok2 || tok2->str() != ";") { if (tok2 == endPar && init1) { tok->next()->astOperand2(init1); tok->next()->astOperand1(tok); } return tok2; } Token * const init = init1 ? init1 : tok2; Token * const semicolon1 = tok2; tok2 = tok2->next(); AST_state state2(cpp); compileExpression(tok2, state2); Token * const semicolon2 = tok2; if (!semicolon2) return nullptr; // invalid code #7235 if (semicolon2->str() == ";") { tok2 = tok2->next(); AST_state state3(cpp); if (Token::simpleMatch(tok2, "( {")) { state3.op.push(tok2->next()); tok2 = tok2->link()->next(); } compileExpression(tok2, state3); tok2 = findAstTop(semicolon1->next(), semicolon2); if (tok2) semicolon2->astOperand1(tok2); tok2 = findAstTop(semicolon2->next(), endPar); if (tok2) semicolon2->astOperand2(tok2); else if (!state3.op.empty()) semicolon2->astOperand2(state3.op.top()); semicolon1->astOperand2(semicolon2); } else { if (!cpp || !Token::simpleMatch(state2.op.top(), ":")) throw InternalError(tok, "syntax error", InternalError::SYNTAX); semicolon1->astOperand2(state2.op.top()); } if (init != semicolon1) semicolon1->astOperand1(init->astTop()); tok->next()->astOperand1(tok); tok->next()->astOperand2(semicolon1); createAstAtTokenInner(endPar->link(), endPar, cpp); return endPar; } if (Token::simpleMatch(tok, "( {")) return tok; if (Token::Match(tok, "%type% <") && tok->linkAt(1) && !Token::Match(tok->linkAt(1), "> [({]")) return tok->linkAt(1); if (cpp && Token::Match(tok, "%type% ::|<|%name%")) { Token *tok2 = tok; while (true) { if (Token::Match(tok2, "%name%|> :: %name%")) tok2 = tok2->tokAt(2); else if (Token::Match(tok2, "%name% <") && tok2->linkAt(1)) tok2 = tok2->linkAt(1); else break; } if (Token::Match(tok2, "%name%|> %name% {") && tok2->next()->varId() && iscpp11init(tok2->tokAt(2))) { Token *const tok1 = tok = tok2->next(); AST_state state(cpp); compileExpression(tok, state); createAstAtTokenInner(tok1->next(), tok1->linkAt(1), cpp); return tok; } } if (Token::Match(tok, "%type% %name%|*|&|::") && tok->str() != "return") { int typecount = 0; Token *typetok = tok; while (Token::Match(typetok, "%type%|::|*|&")) { if (typetok->isName() && !Token::simpleMatch(typetok->previous(), "::")) typecount++; typetok = typetok->next(); } if (Token::Match(typetok, "%var% =") && typetok->varId()) tok = typetok; // Do not create AST for function declaration if (typetok && typecount >= 2 && !Token::Match(tok, "return|throw") && Token::Match(typetok->previous(), "%name% (") && typetok->previous()->varId() == 0 && !typetok->previous()->isKeyword() && Token::Match(typetok->link(), ") const|;|{")) return typetok; } if (Token::Match(tok, "return|case") || (cpp && tok->str() == "throw") || !tok->previous() || Token::Match(tok, "%name% %op%|(|[|.|::|<|?|;") || (cpp && Token::Match(tok, "%name% {") && iscpp11init(tok->next())) || Token::Match(tok->previous(), "[;{}] %cop%|++|--|( !!{") || Token::Match(tok->previous(), "[;{}] %num%|%str%|%char%")) { if (cpp && (Token::Match(tok->tokAt(-2), "[;{}] new|delete %name%") || Token::Match(tok->tokAt(-3), "[;{}] :: new|delete %name%"))) tok = tok->previous(); Token * const tok1 = tok; AST_state state(cpp); if (Token::Match(tok, "%name% (")) state.functionCallEndPar = tok->linkAt(1); compileExpression(tok, state); const Token * const endToken = tok; if (endToken == tok1 || !endToken) return tok1; createAstAtTokenInner(tok1->next(), endToken, cpp); return endToken->previous(); } if (cpp && tok->str() == "{" && iscpp11init(tok)) { Token * const tok1 = tok; AST_state state(cpp); compileExpression(tok, state); const Token * const endToken = tok; if (endToken == tok1 || !endToken) return tok1; createAstAtTokenInner(tok1->next(), endToken, cpp); return endToken->previous(); } return tok; } void TokenList::createAst() const { for (Token *tok = mTokensFrontBack.front; tok; tok = tok ? tok->next() : nullptr) { tok = createAstAtToken(tok, isCPP()); } } struct OnException { std::function f; ~OnException() { #ifndef _MSC_VER if (std::uncaught_exception()) f(); #endif } }; void TokenList::validateAst() const { OnException oe{[&] { if (mSettings->debugnormal) mTokensFrontBack.front->printOut(); }}; // Check for some known issues in AST to avoid crash/hang later on std::set safeAstTokens; // list of "safe" AST tokens without endless recursion for (const Token *tok = mTokensFrontBack.front; tok; tok = tok->next()) { // Syntax error if binary operator only has 1 operand if ((tok->isAssignmentOp() || tok->isComparisonOp() || Token::Match(tok,"[|^/%]")) && tok->astOperand1() && !tok->astOperand2()) throw InternalError(tok, "Syntax Error: AST broken, binary operator has only one operand.", InternalError::AST); // Syntax error if we encounter "?" with operand2 that is not ":" if (tok->str() == "?") { if (!tok->astOperand1() || !tok->astOperand2()) throw InternalError(tok, "AST broken, ternary operator missing operand(s)", InternalError::AST); else if (tok->astOperand2()->str() != ":") throw InternalError(tok, "Syntax Error: AST broken, ternary operator lacks ':'.", InternalError::AST); } // Check for endless recursion const Token* parent = tok->astParent(); if (parent) { std::set astTokens; // list of ancestors astTokens.insert(tok); do { if (safeAstTokens.find(parent) != safeAstTokens.end()) break; if (astTokens.find(parent) != astTokens.end()) throw InternalError(tok, "AST broken: endless recursion from '" + tok->str() + "'", InternalError::AST); astTokens.insert(parent); } while ((parent = parent->astParent()) != nullptr); safeAstTokens.insert(astTokens.begin(), astTokens.end()); } else if (tok->str() == ";") { safeAstTokens.clear(); } else { safeAstTokens.insert(tok); } // Don't check templates if (tok->str() == "<" && tok->link()) { tok = tok->link(); continue; } // Check binary operators if (Token::Match(tok, "%or%|%oror%|%assign%|%comp%")) { // Skip lambda captures if (Token::Match(tok, "= ,|]")) continue; // Skip pure virtual functions if (Token::simpleMatch(tok->previous(), ") = 0")) continue; // Skip operator definitions if (Token::simpleMatch(tok->previous(), "operator")) continue; // Skip incomplete code if (!tok->astOperand1() && !tok->astOperand2() && !tok->astParent()) continue; // Skip lambda assignment and/or initializer if (Token::Match(tok, "= {|^|[")) continue; // FIXME: Workaround broken AST assignment in type aliases if (Token::Match(tok->previous(), "%name% = %name%")) continue; if (!tok->astOperand1() || !tok->astOperand2()) throw InternalError(tok, "Syntax Error: AST broken, binary operator '" + tok->str() + "' doesn't have two operands.", InternalError::AST); } // Check control blocks and asserts if (Token::Match(tok->previous(), "if|while|for|switch|assert|ASSERT (")) { if (!tok->astOperand1() || !tok->astOperand2()) throw InternalError(tok, "Syntax Error: AST broken, '" + tok->previous()->str() + "' doesn't have two operands.", InternalError::AST); } // Check member access if (Token::Match(tok, "%var% .")) { if (!tok->astParent()) { throw InternalError( tok, "Syntax Error: AST broken, '" + tok->str() + "' doesn't have a parent.", InternalError::AST); } if (!tok->next()->astOperand1() || !tok->next()->astOperand2()) { const std::string& op = tok->next()->originalName().empty() ? tok->next()->str() : tok->next()->originalName(); throw InternalError( tok, "Syntax Error: AST broken, '" + op + "' doesn't have two operands.", InternalError::AST); } } } } std::string TokenList::getOrigFile(const Token *tok) const { return mOrigFiles.at(tok->fileIndex()); } const std::string& TokenList::file(const Token *tok) const { return mFiles.at(tok->fileIndex()); } std::string TokenList::fileLine(const Token *tok) const { return ErrorMessage::FileLocation(tok, this).stringify(); } bool TokenList::validateToken(const Token* tok) const { if (!tok) return true; for (const Token *t = mTokensFrontBack.front; t; t = t->next()) { if (tok==t) return true; } return false; } void TokenList::simplifyPlatformTypes() { const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11; enum { isLongLong, isLong, isInt } type; /** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */ if (mSettings->sizeof_size_t == mSettings->sizeof_long) type = isLong; else if (mSettings->sizeof_size_t == mSettings->sizeof_long_long) type = isLongLong; else if (mSettings->sizeof_size_t == mSettings->sizeof_int) type = isInt; else return; for (Token *tok = front(); tok; tok = tok->next()) { // pre-check to reduce unneeded match calls if (!Token::Match(tok, "std| ::| %type%")) continue; bool isUnsigned; if (Token::Match(tok, "std| ::| size_t|uintptr_t|uintmax_t")) { if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") continue; isUnsigned = true; } else if (Token::Match(tok, "std| ::| ssize_t|ptrdiff_t|intptr_t|intmax_t")) { if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") continue; isUnsigned = false; } else continue; bool inStd = false; if (tok->str() == "::") { tok->deleteThis(); } else if (tok->str() == "std") { if (tok->next()->str() != "::") continue; inStd = true; tok->deleteNext(); tok->deleteThis(); } if (inStd) tok->originalName("std::" + tok->str()); else tok->originalName(tok->str()); if (isUnsigned) tok->isUnsigned(true); switch (type) { case isLongLong: tok->isLong(true); tok->str("long"); break; case isLong: tok->str("long"); break; case isInt: tok->str("int"); break; } } const std::string platform_type(mSettings->platformString()); for (Token *tok = front(); tok; tok = tok->next()) { if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) continue; const Library::PlatformType * const platformtype = mSettings->library.platform_type(tok->str(), platform_type); if (platformtype) { // check for namespace if (tok->strAt(-1) == "::") { const Token * tok1 = tok->tokAt(-2); // skip when non-global namespace defined if (tok1 && tok1->tokType() == Token::eName) continue; tok = tok->previous(); tok->deleteThis(); } Token *typeToken; if (platformtype->mConstPtr) { tok->str("const"); tok->insertToken("*"); tok->insertToken(platformtype->mType); typeToken = tok; } else if (platformtype->mPointer) { tok->str(platformtype->mType); typeToken = tok; tok->insertToken("*"); } else if (platformtype->mPtrPtr) { tok->str(platformtype->mType); typeToken = tok; tok->insertToken("*"); tok->insertToken("*"); } else { tok->originalName(tok->str()); tok->str(platformtype->mType); typeToken = tok; } if (platformtype->mSigned) typeToken->isSigned(true); if (platformtype->mUnsigned) typeToken->isUnsigned(true); if (platformtype->mLong) typeToken->isLong(true); } } } void TokenList::simplifyStdType() { for (Token *tok = front(); tok; tok = tok->next()) { if (Token::Match(tok, "const|extern *|&|%name%") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { if (Token::Match(tok->next(), "%name% !!;")) continue; tok->insertToken("int"); tok->next()->isImplicitInt(true); continue; } if (Token::Match(tok, "char|short|int|long|unsigned|signed|double|float") || (mSettings->standards.c >= Standards::C99 && Token::Match(tok, "complex|_Complex"))) { bool isFloat= false; bool isSigned = false; bool isUnsigned = false; bool isComplex = false; int countLong = 0; Token* typeSpec = nullptr; Token* tok2 = tok; for (; tok2->next(); tok2 = tok2->next()) { if (tok2->str() == "long") { countLong++; if (!isFloat) typeSpec = tok2; } else if (tok2->str() == "short") { typeSpec = tok2; } else if (tok2->str() == "unsigned") isUnsigned = true; else if (tok2->str() == "signed") isSigned = true; else if (Token::Match(tok2, "float|double")) { isFloat = true; typeSpec = tok2; } else if (mSettings->standards.c >= Standards::C99 && Token::Match(tok2, "complex|_Complex")) isComplex = !isFloat || tok2->str() == "_Complex" || Token::Match(tok2->next(), "*|&|%name%"); // Ensure that "complex" is not the variables name else if (Token::Match(tok2, "char|int")) { if (!typeSpec) typeSpec = tok2; } else break; } if (!typeSpec) { // unsigned i; or similar declaration if (!isComplex) { // Ensure that "complex" is not the variables name tok->str("int"); tok->isSigned(isSigned); tok->isUnsigned(isUnsigned); tok->isImplicitInt(true); } } else { typeSpec->isLong(typeSpec->isLong() || (isFloat && countLong == 1) || countLong > 1); typeSpec->isComplex(typeSpec->isComplex() || (isFloat && isComplex)); typeSpec->isSigned(typeSpec->isSigned() || isSigned); typeSpec->isUnsigned(typeSpec->isUnsigned() || isUnsigned); // Remove specifiers const Token* tok3 = tok->previous(); tok2 = tok2->previous(); while (tok3 != tok2) { if (tok2 != typeSpec && (isComplex || !Token::Match(tok2, "complex|_Complex"))) // Ensure that "complex" is not the variables name tok2->deleteThis(); tok2 = tok2->previous(); } } } } } bool TokenList::isKeyword(const std::string &str) const { return mKeywords.find(str) != mKeywords.end(); } cppcheck-2.7/lib/tokenlist.h000066400000000000000000000145611417746362400161220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef tokenlistH #define tokenlistH //--------------------------------------------------------------------------- #include "config.h" #include "token.h" #include "utils.h" #include #include #include #include class Settings; namespace simplecpp { class TokenList; } /// @addtogroup Core /// @{ class CPPCHECKLIB TokenList { public: explicit TokenList(const Settings* settings); ~TokenList(); void setSettings(const Settings *settings) { mSettings = settings; } const Settings *getSettings() const { return mSettings; } /** @return the source file path. e.g. "file.cpp" */ const std::string& getSourceFilePath() const; /** Is the code C. Used for bailouts */ bool isC() const { return mIsC; } /** Is the code CPP. Used for bailouts */ bool isCPP() const { return mIsCpp; } /** * Delete all tokens in given token list * @param tok token list to delete */ static void deleteTokens(Token *tok); void addtoken(std::string str, const nonneg int lineno, const nonneg int column, const nonneg int fileno, bool split = false); void addtoken(std::string str, const Token *locationTok); void addtoken(const Token *tok, const nonneg int lineno, const nonneg int column, const nonneg int fileno); void addtoken(const Token *tok, const Token *locationTok); void addtoken(const Token *tok); static void insertTokens(Token *dest, const Token *src, nonneg int n); /** * Copy tokens. * @param dest destination token where copied tokens will be inserted after * @param first first token to copy * @param last last token to copy * @param one_line true=>copy all tokens to the same line as dest. false=>copy all tokens to dest while keeping the 'line breaks' * @return new location of last token copied */ static Token *copyTokens(Token *dest, const Token *first, const Token *last, bool one_line = true); /** * Create tokens from code. * The code must be preprocessed first: * - multiline strings are not handled. * - UTF in the code are not handled. * - comments are not handled. * @param code input stream for code * @param file0 source file name */ bool createTokens(std::istream &code, const std::string& file0 = emptyString); void createTokens(simplecpp::TokenList&& tokenList); /** Deallocate list */ void deallocateTokens(); /** append file name if seen the first time; return its index in any case */ int appendFileIfNew(const std::string &fileName); /** get first token of list */ const Token *front() const { return mTokensFrontBack.front; } Token *front() { return mTokensFrontBack.front; } /** get last token of list */ const Token *back() const { return mTokensFrontBack.back; } Token *back() { return mTokensFrontBack.back; } /** * Get filenames (the sourcefile + the files it include). * The first filename is the filename for the sourcefile * @return vector with filenames */ const std::vector& getFiles() const { return mFiles; } std::string getOrigFile(const Token *tok) const; /** * get filename for given token * @param tok The given token * @return filename for the given token */ const std::string& file(const Token *tok) const; /** * Get file:line for a given token * @param tok given token * @return location for given token */ std::string fileLine(const Token *tok) const; /** * Calculates a 64-bit checksum of the token list used to compare * multiple token lists with each other as quickly as possible. */ unsigned long long calculateChecksum() const; /** * Create abstract syntax tree. */ void createAst() const; /** * Check abstract syntax tree. * Throws InternalError on failure */ void validateAst() const; /** * Verify that the given token is an element of the tokenlist. * That method is implemented for debugging purposes. * @param[in] tok token to be checked * \return true if token was found in tokenlist, false else. In case of nullptr true is returned. */ bool validateToken(const Token* tok) const; /** * Convert platform dependent types to standard types. * 32 bits: size_t -> unsigned long * 64 bits: size_t -> unsigned long long */ void simplifyPlatformTypes(); /** * Collapse compound standard types into a single token. * unsigned long long int => long _isUnsigned=true,_isLong=true */ void simplifyStdType(); void clangSetOrigFiles(); bool isKeyword(const std::string &str) const; private: /** Disable copy constructor, no implementation */ TokenList(const TokenList &); /** Disable assignment operator, no implementation */ TokenList &operator=(const TokenList &); void determineCppC(); /** Token list */ TokensFrontBack mTokensFrontBack; /** filenames for the tokenized source code (source + included) */ std::vector mFiles; /** Original filenames for the tokenized source code (source + included) */ std::vector mOrigFiles; /** settings */ const Settings* mSettings; std::unordered_set mKeywords; /** File is known to be C/C++ code */ bool mIsC; bool mIsCpp; }; /// @} //--------------------------------------------------------------------------- #endif // tokenlistH cppcheck-2.7/lib/tokenrange.h000066400000000000000000000045411417746362400162400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef tokenrangeH #define tokenrangeH //--------------------------------------------------------------------------- #include "config.h" template )> class TokenRangeBase { T* mFront; T* mBack; public: TokenRangeBase(T* front, T* back) : mFront(front), mBack(back) {} struct TokenIterator { using iterator_category = std::forward_iterator_tag; using value_type = T*; using difference_type = std::ptrdiff_t; using pointer = void; using reference = T*; T* mt; TokenIterator() : mt(nullptr) {} explicit TokenIterator(T* t) : mt(t) {} TokenIterator& operator++() { mt = mt->next(); return *this; } bool operator==(const TokenIterator& b) const { return mt == b.mt; } bool operator!=(const TokenIterator& b) const { return mt != b.mt; } T* operator*() const { return mt; } }; TokenIterator begin() const { return TokenIterator(mFront); } TokenIterator end() const { return TokenIterator(mBack); } }; class TokenRange : public TokenRangeBase { public: TokenRange(Token* front, Token* back) : TokenRangeBase(front, back) {} }; class ConstTokenRange : public TokenRangeBase { public: ConstTokenRange(const Token* front, const Token* back) : TokenRangeBase(front, back) {} }; #endif // tokenrangeH cppcheck-2.7/lib/utils.cpp000066400000000000000000000073601417746362400156000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "utils.h" #include #include #include #include #include int caseInsensitiveStringCompare(const std::string &lhs, const std::string &rhs) { if (lhs.size() != rhs.size()) return (lhs.size() < rhs.size()) ? -1 : 1; for (unsigned int i = 0; i < lhs.size(); ++i) { const int c1 = std::toupper(lhs[i]); const int c2 = std::toupper(rhs[i]); if (c1 != c2) return (c1 < c2) ? -1 : 1; } return 0; } bool isValidGlobPattern(const std::string& pattern) { for (std::string::const_iterator i = pattern.begin(); i != pattern.end(); ++i) { if (*i == '*' || *i == '?') { std::string::const_iterator j = i + 1; if (j != pattern.end() && (*j == '*' || *j == '?')) { return false; } } } return true; } bool matchglob(const std::string& pattern, const std::string& name) { const char* p = pattern.c_str(); const char* n = name.c_str(); std::stack> backtrack; for (;;) { bool matching = true; while (*p != '\0' && matching) { switch (*p) { case '*': // Step forward until we match the next character after * while (*n != '\0' && *n != p[1]) { n++; } if (*n != '\0') { // If this isn't the last possibility, save it for later backtrack.push(std::make_pair(p, n)); } break; case '?': // Any character matches unless we're at the end of the name if (*n != '\0') { n++; } else { matching = false; } break; default: // Non-wildcard characters match literally if (*n == *p) { n++; } else if (*n == '\\' && *p == '/') { n++; } else if (*n == '/' && *p == '\\') { n++; } else { matching = false; } break; } p++; } // If we haven't failed matching and we've reached the end of the name, then success if (matching && *n == '\0') { return true; } // If there are no other paths to try, then fail if (backtrack.empty()) { return false; } // Restore pointers from backtrack stack p = backtrack.top().first; n = backtrack.top().second; backtrack.pop(); // Advance name pointer by one because the current position didn't work n++; } } bool matchglobs(const std::vector &patterns, const std::string &name) { return std::any_of(begin(patterns), end(patterns), [&name](const std::string &pattern) { return matchglob(pattern, name); }); } cppcheck-2.7/lib/utils.h000066400000000000000000000113201417746362400152340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef utilsH #define utilsH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include #include struct SelectMapKeys { template typename Pair::first_type operator()(const Pair& p) const { return p.first; } }; struct SelectMapValues { template typename Pair::second_type operator()(const Pair& p) const { return p.second; } }; template bool contains(const Range& r, const T& x) { return std::find(r.begin(), r.end(), x) != r.end(); } template bool contains(const std::initializer_list& r, const T& x) { return std::find(r.begin(), r.end(), x) != r.end(); } template bool contains(const std::initializer_list& r, const U& x) { return std::find(r.begin(), r.end(), x) != r.end(); } // Enum hash for C++11. This is not needed in C++14 struct EnumClassHash { template std::size_t operator()(T t) const { return static_cast(t); } }; inline bool endsWith(const std::string &str, char c) { return !str.empty() && str.back() == c; } inline bool endsWith(const std::string &str, const char end[], std::size_t endlen) { return (str.size() >= endlen) && (str.compare(str.size()-endlen, endlen, end)==0); } template bool endsWith(const std::string& str, const char (&end)[N]) { return endsWith(str, end, N - 1); } inline static bool isPrefixStringCharLiteral(const std::string &str, char q, const std::string& p) { if (!endsWith(str, q)) return false; if ((str.length() + 1) > p.length() && (str.compare(0, p.size() + 1, p + q) == 0)) return true; return false; } inline static bool isStringCharLiteral(const std::string &str, char q) { static const std::vector suffixes{"", "u8", "u", "U", "L"}; for (const std::string & p: suffixes) { if (isPrefixStringCharLiteral(str, q, p)) return true; } return false; } inline static bool isStringLiteral(const std::string &str) { return isStringCharLiteral(str, '"'); } inline static bool isCharLiteral(const std::string &str) { return isStringCharLiteral(str, '\''); } inline static std::string getStringCharLiteral(const std::string &str, char q) { const std::size_t quotePos = str.find(q); return str.substr(quotePos + 1U, str.size() - quotePos - 2U); } inline static std::string getStringLiteral(const std::string &str) { if (isStringLiteral(str)) return getStringCharLiteral(str, '"'); return ""; } inline static std::string getCharLiteral(const std::string &str) { if (isCharLiteral(str)) return getStringCharLiteral(str, '\''); return ""; } inline static const char *getOrdinalText(int i) { if (i == 1) return "st"; if (i == 2) return "nd"; if (i == 3) return "rd"; return "th"; } CPPCHECKLIB int caseInsensitiveStringCompare(const std::string& lhs, const std::string& rhs); CPPCHECKLIB bool isValidGlobPattern(const std::string& pattern); CPPCHECKLIB bool matchglob(const std::string& pattern, const std::string& name); CPPCHECKLIB bool matchglobs(const std::vector &patterns, const std::string &name); #define UNUSED(x) (void)(x) // Use the nonneg macro when you want to assert that a variable/argument is not negative #ifdef __CPPCHECK__ #define nonneg __cppcheck_low__(0) #elif defined(NONNEG) // Enable non-negative values checking // TODO : investigate using annotations/contracts for stronger value checking #define nonneg unsigned #else // Disable non-negative values checking #define nonneg #endif #if defined(__has_feature) #if __has_feature(address_sanitizer) #define ASAN 1 #endif #endif #ifndef ASAN #ifdef __SANITIZE_ADDRESS__ #define ASAN 1 #else #define ASAN 0 #endif #endif #endif cppcheck-2.7/lib/valueflow.cpp000066400000000000000000012173231417746362400164470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * @brief This is the ValueFlow component in Cppcheck. * * Each @sa Token in the token list has a list of values. These are * the "possible" values for the Token at runtime. * * In the --debug and --debug-normal output you can see the ValueFlow data. For example: * * int f() * { * int x = 10; * return 4 * x + 2; * } * * The --debug-normal output says: * * ##Value flow * Line 3 * 10 always 10 * Line 4 * 4 always 4 * * always 40 * x always 10 * + always 42 * 2 always 2 * * All value flow analysis is executed in the ValueFlow::setValues() function. The ValueFlow analysis is executed after * the tokenizer/ast/symboldatabase/etc.. The ValueFlow analysis is done in a series of valueFlow* function calls, where * each such function call can only use results from previous function calls. The function calls should be arranged so * that valueFlow* that do not require previous ValueFlow information should be first. * * Type of analysis * ================ * * This is "flow sensitive" value flow analysis. We _usually_ track the value for 1 variable at a time. * * How are calculations handled * ============================ * * Here is an example code: * * x = 3 + 4; * * The valueFlowNumber set the values for the "3" and "4" tokens by calling setTokenValue(). * The setTokenValue() handle the calculations automatically. When both "3" and "4" have values, the "+" can be * calculated. setTokenValue() recursively calls itself when parents in calculations can be calculated. * * Forward / Reverse flow analysis * =============================== * * In forward value flow analysis we know a value and see what happens when we are stepping the program forward. Like * normal execution. The valueFlowForward is used in this analysis. * * In reverse value flow analysis we know the value of a variable at line X. And try to "execute backwards" to determine * possible values before line X. The valueFlowReverse is used in this analysis. * * */ #include "valueflow.h" #include "analyzer.h" #include "astutils.h" #include "calculate.h" #include "checkuninitvar.h" #include "config.h" #include "errorlogger.h" #include "errortypes.h" #include "forwardanalyzer.h" #include "infer.h" #include "library.h" #include "mathlib.h" #include "path.h" #include "platform.h" #include "programmemory.h" #include "reverseanalyzer.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenlist.h" #include "utils.h" #include "valueptr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void bailoutInternal(const std::string& type, TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, std::string function) { if (function.find("operator") != std::string::npos) function = "(valueFlow)"; std::list callstack(1, ErrorMessage::FileLocation(tok, tokenlist)); ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::debug, Path::stripDirectoryPart(file) + ":" + MathLib::toString(line) + ":" + function + " bailout: " + what, type, Certainty::normal); errorLogger->reportErr(errmsg); } #if (defined __cplusplus) && __cplusplus >= 201103L #define bailout2(type, tokenlist, errorLogger, tok, what) bailoutInternal(type, tokenlist, errorLogger, tok, what, __FILE__, __LINE__, __func__) #elif (defined __GNUC__) || (defined __clang__) || (defined _MSC_VER) #define bailout2(type, tokenlist, errorLogger, tok, what) bailoutInternal(type, tokenlist, errorLogger, tok, what, __FILE__, __LINE__, __FUNCTION__) #else #define bailout2(type, tokenlist, errorLogger, tok, what) bailoutInternal(type, tokenlist, errorLogger, tok, what, __FILE__, __LINE__, "(valueFlow)") #endif #define bailout(tokenlist, errorLogger, tok, what) bailout2("valueFlowBailout", tokenlist, errorLogger, tok, what) #define bailoutIncompleteVar(tokenlist, errorLogger, tok, what) bailout2("valueFlowBailoutIncompleteVar", tokenlist, errorLogger, tok, what) static void changeKnownToPossible(std::list &values, int indirect=-1) { for (ValueFlow::Value& v: values) { if (indirect >= 0 && v.indirect != indirect) continue; v.changeKnownToPossible(); } } static void removeImpossible(std::list& values, int indirect = -1) { values.remove_if([&](const ValueFlow::Value& v) { if (indirect >= 0 && v.indirect != indirect) return false; return v.isImpossible(); }); } static void lowerToPossible(std::list& values, int indirect = -1) { changeKnownToPossible(values, indirect); removeImpossible(values, indirect); } static void changePossibleToKnown(std::list& values, int indirect = -1) { for (ValueFlow::Value& v : values) { if (indirect >= 0 && v.indirect != indirect) continue; if (!v.isPossible()) continue; if (v.bound != ValueFlow::Value::Bound::Point) continue; v.setKnown(); } } static void setValueUpperBound(ValueFlow::Value& value, bool upper) { if (upper) value.bound = ValueFlow::Value::Bound::Upper; else value.bound = ValueFlow::Value::Bound::Lower; } static void setValueBound(ValueFlow::Value& value, const Token* tok, bool invert) { if (Token::Match(tok, "<|<=")) { setValueUpperBound(value, !invert); } else if (Token::Match(tok, ">|>=")) { setValueUpperBound(value, invert); } } static void setConditionalValues(const Token* tok, bool lhs, MathLib::bigint value, ValueFlow::Value& true_value, ValueFlow::Value& false_value) { if (Token::Match(tok, "==|!=|>=|<=")) { true_value = ValueFlow::Value{tok, value}; const char* greaterThan = ">="; const char* lessThan = "<="; if (lhs) std::swap(greaterThan, lessThan); if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) { false_value = ValueFlow::Value{tok, value - 1}; } else if (Token::simpleMatch(tok, lessThan, strlen(lessThan))) { false_value = ValueFlow::Value{tok, value + 1}; } else { false_value = ValueFlow::Value{tok, value}; } } else { const char* greaterThan = ">"; const char* lessThan = "<"; if (lhs) std::swap(greaterThan, lessThan); if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) { true_value = ValueFlow::Value{tok, value + 1}; false_value = ValueFlow::Value{tok, value}; } else if (Token::simpleMatch(tok, lessThan, strlen(lessThan))) { true_value = ValueFlow::Value{tok, value - 1}; false_value = ValueFlow::Value{tok, value}; } } setValueBound(true_value, tok, lhs); setValueBound(false_value, tok, !lhs); } static bool isSaturated(MathLib::bigint value) { return value == std::numeric_limits::max() || value == std::numeric_limits::min(); } const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value, const std::function(const Token*)>& evaluate) { if (!tok->astOperand1() || !tok->astOperand2()) return nullptr; if (tok->isComparisonOp()) { std::vector value1 = evaluate(tok->astOperand1()); std::vector value2 = evaluate(tok->astOperand2()); if (!value1.empty() && !value2.empty()) { if (tok->astOperand1()->hasKnownIntValue()) value2.clear(); if (tok->astOperand2()->hasKnownIntValue()) value1.clear(); } if (!value1.empty()) { if (isSaturated(value1.front())) return nullptr; setConditionalValues(tok, true, value1.front(), true_value, false_value); return tok->astOperand2(); } else if (!value2.empty()) { if (isSaturated(value2.front())) return nullptr; setConditionalValues(tok, false, value2.front(), true_value, false_value); return tok->astOperand1(); } } return nullptr; } const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value) { return parseCompareInt(tok, true_value, false_value, [](const Token* t) -> std::vector { if (t->hasKnownIntValue()) return {t->values().front().intvalue}; return std::vector{}; }); } static bool isEscapeScope(const Token* tok, TokenList * tokenlist, bool unknown = false) { if (!Token::simpleMatch(tok, "{")) return false; // TODO this search for termTok in all subscopes. It should check the end of the scope. const Token * termTok = Token::findmatch(tok, "return|continue|break|throw|goto", tok->link()); if (termTok && termTok->scope() == tok->scope()) return true; std::string unknownFunction; if (tokenlist && tokenlist->getSettings()->library.isScopeNoReturn(tok->link(), &unknownFunction)) return unknownFunction.empty() || unknown; return false; } static ValueFlow::Value castValue(ValueFlow::Value value, const ValueType::Sign sign, nonneg int bit) { if (value.isFloatValue()) { value.valueType = ValueFlow::Value::ValueType::INT; if (value.floatValue >= std::numeric_limits::min() && value.floatValue <= std::numeric_limits::max()) { value.intvalue = value.floatValue; } else { // don't perform UB value.intvalue = 0; } } if (bit < MathLib::bigint_bits) { const MathLib::biguint one = 1; value.intvalue &= (one << bit) - 1; if (sign == ValueType::Sign::SIGNED && value.intvalue & (one << (bit - 1))) { value.intvalue |= ~((one << bit) - 1ULL); } } return value; } static bool isNumeric(const ValueFlow::Value& value) { return value.isIntValue() || value.isFloatValue(); } static void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result) { if (value1.isKnown() && value2.isKnown()) result->setKnown(); else if (value1.isImpossible() || value2.isImpossible()) result->setImpossible(); else if (value1.isInconclusive() || value2.isInconclusive()) result->setInconclusive(); else result->setPossible(); if (value1.isSymbolicValue()) { result->valueType = value1.valueType; result->tokvalue = value1.tokvalue; } if (value2.isSymbolicValue()) { result->valueType = value2.valueType; result->tokvalue = value2.tokvalue; } if (value1.isIteratorValue()) result->valueType = value1.valueType; if (value2.isIteratorValue()) result->valueType = value2.valueType; result->condition = value1.condition ? value1.condition : value2.condition; result->varId = (value1.varId != 0) ? value1.varId : value2.varId; result->varvalue = (result->varId == value1.varId) ? value1.varvalue : value2.varvalue; result->errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath; result->safe = value1.safe || value2.safe; if (value1.bound == ValueFlow::Value::Bound::Point || value2.bound == ValueFlow::Value::Bound::Point) { if (value1.bound == ValueFlow::Value::Bound::Upper || value2.bound == ValueFlow::Value::Bound::Upper) result->bound = ValueFlow::Value::Bound::Upper; if (value1.bound == ValueFlow::Value::Bound::Lower || value2.bound == ValueFlow::Value::Bound::Lower) result->bound = ValueFlow::Value::Bound::Lower; } if (value1.path != value2.path) result->path = -1; else result->path = value1.path; } static const Token *getCastTypeStartToken(const Token *parent) { // TODO: This might be a generic utility function? if (!Token::Match(parent, "{|(")) return nullptr; // Functional cast if (parent->isBinaryOp() && Token::Match(parent->astOperand1(), "%type% (|{") && parent->astOperand1()->tokType() == Token::eType && astIsPrimitive(parent)) return parent->astOperand1(); if (parent->str() != "(") return nullptr; if (!parent->astOperand2() && Token::Match(parent,"( %name%")) return parent->next(); if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) return parent->astOperand1()->tokAt(2); return nullptr; } // does the operation cause a loss of information? static bool isNonInvertibleOperation(const Token* tok) { return tok->isComparisonOp() || Token::Match(tok, "%|/|&|%or%|<<|>>"); } static bool isComputableValue(const Token* parent, const ValueFlow::Value& value) { const bool noninvertible = isNonInvertibleOperation(parent); if (noninvertible && value.isImpossible()) return false; if (!value.isIntValue() && !value.isFloatValue() && !value.isTokValue() && !value.isIteratorValue()) return false; if (value.isIteratorValue() && !Token::Match(parent, "+|-")) return false; if (value.isTokValue() && (!parent->isComparisonOp() || value.tokvalue->tokType() != Token::eString)) return false; return true; } /** Set token value for cast */ static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings); static bool isCompatibleValueTypes(ValueFlow::Value::ValueType x, ValueFlow::Value::ValueType y) { static const std::unordered_map, EnumClassHash> compatibleTypes = { {ValueFlow::Value::ValueType::INT, {ValueFlow::Value::ValueType::FLOAT, ValueFlow::Value::ValueType::SYMBOLIC, ValueFlow::Value::ValueType::TOK}}, {ValueFlow::Value::ValueType::FLOAT, {ValueFlow::Value::ValueType::INT}}, {ValueFlow::Value::ValueType::TOK, {ValueFlow::Value::ValueType::INT}}, {ValueFlow::Value::ValueType::ITERATOR_START, {ValueFlow::Value::ValueType::INT}}, {ValueFlow::Value::ValueType::ITERATOR_END, {ValueFlow::Value::ValueType::INT}}, }; if (x == y) return true; auto it = compatibleTypes.find(x); if (it == compatibleTypes.end()) return false; return it->second.count(y) > 0; } static bool isCompatibleValues(const ValueFlow::Value& value1, const ValueFlow::Value& value2) { if (value1.isSymbolicValue() && value2.isSymbolicValue() && value1.tokvalue->exprId() != value2.tokvalue->exprId()) return false; if (!isCompatibleValueTypes(value1.valueType, value2.valueType)) return false; if (value1.isKnown() || value2.isKnown()) return true; if (value1.isImpossible() || value2.isImpossible()) return false; if (value1.varId == 0 || value2.varId == 0) return true; if (value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue()) return true; return false; } static ValueFlow::Value truncateImplicitConversion(Token* parent, const ValueFlow::Value& value, const Settings* settings) { if (!value.isIntValue() && !value.isFloatValue()) return value; if (!parent) return value; if (!parent->isBinaryOp()) return value; if (!parent->isConstOp()) return value; if (!astIsIntegral(parent->astOperand1(), false)) return value; if (!astIsIntegral(parent->astOperand2(), false)) return value; const ValueType* vt1 = parent->astOperand1()->valueType(); const ValueType* vt2 = parent->astOperand2()->valueType(); // If the sign is the same there is no truncation if (vt1->sign == vt2->sign) return value; size_t n1 = ValueFlow::getSizeOf(*vt1, settings); size_t n2 = ValueFlow::getSizeOf(*vt2, settings); ValueType::Sign sign = ValueType::Sign::UNSIGNED; if (n1 < n2) sign = vt2->sign; else if (n1 > n2) sign = vt1->sign; ValueFlow::Value v = castValue(value, sign, std::max(n1, n2) * 8); v.wideintvalue = value.intvalue; return v; } /** set ValueFlow value and perform calculations if possible */ static void setTokenValue(Token* tok, ValueFlow::Value value, const Settings* settings) { // Skip setting values that are too big since its ambiguous if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) && ValueFlow::getSizeOf(*tok->valueType(), settings) >= sizeof(MathLib::bigint)) return; if (!value.isImpossible() && value.isIntValue()) value = truncateImplicitConversion(tok->astParent(), value, settings); if (!tok->addValue(value)) return; if (value.path < 0) return; Token *parent = tok->astParent(); if (!parent) return; if (Token::simpleMatch(parent, "=") && astIsRHS(tok) && !value.isLifetimeValue()) { setTokenValue(parent, value, settings); return; } if (value.isContainerSizeValue()) { // .empty, .size, +"abc", +'a' if (Token::Match(parent, "+|==|!=") && parent->astOperand1() && parent->astOperand2()) { for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { if (value1.isImpossible()) continue; for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { if (value2.isImpossible()) continue; if (value1.path != value2.path) continue; ValueFlow::Value result; if (Token::Match(parent, "%comp%")) result.valueType = ValueFlow::Value::ValueType::INT; else result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; if (value1.isContainerSizeValue() && value2.isContainerSizeValue()) result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue); else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString) result.intvalue = calculate(parent->str(), value1.intvalue, MathLib::bigint(Token::getStrLength(value2.tokvalue))); else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString) result.intvalue = calculate(parent->str(), MathLib::bigint(Token::getStrLength(value1.tokvalue)), value2.intvalue); else continue; combineValueProperties(value1, value2, &result); if (Token::simpleMatch(parent, "==") && result.intvalue) continue; if (Token::simpleMatch(parent, "!=") && !result.intvalue) continue; setTokenValue(parent, result, settings); } } } else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) && parent->astOperand1() && parent->astOperand1()->valueType()) { const Library::Container* c = getLibraryContainer(parent->astOperand1()); const Library::Container::Yield yields = c ? c->getYield(parent->strAt(1)) : Library::Container::Yield::NO_YIELD; if (yields == Library::Container::Yield::SIZE) { ValueFlow::Value v(value); v.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(parent->astParent(), v, settings); } else if (yields == Library::Container::Yield::EMPTY) { ValueFlow::Value v(value); v.intvalue = !v.intvalue; v.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(parent->astParent(), v, settings); } } else if (Token::Match(parent->previous(), "%name% (")) { if (const Library::Function* f = settings->library.getFunction(parent->previous())) { if (f->containerYield == Library::Container::Yield::SIZE) { ValueFlow::Value v(value); v.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(parent, v, settings); } else if (f->containerYield == Library::Container::Yield::EMPTY) { ValueFlow::Value v(value); v.intvalue = !v.intvalue; v.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(parent, v, settings); } } } return; } if (value.isLifetimeValue()) { if (!isLifetimeBorrowed(parent, settings)) return; if (value.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator && astIsIterator(parent)) { setTokenValue(parent,value,settings); } else if (astIsPointer(tok) && astIsPointer(parent) && !parent->isUnaryOp("*") && (parent->isArithmeticalOp() || parent->isCast())) { setTokenValue(parent,value,settings); } return; } if (value.isUninitValue()) { if (Token::Match(tok, ". %var%")) setTokenValue(tok->next(), value, settings); ValueFlow::Value pvalue = value; if (!value.subexpressions.empty() && Token::Match(parent, ". %var%")) { if (contains(value.subexpressions, parent->next()->str())) pvalue.subexpressions.clear(); else return; } if (parent->isUnaryOp("&")) { pvalue.indirect++; setTokenValue(parent, pvalue, settings); } else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok) { if (parent->originalName() == "->" && pvalue.indirect > 0) pvalue.indirect--; setTokenValue(parent->astOperand2(), pvalue, settings); } else if (Token::Match(parent->astParent(), ". %var%") && parent->astParent()->astOperand1() == parent) { if (parent->astParent()->originalName() == "->" && pvalue.indirect > 0) pvalue.indirect--; setTokenValue(parent->astParent()->astOperand2(), pvalue, settings); } else if (parent->isUnaryOp("*") && pvalue.indirect > 0) { pvalue.indirect--; setTokenValue(parent, pvalue, settings); } return; } // cast.. if (const Token *castType = getCastTypeStartToken(parent)) { if (((tok->valueType() == nullptr && value.isImpossible()) || astIsPointer(tok)) && contains({ValueFlow::Value::ValueType::INT, ValueFlow::Value::ValueType::SYMBOLIC}, value.valueType) && Token::simpleMatch(parent->astOperand1(), "dynamic_cast")) return; const ValueType &valueType = ValueType::parseDecl(castType, settings); if (value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) && valueType.sign == ValueType::SIGNED && tok->valueType() && ValueFlow::getSizeOf(*tok->valueType(), settings) >= ValueFlow::getSizeOf(valueType, settings)) return; setTokenValueCast(parent, valueType, value, settings); } else if (parent->str() == ":") { setTokenValue(parent,value,settings); } else if (parent->str() == "?" && tok->str() == ":" && tok == parent->astOperand2() && parent->astOperand1()) { // is condition always true/false? if (parent->astOperand1()->hasKnownValue()) { const ValueFlow::Value &condvalue = parent->astOperand1()->values().front(); const bool cond(condvalue.isTokValue() || (condvalue.isIntValue() && condvalue.intvalue != 0)); if (cond && !tok->astOperand1()) { // true condition, no second operator setTokenValue(parent, condvalue, settings); } else { const Token *op = cond ? tok->astOperand1() : tok->astOperand2(); if (!op) // #7769 segmentation fault at setTokenValue() return; const std::list &values = op->values(); if (std::find(values.begin(), values.end(), value) != values.end()) setTokenValue(parent, value, settings); } } else if (!value.isImpossible()) { // is condition only depending on 1 variable? // cppcheck-suppress[variableScope] #8541 nonneg int varId = 0; bool ret = false; visitAstNodes(parent->astOperand1(), [&](const Token *t) { if (t->varId()) { if (varId > 0 || value.varId != 0) ret = true; varId = t->varId(); } else if (t->str() == "(" && Token::Match(t->previous(), "%name%")) ret = true; // function call return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (ret) return; ValueFlow::Value v(value); v.conditional = true; v.changeKnownToPossible(); setTokenValue(parent, v, settings); } } else if (parent->str() == "?" && value.isIntValue() && tok == parent->astOperand1() && value.isKnown() && parent->astOperand2() && parent->astOperand2()->astOperand1() && parent->astOperand2()->astOperand2()) { const std::list &values = (value.intvalue == 0 ? parent->astOperand2()->astOperand2()->values() : parent->astOperand2()->astOperand1()->values()); for (const ValueFlow::Value &v : values) setTokenValue(parent, v, settings); } // Calculations.. else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp) || (parent->tokType() == Token::eLogicalOp)) && parent->astOperand1() && parent->astOperand2()) { const bool noninvertible = isNonInvertibleOperation(parent); // Skip operators with impossible values that are not invertible if (noninvertible && value.isImpossible()) return; // known result when a operand is 0. if (Token::Match(parent, "[&*]") && value.isKnown() && value.isIntValue() && value.intvalue==0) { setTokenValue(parent, value, settings); return; } // known result when a operand is true. if (Token::simpleMatch(parent, "&&") && value.isKnown() && value.isIntValue() && value.intvalue==0) { setTokenValue(parent, value, settings); return; } // known result when a operand is false. if (Token::simpleMatch(parent, "||") && value.isKnown() && value.isIntValue() && value.intvalue!=0) { setTokenValue(parent, value, settings); return; } for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { if (!isComputableValue(parent, value1)) continue; for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { if (value1.path != value2.path) continue; if (!isComputableValue(parent, value2)) continue; if (value1.isIteratorValue() && value2.isIteratorValue()) continue; if (!isCompatibleValues(value1, value2)) continue; ValueFlow::Value result(0); combineValueProperties(value1, value2, &result); if (astIsFloat(parent, false)) { if (!result.isIntValue() && !result.isFloatValue()) continue; result.valueType = ValueFlow::Value::ValueType::FLOAT; } const double floatValue1 = value1.isFloatValue() ? value1.floatValue : value1.intvalue; const double floatValue2 = value2.isFloatValue() ? value2.floatValue : value2.intvalue; const MathLib::bigint intValue1 = value1.isFloatValue() ? static_cast(value1.floatValue) : value1.intvalue; const MathLib::bigint intValue2 = value2.isFloatValue() ? static_cast(value2.floatValue) : value2.intvalue; if ((value1.isFloatValue() || value2.isFloatValue()) && Token::Match(parent, "&|^|%|<<|>>|==|!=|%or%")) continue; if (Token::Match(parent, "==|!=")) { if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) { if (parent->str() == "==") result.intvalue = 0; else if (parent->str() == "!=") result.intvalue = 1; } else if (value1.isIntValue() && value2.isIntValue()) { bool error = false; result.intvalue = calculate(parent->str(), intValue1, intValue2, &error); if (error) continue; } else { continue; } setTokenValue(parent, result, settings); } else if (Token::Match(parent, "%op%")) { if (Token::Match(parent, "%comp%")) { if (!result.isFloatValue() && !value1.isIntValue() && !value2.isIntValue()) continue; } else { if (value1.isTokValue() || value2.isTokValue()) break; } bool error = false; if (result.isFloatValue()) { result.floatValue = calculate(parent->str(), floatValue1, floatValue2, &error); } else { result.intvalue = calculate(parent->str(), intValue1, intValue2, &error); } if (error) continue; // If the bound comes from the second value then invert the bound when subtracting if (Token::simpleMatch(parent, "-") && value2.bound == result.bound && value2.bound != ValueFlow::Value::Bound::Point) result.invertBound(); setTokenValue(parent, result, settings); } } } } // ! else if (parent->str() == "!") { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue()) continue; if (val.isImpossible() && val.intvalue != 0) continue; ValueFlow::Value v(val); v.intvalue = !v.intvalue; setTokenValue(parent, v, settings); } } // ~ else if (parent->str() == "~") { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue()) continue; ValueFlow::Value v(val); v.intvalue = ~v.intvalue; int bits = 0; if (settings && tok->valueType() && tok->valueType()->sign == ValueType::Sign::UNSIGNED && tok->valueType()->pointer == 0) { if (tok->valueType()->type == ValueType::Type::INT) bits = settings->int_bit; else if (tok->valueType()->type == ValueType::Type::LONG) bits = settings->long_bit; } if (bits > 0 && bits < MathLib::bigint_bits) v.intvalue &= (((MathLib::biguint)1)<isUnaryOp("-")) { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue() && !val.isFloatValue()) continue; ValueFlow::Value v(val); if (v.isIntValue()) { if (v.intvalue == LLONG_MIN) // Value can't be inverted continue; v.intvalue = -v.intvalue; } else v.floatValue = -v.floatValue; v.invertBound(); setTokenValue(parent, v, settings); } } // increment else if (parent->str() == "++") { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue()) continue; ValueFlow::Value v(val); if (parent == tok->previous()) { if (v.isIntValue() || v.isSymbolicValue()) v.intvalue = v.intvalue + 1; else v.floatValue = v.floatValue + 1.0; } setTokenValue(parent, v, settings); } } // decrement else if (parent->str() == "--") { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue()) continue; ValueFlow::Value v(val); if (parent == tok->previous()) { if (v.isIntValue() || v.isSymbolicValue()) v.intvalue = v.intvalue - 1; else v.floatValue = v.floatValue - 1.0; } setTokenValue(parent, v, settings); } } // Array element else if (parent->str() == "[" && parent->isBinaryOp()) { for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { if (!value1.isTokValue()) continue; for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { if (!value2.isIntValue()) continue; if (value1.varId == 0 || value2.varId == 0 || (value1.varId == value2.varId && value1.varvalue == value2.varvalue)) { ValueFlow::Value result(0); result.condition = value1.condition ? value1.condition : value2.condition; result.setInconclusive(value1.isInconclusive() | value2.isInconclusive()); result.varId = (value1.varId != 0) ? value1.varId : value2.varId; result.varvalue = (result.varId == value1.varId) ? value1.intvalue : value2.intvalue; if (value1.valueKind == value2.valueKind) result.valueKind = value1.valueKind; if (value1.tokvalue->tokType() == Token::eString) { const std::string s = value1.tokvalue->strValue(); const MathLib::bigint index = value2.intvalue; if (index == s.size()) { result.intvalue = 0; setTokenValue(parent, result, settings); } else if (index >= 0 && index < s.size()) { result.intvalue = s[index]; setTokenValue(parent, result, settings); } } else if (value1.tokvalue->str() == "{") { MathLib::bigint index = value2.intvalue; const Token *element = value1.tokvalue->next(); while (index > 0 && element->str() != "}") { if (element->str() == ",") --index; if (Token::Match(element, "[{}()[]]")) break; element = element->next(); } if (Token::Match(element, "%num% [,}]")) { result.intvalue = MathLib::toLongNumber(element->str()); setTokenValue(parent, result, settings); } } } } } } else if (Token::Match(parent, ":: %name%") && parent->astOperand2() == tok) { setTokenValue(parent, value, settings); } // Calling std::size or std::empty on an array else if (value.isTokValue() && Token::simpleMatch(value.tokvalue, "{") && tok->variable() && tok->variable()->isArray() && Token::Match(parent->previous(), "%name% (") && astIsRHS(tok)) { std::vector args = getArguments(value.tokvalue); if (const Library::Function* f = settings->library.getFunction(parent->previous())) { if (f->containerYield == Library::Container::Yield::SIZE) { ValueFlow::Value v(value); v.valueType = ValueFlow::Value::ValueType::INT; v.intvalue = args.size(); setTokenValue(parent, v, settings); } else if (f->containerYield == Library::Container::Yield::EMPTY) { ValueFlow::Value v(value); v.intvalue = args.empty(); v.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(parent, v, settings); } } } } static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings) { if (valueType.pointer || value.isImpossible()) setTokenValue(parent,value,settings); else if (valueType.type == ValueType::Type::CHAR) setTokenValue(parent, castValue(value, valueType.sign, settings->char_bit), settings); else if (valueType.type == ValueType::Type::SHORT) setTokenValue(parent, castValue(value, valueType.sign, settings->short_bit), settings); else if (valueType.type == ValueType::Type::INT) setTokenValue(parent, castValue(value, valueType.sign, settings->int_bit), settings); else if (valueType.type == ValueType::Type::LONG) setTokenValue(parent, castValue(value, valueType.sign, settings->long_bit), settings); else if (valueType.type == ValueType::Type::LONGLONG) setTokenValue(parent, castValue(value, valueType.sign, settings->long_long_bit), settings); else if (valueType.isFloat() && isNumeric(value)) { ValueFlow::Value floatValue = value; floatValue.valueType = ValueFlow::Value::ValueType::FLOAT; if (value.isIntValue()) floatValue.floatValue = value.intvalue; setTokenValue(parent, floatValue, settings); } else if (value.isIntValue()) { const long long charMax = settings->signedCharMax(); const long long charMin = settings->signedCharMin(); if (charMin <= value.intvalue && value.intvalue <= charMax) { // unknown type, but value is small so there should be no truncation etc setTokenValue(parent,value,settings); } } } static nonneg int getSizeOfType(const Token *typeTok, const Settings *settings) { const ValueType &valueType = ValueType::parseDecl(typeTok, settings); if (valueType.pointer > 0) return settings->sizeof_pointer; if (valueType.type == ValueType::Type::BOOL || valueType.type == ValueType::Type::CHAR) return 1; if (valueType.type == ValueType::Type::SHORT) return settings->sizeof_short; if (valueType.type == ValueType::Type::INT) return settings->sizeof_int; if (valueType.type == ValueType::Type::LONG) return settings->sizeof_long; if (valueType.type == ValueType::Type::LONGLONG) return settings->sizeof_long_long; if (valueType.type == ValueType::Type::WCHAR_T) return settings->sizeof_wchar_t; return 0; } size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings *settings) { if (vt.pointer) return settings->sizeof_pointer; if (vt.type == ValueType::Type::CHAR) return 1; if (vt.type == ValueType::Type::SHORT) return settings->sizeof_short; if (vt.type == ValueType::Type::WCHAR_T) return settings->sizeof_wchar_t; if (vt.type == ValueType::Type::INT) return settings->sizeof_int; if (vt.type == ValueType::Type::LONG) return settings->sizeof_long; if (vt.type == ValueType::Type::LONGLONG) return settings->sizeof_long_long; if (vt.type == ValueType::Type::FLOAT) return settings->sizeof_float; if (vt.type == ValueType::Type::DOUBLE) return settings->sizeof_double; if (vt.type == ValueType::Type::LONGDOUBLE) return settings->sizeof_long_double; return 0; } // Handle various constants.. static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, bool cpp) { if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) { try { ValueFlow::Value value(MathLib::toLongNumber(tok->str())); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } catch (const std::exception & /*e*/) { // Bad character literal } } else if (tok->isNumber() && MathLib::isFloat(tok->str())) { ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::FLOAT; value.floatValue = MathLib::toDoubleNumber(tok->str()); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } else if (tok->enumerator() && tok->enumerator()->value_known) { ValueFlow::Value value(tok->enumerator()->value); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } else if (tok->str() == "NULL" || (cpp && tok->str() == "nullptr")) { ValueFlow::Value value(0); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } else if (Token::simpleMatch(tok, "sizeof (")) { if (tok->next()->astOperand2() && !tok->next()->astOperand2()->isLiteral() && tok->next()->astOperand2()->valueType() && tok->next()->astOperand2()->valueType()->pointer == 0 && // <- TODO this is a bailout, abort when there are array->pointer conversions !tok->next()->astOperand2()->valueType()->isEnum()) { // <- TODO this is a bailout, handle enum with non-int types const size_t sz = ValueFlow::getSizeOf(*tok->next()->astOperand2()->valueType(), settings); if (sz) { ValueFlow::Value value(sz); value.setKnown(); setTokenValue(tok->next(), value, settings); return tok->linkAt(1); } } const Token *tok2 = tok->tokAt(2); // skip over tokens to find variable or type while (Token::Match(tok2, "%name% ::|.|[")) { if (tok2->next()->str() == "[") tok2 = tok2->linkAt(1)->next(); else tok2 = tok2->tokAt(2); } if (Token::simpleMatch(tok, "sizeof ( *")) { const ValueType *vt = tok->tokAt(2)->valueType(); const size_t sz = vt ? ValueFlow::getSizeOf(*vt, settings) : 0; if (sz > 0) { ValueFlow::Value value(sz); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok->next(), value, settings); } } else if (tok2->enumerator() && tok2->enumerator()->scope) { long long size = settings->sizeof_int; const Token * type = tok2->enumerator()->scope->enumType; if (type) { size = getSizeOfType(type, settings); if (size == 0) tok->linkAt(1); } ValueFlow::Value value(size); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok, value, settings); setTokenValue(tok->next(), value, settings); } else if (tok2->type() && tok2->type()->isEnumType()) { long long size = settings->sizeof_int; if (tok2->type()->classScope) { const Token * type = tok2->type()->classScope->enumType; if (type) { size = getSizeOfType(type, settings); } } ValueFlow::Value value(size); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok, value, settings); setTokenValue(tok->next(), value, settings); } else if (Token::Match(tok, "sizeof ( %var% ) / sizeof (") && tok->next()->astParent() == tok->tokAt(4)) { // Get number of elements in array const Token *sz1 = tok->tokAt(2); const Token *sz2 = tok->tokAt(7); const nonneg int varid1 = sz1->varId(); if (varid1 && sz1->variable() && sz1->variable()->isArray() && !sz1->variable()->dimensions().empty() && sz1->variable()->dimensionKnown(0) && (Token::Match(sz2, "* %varid% )", varid1) || Token::Match(sz2, "%varid% [ 0 ] )", varid1))) { ValueFlow::Value value(sz1->variable()->dimension(0)); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok->tokAt(4), value, settings); } } else if (Token::Match(tok2, "%var% )")) { const Variable *var = tok2->variable(); // only look for single token types (no pointers or references yet) if (var && var->typeStartToken() == var->typeEndToken()) { // find the size of the type size_t size = 0; if (var->isEnumType()) { size = settings->sizeof_int; if (var->type()->classScope && var->type()->classScope->enumType) size = getSizeOfType(var->type()->classScope->enumType, settings); } else if (var->valueType()) { size = ValueFlow::getSizeOf(*var->valueType(), settings); } else if (!var->type()) { size = getSizeOfType(var->typeStartToken(), settings); } // find the number of elements size_t count = 1; for (size_t i = 0; i < var->dimensions().size(); ++i) { if (var->dimensionKnown(i)) count *= var->dimension(i); else count = 0; } if (size && count > 0) { ValueFlow::Value value(count * size); if (settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok, value, settings); setTokenValue(tok->next(), value, settings); } } } else if (tok2->tokType() == Token::eString) { size_t sz = Token::getStrSize(tok2, settings); if (sz > 0) { ValueFlow::Value value(sz); value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } } else if (tok2->tokType() == Token::eChar) { nonneg int sz = 0; if (cpp && settings->standards.cpp >= Standards::CPP20 && tok2->isUtf8()) sz = 1; else if (tok2->isUtf16()) sz = 2; else if (tok2->isUtf32()) sz = 4; else if (tok2->isLong()) sz = settings->sizeof_wchar_t; else if ((tok2->isCChar() && !cpp) || (tok2->isCMultiChar())) sz = settings->sizeof_int; else sz = 1; if (sz > 0) { ValueFlow::Value value(sz); value.setKnown(); setTokenValue(tok->next(), value, settings); } } else if (!tok2->type()) { const ValueType &vt = ValueType::parseDecl(tok2,settings); const size_t sz = ValueFlow::getSizeOf(vt, settings); if (sz > 0) { ValueFlow::Value value(sz); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok->next(), value, settings); } } // skip over enum tok = tok->linkAt(1); } return tok->next(); } static void valueFlowNumber(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok;) { tok = valueFlowSetConstantValue(tok, tokenlist->getSettings(), tokenlist->isCPP()); } if (tokenlist->isCPP()) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->isName() && !tok->varId() && Token::Match(tok, "false|true")) { ValueFlow::Value value(tok->str() == "true"); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } else if (Token::Match(tok, "[(,] NULL [,)]")) { // NULL function parameters are not simplified in the // normal tokenlist ValueFlow::Value value(0); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok->next(), value, tokenlist->getSettings()); } } } } static void valueFlowString(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->tokType() == Token::eString) { ValueFlow::Value strvalue; strvalue.valueType = ValueFlow::Value::ValueType::TOK; strvalue.tokvalue = tok; strvalue.setKnown(); setTokenValue(tok, strvalue, tokenlist->getSettings()); } } } static void valueFlowArray(TokenList *tokenlist) { std::map constantArrays; for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->varId() > 0) { // array const std::map::const_iterator it = constantArrays.find(tok->varId()); if (it != constantArrays.end()) { ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::TOK; value.tokvalue = it->second; value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } // const array decl else if (tok->variable() && tok->variable()->isArray() && tok->variable()->isConst() && tok->variable()->nameToken() == tok && Token::Match(tok, "%var% [ %num%| ] = {")) { const Token* rhstok = tok->next()->link()->tokAt(2); constantArrays[tok->varId()] = rhstok; tok = rhstok->link(); } // pointer = array else if (tok->variable() && tok->variable()->isArray() && Token::simpleMatch(tok->astParent(), "=") && astIsRHS(tok) && tok->astParent()->astOperand1() && tok->astParent()->astOperand1()->variable() && tok->astParent()->astOperand1()->variable()->isPointer()) { ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::TOK; value.tokvalue = tok; value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } continue; } if (Token::Match(tok, "const %type% %var% [ %num%| ] = {")) { const Token *vartok = tok->tokAt(2); const Token *rhstok = vartok->next()->link()->tokAt(2); constantArrays[vartok->varId()] = rhstok; tok = rhstok->link(); continue; } else if (Token::Match(tok, "const char %var% [ %num%| ] = %str% ;")) { const Token *vartok = tok->tokAt(2); const Token *strtok = vartok->next()->link()->tokAt(2); constantArrays[vartok->varId()] = strtok; tok = strtok->next(); continue; } } } static bool isNonZero(const Token *tok) { return tok && (!tok->hasKnownIntValue() || tok->values().front().intvalue != 0); } static const Token *getOtherOperand(const Token *tok) { if (!tok) return nullptr; if (!tok->astParent()) return nullptr; if (tok->astParent()->astOperand1() != tok) return tok->astParent()->astOperand1(); if (tok->astParent()->astOperand2() != tok) return tok->astParent()->astOperand2(); return nullptr; } static void valueFlowArrayBool(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->hasKnownIntValue()) continue; const Variable *var = nullptr; bool known = false; std::list::const_iterator val = std::find_if(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue)); if (val == tok->values().end()) { var = tok->variable(); known = true; } else { var = val->tokvalue->variable(); known = val->isKnown(); } if (!var) continue; if (!var->isArray() || var->isArgument() || var->isStlType()) continue; if (isNonZero(getOtherOperand(tok)) && Token::Match(tok->astParent(), "%comp%")) continue; // TODO: Check for function argument if ((astIsBool(tok->astParent()) && !Token::Match(tok->astParent(), "(|%name%")) || (tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|for ("))) { ValueFlow::Value value{1}; if (known) value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } } } static void valueFlowPointerAlias(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { // not address of if (!tok->isUnaryOp("&")) continue; // parent should be a '=' if (!Token::simpleMatch(tok->astParent(), "=")) continue; // child should be some buffer or variable const Token *vartok = tok->astOperand1(); while (vartok) { if (vartok->str() == "[") vartok = vartok->astOperand1(); else if (vartok->str() == "." || vartok->str() == "::") vartok = vartok->astOperand2(); else break; } if (!(vartok && vartok->variable() && !vartok->variable()->isPointer())) continue; ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::TOK; value.tokvalue = tok; setTokenValue(tok, value, tokenlist->getSettings()); } } static void valueFlowBitAnd(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->str() != "&") continue; if (tok->hasKnownValue()) continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; MathLib::bigint number; if (MathLib::isInt(tok->astOperand1()->str())) number = MathLib::toLongNumber(tok->astOperand1()->str()); else if (MathLib::isInt(tok->astOperand2()->str())) number = MathLib::toLongNumber(tok->astOperand2()->str()); else continue; int bit = 0; while (bit <= (MathLib::bigint_bits - 2) && ((((MathLib::bigint)1) << bit) < number)) ++bit; if ((((MathLib::bigint)1) << bit) == number) { setTokenValue(tok, ValueFlow::Value(0), tokenlist->getSettings()); setTokenValue(tok, ValueFlow::Value(number), tokenlist->getSettings()); } } } static void valueFlowSameExpressions(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->hasKnownIntValue()) continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; if (tok->astOperand1()->isLiteral() || tok->astOperand2()->isLiteral()) continue; if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) continue; ValueFlow::Value val; if (Token::Match(tok, "==|>=|<=|/")) { val = ValueFlow::Value(1); val.setKnown(); } if (Token::Match(tok, "!=|>|<|%|-")) { val = ValueFlow::Value(0); val.setKnown(); } if (!val.isKnown()) continue; if (isSameExpression(tokenlist->isCPP(), false, tok->astOperand1(), tok->astOperand2(), tokenlist->getSettings()->library, true, true, &val.errorPath)) { setTokenValue(tok, val, tokenlist->getSettings()); } } } static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue) { if (expr->hasKnownIntValue()) { if (minvalue) *minvalue = expr->values().front().intvalue; if (maxvalue) *maxvalue = expr->values().front().intvalue; return true; } if (expr->str() == "&" && expr->astOperand1() && expr->astOperand2()) { MathLib::bigint vals[4]; bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); bool rhsHasKnownRange = getExpressionRange(expr->astOperand2(), &vals[2], &vals[3]); if (!lhsHasKnownRange && !rhsHasKnownRange) return false; if (!lhsHasKnownRange || !rhsHasKnownRange) { if (minvalue) *minvalue = lhsHasKnownRange ? vals[0] : vals[2]; if (maxvalue) *maxvalue = lhsHasKnownRange ? vals[1] : vals[3]; } else { if (minvalue) *minvalue = vals[0] & vals[2]; if (maxvalue) *maxvalue = vals[1] & vals[3]; } return true; } if (expr->str() == "%" && expr->astOperand1() && expr->astOperand2()) { MathLib::bigint vals[4]; if (!getExpressionRange(expr->astOperand2(), &vals[2], &vals[3])) return false; if (vals[2] <= 0) return false; bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); if (lhsHasKnownRange && vals[0] < 0) return false; // If lhs has unknown value, it must be unsigned if (!lhsHasKnownRange && (!expr->astOperand1()->valueType() || expr->astOperand1()->valueType()->sign != ValueType::Sign::UNSIGNED)) return false; if (minvalue) *minvalue = 0; if (maxvalue) *maxvalue = vals[3] - 1; return true; } return false; } static void valueFlowRightShift(TokenList *tokenList, const Settings* settings) { for (Token *tok = tokenList->front(); tok; tok = tok->next()) { if (tok->str() != ">>") continue; if (tok->hasKnownValue()) continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; if (!tok->astOperand2()->hasKnownValue()) continue; const MathLib::bigint rhsvalue = tok->astOperand2()->values().front().intvalue; if (rhsvalue < 0) continue; if (!tok->astOperand1()->valueType() || !tok->astOperand1()->valueType()->isIntegral()) continue; if (!tok->astOperand2()->valueType() || !tok->astOperand2()->valueType()->isIntegral()) continue; MathLib::bigint lhsmax=0; if (!getExpressionRange(tok->astOperand1(), nullptr, &lhsmax)) continue; if (lhsmax < 0) continue; int lhsbits; if ((tok->astOperand1()->valueType()->type == ValueType::Type::CHAR) || (tok->astOperand1()->valueType()->type == ValueType::Type::SHORT) || (tok->astOperand1()->valueType()->type == ValueType::Type::WCHAR_T) || (tok->astOperand1()->valueType()->type == ValueType::Type::BOOL) || (tok->astOperand1()->valueType()->type == ValueType::Type::INT)) lhsbits = settings->int_bit; else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONG) lhsbits = settings->long_bit; else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONGLONG) lhsbits = settings->long_long_bit; else continue; if (rhsvalue >= lhsbits || rhsvalue >= MathLib::bigint_bits || (1ULL << rhsvalue) <= lhsmax) continue; ValueFlow::Value val(0); val.setKnown(); setTokenValue(tok, val, tokenList->getSettings()); } } static std::vector minUnsignedValue(const Token* tok, int depth = 8) { std::vector result = {}; if (!tok) return result; if (depth < 0) return result; if (tok->hasKnownIntValue()) { result = {tok->values().front().intvalue}; } else if (!Token::Match(tok, "-|%|&|^") && tok->isConstOp() && tok->astOperand1() && tok->astOperand2()) { std::vector op1 = minUnsignedValue(tok->astOperand1(), depth - 1); std::vector op2 = minUnsignedValue(tok->astOperand2(), depth - 1); if (!op1.empty() && !op2.empty()) { result = calculate>(tok->str(), op1.front(), op2.front()); } } if (result.empty() && astIsUnsigned(tok)) result = {0}; return result; } static void valueFlowImpossibleValues(TokenList* tokenList, const Settings* settings) { for (Token* tok = tokenList->front(); tok; tok = tok->next()) { if (tok->hasKnownIntValue()) continue; if (astIsUnsigned(tok) && !astIsPointer(tok)) { std::vector minvalue = minUnsignedValue(tok); if (minvalue.empty()) continue; ValueFlow::Value value{std::max(0, minvalue.front()) - 1}; value.bound = ValueFlow::Value::Bound::Upper; value.setImpossible(); setTokenValue(tok, value, settings); } if (Token::simpleMatch(tok, "%") && tok->astOperand2() && tok->astOperand2()->hasKnownIntValue()) { ValueFlow::Value value{tok->astOperand2()->values().front()}; value.bound = ValueFlow::Value::Bound::Lower; value.setImpossible(); setTokenValue(tok, value, settings); } else if (Token::Match(tok, "abs|labs|llabs|fabs|fabsf|fabsl (")) { ValueFlow::Value value{-1}; value.bound = ValueFlow::Value::Bound::Upper; value.setImpossible(); setTokenValue(tok->next(), value, settings); } else if (Token::Match(tok, ". data|c_str (") && astIsContainerOwned(tok->astOperand1())) { const Library::Container* container = getLibraryContainer(tok->astOperand1()); if (!container) continue; if (!container->stdStringLike) continue; if (container->view) continue; ValueFlow::Value value{0}; value.setImpossible(); setTokenValue(tok->tokAt(2), value, settings); } else if (Token::Match(tok, "make_shared|make_unique <") && Token::simpleMatch(tok->linkAt(1), "> (")) { ValueFlow::Value value{0}; value.setImpossible(); setTokenValue(tok->linkAt(1)->next(), value, settings); } else if (tokenList->isCPP() && Token::simpleMatch(tok, "this")) { ValueFlow::Value value{0}; value.setImpossible(); setTokenValue(tok, value, settings); } } } static void valueFlowEnumValue(SymbolDatabase * symboldatabase, const Settings * settings) { for (Scope & scope : symboldatabase->scopeList) { if (scope.type != Scope::eEnum) continue; MathLib::bigint value = 0; bool prev_enum_is_known = true; for (Enumerator & enumerator : scope.enumeratorList) { if (enumerator.start) { Token *rhs = enumerator.start->previous()->astOperand2(); ValueFlow::valueFlowConstantFoldAST(rhs, settings); if (rhs && rhs->hasKnownIntValue()) { enumerator.value = rhs->values().front().intvalue; enumerator.value_known = true; value = enumerator.value + 1; prev_enum_is_known = true; } else prev_enum_is_known = false; } else if (prev_enum_is_known) { enumerator.value = value++; enumerator.value_known = true; } } } } static void valueFlowGlobalConstVar(TokenList* tokenList, const Settings *settings) { // Get variable values... std::map vars; for (const Token* tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; // Initialization... if (tok == tok->variable()->nameToken() && !tok->variable()->isVolatile() && !tok->variable()->isArgument() && tok->variable()->isConst() && tok->valueType() && tok->valueType()->isIntegral() && tok->valueType()->pointer == 0 && tok->valueType()->constness == 1 && Token::Match(tok, "%name% =") && tok->next()->astOperand2() && tok->next()->astOperand2()->hasKnownIntValue()) { vars[tok->variable()] = tok->next()->astOperand2()->values().front(); } } // Set values.. for (Token* tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; std::map::const_iterator var = vars.find(tok->variable()); if (var == vars.end()) continue; setTokenValue(tok, var->second, settings); } } static void valueFlowGlobalStaticVar(TokenList *tokenList, const Settings *settings) { // Get variable values... std::map vars; for (const Token *tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; // Initialization... if (tok == tok->variable()->nameToken() && tok->variable()->isStatic() && !tok->variable()->isConst() && tok->valueType() && tok->valueType()->isIntegral() && tok->valueType()->pointer == 0 && tok->valueType()->constness == 0 && Token::Match(tok, "%name% =") && tok->next()->astOperand2() && tok->next()->astOperand2()->hasKnownIntValue()) { vars[tok->variable()] = tok->next()->astOperand2()->values().front(); } else { // If variable is written anywhere in TU then remove it from vars if (!tok->astParent()) continue; if (Token::Match(tok->astParent(), "++|--|&") && !tok->astParent()->astOperand2()) vars.erase(tok->variable()); else if (tok->astParent()->isAssignmentOp()) { if (tok == tok->astParent()->astOperand1()) vars.erase(tok->variable()); else if (tokenList->isCPP() && Token::Match(tok->astParent()->tokAt(-2), "& %name% =")) vars.erase(tok->variable()); } else if (isLikelyStreamRead(tokenList->isCPP(), tok->astParent())) { vars.erase(tok->variable()); } else if (Token::Match(tok->astParent(), "[(,]")) vars.erase(tok->variable()); } } // Set values.. for (Token *tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; std::map::const_iterator var = vars.find(tok->variable()); if (var == vars.end()) continue; setTokenValue(tok, var->second, settings); } } static Analyzer::Result valueFlowForward(Token* startToken, const Token* endToken, const Token* exprTok, std::list values, TokenList* const tokenlist, const Settings* settings); static void valueFlowReverse(TokenList* tokenlist, Token* tok, const Token* const varToken, ValueFlow::Value val, ValueFlow::Value val2, ErrorLogger* errorLogger, const Settings* settings); static bool isConditionKnown(const Token* tok, bool then) { const char* op = "||"; if (then) op = "&&"; const Token* parent = tok->astParent(); while (parent && (parent->str() == op || parent->str() == "!")) parent = parent->astParent(); return Token::Match(parent, "(|;"); } static const std::string& invertAssign(const std::string& assign) { static std::unordered_map lookup = {{"=", "="}, {"+=", "-="}, {"-=", "+="}, {"*=", "/="}, {"/=", "*="}, {"<<=", ">>="}, {">>=", "<<="}, {"^=", "^="}}; static std::string empty; auto it = lookup.find(assign); if (it == lookup.end()) return empty; else return it->second; } static std::string removeAssign(const std::string& assign) { return std::string{assign.begin(), assign.end() - 1}; } template static T calculateAssign(const std::string& assign, const T& x, const U& y, bool* error = nullptr) { if (assign.empty() || assign.back() != '=') { if (error) *error = true; return T{}; } if (assign == "=") return y; return calculate(removeAssign(assign), x, y, error); } template static void assignValueIfMutable(T& x, const U& y) { x = y; } template static void assignValueIfMutable(const T&, const U&) {} template )> static bool evalAssignment(Value& lhsValue, const std::string& assign, const ValueFlow::Value& rhsValue) { bool error = false; if (lhsValue.isSymbolicValue() && rhsValue.isIntValue()) { if (assign != "+=" && assign != "-=") return false; assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); } else if (lhsValue.isIntValue() && rhsValue.isIntValue()) { assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); } else if (lhsValue.isFloatValue() && rhsValue.isIntValue()) { assignValueIfMutable(lhsValue.floatValue, calculateAssign(assign, lhsValue.floatValue, rhsValue.intvalue, &error)); } else { return false; } return !error; } template struct SingleRange { T* x; T* begin() const { return x; } T* end() const { return x+1; } }; template SingleRange MakeSingleRange(T& x) { return {&x}; } class SelectValueFromVarIdMapRange { using M = std::unordered_map; struct Iterator { using iterator_category = std::forward_iterator_tag; using value_type = const ValueFlow::Value; using pointer = value_type *; using reference = value_type &; explicit Iterator(const M::const_iterator &it) : mIt(it) {} reference operator*() const { return mIt->second; } pointer operator->() { return &mIt->second; } Iterator &operator++() { // cppcheck-suppress postfixOperator - forward iterator needs to perform post-increment mIt++; return *this; } friend bool operator==(const Iterator &a, const Iterator &b) { return a.mIt == b.mIt; } friend bool operator!=(const Iterator &a, const Iterator &b) { return a.mIt != b.mIt; } private: M::const_iterator mIt; }; public: explicit SelectValueFromVarIdMapRange(const M *m) : mMap(m) {} Iterator begin() const { return Iterator(mMap->begin()); } Iterator end() const { return Iterator(mMap->end()); } private: const M *mMap; }; // Check if its an alias of the variable or is being aliased to this variable template static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const V& values, bool* inconclusive = nullptr) { if (tok->varId() == varid) return false; if (tok->varId() == 0) return false; if (isAliasOf(tok, varid, inconclusive)) return true; if (var && !var->isPointer()) return false; // Search through non value aliases for (const ValueFlow::Value &val : values) { if (!val.isNonValue()) continue; if (val.isInconclusive()) continue; if (val.isLifetimeValue() && !val.isLocalLifetimeValue()) continue; if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) continue; if (!Token::Match(val.tokvalue, ".|&|*|%var%")) continue; if (astHasVar(val.tokvalue, tok->varId())) return true; } return false; } static bool bifurcate(const Token* tok, const std::set& varids, const Settings* settings, int depth = 20); static bool bifurcateVariableChanged(const Variable* var, const std::set& varids, const Token* start, const Token* end, const Settings* settings, int depth = 20) { bool result = false; const Token* tok = start; while ((tok = findVariableChanged( tok->next(), end, var->isPointer(), var->declarationId(), var->isGlobal(), settings, true))) { if (Token::Match(tok->astParent(), "%assign%")) { if (!bifurcate(tok->astParent()->astOperand2(), varids, settings, depth - 1)) return true; } else { result = true; } } return result; } static bool bifurcate(const Token* tok, const std::set& varids, const Settings* settings, int depth) { if (depth < 0) return false; if (!tok) return true; if (tok->hasKnownIntValue()) return true; if (Token::Match(tok, "%cop%")) return bifurcate(tok->astOperand1(), varids, settings, depth) && bifurcate(tok->astOperand2(), varids, settings, depth); if (Token::Match(tok, "%var%")) { if (varids.count(tok->varId()) > 0) return true; const Variable* var = tok->variable(); if (!var) return false; const Token* start = var->declEndToken(); if (!start) return false; if (start->strAt(-1) == ")" || start->strAt(-1) == "}") return false; if (Token::Match(start, "; %varid% =", var->declarationId())) start = start->tokAt(2); if (var->isConst() || !bifurcateVariableChanged(var, varids, start, tok, settings, depth)) return var->isArgument() || bifurcate(start->astOperand2(), varids, settings, depth - 1); return false; } return false; } struct ValueFlowAnalyzer : Analyzer { const TokenList* tokenlist; ProgramMemoryState pms; ValueFlowAnalyzer() : tokenlist(nullptr), pms(nullptr) {} explicit ValueFlowAnalyzer(const TokenList* t) : tokenlist(t), pms(tokenlist->getSettings()) {} virtual const ValueFlow::Value* getValue(const Token* tok) const = 0; virtual ValueFlow::Value* getValue(const Token* tok) = 0; virtual void makeConditional() = 0; virtual void addErrorPath(const Token* tok, const std::string& s) = 0; virtual bool match(const Token* tok) const = 0; virtual bool internalMatch(const Token*) const { return false; } virtual bool isAlias(const Token* tok, bool& inconclusive) const = 0; using ProgramState = std::unordered_map; virtual ProgramState getProgramState() const = 0; virtual int getIndirect(const Token* tok) const { const ValueFlow::Value* value = getValue(tok); if (value) return value->indirect; return 0; } virtual bool isGlobal() const { return false; } virtual bool dependsOnThis() const { return false; } virtual bool isVariable() const { return false; } virtual bool invalid() const { return false; } bool isCPP() const { return tokenlist->isCPP(); } const Settings* getSettings() const { return tokenlist->getSettings(); } struct ConditionState { bool dependent = true; bool unknown = true; bool isUnknownDependent() const { return unknown && dependent; } }; std::unordered_map getSymbols(const Token* tok) const { std::unordered_map result; if (!tok) return result; for (const ValueFlow::Value& v : tok->values()) { if (!v.isSymbolicValue()) continue; if (v.isImpossible()) continue; if (!v.tokvalue) continue; if (v.tokvalue->exprId() == 0) continue; if (match(v.tokvalue)) continue; result[v.tokvalue->exprId()] = v.tokvalue; } return result; } ConditionState analyzeCondition(const Token* tok, int depth = 20) const { ConditionState result; if (!tok) return result; if (depth < 0) return result; depth--; if (analyze(tok, Direction::Forward).isRead()) { result.dependent = true; result.unknown = false; return result; } else if (tok->hasKnownIntValue() || tok->isLiteral()) { result.dependent = false; result.unknown = false; return result; } else if (Token::Match(tok, "%cop%")) { if (isLikelyStream(isCPP(), tok->astOperand1())) { result.dependent = false; return result; } ConditionState lhs = analyzeCondition(tok->astOperand1(), depth - 1); if (lhs.isUnknownDependent()) return lhs; ConditionState rhs = analyzeCondition(tok->astOperand2(), depth - 1); if (rhs.isUnknownDependent()) return rhs; if (Token::Match(tok, "%comp%")) result.dependent = lhs.dependent && rhs.dependent; else result.dependent = lhs.dependent || rhs.dependent; result.unknown = lhs.unknown || rhs.unknown; return result; } else if (Token::Match(tok->previous(), "%name% (")) { std::vector args = getArguments(tok->previous()); if (Token::Match(tok->tokAt(-2), ". %name% (")) { args.push_back(tok->tokAt(-2)->astOperand1()); } result.dependent = std::any_of(args.begin(), args.end(), [&](const Token* arg) { ConditionState cs = analyzeCondition(arg, depth - 1); return cs.dependent; }); if (result.dependent) { // Check if we can evaluate the function if (!evaluate(Evaluate::Integral, tok).empty()) result.unknown = false; } return result; } else { std::unordered_map symbols = getSymbols(tok); result.dependent = false; for (auto&& p : symbols) { const Token* arg = p.second; ConditionState cs = analyzeCondition(arg, depth - 1); result.dependent = cs.dependent; if (result.dependent) break; } if (result.dependent) { // Check if we can evaluate the token if (!evaluate(Evaluate::Integral, tok).empty()) result.unknown = false; } return result; } } virtual Action isModified(const Token* tok) const { Action read = Action::Read; bool inconclusive = false; if (isVariableChangedByFunctionCall(tok, getIndirect(tok), getSettings(), &inconclusive)) return read | Action::Invalid; if (inconclusive) return read | Action::Inconclusive; if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) { if (Token::Match(tok->astParent(), "*|[|.|++|--")) return read | Action::Invalid; const ValueFlow::Value* value = getValue(tok); // Check if its assigned to the same value if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) && astIsIntegral(tok->astParent()->astOperand2(), false)) { std::vector result = evaluate(Evaluate::Integral, tok->astParent()->astOperand2()); if (!result.empty() && value->equalTo(result.front())) return Action::Idempotent; } return Action::Invalid; } return read; } virtual Action isAliasModified(const Token* tok) const { // Lambda function call if (Token::Match(tok, "%var% (")) // TODO: Check if modified in the lambda function return Action::Invalid; int indirect = 0; if (tok->valueType()) indirect = tok->valueType()->pointer; if (isVariableChanged(tok, indirect, getSettings(), isCPP())) return Action::Invalid; return Action::None; } virtual Action isThisModified(const Token* tok) const { if (isThisChanged(tok, 0, getSettings(), isCPP())) return Action::Invalid; return Action::None; } Action isGlobalModified(const Token* tok) const { if (tok->function()) { if (!tok->function()->isConstexpr() && !isConstFunctionCall(tok, getSettings()->library)) return Action::Invalid; } else if (getSettings()->library.getFunction(tok)) { // Assume library function doesn't modify user-global variables return Action::None; // Function cast does not modify global variables } else if (tok->tokType() == Token::eType && astIsPrimitive(tok->next())) { return Action::None; } else if (Token::Match(tok, "%name% (")) { return Action::Invalid; } return Action::None; } static const std::string& getAssign(const Token* tok, Direction d) { if (d == Direction::Forward) return tok->str(); else return invertAssign(tok->str()); } virtual Action isWritable(const Token* tok, Direction d) const { const ValueFlow::Value* value = getValue(tok); if (!value) return Action::None; if (!(value->isIntValue() || value->isFloatValue() || value->isSymbolicValue())) return Action::None; const Token* parent = tok->astParent(); // Only if its invertible if (value->isImpossible() && !Token::Match(parent, "+=|-=|*=|++|--")) return Action::None; if (parent && parent->isAssignmentOp() && astIsLHS(tok) && parent->astOperand2()->hasKnownValue()) { const Token* rhs = parent->astOperand2(); const ValueFlow::Value* rhsValue = rhs->getKnownValue(ValueFlow::Value::ValueType::INT); Action a; if (!rhsValue || !evalAssignment(*value, getAssign(parent, d), *rhsValue)) a = Action::Invalid; else a = Action::Write; if (parent->str() != "=") { a |= Action::Read; } else { if (rhsValue && !value->isImpossible() && value->equalValue(*rhsValue)) a = Action::Idempotent; a |= Action::Incremental; } return a; } // increment/decrement if (Token::Match(tok->astParent(), "++|--")) { return Action::Read | Action::Write | Action::Incremental; } return Action::None; } virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const { if (!value) return; if (!tok->astParent()) return; if (tok->astParent()->isAssignmentOp()) { const ValueFlow::Value* rhsValue = tok->astParent()->astOperand2()->getKnownValue(ValueFlow::Value::ValueType::INT); assert(rhsValue); if (evalAssignment(*value, getAssign(tok->astParent(), d), *rhsValue)) { const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " + value->infoString()); if (tok->astParent()->str() == "=") value->errorPath.clear(); value->errorPath.emplace_back(tok, info); } else { assert(false && "Writable value cannot be evaluated"); // TODO: Don't set to zero value->intvalue = 0; } } else if (tok->astParent()->tokType() == Token::eIncDecOp) { bool inc = tok->astParent()->str() == "++"; std::string opName(inc ? "incremented" : "decremented"); if (d == Direction::Reverse) inc = !inc; value->intvalue += (inc ? 1 : -1); const std::string info(tok->str() + " is " + opName + "', new value is " + value->infoString()); value->errorPath.emplace_back(tok, info); } } virtual bool useSymbolicValues() const { return true; } const Token* findMatch(const Token* tok) const { return findAstNode(tok, [&](const Token* child) { return match(child); }); } bool isSameSymbolicValue(const Token* tok, ValueFlow::Value* value = nullptr) const { if (!useSymbolicValues()) return false; if (Token::Match(tok, "%assign%")) return false; const ValueFlow::Value* currValue = getValue(tok); if (!currValue) return false; const bool exact = !currValue->isIntValue() || currValue->isImpossible(); for (const ValueFlow::Value& v : tok->values()) { if (!v.isSymbolicValue()) continue; const bool toImpossible = v.isImpossible() && currValue->isKnown(); if (!v.isKnown() && !toImpossible) continue; if (exact && v.intvalue != 0) continue; std::vector r; ValueFlow::Value::Bound bound = currValue->bound; if (match(v.tokvalue)) { r = {currValue->intvalue}; } else if (!exact && findMatch(v.tokvalue)) { r = evaluate(Evaluate::Integral, v.tokvalue, tok); if (bound == ValueFlow::Value::Bound::Point) bound = v.bound; } if (!r.empty()) { if (value) { value->errorPath.insert(value->errorPath.end(), v.errorPath.begin(), v.errorPath.end()); value->intvalue = r.front() + v.intvalue; if (toImpossible) value->setImpossible(); value->bound = bound; } return true; } } return false; } Action analyzeMatch(const Token* tok, Direction d) const { const Token* parent = tok->astParent(); if (d == Direction::Reverse && isGlobal() && !dependsOnThis() && Token::Match(parent, ". %name% (")) { Action a = isGlobalModified(parent->next()); if (a != Action::None) return a; } if ((astIsPointer(tok) || astIsSmartPointer(tok)) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0) return Action::Read; Action w = isWritable(tok, d); if (w != Action::None) return w; // Check for modifications by function calls return isModified(tok); } Action analyzeToken(const Token* ref, const Token* tok, Direction d, bool inconclusiveRef) const { if (!ref) return Action::None; // If its an inconclusiveRef then ref != tok assert(!inconclusiveRef || ref != tok); bool inconclusive = false; if (match(ref)) { if (inconclusiveRef) { Action a = isModified(tok); if (a.isModified() || a.isInconclusive()) return Action::Inconclusive; } else { return analyzeMatch(tok, d) | Action::Match; } } else if (ref->isUnaryOp("*")) { const Token* lifeTok = nullptr; for (const ValueFlow::Value& v:ref->astOperand1()->values()) { if (!v.isLocalLifetimeValue()) continue; if (lifeTok) return Action::None; lifeTok = v.tokvalue; } if (lifeTok && match(lifeTok)) { Action a = Action::Read; if (isModified(tok).isModified()) a = Action::Invalid; if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) a |= Action::Invalid; if (inconclusiveRef && a.isModified()) return Action::Inconclusive; return a; } return Action::None; } else if (isAlias(ref, inconclusive)) { inconclusive |= inconclusiveRef; Action a = isAliasModified(tok); if (inconclusive && a.isModified()) return Action::Inconclusive; else return a; } else if (isSameSymbolicValue(ref)) { return Action::Read | Action::SymbolicMatch; } return Action::None; } virtual Action analyze(const Token* tok, Direction d) const OVERRIDE { if (invalid()) return Action::Invalid; // Follow references std::vector refs = followAllReferences(tok); const bool inconclusiveRefs = refs.size() != 1; if (std::none_of(refs.begin(), refs.end(), [&](const ReferenceToken& ref) { return tok == ref.token; })) refs.push_back(ReferenceToken{tok, {}}); for (const ReferenceToken& ref:refs) { Action a = analyzeToken(ref.token, tok, d, inconclusiveRefs && ref.token != tok); if (internalMatch(ref.token)) a |= Action::Internal; if (a != Action::None) return a; } if (dependsOnThis() && exprDependsOnThis(tok, !isVariable())) return isThisModified(tok); // bailout: global non-const variables if (isGlobal() && !dependsOnThis() && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { return isGlobalModified(tok); } return Action::None; } virtual std::vector evaluate(Evaluate e, const Token* tok, const Token* ctx = nullptr) const OVERRIDE { if (e == Evaluate::Integral) { if (tok->hasKnownIntValue()) return {static_cast(tok->values().front().intvalue)}; std::vector result; ProgramMemory pm = pms.get(tok, ctx, getProgramState()); if (Token::Match(tok, "&&|%oror%")) { if (conditionIsTrue(tok, pm, getSettings())) result.push_back(1); if (conditionIsFalse(tok, pm, getSettings())) result.push_back(0); } else { MathLib::bigint out = 0; bool error = false; execute(tok, &pm, &out, &error, getSettings()); if (!error) result.push_back(out); } return result; } else if (e == Evaluate::ContainerEmpty) { const ValueFlow::Value* value = ValueFlow::findValue(tok->values(), nullptr, [](const ValueFlow::Value& v) { return v.isKnown() && v.isContainerSizeValue(); }); if (value) return {value->intvalue == 0}; ProgramMemory pm = pms.get(tok, ctx, getProgramState()); MathLib::bigint out = 0; if (pm.getContainerEmptyValue(tok->exprId(), &out)) return {static_cast(out)}; return {}; } else { return {}; } } virtual void assume(const Token* tok, bool state, unsigned int flags) OVERRIDE { // Update program state pms.removeModifiedVars(tok); pms.addState(tok, getProgramState()); pms.assume(tok, state, flags & Assume::ContainerEmpty); bool isCondBlock = false; const Token* parent = tok->astParent(); if (parent) { isCondBlock = Token::Match(parent->previous(), "if|while ("); } if (isCondBlock) { const Token* startBlock = parent->link()->next(); if (Token::simpleMatch(startBlock, ";") && Token::simpleMatch(parent->tokAt(-2), "} while (")) startBlock = parent->linkAt(-2); const Token* endBlock = startBlock->link(); pms.removeModifiedVars(endBlock); if (state) pms.addState(endBlock->previous(), getProgramState()); else if (Token::simpleMatch(endBlock, "} else {")) pms.addState(endBlock->linkAt(2)->previous(), getProgramState()); } if (!(flags & Assume::Quiet)) { if (flags & Assume::ContainerEmpty) { std::string s = state ? "empty" : "not empty"; addErrorPath(tok, "Assuming container is " + s); } else { std::string s = state ? "true" : "false"; addErrorPath(tok, "Assuming condition is " + s); } } if (!(flags & Assume::Absolute)) makeConditional(); } virtual void internalUpdate(Token*, const ValueFlow::Value&, Direction) { assert(false && "Internal update unimplemented."); } virtual void update(Token* tok, Action a, Direction d) OVERRIDE { ValueFlow::Value* value = getValue(tok); if (!value) return; ValueFlow::Value localValue; if (a.isSymbolicMatch()) { // Make a copy of the value to modify it localValue = *value; value = &localValue; isSameSymbolicValue(tok, &localValue); } if (a.isInternal()) internalUpdate(tok, *value, d); // Read first when moving forward if (d == Direction::Forward && a.isRead()) setTokenValue(tok, *value, getSettings()); if (a.isInconclusive()) lowerToInconclusive(); if (a.isWrite() && tok->astParent()) { writeValue(value, tok, d); } // Read last when moving in reverse if (d == Direction::Reverse && a.isRead()) setTokenValue(tok, *value, getSettings()); } virtual ValuePtr reanalyze(Token*, const std::string&) const OVERRIDE { return {}; } }; ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist); struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { std::unordered_map varids; std::unordered_map aliases; ValueFlow::Value value; SingleValueFlowAnalyzer() : ValueFlowAnalyzer() {} SingleValueFlowAnalyzer(const ValueFlow::Value& v, const TokenList* t) : ValueFlowAnalyzer(t), value(v) {} const std::unordered_map& getVars() const { return varids; } const std::unordered_map& getAliasedVars() const { return aliases; } virtual const ValueFlow::Value* getValue(const Token*) const OVERRIDE { return &value; } virtual ValueFlow::Value* getValue(const Token*) OVERRIDE { return &value; } virtual void makeConditional() OVERRIDE { value.conditional = true; } virtual bool useSymbolicValues() const OVERRIDE { if (value.isUninitValue()) return false; if (value.isLifetimeValue()) return false; return true; } virtual void addErrorPath(const Token* tok, const std::string& s) OVERRIDE { value.errorPath.emplace_back(tok, s); } virtual bool isAlias(const Token* tok, bool& inconclusive) const OVERRIDE { if (value.isLifetimeValue()) return false; for (const auto& m: { std::ref(getVars()), std::ref(getAliasedVars()) }) { for (const auto& p:m.get()) { nonneg int varid = p.first; const Variable* var = p.second; if (tok->varId() == varid) return true; if (isAliasOf(var, tok, varid, MakeSingleRange(value), &inconclusive)) return true; } } return false; } virtual bool isGlobal() const OVERRIDE { for (const auto&p:getVars()) { const Variable* var = p.second; if (!var->isLocal() && !var->isArgument() && !var->isConst()) return true; } return false; } virtual bool lowerToPossible() OVERRIDE { if (value.isImpossible()) return false; value.changeKnownToPossible(); return true; } virtual bool lowerToInconclusive() OVERRIDE { if (value.isImpossible()) return false; value.setInconclusive(); return true; } virtual bool isConditional() const OVERRIDE { if (value.conditional) return true; if (value.condition) return !value.isKnown() && !value.isImpossible(); return false; } virtual bool stopOnCondition(const Token* condTok) const OVERRIDE { if (value.isNonValue()) return false; if (value.isImpossible()) return false; if (isConditional() && !value.isKnown() && !value.isImpossible()) return true; if (value.isSymbolicValue()) return false; ConditionState cs = analyzeCondition(condTok); return cs.isUnknownDependent(); } virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE { const Scope* scope = endBlock->scope(); if (!scope) return false; if (scope->type == Scope::eLambda) { return value.isLifetimeValue(); } else if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || scope->type == Scope::eFor) { if (value.isKnown() || value.isImpossible()) return true; if (value.isLifetimeValue()) return true; if (isConditional()) return false; const Token* condTok = getCondTokFromEnd(endBlock); std::set varids2; std::transform(getVars().begin(), getVars().end(), std::inserter(varids2, varids2.begin()), SelectMapKeys{}); return bifurcate(condTok, varids2, getSettings()); } return false; } virtual ValuePtr reanalyze(Token* tok, const std::string& msg) const OVERRIDE { ValueFlow::Value newValue = value; newValue.errorPath.emplace_back(tok, msg); return makeAnalyzer(tok, newValue, tokenlist); } }; struct ExpressionAnalyzer : SingleValueFlowAnalyzer { const Token* expr; bool local; bool unknown; bool dependOnThis; ExpressionAnalyzer() : SingleValueFlowAnalyzer(), expr(nullptr), local(true), unknown(false), dependOnThis(false) {} ExpressionAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t) : SingleValueFlowAnalyzer(val, t), expr(e), local(true), unknown(false), dependOnThis(false) { assert(e && e->exprId() != 0 && "Not a valid expression"); dependOnThis = exprDependsOnThis(expr); setupExprVarIds(expr); if (val.isSymbolicValue()) setupExprVarIds(val.tokvalue); } static bool nonLocal(const Variable* var, bool deref) { return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern(); } void setupExprVarIds(const Token* start, int depth = 0) { const int maxDepth = 4; if (depth > maxDepth) return; visitAstNodes(start, [&](const Token* tok) { const bool top = depth == 0 && tok == start; const bool ispointer = astIsPointer(tok) || astIsSmartPointer(tok) || astIsIterator(tok); if (!top || !ispointer || value.indirect != 0) { for (const ValueFlow::Value& v : tok->values()) { if (!(v.isLocalLifetimeValue() || (ispointer && v.isSymbolicValue() && v.isKnown()))) continue; if (!v.tokvalue) continue; if (v.tokvalue == tok) continue; setupExprVarIds(v.tokvalue, depth + 1); } } if (depth == 0 && tok->varId() == 0 && !tok->function() && tok->isName() && tok->previous()->str() != ".") { // unknown variable unknown = true; return ChildrenToVisit::none; } if (tok->varId() > 0) { varids[tok->varId()] = tok->variable(); if (!Token::simpleMatch(tok->previous(), ".")) { const Variable* var = tok->variable(); if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && !isGlobalData(var->nameToken()->next()->astOperand2(), isCPP())) return ChildrenToVisit::none; const bool deref = tok->astParent() && (tok->astParent()->isUnaryOp("*") || (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); local &= !nonLocal(tok->variable(), deref); } } return ChildrenToVisit::op1_and_op2; }); } virtual bool invalid() const OVERRIDE { return unknown; } virtual ProgramState getProgramState() const OVERRIDE { ProgramState ps; ps[expr->exprId()] = value; return ps; } virtual bool match(const Token* tok) const OVERRIDE { return tok->exprId() == expr->exprId(); } virtual bool dependsOnThis() const OVERRIDE { return dependOnThis; } virtual bool isGlobal() const OVERRIDE { return !local; } virtual bool isVariable() const OVERRIDE { return expr->varId() > 0; } }; struct OppositeExpressionAnalyzer : ExpressionAnalyzer { bool isNot; OppositeExpressionAnalyzer() : ExpressionAnalyzer(), isNot(false) {} OppositeExpressionAnalyzer(bool pIsNot, const Token* e, const ValueFlow::Value& val, const TokenList* t) : ExpressionAnalyzer(e, val, t), isNot(pIsNot) {} virtual bool match(const Token* tok) const OVERRIDE { return isOppositeCond(isNot, isCPP(), expr, tok, getSettings()->library, true, true); } }; struct SubExpressionAnalyzer : ExpressionAnalyzer { using PartialReadContainer = std::vector>; // A shared_ptr is used so partial reads can be captured even after forking std::shared_ptr partialReads; SubExpressionAnalyzer() : ExpressionAnalyzer(), partialReads(nullptr) {} SubExpressionAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t) : ExpressionAnalyzer(e, val, t), partialReads(std::make_shared()) {} virtual bool submatch(const Token* tok, bool exact = true) const = 0; virtual bool isAlias(const Token* tok, bool& inconclusive) const OVERRIDE { if (tok->exprId() == expr->exprId() && tok->astParent() && submatch(tok->astParent(), false)) return false; return ExpressionAnalyzer::isAlias(tok, inconclusive); } virtual bool match(const Token* tok) const OVERRIDE { return tok->astOperand1() && tok->astOperand1()->exprId() == expr->exprId() && submatch(tok); } virtual bool internalMatch(const Token* tok) const OVERRIDE { return tok->exprId() == expr->exprId() && !(astIsLHS(tok) && submatch(tok->astParent(), false)); } virtual void internalUpdate(Token* tok, const ValueFlow::Value& v, Direction) OVERRIDE { partialReads->push_back(std::make_pair(tok, v)); } // No reanalysis for subexression virtual ValuePtr reanalyze(Token*, const std::string&) const OVERRIDE { return {}; } }; struct MemberExpressionAnalyzer : SubExpressionAnalyzer { std::string varname; MemberExpressionAnalyzer() : SubExpressionAnalyzer(), varname() {} MemberExpressionAnalyzer(std::string varname, const Token* e, const ValueFlow::Value& val, const TokenList* t) : SubExpressionAnalyzer(e, val, t), varname(std::move(varname)) {} virtual bool submatch(const Token* tok, bool exact) const OVERRIDE { if (!Token::Match(tok, ". %var%")) return false; if (!exact) return true; return tok->next()->str() == varname; } }; static Analyzer::Result valueFlowForwardExpression(Token* startToken, const Token* endToken, const Token* exprTok, const std::list& values, const TokenList* const tokenlist, const Settings* settings) { Analyzer::Result result{}; for (const ValueFlow::Value& v : values) { ExpressionAnalyzer a(exprTok, v, tokenlist); result.update(valueFlowGenericForward(startToken, endToken, a, settings)); } return result; } static const Token* parseBinaryIntOp(const Token* expr, MathLib::bigint& known) { if (!expr) return nullptr; if (!expr->astOperand1() || !expr->astOperand2()) return nullptr; if (expr->astOperand1()->exprId() == 0 && !expr->astOperand1()->hasKnownIntValue()) return nullptr; if (expr->astOperand2()->exprId() == 0 && !expr->astOperand2()->hasKnownIntValue()) return nullptr; const Token* knownTok = nullptr; const Token* varTok = nullptr; if (expr->astOperand1()->hasKnownIntValue() && !expr->astOperand2()->hasKnownIntValue()) { varTok = expr->astOperand2(); knownTok = expr->astOperand1(); } else if (expr->astOperand2()->hasKnownIntValue() && !expr->astOperand1()->hasKnownIntValue()) { varTok = expr->astOperand1(); knownTok = expr->astOperand2(); } if (knownTok) known = knownTok->values().front().intvalue; return varTok; } static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) { if (!value.isIntValue() && !value.isIteratorValue() && !value.isSymbolicValue()) return expr; if (value.isSymbolicValue() && !Token::Match(expr, "+|-")) return expr; MathLib::bigint intval; const Token* binaryTok = parseBinaryIntOp(expr, intval); if (binaryTok && expr->str().size() == 1) { switch (expr->str()[0]) { case '+': { value.intvalue -= intval; return solveExprValue(binaryTok, value); } case '-': { value.intvalue += intval; return solveExprValue(binaryTok, value); } case '*': { if (intval == 0) break; value.intvalue /= intval; return solveExprValue(binaryTok, value); } case '^': { value.intvalue ^= intval; return solveExprValue(binaryTok, value); } } } return expr; } ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList* tokenlist) { const Token* expr = solveExprValue(exprTok, value); return ExpressionAnalyzer(expr, value, tokenlist); } static Analyzer::Result valueFlowForward(Token* startToken, const Token* endToken, const Token* exprTok, std::list values, TokenList* const tokenlist, const Settings* settings) { Analyzer::Result result{}; for (const ValueFlow::Value& v : values) { result.update(valueFlowGenericForward(startToken, endToken, makeAnalyzer(exprTok, v, tokenlist), settings)); } return result; } static Analyzer::Result valueFlowForward(Token* top, const Token* exprTok, const std::list& values, TokenList* const tokenlist, const Settings* settings) { Analyzer::Result result{}; for (const ValueFlow::Value& v : values) { result.update(valueFlowGenericForward(top, makeAnalyzer(exprTok, v, tokenlist), settings)); } return result; } static void valueFlowReverse(Token* tok, const Token* const endToken, const Token* const varToken, const std::list& values, TokenList* tokenlist, const Settings* settings) { for (const ValueFlow::Value& v : values) { ExpressionAnalyzer a(varToken, v, tokenlist); valueFlowGenericReverse(tok, endToken, a, settings); } } static void valueFlowReverse(TokenList* tokenlist, Token* tok, const Token* const varToken, ValueFlow::Value val, ValueFlow::Value val2, ErrorLogger* /*errorLogger*/, const Settings* settings) { std::list values = {val}; if (val2.varId != 0) values.push_back(val2); valueFlowReverse(tok, nullptr, varToken, values, tokenlist, settings); } std::string lifetimeType(const Token *tok, const ValueFlow::Value *val) { std::string result; if (!val) return "object"; switch (val->lifetimeKind) { case ValueFlow::Value::LifetimeKind::Lambda: result = "lambda"; break; case ValueFlow::Value::LifetimeKind::Iterator: result = "iterator"; break; case ValueFlow::Value::LifetimeKind::Object: case ValueFlow::Value::LifetimeKind::SubObject: case ValueFlow::Value::LifetimeKind::Address: if (astIsPointer(tok)) result = "pointer"; else result = "object"; break; } return result; } std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ErrorPath &errorPath) { const Token *tokvalue = val ? val->tokvalue : nullptr; const Variable *tokvar = tokvalue ? tokvalue->variable() : nullptr; const Token *vartok = tokvar ? tokvar->nameToken() : nullptr; const bool classVar = tokvar ? (!tokvar->isLocal() && !tokvar->isArgument() && !tokvar->isGlobal()) : false; std::string type = lifetimeType(tok, val); std::string msg = type; if (vartok) { if (!classVar) errorPath.emplace_back(vartok, "Variable created here."); const Variable * var = vartok->variable(); std::string submessage; if (var) { switch (val->lifetimeKind) { case ValueFlow::Value::LifetimeKind::SubObject: case ValueFlow::Value::LifetimeKind::Object: case ValueFlow::Value::LifetimeKind::Address: if (type == "pointer") submessage = " to local variable"; else submessage = " that points to local variable"; break; case ValueFlow::Value::LifetimeKind::Lambda: submessage = " that captures local variable"; break; case ValueFlow::Value::LifetimeKind::Iterator: submessage = " to local container"; break; } if (classVar) submessage.replace(submessage.find("local"), 5, "member"); msg += submessage + " '" + var->name() + "'"; } } return msg; } std::vector getLifetimeObjValues(const Token* tok, bool inconclusive, MathLib::bigint path) { std::vector result; auto pred = [&](const ValueFlow::Value& v) { if (!v.isLocalLifetimeValue() && !(path != 0 && v.isSubFunctionLifetimeValue())) return false; if (!inconclusive && v.isInconclusive()) return false; if (!v.tokvalue) return false; if (path >= 0 && v.path != 0 && v.path != path) return false; return true; }; std::copy_if(tok->values().begin(), tok->values().end(), std::back_inserter(result), pred); return result; } ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive) { std::vector values = getLifetimeObjValues(tok, inconclusive); // There should only be one lifetime if (values.size() != 1) return ValueFlow::Value{}; return values.front(); } template static std::vector getLifetimeTokens(const Token* tok, bool escape, ValueFlow::Value::ErrorPath errorPath, Predicate pred, int depth = 20) { if (!tok) return std::vector {}; const Variable *var = tok->variable(); if (pred(tok)) return {{tok, std::move(errorPath)}}; if (depth < 0) return {{tok, std::move(errorPath)}}; if (var && var->declarationId() == tok->varId()) { if (var->isReference() || var->isRValueReference()) { if (!var->declEndToken()) return {{tok, true, std::move(errorPath)}}; if (var->isArgument()) { errorPath.emplace_back(var->declEndToken(), "Passed to reference."); return {{tok, true, std::move(errorPath)}}; } else if (Token::simpleMatch(var->declEndToken(), "=")) { errorPath.emplace_back(var->declEndToken(), "Assigned to reference."); const Token *vartok = var->declEndToken()->astOperand2(); const bool temporary = isTemporary(true, vartok, nullptr, true); const bool nonlocal = var->isStatic() || var->isGlobal(); if (vartok == tok || (nonlocal && temporary) || (!escape && (var->isConst() || var->isRValueReference()) && temporary)) return {{tok, true, std::move(errorPath)}}; if (vartok) return getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1); } else if (Token::simpleMatch(var->nameToken()->astParent(), ":") && var->nameToken()->astParent()->astParent() && Token::simpleMatch(var->nameToken()->astParent()->astParent()->previous(), "for (")) { errorPath.emplace_back(var->nameToken(), "Assigned to reference."); const Token* vartok = var->nameToken(); if (vartok == tok) return {{tok, true, std::move(errorPath)}}; const Token* contok = var->nameToken()->astParent()->astOperand2(); if (astIsContainer(contok)) return getLifetimeTokens(contok, escape, std::move(errorPath), pred, depth - 1); else return std::vector{}; } else { return std::vector {}; } } } else if (Token::Match(tok->previous(), "%name% (")) { const Function *f = tok->previous()->function(); if (f) { if (!Function::returnsReference(f)) return {{tok, std::move(errorPath)}}; std::vector result; std::vector returns = Function::findReturns(f); for (const Token* returnTok : returns) { if (returnTok == tok) continue; for (LifetimeToken& lt : getLifetimeTokens(returnTok, escape, errorPath, pred, depth - returns.size())) { const Token* argvarTok = lt.token; const Variable* argvar = argvarTok->variable(); if (!argvar) continue; if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { int n = getArgumentPos(argvar, f); if (n < 0) return std::vector {}; std::vector args = getArguments(tok->previous()); // TODO: Track lifetimes of default parameters if (n >= args.size()) return std::vector {}; const Token* argTok = args[n]; lt.errorPath.emplace_back(returnTok, "Return reference."); lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); std::vector arglts = LifetimeToken::setInconclusive( getLifetimeTokens(argTok, escape, std::move(lt.errorPath), pred, depth - returns.size()), returns.size() > 1); result.insert(result.end(), arglts.begin(), arglts.end()); } } } return result; } else if (Token::Match(tok->tokAt(-2), ". %name% (") && tok->tokAt(-2)->originalName() != "->" && astIsContainer(tok->tokAt(-2)->astOperand1())) { const Library::Container* library = getLibraryContainer(tok->tokAt(-2)->astOperand1()); Library::Container::Yield y = library->getYield(tok->previous()->str()); if (y == Library::Container::Yield::AT_INDEX || y == Library::Container::Yield::ITEM) { errorPath.emplace_back(tok->previous(), "Accessing container."); return LifetimeToken::setAddressOf( getLifetimeTokens(tok->tokAt(-2)->astOperand1(), escape, std::move(errorPath), pred, depth - 1), false); } } } else if (Token::Match(tok, ".|::|[") || tok->isUnaryOp("*")) { const Token *vartok = tok; if (tok->isUnaryOp("*")) vartok = tok->astOperand1(); while (vartok) { if (vartok->str() == "[" || vartok->originalName() == "->") vartok = vartok->astOperand1(); else if (vartok->str() == "." || vartok->str() == "::") vartok = vartok->astOperand2(); else break; } if (!vartok) return {{tok, std::move(errorPath)}}; const Variable *tokvar = vartok->variable(); const bool isContainer = astIsContainer(vartok) && !astIsPointer(vartok); if (!astIsUniqueSmartPointer(vartok) && !isContainer && !(tokvar && tokvar->isArray() && !tokvar->isArgument()) && (Token::Match(vartok->astParent(), "[|*") || vartok->astParent()->originalName() == "->")) { for (const ValueFlow::Value &v : vartok->values()) { if (!v.isLocalLifetimeValue()) continue; if (v.tokvalue == tok) continue; errorPath.insert(errorPath.end(), v.errorPath.begin(), v.errorPath.end()); return getLifetimeTokens(v.tokvalue, escape, std::move(errorPath), pred, depth - 1); } } else { return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1), !(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "["))); } } return {{tok, std::move(errorPath)}}; } std::vector getLifetimeTokens(const Token* tok, bool escape, ValueFlow::Value::ErrorPath errorPath) { return getLifetimeTokens(tok, escape, std::move(errorPath), [](const Token*) { return false; }); } bool hasLifetimeToken(const Token* tok, const Token* lifetime) { bool result = false; getLifetimeTokens(tok, false, ValueFlow::Value::ErrorPath{}, [&](const Token* tok2) { result = tok2->exprId() == lifetime->exprId(); return result; }); return result; } static const Token* getLifetimeToken(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr) { std::vector lts = getLifetimeTokens(tok); if (lts.size() != 1) return nullptr; if (lts.front().inconclusive) return nullptr; if (addressOf) *addressOf = lts.front().addressOf; errorPath.insert(errorPath.end(), lts.front().errorPath.begin(), lts.front().errorPath.end()); return lts.front().token; } const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf) { const Token* tok2 = getLifetimeToken(tok, errorPath, addressOf); if (tok2 && tok2->variable()) return tok2->variable(); return nullptr; } const Variable* getLifetimeVariable(const Token* tok) { ValueFlow::Value::ErrorPath errorPath; return getLifetimeVariable(tok, errorPath, nullptr); } static bool isNotLifetimeValue(const ValueFlow::Value& val) { return !val.isLifetimeValue(); } static bool isLifetimeOwned(const ValueType* vtParent) { if (vtParent->container) return !vtParent->container->view; return vtParent->type == ValueType::CONTAINER; } static bool isLifetimeOwned(const ValueType *vt, const ValueType *vtParent) { if (!vtParent) return false; if (!vt) { if (isLifetimeOwned(vtParent)) return true; return false; } // If converted from iterator to pointer then the iterator is most likely a pointer if (vtParent->pointer == 1 && vt->pointer == 0 && vt->type == ValueType::ITERATOR) return false; if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE) { if (vt->pointer != vtParent->pointer) return true; if (vt->type != vtParent->type) { if (vtParent->type == ValueType::RECORD) return true; if (isLifetimeOwned(vtParent)) return true; } } return false; } static bool isLifetimeBorrowed(const ValueType *vt, const ValueType *vtParent) { if (!vtParent) return false; if (!vt) return false; if (vt->pointer > 0 && vt->pointer == vtParent->pointer) return true; if (vtParent->container && vtParent->container->view) return true; if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE && vtParent->container == vt->container) { if (vtParent->pointer > vt->pointer) return true; if (vtParent->pointer < vt->pointer && vtParent->isIntegral()) return true; if (vtParent->str() == vt->str()) return true; if (vtParent->pointer == vt->pointer && vtParent->type == vt->type && vtParent->isIntegral()) // sign conversion return true; } return false; } static const Token* skipCVRefs(const Token* tok, const Token* endTok) { while (tok != endTok && Token::Match(tok, "const|volatile|auto|&|&&")) tok = tok->next(); return tok; } static bool isNotEqual(std::pair x, std::pair y) { const Token* start1 = x.first; const Token* start2 = y.first; if (start1 == nullptr || start2 == nullptr) return false; while (start1 != x.second && start2 != y.second) { const Token* tok1 = skipCVRefs(start1, x.second); if (tok1 != start1) { start1 = tok1; continue; } const Token* tok2 = skipCVRefs(start2, y.second); if (tok2 != start2) { start2 = tok2; continue; } if (start1->str() != start2->str()) return true; start1 = start1->next(); start2 = start2->next(); } start1 = skipCVRefs(start1, x.second); start2 = skipCVRefs(start2, y.second); return !(start1 == x.second && start2 == y.second); } static bool isNotEqual(std::pair x, const std::string& y) { TokenList tokenList(nullptr); std::istringstream istr(y); tokenList.createTokens(istr); return isNotEqual(x, std::make_pair(tokenList.front(), tokenList.back())); } static bool isNotEqual(std::pair x, const ValueType* y) { if (y == nullptr) return false; if (y->originalTypeName.empty()) return false; return isNotEqual(x, y->originalTypeName); } static bool isDifferentType(const Token* src, const Token* dst) { const Type* t = Token::typeOf(src); const Type* parentT = Token::typeOf(dst); if (t && parentT) { if (t->classDef && parentT->classDef && t->classDef != parentT->classDef) return true; } else { std::pair decl = Token::typeDecl(src); std::pair parentdecl = Token::typeDecl(dst); if (isNotEqual(decl, parentdecl)) return true; if (isNotEqual(decl, dst->valueType())) return true; if (isNotEqual(parentdecl, src->valueType())) return true; } return false; } static std::vector getParentValueTypes(const Token* tok, const Settings* settings = nullptr) { if (!tok) return {}; if (!tok->astParent()) return {}; if (Token::Match(tok->astParent(), "(|{|,")) { int argn = -1; const Token* ftok = getTokenArgumentFunction(tok, argn); if (ftok && ftok->function()) { std::vector result; std::vector argsVars = getArgumentVars(ftok, argn); for (const Variable* var : getArgumentVars(ftok, argn)) { if (!var) continue; if (!var->valueType()) continue; result.push_back(*var->valueType()); } return result; } } if (settings && Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") && astIsContainer(tok->astParent()->tokAt(-2)->astOperand1())) { const Token* contTok = tok->astParent()->tokAt(-2)->astOperand1(); const ValueType* vtCont = contTok->valueType(); if (!vtCont->containerTypeToken) return {}; ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings); return {std::move(vtParent)}; } if (tok->astParent()->valueType()) return {*tok->astParent()->valueType()}; return {}; } bool isLifetimeBorrowed(const Token *tok, const Settings *settings) { if (!tok) return true; if (tok->str() == ",") return true; if (!tok->astParent()) return true; if (!Token::Match(tok->astParent()->previous(), "%name% (") && !Token::simpleMatch(tok->astParent(), ",")) { if (!Token::simpleMatch(tok, "{")) { const ValueType *vt = tok->valueType(); const ValueType *vtParent = tok->astParent()->valueType(); if (isLifetimeBorrowed(vt, vtParent)) return true; if (isLifetimeOwned(vt, vtParent)) return false; } if (Token::Match(tok->astParent(), "return|(|{|%assign%")) { if (isDifferentType(tok, tok->astParent())) return false; } } else if (Token::Match(tok->astParent()->tokAt(-3), "%var% . push_back|push_front|insert|push (") && astIsContainer(tok->astParent()->tokAt(-3))) { const ValueType *vt = tok->valueType(); const ValueType *vtCont = tok->astParent()->tokAt(-3)->valueType(); if (!vtCont->containerTypeToken) return true; ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings); if (isLifetimeBorrowed(vt, &vtParent)) return true; if (isLifetimeOwned(vt, &vtParent)) return false; } return true; } static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings); static void valueFlowLifetimeConstructor(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings); static const Token* getEndOfVarScope(const Variable* var) { if (!var) return nullptr; const Scope* innerScope = var->scope(); const Scope* outerScope = innerScope; if (var->typeStartToken() && var->typeStartToken()->scope()) outerScope = var->typeStartToken()->scope(); if (!innerScope && outerScope) innerScope = outerScope; if (!innerScope || !outerScope) return nullptr; if (!innerScope->isExecutable()) return nullptr; // If the variable is defined in a for/while initializer then we want to // pick one token after the end so forward analysis can analyze the exit // conditions if (innerScope != outerScope && outerScope->isExecutable() && innerScope->isLocal()) return innerScope->bodyEnd->next(); return innerScope->bodyEnd; } static const Token* getEndOfExprScope(const Token* tok, const Scope* defaultScope = nullptr) { const Token* end = nullptr; bool local = false; visitAstNodes(tok, [&](const Token* child) { if (const Variable* var = child->variable()) { local |= var->isLocal(); if (var->isLocal() || var->isArgument()) { const Token* varEnd = getEndOfVarScope(var); if (!end || precedes(varEnd, end)) end = varEnd; } } return ChildrenToVisit::op1_and_op2; }); if (!end && defaultScope) end = defaultScope->bodyEnd; if (!end) { const Scope* scope = tok->scope(); if (scope) end = scope->bodyEnd; // If there is no local variables then pick the function scope if (!local) { while (scope && scope->isLocal()) scope = scope->nestedIn; if (scope && scope->isExecutable()) end = scope->bodyEnd; } } return end; } static const Token* getEndOfVarScope(const Token* tok, const std::vector& vars) { const Token* endOfVarScope = nullptr; for (const Variable* var : vars) { const Scope *varScope = nullptr; if (var && (var->isLocal() || var->isArgument()) && var->typeStartToken()->scope()->type != Scope::eNamespace) varScope = var->typeStartToken()->scope(); else if (!endOfVarScope) { varScope = tok->scope(); // A "local member" will be a expression like foo.x where foo is a local variable. // A "global member" will be a member that belongs to a global object. const bool globalMember = vars.size() == 1; // <- could check if it's a member here also but it seems redundant if (var && (var->isGlobal() || var->isNamespace() || globalMember)) { // Global variable => end of function while (varScope->isLocal()) varScope = varScope->nestedIn; } } if (varScope && (!endOfVarScope || precedes(varScope->bodyEnd, endOfVarScope))) endOfVarScope = varScope->bodyEnd; } return endOfVarScope; } static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { // Forward lifetimes to constructed variable if (Token::Match(tok->previous(), "%var% {")) { std::list values = tok->values(); values.remove_if(&isNotLifetimeValue); valueFlowForward(nextAfterAstRightmostLeaf(tok), getEndOfVarScope(tok, {tok->variable()}), tok->previous(), values, tokenlist, settings); return; } Token *parent = tok->astParent(); while (parent && parent->str() == ",") parent = parent->astParent(); if (!parent) return; // Assignment if (parent->str() == "=" && (!parent->astParent() || Token::simpleMatch(parent->astParent(), ";"))) { // Rhs values.. if (!parent->astOperand2() || parent->astOperand2()->values().empty()) return; if (!isLifetimeBorrowed(parent->astOperand2(), settings)) return; std::vector vars = getLHSVariables(parent); const Token* endOfVarScope = getEndOfVarScope(tok, vars); // Only forward lifetime values std::list values = parent->astOperand2()->values(); values.remove_if(&isNotLifetimeValue); // Skip RHS const Token *nextExpression = nextAfterAstRightmostLeaf(parent); if (Token::Match(parent->astOperand1(), ".|[|(") && parent->astOperand1()->exprId() > 0) { valueFlowForwardExpression( const_cast(nextExpression), endOfVarScope, parent->astOperand1(), values, tokenlist, settings); for (ValueFlow::Value& val : values) { if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::Address) val.lifetimeKind = ValueFlow::Value::LifetimeKind::SubObject; } } for (const Variable* var : vars) { valueFlowForward( const_cast(nextExpression), endOfVarScope, var->nameToken(), values, tokenlist, settings); if (tok->astTop() && Token::simpleMatch(tok->astTop()->previous(), "for (") && Token::simpleMatch(tok->astTop()->link(), ") {")) { Token* start = tok->astTop()->link()->next(); valueFlowForward(start, start->link(), var->nameToken(), values, tokenlist, settings); } } // Constructor } else if (Token::simpleMatch(parent, "{") && !isScopeBracket(parent)) { valueFlowLifetimeConstructor(parent, tokenlist, errorLogger, settings); valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); // Function call } else if (Token::Match(parent->previous(), "%name% (")) { valueFlowLifetimeFunction(parent->previous(), tokenlist, errorLogger, settings); valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); // Variable } else if (tok->variable()) { const Variable *var = tok->variable(); const Token *endOfVarScope = var->scope()->bodyEnd; std::list values = tok->values(); const Token *nextExpression = nextAfterAstRightmostLeaf(parent); // Only forward lifetime values values.remove_if(&isNotLifetimeValue); valueFlowForward(const_cast(nextExpression), endOfVarScope, tok, values, tokenlist, settings); // Cast } else if (parent->isCast()) { std::list values = tok->values(); // Only forward lifetime values values.remove_if(&isNotLifetimeValue); for (const ValueFlow::Value& value:values) setTokenValue(parent, value, tokenlist->getSettings()); valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); } } struct LifetimeStore { const Token *argtok; std::string message; ValueFlow::Value::LifetimeKind type; ErrorPath errorPath; bool inconclusive; bool forward; struct Context { Token* tok; TokenList* tokenlist; ErrorLogger* errorLogger; const Settings* settings; }; LifetimeStore() : argtok(nullptr), message(), type(), errorPath(), inconclusive(false), forward(true), mContext(nullptr) {} LifetimeStore(const Token* argtok, const std::string& message, ValueFlow::Value::LifetimeKind type = ValueFlow::Value::LifetimeKind::Object, bool inconclusive = false) : argtok(argtok), message(message), type(type), errorPath(), inconclusive(inconclusive), forward(true), mContext(nullptr) {} template static void forEach(const std::vector& argtoks, const std::string& message, ValueFlow::Value::LifetimeKind type, F f) { std::map forwardToks; for (const Token* arg : argtoks) { LifetimeStore ls{arg, message, type}; Context c{}; ls.mContext = &c; ls.forward = false; f(ls); if (c.tok) forwardToks[c.tok] = c; } for (const auto& p : forwardToks) { const Context& c = p.second; valueFlowForwardLifetime(c.tok, c.tokenlist, c.errorLogger, c.settings); } } static LifetimeStore fromFunctionArg(const Function * f, Token *tok, const Variable *var, TokenList *tokenlist, ErrorLogger *errorLogger) { if (!var) return LifetimeStore{}; if (!var->isArgument()) return LifetimeStore{}; int n = getArgumentPos(var, f); if (n < 0) return LifetimeStore{}; std::vector args = getArguments(tok); if (n >= args.size()) { if (tokenlist->getSettings()->debugwarnings) bailout(tokenlist, errorLogger, tok, "Argument mismatch: Function '" + tok->str() + "' returning lifetime from argument index " + std::to_string(n) + " but only " + std::to_string(args.size()) + " arguments are available."); return LifetimeStore{}; } const Token *argtok2 = args[n]; return LifetimeStore{argtok2, "Passed to '" + tok->expressionString() + "'.", ValueFlow::Value::LifetimeKind::Object}; } template bool byRef(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, Predicate pred) const { if (!argtok) return false; bool update = false; for (const LifetimeToken& lt : getLifetimeTokens(argtok)) { if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) continue; ErrorPath er = errorPath; er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end()); if (!lt.token) return false; if (!pred(lt.token)) return false; er.emplace_back(argtok, message); ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; value.tokvalue = lt.token; value.errorPath = std::move(er); value.lifetimeKind = type; value.setInconclusive(lt.inconclusive || inconclusive); // Don't add the value a second time if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) return false; setTokenValue(tok, value, tokenlist->getSettings()); update = true; } if (update && forward) forwardLifetime(tok, tokenlist, errorLogger, settings); return update; } bool byRef(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const { return byRef(tok, tokenlist, errorLogger, settings, [](const Token*) { return true; }); } template bool byVal(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, Predicate pred) const { if (!argtok) return false; bool update = false; if (argtok->values().empty()) { ErrorPath er; er.emplace_back(argtok, message); for (const LifetimeToken& lt : getLifetimeTokens(argtok)) { if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) continue; ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.tokvalue = lt.token; value.errorPath = er; value.lifetimeKind = type; value.setInconclusive(inconclusive || lt.inconclusive); const Variable* var = lt.token->variable(); if (var && var->isArgument()) { value.lifetimeScope = ValueFlow::Value::LifetimeScope::Argument; } else { continue; } // Don't add the value a second time if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) continue; ; setTokenValue(tok, value, tokenlist->getSettings()); update = true; } } for (const ValueFlow::Value &v : argtok->values()) { if (!v.isLifetimeValue()) continue; const Token *tok3 = v.tokvalue; for (const LifetimeToken& lt : getLifetimeTokens(tok3)) { if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) continue; ErrorPath er = v.errorPath; er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end()); if (!lt.token) return false; if (!pred(lt.token)) return false; er.emplace_back(argtok, message); er.insert(er.end(), errorPath.begin(), errorPath.end()); ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.lifetimeScope = v.lifetimeScope; value.path = v.path; value.tokvalue = lt.token; value.errorPath = std::move(er); value.lifetimeKind = type; value.setInconclusive(lt.inconclusive || v.isInconclusive() || inconclusive); // Don't add the value a second time if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) continue; setTokenValue(tok, value, tokenlist->getSettings()); update = true; } } if (update && forward) forwardLifetime(tok, tokenlist, errorLogger, settings); return update; } bool byVal(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const { return byVal(tok, tokenlist, errorLogger, settings, [](const Token*) { return true; }); } template void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { if (!settings->certainty.isEnabled(Certainty::inconclusive) && inconclusive) return; if (!argtok) return; for (const ValueFlow::Value &v : argtok->values()) { if (!v.isLifetimeValue()) continue; const Token *tok2 = v.tokvalue; ErrorPath er = v.errorPath; const Variable *var = getLifetimeVariable(tok2, er); er.insert(er.end(), errorPath.begin(), errorPath.end()); if (!var) continue; for (const Token *tok3 = tok; tok3 && tok3 != var->declEndToken(); tok3 = tok3->previous()) { if (tok3->varId() == var->declarationId()) { LifetimeStore{tok3, message, type, inconclusive}.byVal(tok, tokenlist, errorLogger, settings, pred); break; } } } } void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { byDerefCopy(tok, tokenlist, errorLogger, settings, [](const Token *) { return true; }); } private: Context* mContext; void forwardLifetime(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) const { if (mContext) { mContext->tok = tok; mContext->tokenlist = tokenlist; mContext->errorLogger = errorLogger; mContext->settings = settings; } valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } }; static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { if (!Token::Match(tok, "%name% (")) return; Token* memtok = nullptr; if (Token::Match(tok->astParent(), ". %name% (") && astIsRHS(tok)) memtok = tok->astParent()->astOperand1(); int returnContainer = settings->library.returnValueContainer(tok); if (returnContainer >= 0) { std::vector args = getArguments(tok); for (int argnr = 1; argnr <= args.size(); ++argnr) { const Library::ArgumentChecks::IteratorInfo *i = settings->library.getArgIteratorInfo(tok, argnr); if (!i) continue; if (i->container != returnContainer) continue; const Token * const argTok = args[argnr - 1]; bool forward = false; for (ValueFlow::Value val : argTok->values()) { if (!val.isLifetimeValue()) continue; val.errorPath.emplace_back(argTok, "Passed to '" + tok->str() + "'."); setTokenValue(tok->next(), val, settings); forward = true; } // Check if lifetime is available to avoid adding the lifetime twice if (forward) { valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); break; } } } else if (Token::Match(tok->tokAt(-2), "std :: ref|cref|tie|front_inserter|back_inserter")) { for (const Token *argtok : getArguments(tok)) { LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}.byRef( tok->next(), tokenlist, errorLogger, settings); } } else if (Token::Match(tok->tokAt(-2), "std :: make_tuple|tuple_cat|make_pair|make_reverse_iterator|next|prev|move|bind")) { for (const Token *argtok : getArguments(tok)) { LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}.byVal( tok->next(), tokenlist, errorLogger, settings); } } else if (memtok && Token::Match(tok->astParent(), ". push_back|push_front|insert|push|assign") && astIsContainer(memtok)) { std::vector args = getArguments(tok); std::size_t n = args.size(); if (n > 1 && Token::typeStr(args[n - 2]) == Token::typeStr(args[n - 1]) && (((astIsIterator(args[n - 2]) && astIsIterator(args[n - 1])) || (astIsPointer(args[n - 2]) && astIsPointer(args[n - 1]))))) { LifetimeStore{ args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byDerefCopy(memtok, tokenlist, errorLogger, settings); } else if (!args.empty() && isLifetimeBorrowed(args.back(), settings)) { LifetimeStore{ args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byVal(memtok, tokenlist, errorLogger, settings); } } else if (tok->function()) { const Function *f = tok->function(); if (Function::returnsReference(f)) return; std::vector returns = Function::findReturns(f); const bool inconclusive = returns.size() > 1; bool update = false; for (const Token* returnTok : returns) { if (returnTok == tok) continue; const Variable *returnVar = getLifetimeVariable(returnTok); if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, settings, tokenlist->isCPP()))) { LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, errorLogger); ls.inconclusive = inconclusive; ls.forward = false; update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings); } for (const ValueFlow::Value &v : returnTok->values()) { if (!v.isLifetimeValue()) continue; if (!v.tokvalue) continue; if (memtok && (contains({ValueFlow::Value::LifetimeScope::ThisPointer, ValueFlow::Value::LifetimeScope::ThisValue}, v.lifetimeScope) || exprDependsOnThis(v.tokvalue))) { LifetimeStore ls = LifetimeStore{memtok, "Passed to member function '" + tok->expressionString() + "'.", ValueFlow::Value::LifetimeKind::Object}; ls.inconclusive = inconclusive; ls.forward = false; ls.errorPath = v.errorPath; ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + "."); if (v.lifetimeScope == ValueFlow::Value::LifetimeScope::ThisValue) update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings); else update |= ls.byRef(tok->next(), tokenlist, errorLogger, settings); continue; } const Variable *var = v.tokvalue->variable(); LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, var, tokenlist, errorLogger); if (!ls.argtok) continue; ls.forward = false; ls.inconclusive = inconclusive; ls.errorPath = v.errorPath; ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + "."); if (!v.isArgumentLifetimeValue() && (var->isReference() || var->isRValueReference())) { update |= ls.byRef(tok->next(), tokenlist, errorLogger, settings); } else if (v.isArgumentLifetimeValue()) { update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings); } } } if (update) valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings); } else if (tok->valueType()) { // TODO: Propagate lifetimes with library functions if (settings->library.getFunction(tok->previous())) return; // Assume constructing the valueType valueFlowLifetimeConstructor(tok, tokenlist, errorLogger, settings); valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings); } } static void valueFlowLifetimeConstructor(Token* tok, const Type* t, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) { if (!Token::Match(tok, "(|{")) return; if (!t) { if (tok->valueType() && tok->valueType()->type != ValueType::RECORD) return; // If the type is unknown then assume it captures by value in the // constructor, but make each lifetime inconclusive std::vector args = getArguments(tok); LifetimeStore::forEach( args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::SubObject, [&](LifetimeStore& ls) { ls.inconclusive = true; ls.byVal(tok, tokenlist, errorLogger, settings); }); return; } const Scope* scope = t->classScope; if (!scope) return; // Only support aggregate constructors for now if (scope->numConstructors == 0 && t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) { std::vector args = getArguments(tok); auto it = scope->varlist.begin(); LifetimeStore::forEach(args, "Passed to constructor of '" + t->name() + "'.", ValueFlow::Value::LifetimeKind::SubObject, [&](const LifetimeStore& ls) { if (it == scope->varlist.end()) return; const Variable& var = *it; if (var.isReference() || var.isRValueReference()) { ls.byRef(tok, tokenlist, errorLogger, settings); } else { ls.byVal(tok, tokenlist, errorLogger, settings); } it++; }); } } static bool hasInitList(const Token* tok) { if (astIsPointer(tok)) return true; if (astIsContainer(tok)) { const Library::Container * library = getLibraryContainer(tok); if (!library) return false; return library->hasInitializerListConstructor; } return false; } static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) { if (!Token::Match(tok, "(|{")) return; Token* parent = tok->astParent(); while (Token::simpleMatch(parent, ",")) parent = parent->astParent(); if (Token::Match(tok, "{|(") && astIsContainerView(tok) && !tok->function()) { std::vector args = getArguments(tok); if (args.size() == 1 && astIsContainerOwned(args.front())) { LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject}.byRef( tok, tokenlist, errorLogger, settings); } } else if (Token::simpleMatch(parent, "{") && hasInitList(parent->astParent())) { valueFlowLifetimeConstructor(tok, Token::typeOf(parent->previous()), tokenlist, errorLogger, settings); } else if (Token::simpleMatch(tok, "{") && hasInitList(parent)) { std::vector args = getArguments(tok); // Assume range constructor if passed a pair of iterators if (astIsContainer(parent) && args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1])) { LifetimeStore::forEach( args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::SubObject, [&](const LifetimeStore& ls) { ls.byDerefCopy(tok, tokenlist, errorLogger, settings); }); } else { LifetimeStore::forEach(args, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::SubObject, [&](const LifetimeStore& ls) { ls.byVal(tok, tokenlist, errorLogger, settings); }); } } else { valueFlowLifetimeConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings); } } struct Lambda { enum class Capture { Undefined, ByValue, ByReference }; explicit Lambda(const Token * tok) : capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr), explicitCaptures(), implicitCapture(Capture::Undefined) { if (!Token::simpleMatch(tok, "[") || !tok->link()) return; capture = tok; if (Token::simpleMatch(capture->link(), "] (")) { arguments = capture->link()->next(); } const Token * afterArguments = arguments ? arguments->link()->next() : capture->link()->next(); if (afterArguments && afterArguments->originalName() == "->") { returnTok = afterArguments->next(); bodyTok = Token::findsimplematch(returnTok, "{"); } else if (Token::simpleMatch(afterArguments, "{")) { bodyTok = afterArguments; } for (const Token* c:getCaptures()) { if (Token::Match(c, "this !!.")) { explicitCaptures[c->variable()] = std::make_pair(c, Capture::ByReference); } else if (Token::simpleMatch(c, "* this")) { explicitCaptures[c->next()->variable()] = std::make_pair(c->next(), Capture::ByValue); } else if (c->variable()) { explicitCaptures[c->variable()] = std::make_pair(c, Capture::ByValue); } else if (c->isUnaryOp("&") && Token::Match(c->astOperand1(), "%var%")) { explicitCaptures[c->astOperand1()->variable()] = std::make_pair(c->astOperand1(), Capture::ByReference); } else { const std::string& s = c->expressionString(); if (s == "=") implicitCapture = Capture::ByValue; else if (s == "&") implicitCapture = Capture::ByReference; } } } const Token * capture; const Token * arguments; const Token * returnTok; const Token * bodyTok; std::unordered_map> explicitCaptures; Capture implicitCapture; std::vector getCaptures() { return getArguments(capture); } bool isLambda() const { return capture && bodyTok; } }; static bool isDecayedPointer(const Token *tok) { if (!tok) return false; if (!tok->astParent()) return false; if (astIsPointer(tok->astParent()) && !Token::simpleMatch(tok->astParent(), "return")) return true; if (tok->astParent()->isConstOp()) return true; if (!Token::simpleMatch(tok->astParent(), "return")) return false; return astIsPointer(tok->astParent()); } static bool isConvertedToView(const Token* tok, const Settings* settings) { std::vector vtParents = getParentValueTypes(tok, settings); return std::any_of(vtParents.begin(), vtParents.end(), [&](const ValueType& vt) { if (!vt.container) return false; return vt.container->view; }); } static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->scope()) continue; if (tok->scope()->type == Scope::eGlobal) continue; Lambda lam(tok); // Lamdas if (lam.isLambda()) { const Scope * bodyScope = lam.bodyTok->scope(); std::set scopes; // Avoid capturing a variable twice std::set varids; bool capturedThis = false; auto isImplicitCapturingVariable = [&](const Token *varTok) { const Variable *var = varTok->variable(); if (!var) return false; if (varids.count(var->declarationId()) > 0) return false; if (!var->isLocal() && !var->isArgument()) return false; const Scope *scope = var->scope(); if (!scope) return false; if (scopes.count(scope) > 0) return false; if (scope->isNestedIn(bodyScope)) return false; scopes.insert(scope); varids.insert(var->declarationId()); return true; }; bool update = false; auto captureVariable = [&](const Token* tok2, Lambda::Capture c, std::function pred) { if (varids.count(tok->varId()) > 0) return; ErrorPath errorPath; if (c == Lambda::Capture::ByReference) { LifetimeStore ls{ tok2, "Lambda captures variable by reference here.", ValueFlow::Value::LifetimeKind::Lambda}; ls.forward = false; update |= ls.byRef(tok, tokenlist, errorLogger, settings, pred); } else if (c == Lambda::Capture::ByValue) { LifetimeStore ls{ tok2, "Lambda captures variable by value here.", ValueFlow::Value::LifetimeKind::Lambda}; ls.forward = false; update |= ls.byVal(tok, tokenlist, errorLogger, settings, pred); pred(tok2); } }; auto captureThisVariable = [&](const Token* tok2, Lambda::Capture c) { ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; if (c == Lambda::Capture::ByReference) value.lifetimeScope = ValueFlow::Value::LifetimeScope::ThisPointer; else if (c == Lambda::Capture::ByValue) value.lifetimeScope = ValueFlow::Value::LifetimeScope::ThisValue; value.tokvalue = tok2; value.errorPath.push_back({tok2, "Lambda captures the 'this' variable here."}); value.lifetimeKind = ValueFlow::Value::LifetimeKind::Lambda; capturedThis = true; // Don't add the value a second time if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) return; setTokenValue(tok, value, tokenlist->getSettings()); update |= true; }; // Handle explicit capture for (const auto& p:lam.explicitCaptures) { const Variable* var = p.first; const Token* tok2 = p.second.first; Lambda::Capture c = p.second.second; if (Token::Match(tok2, "this !!.")) { captureThisVariable(tok2, c); } else if (var) { captureVariable(tok2, c, [](const Token*) { return true; }); varids.insert(var->declarationId()); } } auto isImplicitCapturingThis = [&](const Token* tok2) { if (capturedThis) return false; if (Token::simpleMatch(tok2, "this")) { return true; } else if (tok2->variable()) { if (Token::simpleMatch(tok2->previous(), ".")) return false; const Variable* var = tok2->variable(); if (var->isLocal()) return false; if (var->isArgument()) return false; return exprDependsOnThis(tok2); } else if (Token::simpleMatch(tok2, "(")) { return exprDependsOnThis(tok2); } return false; }; for (const Token * tok2 = lam.bodyTok; tok2 != lam.bodyTok->link(); tok2 = tok2->next()) { if (isImplicitCapturingThis(tok2)) { captureThisVariable(tok2, Lambda::Capture::ByReference); } else if (tok2->variable()) { captureVariable(tok2, lam.implicitCapture, isImplicitCapturingVariable); } } if (update) valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } // address of else if (tok->isUnaryOp("&")) { for (const LifetimeToken& lt : getLifetimeTokens(tok->astOperand1())) { if (!settings->certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) continue; ErrorPath errorPath = lt.errorPath; errorPath.emplace_back(tok, "Address of variable taken here."); ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; value.tokvalue = lt.token; value.errorPath = std::move(errorPath); if (lt.addressOf || astIsPointer(lt.token) || !Token::Match(lt.token->astParent(), ".|[")) value.lifetimeKind = ValueFlow::Value::LifetimeKind::Address; value.setInconclusive(lt.inconclusive); setTokenValue(tok, value, tokenlist->getSettings()); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } // Converting to container view else if (astIsContainerOwned(tok) && isConvertedToView(tok, settings)) { LifetimeStore ls = LifetimeStore{tok, "Converted to container view", ValueFlow::Value::LifetimeKind::SubObject}; ls.byRef(tok, tokenlist, errorLogger, settings); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } // container lifetimes else if (astIsContainer(tok)) { Token * parent = astParentSkipParens(tok); if (!Token::Match(parent, ". %name% (")) continue; bool isContainerOfPointers = true; const Token* containerTypeToken = tok->valueType()->containerTypeToken; if (containerTypeToken) { ValueType vt = ValueType::parseDecl(containerTypeToken, settings); isContainerOfPointers = vt.pointer > 0; } ValueFlow::Value master; master.valueType = ValueFlow::Value::ValueType::LIFETIME; master.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; if (astIsIterator(parent->tokAt(2))) { master.errorPath.emplace_back(parent->tokAt(2), "Iterator to container is created here."); master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator; } else if ((astIsPointer(parent->tokAt(2)) && !isContainerOfPointers) || Token::Match(parent->next(), "data|c_str")) { master.errorPath.emplace_back(parent->tokAt(2), "Pointer to container is created here."); master.lifetimeKind = ValueFlow::Value::LifetimeKind::Object; } else { continue; } std::vector toks = {}; if (tok->isUnaryOp("*") || parent->originalName() == "->") { for (const ValueFlow::Value& v : tok->values()) { if (!v.isLocalLifetimeValue()) continue; if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) continue; if (!v.tokvalue) continue; toks.push_back(v.tokvalue); } } else if (astIsContainerView(tok)) { for (const ValueFlow::Value& v : tok->values()) { if (!v.isLifetimeValue()) continue; if (!v.tokvalue) continue; if (!astIsContainerOwned(v.tokvalue)) continue; toks.push_back(v.tokvalue); } } else { toks = {tok}; } for (const Token* tok2 : toks) { for (const ReferenceToken& rt : followAllReferences(tok2, false)) { ValueFlow::Value value = master; value.tokvalue = rt.token; value.errorPath.insert(value.errorPath.begin(), rt.errors.begin(), rt.errors.end()); setTokenValue(parent->tokAt(2), value, tokenlist->getSettings()); if (!rt.token->variable()) { LifetimeStore ls = LifetimeStore{ rt.token, master.errorPath.back().second, ValueFlow::Value::LifetimeKind::Object}; ls.byRef(parent->tokAt(2), tokenlist, errorLogger, settings); } } } valueFlowForwardLifetime(parent->tokAt(2), tokenlist, errorLogger, settings); } // Check constructors else if (Token::Match(tok, "=|return|%type%|%var% {")) { valueFlowLifetimeConstructor(tok->next(), tokenlist, errorLogger, settings); } // Check function calls else if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->next()->link(), ") {")) { valueFlowLifetimeFunction(tok, tokenlist, errorLogger, settings); } // Unique pointer lifetimes else if (astIsUniqueSmartPointer(tok) && astIsLHS(tok) && Token::simpleMatch(tok->astParent(), ". get ( )")) { Token* ptok = tok->astParent()->tokAt(2); ErrorPath errorPath = {{ptok, "Raw pointer to smart pointer created here."}}; ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; value.lifetimeKind = ValueFlow::Value::LifetimeKind::SubObject; value.tokvalue = tok; value.errorPath = errorPath; setTokenValue(ptok, value, tokenlist->getSettings()); valueFlowForwardLifetime(ptok, tokenlist, errorLogger, settings); } // Check variables else if (tok->variable()) { ErrorPath errorPath; const Variable * var = getLifetimeVariable(tok, errorPath); if (!var) continue; if (var->nameToken() == tok) continue; if (var->isArray() && !var->isStlType() && !var->isArgument() && isDecayedPointer(tok)) { errorPath.emplace_back(tok, "Array decayed to pointer here."); ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; value.tokvalue = var->nameToken(); value.errorPath = errorPath; setTokenValue(tok, value, tokenlist->getSettings()); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } // Forward any lifetimes else if (std::any_of(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } } static bool isStdMoveOrStdForwarded(Token * tok, ValueFlow::Value::MoveKind * moveKind, Token ** varTok = nullptr) { if (tok->str() != "std") return false; ValueFlow::Value::MoveKind kind = ValueFlow::Value::MoveKind::NonMovedVariable; Token * variableToken = nullptr; if (Token::Match(tok, "std :: move ( %var% )")) { variableToken = tok->tokAt(4); kind = ValueFlow::Value::MoveKind::MovedVariable; } else if (Token::simpleMatch(tok, "std :: forward <")) { const Token * const leftAngle = tok->tokAt(3); Token * rightAngle = leftAngle->link(); if (Token::Match(rightAngle, "> ( %var% )")) { variableToken = rightAngle->tokAt(2); kind = ValueFlow::Value::MoveKind::ForwardedVariable; } } if (!variableToken) return false; if (variableToken->strAt(2) == ".") // Only partially moved return false; if (variableToken->valueType() && variableToken->valueType()->type >= ValueType::Type::VOID) return false; if (moveKind != nullptr) *moveKind = kind; if (varTok != nullptr) *varTok = variableToken; return true; } static bool isOpenParenthesisMemberFunctionCallOfVarId(const Token * openParenthesisToken, nonneg int varId) { const Token * varTok = openParenthesisToken->tokAt(-3); return Token::Match(varTok, "%varid% . %name% (", varId) && varTok->next()->originalName() == emptyString; } static const Token * findOpenParentesisOfMove(const Token * moveVarTok) { const Token * tok = moveVarTok; while (tok && tok->str() != "(") tok = tok->previous(); return tok; } static const Token * findEndOfFunctionCallForParameter(const Token * parameterToken) { if (!parameterToken) return nullptr; const Token * parent = parameterToken->astParent(); while (parent && !parent->isOp() && parent->str() != "(") parent = parent->astParent(); if (!parent) return nullptr; return nextAfterAstRightmostLeaf(parent); } static void valueFlowAfterMove(TokenList* tokenlist, SymbolDatabase* symboldatabase, const Settings* settings) { if (!tokenlist->isCPP() || settings->standards.cpp < Standards::CPP11) return; for (const Scope * scope : symboldatabase->functionScopes) { if (!scope) continue; const Token * start = scope->bodyStart; if (scope->function) { const Token * memberInitializationTok = scope->function->constructorMemberInitialization(); if (memberInitializationTok) start = memberInitializationTok; } for (Token* tok = const_cast(start); tok != scope->bodyEnd; tok = tok->next()) { Token * varTok; if (Token::Match(tok, "%var% . reset|clear (") && tok->next()->originalName() == emptyString) { varTok = tok; ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::MOVED; value.moveKind = ValueFlow::Value::MoveKind::NonMovedVariable; value.errorPath.emplace_back(tok, "Calling " + tok->next()->expressionString() + " makes " + tok->str() + " 'non-moved'"); value.setKnown(); std::list values; values.push_back(value); const Variable *var = varTok->variable(); if (!var || (!var->isLocal() && !var->isArgument())) continue; const Token * const endOfVarScope = var->scope()->bodyEnd; setTokenValue(varTok, value, settings); valueFlowForward(varTok->next(), endOfVarScope, varTok, values, tokenlist, settings); continue; } ValueFlow::Value::MoveKind moveKind; if (!isStdMoveOrStdForwarded(tok, &moveKind, &varTok)) continue; const nonneg int varId = varTok->varId(); // x is not MOVED after assignment if code is: x = ... std::move(x) .. ; const Token *parent = tok->astParent(); while (parent && parent->str() != "=" && parent->str() != "return" && !(parent->str() == "(" && isOpenParenthesisMemberFunctionCallOfVarId(parent, varId))) parent = parent->astParent(); if (parent && (parent->str() == "return" || // MOVED in return statement parent->str() == "(")) // MOVED in self assignment, isOpenParenthesisMemberFunctionCallOfVarId == true continue; if (parent && parent->astOperand1() && parent->astOperand1()->varId() == varId) continue; const Variable *var = varTok->variable(); if (!var) continue; const Token * const endOfVarScope = var->scope()->bodyEnd; ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::MOVED; value.moveKind = moveKind; if (moveKind == ValueFlow::Value::MoveKind::MovedVariable) value.errorPath.emplace_back(tok, "Calling std::move(" + varTok->str() + ")"); else // if (moveKind == ValueFlow::Value::ForwardedVariable) value.errorPath.emplace_back(tok, "Calling std::forward(" + varTok->str() + ")"); value.setKnown(); std::list values; values.push_back(value); const Token * openParentesisOfMove = findOpenParentesisOfMove(varTok); const Token * endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove); if (endOfFunctionCall) valueFlowForward( const_cast(endOfFunctionCall), endOfVarScope, varTok, values, tokenlist, settings); } } } static const Token* findIncompleteVar(const Token* start, const Token* end) { for (const Token* tok = start; tok != end; tok = tok->next()) { if (tok->isIncompleteVar()) return tok; } return nullptr; } static ValueFlow::Value makeConditionValue(long long val, const Token* condTok, bool assume) { ValueFlow::Value v(val); v.setKnown(); v.condition = condTok; if (assume) v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is true"); else v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is false"); return v; } static std::vector getConditions(const Token* tok, const char* op) { std::vector conds = {tok}; if (tok->str() == op) { std::vector args = astFlatten(tok, op); std::copy_if(args.begin(), args.end(), std::back_inserter(conds), [&](const Token* tok2) { if (tok2->exprId() == 0) return false; if (tok2->hasKnownIntValue()) return false; if (Token::Match(tok2, "%var%|.") && !astIsBool(tok2)) return false; return true; }); } return conds; } // static void valueFlowConditionExpressions(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope * scope : symboldatabase->functionScopes) { if (const Token* incompleteTok = findIncompleteVar(scope->bodyStart, scope->bodyEnd)) { if (incompleteTok->isIncompleteVar()) { if (settings->debugwarnings) bailoutIncompleteVar(tokenlist, errorLogger, incompleteTok, "Skipping function due to incomplete variable " + incompleteTok->str()); break; } } for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; Token * parenTok = tok->next(); if (!Token::simpleMatch(parenTok->link(), ") {")) continue; Token * blockTok = parenTok->link()->tokAt(1); const Token* condTok = parenTok->astOperand2(); if (condTok->exprId() == 0) continue; if (condTok->hasKnownIntValue()) continue; if (!isConstExpression(condTok, settings->library, true, tokenlist->isCPP())) continue; const bool is1 = (condTok->isComparisonOp() || condTok->tokType() == Token::eLogicalOp || astIsBool(condTok)); Token* startTok = blockTok; // Inner condition { for (const Token* condTok2 : getConditions(condTok, "&&")) { if (is1) { ExpressionAnalyzer a1(condTok2, makeConditionValue(1, condTok2, true), tokenlist); valueFlowGenericForward(startTok, startTok->link(), a1, settings); } OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(0, condTok2, true), tokenlist); valueFlowGenericForward(startTok, startTok->link(), a2, settings); } } std::vector conds = getConditions(condTok, "||"); // Check else block if (Token::simpleMatch(startTok->link(), "} else {")) { startTok = startTok->link()->tokAt(2); for (const Token* condTok2:conds) { ExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false), tokenlist); valueFlowGenericForward(startTok, startTok->link(), a1, settings); if (is1) { OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false), tokenlist); valueFlowGenericForward(startTok, startTok->link(), a2, settings); } } } // Check if the block terminates early if (isEscapeScope(blockTok, tokenlist)) { for (const Token* condTok2:conds) { ExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false), tokenlist); valueFlowGenericForward(startTok->link()->next(), scope->bodyEnd, a1, settings); if (is1) { OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false), tokenlist); valueFlowGenericForward(startTok->link()->next(), scope->bodyEnd, a2, settings); } } } } } } static bool isTruncated(const ValueType* src, const ValueType* dst, const Settings* settings) { if (src->pointer > 0 || dst->pointer > 0) return src->pointer != dst->pointer; if (src->smartPointer && dst->smartPointer) return false; if ((src->isIntegral() && dst->isIntegral()) || (src->isFloat() && dst->isFloat())) { size_t srcSize = ValueFlow::getSizeOf(*src, settings); size_t dstSize = ValueFlow::getSizeOf(*dst, settings); if (srcSize > dstSize) return true; if (srcSize == dstSize && src->sign != dst->sign) return true; } else if (src->type == dst->type) { if (src->type == ValueType::Type::RECORD) return src->typeScope != dst->typeScope; } else { return true; } return false; } static void setSymbolic(ValueFlow::Value& value, const Token* tok) { assert(tok && tok->exprId() > 0 && "Missing expr id for symbolic value"); value.valueType = ValueFlow::Value::ValueType::SYMBOLIC; value.tokvalue = tok; } static ValueFlow::Value makeSymbolic(const Token* tok, MathLib::bigint delta = 0) { ValueFlow::Value value; value.setKnown(); setSymbolic(value, tok); value.intvalue = delta; return value; } static std::set getVarIds(const Token* tok) { std::set result; visitAstNodes(tok, [&](const Token* child) { if (child->varId() > 0) result.insert(child->varId()); return ChildrenToVisit::op1_and_op2; }); return result; } static void valueFlowSymbolic(TokenList* tokenlist, SymbolDatabase* symboldatabase) { for (const Scope* scope : symboldatabase->functionScopes) { for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "=")) continue; if (tok->astParent()) continue; if (!tok->astOperand1()) continue; if (!tok->astOperand2()) continue; if (tok->astOperand1()->hasKnownIntValue()) continue; if (tok->astOperand2()->hasKnownIntValue()) continue; if (tok->astOperand1()->exprId() == 0) continue; if (tok->astOperand2()->exprId() == 0) continue; if (!isConstExpression(tok->astOperand2(), tokenlist->getSettings()->library, true, tokenlist->isCPP())) continue; if (tok->astOperand1()->valueType() && tok->astOperand2()->valueType()) { if (isTruncated( tok->astOperand2()->valueType(), tok->astOperand1()->valueType(), tokenlist->getSettings())) continue; } else if (isDifferentType(tok->astOperand2(), tok->astOperand1())) { continue; } const std::set rhsVarIds = getVarIds(tok->astOperand2()); const std::vector vars = getLHSVariables(tok); if (std::any_of(vars.begin(), vars.end(), [&](const Variable* var) { if (rhsVarIds.count(var->declarationId()) > 0) return true; if (var->isLocal()) return var->isStatic(); return !var->isArgument(); })) continue; Token* start = nextAfterAstRightmostLeaf(tok); const Token* end = scope->bodyEnd; ValueFlow::Value rhs = makeSymbolic(tok->astOperand2()); rhs.errorPath.emplace_back(tok, tok->astOperand1()->expressionString() + " is assigned '" + tok->astOperand2()->expressionString() + "' here."); valueFlowForward(start, end, tok->astOperand1(), {rhs}, tokenlist, tokenlist->getSettings()); ValueFlow::Value lhs = makeSymbolic(tok->astOperand1()); lhs.errorPath.emplace_back(tok, tok->astOperand1()->expressionString() + " is assigned '" + tok->astOperand2()->expressionString() + "' here."); valueFlowForward(start, end, tok->astOperand2(), {lhs}, tokenlist, tokenlist->getSettings()); } } } static void valueFlowSymbolicIdentity(TokenList* tokenlist) { for (Token* tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->hasKnownIntValue()) continue; if (!Token::Match(tok, "*|/|<<|>>|^|+|-|%or%")) continue; if (!tok->astOperand1()) continue; if (!tok->astOperand2()) continue; if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) continue; const ValueFlow::Value* constant = nullptr; const Token* vartok = nullptr; if (tok->astOperand1()->hasKnownIntValue()) { constant = &tok->astOperand1()->values().front(); vartok = tok->astOperand2(); } if (tok->astOperand2()->hasKnownIntValue()) { constant = &tok->astOperand2()->values().front(); vartok = tok->astOperand1(); } if (!constant) continue; if (!vartok) continue; if (vartok->exprId() == 0) continue; if (Token::Match(tok, "<<|>>|/") && !astIsLHS(vartok)) continue; if (Token::Match(tok, "<<|>>|^|+|-|%or%") && constant->intvalue != 0) continue; if (Token::Match(tok, "*|/") && constant->intvalue != 1) continue; std::vector values = {makeSymbolic(vartok)}; std::unordered_set ids = {vartok->exprId()}; std::copy_if( vartok->values().begin(), vartok->values().end(), std::back_inserter(values), [&](const ValueFlow::Value& v) { if (!v.isSymbolicValue()) return false; if (!v.tokvalue) return false; return ids.insert(v.tokvalue->exprId()).second; }); for (const ValueFlow::Value& v : values) setTokenValue(tok, v, tokenlist->getSettings()); } } static void valueFlowSymbolicAbs(TokenList* tokenlist, SymbolDatabase* symboldatabase) { for (const Scope* scope : symboldatabase->functionScopes) { for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "abs|labs|llabs|fabs|fabsf|fabsl (")) continue; if (tok->hasKnownIntValue()) continue; const Token* arg = tok->next()->astOperand2(); if (!arg) continue; ValueFlow::Value c = inferCondition(">=", arg, 0); if (!c.isKnown()) continue; ValueFlow::Value v = makeSymbolic(arg); v.errorPath = c.errorPath; v.errorPath.emplace_back(tok, "Passed to " + tok->str()); if (c.intvalue == 0) v.setImpossible(); else v.setKnown(); setTokenValue(tok->next(), v, tokenlist->getSettings()); } } } struct SymbolicInferModel : InferModel { const Token* expr; explicit SymbolicInferModel(const Token* tok) : expr(tok) { assert(expr->exprId() != 0); } virtual bool match(const ValueFlow::Value& value) const OVERRIDE { return value.isSymbolicValue() && value.tokvalue && value.tokvalue->exprId() == expr->exprId(); } virtual ValueFlow::Value yield(MathLib::bigint value) const OVERRIDE { ValueFlow::Value result(value); result.valueType = ValueFlow::Value::ValueType::SYMBOLIC; result.tokvalue = expr; result.setKnown(); return result; } }; static void valueFlowSymbolicInfer(TokenList* tokenlist, SymbolDatabase* symboldatabase) { for (const Scope* scope : symboldatabase->functionScopes) { for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "-|%comp%")) continue; if (tok->hasKnownIntValue()) continue; if (!tok->astOperand1()) continue; if (!tok->astOperand2()) continue; if (tok->astOperand1()->exprId() == 0) continue; if (tok->astOperand2()->exprId() == 0) continue; if (tok->astOperand1()->hasKnownIntValue()) continue; if (tok->astOperand2()->hasKnownIntValue()) continue; if (astIsFloat(tok->astOperand1(), false)) continue; if (astIsFloat(tok->astOperand2(), false)) continue; SymbolicInferModel leftModel{tok->astOperand1()}; std::vector values = infer(leftModel, tok->str(), 0, tok->astOperand2()->values()); if (values.empty()) { SymbolicInferModel rightModel{tok->astOperand2()}; values = infer(rightModel, tok->str(), tok->astOperand1()->values(), 0); } for (const ValueFlow::Value& value : values) { setTokenValue(tok, value, tokenlist->getSettings()); } } } } static void valueFlowForwardConst(Token* start, const Token* end, const Variable* var, const std::list& values, const Settings* const settings) { for (Token* tok = start; tok != end; tok = tok->next()) { if (tok->varId() == var->declarationId()) { for (const ValueFlow::Value& value : values) setTokenValue(tok, value, settings); } else { [&] { // Follow references std::vector refs = followAllReferences(tok); ValueFlow::Value::ValueKind refKind = refs.size() == 1 ? ValueFlow::Value::ValueKind::Known : ValueFlow::Value::ValueKind::Inconclusive; for (const ReferenceToken& ref : refs) { if (ref.token->varId() == var->declarationId()) { for (ValueFlow::Value value : values) { value.valueKind = refKind; value.errorPath.insert(value.errorPath.end(), ref.errors.begin(), ref.errors.end()); setTokenValue(tok, value, settings); } return; } } // Follow symbolic vaues for (const ValueFlow::Value& v : tok->values()) { if (!v.isSymbolicValue()) continue; if (!v.tokvalue) continue; if (v.tokvalue->varId() != var->declarationId()) continue; for (ValueFlow::Value value : values) { if (v.intvalue != 0) { if (!value.isIntValue()) continue; value.intvalue += v.intvalue; } value.valueKind = v.valueKind; value.bound = v.bound; value.errorPath.insert(value.errorPath.end(), v.errorPath.begin(), v.errorPath.end()); setTokenValue(tok, value, settings); } } }(); } } } static void valueFlowForwardAssign(Token* const tok, const Token* expr, std::vector vars, std::list values, const bool init, TokenList* const tokenlist, ErrorLogger* const errorLogger, const Settings* const settings) { if (Token::simpleMatch(tok->astParent(), "return")) return; const Token* endOfVarScope = getEndOfVarScope(tok, vars); if (std::any_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); values.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue)); } if (std::all_of( vars.begin(), vars.end(), [&](const Variable* var) { return !var->isPointer() && !var->isSmartPointer(); })) values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue)); if (tok->astParent()) { for (ValueFlow::Value& value : values) { std::string valueKind; if (value.valueKind == ValueFlow::Value::ValueKind::Impossible) { if (value.bound == ValueFlow::Value::Bound::Point) valueKind = "never "; else if (value.bound == ValueFlow::Value::Bound::Lower) valueKind = "less than "; else if (value.bound == ValueFlow::Value::Bound::Upper) valueKind = "greater than "; } const std::string info = "Assignment '" + tok->astParent()->expressionString() + "', assigned value is " + valueKind + value.infoString(); value.errorPath.emplace_back(tok, info); } } if (tokenlist->isCPP() && vars.size() == 1 && Token::Match(vars.front()->typeStartToken(), "bool|_Bool")) { for (ValueFlow::Value& value : values) { if (value.isImpossible()) continue; if (value.isIntValue()) value.intvalue = (value.intvalue != 0); if (value.isTokValue()) value.intvalue = (value.tokvalue != nullptr); } } // Static variable initialisation? if (vars.size() == 1 && vars.front()->isStatic() && init) lowerToPossible(values); // is volatile if (std::any_of(vars.begin(), vars.end(), [&](const Variable* var) { return var->isVolatile(); })) lowerToPossible(values); // Skip RHS const Token * nextExpression = tok->astParent() ? nextAfterAstRightmostLeaf(tok->astParent()) : tok->next(); for (ValueFlow::Value& value : values) { if (value.isSymbolicValue()) continue; if (value.isTokValue()) continue; value.tokvalue = tok; } // Const variable if (expr->variable() && expr->variable()->isConst() && !expr->variable()->isReference()) { auto it = std::remove_if(values.begin(), values.end(), [](const ValueFlow::Value& value) { if (!value.isKnown()) return false; if (value.isIntValue()) return true; if (value.isFloatValue()) return true; if (value.isContainerSizeValue()) return true; if (value.isIteratorValue()) return true; return false; }); std::list constValues; constValues.splice(constValues.end(), values, it, values.end()); valueFlowForwardConst(const_cast(nextExpression), endOfVarScope, expr->variable(), constValues, settings); } valueFlowForward(const_cast(nextExpression), endOfVarScope, expr, values, tokenlist, settings); } static void valueFlowForwardAssign(Token* const tok, const Variable* const var, const std::list& values, const bool, const bool init, TokenList* const tokenlist, ErrorLogger* const errorLogger, const Settings* const settings) { valueFlowForwardAssign(tok, var->nameToken(), {var}, values, init, tokenlist, errorLogger, settings); } static std::list truncateValues(std::list values, const ValueType* dst, const ValueType* src, const Settings* settings) { if (!dst || !dst->isIntegral()) return values; const size_t sz = ValueFlow::getSizeOf(*dst, settings); if (src) { const size_t osz = ValueFlow::getSizeOf(*src, settings); if (osz >= sz && dst->sign == ValueType::Sign::SIGNED && src->sign == ValueType::Sign::UNSIGNED) { values.remove_if([&](const ValueFlow::Value& value) { if (!value.isIntValue()) return false; if (!value.isImpossible()) return false; if (value.bound != ValueFlow::Value::Bound::Upper) return false; if (osz == sz && value.intvalue < 0) return true; if (osz > sz) return true; return false; }); } } for (ValueFlow::Value &value : values) { // Don't truncate impossible values since those can be outside of the valid range if (value.isImpossible()) continue; if (value.isFloatValue()) { value.intvalue = value.floatValue; value.valueType = ValueFlow::Value::ValueType::INT; } if (value.isIntValue() && sz > 0 && sz < 8) { const MathLib::biguint unsignedMaxValue = (1ULL << (sz * 8)) - 1ULL; const MathLib::biguint signBit = 1ULL << (sz * 8 - 1); value.intvalue &= unsignedMaxValue; if (dst->sign == ValueType::Sign::SIGNED && (value.intvalue & signBit)) value.intvalue |= ~unsignedMaxValue; } } return values; } static bool isVariableInit(const Token *tok) { return (tok->str() == "(" || tok->str() == "{") && tok->isBinaryOp() && tok->astOperand1()->variable() && tok->astOperand1()->variable()->nameToken() == tok->astOperand1() && tok->astOperand1()->variable()->valueType() && tok->astOperand1()->variable()->valueType()->type >= ValueType::Type::VOID && !Token::simpleMatch(tok->astOperand2(), ","); } static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope * scope : symboldatabase->functionScopes) { std::set aliased; for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { // Alias if (tok->isUnaryOp("&")) { aliased.insert(tok->astOperand1()->exprId()); continue; } // Assignment if ((tok->str() != "=" && !isVariableInit(tok)) || (tok->astParent())) continue; // Lhs should be a variable if (!tok->astOperand1() || !tok->astOperand1()->exprId()) continue; const nonneg int exprid = tok->astOperand1()->exprId(); if (aliased.find(exprid) != aliased.end()) continue; std::vector vars = getLHSVariables(tok); // Rhs values.. if (!tok->astOperand2() || tok->astOperand2()->values().empty()) continue; std::list values = truncateValues( tok->astOperand2()->values(), tok->astOperand1()->valueType(), tok->astOperand2()->valueType(), settings); // Remove known values std::set types; if (tok->astOperand1()->hasKnownValue()) { for (const ValueFlow::Value& value:tok->astOperand1()->values()) { if (value.isKnown() && !value.isSymbolicValue()) types.insert(value.valueType); } } values.remove_if([&](const ValueFlow::Value& value) { return types.count(value.valueType) > 0; }); // Remove container size if its not a container if (!astIsContainer(tok->astOperand2())) values.remove_if([&](const ValueFlow::Value& value) { return value.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE; }); // Remove symbolic values that are the same as the LHS values.remove_if([&](const ValueFlow::Value& value) { if (value.isSymbolicValue() && value.tokvalue) return value.tokvalue->exprId() == tok->astOperand1()->exprId(); return false; }); // If assignment copy by value, remove Uninit values.. if ((tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->pointer == 0) || (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference() && tok->astOperand1()->variable()->nameToken() == tok->astOperand1())) values.remove_if([&](const ValueFlow::Value& value) { return value.isUninitValue(); }); if (values.empty()) continue; const bool init = vars.size() == 1 && vars.front()->nameToken() == tok->astOperand1(); valueFlowForwardAssign( tok->astOperand2(), tok->astOperand1(), vars, values, init, tokenlist, errorLogger, settings); // Back propagate symbolic values if (tok->astOperand1()->exprId() > 0) { Token* start = nextAfterAstRightmostLeaf(tok); const Token* end = scope->bodyEnd; for (ValueFlow::Value value : values) { if (!value.isSymbolicValue()) continue; const Token* expr = value.tokvalue; value.intvalue = -value.intvalue; value.tokvalue = tok->astOperand1(); value.errorPath.emplace_back(tok, tok->astOperand1()->expressionString() + " is assigned '" + tok->astOperand2()->expressionString() + "' here."); valueFlowForward(start, end, expr, {value}, tokenlist, settings); } } } } } static std::vector getVariables(const Token* tok) { std::vector result; visitAstNodes(tok, [&](const Token* child) { if (child->variable()) result.push_back(child->variable()); return ChildrenToVisit::op1_and_op2; }); return result; } static void valueFlowAfterSwap(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings) { for (const Scope* scope : symboldatabase->functionScopes) { for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "swap (")) continue; if (!Token::simpleMatch(tok->next()->astOperand2(), ",")) continue; std::vector args = astFlatten(tok->next()->astOperand2(), ","); if (args.size() != 2) continue; if (args[0]->exprId() == 0) continue; if (args[1]->exprId() == 0) continue; for (int i = 0; i < 2; i++) { std::vector vars = getVariables(args[0]); std::list values = args[0]->values(); valueFlowForwardAssign(args[0], args[1], vars, values, false, tokenlist, errorLogger, settings); std::swap(args[0], args[1]); } } } } static void valueFlowSetConditionToKnown(const Token* tok, std::list& values, bool then) { if (values.empty()) return; if (then && !Token::Match(tok, "==|!|(")) return; if (!then && !Token::Match(tok, "!=|%var%|(")) return; if (isConditionKnown(tok, then)) changePossibleToKnown(values); } static bool isBreakScope(const Token* const endToken) { if (!Token::simpleMatch(endToken, "}")) return false; if (!Token::simpleMatch(endToken->link(), "{")) return false; return Token::findmatch(endToken->link(), "break|goto", endToken); } static ValueFlow::Value asImpossible(ValueFlow::Value v) { v.invertRange(); v.setImpossible(); return v; } static void insertImpossible(std::list& values, const std::list& input) { std::transform(input.begin(), input.end(), std::back_inserter(values), &asImpossible); } static void insertNegateKnown(std::list& values, const std::list& input) { for (ValueFlow::Value value:input) { if (!value.isIntValue() && !value.isContainerSizeValue()) continue; value.intvalue = !value.intvalue; value.setKnown(); values.push_back(value); } } struct ConditionHandler { struct Condition { const Token *vartok; std::list true_values; std::list false_values; bool inverted = false; // Whether to insert impossible values for the condition or only use possible values bool impossible = true; Condition() : vartok(nullptr), true_values(), false_values(), inverted(false), impossible(true) {} }; virtual Analyzer::Result forward(Token* start, const Token* stop, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings* settings) const = 0; virtual Analyzer::Result forward(Token* top, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings* settings) const = 0; virtual void reverse(Token* start, const Token* endToken, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings* settings) const = 0; virtual std::vector parse(const Token* tok, const Settings* settings) const = 0; void traverseCondition(TokenList* tokenlist, SymbolDatabase* symboldatabase, const std::function& f) const { for (const Scope *scope : symboldatabase->functionScopes) { for (Token *tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "if|while|for (")) continue; if (Token::Match(tok, ":|;|,")) continue; const Token* top = tok->astTop(); if (!top) continue; if (!Token::Match(top->previous(), "if|while|for (") && !Token::Match(tok->astParent(), "&&|%oror%|?")) continue; for (const Condition& cond : parse(tok, tokenlist->getSettings())) { if (!cond.vartok) continue; if (cond.vartok->exprId() == 0) continue; if (cond.vartok->hasKnownIntValue()) continue; if (cond.true_values.empty() || cond.false_values.empty()) continue; if (!isConstExpression(cond.vartok, tokenlist->getSettings()->library, true, tokenlist->isCPP())) continue; f(cond, tok, scope); } } } } void beforeCondition(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings) const { traverseCondition(tokenlist, symboldatabase, [&](const Condition& cond, Token* tok, const Scope*) { if (cond.vartok->exprId() == 0) return; // If condition is known then don't propagate value if (tok->hasKnownIntValue()) return; const Token* top = tok->astTop(); if (Token::Match(top, "%assign%")) return; if (Token::Match(cond.vartok->astParent(), "%assign%|++|--")) return; if (Token::simpleMatch(tok->astParent(), "?") && tok->astParent()->isExpandedMacro()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable '" + cond.vartok->expressionString() + "', condition is defined in macro"); return; } // if,macro => bailout if (Token::simpleMatch(top->previous(), "if (") && top->previous()->isExpandedMacro()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable '" + cond.vartok->expressionString() + "', condition is defined in macro"); return; } std::list values = cond.true_values; if (cond.true_values != cond.false_values) values.insert(values.end(), cond.false_values.begin(), cond.false_values.end()); // extra logic for unsigned variables 'i>=1' => possible value can also be 0 if (Token::Match(tok, "<|>")) { values.remove_if([](const ValueFlow::Value& v) { if (v.isIntValue()) return v.intvalue != 0; return false; }); if (cond.vartok->valueType() && cond.vartok->valueType()->sign != ValueType::Sign::UNSIGNED) return; } if (values.empty()) return; // bailout: for/while-condition, variable is changed in while loop if (Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) { // Variable changed in 3rd for-expression if (Token::simpleMatch(top->previous(), "for (")) { if (top->astOperand2() && top->astOperand2()->astOperand2() && isExpressionChanged( cond.vartok, top->astOperand2()->astOperand2(), top->link(), settings, tokenlist->isCPP())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable '" + cond.vartok->expressionString() + "' used in loop"); return; } } // Variable changed in loop code const Token* const start = top; const Token* const block = top->link()->next(); const Token* const end = block->link(); if (isExpressionChanged(cond.vartok, start, end, settings, tokenlist->isCPP())) { // If its reassigned in loop then analyze from the end if (!Token::Match(tok, "%assign%|++|--") && findExpression(cond.vartok->exprId(), start, end, [&](const Token* tok2) { return Token::Match(tok2->astParent(), "%assign%") && astIsLHS(tok2); })) { // Start at the end of the loop body Token* bodyTok = top->link()->next(); reverse(bodyTok->link(), bodyTok, cond.vartok, values, tokenlist, settings); } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable '" + cond.vartok->expressionString() + "' used in loop"); return; } } Token* startTok = nullptr; if (astIsRHS(tok)) startTok = tok->astParent(); else if (astIsLHS(tok)) startTok = previousBeforeAstLeftmostLeaf(tok->astParent()); if (!startTok) startTok = tok->previous(); reverse(startTok, nullptr, cond.vartok, values, tokenlist, settings); }); } void afterCondition(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings) const { traverseCondition(tokenlist, symboldatabase, [&](const Condition& cond, Token* tok, const Scope* scope) { if (Token::simpleMatch(tok->astParent(), "?")) return; const Token* top = tok->astTop(); std::list thenValues; std::list elseValues; if (!Token::Match(tok, "!=|=|(|.") && tok != cond.vartok) { thenValues.insert(thenValues.end(), cond.true_values.begin(), cond.true_values.end()); if (cond.impossible && isConditionKnown(tok, false)) insertImpossible(elseValues, cond.false_values); } if (!Token::Match(tok, "==|!")) { elseValues.insert(elseValues.end(), cond.false_values.begin(), cond.false_values.end()); if (cond.impossible && isConditionKnown(tok, true)) { insertImpossible(thenValues, cond.true_values); if (tok == cond.vartok && astIsBool(tok)) insertNegateKnown(thenValues, cond.true_values); } } if (cond.inverted) std::swap(thenValues, elseValues); if (Token::Match(tok->astParent(), "%oror%|&&")) { Token* parent = tok->astParent(); if (astIsRHS(tok) && astIsLHS(parent) && parent->astParent() && parent->str() == parent->astParent()->str()) parent = parent->astParent(); else if (!astIsLHS(tok)) { parent = nullptr; } if (parent) { std::vector nextExprs = {parent->astOperand2()}; if (astIsLHS(parent) && parent->astParent() && parent->astParent()->str() == parent->str()) { nextExprs.push_back(parent->astParent()->astOperand2()); } const std::string& op(parent->str()); std::list values; if (op == "&&") values = thenValues; else if (op == "||") values = elseValues; if (Token::Match(tok, "==|!=") || (tok == cond.vartok && astIsBool(tok))) changePossibleToKnown(values); if (astIsFloat(cond.vartok, false) || (!cond.vartok->valueType() && std::all_of(values.begin(), values.end(), [](const ValueFlow::Value& v) { return v.isIntValue() || v.isFloatValue(); }))) values.remove_if([&](const ValueFlow::Value& v) { return v.isImpossible(); }); for (Token* start:nextExprs) { Analyzer::Result r = forward(start, cond.vartok, values, tokenlist, settings); if (r.terminate != Analyzer::Terminate::None) return; } } } { const Token* tok2 = tok; std::string op; bool mixedOperators = false; while (tok2->astParent()) { const Token* parent = tok2->astParent(); if (Token::Match(parent, "%oror%|&&")) { if (op.empty()) { op = parent->str(); } else if (op != parent->str()) { mixedOperators = true; break; } } if (parent->str() == "!") { op = (op == "&&" ? "||" : "&&"); } tok2 = parent; } if (mixedOperators) { return; } } if (!top) return; if (top->previous()->isExpandedMacro()) { for (std::list* values : {&thenValues, &elseValues}) { for (ValueFlow::Value& v : *values) v.macro = true; } } if (!Token::Match(top->previous(), "if|while|for (")) return; if (top->previous()->str() == "for") { if (!Token::Match(tok, "%comp%")) return; if (!Token::simpleMatch(tok->astParent(), ";")) return; const Token* stepTok = getStepTok(top); if (cond.vartok->varId() == 0) return; if (!cond.vartok->variable()) return; if (!Token::Match(stepTok, "++|--")) return; std::set bounds; for (const ValueFlow::Value& v : thenValues) { if (v.bound != ValueFlow::Value::Bound::Point && v.isImpossible()) continue; bounds.insert(v.bound); } if (Token::simpleMatch(stepTok, "++") && bounds.count(ValueFlow::Value::Bound::Lower) > 0) return; if (Token::simpleMatch(stepTok, "--") && bounds.count(ValueFlow::Value::Bound::Upper) > 0) return; const Token* childTok = tok->astOperand1(); if (!childTok) childTok = tok->astOperand2(); if (!childTok) return; if (childTok->varId() != cond.vartok->varId()) return; const Token* startBlock = top->link()->next(); if (isVariableChanged(startBlock, startBlock->link(), cond.vartok->varId(), cond.vartok->variable()->isGlobal(), settings, tokenlist->isCPP())) return; // Check if condition in for loop is always false const Token* initTok = getInitTok(top); ProgramMemory pm; execute(initTok, &pm, nullptr, nullptr); MathLib::bigint result = 1; execute(tok, &pm, &result, nullptr); if (result == 0) return; // Remove condition since for condition is not redundant for (std::list* values : {&thenValues, &elseValues}) { for (ValueFlow::Value& v : *values) { v.condition = nullptr; v.conditional = true; } } } // if astParent is "!" we need to invert codeblock { const Token* tok2 = tok; while (tok2->astParent()) { const Token* parent = tok2->astParent(); while (parent && parent->str() == "&&") parent = parent->astParent(); if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false"))) std::swap(thenValues, elseValues); tok2 = parent; } } bool deadBranch[] = {false, false}; // start token of conditional code Token* startTokens[] = {nullptr, nullptr}; // determine startToken(s) if (Token::simpleMatch(top->link(), ") {")) startTokens[0] = top->link()->next(); if (Token::simpleMatch(top->link()->linkAt(1), "} else {")) startTokens[1] = top->link()->linkAt(1)->tokAt(2); int changeBlock = -1; int bailBlock = -1; for (int i = 0; i < 2; i++) { const Token* const startToken = startTokens[i]; if (!startToken) continue; std::list& values = (i == 0 ? thenValues : elseValues); valueFlowSetConditionToKnown(tok, values, i == 0); Analyzer::Result r = forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, settings); deadBranch[i] = r.terminate == Analyzer::Terminate::Escape; if (r.action.isModified() && !deadBranch[i]) changeBlock = i; if (r.terminate != Analyzer::Terminate::None && r.terminate != Analyzer::Terminate::Escape && r.terminate != Analyzer::Terminate::Modified) bailBlock = i; changeKnownToPossible(values); } if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, startTokens[changeBlock]->link(), "valueFlowAfterCondition: " + cond.vartok->expressionString() + " is changed in conditional block"); return; } else if (bailBlock >= 0) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, startTokens[bailBlock]->link(), "valueFlowAfterCondition: bailing in conditional block"); return; } // After conditional code.. if (Token::simpleMatch(top->link(), ") {")) { Token* after = top->link()->linkAt(1); bool dead_if = deadBranch[0]; bool dead_else = deadBranch[1]; const Token* unknownFunction = nullptr; if (tok->astParent() && Token::Match(top->previous(), "while|for (")) dead_if = !isBreakScope(after); else if (!dead_if) dead_if = isReturnScope(after, &settings->library, &unknownFunction); if (!dead_if && unknownFunction) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope"); return; } if (Token::simpleMatch(after, "} else {")) { after = after->linkAt(2); unknownFunction = nullptr; if (!dead_else) dead_else = isReturnScope(after, &settings->library, &unknownFunction); if (!dead_else && unknownFunction) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope"); return; } } if (dead_if && dead_else) return; std::list values; if (dead_if) { values = elseValues; } else if (dead_else) { values = thenValues; } else { std::copy_if(thenValues.begin(), thenValues.end(), std::back_inserter(values), std::mem_fn(&ValueFlow::Value::isPossible)); std::copy_if(elseValues.begin(), elseValues.end(), std::back_inserter(values), std::mem_fn(&ValueFlow::Value::isPossible)); } if (values.empty()) return; if (dead_if || dead_else) { const Token* parent = tok->astParent(); // Skip the not operator while (Token::simpleMatch(parent, "!")) parent = parent->astParent(); bool possible = false; if (Token::Match(parent, "&&|%oror%")) { std::string op = parent->str(); while (parent && parent->str() == op) parent = parent->astParent(); if (Token::simpleMatch(parent, "!") || Token::simpleMatch(parent, "== false")) possible = op == "||"; else possible = op == "&&"; } if (possible) { values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible)); changeKnownToPossible(values); } else { valueFlowSetConditionToKnown(tok, values, true); valueFlowSetConditionToKnown(tok, values, false); } } if (values.empty()) return; forward(after, getEndOfExprScope(cond.vartok, scope), cond.vartok, values, tokenlist, settings); } }); } virtual ~ConditionHandler() {} }; static void valueFlowCondition(const ValuePtr& handler, TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings) { handler->beforeCondition(tokenlist, symboldatabase, errorLogger, settings); handler->afterCondition(tokenlist, symboldatabase, errorLogger, settings); } struct SimpleConditionHandler : ConditionHandler { virtual Analyzer::Result forward(Token* start, const Token* stop, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings* settings) const OVERRIDE { return valueFlowForward(start->next(), stop, exprTok, values, tokenlist, settings); } virtual Analyzer::Result forward(Token* top, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings* settings) const OVERRIDE { return valueFlowForward(top, exprTok, values, tokenlist, settings); } virtual void reverse(Token* start, const Token* endToken, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings* settings) const OVERRIDE { return valueFlowReverse(start, endToken, exprTok, values, tokenlist, settings); } virtual std::vector parse(const Token* tok, const Settings*) const OVERRIDE { Condition cond; ValueFlow::Value true_value; ValueFlow::Value false_value; const Token *vartok = parseCompareInt(tok, true_value, false_value); if (vartok) { if (vartok->hasKnownIntValue()) return {}; if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) vartok = vartok->astOperand1(); cond.true_values.push_back(true_value); cond.false_values.push_back(false_value); cond.vartok = vartok; return {cond}; } if (tok->str() == "!") { vartok = tok->astOperand1(); } else if (tok->astParent() && (Token::Match(tok->astParent(), "%oror%|&&|?") || Token::Match(tok->astParent()->previous(), "if|while ("))) { if (Token::simpleMatch(tok, "=")) vartok = tok->astOperand1(); else if (!Token::Match(tok, "%comp%|%assign%")) vartok = tok; } if (!vartok) return {}; cond.true_values.emplace_back(tok, 0LL); cond.false_values.emplace_back(tok, 0LL); cond.vartok = vartok; return {cond}; } }; struct IntegralInferModel : InferModel { virtual bool match(const ValueFlow::Value& value) const OVERRIDE { return value.isIntValue(); } virtual ValueFlow::Value yield(MathLib::bigint value) const OVERRIDE { ValueFlow::Value result(value); result.valueType = ValueFlow::Value::ValueType::INT; result.setKnown(); return result; } }; ValuePtr makeIntegralInferModel() { return IntegralInferModel{}; } ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val) { if (!varTok) return ValueFlow::Value{}; if (varTok->hasKnownIntValue()) return ValueFlow::Value{}; std::vector r = infer(IntegralInferModel{}, op, varTok->values(), val); if (r.size() == 1 && r.front().isKnown()) return r.front(); return ValueFlow::Value{}; } ValueFlow::Value inferCondition(std::string op, MathLib::bigint val, const Token* varTok) { if (!varTok) return ValueFlow::Value{}; if (varTok->hasKnownIntValue()) return ValueFlow::Value{}; std::vector r = infer(IntegralInferModel{}, op, val, varTok->values()); if (r.size() == 1 && r.front().isKnown()) return r.front(); return ValueFlow::Value{}; } struct IteratorInferModel : InferModel { virtual ValueFlow::Value::ValueType getType() const = 0; virtual bool match(const ValueFlow::Value& value) const OVERRIDE { return value.valueType == getType(); } virtual ValueFlow::Value yield(MathLib::bigint value) const OVERRIDE { ValueFlow::Value result(value); result.valueType = getType(); result.setKnown(); return result; } }; struct EndIteratorInferModel : IteratorInferModel { virtual ValueFlow::Value::ValueType getType() const OVERRIDE { return ValueFlow::Value::ValueType::ITERATOR_END; } }; struct StartIteratorInferModel : IteratorInferModel { virtual ValueFlow::Value::ValueType getType() const OVERRIDE { return ValueFlow::Value::ValueType::ITERATOR_END; } }; static void valueFlowInferCondition(TokenList* tokenlist, const Settings* settings) { for (Token* tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->astParent()) continue; if (tok->hasKnownIntValue()) continue; if (tok->variable() && (Token::Match(tok->astParent(), "?|&&|!|%oror%") || Token::Match(tok->astParent()->previous(), "if|while ("))) { std::vector result = infer(IntegralInferModel{}, "!=", tok->values(), 0); if (result.size() != 1) continue; ValueFlow::Value value = result.front(); value.intvalue = 1; value.bound = ValueFlow::Value::Bound::Point; setTokenValue(tok, value, settings); } else if (Token::Match(tok, "%comp%|-") && tok->astOperand1() && tok->astOperand2()) { if (astIsIterator(tok->astOperand1()) || astIsIterator(tok->astOperand2())) { static const std::array, 2> iteratorModels = {EndIteratorInferModel{}, StartIteratorInferModel{}}; for (const ValuePtr& model : iteratorModels) { std::vector result = infer(model, tok->str(), tok->astOperand1()->values(), tok->astOperand2()->values()); for (ValueFlow::Value value : result) { value.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(tok, value, settings); } } } else { std::vector result = infer(IntegralInferModel{}, tok->str(), tok->astOperand1()->values(), tok->astOperand2()->values()); for (const ValueFlow::Value& value : result) { setTokenValue(tok, value, settings); } } } } } struct SymbolicConditionHandler : SimpleConditionHandler { virtual std::vector parse(const Token* tok, const Settings*) const OVERRIDE { if (!Token::Match(tok, "%comp%")) return {}; if (tok->hasKnownIntValue()) return {}; if (!tok->astOperand1() || tok->astOperand1()->hasKnownIntValue() || tok->astOperand1()->isLiteral()) return {}; if (!tok->astOperand2() || tok->astOperand2()->hasKnownIntValue() || tok->astOperand2()->isLiteral()) return {}; std::vector result; for (int i = 0; i < 2; i++) { const bool lhs = i == 0; const Token* vartok = lhs ? tok->astOperand1() : tok->astOperand2(); const Token* valuetok = lhs ? tok->astOperand2() : tok->astOperand1(); if (valuetok->exprId() == 0) continue; if (valuetok->hasKnownSymbolicValue(vartok)) continue; if (vartok->hasKnownSymbolicValue(valuetok)) continue; ValueFlow::Value true_value; ValueFlow::Value false_value; setConditionalValues(tok, !lhs, 0, true_value, false_value); setSymbolic(true_value, valuetok); setSymbolic(false_value, valuetok); Condition cond; cond.true_values = {true_value}; cond.false_values = {false_value}; cond.vartok = vartok; result.push_back(cond); } return result; } }; static bool valueFlowForLoop2(const Token *tok, ProgramMemory *memory1, ProgramMemory *memory2, ProgramMemory *memoryAfter) { // for ( firstExpression ; secondExpression ; thirdExpression ) const Token *firstExpression = tok->next()->astOperand2()->astOperand1(); const Token *secondExpression = tok->next()->astOperand2()->astOperand2()->astOperand1(); const Token *thirdExpression = tok->next()->astOperand2()->astOperand2()->astOperand2(); ProgramMemory programMemory; MathLib::bigint result(0); bool error = false; execute(firstExpression, &programMemory, &result, &error); if (error) return false; execute(secondExpression, &programMemory, &result, &error); if (result == 0) // 2nd expression is false => no looping return false; if (error) { // If a variable is reassigned in second expression, return false bool reassign = false; visitAstNodes(secondExpression, [&](const Token *t) { if (t->str() == "=" && t->astOperand1() && programMemory.hasValue(t->astOperand1()->varId())) // TODO: investigate what variable is assigned. reassign = true; return reassign ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (reassign) return false; } ProgramMemory startMemory(programMemory); ProgramMemory endMemory; int maxcount = 10000; while (result != 0 && !error && --maxcount > 0) { endMemory = programMemory; execute(thirdExpression, &programMemory, &result, &error); if (!error) execute(secondExpression, &programMemory, &result, &error); } if (memory1) memory1->swap(startMemory); if (!error) { if (memory2) memory2->swap(endMemory); if (memoryAfter) memoryAfter->swap(programMemory); } return true; } static void valueFlowForLoopSimplify(Token * const bodyStart, const nonneg int varid, bool globalvar, const MathLib::bigint value, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { const Token * const bodyEnd = bodyStart->link(); // Is variable modified inside for loop if (isVariableChanged(bodyStart, bodyEnd, varid, globalvar, settings, tokenlist->isCPP())) return; for (Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) { if (tok2->varId() == varid) { const Token * parent = tok2->astParent(); while (parent) { const Token * const p = parent; parent = parent->astParent(); if (!parent || parent->str() == ":") break; if (parent->str() == "?") { if (parent->astOperand2() != p) parent = nullptr; break; } } if (parent) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable " + tok2->str() + " stopping on ?"); continue; } ValueFlow::Value value1(value); value1.varId = tok2->varId(); setTokenValue(tok2, value1, settings); } if (Token::Match(tok2, "%oror%|&&")) { const ProgramMemory programMemory(getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value), settings)); if ((tok2->str() == "&&" && !conditionIsTrue(tok2->astOperand1(), programMemory)) || (tok2->str() == "||" && !conditionIsFalse(tok2->astOperand1(), programMemory))) { // Skip second expression.. const Token *parent = tok2; while (parent && parent->str() == tok2->str()) parent = parent->astParent(); // Jump to end of condition if (parent && parent->str() == "(") { tok2 = parent->link(); // cast if (Token::simpleMatch(tok2, ") (")) tok2 = tok2->linkAt(1); } } } if ((tok2->str() == "&&" && conditionIsFalse(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value), settings))) || (tok2->str() == "||" && conditionIsTrue(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value), settings)))) break; else if (Token::simpleMatch(tok2, ") {") && Token::findmatch(tok2->link(), "%varid%", tok2, varid)) { if (Token::findmatch(tok2, "continue|break|return", tok2->linkAt(1), varid)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable bailout on conditional continue|break|return"); break; } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable skipping conditional scope"); tok2 = tok2->next()->link(); if (Token::simpleMatch(tok2, "} else {")) { if (Token::findmatch(tok2, "continue|break|return", tok2->linkAt(2), varid)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable bailout on conditional continue|break|return"); break; } tok2 = tok2->linkAt(2); } } else if (Token::simpleMatch(tok2, ") {")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop skipping {} code"); tok2 = tok2->linkAt(1); if (Token::simpleMatch(tok2, "} else {")) tok2 = tok2->linkAt(2); } } } static void valueFlowForLoopSimplifyAfter(Token* fortok, nonneg int varid, const MathLib::bigint num, TokenList* tokenlist, const Settings* settings) { const Token *vartok = nullptr; for (const Token *tok = fortok; tok; tok = tok->next()) { if (tok->varId() == varid) { vartok = tok; break; } } if (!vartok || !vartok->variable()) return; const Variable *var = vartok->variable(); const Token *endToken = nullptr; if (var->isLocal()) endToken = var->scope()->bodyEnd; else endToken = fortok->scope()->bodyEnd; Token* blockTok = fortok->linkAt(1)->linkAt(1); std::list values; values.emplace_back(num); values.back().errorPath.emplace_back(fortok,"After for loop, " + var->name() + " has value " + values.back().infoString()); if (blockTok != endToken) { valueFlowForward(blockTok->next(), endToken, vartok, values, tokenlist, settings); } } static void valueFlowForLoop(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope &scope : symboldatabase->scopeList) { if (scope.type != Scope::eFor) continue; Token* tok = const_cast(scope.classDef); Token* const bodyStart = const_cast(scope.bodyStart); if (!Token::simpleMatch(tok->next()->astOperand2(), ";") || !Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) continue; nonneg int varid; bool knownInitValue, partialCond; MathLib::bigint initValue, stepValue, lastValue; if (extractForLoopValues(tok, &varid, &knownInitValue, &initValue, &partialCond, &stepValue, &lastValue)) { const bool executeBody = !knownInitValue || initValue <= lastValue; const Token* vartok = Token::findmatch(tok, "%varid%", bodyStart, varid); if (executeBody && vartok) { std::list initValues; initValues.emplace_back(initValue, ValueFlow::Value::Bound::Lower); initValues.push_back(asImpossible(initValues.back())); Analyzer::Result result = valueFlowForward(bodyStart, bodyStart->link(), vartok, initValues, tokenlist, settings); if (!result.action.isModified()) { std::list lastValues; lastValues.emplace_back(lastValue, ValueFlow::Value::Bound::Upper); lastValues.back().conditional = true; lastValues.push_back(asImpossible(lastValues.back())); if (stepValue != 1) lastValues.pop_front(); valueFlowForward(bodyStart, bodyStart->link(), vartok, lastValues, tokenlist, settings); } } const MathLib::bigint afterValue = executeBody ? lastValue + stepValue : initValue; valueFlowForLoopSimplifyAfter(tok, varid, afterValue, tokenlist, settings); } else { ProgramMemory mem1, mem2, memAfter; if (valueFlowForLoop2(tok, &mem1, &mem2, &memAfter)) { ProgramMemory::Map::const_iterator it; for (it = mem1.values.begin(); it != mem1.values.end(); ++it) { if (!it->second.isIntValue()) continue; valueFlowForLoopSimplify(bodyStart, it->first, false, it->second.intvalue, tokenlist, errorLogger, settings); } for (it = mem2.values.begin(); it != mem2.values.end(); ++it) { if (!it->second.isIntValue()) continue; valueFlowForLoopSimplify(bodyStart, it->first, false, it->second.intvalue, tokenlist, errorLogger, settings); } for (it = memAfter.values.begin(); it != memAfter.values.end(); ++it) { if (!it->second.isIntValue()) continue; valueFlowForLoopSimplifyAfter(tok, it->first, it->second.intvalue, tokenlist, settings); } } } } } struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { std::unordered_map values; std::unordered_map vars; SymbolDatabase* symboldatabase; MultiValueFlowAnalyzer() : ValueFlowAnalyzer(), values(), vars(), symboldatabase(nullptr) {} MultiValueFlowAnalyzer(const std::unordered_map& args, const TokenList* t, SymbolDatabase* s) : ValueFlowAnalyzer(t), values(), vars(), symboldatabase(s) { for (const auto& p:args) { values[p.first->declarationId()] = p.second; vars[p.first->declarationId()] = p.first; } } virtual const std::unordered_map& getVars() const { return vars; } virtual const ValueFlow::Value* getValue(const Token* tok) const OVERRIDE { if (tok->varId() == 0) return nullptr; auto it = values.find(tok->varId()); if (it == values.end()) return nullptr; return &it->second; } virtual ValueFlow::Value* getValue(const Token* tok) OVERRIDE { if (tok->varId() == 0) return nullptr; auto it = values.find(tok->varId()); if (it == values.end()) return nullptr; return &it->second; } virtual void makeConditional() OVERRIDE { for (auto&& p:values) { p.second.conditional = true; } } virtual void addErrorPath(const Token* tok, const std::string& s) OVERRIDE { for (auto&& p:values) { p.second.errorPath.emplace_back(tok, "Assuming condition is " + s); } } virtual bool isAlias(const Token* tok, bool& inconclusive) const OVERRIDE { const auto range = SelectValueFromVarIdMapRange(&values); for (const auto& p:getVars()) { nonneg int varid = p.first; const Variable* var = p.second; if (tok->varId() == varid) return true; if (isAliasOf(var, tok, varid, range, &inconclusive)) return true; } return false; } virtual bool isGlobal() const OVERRIDE { return false; } virtual bool lowerToPossible() OVERRIDE { for (auto&& p:values) { if (p.second.isImpossible()) return false; p.second.changeKnownToPossible(); } return true; } virtual bool lowerToInconclusive() OVERRIDE { for (auto&& p:values) { if (p.second.isImpossible()) return false; p.second.setInconclusive(); } return true; } virtual bool isConditional() const OVERRIDE { for (auto&& p:values) { if (p.second.conditional) return true; if (p.second.condition) return !p.second.isImpossible(); } return false; } virtual bool stopOnCondition(const Token*) const OVERRIDE { return isConditional(); } virtual bool updateScope(const Token* endBlock, bool) const OVERRIDE { const Scope* scope = endBlock->scope(); if (!scope) return false; if (scope->type == Scope::eLambda) { for (const auto& p:values) { if (!p.second.isLifetimeValue()) return false; } return true; } else if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || scope->type == Scope::eFor) { auto pred = [](const ValueFlow::Value& value) { if (value.isKnown()) return true; if (value.isImpossible()) return true; if (value.isLifetimeValue()) return true; return false; }; if (std::all_of(values.begin(), values.end(), std::bind(pred, std::bind(SelectMapValues{}, std::placeholders::_1)))) return true; if (isConditional()) return false; const Token* condTok = getCondTokFromEnd(endBlock); std::set varids; std::transform(getVars().begin(), getVars().end(), std::inserter(varids, varids.begin()), SelectMapKeys{}); return bifurcate(condTok, varids, getSettings()); } return false; } virtual bool match(const Token* tok) const OVERRIDE { return values.count(tok->varId()) > 0; } virtual ProgramState getProgramState() const OVERRIDE { ProgramState ps; for (const auto& p:values) ps[p.first] = p.second; return ps; } virtual void forkScope(const Token* endBlock) OVERRIDE { ProgramMemory pm = {getProgramState()}; const Scope* scope = endBlock->scope(); const Token* condTok = getCondTokFromEnd(endBlock); if (scope && condTok) programMemoryParseCondition(pm, condTok, nullptr, getSettings(), scope->type != Scope::eElse); if (condTok && Token::simpleMatch(condTok->astParent(), ";")) { ProgramMemory endMemory; if (valueFlowForLoop2(condTok->astTop()->previous(), nullptr, &endMemory, nullptr)) pm.replace(endMemory); } // ProgramMemory pm = pms.get(endBlock->link()->next(), getProgramState()); for (const auto& p:pm.values) { nonneg int varid = p.first; if (symboldatabase && !symboldatabase->isVarId(varid)) continue; ValueFlow::Value value = p.second; if (vars.count(varid) != 0) continue; if (value.isImpossible()) continue; value.setPossible(); values[varid] = value; if (symboldatabase) vars[varid] = symboldatabase->getVariableFromVarId(varid); } } }; template bool productParams(const std::unordered_map>& vars, F f) { using Args = std::vector>; Args args(1); // Compute cartesian product of all arguments for (const auto& p:vars) { if (p.second.empty()) continue; args.back()[p.first] = p.second.front(); } for (const auto& p:vars) { if (args.size() > 256) return false; if (p.second.empty()) continue; std::for_each(std::next(p.second.begin()), p.second.end(), [&](const ValueFlow::Value& value) { Args new_args; for (auto arg:args) { if (value.path != 0) { for (const auto& q:arg) { if (q.second.path == 0) continue; if (q.second.path != value.path) return; } } arg[p.first] = value; new_args.push_back(arg); } std::copy(new_args.begin(), new_args.end(), std::back_inserter(args)); }); } for (const auto& arg:args) { if (arg.empty()) continue; bool skip = false; // Make sure all arguments are the same path MathLib::bigint path = arg.begin()->second.path; for (const auto& p:arg) { if (p.second.path != path) { skip = true; break; } } if (skip) continue; f(arg); } return true; } static void valueFlowInjectParameter(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings, const Scope* functionScope, const std::unordered_map>& vars) { bool r = productParams(vars, [&](const std::unordered_map& arg) { MultiValueFlowAnalyzer a(arg, tokenlist, symboldatabase); valueFlowGenericForward(const_cast(functionScope->bodyStart), functionScope->bodyEnd, a, settings); }); if (!r) { std::string fname = ""; Function* f = functionScope->function; if (f) fname = f->name(); if (settings->debugwarnings) bailout(tokenlist, errorLogger, functionScope->bodyStart, "Too many argument passed to " + fname); } } static void valueFlowInjectParameter(TokenList* tokenlist, const Settings* settings, const Variable* arg, const Scope* functionScope, const std::list& argvalues) { // Is argument passed by value or const reference, and is it a known non-class type? if (arg->isReference() && !arg->isConst() && !arg->isClass()) return; // Set value in function scope.. const nonneg int varid2 = arg->declarationId(); if (!varid2) return; valueFlowForward(const_cast(functionScope->bodyStart->next()), functionScope->bodyEnd, arg->nameToken(), argvalues, tokenlist, settings); } static void valueFlowSwitchVariable(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope &scope : symboldatabase->scopeList) { if (scope.type != Scope::ScopeType::eSwitch) continue; if (!Token::Match(scope.classDef, "switch ( %var% ) {")) continue; const Token *vartok = scope.classDef->tokAt(2); const Variable *var = vartok->variable(); if (!var) continue; // bailout: global non-const variables if (!(var->isLocal() || var->isArgument()) && !var->isConst()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, vartok, "switch variable " + var->name() + " is global"); continue; } for (Token *tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() == "{") { tok = tok->link(); continue; } if (Token::Match(tok, "case %num% :")) { std::list values; values.emplace_back(MathLib::toLongNumber(tok->next()->str())); values.back().condition = tok; const std::string info("case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here."); values.back().errorPath.emplace_back(tok, info); bool known = false; if ((Token::simpleMatch(tok->previous(), "{") || Token::simpleMatch(tok->tokAt(-2), "break ;")) && !Token::Match(tok->tokAt(3), ";| case")) known = true; while (Token::Match(tok->tokAt(3), ";| case %num% :")) { known = false; tok = tok->tokAt(3); if (!tok->isName()) tok = tok->next(); values.emplace_back(MathLib::toLongNumber(tok->next()->str())); values.back().condition = tok; const std::string info2("case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here."); values.back().errorPath.emplace_back(tok, info2); } for (std::list::const_iterator val = values.begin(); val != values.end(); ++val) { valueFlowReverse(tokenlist, const_cast(scope.classDef), vartok, *val, ValueFlow::Value(), errorLogger, settings); } if (vartok->variable()->scope()) { if (known) values.back().setKnown(); // FIXME We must check if there is a return. See #9276 /* valueFlowForwardVariable(tok->tokAt(3), vartok->variable()->scope()->bodyEnd, vartok->variable(), vartok->varId(), values, values.back().isKnown(), false, tokenlist, errorLogger, settings); */ } } } } } static std::list getFunctionArgumentValues(const Token *argtok) { std::list argvalues(argtok->values()); removeImpossible(argvalues); if (argvalues.empty() && Token::Match(argtok, "%comp%|%oror%|&&|!")) { argvalues.emplace_back(0); argvalues.emplace_back(1); } return argvalues; } static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings *settings) { std::unordered_map> argValues; int argn = 1; for (const Token *argtok : getArguments(tok->previous())) { argValues[argn] = getFunctionArgumentValues(argtok); argn++; } if (returnValue.find("arg") != std::string::npos && argValues.empty()) return; TokenList tokenList(settings); { const std::string code = "return " + returnValue + ";"; std::istringstream istr(code); if (!tokenList.createTokens(istr)) return; } // combine operators, set links, etc.. std::stack lpar; for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[!<>=] =")) { tok2->str(tok2->str() + "="); tok2->deleteNext(); } else if (tok2->str() == "(") lpar.push(tok2); else if (tok2->str() == ")") { if (lpar.empty()) return; Token::createMutualLinks(lpar.top(), tok2); lpar.pop(); } } if (!lpar.empty()) return; // set varids for (Token* tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (tok2->str().compare(0, 3, "arg") != 0) continue; nonneg int id = std::atoi(tok2->str().c_str() + 3); tok2->varId(id); } // Evaluate expression tokenList.createAst(); Token* expr = tokenList.front()->astOperand1(); ValueFlow::valueFlowConstantFoldAST(expr, settings); productParams(argValues, [&](const std::unordered_map& arg) { ProgramMemory pm{arg}; MathLib::bigint result = 0; bool error = false; execute(expr, &pm, &result, &error); if (error) return; ValueFlow::Value value(result); value.setKnown(); for (auto&& p : arg) { if (p.second.isPossible()) value.setPossible(); if (p.second.isInconclusive()) { value.setInconclusive(); break; } } setTokenValue(tok, value, settings); }); } template struct IteratorRange { Iterator mBegin; Iterator mEnd; Iterator begin() const { return mBegin; } Iterator end() const { return mEnd; } }; template IteratorRange MakeIteratorRange(Iterator start, Iterator last) { return {start, last}; } static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings) { int id = 0; for (const Scope* scope : MakeIteratorRange(symboldatabase->functionScopes.rbegin(), symboldatabase->functionScopes.rend())) { const Function* function = scope->function; if (!function) continue; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; const Function * const calledFunction = tok->function(); if (!calledFunction) { // library function? const std::string& returnValue(settings->library.returnValue(tok)); if (!returnValue.empty()) valueFlowLibraryFunction(tok->next(), returnValue, settings); continue; } const Scope * const calledFunctionScope = calledFunction->functionScope; if (!calledFunctionScope) continue; id++; std::unordered_map> argvars; // TODO: Rewrite this. It does not work well to inject 1 argument at a time. const std::vector &callArguments = getArguments(tok); for (int argnr = 0U; argnr < callArguments.size(); ++argnr) { const Token *argtok = callArguments[argnr]; // Get function argument const Variable * const argvar = calledFunction->getArgumentVar(argnr); if (!argvar) break; // passing value(s) to function std::list argvalues(getFunctionArgumentValues(argtok)); // Remove non-local lifetimes argvalues.remove_if([](const ValueFlow::Value& v) { if (v.isLifetimeValue()) return !v.isLocalLifetimeValue() && !v.isSubFunctionLifetimeValue(); return false; }); // Remove uninit values if argument is passed by value if (argtok->variable() && !argtok->variable()->isPointer() && argvalues.size() == 1 && argvalues.front().isUninitValue()) { if (CheckUninitVar::isVariableUsage(tokenlist->isCPP(), argtok, settings->library, false, CheckUninitVar::Alloc::NO_ALLOC, 0)) continue; } if (argvalues.empty()) continue; // Error path.. for (ValueFlow::Value &v : argvalues) { const std::string nr = MathLib::toString(argnr + 1) + getOrdinalText(argnr + 1); v.errorPath.emplace_back(argtok, "Calling function '" + calledFunction->name() + "', " + nr + " argument '" + argtok->expressionString() + "' value is " + v.infoString()); v.path = 256 * v.path + id % 256; // Change scope of lifetime values if (v.isLifetimeValue()) v.lifetimeScope = ValueFlow::Value::LifetimeScope::SubFunction; } // passed values are not "known".. lowerToPossible(argvalues); argvars[argvar] = argvalues; } valueFlowInjectParameter(tokenlist, symboldatabase, errorLogger, settings, calledFunctionScope, argvars); } } } static void valueFlowFunctionDefaultParameter(TokenList* tokenlist, SymbolDatabase* symboldatabase, const Settings* settings) { if (!tokenlist->isCPP()) return; for (const Scope* scope : symboldatabase->functionScopes) { const Function* function = scope->function; if (!function) continue; for (std::size_t arg = function->minArgCount(); arg < function->argCount(); arg++) { const Variable* var = function->getArgumentVar(arg); if (var && var->hasDefault() && Token::Match(var->nameToken(), "%var% = %num%|%str% [,)]")) { const std::list &values = var->nameToken()->tokAt(2)->values(); std::list argvalues; for (const ValueFlow::Value &value : values) { ValueFlow::Value v(value); v.defaultArg = true; v.changeKnownToPossible(); if (v.isPossible()) argvalues.push_back(v); } if (!argvalues.empty()) valueFlowInjectParameter(tokenlist, settings, var, scope, argvalues); } } } } static bool isKnown(const Token * tok) { return tok && tok->hasKnownIntValue(); } static void valueFlowFunctionReturn(TokenList *tokenlist, ErrorLogger *errorLogger) { for (Token *tok = tokenlist->back(); tok; tok = tok->previous()) { if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand1()->function()) continue; if (tok->hasKnownValue()) continue; // Arguments.. std::vector parvalues; if (tok->astOperand2()) { const Token *partok = tok->astOperand2(); while (partok && partok->str() == "," && isKnown(partok->astOperand2())) partok = partok->astOperand1(); if (!isKnown(partok)) continue; parvalues.push_back(partok->values().front().intvalue); partok = partok->astParent(); while (partok && partok->str() == ",") { parvalues.push_back(partok->astOperand2()->values().front().intvalue); partok = partok->astParent(); } if (partok != tok) continue; } // Get scope and args of function const Function * const function = tok->astOperand1()->function(); const Scope * const functionScope = function->functionScope; if (!functionScope || !Token::simpleMatch(functionScope->bodyStart, "{ return")) { if (functionScope && tokenlist->getSettings()->debugwarnings && Token::findsimplematch(functionScope->bodyStart, "return", functionScope->bodyEnd)) bailout(tokenlist, errorLogger, tok, "function return; nontrivial function body"); continue; } ProgramMemory programMemory; for (std::size_t i = 0; i < parvalues.size(); ++i) { const Variable * const arg = function->getArgumentVar(i); if (!arg || !Token::Match(arg->typeStartToken(), "%type% %name% ,|)")) { if (tokenlist->getSettings()->debugwarnings) bailout(tokenlist, errorLogger, tok, "function return; unhandled argument type"); programMemory.clear(); break; } programMemory.setIntValue(arg->declarationId(), parvalues[i]); } if (programMemory.empty() && !parvalues.empty()) continue; // Determine return value of subfunction.. MathLib::bigint result = 0; bool error = false; execute(functionScope->bodyStart->next()->astOperand1(), &programMemory, &result, &error); if (!error) { ValueFlow::Value v(result); if (function->hasVirtualSpecifier()) v.setPossible(); else v.setKnown(); setTokenValue(tok, v, tokenlist->getSettings()); } } } static bool needsInitialization(const Variable* var, bool cpp) { if (!var) return false; if (var->isPointer()) return true; if (var->type() && var->type()->isUnionType()) return false; if (!cpp) return true; if (var->type() && var->type()->needInitialization == Type::NeedInitialization::True) return true; if (var->valueType() && var->valueType()->isPrimitive()) return true; return false; } static void addToErrorPath(ValueFlow::Value& value, const ValueFlow::Value& from) { std::unordered_set locations; if (from.condition && !value.condition) value.condition = from.condition; std::copy_if(from.errorPath.begin(), from.errorPath.end(), std::back_inserter(value.errorPath), [&](const ErrorPathItem& e) { return locations.insert(e.first).second; }); } static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDatabase*/, const Settings* settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!Token::Match(tok,"[;{}] %type%")) continue; if (!tok->scope()->isExecutable()) continue; const Token *vardecl = tok->next(); bool stdtype = false; bool pointer = false; while (Token::Match(vardecl, "%name%|::|*") && vardecl->varId() == 0) { stdtype |= vardecl->isStandardType(); pointer |= vardecl->str() == "*"; vardecl = vardecl->next(); } // if (!stdtype && !pointer) // continue; if (!Token::Match(vardecl, "%var% ;")) continue; const Variable *var = vardecl->variable(); if (!needsInitialization(var, tokenlist->isCPP())) continue; if (var->nameToken() != vardecl || var->isInit()) continue; if (!var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow()) continue; if (!var->type() && !stdtype && !pointer) continue; ValueFlow::Value uninitValue; uninitValue.setKnown(); uninitValue.valueType = ValueFlow::Value::ValueType::UNINIT; uninitValue.tokvalue = vardecl; bool partial = false; std::map partialReads; if (const Scope* scope = var->typeScope()) { if (Token::findsimplematch(scope->bodyStart, "union", scope->bodyEnd)) continue; for (const Variable& memVar : scope->varlist) { if (!memVar.isPublic()) continue; // Skip array since we can't track partial initialization from nested subexpressions if (memVar.isArray()) continue; if (!needsInitialization(&memVar, tokenlist->isCPP())) { partial = true; continue; } MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), vardecl, uninitValue, tokenlist); valueFlowGenericForward(vardecl->next(), vardecl->scope()->bodyEnd, analyzer, settings); for (auto&& p : *analyzer.partialReads) { Token* tok2 = p.first; const ValueFlow::Value& v = p.second; // Try to insert into map auto pp = partialReads.insert(std::make_pair(tok2, v)); ValueFlow::Value& v2 = pp.first->second; bool inserted = pp.second; // Merge the two values if it is already in map if (!inserted) { if (v.valueType != v2.valueType) continue; addToErrorPath(v2, v); } v2.subexpressions.push_back(memVar.nameToken()->str()); } } } for (auto&& p : partialReads) { Token* tok2 = p.first; const ValueFlow::Value& v = p.second; setTokenValue(tok2, v, settings); } if (partial) continue; valueFlowForward(vardecl->next(), vardecl->scope()->bodyEnd, var->nameToken(), {uninitValue}, tokenlist, settings); } } static bool isContainerSizeChanged(nonneg int varId, const Token* start, const Token* end, const Settings* settings = nullptr, int depth = 20); static bool isContainerSizeChangedByFunction(const Token* tok, const Settings* settings = nullptr, int depth = 20) { if (!tok->valueType()) return false; if (!astIsContainer(tok)) return false; // If we are accessing an element then we are not changing the container size if (Token::Match(tok, "%name% . %name% (")) { Library::Container::Yield yield = getLibraryContainer(tok)->getYield(tok->strAt(2)); if (yield != Library::Container::Yield::NO_YIELD) return false; } if (Token::simpleMatch(tok->astParent(), "[")) return false; // address of variable const bool addressOf = tok->valueType()->pointer || (tok->astParent() && tok->astParent()->isUnaryOp("&")); int narg; const Token * ftok = getTokenArgumentFunction(tok, narg); if (!ftok) return false; // not a function => variable not changed const Function * fun = ftok->function(); if (fun && !fun->hasVirtualSpecifier()) { const Variable *arg = fun->getArgumentVar(narg); if (arg) { if (!arg->isReference() && !addressOf) return false; if (!addressOf && arg->isConst()) return false; if (arg->valueType() && arg->valueType()->constness == 1) return false; const Scope * scope = fun->functionScope; if (scope) { // Argument not used if (!arg->nameToken()) return false; if (depth > 0) return isContainerSizeChanged( arg->declarationId(), scope->bodyStart, scope->bodyEnd, settings, depth - 1); } // Don't know => Safe guess return true; } } bool inconclusive = false; const bool isChanged = isVariableChangedByFunctionCall(tok, 0, settings, &inconclusive); return (isChanged || inconclusive); } struct ContainerExpressionAnalyzer : ExpressionAnalyzer { ContainerExpressionAnalyzer() : ExpressionAnalyzer() {} ContainerExpressionAnalyzer(const Token* expr, const ValueFlow::Value& val, const TokenList* t) : ExpressionAnalyzer(expr, val, t) {} virtual bool match(const Token* tok) const OVERRIDE { return tok->exprId() == expr->exprId() || (astIsIterator(tok) && isAliasOf(tok, expr->exprId())); } virtual Action isWritable(const Token* tok, Direction d) const OVERRIDE { if (astIsIterator(tok)) return Action::None; if (d == Direction::Reverse) return Action::None; if (!getValue(tok)) return Action::None; if (!tok->valueType()) return Action::None; if (!astIsContainer(tok)) return Action::None; const Token* parent = tok->astParent(); const Library::Container* container = getLibraryContainer(tok); if (container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) { const Token* rhs = parent->astOperand2(); if (rhs->tokType() == Token::eString) return Action::Read | Action::Write | Action::Incremental; const Library::Container* rhsContainer = getLibraryContainer(rhs); if (rhsContainer && rhsContainer->stdStringLike) { if (std::any_of(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value &rhsval) { return rhsval.isKnown() && rhsval.isContainerSizeValue(); })) return Action::Read | Action::Write | Action::Incremental; } } else if (Token::Match(tok, "%name% . %name% (")) { Library::Container::Action action = container->getAction(tok->strAt(2)); if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP) { std::vector args = getArguments(tok->tokAt(3)); if (args.size() < 2) return Action::Read | Action::Write | Action::Incremental; } } return Action::None; } virtual void writeValue(ValueFlow::Value* val, const Token* tok, Direction d) const OVERRIDE { if (d == Direction::Reverse) return; if (!val) return; if (!tok->astParent()) return; if (!tok->valueType()) return; if (!astIsContainer(tok)) return; const Token* parent = tok->astParent(); const Library::Container* container = getLibraryContainer(tok); if (container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) { const Token* rhs = parent->astOperand2(); const Library::Container* rhsContainer = getLibraryContainer(rhs); if (rhs->tokType() == Token::eString) val->intvalue += Token::getStrLength(rhs); else if (rhsContainer && rhsContainer->stdStringLike) { for (const ValueFlow::Value &rhsval : rhs->values()) { if (rhsval.isKnown() && rhsval.isContainerSizeValue()) { val->intvalue += rhsval.intvalue; } } } } else if (Token::Match(tok, "%name% . %name% (")) { Library::Container::Action action = container->getAction(tok->strAt(2)); if (action == Library::Container::Action::PUSH) val->intvalue++; if (action == Library::Container::Action::POP) val->intvalue--; } } virtual Action isModified(const Token* tok) const OVERRIDE { Action read = Action::Read; // An iterator won't change the container size if (astIsIterator(tok)) return read; if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) return Action::Invalid; if (isLikelyStreamRead(isCPP(), tok->astParent())) return Action::Invalid; if (astIsContainer(tok) && isContainerSizeChanged(tok, getSettings())) return Action::Invalid; return read; } }; static Analyzer::Result valueFlowContainerForward(Token* startToken, const Token* endToken, const Token* exprTok, const ValueFlow::Value& value, TokenList* tokenlist) { ContainerExpressionAnalyzer a(exprTok, value, tokenlist); return valueFlowGenericForward(startToken, endToken, a, tokenlist->getSettings()); } static Analyzer::Result valueFlowContainerForwardRecursive(Token* top, const Token* exprTok, const ValueFlow::Value& value, TokenList* tokenlist) { ContainerExpressionAnalyzer a(exprTok, value, tokenlist); return valueFlowGenericForward(top, a, tokenlist->getSettings()); } static Analyzer::Result valueFlowContainerForward(Token* startToken, const Token* exprTok, const ValueFlow::Value& value, TokenList* tokenlist) { const Token* endToken = nullptr; const Function* f = Scope::nestedInFunction(startToken->scope()); if (f && f->functionScope) endToken = f->functionScope->bodyEnd; return valueFlowContainerForward(startToken, endToken, exprTok, value, tokenlist); } static void valueFlowContainerReverse(Token* tok, const Token* const endToken, const Token* const varToken, const std::list& values, TokenList* tokenlist, const Settings* settings) { for (const ValueFlow::Value& value : values) { ContainerExpressionAnalyzer a(varToken, value, tokenlist); valueFlowGenericReverse(tok, endToken, a, settings); } } bool isContainerSizeChanged(const Token* tok, const Settings* settings, int depth) { if (!tok) return false; if (!tok->valueType() || !tok->valueType()->container) return true; if (Token::Match(tok, "%name% %assign%|<<")) return true; if (Token::Match(tok, "%var% [") && tok->valueType()->container->stdAssociativeLike) return true; if (Token::Match(tok, "%name% . %name% (")) { Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2)); Library::Container::Yield yield = tok->valueType()->container->getYield(tok->strAt(2)); switch (action) { case Library::Container::Action::RESIZE: case Library::Container::Action::CLEAR: case Library::Container::Action::PUSH: case Library::Container::Action::POP: case Library::Container::Action::CHANGE: case Library::Container::Action::INSERT: case Library::Container::Action::ERASE: return true; case Library::Container::Action::NO_ACTION: // might be unknown action return yield == Library::Container::Yield::NO_YIELD; case Library::Container::Action::FIND: case Library::Container::Action::CHANGE_CONTENT: case Library::Container::Action::CHANGE_INTERNAL: break; } } if (isContainerSizeChangedByFunction(tok, settings, depth)) return true; return false; } static bool isContainerSizeChanged(nonneg int varId, const Token* start, const Token* end, const Settings* settings, int depth) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->varId() != varId) continue; if (isContainerSizeChanged(tok, settings, depth)) return true; } return false; } static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->scope()) continue; if (!tok->scope()->isExecutable()) continue; if (!astIsSmartPointer(tok)) continue; if (tok->variable() && Token::Match(tok, "%var% (|{|;")) { const Variable* var = tok->variable(); if (!var->isSmartPointer()) continue; if (var->nameToken() == tok) { if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() && tok->next()->astOperand2()->str() != ",") { Token* inTok = tok->next()->astOperand2(); std::list values = inTok->values(); const bool constValue = inTok->isNumber(); valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings); } else if (Token::Match(tok, "%var% ;")) { std::list values; ValueFlow::Value v(0); v.setKnown(); values.push_back(v); valueFlowForwardAssign(tok, var, values, false, true, tokenlist, errorLogger, settings); } } } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (") && tok->astParent()->originalName() != "->") { std::vector vars = getVariables(tok); Token* ftok = tok->astParent()->tokAt(2); if (Token::simpleMatch(tok->astParent(), ". reset (")) { if (Token::simpleMatch(ftok, "( )")) { std::list values; ValueFlow::Value v(0); v.setKnown(); values.push_back(v); valueFlowForwardAssign(ftok, tok, vars, values, false, tokenlist, errorLogger, settings); } else { tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue)); Token* inTok = ftok->astOperand2(); if (!inTok) continue; std::list values = inTok->values(); valueFlowForwardAssign(inTok, tok, vars, values, false, tokenlist, errorLogger, settings); } } else if (Token::simpleMatch(tok->astParent(), ". release ( )")) { const Token* parent = ftok->astParent(); bool hasParentReset = false; while (parent) { if (Token::Match(parent->tokAt(-2), ". release|reset (") && parent->tokAt(-2)->astOperand1()->exprId() == tok->exprId()) { hasParentReset = true; break; } parent = parent->astParent(); } if (hasParentReset) continue; std::list values; ValueFlow::Value v(0); v.setKnown(); values.push_back(v); valueFlowForwardAssign(ftok, tok, vars, values, false, tokenlist, errorLogger, settings); } else if (Token::simpleMatch(tok->astParent(), ". get ( )")) { ValueFlow::Value v = makeSymbolic(tok); setTokenValue(tok->astParent()->tokAt(2), v, settings); } } else if (Token::Match(tok->previous(), "%name%|> (|{") && astIsSmartPointer(tok) && astIsSmartPointer(tok->astOperand1())) { std::vector args = getArguments(tok); if (args.empty()) continue; for (const ValueFlow::Value& v : args.front()->values()) setTokenValue(tok, v, settings); } } } static void valueFlowIterators(TokenList *tokenlist, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->scope()) continue; if (!tok->scope()->isExecutable()) continue; if (!astIsContainer(tok)) continue; if (Token::Match(tok->astParent(), ". %name% (")) { Library::Container::Yield yield = getLibraryContainer(tok)->getYield(tok->astParent()->strAt(1)); ValueFlow::Value v(0); v.setKnown(); if (yield == Library::Container::Yield::START_ITERATOR) { v.valueType = ValueFlow::Value::ValueType::ITERATOR_START; setTokenValue(tok->astParent()->tokAt(2), v, settings); } else if (yield == Library::Container::Yield::END_ITERATOR) { v.valueType = ValueFlow::Value::ValueType::ITERATOR_END; setTokenValue(tok->astParent()->tokAt(2), v, settings); } } } } static std::list getIteratorValues(std::list values, const ValueFlow::Value::ValueKind* kind = nullptr) { values.remove_if([&](const ValueFlow::Value& v) { if (kind && v.valueKind != *kind) return true; return !v.isIteratorValue(); }); return values; } struct IteratorConditionHandler : SimpleConditionHandler { virtual std::vector parse(const Token* tok, const Settings*) const OVERRIDE { Condition cond; ValueFlow::Value true_value; ValueFlow::Value false_value; if (Token::Match(tok, "==|!=")) { if (!tok->astOperand1() || !tok->astOperand2()) return {}; ValueFlow::Value::ValueKind kind = ValueFlow::Value::ValueKind::Known; std::list values = getIteratorValues(tok->astOperand1()->values(), &kind); if (!values.empty()) { cond.vartok = tok->astOperand2(); } else { values = getIteratorValues(tok->astOperand2()->values(), &kind); if (!values.empty()) cond.vartok = tok->astOperand1(); } for (ValueFlow::Value& v:values) { v.setPossible(); v.assumeCondition(tok); } cond.true_values = values; cond.false_values = values; } return {cond}; } }; static void valueFlowIteratorInfer(TokenList *tokenlist, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->scope()) continue; if (!tok->scope()->isExecutable()) continue; std::list values = getIteratorValues(tok->values()); values.remove_if([&](const ValueFlow::Value& v) { if (!v.isImpossible()) return true; if (!v.condition) return true; if (v.bound != ValueFlow::Value::Bound::Point) return true; if (v.isIteratorEndValue() && v.intvalue <= 0) return true; if (v.isIteratorStartValue() && v.intvalue >= 0) return true; return false; }); for (ValueFlow::Value& v:values) { v.setPossible(); if (v.isIteratorStartValue()) v.intvalue++; if (v.isIteratorEndValue()) v.intvalue--; setTokenValue(tok, v, settings); } } } static std::vector getContainerValues(const Token* tok) { std::vector values; if (tok) { std::copy_if(tok->values().begin(), tok->values().end(), std::back_inserter(values), std::mem_fn(&ValueFlow::Value::isContainerSizeValue)); } return values; } static ValueFlow::Value makeContainerSizeValue(std::size_t s, bool known = true) { ValueFlow::Value value(s); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; if (known) value.setKnown(); return value; } static std::vector makeContainerSizeValue(const Token* tok, bool known = true) { if (tok->hasKnownIntValue()) return {makeContainerSizeValue(tok->values().front().intvalue, known)}; return {}; } static std::vector getInitListSize(const Token* tok, const Library::Container* container, bool known = true) { std::vector args = getArguments(tok); if (!args.empty() && container->stdStringLike) { if (astIsGenericChar(args[0])) // init list of chars return { makeContainerSizeValue(args.size(), known) }; if (astIsIntegral(args[0], false)) { // { count, 'c' } if (args.size() > 1) return {makeContainerSizeValue(args[0], known)}; } else if (astIsPointer(args[0])) { // TODO: Try to read size of string literal { "abc" } if (args.size() == 2 && astIsIntegral(args[1], false)) // { char*, count } return {makeContainerSizeValue(args[1], known)}; } else if (astIsContainer(args[0])) { if (args.size() == 1) // copy constructor { str } return getContainerValues(args[0]); if (args.size() == 3) // { str, pos, count } return {makeContainerSizeValue(args[2], known)}; // TODO: { str, pos }, { ..., alloc } } return {}; } else if ((args.size() == 1 && astIsContainer(args[0]) && args[0]->valueType()->container == container) || isIteratorPair(args)) { return getContainerValues(args[0]); } return {makeContainerSizeValue(args.size(), known)}; } static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger * /*errorLogger*/, const Settings *settings) { std::map static_sizes; // declaration for (const Variable *var : symboldatabase->variableList()) { bool known = true; if (!var || !var->isLocal() || var->isPointer() || var->isReference() || var->isStatic()) continue; if (!var->valueType() || !var->valueType()->container) continue; if (!astIsContainer(var->nameToken())) continue; if (var->nameToken()->hasKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE)) continue; if (!Token::Match(var->nameToken(), "%name% ;") && !(Token::Match(var->nameToken(), "%name% {") && Token::simpleMatch(var->nameToken()->next()->link(), "} ;"))) continue; if (var->nameToken()->astTop() && Token::Match(var->nameToken()->astTop()->previous(), "for|while")) known = !isVariableChanged(var, settings, true); if (var->valueType()->container->size_templateArgNo >= 0) { if (var->dimensions().size() == 1 && var->dimensions().front().known) static_sizes[var->declarationId()] = var->dimensions().front().num; continue; } std::vector values{ValueFlow::Value{0}}; values.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; if (known) values.back().setKnown(); if (Token::simpleMatch(var->nameToken()->next(), "{")) { const Token* initList = var->nameToken()->next(); values = getInitListSize(initList, var->valueType()->container, known); } for (const ValueFlow::Value& value : values) valueFlowContainerForward(var->nameToken()->next(), var->nameToken(), value, tokenlist); } // after assignment for (const Scope *functionScope : symboldatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (static_sizes.count(tok->varId()) > 0) { ValueFlow::Value value(static_sizes.at(tok->varId())); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; value.setKnown(); setTokenValue(const_cast(tok), value, settings); } else if (Token::Match(tok, "%name%|;|{|} %var% = %str% ;")) { const Token *containerTok = tok->next(); if (containerTok->exprId() == 0) continue; if (containerTok->valueType() && containerTok->valueType()->container && containerTok->valueType()->container->stdStringLike) { ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2))); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; value.setKnown(); valueFlowContainerForward(containerTok->next(), containerTok, value, tokenlist); } } else if (Token::Match(tok, "%name%|;|{|}|> %var% = {") && Token::simpleMatch(tok->linkAt(3), "} ;")) { const Token* containerTok = tok->next(); if (containerTok->exprId() == 0) continue; if (astIsContainer(containerTok) && containerTok->valueType()->container->size_templateArgNo < 0) { std::vector values = getInitListSize(tok->tokAt(3), containerTok->valueType()->container); for (const ValueFlow::Value& value : values) valueFlowContainerForward(containerTok->next(), containerTok, value, tokenlist); } } else if (Token::Match(tok, ". %name% (") && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->container) { const Token* containerTok = tok->astOperand1(); if (containerTok->exprId() == 0) continue; Library::Container::Action action = containerTok->valueType()->container->getAction(tok->strAt(1)); if (action == Library::Container::Action::CLEAR) { ValueFlow::Value value(0); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; value.setKnown(); valueFlowContainerForward(tok->next(), containerTok, value, tokenlist); } else if (action == Library::Container::Action::RESIZE && tok->tokAt(2)->astOperand2() && tok->tokAt(2)->astOperand2()->hasKnownIntValue()) { ValueFlow::Value value(tok->tokAt(2)->astOperand2()->values().front()); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; value.setKnown(); valueFlowContainerForward(tok->next(), containerTok, value, tokenlist); } } } } } struct ContainerConditionHandler : ConditionHandler { virtual Analyzer::Result forward(Token* start, const Token* stop, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings*) const OVERRIDE { Analyzer::Result result{}; for (const ValueFlow::Value& value : values) result.update(valueFlowContainerForward(start->next(), stop, exprTok, value, tokenlist)); return result; } virtual Analyzer::Result forward(Token* top, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings*) const OVERRIDE { Analyzer::Result result{}; for (const ValueFlow::Value& value : values) result.update(valueFlowContainerForwardRecursive(top, exprTok, value, tokenlist)); return result; } virtual void reverse(Token* start, const Token* endTok, const Token* exprTok, const std::list& values, TokenList* tokenlist, const Settings* settings) const OVERRIDE { return valueFlowContainerReverse(start, endTok, exprTok, values, tokenlist, settings); } virtual std::vector parse(const Token* tok, const Settings* settings) const OVERRIDE { Condition cond; ValueFlow::Value true_value; ValueFlow::Value false_value; const Token *vartok = parseCompareInt(tok, true_value, false_value); if (vartok) { vartok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE); if (!vartok) return {}; true_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; false_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; cond.true_values.push_back(true_value); cond.false_values.push_back(false_value); cond.vartok = vartok; return {cond}; } // Empty check if (tok->str() == "(") { vartok = settings->library.getContainerFromYield(tok, Library::Container::Yield::EMPTY); // TODO: Handle .size() if (!vartok) return {}; const Token *parent = tok->astParent(); while (parent) { if (Token::Match(parent, "%comp%")) return {}; parent = parent->astParent(); } ValueFlow::Value value(tok, 0LL); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; cond.true_values.emplace_back(value); cond.false_values.emplace_back(std::move(value)); cond.vartok = vartok; cond.inverted = true; return {cond}; } // String compare if (Token::Match(tok, "==|!=")) { const Token *strtok = nullptr; if (Token::Match(tok->astOperand1(), "%str%")) { strtok = tok->astOperand1(); vartok = tok->astOperand2(); } else if (Token::Match(tok->astOperand2(), "%str%")) { strtok = tok->astOperand2(); vartok = tok->astOperand1(); } if (!strtok) return {}; if (!astIsContainer(vartok)) return {}; ValueFlow::Value value(tok, Token::getStrLength(strtok)); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; cond.false_values.emplace_back(value); cond.true_values.emplace_back(std::move(value)); cond.vartok = vartok; cond.impossible = false; return {cond}; } return {}; } }; static void valueFlowDynamicBufferSize(TokenList* tokenlist, SymbolDatabase* symboldatabase, const Settings* settings) { for (const Scope *functionScope : symboldatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "[;{}] %var% =")) continue; if (!tok->next()->variable()) continue; const Token *rhs = tok->tokAt(2)->astOperand2(); while (rhs && rhs->isCast()) rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); if (!rhs) continue; if (!Token::Match(rhs->previous(), "%name% (")) continue; const Library::AllocFunc *allocFunc = settings->library.getAllocFuncInfo(rhs->previous()); if (!allocFunc) allocFunc = settings->library.getReallocFuncInfo(rhs->previous()); if (!allocFunc || allocFunc->bufferSize == Library::AllocFunc::BufferSize::none) continue; const std::vector args = getArguments(rhs->previous()); const Token * const arg1 = (args.size() >= allocFunc->bufferSizeArg1) ? args[allocFunc->bufferSizeArg1 - 1] : nullptr; const Token * const arg2 = (args.size() >= allocFunc->bufferSizeArg2) ? args[allocFunc->bufferSizeArg2 - 1] : nullptr; MathLib::bigint sizeValue = -1; switch (allocFunc->bufferSize) { case Library::AllocFunc::BufferSize::none: break; case Library::AllocFunc::BufferSize::malloc: if (arg1 && arg1->hasKnownIntValue()) sizeValue = arg1->getKnownIntValue(); break; case Library::AllocFunc::BufferSize::calloc: if (arg1 && arg2 && arg1->hasKnownIntValue() && arg2->hasKnownIntValue()) sizeValue = arg1->getKnownIntValue() * arg2->getKnownIntValue(); break; case Library::AllocFunc::BufferSize::strdup: if (arg1 && arg1->hasKnownValue()) { const ValueFlow::Value &value = arg1->values().back(); if (value.isTokValue() && value.tokvalue->tokType() == Token::eString) sizeValue = Token::getStrLength(value.tokvalue) + 1; // Add one for the null terminator } break; } if (sizeValue < 0) continue; ValueFlow::Value value(sizeValue); value.errorPath.emplace_back(tok->tokAt(2), "Assign " + tok->strAt(1) + ", buffer with size " + MathLib::toString(sizeValue)); value.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; value.setKnown(); const std::list values{value}; valueFlowForward(const_cast(rhs), functionScope->bodyEnd, tok->next(), values, tokenlist, settings); } } } static bool getMinMaxValues(const ValueType *vt, const cppcheck::Platform &platform, MathLib::bigint *minValue, MathLib::bigint *maxValue) { if (!vt || !vt->isIntegral() || vt->pointer) return false; int bits; switch (vt->type) { case ValueType::Type::BOOL: bits = 1; break; case ValueType::Type::CHAR: bits = platform.char_bit; break; case ValueType::Type::SHORT: bits = platform.short_bit; break; case ValueType::Type::INT: bits = platform.int_bit; break; case ValueType::Type::LONG: bits = platform.long_bit; break; case ValueType::Type::LONGLONG: bits = platform.long_long_bit; break; default: return false; } if (bits == 1) { *minValue = 0; *maxValue = 1; } else if (bits < 62) { if (vt->sign == ValueType::Sign::UNSIGNED) { *minValue = 0; *maxValue = (1LL << bits) - 1; } else { *minValue = -(1LL << (bits - 1)); *maxValue = (1LL << (bits - 1)) - 1; } } else if (bits == 64) { if (vt->sign == ValueType::Sign::UNSIGNED) { *minValue = 0; *maxValue = LLONG_MAX; // todo max unsigned value } else { *minValue = LLONG_MIN; *maxValue = LLONG_MAX; } } else { return false; } return true; } static bool getMinMaxValues(const std::string &typestr, const Settings *settings, MathLib::bigint *minvalue, MathLib::bigint *maxvalue) { TokenList typeTokens(settings); std::istringstream istr(typestr+";"); if (!typeTokens.createTokens(istr)) return false; typeTokens.simplifyPlatformTypes(); typeTokens.simplifyStdType(); const ValueType &vt = ValueType::parseDecl(typeTokens.front(), settings); return getMinMaxValues(&vt, *settings, minvalue, maxvalue); } static void valueFlowSafeFunctions(TokenList* tokenlist, SymbolDatabase* symboldatabase, const Settings* settings) { for (const Scope *functionScope : symboldatabase->functionScopes) { if (!functionScope->bodyStart) continue; const Function *function = functionScope->function; if (!function) continue; const bool safe = function->isSafe(settings); const bool all = safe && settings->platformType != cppcheck::Platform::PlatformType::Unspecified; for (const Variable &arg : function->argumentList) { if (!arg.nameToken() || !arg.valueType()) continue; if (arg.valueType()->type == ValueType::Type::CONTAINER) { if (!safe) continue; std::list argValues; argValues.emplace_back(0); argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " is empty"); argValues.back().safe = true; argValues.emplace_back(1000000); argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " size is 1000000"); argValues.back().safe = true; for (const ValueFlow::Value &value : argValues) valueFlowContainerForward( const_cast(functionScope->bodyStart), arg.nameToken(), value, tokenlist); continue; } MathLib::bigint low, high; bool isLow = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low); bool isHigh = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high); if (!isLow && !isHigh && !all) continue; const bool safeLow = !isLow; const bool safeHigh = !isHigh; if ((!isLow || !isHigh) && all) { MathLib::bigint minValue, maxValue; if (getMinMaxValues(arg.valueType(), *settings, &minValue, &maxValue)) { if (!isLow) low = minValue; if (!isHigh) high = maxValue; isLow = isHigh = true; } else if (arg.valueType()->type == ValueType::Type::FLOAT || arg.valueType()->type == ValueType::Type::DOUBLE || arg.valueType()->type == ValueType::Type::LONGDOUBLE) { std::list argValues; argValues.emplace_back(0); argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT; argValues.back().floatValue = isLow ? low : -1E25f; argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue)); argValues.back().safe = true; argValues.emplace_back(0); argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT; argValues.back().floatValue = isHigh ? high : 1E25f; argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue)); argValues.back().safe = true; valueFlowForward(const_cast(functionScope->bodyStart->next()), functionScope->bodyEnd, arg.nameToken(), argValues, tokenlist, settings); continue; } } std::list argValues; if (isLow) { argValues.emplace_back(low); argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeLow ? "Safe checks: " : "") + "Assuming argument has value " + MathLib::toString(low)); argValues.back().safe = safeLow; } if (isHigh) { argValues.emplace_back(high); argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeHigh ? "Safe checks: " : "") + "Assuming argument has value " + MathLib::toString(high)); argValues.back().safe = safeHigh; } if (!argValues.empty()) valueFlowForward(const_cast(functionScope->bodyStart->next()), functionScope->bodyEnd, arg.nameToken(), argValues, tokenlist, settings); } } } static void valueFlowUnknownFunctionReturn(TokenList *tokenlist, const Settings *settings) { if (settings->checkUnknownFunctionReturn.empty()) return; for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->astParent() || tok->str() != "(" || !tok->previous()->isName()) continue; if (settings->checkUnknownFunctionReturn.find(tok->previous()->str()) == settings->checkUnknownFunctionReturn.end()) continue; std::vector unknownValues = settings->library.unknownReturnValues(tok->astOperand1()); if (unknownValues.empty()) continue; // Get min/max values for return type const std::string &typestr = settings->library.returnValueType(tok->previous()); MathLib::bigint minvalue, maxvalue; if (!getMinMaxValues(typestr, settings, &minvalue, &maxvalue)) continue; for (MathLib::bigint value : unknownValues) { if (value < minvalue) value = minvalue; else if (value > maxvalue) value = maxvalue; setTokenValue(const_cast(tok), ValueFlow::Value(value), settings); } } } ValueFlow::Value::Value(const Token* c, long long val, Bound b) : valueType(ValueType::INT), bound(b), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(MoveKind::NonMovedVariable), varvalue(val), condition(c), varId(0), safe(false), conditional(false), macro(false), defaultArg(false), indirect(0), path(0), wideintvalue(0), subexpressions(), lifetimeKind(LifetimeKind::Object), lifetimeScope(LifetimeScope::Local), valueKind(ValueKind::Possible) { errorPath.emplace_back(c, "Assuming that condition '" + c->expressionString() + "' is not redundant"); } void ValueFlow::Value::assumeCondition(const Token* tok) { condition = tok; errorPath.emplace_back(tok, "Assuming that condition '" + tok->expressionString() + "' is not redundant"); } std::string ValueFlow::Value::infoString() const { switch (valueType) { case ValueType::INT: return MathLib::toString(intvalue); case ValueType::TOK: return tokvalue->str(); case ValueType::FLOAT: return MathLib::toString(floatValue); case ValueType::MOVED: return ""; case ValueType::UNINIT: return ""; case ValueType::BUFFER_SIZE: case ValueType::CONTAINER_SIZE: return "size=" + MathLib::toString(intvalue); case ValueType::ITERATOR_START: return "start=" + MathLib::toString(intvalue); case ValueType::ITERATOR_END: return "end=" + MathLib::toString(intvalue); case ValueType::LIFETIME: return "lifetime=" + tokvalue->str(); case ValueType::SYMBOLIC: std::string result = "symbolic=" + tokvalue->expressionString(); if (intvalue > 0) result += "+" + MathLib::toString(intvalue); else if (intvalue < 0) result += "-" + MathLib::toString(-intvalue); return result; } throw InternalError(nullptr, "Invalid ValueFlow Value type"); } const char* ValueFlow::Value::toString(MoveKind moveKind) { switch (moveKind) { case MoveKind::NonMovedVariable: return "NonMovedVariable"; case MoveKind::MovedVariable: return "MovedVariable"; case MoveKind::ForwardedVariable: return "ForwardedVariable"; } return ""; } const char* ValueFlow::Value::toString(LifetimeKind lifetimeKind) { switch (lifetimeKind) { case LifetimeKind::Object: return "Object"; case LifetimeKind::SubObject: return "SubObject"; case LifetimeKind::Lambda: return "Lambda"; case LifetimeKind::Iterator: return "Iterator"; case LifetimeKind::Address: return "Address"; } return ""; } bool ValueFlow::Value::sameToken(const Token* tok1, const Token* tok2) { if (tok1 == tok2) return true; if (!tok1) return false; if (tok1->exprId() == 0 || tok2->exprId() == 0) return false; return tok1->exprId() == tok2->exprId(); } const char* ValueFlow::Value::toString(LifetimeScope lifetimeScope) { switch (lifetimeScope) { case ValueFlow::Value::LifetimeScope::Local: return "Local"; case ValueFlow::Value::LifetimeScope::Argument: return "Argument"; case ValueFlow::Value::LifetimeScope::SubFunction: return "SubFunction"; } return ""; } const char* ValueFlow::Value::toString(Bound bound) { switch (bound) { case ValueFlow::Value::Bound::Point: return "Point"; case ValueFlow::Value::Bound::Upper: return "Upper"; case ValueFlow::Value::Bound::Lower: return "Lower"; } return ""; } const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(Token *expr, const Settings *settings) { if (expr && expr->values().empty()) { valueFlowConstantFoldAST(expr->astOperand1(), settings); valueFlowConstantFoldAST(expr->astOperand2(), settings); valueFlowSetConstantValue(expr, settings, true /* TODO: this is a guess */); } return expr && expr->hasKnownValue() ? &expr->values().front() : nullptr; } static std::size_t getTotalValues(TokenList *tokenlist) { std::size_t n = 1; for (Token *tok = tokenlist->front(); tok; tok = tok->next()) n += tok->values().size(); return n; } void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) tok->clearValueFlow(); valueFlowEnumValue(symboldatabase, settings); valueFlowNumber(tokenlist); valueFlowString(tokenlist); valueFlowArray(tokenlist); valueFlowUnknownFunctionReturn(tokenlist, settings); valueFlowGlobalConstVar(tokenlist, settings); valueFlowEnumValue(symboldatabase, settings); valueFlowNumber(tokenlist); valueFlowGlobalStaticVar(tokenlist, settings); valueFlowPointerAlias(tokenlist); valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); valueFlowSymbolic(tokenlist, symboldatabase); valueFlowBitAnd(tokenlist); valueFlowSameExpressions(tokenlist); valueFlowConditionExpressions(tokenlist, symboldatabase, errorLogger, settings); std::size_t values = 0; std::size_t n = 4; while (n > 0 && values < getTotalValues(tokenlist)) { values = getTotalValues(tokenlist); valueFlowImpossibleValues(tokenlist, settings); valueFlowSymbolicIdentity(tokenlist); valueFlowSymbolicAbs(tokenlist, symboldatabase); valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings); valueFlowSymbolicInfer(tokenlist, symboldatabase); valueFlowArrayBool(tokenlist); valueFlowRightShift(tokenlist, settings); valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterSwap(tokenlist, symboldatabase, errorLogger, settings); valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings); valueFlowInferCondition(tokenlist, settings); valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings); valueFlowFunctionReturn(tokenlist, errorLogger); valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, settings); valueFlowUninit(tokenlist, symboldatabase, settings); if (tokenlist->isCPP()) { valueFlowAfterMove(tokenlist, symboldatabase, settings); valueFlowSmartPointer(tokenlist, errorLogger, settings); valueFlowIterators(tokenlist, settings); valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings); valueFlowIteratorInfer(tokenlist, settings); valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings); valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings); } valueFlowSafeFunctions(tokenlist, symboldatabase, settings); n--; } valueFlowDynamicBufferSize(tokenlist, symboldatabase, settings); } ValueFlow::Value ValueFlow::Value::unknown() { Value v; v.valueType = Value::ValueType::UNINIT; return v; } std::string ValueFlow::eitherTheConditionIsRedundant(const Token *condition) { if (!condition) return "Either the condition is redundant"; if (condition->str() == "case") { std::string expr; for (const Token *tok = condition; tok && tok->str() != ":"; tok = tok->next()) { expr += tok->str(); if (Token::Match(tok, "%name%|%num% %name%|%num%")) expr += ' '; } return "Either the switch case '" + expr + "' is redundant"; } return "Either the condition '" + condition->expressionString() + "' is redundant"; } const ValueFlow::Value* ValueFlow::findValue(const std::list& values, const Settings* settings, std::function pred) { const ValueFlow::Value* ret = nullptr; for (const ValueFlow::Value& v : values) { if (pred(v)) { if (!ret || ret->isInconclusive() || (ret->condition && !v.isInconclusive())) ret = &v; if (!ret->isInconclusive() && !ret->condition) break; } } if (settings && ret) { if (ret->isInconclusive() && !settings->certainty.isEnabled(Certainty::inconclusive)) return nullptr; if (ret->condition && !settings->severity.isEnabled(Severity::warning)) return nullptr; } return ret; } static std::vector isOutOfBoundsImpl(const ValueFlow::Value& size, const Token* indexTok, bool condition) { if (!indexTok) return {}; const ValueFlow::Value* indexValue = indexTok->getMaxValue(condition, size.path); if (!indexValue) return {}; if (indexValue->intvalue >= size.intvalue) return {*indexValue}; if (!condition) return {}; // TODO: Use a better way to decide if the variable in unconstrained if (!indexTok->variable() || !indexTok->variable()->isArgument()) return {}; if (std::any_of(indexTok->values().begin(), indexTok->values().end(), [&](const ValueFlow::Value& v) { return v.isSymbolicValue() && v.isPossible() && v.bound == ValueFlow::Value::Bound::Upper; })) return {}; if (indexValue->bound != ValueFlow::Value::Bound::Lower) return {}; if (size.bound == ValueFlow::Value::Bound::Lower) return {}; ValueFlow::Value value = inferCondition(">=", indexTok, indexValue->intvalue); if (!value.isKnown()) return {}; if (value.intvalue == 0) return {}; value.intvalue = size.intvalue; value.bound = ValueFlow::Value::Bound::Lower; return {value}; } std::vector ValueFlow::isOutOfBounds(const Value& size, const Token* indexTok, bool possible) { ValueFlow::Value inBoundsValue = inferCondition("<", indexTok, size.intvalue); if (inBoundsValue.isKnown() && inBoundsValue.intvalue != 0) return {}; std::vector result = isOutOfBoundsImpl(size, indexTok, false); if (!result.empty()) return result; if (!possible) return result; return isOutOfBoundsImpl(size, indexTok, true); } cppcheck-2.7/lib/valueflow.h000066400000000000000000000416601417746362400161120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef valueflowH #define valueflowH //--------------------------------------------------------------------------- #include "config.h" #include "mathlib.h" #include "utils.h" #include #include #include #include #include #include #include #include #include class ErrorLogger; struct InferModel; class Settings; class SymbolDatabase; class Token; class TokenList; class ValueType; class Variable; template class ValuePtr; namespace ValueFlow { struct increment { template void operator()(T& x) const { x++; } }; struct decrement { template void operator()(T& x) const { x--; } }; struct less { template bool operator()(const T& x, const U& y) const { return x < y; } }; struct adjacent { template bool operator()(const T& x, const U& y) const { return std::abs(x - y) == 1; } }; struct equalVisitor { template void operator()(bool& result, T x, U y) const { result = !(x > y || x < y); } }; class CPPCHECKLIB Value { public: typedef std::pair ErrorPathItem; typedef std::list ErrorPath; enum class Bound { Upper, Lower, Point }; explicit Value(long long val = 0, Bound b = Bound::Point) : valueType(ValueType::INT), bound(b), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(MoveKind::NonMovedVariable), varvalue(val), condition(nullptr), varId(0U), safe(false), conditional(false), macro(false), defaultArg(false), indirect(0), path(0), wideintvalue(val), subexpressions(), lifetimeKind(LifetimeKind::Object), lifetimeScope(LifetimeScope::Local), valueKind(ValueKind::Possible) {} Value(const Token* c, long long val, Bound b = Bound::Point); static Value unknown(); bool equalValue(const ValueFlow::Value& rhs) const { if (valueType != rhs.valueType) return false; switch (valueType) { case ValueType::INT: case ValueType::CONTAINER_SIZE: case ValueType::BUFFER_SIZE: case ValueType::ITERATOR_START: case ValueType::ITERATOR_END: if (intvalue != rhs.intvalue) return false; break; case ValueType::TOK: if (tokvalue != rhs.tokvalue) return false; break; case ValueType::FLOAT: // TODO: Write some better comparison if (floatValue > rhs.floatValue || floatValue < rhs.floatValue) return false; break; case ValueType::MOVED: if (moveKind != rhs.moveKind) return false; break; case ValueType::UNINIT: break; case ValueType::LIFETIME: if (tokvalue != rhs.tokvalue) return false; break; case ValueType::SYMBOLIC: if (!sameToken(tokvalue, rhs.tokvalue)) return false; if (intvalue != rhs.intvalue) return false; break; } return true; } template static void visitValue(T& self, F f) { switch (self.valueType) { case ValueType::INT: case ValueType::SYMBOLIC: case ValueType::BUFFER_SIZE: case ValueType::CONTAINER_SIZE: case ValueType::ITERATOR_START: case ValueType::ITERATOR_END: { f(self.intvalue); break; } case ValueType::FLOAT: { f(self.floatValue); break; } case ValueType::UNINIT: case ValueType::TOK: case ValueType::LIFETIME: case ValueType::MOVED: break; } } struct compareVisitor { struct innerVisitor { template void operator()(bool& result, Compare compare, T x, U y) const { result = compare(x, y); } }; template void operator()(bool& result, const Value& rhs, Compare compare, T x) const { visitValue(rhs, std::bind(innerVisitor{}, std::ref(result), std::move(compare), x, std::placeholders::_1)); } }; template bool compareValue(const Value& rhs, Compare compare) const { assert((!this->isSymbolicValue() && !rhs.isSymbolicValue()) || (this->valueType == rhs.valueType && sameToken(this->tokvalue, rhs.tokvalue))); bool result = false; visitValue( *this, std::bind(compareVisitor{}, std::ref(result), std::ref(rhs), std::move(compare), std::placeholders::_1)); return result; } bool operator==(const Value &rhs) const { if (!equalValue(rhs)) return false; return varvalue == rhs.varvalue && condition == rhs.condition && varId == rhs.varId && conditional == rhs.conditional && defaultArg == rhs.defaultArg && indirect == rhs.indirect && valueKind == rhs.valueKind; } bool operator!=(const Value &rhs) const { return !(*this == rhs); } template )> bool equalTo(const T& x) const { bool result = false; visitValue(*this, std::bind(equalVisitor{}, std::ref(result), x, std::placeholders::_1)); return result; } void decreaseRange() { if (bound == Bound::Lower) visitValue(*this, increment{}); else if (bound == Bound::Upper) visitValue(*this, decrement{}); } void invertBound() { if (bound == Bound::Lower) bound = Bound::Upper; else if (bound == Bound::Upper) bound = Bound::Lower; } void invertRange() { invertBound(); decreaseRange(); } void assumeCondition(const Token* tok); std::string infoString() const; enum class ValueType { INT, TOK, FLOAT, MOVED, UNINIT, CONTAINER_SIZE, LIFETIME, BUFFER_SIZE, ITERATOR_START, ITERATOR_END, SYMBOLIC } valueType; bool isIntValue() const { return valueType == ValueType::INT; } bool isTokValue() const { return valueType == ValueType::TOK; } bool isFloatValue() const { return valueType == ValueType::FLOAT; } bool isMovedValue() const { return valueType == ValueType::MOVED; } bool isUninitValue() const { return valueType == ValueType::UNINIT; } bool isContainerSizeValue() const { return valueType == ValueType::CONTAINER_SIZE; } bool isLifetimeValue() const { return valueType == ValueType::LIFETIME; } bool isBufferSizeValue() const { return valueType == ValueType::BUFFER_SIZE; } bool isIteratorValue() const { return valueType == ValueType::ITERATOR_START || valueType == ValueType::ITERATOR_END; } bool isIteratorStartValue() const { return valueType == ValueType::ITERATOR_START; } bool isIteratorEndValue() const { return valueType == ValueType::ITERATOR_END; } bool isSymbolicValue() const { return valueType == ValueType::SYMBOLIC; } bool isLocalLifetimeValue() const { return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Local; } bool isArgumentLifetimeValue() const { return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Argument; } bool isSubFunctionLifetimeValue() const { return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::SubFunction; } bool isNonValue() const { return isMovedValue() || isUninitValue() || isLifetimeValue(); } /** The value bound */ Bound bound; /** int value (or sometimes bool value?) */ long long intvalue; /** token value - the token that has the value. this is used for pointer aliases, strings, etc. */ const Token *tokvalue; /** float value */ double floatValue; /** kind of moved */ enum class MoveKind {NonMovedVariable, MovedVariable, ForwardedVariable} moveKind; /** For calculated values - variable value that calculated value depends on */ long long varvalue; /** Condition that this value depends on */ const Token *condition; ErrorPath errorPath; /** For calculated values - varId that calculated value depends on */ nonneg int varId; /** value relies on safe checking */ bool safe; /** Conditional value */ bool conditional; /** Value is is from an expanded macro */ bool macro; /** Is this value passed as default parameter to the function? */ bool defaultArg; int indirect; /** Path id */ MathLib::bigint path; /** int value before implicit truncation */ long long wideintvalue; std::vector subexpressions; enum class LifetimeKind { // Pointer points to a member of lifetime Object, // A member of object points to the lifetime SubObject, // Lambda has captured lifetime(similar to SubObject) Lambda, // Iterator points to the lifetime of a container(similar to Object) Iterator, // A pointer that holds the address of the lifetime Address } lifetimeKind; enum class LifetimeScope { Local, Argument, SubFunction, ThisPointer, ThisValue } lifetimeScope; static const char* toString(MoveKind moveKind); static const char* toString(LifetimeKind lifetimeKind); static const char* toString(LifetimeScope lifetimeScope); static const char* toString(Bound bound); /** How known is this value */ enum class ValueKind { /** This value is possible, other unlisted values may also be possible */ Possible, /** Only listed values are possible */ Known, /** Inconclusive */ Inconclusive, /** Listed values are impossible */ Impossible } valueKind; void setKnown() { valueKind = ValueKind::Known; } bool isKnown() const { return valueKind == ValueKind::Known; } void setPossible() { valueKind = ValueKind::Possible; } bool isPossible() const { return valueKind == ValueKind::Possible; } bool isImpossible() const { return valueKind == ValueKind::Impossible; } void setImpossible() { valueKind = ValueKind::Impossible; } void setInconclusive(bool inconclusive = true) { if (inconclusive) valueKind = ValueKind::Inconclusive; } bool isInconclusive() const { return valueKind == ValueKind::Inconclusive; } void changeKnownToPossible() { if (isKnown()) valueKind = ValueKind::Possible; } bool errorSeverity() const { return !condition && !defaultArg; } static bool sameToken(const Token* tok1, const Token* tok2); }; /// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues). const ValueFlow::Value * valueFlowConstantFoldAST(Token *expr, const Settings *settings); /// Perform valueflow analysis. void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings); std::string eitherTheConditionIsRedundant(const Token *condition); size_t getSizeOf(const ValueType &vt, const Settings *settings); const ValueFlow::Value* findValue(const std::list& values, const Settings* settings, std::function pred); std::vector isOutOfBounds(const Value& size, const Token* indexTok, bool possible = true); } bool isContainerSizeChanged(const Token* tok, const Settings* settings = nullptr, int depth = 20); struct LifetimeToken { const Token* token; bool addressOf; ValueFlow::Value::ErrorPath errorPath; bool inconclusive; LifetimeToken() : token(nullptr), addressOf(false), errorPath(), inconclusive(false) {} LifetimeToken(const Token* token, ValueFlow::Value::ErrorPath errorPath) : token(token), addressOf(false), errorPath(std::move(errorPath)), inconclusive(false) {} LifetimeToken(const Token* token, bool addressOf, ValueFlow::Value::ErrorPath errorPath) : token(token), addressOf(addressOf), errorPath(std::move(errorPath)), inconclusive(false) {} static std::vector setAddressOf(std::vector v, bool b) { for (LifetimeToken& x : v) x.addressOf = b; return v; } static std::vector setInconclusive(std::vector v, bool b) { for (LifetimeToken& x : v) x.inconclusive = b; return v; } }; const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value, const std::function(const Token*)>& evaluate); const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value); ValueFlow::Value inferCondition(std::string op, MathLib::bigint val, const Token* varTok); ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val); CPPCHECKLIB ValuePtr makeIntegralInferModel(); std::vector getLifetimeTokens(const Token* tok, bool escape = false, ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{}); bool hasLifetimeToken(const Token* tok, const Token* lifetime); const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr); const Variable* getLifetimeVariable(const Token* tok); bool isLifetimeBorrowed(const Token *tok, const Settings *settings); std::string lifetimeType(const Token *tok, const ValueFlow::Value *val); std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ValueFlow::Value::ErrorPath &errorPath); CPPCHECKLIB ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive = false); CPPCHECKLIB std::vector getLifetimeObjValues(const Token* tok, bool inconclusive = false, MathLib::bigint path = 0); #endif // valueflowH cppcheck-2.7/lib/valueptr.h000066400000000000000000000050241417746362400157420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef valueptrH #define valueptrH //--------------------------------------------------------------------------- #include "config.h" #include #include template class CPPCHECKLIB ValuePtr { template struct cloner { static T* apply(const T* x) { return new U(*static_cast(x)); } }; public: using pointer = T*; using element_type = T; using cloner_type = decltype(&cloner::apply); ValuePtr() : mPtr(nullptr), mClone() {} template // cppcheck-suppress noExplicitConstructor ValuePtr(const U& value) : mPtr(cloner::apply(&value)), mClone(&cloner::apply) {} ValuePtr(const ValuePtr& rhs) : mPtr(nullptr), mClone(rhs.mClone) { if (rhs) { mPtr.reset(mClone(rhs.get())); } } ValuePtr(ValuePtr&& rhs) : mPtr(std::move(rhs.mPtr)), mClone(std::move(rhs.mClone)) {} pointer release() { return mPtr.release(); } T* get() NOEXCEPT { return mPtr.get(); } const T* get() const NOEXCEPT { return mPtr.get(); } T& operator*() { return *get(); } const T& operator*() const { return *get(); } T* operator->() NOEXCEPT { return get(); } const T* operator->() const NOEXCEPT { return get(); } void swap(ValuePtr& rhs) { using std::swap; swap(mPtr, rhs.mPtr); swap(mClone, rhs.mClone); } ValuePtr& operator=(ValuePtr rhs) { swap(rhs); return *this; } operator bool() const NOEXCEPT { return !!mPtr; } ~ValuePtr() {} private: std::shared_ptr mPtr; cloner_type mClone; }; #endif cppcheck-2.7/lib/version.h000066400000000000000000000013341417746362400155650ustar00rootroot00000000000000// For a release version x.y the MAJOR should be x and both MINOR and DEVMINOR should be y. // After a release the DEVMINOR is incremented. MAJOR=x MINOR=y, DEVMINOR=y+1 #define CPPCHECK_MAJOR 2 #define CPPCHECK_MINOR 7 #define CPPCHECK_DEVMINOR 7 #define STRINGIFY(x) STRING(x) #define STRING(VER) #VER #if CPPCHECK_MINOR == CPPCHECK_DEVMINOR #define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR) "." STRINGIFY(CPPCHECK_DEVMINOR) #define CPPCHECK_VERSION CPPCHECK_MAJOR,CPPCHECK_MINOR,0,0 #else #define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR) "." STRINGIFY(CPPCHECK_DEVMINOR) " dev" #define CPPCHECK_VERSION CPPCHECK_MAJOR,CPPCHECK_MINOR,99,0 #endif #define LEGALCOPYRIGHT L"Copyright (C) 2007-2022 Cppcheck team." cppcheck-2.7/lib/version.rc000066400000000000000000000016371417746362400157500ustar00rootroot00000000000000#include "version.h" #include "winresrc.h" VS_VERSION_INFO VERSIONINFO FILEVERSION CPPCHECK_VERSION PRODUCTVERSION CPPCHECK_VERSION FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS (0x1L|VS_FF_PRERELEASE) #else FILEFLAGS (0x0L|VS_FF_PRERELEASE) #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "cppcheck core library" VALUE "FileVersion", CPPCHECK_VERSION_STRING VALUE "InternalName", "cppcheck" VALUE "LegalCopyright", LEGALCOPYRIGHT VALUE "OriginalFilename", "cppcheck.exe" VALUE "ProductName", "cppcheck core library" VALUE "ProductVersion", CPPCHECK_VERSION_STRING END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END cppcheck-2.7/man/000077500000000000000000000000001417746362400137335ustar00rootroot00000000000000cppcheck-2.7/man/build-html.sh000077500000000000000000000001441417746362400163320ustar00rootroot00000000000000#!/bin/sh echo Building $1.html pandoc $1.md -o $1.html -s --number-sections --toc --css manual.css cppcheck-2.7/man/build-pdf.sh000077500000000000000000000013671417746362400161470ustar00rootroot00000000000000#!/bin/sh # This uses installed fonts, which vary between systems # Segoe UI and Consolas are standard in Windows 10, DejaVu is more common on Linux echo Building $1.pdf MainFont="Segoe UI" MonoFont="Consolas" is_font_installed() { fontname=$1 fc-list | grep -i "$fontname" >/dev/null } if ! is_font_installed "$MainFont"; then MainFont="DejaVu Sans" fi if ! is_font_installed "$MonoFont"; then MonoFont="DejaVu Sans Mono" fi # echo Using $MainFont / $MonoFont pandoc $1.md -o $1.pdf -s --number-sections --toc \ --pdf-engine=xelatex \ --listings \ -f markdown \ -V mainfont="$MainFont" \ -V monofont="$MonoFont" \ -V geometry:a4paper \ -V geometry:margin=2.4cm \ -V subparagraph \ -H manual-style.tex cppcheck-2.7/man/buildman.sh000077500000000000000000000005231417746362400160650ustar00rootroot00000000000000#!/bin/sh # To install required tools in debian: # sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-latex-extra # For Windows you can use the MiKTeX installer https://miktex.org/download ./build-pdf.sh manual ./build-html.sh manual ./build-pdf.sh reference-cfg-format ./build-html.sh reference-cfg-format cppcheck-2.7/man/cppcheck-design.docbook000066400000000000000000000130341417746362400203250ustar00rootroot00000000000000 Cppcheck Design DanielMarjamäki Cppcheck 2010
Introduction The goal with this article is to give users an idea of how Cppcheck works. Cppcheck is a static analysis tool that tries to completely avoid false warnings. A false warning is when the tool reports that there is an error even though there is no error. Cppcheck is a relatively simple tool. I hope that this article will highlight that it is possible to avoid false warnings even with simple analysis.
Limitations of static analysis There are many bugs in programs that are really hard to detect for tools. Here is an example: // calculate the number of days int days = hours / 23; A human programmer knows that there are 24 hours in a day and therefore he could see that "23" is wrong. A tool will probably not know that there are 24 hours in a day. A tool that tries to detect all bugs could write a warning message for every calculation in the program. Then it will correctly report that "hours / 23" is wrong but incorrectly warn about "hours / 24". Cppcheck will only write a warning message if it can determine that the calculation is wrong. In this case, no error will be written.
Control flow analysis When you review code you will probably use "control flow analysis" in your head to determine if there are bugs or not. Control flow analysis is when you try to determine what the possible execution paths are. The control flow analysis in Cppcheck is quite simple.
Buffer overflows This is a simple description of how buffer overflows are detected by Cppcheck. If an array is accessed out of bounds somewhere in its scope then an error message will be written. An example code: void f() { char a[10]; if (x + y == 2) { a[20] = 0; } } Cppcheck will report this message: Array 'a[10]' index 20 out of bounds No control flow analysis is used. Cppcheck will not try to determine how execution can reach the "a[20] = 0;" statement. It is assumed that all statements are reachable. Cppcheck will detect the error even if it is really impossible that "x + y == 2" is true. I still claim that this is a correct warning because the statement is there and it has the error. Cppcheck will also investigate function calls. But then control flow analysis can be needed to avoid false warnings. Here is an example that logically is the same as the previous example: void f1(char *s) { s[20] = 0; } void f2() { char a[10]; if (x + y == 2) { f1(a); } } Cppcheck will report this message: Array 'a[10]' index 20 out of bounds If the execution reaches the function call then there will be an error. But if the condition is moved into "f1" then it will be necessary to prove that "x+y==2" can be true when the function is called from "f2". No error message is reported for this code: void f1(char *s) { if (x + y == 2) { s[20] = 0; } } void f2() { char a[10]; f1(a); }
Memory leaks The check uses simple control-flow analysis. The control flow analysis assumes that all conditions can always be either true or false. It is assumed that all statements are reachable. Here is an example: void f() { char *a = malloc(10); if (x + y == 2) { return; } free(a); } Cppcheck will determine that there is a leak at the "return;" statement: Memory leak: a Cppcheck doesn't try to determine how the execution reaches the "return;" statement. It will only see that if the execution reaches the "return;" then there will be a memory leak. Lack of advanced control-flow analysis means that many bugs are not detected: void f(int x) { char *a = 0; if (x == 10) a = malloc(10); if (x == 20) free(a); } Cppcheck doesn't detect any error. The "all conditions can be either true/false" means that cppcheck doesn't know that "if (x==20)" is always false when "if (x==10)" is true. So Cppcheck can't establish that there is a leak. Many other static analysis tools will probably detect that there will be a leak if x is 10.
Final thoughts You cannot trust that Cppcheck will detect all bugs. Cppcheck will just find some bugs. It is likely that you won't find these bugs unless you use Cppcheck.
cppcheck-2.7/man/cppcheck.1.xml000066400000000000000000000603621417746362400164030ustar00rootroot00000000000000 .
will be generated. You may view the manual page with: nroff -man .
| less'. A typical entry in a Makefile or Makefile.am is: DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" manpage.1: manpage.xml $(XP) $(DB2MAN) $< The xsltproc binary is found in the xsltproc package. The XSL files are in docbook-xsl. A description of the parameters you can use can be found in the docbook-xsl-doc-* packages. Please remember that if you create the nroff version in one of the debian/rules file targets (such as build), you will need to include xsltproc and docbook-xsl in your Build-Depends control field. Alternatively use the xmlto command/package. That will also automatically pull in xsltproc and docbook-xsl. Notes for using docbook2x: docbook2x-man does not automatically create the AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as ... . To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be found in the docbook-xsl-doc-html package. Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` General documentation about man-pages and man-page-formatting: man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ --> ]> &dhtitle; &dhpackage; &dhfirstname; &dhsurname; Wrote this manpage for the Debian system.
&dhemail;
2009 - 2016 &dhusername; This manual page was written for the Debian system (but may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or (at your option) any later version published by the Free Software Foundation. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3.
&dhucpackage; &dhsection; &dhpackage; Tool for static C/C++ code analysis &dhpackage; DESCRIPTION Cppcheck is a command-line tool that tries to detect bugs that your C/C++ compiler doesn't see. It is versatile, and can check non-standard code including various compiler extensions, inline assembly code, etc. Its internal preprocessor can handle includes, macros, and several preprocessor commands. While Cppcheck is highly configurable, you can start using it just by giving it a path to the source code. OPTIONS Analyze given C/C++ files for common errors. Check Cppcheck configuration. The normal code analysis is disabled by this flag. Show information messages when library files have incomplete info. By default Cppcheck checks all configurations. Use -D to limit the checking. When -D is used the checking is limited to the given configuration. Example: -DDEBUG=1 -D__cplusplus By default Cppcheck checks all configurations. Use '-U' to explicitly hide certain #ifdef <id> code paths from checking. Example: '-UDEBUG' Enable additional checks. The available ids are: allEnable all checks. It is recommended to only use --enable=all when the whole program is scanned, because this enables unusedFunction.warningEnable warning messagesstyleEnable all coding style checks. All messages with the severities 'style', 'performance' and 'portability' are enabled.performanceEnable performance messagesportabilityEnable portability messagesinformationEnable information messagesunusedFunctionCheck for unused functions. It is recommend to only enable this when the whole program is scannedmissingIncludeWarn if there are missing includes. For detailed information use --check-config By default none of the additional checks are enabled. Several ids can be given if you separate them with commas, e.g. --enable=style,unusedFunction. See also --std If errors are found, integer <n> is returned instead of default 0. EXIT_FAILURE is returned if arguments are not valid or if no input files are provided. Note that your operating system can modify this value, e.g. 256 can become 0. Print a list of all possible error messages in XML format. Used when certain messages should be displayed but should not cause a non-zero exitcode. Specify the files to check in a text file. One filename per line. When file is -, the file list will be read from standard input. Force checking of files that have a lot of configurations. Error is printed if such a file is found so there is no reason to use this by default. If used together with --max-configs=, the last option is the one that is effective. Print help text. Give path to search for include files. Give several -I parameters to give several paths. First given path is searched for contained header files first. If paths are relative to source files, this is not needed. Specify directory paths to search for included header files in a text file. Add one include path per line. First given path is searched for contained header files first. If paths are relative to source files, this is not needed. Path (prefix) to be excluded from configuration checking. Preprocessor configurations defined in headers (but not sources) matching the prefix will not be considered for evaluation of configuration alternatives. A file that contains a list of config-excludes. Force inclusion of a file before the checked file. Can be used for example when checking the Linux kernel, where autoconf.h needs to be included for every file compiled. Works the same way as the GCC -include option. Give path to ignore. Give several -i parameters to ignore several paths. Give directory name or filename with path as parameter. Directory name is matched to all parts of the path. Allow that Cppcheck reports even though the analysis is inconclusive. There are false positives with this option. Each result must be carefully investigated before you know if it is good or bad. Enable inline suppressions. Use them by placing comments in the form: // cppcheck-suppress memleak before the line to suppress. Start <jobs> threads to do the checking work. Specifies that no new threads should be started if there are other threads running and the load average is at least <load> (ignored on non UNIX-like systems) Forces cppcheck to check all files as the given language. Valid values are: c, c++ Use library configuration. Maximum number of configurations to check in a file before skipping it. Default is 12. If used together with --force, the last option is the one that is effective. Maximum depth in whole program analysis. Default is 2. Specifies platform specific types and sizes.The available platforms are: unix3232 bit unix variantunix6464 bit unix variantwin32A32 bit Windows ASCII character encodingwin32W32 bit Windows UNICODE character encodingwin6464 bit Windows By default the platform which was used to compile Cppcheck is used. Only print something when there is an error. Use relative paths in output. When given, <paths> are used as base. You can separate multiple paths by ';'. Otherwise path where source files are searched is used. E.g. if given value is test, when checking test/test.cpp, the path in output will be test.cpp instead of test/test.cpp. The feature uses string comparison to create relative paths, so using e.g. ~ for home folder does not work. It is currently only possible to apply the base paths to files that are on a lower level in the directory tree. Report progress when checking a file. Match regular expression to create your own checks. E.g. rule "/ 0" can be used to check division by zero. This command is only available if cppcheck was compiled with HAVE_RULES=yes. Use given rule XML file. See https://sourceforge.net/projects/cppcheck/files/Articles/ for more info about the syntax. This command is only available if cppcheck was compiled with HAVE_RULES=yes. Set standard. The available options are: posixPOSIX compatible codec89C code is C89 compatiblec99C code is C99 compatiblec11C code is C11 compatible (default)c++03C++ code is C++03 compatiblec++11C++ code is C++11 compatible (default) Example to set more than one standards: 'cppcheck --std=c99 --std=posix file.cpp' Suppress a specific warning. The format of <spec> is: [error id]:[filename]:[line]. The [filename] and [line] are optional. [error id] may be * to suppress all warnings (for a specified file or files). [filename] may contain the wildcard characters * or ?. Suppress warnings listed in the file. Each suppression is in the format of <spec> above. Use suppressions defined in xml as described in the manual Format the error messages. E.g. '{file}:{line},{severity},{id},{message}' or '{file}({line}):({severity}) {message}'. Pre-defined templates: gcc, vs More detailed error reports Print out version information Write results in XML to error stream Select the XML file version. Currently versions 1 and 2 are available. The default version is 1. AUTHOR The program was written by Daniel Marjamäki and Cppcheck team. See AUTHORS file for list of team members. SEE ALSO Full list of features: https://sourceforge.net/p/cppcheck/wiki/Home/
cppcheck-2.7/man/images/000077500000000000000000000000001417746362400152005ustar00rootroot00000000000000cppcheck-2.7/man/images/gui-newproject-addons.png000066400000000000000000000502311417746362400221170ustar00rootroot00000000000000PNG  IHDR$sBIT|dtEXtSoftwaregnome-screenshot> IDATxw\e8WT@qQ.R\q0,Kqe+Y6,r;3'ʕ)@Pa\E{/2z><{|?^> TZj̝;wu''<Q ]LSNxܿII2t'9?յk> NFQF1Kq 9::/\_~IG%yt/..h4d2eu4DžڴeΞ;'WWWl\'Oj=CI=M(I샇c7l"nnZ4ZmZ5jO%b3L8m&}>c9?UjT5k鵞=G ~\(ܹլ.$ 6\٫OL3uHʿʔVz%6zS?_ɓ_YWz*T Tۖ F?Hڴ3n!}Ol;d҈z{@uo<:}Oclٴh2u௼Vo\QC9P$_ۂ$I~ߣe0t4$D7nܐ=RDjT@y2e$InU)*XbܵUUɗfboܐg)\׭wvm̑C mKƓc/˽e+&&V5UXnkzؾ'zrrrҷsx }g99'AC?hw5w4KHwNZھsF8A^|J988K/=PK ն_ӻ#GR 4~hVfM/w 5_O111r͓GwbEJf~Be2zWӧ5kR-..N)ɨKX] [ӡ}[}vڭ-+giϽ}ҋI+6:u$?yRS>… i՚= rkN=zWtM-Z+_y/'NTҥɔI3>DCin`GcǨS>~XH5U+e~0R];wRhx;W쑳BzފsRD$EjO")h* }Ӯ "nnZt._jlNg]Eڵ7iѬYiZ⢿G .Kn}4nUbUS0}<..r03gNz4c7־];cf͝=+ɩ ٳ tI2\~=/t~Tnڰ~ *$'GG-q*T(%Kd`2̗h?JxrN&Ο{˛7gx_ءqcFlٲt~enݍ7|S:xfVoW׮]U|\WjTH~ ]#4z/U[exbEkj[ppGK<(^|6TbQjִ\\\jܹsSJ{{_ۿgwĜ2bɓGI̽2LZn>6U-{Nҝt:YYȝ;}oiȇTfMн k^0jAZfԩc$TǏ%KUJML1-Ie+TW_sȳԭ6oܸCbjZAƢAA+̯EGGkTZuU\USTT$i5oYN}T~CIwNLK־c/*VԾC'֩۷5q=S}ʩGݷ/Ix_;~ VjU5pL&]rEow{vk 5K9Jiݒd4&(4,LF_a?pF$hϞ=~,-YPŋOӘȑCk{tE:mܰ^3n$i'rԦuk =}MK־c/ z_?}Kmۢ%Jcr*}5 ٵC ԗN)^Z%齑hƍ՗n%K:'3CLL.^dt$i!2 C$:o6oܠjUs<9mΖњ }CVogϞ]ԺMkIRV~zH._E3Ԡ~}UPAC1/oի5[}zN=S=jW/2I,۵ӳrww׫]pAW\hTM%Jqƙ0:we?Ҿ+碭z23g$9:쬞=^Rm$I<}6[)O<:vxӷ׾]6s{ZH]vMFQ UP!IJ5q-Z^;>н.^ٲ)hJ-]|lٲ>x&Q"n:$sZ:ot7^͔bOHHH}[0Y/4v(5kTt-2R/ТFy}7gƍ}K{5pV.SjwDN\rT ̙SL.ݟOz7Z\ٲeK\z5y^saнTqNг~MԺUK}0}y*eS;ۛiZvVZ/hUsҸY=ڲoɀmc+WTUH]p^5jV׍7auаP%$$vZɴkX~2F4o.7$g}u-s8cҥ^Ϫgϗ5pyyyZ?f4,sGZwJiLN5sQdLI , 'ݿ>8h*>o\my*V-SԽk<ɖu-vg=t9%$$zjVǙK.xԠVDFLi?)O9jaz4_;':~JF1AUHH[ګ^-}4hxgxeSn]5x0fٟSJugMJ+jɢ+5|vZ4oUb"&W~4.msnn7dB/_Gw O<~`H 9::Z-]Y_JTըFˌ@j&'fNef< e)7*V 'IΞ=;~zZlߧ"nni^fZK 53Lì;ε)!h۷ok}ݹ:wo;.=ϝyuYӾ-cR/In%GM&~+J&JX=wmUFu}?[֫LڊOH5Ka9kG[oٗ#.+88X?N ??5k*Vx`={$wؙ|TkC;c\v*͝3G#?1YX+=?iY@F˒%ڱs܋Riooyh/&~g8ibccӴxyz`0˳on޼XʗWewU-ZT0Zׯ_Wioo}dxnKN>o%M?~5kDŊKu{oSHU\YA˗vZI3J'O}ԿEsNg;?ڷe 5qҫ"#J*ZzefZiS&k̸ձSEEGS#Igo%I_~_2x$i1H_͜Rψwʼnm IDATct%}t}t=l5U>}F$UaB5oݺuKeJ{k1ֵ>JҥKj٦._5j(h2eϞuxۣr_"(Ųw4/_>v'OZ*Ϟ=L)'+ݽn2/Һa?g͜ν}F` K>nVfEFEٴL^WWUQ3"~eȨ(zM?)%􃇼(!!!}1C=J)**JGCB=],f-Z?>5k۸ -]3IOҫR^eTH1uu4(饼 E6 9v!G $S͛⏓ɓ䵔.\_zOH&N- Tzui ?y\ujR6mqټ̶`Zo {ۣ"EԲU[ƚ^VʕMt!L&I/4kl}:|p [qO,SuQu˫Q뒤 MTٲe KF}R%K@p99:闍L4Uo6[xb4i5:aoS = Νu 6+m*wѐ$_`,IУ4g]xQJj/G Mx5sַ|'rpH ʑ#%9ifk\YϵhV3g*gΜʙ3_d2U|R> dI>w\EGGwʕ+U888hgͷҋϫF)?[ $IgΜdWzޥ,_54qeסCչkwٳWG)xeJYfi21cԯ_?ک]:w꨽ñcSg24w ϫ|rʖ-gϞn^WW]|%k׮E*GnW5k!>e,P@ժUաCհ_6{eJ>}hĉڰaN8vY}zjԨ.w*Tu>4Y>k T\\ԍV &y-_q]G_x^ڴWppwWfs2)Ao޼|||zj 2D4` Y5k5Zx\]]ͯ`0(,<"lZ&רA`2,AR۴ۓ}lJ Oq P߀5ko֦u'카z7ڌ+zk*V6^ŊMu($GGt2,AyX9sY[lTBB$iŚ9smd./?kfVɭKyL0Q[3!B@z=5֯_ UP!պE1^ 999ɽx.9}FUY&qQQQ3g.^RJ[ -V*OP6ml.s+ߣFM%o)**J}+*^BoWw5zʔܮUf|,66VoWKD)o <4ؖZ_>y[5;PTbIJ5v5n*TzMuU'I>>Zz "I0`@f:E{K[Ѻ}3Fb%I>MQgˆϩ06VAK/+4,LU,'-q{Zzn޼˖[.2 Vx;88( @FQ!!!TswV(Ue˦]w'[Jxx*㣼y] mҥe0w>g0\Zjl}0jbbbR]OZNt͚6VYVsz;ؙLI\Ϟ=Ϳd>^/KqYG(wʞ={IKܒ榐>sF'N$e˖M:vQbTRF@:ɴU7oգGZmNmZ /PMQgˆϩ0+ZԦRꧭm[j־2V$n#4u:w>K`]}NUX1I=t<>>>rrr-Z4ךU+gQcc.7U%<<Կ__Q޼|KmkR'Cn]m6]f7GG꫙h*P zSCt!$*:wChHbccvOi@KIZrUh]tIuhrέٳKgɓ*RMaaڲu.^ ',ֶ-go̕ `Y35u$-[MIZt\\\$IMD[T.UG7no۶$9}/gXѢZʔ y)? Y,jq[kwXJ~\`ֻ]6*\*W^#߆ 5lujzZף[}~6ڳ7@2\tdm卛6CǎEUttɓm)W| ޲IM׶*H@5mg"z=eDy)"BTu7s OsT?ofsɓ|r<h4 Tr/饣!ǴxǺ;U(_^˖du$A`H$t :v;@ A`H$j}i^fEʰ=2*Jo2{woXg;o++WN}Rhdc =dX.IyM,&&F9s攃ugٛL& 4X,՘ѣ4~XkΝLs'L`m4=5֯_ UPbVDžon*YzZxeM⢢4g]xQJj1>!$ɵ签r/ᩢ%ƛ}y:xb=jԤ\Pi UNsJu}@Ȓ}ܹV޽+W.o޼GCԬITҫΟ];Ӟ;uI ]sy||^z :D׆4jm[:t`C ȔSccc`UZUk֌3~Y?Oh4{>sFV_'^N/`nS&u[$ӧڷo7|S'Nէ/.ΫXѢ9~J];). E6o\>>>ZzN8!I0`ʕKe}|e6լY#:$E^9r<|dLA2 QJԨQ#MΝ?lyR%-[6ڽF2&q={4 `ݺvQ6mTN]n9ÇW3y'OZ/ }ƍ:xVZjnnn 3gt4 i 6oެG6/o04{LM2I˖qfjҼ,]&Iwn׶Mku%*R\]nvmTPaUZCGoNSxzǴƍ?t/<]/<=ٲlٲi 4qB/^xൢE#iӆu6/(ժgT#IDAT:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@hYR+mwuiE+{p}}9*lI z%O 4@@:$Pd5s֬XX?P oEt1z/<ʇgƔY hdkwQVt^E߃YTvn{eOEDl"үxxF_gkl-woQa}%@UXZ4q(Twv&yKEQ*:pט>y]Kp(n 6o lb[d}$@CH~m s[RT9EHT3B(R@U|r!+Ղ>@) Wп@oT־m6o޽Gle>|D~zw{wӪZV.߯:+zt;Cѱ`OO]]yz[o.^x]^)W0Z7.nmqiřg}QL<9,Y*Ūm8(Nr9~rSOO>%λ;gN|M1x]㪟)qQG\ jj@{&N[o5zݻwqvUi7>*[n',}brt%"" GO8>Ə7v\իcN?Vi͟U'{l\ri {lE?``<8ScmknȑFQdI}uTU^qQxɒOGr/.2ۅ^GsLlѹ/"O{>ƫElAŮDZǟ..E[+VO= 1|D;qb̝Vyq+3fDQ̘sN<31lذ8#!@\;x1~ܸӧOEt1oxI1W_O/VN[v[Q.cXgzDD|k@^՟xv[ ,]~;vme-;'N.]Ɛ]v{;6jZ0_+_ap_~1i#M3FCM3^ݻ403[iӈX0n/~7/eK|fuW=?5c^zlz""i>wL޽b֋'|rcWr.ƌ+/,-|s.om(+x㎍;>>øm@hqw@ՍCw ^ӦM/No.]ƬYL މyA̛79c6?1sklxcƌWc6=4ƌ|Xx1q}ϵFŔ)ONJeN1hmc1cFL4)=:~}qEJ|s7p8.9s+%*RV*EUW/='ث.'nh cacnjې!/ư|/$40'/N~rqG(>Hlv,XFߏg.Ti;*? Ⱥ\.P*555ň{q>8ehh0С&swkμfw`f{1_Wv t*F\DTNTSSrM}ͣ\DM Y:DC{m^y 6$^>-RD{HQ.1ѩS&cӸ1{FZtuJ)\.⒇v=7T߃TWghѢZ6:u}w_sijo.lݶyls8jm*:O@5=@@ @:$   tH@@lOAIENDB`cppcheck-2.7/man/images/gui-newproject-pathsanddefines.png000066400000000000000000001071541417746362400240160ustar00rootroot00000000000000PNG  IHDR$sBIT|dtEXtSoftwaregnome-screenshot> IDATxwXwaU EkTTT{l1Ėk4Ec71&$^[ػXPl6au+>{=3|ٍΙJmժU?SBB 胈*X,..**ʼsΥwy/^pXM>|;cּ-g*um7nw9,nsAmƢ6nR^y!""Bm@DDaCס 9%o7@P:k԰Ʈ`bbR*uJ `¸h߮YYYغ=>y6.\kkk<\^y!""n|;iX4o.z,\|g)WLЉHe5aoob#p9&bo;̞1999u:>{32FDs{hԠ֬V̹s(**F-0q8ԨQo>z =%zvxDFE&L mK}+_HtOtZ˗췢Db1&Lb?~ zrc;[[h惽0rpXZZ`-073C`Wl8uG]%/k@<=Eu`8:wp=܊տe#>Bzºӧz,\ ӳdA^Zw)Xfӧ _1BDDՇ[ݺz M/@ )S:k^&DDT~~>&'hՃ [ofMbgAdT:uD:us8~]uŘ׸q AvNvރ,,[0gB=~>M{``meQT\苗4ujF-GW k;S+q(/ܺ 'O!l.w'000/p>9tX:s"Z͋:J؇|ܿ?n@M{{_J~OύЦuk4mRl-8y4 ?U&NBB|6y Q˗,*wLЉH/g̔&^$1Ÿy6"Цuk,3Za&̞1]j+1Wq%* 'cF޻瑑d=~|b_9K}9&/C{nRf?:]i2+ج,-?@ar>MB‘!aמmڷk;;[?y yy066''@`Wܽ3/qr(o//XF}2$VZwz%7suq? ITTt4~[]]S}閚X{IqRrt,wLЉHF ©#,`S6 $)U{v$++]@hN*{ ݧ%䚢,6+KKɒge .ľ#?j֔F__?ęsЮ[Zde^ԪxruvqqdffVJߟcySSS1Qa 4?P0A'""ތLUbr"A__--s'?2Wg$$&J[Ŧ]տ"3 4H6}{6C! 2/bժF044PZnݥy;bP JnK{ MI9w>><+W,Q]LЉyΝB~rssn^͚R8|AOO)]*;;'G؀P)20b_ udnSl9~Keѕy);^xY|B"sТYs]VE^߃P(Ěc$w'"m_I+M{c/U/t""t֭pY̚nuc.aᄇ;:匙hܨ!222̟= ѡ/aia SZk!>!bG|X؅B!pn=ƌ0p"ܶMuPS6A8{.=wqS.wߑ/Eu ܊Òߣ=v=U[ڮÎ۹  +/H܎'/Y$9[rrL%]?%H 94Wca֭puq~}|3 >1QQ000%_OG‹X8p){7+z꤉ptp_C06&wuyպ&3+N]}RT޽ظe+9hTreu+R9`Q\\UuH9vMj) \[hҽIZ~~> vmKiS楠r""gЉe{MP+9v$=k$煈HۘQzkU`O><[8/DDDTի8t(5hdܘqeãJiA'""""""9. p/Νr#*^V@DDgЙlSZxp\8U_;8Fq'""]Ϡ:Qw"""""""%7mP242F fZD(.}MܵKVǎYMj,uCppΟRL4DRR͟ĊvDנ;;;.b]ń 1v8q}͛@MXe>7ŽwnZ8::lݪV=VƅhDĹg`mmukj\.|;aW$a (G0pP)}P5*T Μ9[:*K~~>|¥h?˗h;J'wᄊCT˖'?ʎ?]:>+"ƷXJ_{FTZ[=EDDT]hxB..Βw<#t:?*rb5Jc &LD޽`aaR I/I޻{'LW-GGÉ'1˯vm۔llܴM4k[ 9s;B+TOy!>t{VVU.8u Ξ oooC/vh.777oAÆ PNmmSi"퇝"û v8:ݵ;<>".,<{vKkX[WJ;DDDoϠ|G("//|3^015G]7π۷?̝O@OMH۲y+Zl 3sKo>>abj:u1u4kbK.G۶W=۷_Rѣ3\\`„HN٤ ͛JĘ1N=z 2gb`Ԩ5 KkxoqPCs&}NjV-]8~D*`aaKQ (wB26/SSSm3b1"ѳG[oa歷^333ŨH]ۡT{#uLK6 bk֬1̌y+c,vE;$"? hFMp% Am7ܹ{Xx vG7 pr#Y L|*4kM|0iTdfl[PP.D[! Up,]gΞ Wb/ox/rss,n߱-ٳgppr͛$9sN.sss1iT4j >[boP6$`ffxۨ`֜nгO_%򪔿|*={#йk\뺣y0{<7j'K.Rsݰ1~Y=zg=Qo4EHPBPv)+HEd /A붾p`*6/9 "" ޽{@_h3s QFb:&&&%bc_K=x@t`HJJBzz:=KH$B׀ppp@ڵѥK(k~|HMMœ'O[+ѡ}{4jSL.99BRC{ng&Ǟ={ժS^Ww 5)??sP\\?~SǏNflڵ QΠC"q'"99߇C@bb"fϝ+)z7!g\8qcǢN:ظO|>e2|۵GI8q5%;;)WJJ `XR%KY1y)ϧ}(_mgggreciSN0ad$\]?‘C*u6WQcTѸ<{ c>CŨHe/Ss #>!3gѱC|6a"D"ZmiJľ :v耈}ʕ_1*.YjΝ}ݼgao 7ETouua4נuvv™ӧzPXڬc%BFF&QXXR[? ϯƏؔ߹`I;xhݺؔ?~ӧpqq F❒;{xJE"by-;{)_1OP}$޻׷9fJ?mѢEs%P=r(;Ξ%UGpq): Njƌi_~URؼe+vYtTT5kauMMMq'6LLL0hۨUŮ044#|.lߺEdۻ̛@VaCi&;o:wE^=͌p[Wzs7"w{ᆪRycTٸ?H?JkHNIAvvJmi'Op!:.(9̚*=c{@*m߽>Jr-xzx*7gқĹ8VZesҥ+tCprU}nل4iŋܶ"m W$;R8wF̌t ?E(.*p[ԯjB-GG\¥(\ qX0~Н;DRmqqq_zV[mZNu0ryfbM0O%I^woKڷ!1iTٟ;w͛ˮKVƍaؿf;uSѸQ#ge=F[ݺճH9TTVR+,(P-MٿjԨ$'iӦ]SZPEp%|0b|g=xEEjǩxgNta4I+7+}8~]]ٸqWVV%XmD"^Kzt=c73~۲=/O\r_啭Zy:d ԅ!Ν=޸BI[J&.B/{8R?RrR Dopb4*]9s@DDT8^ypQ$''cѣekxnVP_BlݺHMMAXPeo{{; IFDD}{ee'Ocа0(ǟVٵ6\j;ヌx9|vK[}`a,E3i_~k|Wk5j+ۛwŴ/+1õX?p`ccHs9998sdrM{{ܺ}=BBbbC%' //E3}:ŤɟHipEk_۶G8u4ھ]Ҿ-f$VӲ4p6nڌ+11HHLĦ[0kÕ#G4̘9 FFRAPV^q)**BΝHMMųgpmPaYY~dQںB IDAT-M8?o;_/^J?e}J{PfM{ܹ{NFjj*_<}Z8UoU.&i4zaxar~ ] V-G\*&?>UŅ싀*,5Yogv?b/^x [$-E eWv\Ů~GGPW,\0 /#F!#96m=.͜3{&.^}OV-G A=K,2/^ 3Ok֭GN]аAAx͡~'O"&O?w6u/Wcg/YW|W|ϧNo?ݒOץ+jԨprr+*=i]x91d v))٧вE /S4.Ϟc_1YʂB)r^eд}ѫgWn:i{#"ߥOYyuR21n'1j4M1(d 6iRXUoU)=5믵>DDD$HOyzC 8d j2UgE.ik>._fZĸWVV5V6ǩ~&~wrTPVSvi{*-Zh;""W'g礌cM,YU]N~IDDDՕ :Np-D"[DDDDDz8"M;|z폡CqFHr,qGhUTnp/1^aP5r3jʉʋ=%DxR(q8wqv'"" 'tuH$o"Ν Ǩ vS}"B#S?qR|I[ ++ui}|S:Ģ|tDľ Mm/^BAU.Y3,D`^ qvr8M+1q8yTºu#6z(J -A10$RۈD"޽!Aڵ Ǩ)]U٣;+!2ѥyoz<v(DDDDj;]% ,;qtء\uZZZnb\emCaoENN.|<A>%DFd4Fm>D"Ē +!*z"0nX,X]j;""""zMUi V6 nW_πwCtĸ ,:KjV6 .DK+((YsPa԰s@';)զ ΟB箁0A@;N8 )6orByyy2OHP)#S ɲL|XvC-XdddJ3QatD`^t2 V+Yc?!;wf=hٲ֕LᎰp4kf5QӾ j'N@hd*{9n3p_V<ڕwUbɥjIS+gUQH{ zY.o>Um??e2e˔cAޱٲ/Iu}Wy'̙zq """"*MЋq5ZΟ;OѲu[?y5OOÇv̜1~~QKѯ<'OBV-eRnnnؽ3L>=CTM^HpFD kjj~= =$cѳgwU[`ߋԧŘ8i nݾt?E}g8s,kmW۪;v[`,--e˛OU۬Ϗ"ʎ͗)_yǂTY`.?f>Mѭg/xBʎge}EV-qaGDDDDTU~ C׷À~剩S&n:-JLdC[`<=<051HO֬+[o-f~3͚_VKݒEݫ'օj]w&qV68x(FCe֛r< ) /?gg8;cggOٳtOZZj kk+t c}% B!7n$y_߾3g7nD\|Y3D"áfMvuEΝUnCpw R+Ҟa_۰h 6IenqUX,FXN(OuڬϏ"ʎ򌯺ǂ:/P(Ďp*:Ts-}QVoݛQXP(s{}}}4۷HK:u7oJ'}ٛ=T~zՉ$#S Z`aHNI}y5},UQDٱ2ePcbXᾊ+Ulhh|WJ p% 7nOOO=wNjgϢqFrj*ɚ:ԍn:044ĹH7RGGGGس74@n=w;fldgg@`YN8ڮӱWVV$)-]Y}WK\V~zy+XargEeUR:ʎ͗)_Ue굫hԨ}W9-5 U"""""|~￑TL|*10$6650t\L,n)\qc7nARnUcԍG& :"rssqJ vTo&NB\|!GCrJ ,\GKq|+b`L2o@NN"y#tgEee}~^Vv>/S6ʎSI&N3S3)WJĠw烈HOП>MFU!p066r!ݫ:SǏ*]ۯ_Դ&>-0rG*] un"C {Gg : WwU?Mε0y4ܿ_>vuE=//=z $=|ojZܰ>[vލ7J_\A>hܸ ]1ud 6ZjYh޼sfa`j+b)˕pU >[;"㎰pw rDz$YcYE^e}?ʎӯcG@vvڶ xW/Em);9''gE"0A'""""<%APp0`irerproWU^U02?"ϯJ[x BwB ]x,,,*L̬R(m|~41b߰l#L_]t D,+Z~1޽fI---u.9ז59/..Ɗ~Ӧ29'""""a^Es"U֮%ƍQemnN><;^c? e11ӴJ۞J_JU&D^VNN֮]WJc š5kp5mBDDDDD$+P<ՇIxp=:L$NtKtn9ݻ'H$Ž;t UYZZ*D^qw"" Qשn_>s-EKUΠ㫯ѵkW\xQvvv8ts!-- {p]w}Wɓ'G ۘ6m<<<C7Hڛ2e ZhsssmQQQRDFF&&&GAAf̘///XZZcǎ8{TlBǎS4DDDDDT :++42W38Ņ7i?,حLHT3*KǏ{b͸{.Mwww EaiiYJ 2{EAA3<<Vٳ!0sLy󠯯3fHNJJ… qej x9Æ ôipucҤI믿qF$&&:u WxǦCyH((mÍs_-(*5LٳgXnV^֭[ݻw}b1v؁V틼<=zpe$%%a 055Ś5kb [˖-add$F~ضm|ٲeӧܐ_VB6m`ggٳgyXrZ}""""""'+30V8Y<fv\x3G~߸|b&2T*JAH$B֭… x1[ʲD^ݻ#,, ݺu "##ѥK|5jL6mmG__͚5í[$ {i:v(yO SNvDDDDD]m}zA/=*=OZhh(%g+R]vA$!<<C Q +++!,**2ym Q%V?=c##Bh]dkg{aqֆ]_g)Wd֥<8]_T% '(<#,k X,Fhh( Tdӧ222aš_~*+WbѢEX|9QPP˗/I&2˽ pO>ƍ""""">P4EFB$AT\ 'vʄ>Ɩ00xteq%Uaذa7n␝ǏK}#22yyyR]rߗ$K$wEhh=sss_|z!9KB5 &M_|cȑ(,,Ի~$''#%%'Nԏ e`ȑOpeddd`ѢE|2Ə_Q'""""" k&QPPb Ct[ܼ[灰.יު8٬U\,QcsJK.+|||ХK8::bҤIgϟ?֭,Ohh(v  UXX#..N!C 99Yrvem,_ǪXUɪuͶo> :|5kp044ƊH{"8T}IhzWJ_~ɩ:H;lmm! 4vD"lmmn%w"""""ׄ ߈E !Tr b"ܸ~6Jt.q/ꚠkj""""""(jܸ1^˗.!--M#5RH$@;deXʄXnkcHlFSM QJ&MWQ4iLF aÆcG"5%ES;{{ڢaE8QeeBEiFjz""""""*']8O1A'"""""" LЉtt"""""""H0A'""""""LЉtt"""""""H0A'""""""LЉtPS_LD-5Lj^* Bhd~x\kJ^IQB0x0e|::A$a xF-l,0}Lxy75y"(d.\l#P\\\XF8~D"233}zKޏ< 9oi1J@,#|N}5ڮ.]Imz$Ra juвe =t o~򚵜hV6W[R>sxy7e {zc/dc&7lۚ .]V>E>ok!,|'lѠQWb; 6o~ n8}Jйk ̭l weAyPgR1HDDDD&v z^=all .sTyqq1n߾.@GH$t {~'M۷_igعk $m݁E(ϑ&O͛WFOp)DE8LK}R5&UaoD޵{B^655 ooٳmU$Y3g`9#a9c::(?1eO> gΜp*w)SaPlc=n^M'O}qH} S? 777St|uƌwuuwnYf_{ eJEP@()nl 8:(3th R-M钴IڴIv4=G&M{roz_OA~MysںeQ_mo}ԴoߘIW}^umf8w8L޸g|-=<=?+ 9i|} 1rfҴiӴ xF ~/=/ip'Х\?i*okot[˗/Y_);y<-z-[ŋtnt׏~O}nOA͔ O}RYYQӾ}ZtzzzǬq5EffVq|pkjɒ*5stig[nmwuWU-ҳO?_yYjidD(G=wΝ2 C|rfN=U[,_~c'ҟt0 -Yxszۉ? p2}͚'PQI JᏞ2x|2c댕g?w(/7_v%ZS1]zŲl>564{NXFF$=Ьݝ:ݩސtGh*=\jvzF/ӝw=1Y>tú !)8C4MIbl"WeeN1?hHs<<;$)П|)|^}ӆϨC/}ΚKܬ;~Cٻ7Jw׫WV]ӂCMz)DŽvk 7~M6mܸIO}2*5 ?,ݪE(/񼁁=ﵿY--ުtvD}A74ŗ^oxhuSg~l_w;vWݯuuDΛ9snݪm߱cԚOg.Z[o][[|Ɨ\uÍ_ƍޡ;[7nҍ7^.Mܪ;w* iݺWm 7^+|#?3~x@"}OO!fҥKz͚Q8s ~ukk~_txK/Q=۷ءG3JMD{˳u޹eBsueƍGߑ{"5TH.Zu]ZZ[>%TRV׮#{0G{7iނ*} G|l6}Yׇd{~?\͘>C /~YǭIϔ%5b[F#~SY}䡨? @lp7c(/W/{B7wVc]0@ :q@ :q@pZ>hU̕W\aI_E &}=mY@hN4A t8@@ =fm*zb]Dʕ+׫;R[{$V4u>\*,,l 0I.>^6^aмUXWc**,:$PWH-)3֥L)**Uie w$OH{u[,IC{XΔ+5ݣy t~= P8rlf^X@b1M^yY<˒=ᦛ5Bzq;~֭ %@B2% f{KTA~~}Ey[F=¨`%=dmJ=SjٷGwun6m۾]k_|tij@6uCө;pۨ˗,4Ҫ?wJܦ_z^ IRRR-ZLӴ^WSSӘ>|Y%u~)iu3$rɑU^ ǣ-[5}^tB{m 2 C+W3$)l:ݮz *+gkUIIIji h|6j=:~=,A]Cu?l7ֿz]t**)7s:::?oSyY.:3u˭v.K>z{(VqQ500뒦 K+7'GN {}XyN?c˟Wrs'GɑnBMK8c; ڢd`G9wNen|aCEݣK҂s3hY},q?: <. Ôfa.gJ2 S۲1`nilTn~lv$[Ks ކF{()..:r-ǭ" @*).u @@ twq׫;R[{[K|^].X6_ahªX2&} -韀 &ҼEdʌu)SʬJUodYtH0m2aݖ:$"z!@2%BxlvO@d}tHL z!@2MS$T>}׼Z0^4=;[Y| Mv@ܜ 9p:[)SڷoҦ)%%E jЂ.yRp(99yB-[Ă ϠޒXc]w+lfSff\UVk&Eo*m4UU-`-:$I7pRR$VPHr:j;4ٳ+Ш͛;7rΉ(wqojjRSSSnׇ/Z[;RnK4-'Guzv?TÎc\SR~^ 7+4J?&絛CMnk<ѤϠ=c!#gq8y~mRiSY!QK : &yxeEqJ1eUQ>K!~TE "v:?oQXftH@1=v-.뒬#Q >6 /:$x]X>M[af0X3%v)mtH0n46*7?O6Md9N;kק k!tUZR _6oԼsTQ^})4Mڽ[*/%xZ2MSY 2 # Tffw`!f M.{ظGdٔ"4ק`0$I͙]뤒ceeeJ$efd(֦IRJr*͚3R ֦&)Ua]>Wn)S߁c'^ ҸnJʙ9C9Rm.˔nt:g''' Nў,4s`pv iw{)Sv]^O`P^G@@^GC13V> hB3M⒒Uyp>{Ųۇ>r,SGn^?]CFv=+ӧ:J)/gdcg̬ug:$ S}]i*99I6M]]c#2=9)ɒ!흝]yzܢ^|13m&:rb4T͘: Ô]WPaCP!shéكB=k&uwwk6^zd;qb;$#@@̙3TT=U.y1@ {F4wNCP0ئ̌!V?&ذʊkC ?opmK;=;Kӳ9|wGlc1^("@zhx: &cS6SߕDcE@c; X>~t@4y\n).0b]Δ`e<.ec ب<6IXL JQR\\uu󫳫3L n[.[EEA@TR\0 }^ ~+Wƺ@Ǻ@@ .t8]ܣh  b] 22 Z6=J6oެJKTZ\rQԤ6oւքtz-]8e,ӻe=JX'>+8@@ t‥?%gJZUZV.%}: IDATT׿3:::3ȗ5CTQ?P]T|뿪ݵ;f:3ȣW? _]'׫_キŢrifNWz?Izl-^v=*-7h?{$iӦ#;mvx\{j[ohƷ%IG_Z]Bu˦ ھ};eߴbµ0Qoii埻RW_ujwn/=s$)))I-TIq_ YXr(Ioh'_Fp{睧N;U􅫯ֽS8>n`@ah9c$PdCDc0}G;Ủ!4'~`}}}yY.:3uWoԬ xd{__|-U[FF$=Ьݝ:ݩސtGeãΚ-ZLwuwT`,b_G]z$Xz,o,l6u}ꟿ]B!K`8zWWަ&kҷw%+Ir UOO6nܤ'>F``@<{onVKK*==])))2 CW}=zMTj`$UPTYܕWkΝz/ҕW|nyw:O^9jƣ>Q뿥U탪ZL%ez|Z=t:߯ю;V ق{|'AZ%}֬Ygu = G?fӚիǕc E@ t8@@̬Xُ%ٙXHeYNzN0,{7ݿM@DQfV4 Gт c]`b;q@ :q{X V\y6^ݡ&)RaaecO`I.@c -Ċ䯯WQa% BhL.eJUQM,럀 M&|B;`M:Sο|/*}+k@}8Su=,gJZ䕚ѼUWa(9f&3c^}YӣQ7tfWVSO9vҺuz/R`Tdy:::^RK_^[?=/XZ9SҴhɲ?#r4oAHch񲓔PiY|G.֏j!bT|,4=kЖӊ ^'0$I|6=zf6_Wii 7}X> \{j[ohƷ5{{{3W_僁Գ<}{t]w[oӶ۵ηIvtk_7Dq:wZ|2MS--sWꫮTmzu9$%%%iѢ*).de/^t OJi*IW_ZM)5%U7z? #^z)|G%I7})Yn 7ެg>!3u3$rɑU^ ǣ-[5u^tB{m 2 C+W3$)=ݮz`"_ DX8[JJJRKk@3fQD=ḯO 2 #?a|jwI*Dq7s8-u]wkwѡP(1ל}}*/+>_q |50z{(Vy,c%s(ۢpWC4;t~ IR{Y;uS! >fnWܜ!`z,~??#N~ݮ?<~Ѣt]wO?Ϊ%ୁVӕ%ͦ`08=kgMֿAlxWv*hL~Y)nzYY$C_FJJ#)//f;6^RRd[u^QQanzͮ9 cl6u}ꟿ]B V@k@ l6^Z!5TeEΙ!뗕&ggg.Mܪ;w* iݺWmvI+ݥ^^n<KE>=[hnvUoݪng"n[_p״aûƍ''T{NLы/ͺ?Ԟ{#3g֭[UР;vyܬVUWoUzzRRRd5zl ,y߁R+|^eÒij_riZjrsr֣Qۭy/ M@{~ߪZP+?~Jۿ!/IEfI}R7nҼU?~[ WRR}LyZ\*fkTXPiG?y~J} M%}V7nPgXo.r-[auuviɒő?W3ª_uozKk~UdJ*ڵzwt_׿;wNV(t:LJR_ӧ0 dTOoLT+}u:XhZ`1/U,A4uvvvǺIs:)e^+G~2֛oNio+hnۮΣggelV:Tu/],q4wNl6q=1v6]nXZzVbKp1زuvviveOM۴Oa(5%E MԴ[|TZޚlkr<3#CpXr8PA= QMSݭ@-h0+:6e|~SR`0(ǭB{[t:zxi( iڴT 㽏>k:Q>ΩvUR8l{vIZ0qF{M6 bnwcz&cSy (Kܧ"%G@Da2 #L v]ar[6Lۭ=ϓnduIqΔaШt%E]W:::c]ΔvU\TdtH@%ű.c] :q@.zuBjkou)SS˥ @6 [XR䯯WQa% BhL.eJUQM,럀 M&|B;@@DDB;tH@DH/ L4A3tH@iʜĀޖju\ӧO^C ϗQ}Cgg+55U`6O6:$蒔!Nq0ej``@Wڴ4AZ0%O*:`R8%''OeKXPQ|!@[kL0knmlos`̝kJUAAzz{e]GdvsȻɭw'@"tGb 0vDj6ݗ ?fXSSR$I]߯[6Ms*+d;jiAD$/(;N)v*g)6p؏j_hANS9, (&{ne] @"sЧ<: چ>QcaE@KK#r0Llvr.0q- &֞FfIgOe ކF{()..:r-ǭ" @*).u @@ twq׫;R[{[K|^].X6_ahªX2&} -韀 &ҼEdʌu)SʬJUodYtH0m2aݖ:$"z!@2%BxlvO@d}tHL z!@2MS8{[yt:xTZZ&%)7'Gy2MS7hZ`U&{ LCJIIRIq:;Ҧ]wUedqTWWi*+3S`PaDs=, :$ ŽqHl6TR\$ tq$I93kwTrxL]钤̌ ڔ#IJINִTBr\ڿYsfWʕ>x~A~Ԥ"e5 *ݭ>e|;p`L :$ SsMI93g(77G]ݥ\v2sW׍_sC: #ړez Βܮ!n[==2e) (t(=z' MhI\R23Ң:*vu/_XvGeGk1HugeToPiq6̔LsAdjb|_TPv4M%''fk}DG8'%9Y9KRS#^ᰚ[+;z"x^GNlӳUoa>=[V8l(Q 6d6zp85={PGaۤn͜1#rfQ}C^v13B,qtDеrsh9s иG5P>1qvh VzZΩTRs5Y ۔1VYQv9v-^p< nmIsggizv֐>sZtmc~,F EtH@\ ADdl>fhX>~,qDKǏ&-0eeF˙v Ôl :$t[{'&)4Q: JNu~uvuƺ)rqȲ1Jc]Ǻ@@ .t8] RקtKA@+l*֥L)5;__BK'@ u4o"2c]ʔ2R՛6Y?L{GLwX%wL>^6 !A'k9i2g.0Lӌ塀z3&#p|5b[[0@`PgG%?Xw6hPOOTZR"+=[ (]#8n;ho>Uoݪ%KpLvŖ#@"]M0CmYy!m @"":$ S"fitHH&{xN@d̠:$ 4eF1Z))rTP/qNdtH@蒔OeJ𮊋lP~~Z[VrRJJKE3ZP@sێ:nvۻSUUFЄΔ;$cTUWW744Y^׿^-r\}>O٧n,+NжFmGwSUE r9JMu[f74ȝ:~WADO]Ȳ, 1FWyԮ^OuTWWr:lr9Sj%'')>>W5:@a#Yjnn$5:$׫Jn1R\\? ô0xTH㕔GRJJr{dgiiQk[4ъש;եJ\N~˩ۏ3.,Fajx85:>Y8amHWfFq'9Ӿ~:{Y? :xw"@_ : zޱUa1~LqN,f˲HsJ,#gA@>&PYi33d$"]R3Jv 7EQ(''[F%|HsJp8t(';A@Q.@ɉt 8=:Q@ D:Q@=6[o :|yO@f>Pnm\[[ۓSNgmA p,@ D:Q@=U]]HrJ4x\F~v|T*i*;3"M.͞yAU޽8{.捒FvҟHMV-*i>)bB]m&L:GOP gnuWyy2ưwY}v)bBUU CRUUM_{dѱ!up? c۠l!F"+ i,C@V ]5H =X!s:b #={^7Y] effi+ 5{_,> gPư:gDx<]ZX{,7^WڐںukDj#bF_@oM}ݺ%jmmUS =BM{cLDVr,K?ZmܸA.=#*+-k矧Y5Թy&4g{y2N@crx mܰA999gO"'\_|wO}WZZ$iر^PEߦiG;GO?JKK#Rb fI}hFa /To-_$]4?oW)STW_n^xI?2@d}gqtfA!+~tD`٪Djf%,+ҥRvV _s\?u`5p̀aw1#ԑ˾,_J}Z eB }ZE@3L:pw IDATxwtTEߦPC'J:J -4{ R+" MzI"%&$ %dKe޹3ϝ5R5M; G..!L&SB||'O3|{$d2Nr^q̙ov(ԠȌ&wwٳgO!wI~3g|k׮ccc݌FL& S2Lĸ[ժUk+W.a…G ?x7Ό xxzzFשSiþ}~oxT?~|!""⸻Q@xyzzvف(3L^nH$t\ :._۴Ԭ_*...]u߾}[ϼO2Cbt=(L&X^{][R.]udƪn&z$ohO[]>޼uպ28#A HUXQAh":s~}ꌎ/ԙ3 oБmٶM}(=խ5i dh,n.of̚2Ӹ;<=tկ/I{AVVߒ`HSՒys={ ժ={IK1 bɕ+nݺ9[}$IGݻ7o^]~ݮ2ӸۨT~LG W޼y%I7n!CϞڲ4KY!ooy{{ dR-+/kϾ}:p׭O(oʏڼuGD^UZUjѶ*UNt+2R~?I[mS||jլ}(_;_zeln_ܹsU ;$I (I?d>$ќ_E QC_1>d;ޕh>[*qTZU-_RrΥf+gN5kD,$8Hة/ƎkKnܸ';tT_nKu|5jRڷSu4$Dy[/(>.r7bݒKpP?pP2Ԧ՝~3 iĺM&}0x6mެwNڧ8GRos =`BBCUD4K*&&F._;wٳ*_<==+IT=QU"///L&}4tءFA TdI-^L}׏^J%d2iޥ"x2ݺuK-I6rmؠ+E`ݺuKyQ]QTD )s6T. gP&*[սKm.Y&~F=խ<==پޱSaNڲEFQ-5SdddKu$ZbjTN 4utV,.oצպGDzD4ֺ 5mL=^Vm׌Yif7iۥ:? ƨO~:~?@AOHb4KC?G[ޫQd2cھcUK#}"5ԧoM1S ({}եcګSOkq._u6Z* ޳ovGAV͚)nGzuS,̦Zlyri$br.IUTVe4";^A]:uԒeeթ hælٲjæMrs3Y&Zt3.!֑86ʔїI盽js Y|ٲ՝/Ç۟mO:vڥ?}|ַ_N0'ҝ+b.zIUXQEV.//PPʟ/_2w 8y狪^:愤zժʖ-K,NI/Z'[vuuz X-OK1 ޥ6J+WfMȧPdeվM4mٶMu;vv`-ȟ\.KHq(S:@KV[$ƍW]Ӑa#d4<8X9rᩬYT,[C(le2$I-+;$Kj0(ٲ|uĉd>HbWƍݤիѵkeڶ~R?Mkplfdq1dWʖ-d޺l2f؟%K͙7_;vPܹmڮ\ Щӧ>sFoo"Au{2\$i;ԩC{ :|XoV}9,<\5Uoy2e$I_.:-gUbݑQQ6&yj$/OOM~nߖ_))zujmSld<%2.IZ =~BQQ֝Tz=K?'0'M6m{oBԧo>sFiS&^t@+_j [5tH҂K}_6lK*UTDD.>dJE`fzWUddrʥwS"E$I~QOd2՗_JS*3gjS+/[-66VO=&ϝk>tI)dLmifmݶ]Z4WlҴ=I¸3R;v$ /&._!IzݫR[nKݩI϶']xqҋւEpuAŋ Qҥ~z_!iC`0asfΙ4雯U:?u}Ap@ AHp ΎG۷<7#<+9;ykaMGOqHp$t\ :.@ A<2'tM] P@ʓ+ dv 98!77w|١];K&H󊊊ԭ[r^^^ajܴq9X "q+Vϛ cg}\IWd%gϝW|| G$dҹ3\~Eya?JAsXdus%s!p|K'-^(C(M?=D;#JC )+Au 2,2rss ԰.qO:ȹ slqwȲ>usWt8'&$p02te̘9SnWTBE0eJ5>bN:Fx})k~+QJuiY6R2TRej#M7ڲek˺t둬/֋/?챻7oꅗ^r[?H.^gһ NWX19}JgN;oWڸqSu떦LQLw|#F|f]? ތ؟,-&&F#?ZOԕoI?U_|Y{uvhh4f>Ͽ 6OX4?+=}pR%l k˕hĽHZb$)::Z7nҠ?P@@_[h'CT\9i͚qvh.ߧ݊`Zr}igr9ߧK-R?p"t-[ȼ<_޼'*Z~~Zb6lh5Alr2EEԠ-Q`yyyTυ Tv-0{vĜn)h$yeK5h{ڱs٣%kħ׹sueUREcƌV'05֬]6ԍ7ZbԮm7Ny#7&&FÆ 5jh1:XRJ+jwf6L&V\ǏS-%yvm'G9st>N>i5q\|2;_|pwx9tw95jXzWZkԟ$I>l& jļEܼJVAڸ˖P*^_k#R#%\4IjٺJU:u?o߾~RjZ>q^.Zl~͛@UV]+WUuMIRj/3|J.:Hs `b?XR5&;^s;:Z+VM(x=8]ؿ_~~7`~;WӧOWh15nX7'՝_|I.۶j玿uX4ȼ}| ͘1C'jw秥KhȐ0(H qڷo]wvf៎T:uumIw0kPǎ$jgD*S:w}WիWSWG5eV5ڼ6`0(gΜ M-XRe 6\[ʯtYj6%,߽Y%tԩts%4jX=QʖWs׮do!#XrU;SklgmyfXH4j{^-ec@Fx zLL֬C[mSU%I5Լ9r䐆2T XǏkizw@խ.]8!-mTjUN2Lz^{MMwl%ܤIjڶ$ =~\CMԠ~}N_FIһӎ;4ud͝3[ŊKKe˦Z ITy._[WĉdpI҄/U֭u2nlk[>hO=<+:w骞=_Q.]eƏ֭[ϯ"""4ywzvm,XPC QjٳZt|}}wy[˖-tgNIZʛ77o.pfdd._3$I`Иq%ݹ]#IpZv&MN;ޮ>zdɒiڼ4hNɒp{]xQ}\ZժVU=qMFMaJ(}Ξ㡟hɒ7ڱmׯ:'Gg}͂5kV5_|J"kGiH4j{^-er1~s璝 =vmHxemZ?\rȑ.߾WvmΙݻiEеe4@ޒd1qv-TΝˤC3f0e͒E/Ѽ9͗ϖ+WVF|jW$*\G/^$pA~]a\W{jUgj'$$X>獋+-7Kc8MnwHhFׯg~`i'$IFLwo'W+z.ܐh9t2gϮ/ƍSOl~deɒEׯ_׬sx|UvXZ\vϨO*+kjؤZ?JH~JUOaZb.]}ƦÆQf}zD2CsfP%I\N?2\6φHڹk>ylhx'R9?k3CIi$\[۞1I(\}H ȟ$vG_ݸySQQQ_RzUKmQ=ukjI]vbcu%$$zj6iIXXy;־{2m~yۇއY<5 rww||$I7")[l6 >xoPիݻ߀z8qRFQի9`Iҹ*;9-1q1~{C)"Bɒϫ}ΪW̟wOrss)?hu)5hX_{em;ɵG\\ve>;7 = gOY]p.mR=}hܠm2e0Rlޢ*Uo=%Kmۺ-Khyr0-78{G]{ըQ]SW^~Yc}RI_)I#!L/=B{Gݴ ׭[ocJtAU(_>Mӻٲ?;v&{;T\95c}]vM[n9t95kguY]-RHd4=ߋ^NM S_ _~/][G t k իWu1ȑ#{mIԮm :Tǎ۷fZ%>ҥڶ}~ÇJK?ݧh8xPZeSlXߞ>t_5A[?Ν/֬]6mPѢ)qcնm *nzh޻OTTԷ 3]ܮm;cUW^U\~ս|9LъWk޽Ԯm[?~uA|8X'NTTTlf;gΜڽ{bbb%J׹tf:o}ksך[nc1sۧ'Nhtg{ímc?\p]rE UYͰeyRlͫAƍtAM}ʝ;֩͛f򇽏s@4=ߋmS08S ԧכzjҬn݊Tʕ[h Azoz:OAZZU]Vok֌;F}|Ըjz\VPѢE-ÔUZuU^Co['NԢխkd}2D-Z4׫*W_ӁlKӇg։e_`! IDATVO&{/k֬;fZwc%K=J!Gy3Bǎ!w)gΜ7:s"]Q*R߫kWizyzzc8XI'歛߯$ 8i$i1T:uǪVאܹ;W ͜=G{_tAխ#GB;潣%gfΞO?f-Zi/hO7m$UvD]~5kV+=㨑Y`rOլ][6nԊeKlDٙV-RJj*Zb$򇹏s~/˖L)Wp-ڽ[ժPBB#czܺuK^^^帻kݺ?թs9kL&eɖSkVRANE2hjܸC9z޻k|ޔTY>V-[iAΜ߸5kpv(܇C ':pFrc63YOr%x\JSժUp]AGֶM݌0*t8p9rpiewn4e4PX}TY8觇cg}\|?\ z1*S@@Oe~z0vGe"A0Ϡ'(!?(`r2Y8觇cg}\sLɮA_ǟw H2htegnvAةQFn~իi~@-\hW=ŝ3@:8pdvixv"AHp$t\ :.@ AHp$&[GwܵΜ=mF%$$dx)%K9MgzSNDQ\Tz2OGROfx~ Ν[ŊIJ/Gj@ۻ{Ѝ&$)66VCS6 ֶmۓV]NHmܸIYsJ)SW(O~omP;w2/2tʔɿt9};{Ϟ\y cUǟJbbbTr5yt S{` hԩӧCԪe *$I0pΛ?OUȑCjҨ7׉'Z+tB (>&Jٕb ty}ڱM5jyֺ~=BԼYV\.h1o;f~5RU``MuL&yzzJ*U#8#PXAooϗWWpv(f.:|HFIUtv(G!:|*Tp$e#ԨI3gxDa=\x\)uG :.@ AHp$t\ :.@ AHp$t\ :.@ AL^!1L?_^ڱa;wlWHT|>|X\0g z{+|*_C1s]*Tp G :.@ AHpOM&:4AOHH Ad &I a5KW2Lrss`pdsd8$Ѩ۷v*䭰pEEFݑM0 JHHO!=gΜʙ3# S)t\ :.@ AHp$t\I_6:ȧPZp)%K9 kԎhԧ҉'mn#kmظ6ۖ=2CI3NkXzFQ 6w־JsĖv{gzu${`Զ}GL&G@d2w[o'[+l"uhNl:ӈԩ?.V||F ;(2< 2j?}|:r|a<jl{gzu${\?#+_tf͞];Tɒk:LK'Bt[s۷S=5>alcR9sT^4r7mpx9 'Gh@~ɒ{A+:ʓ[u4Νl^QUPa5kgy9*:ʕ*>V57n7{ɷkoRD Nt+^JGLu;ȓ[ "9t!s 7#kd}ڙ(F/-Է߀Tc|Ҷ|wQfʓ_ǏXW^o(K,jӮz߼d2/^W׬s럔)66VCS6 ֶm-n-޾%7n^wᢪR[%yT2tʔɿt9};{s}ӘqUFVOc*S *>o#јvZ _N ﭲ+U۴nRt+WQJHHЂTf-̝O~e5s[*gMzm<6#[K-v:i#q&yd͡*k5V)yTHjۧ%N[ֵ4?ӳ]jqYכoh˖ڿ19,A֡C̆ΟgЮXj#lZ޷-_R3ǎjg^CcFԡ{XS||܋/ŋjֿcV Ѡ&ӮC'5ǃ-߃=j{U歞͛7wo~K[lռ_gk*^x׳Ͽkg%ʕڱcZ6o.Ij׶m8.ט}Ϟ W-,Ӏ4w|MyBRF8{4mJ,ͳ–z=U+kOS/]͛ko~Əc@޶Y._RZO(Z7͚&-_2mVc4泑:4i^iN$-Tݴxƚߛ7uk׮g ?Gajժ}u_-m<6#[K-j@1Q.*%}ZTj3yZ\ (019,A?sFZGnj,X4ʑ=_`uի4ioXSyQfʛ7yݮ]:i&*VΟ׵ku9-]\FŊɷX1V-[Bl;>>^O=gϮS&`0Of*S<<<+Wjίs7_^Xa%ijd+*%7}{$Ij۶($4T|J=Sz>b~aPk'qv&+ծ]K А?RjU5qc66ED qkzYU~mׯeϞ]:vPܹ]aT[ʕ-کL8J,͛\ٲڻo_hvm*(|K/+ MԦOКwP%KԽ[W]vUFQMȧP!(^\5[*e?O:>/\in:FUK[*-f9uڧLOie_ QCv)2%Ŋ`0⅋vUX*g{. hb,wr)9r)k\i] $dz*UٳۼRBBBm^'%ǏWBBj:t'VK y{" V@m$ioԹSG5iHV6׾}TL:NP^=r #)omlmi[(M3.)gӳ:GҲnZ4=qZ7-Ζ}ŧ.q9,Aϑ#ʕ-u6ؽn||ݭ.Oҙ˗Oq5L1Q7uSqё$>sfEjYt$Mo򶈏zmbo }IFzU+]|Y;wR6m?q0 kck>˞k:s7WF fo:s)bW܎u{t殮˖+::Z/Pn]e0y왿(qUVScƍo}[˥!5U`9=:sػnz%=qZ7-Ζ}%K,)2>$nGhugcccw^%ӖHR~Tʒ%mOac=v(uY|ChsRʚ5;*ҥK`0h[LmQpa'mj_ij@9rPV-ZpjԨ⾾vżu۶do޺UUJ>Gc66mlٲ𰫿7nܤŋ޽TlYɓ;M2n[M:7-_%K{eA-[4)x'M[˥!1Q_҃nUR}j=m%em~umwIe_~EE >HCݺm6U<hi_V.),,\lYKNV,X@O讷SHh"##q&+W.ڵ[o޽d2I…}$I};oVoʙ#:w\rɯT)MeΞ;uk/3S+{]P]:wRuaEEE%{@۽-o)::ZO0WBy(^\#>6)/뫲eF}/oWK/www>EDИqw>sIئ<7Mߋ,C> =~\֯05ڮ\ںI_s&szw{*V@G||:W~EVΜ95kdY+ˌZhqϐέdD}>>>:|Ξ;c!!vUR}zoe-}T\qhn04e$;Z,TfjڼΛ////sK.^*SBB돵+[l6-oTJe7kb% 9sƦƍmZʻp1up-[Λ7F ?HQQ)_"'Wuĩd0udݻO?$OO>lzyU"ETz˔ӂ,5%W2en[ ڷkaajݺ֭ZKԾ $}5 ~:w.2evkúd/6%aZ jP5o%eiڤ诮ݟRu-*=u[[\|u|1M>CUT2Zt̚q}ʍ3N|88]"ϐ-K[_vmTȻ*WW^}]A =V9bm]{IM{kOm~w6RҾ۶Y3~0\ hkS;uytR>E}5yUwܼySrrXyKF jv9YllrʫPݺuږ]>Et^-S&C2t.\)S~?y :|p_x Zp6yAǃcoQɹ$z :uIH]Xx$Hьy+K|@F'^>76CŃq ޠ$ :-OӫCq9G&O 1cƩB;gv~%ك2-|X*L:|X jذ{:|*T(P$P~z5i0&ׯ_SP2zUpvf.sY;R.y;;3I@#AL$`pvɐ@`t;VzN􇜛p5 ŋ]yjijh4jĈ:q _׻Md╆+ z&)SJ*ϴmK& z&ӧOg6 3pKcbbL2ʛ76mݻwKsTF yyy'Ў;Z[nnI5kjԨ9r\rZvyٍ7ꫯH"*TDb IDATz쩈Nti.\X|վHKu1.ˮ_^$O",X`RϞ=bŊ߶-%m`ھ}={v8RJ\6A|r͚5KNҠA]2}95J{Q``uu[CcǎѣGUV-K{uEرCwֱct'oӦ6lCG?jժ)88X7o޴SkZ`V^%Jdzz5h :tqv\K^~Uڰajժ%IjѢE2O=J ,ܹskY\ޥKuwM_|Q3gԵk%Kٳ$Wo~⎏Wݕ={v2 ӕ+W+P<==%IO?7o.Io`͟?_m۶Mkʕ+={֬YK*UJR1ݸqz)զM4 &s=$$DFќZjժѣV[wb"k~2yRˍF-u2D}GQm7 %! 5zK( EڰϑRĂAA+XRPDz)x^:$$&f%͖&xu<33{gggѽޫeJvڥ-ZnǎjܸUYfJHHp~{d6u233//~~f@bt y{NԎdgg,;GPP$鯿Rvvstg}͙3G_~6m$Ij޼UY/ߖ^a6%]C^h>E$t= Đnݺ.}b .h۶mjҤ]]j׮RJin=m4 Hr[# |\ݜ/կ_<_[pbzs(h땲vRaaOl2iܹz6لs8Fg"@I`/Uڋ%Uƻ+e@OMM-Pp^2%3X{&WRK/3W bÝܝ#ŪJE]azpPEm޸APԥlڸAAv_ =԰aL>&I+Xbh= &_AڳgVZԔ. r* R NHv֭[t2d!VW|W VppFu)6 ԰aâ.F4.0:@@ `^V۫뼝of3P"XVf-kKgeZ#qVUEYgŋ^[WzժU,zsUxllVHHUë= @\%wq `t 0:@@Wz^szb襗'kJ*U Kz+'YYY䓙ڹsW\++WN"zHX~Zﯾ7ܤ UԮcgmݺ-״QuW JJNֳMTThR5;KOO׃G(FmU 3gm?wjAYO=d!ZfM6^͛5U8eddHujڱ}Ԯclb?iS_1/!t1~6nXD=4~PsPOڧ_V؞kN\➗o117t=wK:EbmӷooE˗׮]U^+C2t\gI|||휯ϕ[镩3>Lgx=_:vx嗢XlN(%%UǎS5e()9Y?^))4y=T=Qh-:{oׂ$]9DJRSӴ{t7x=5EԨ두xtYj~\2en]j4vj}K-Z4wNQ>uwJhvo$ig_YV /v&T54\=hmܟ/\&&T9Uyժ͍*TY'hGy(J5mT%:վuԾ2gLQXDM=pS{W QN]if9SOA&ʒ$;wNM/X8YYYzpEԬFM#TN֬Yx-odeei؈YG"i̸EXk;{#WŁ~ cOLj*^C 1*s5D髯H}ィx::S_~PQqu;;'#FFQ[FԙKշwl7b811Zx9L֘q㕐AhɒpႤKi…8$);;[w{ƍ[6]\?Æk߾x>CEFFf򤗴cuYzbu"mSOc:sNO-czz“EQ̚5Kz^|^_-[*w_>sFvr4~r<L&M|q$)WO>Xm Aso|رck2G5ZxjT(j%w<*9Ϩ\=[K};wґ#G|史Zm۷+55Uzj~GEYGPWs[\ݎ 3G}(u1_6_$= :OQbzvmUNm-[Vrʗn8;^Y#IÇ_nɊZjz{4F iԪeK5oL<<+<<\sRrDg-/ٺs=*[>=L&/WYYYYaY,͘1C>>>9rK˹m`eQTݺ7vj֬ݻ^(5S47պU+UPAcbr}Lz>x ]ߦ*Wg͚/I:u,vUzܹӥNv8WdgDmaoq0d uQdd>U-゚T;9?9;3wƛoUҥ {s="5-M~~cڵI jG,o<9|Kv%wyFRsqJLLRRR$iɒet)ٻ\ǎTb˰^јuj}osu;;'чҢ?蝷TxxШZn>qFMlܤACnWz  ]xQWZ`$iMAAyݯ''bQ͜'"s'G=4`EVmwܥn>qF TL^ܩV,A&'IkdX`" ؿ5&ھ\]5Yzηڼe=z^zZhƎ+Iz e9T9{ՁM&|}/}||4ٚ,%'WVm4uN)W\fGlxW+x6W\x i\J O.SjZwPΝKW'Nh-;Yg5gqN;;'gG콏 ɔc8:Yve>qDYvWU^*/k'UzzէO"XՋ6_v3"W.uCy,s:0Mԣ8 5JE{QƍչsBYwddI区u#e2r=~j;ΚL&C O>@>?QYNoop'؍#poWF J?4Ra#%R)8XCv,Ҽ rMsW7^Tou~[tY1 ¨[s Tz^4Y5""BWۯh:{qre;3g$сӨGFk-:{kŶi<O)k.7v~ZVeE?3hގ15oV=b{nW=>^ԄUFtuyZpnVUԬekaWX&N|NO>-kժU5tB];oM{8ը]G{B;ק).6Vܮz Ul/fE@ .֗_|f.ɫ[Q5j׽7[c̙iã=…˙u/OPN] *So5C㒤צCioznA1=z)~ë}W8/xKժUS+~ΛS|)(g0jo2ԯo_wl/=uqƬ3W+q.W9 }O~u;NwuBk;xWۏ;n>qt**ڿe6GUJN5ڵkԭk3iuѥyJ"o݅ X~it;%>גu-,{?A*N5VZl:vvFSjF_ ,Fq=ű݊- gvRaaNdҼsӭ7̟*VVؿUyKMM$P]vskEߺvsڮdbc{c1[II~azMbmo_~YV}bUBE255hP_ի~#ZrѷG۹vsmڭp\|-vD~cXbJU5Dj޽z- v\;' `t 0:@@ `t *Wbʫ^Ο?_eMQYfJ[M~pXV\ud6nKqu Uw=ۿV"5r=WNz$Zx MMYUBԠQ-[n?^O?4Sp룭۶9sW67|PeEkǟ|*>p9OZAUB35gT?Gb&g#ǟh FMK4vsBêE5bCQ~mU˶ZгOTff)m_^Ջ6_vc+Oyc̾M=7Œ{mYvWU^*/vƕӗ9+Q;:VWpǫqFy1QM﮴=sO~J*&?;ϟ?;vQt[\l쯔>}zʶ@a3t@Ç)$4D whmיtMkԈBBBg^:tHI ,F=2Zlٳg=>^ -tdwhnXNj_|Ci_ٹ*UJoERaaa7l358l6l@իGIO#""T/*J/OyU+UmCkqJJJDۘ9|i= Wҥ$]noA\9۾ԸkϞ҂ TS@rcF;wM=ə}rjR~ZhWuQպؼ/To5V,]Ѻk'WNJ;+T=:q.SC\=&8jŕ:o rT=G?Ӆ8˙cemqV\b{ԄQ zR4}T;w._X r5ԏV闟uIU>Uj5kZ~p.^W>t]w+4n.m$]xQ7m}ǫqjܨ|kːt 4(_nG58ÙL&W)))|llO?~\}viWzԴIuhcO1--M}1[-Z]ȨZp3g [m_^ޘڷkU^|,:csh4ђM=}r~k.7v~ZVeEno+uؼ/ߙUWTh͝7?,+vruxs?qB;uQJLN֏˗L2vwਦwwJϞ8y\Tc'qu?r~G1]X9vffe AgA(%%vvg TQOtk78r*U ʛ3 ۷**nQp+V?Tۛn,r`@)0\QQ"jw)U=ye?,Rǎ<\ rJK;0'>34o\t *;܄sؓ}<)?JB\~s+lGD@7?B=v\{K}#G(33KL}M W݈{c,u9!PRRXfXbJU5Dj޽z-Ҁkʲ+ԩKz5mZ/Pݺ:^e`6l#ϣ;NNxt`t 0:@@z@Z^^|Հn6 jl6{m~^[le=+*L&oZX,:{V/^zЫVTeefכkffBBzm^  *(;@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `~ne2y m.ysziS)GNZ(Qʗ/.}ʂI@@ `.:UKiE]JPreU T:n {,C(ʊ6uQ ޹K;`TIdQVu)ʖMH*NHA7na3?Fvrz.qP"dgE6KhFvu MxV+/ I{o׼p;!--MVvKZZ6yB-t+t%AkKL&V*'b%|N@P\^5LX r# V+{^(;wԷͪV-L-F?H^Z,XevX,׫BCԦUK=4jN8k>qqTJeIt%(3F~}YM~z9s#p6}!!`ZNE{c&;n>}IK4^ԘcTF WLѸqj_BVf%$hK/i-ܹx2ͲZ;Q-YT3?D{1cTV-YVt1[FUz'sZC?$k!׃(Z;w9lhăô}6 1+m%Jk~AY,廬vmo-7S@޵K֕$[ڷ$3t>S?vLeʔѬYb2jRӭ$Zx~IFÏf}vPVlj*$$CC_jK:tN-BN{97P+0L&9zTBNq&ڴi(33K.^zoW$pAbQ˖-u$5nd{b\ ȗM{ q͹l0'NJHH?~\7mkShV Qo ˸^\e5k~;ճW:v蠥?,RR>UVO/VVc PQt>'WiefꀂogZؗ-[V5k3gg.5li=ݧ6ߠjiȑzu"ݽ+9J <|1:rh]V>Aխ[W2d }yNڒv]5M͚5TT)m'b`txZ$oSi'z^SݨzjۮK)W.ã*E[EVJ {bծCG}:svڣ;w>_|UC~eNH+Nѣǜ]4x 7^IIڵ*!!Qt߽1cyVeeZ۲hM:{ǶבP&Mԫ{[%$$h͚5zdHmIDATK25dKpșz9:|p*:ꑇ$ <%͛7_׻;Wեs'~yPm^͚]VC^ޜ&)Oթ'|Rd˓^T\\^ըUSw [;m>5zmVԧ.>|||tf~1x||_Ԥi,!뀁nX/N|HwTQU-Rԥ+>>>Zt7LZO,vu:@uzpk3EΝ"(9=L{Y 47İz+Wq8)Z8^E;E] `t X`4vE : : : : : : e;L^.IENDB`cppcheck-2.7/man/images/gui-newproject.png000066400000000000000000001051601417746362400206530ustar00rootroot00000000000000PNG  IHDR$sBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTUNI{tE#bW,ĵ v* ei*)*E@j(% RhL!cd23g39Sf;;{~x6**D"qA """""""jjZP(J/\p'\PǯXɓ'KTJT*T*U&""""""D"D"bŪU?pF bŊs=rJZvDDDDDDD7%Z'E )׭[wN'ݻwEmm$""""""H޽{/$%%mi퀈nU[tXi`nU "M$ѭL*8[;""""""[ZLЉlt"""""""0A'""~ y { ~ gֶ*2^&DDd4_P(P8|$z|*NJ3ק7ޘ**++obC(.)Atd$蚐\ǢCf ,ZBD0stxxGO.+Wa?qebԈHMMD9s}zG(Ys9n˯?`ѭ>#Jƌ9/@RO?HGrn]떭xG+WAaC㗵3羈#GmJKp񈉎B>}nyW>Gb1p1SSvl꣏@Q[\$z׮d@pq]OVyo~0s&uэ#"<INs'C ]\R <W=й}ojUط?c\DDtÅ>s3.s 3O>xz^/͛o'''zFuE#ZsSp81w| Gn>s6~\o{O&ɤS4~yIL<|%={%3}i'%Я/zv﮵z\}2\]@or ;!.6k֭GΥKL079ož}سoD"Æ ƆM<󒚖g}8`xf&AU޾]/$8}$=9}oK"4$/0ȶ`gjtng|eNDDzuG`?\d2DGEa@~htT`Aq?T}$$]`XC/֬߀574*r*2fЀ:[\]5[ N[ð!XwߍpQ֣'<=5,󒚖nR1QŸzrSJ 0ͷR1|P899nR|9/JaozC^ oFf,Z hb.20BѨ޶d(6S 4_./ 1{3;ݏ?;!ugdG 蘛e^PTT*=M>Vu^~;;;e &7WWb!""VPXfL5YYxn,F_ :H#0n,AgQUUȈfG\9/?ݺtiV矘4a8xjptp%ʼ4/Cu4Jt`u7Ҿ{ HͲ5w'"ۜI} `_~mVLЉյm={`xw6B$lv|Td$=KcxիxÆ?v`3ѯoTTT;%,EZzj5YK$!+;_/]76\z* ~ukcGc8x0FGӐ-#>h1q8T|ɧ_]ԭKK^_whHQTWc Xa#&w^mIIMELt4>}E}/Դ45d"""# |ǏéXz5B5o6'MDZF:'&B*Bu} +/|eeevF aC7mBjZZ]ϝ5~~5(((D*EdD"#"tn6m={jn23bذVE,4/}uL&CffZ &$t6P"A$pttGRĢ%5vȆs~F[ٽ<=÷_ }DDtK65VSS=6I۳[Ҧ-͋\.#ucF3DDʯ_,J+]ܻ1tH$煈ژQ*,*’o^-[@[ߖy!"">&DDԦNŽ]ѡ}{} 3f␗?؁yCkuUUUXr#,,ᴚ-[Ńoplٵiz(Dܺ?brwViVct-H$kGTt d_| ѣͷƞ={!KѩSU+W{p]xرSS5kN2GbuƦmo?|'ի\\[i++*O? !a1c1Q6k D"\Z\iI)z_  |)j'k׭;ڵԀf Ů~SܛH, 0 <޽{a޽U'%bذfa d..899Y;,VeV̟*FȈ~m+۬^svv9bCi5YH>sJJKͱ??_MHčbrttl󘈈n&'gWUWc֭ػoz5aÇ ?!f̜sSiF̟0JE-NjK$X}==zbO@V#/?S<.dc?1j(j[ @T|y}I>ǦBRA _L/?cm 5y|ѻwo;NÏ>W8x))x7੧ĉQZRsg6kC1l\+X3:t'0vDFDŋłW~m1&c ls-++SYs梴Ls\.ǻ-@>Ӯ=&NING/0 n^wm @6|@w*,,_`0Ν;y ]UUYsC.H{Q֒A3tcLu| CDDt,F=ycʛQC-8d(B#ѵmx㭷*kס\ȘA\|G|x Fu7"cq[>رsQm-[KBgL?K ?C7s@g`٫B#ѭvwfdxsLS:DDDנgffB,B,Y悉OӞ_{v#,46n܄}CЯ__0Q絹ӟ\m+vx㭷4{ ؁%_}Gci  sfO^v9{wZTTT 77O̝3 `*1{ 1 /Xy*5*746w|ǘ1kv$TW\];~GLwJʵQ}RXX~S&Ouz <`YSƼT*%1<߷s3fBRԖlٺ Æ=-[6*7 cd瀮.mku;UH=wo:^uƣ1m-%q :`Ѡ@ؿ Y59 ?@bb"JJJQQQ&mhkO}5ĸqc1`x䑇136ms.8Xӎ9P*ٳ ?n^v5P#Bݝ#5* j} {fVMC3^4:)JCb5Ͻ|vXr9u v[oިK/coıDz ucذn tR`;Kkۥۥp!-qϔKήUSTT6ի4[b05d`ԈӻfXܼ *{}ܹ󈎊2)Ncoэgo9A`κDXXjѩs.@-iN*Xr:tqu7|A^SyM5P*-n˘MCKljc8q,I'bz*}m? Dq*?*A@޽ڬ2=n0'B~~> z}]tT6sv9G󸐖)۷;Sc5sE(Jt.CcV:v耟WĶ_n6ͮO,c:"j+oF KDx85ǎǫ^ktP_YC\̫ˍnRm!77;wFuu5v_JC`M0Fy4\p <: $t펈XA59Ncۜ99 ""$$w^ 7WD1*J}M `䈑زy~;{m>*+Lo[[ן 8x*[x 1{5;;;:xȴq?gLƎ%.Box8??F_hSwxbuw;ٙqrN`Mdךc/ m[֭+} :|qR(Zד1E$aٷ%qE8/`YC1-Kٲmе;:u{6wCP0F9`K.a섉۷֮'Yq3ɍ0DDD-aݻw#77._ҠgΞEvv6RRSWGի<$I3Ln[{ƤI1k9{زe(//իWqk^;~e|G O>8=;GEUU%>76nh`\;vC2MAxG..!!J8zTM""gϝ٦J6}3ɤ$,] kLJ`\s._z\dDXMqCck-Q((`tr2ڷkgV1" E _JeL}%CyFg_y5 cd ] }v8xAAx񩈎KXƌ)sn-QKY|>C `{ѭG[=>>9?u/?]"," 7nOVj= kmC|%9+"bf:}_,BPH(C!5- vzq}FSo_L<Nsߔ1)xq:9e|ї8'x2 ? ^|e}2 8m۷<==1a8>|}}pE~׮1m49 ""$f]`!30h<<<0lj %՟U[j..DMM b֛oh3GǨǠݻuk`oootySƥ:oy:˚2}Д8,mmsfΝ-L}I88:йS'c5f9ԼW^YPw;v„VۗF&`54hz5?F>X,G.lb\U^^Lf9N:t§KC巺;C볥nρUwڡ5cWǚZnp4j $"" :( ;* 2lUoGdi;w]bʽѱCkCDDDDD['ZjHvt9|EDDDDDmOɢT*T*^o s\8ݸ8wqv'""j '4/'b̳ѻT*d\ Mz)}Gb={j'[\%5sKC5&S[nkIh&;~FZݦѭ<λ 0lz ŌT\Hũssc߾nK~3fQϘ>B?@0ilwTUU5:FRaӦ_1qxTVVbٲqtrccn0jdVrli[{>-!..] $VI$iuc.Ϭ:]]]q1#B4ϿAUUo *+Ӱc..1\L02 i4Fkk>T*[,\ QY֭8-Ӧ 1tkCDDDD76=~n=a#<ӟJn_B{}߁Gih ~cP:tQ~8r$ C[+{Ab]D"AuuzӍEzfC#Rͱ555xu|/_ q'NcM0ZiӞ0t`lظ17mF[µ֣Kpv@DT,^Hسw/$N&b8,jc]m}7&J<4!4< 3gi^>3>|sH6[ӰMZM_]kAZ6vVIӯjku+c<홧qA:uZ8D&JNE_ȡ{ {'}V.e59.wƱCѭ %9s_Ͽ/C3

\ -P(Z6c^_x<2cϟ;1jUoؾW_>\|Jkؾ7̳UؾWI'hx ;'6AA Œc-",,^zww7 6nFɤH$ر1G={iiZ¢BT* <~ n_#&WWWoiMbO?co>o9fj=֔uՐZƺ#>Mi?Z振kMÆELt4'H$Xn[WGn8v#""""2UoAAAj=^,#!3RRRJ~}jAs^'~}Ûegf(}1AZ)3Q1`{'Lr?rWW^O/%<==0x@lP}æMڵ "#>:* ƎAC0sr~H"&s/BãRjlt(J౦ccǎիOslGCk)cԵ`;uFjj[WW^7QKpwqW(Ś;j0-[NM₃o075@Iaj*PSY (j*g Bu qfDUXHKOG.ݱÏj;+;w߷!B:zmKզ>6[su1t`;ͧrC} ͦ kP(VVߺ2vvv n].dItjtѱC5Ød;;;:cQQQg y~yѸt26ǎDŽt!F_7m˾k@EEm f} sNC\l,\%~SZWn(9z+Z?-]jѦ~> ͦ 1k|s|C5558u:t7Z]> ߘa!""""2' yy&N>>'O& ?'O&aizٳg3SqqqOųgر㨪ɓIظi3R7^<7sRPQQ{ineLڵCuu532=텁w܁gBBBgDGk}B~Fn^ pY8;;...Ǐ[O!-=yyxw|劦\,Ñ#ۮ&N9sqYTVVbm2t Y}Z\|d-T0624B4t<l۾]ӯ'ZC٘>LJB8烈HOЯ]E~w ]]ˆ{4t> }=BпzCLxdd\K_[֕>WVV6NDDDD!]ѽϸ;wa ɭ/0_/YcFI{mo߂; h,k֮[t=͢ ...Zgii)b1[ޖk+/)&'5򇈈H+Au0t`_bgo&jZuז0qE "ɣ%r&JsŹLΉbBSPTpuqmv\1}4,6kfTWWv(DDDDt3,۶|<:i۳gwo͍ ,kl֍hw*m{7Y;""""q; `NDDDDDDd&DDDDDDD6 : `NDDDDDDd,3kQTT>D||C1d#/7ڡ||}aX~:9JtTDDDDDD6Bz*ϠCNO''CP` #_%EEE8x'"""""9]=&++(.)H np"8j-ͪk.oډV/ʆBBN !#<cY,=^LЉڒ-"A=) 9W\T";d2 T*D" 3 :n"&DDDDDD7Cgbb{AA3f7\R ZH$JEqn:nq'"""""I8K ܂* Wr.U-fQtoKUMDDDDDdBaa,Y} d. yT*5Ad37 jxyyY %^8x{n&l6F7OOO9-t&fS3왳x$'@~^!""""" >>B|Š;ԩ X;<бCak;tdDDDDDDdN:Z;jBd :M`NDDDDDDd&DDDDDDD6 : `NDDDDDDd&DDDDDDD6 : `NDDDDDDd$(-+35..ֽ""""""""]KKckRR ̬DG"9x(b#juTӘxybg]:"+|}`fq7Ra'B&sFhH\d2qj (sxI[BT\JKJag'EBx\g՘΁!эb :,S*D&'`J(((lF PT_PPP(J\ gg'&XiQ\R0899A^[¢"Tj1Y9(.-EBViV+P^^n !J!Ucn:7,|6rsѫgw @uu *** P(H達 C|iY䵵X,=Z1:ΰ;::Mϗ9VO.ptt}{8nH*K{3PXT n#:v0JBZEAx7b߰jYu嵵9;!"<.(ߚ_ZdΈ LlT}gJZ:;;;֥QJR%n..((*BX89:c|;d\DYywkΣ9514ԵnEVIгr.AP &*b6H$$ tըBdx(i}[ݍ<<{;;̹x5:c >HMˀ::R??>ސɜ5 d\DqI&8Z > 'cc2.\ltpAa!}_ P[s)7*kn%q#%-U $:ƷkVaޔ W ],&&E]l4pU^:..EŸ|*8`i..2ķEƅLdf嘝/#s6-V곹kf&[A@Td8j5o݉cS JJЩC{ K AAaQIKqIGGGHfDZ<KZ[U+%ʦ2@E! !$sHXt̄y:5ss |sFN?5SG%GxxcVɁ]{Tqܽ5O2p`ʡ;cQ'FMxmߡJdJTT'5T7Sߡ摑 u"4U.Tee*+tP5j$ݦFI+pJ0^xqs~W2}]j:5*WE>kM7Vl+Ce0S^Ѯ?+7n}z@yay<8zWWaaa wuTwӿA m0  >\6u:hNIi6i#Ww?ĴlI nvccKRm{9R}ZlVqo:y{ya %IʻmR:kkv)wV9RpŶ>T5J+CJv~*+p+xԴiwY[vho٬bc[y[1-w_?|XaY&ܱb8zsdf+5IR%OkoParxy>QN]ݪ*5o>R~G7Q&MԱC;5mڴ^UN7MSn8sO78o5bd2;^g*Tu4dȯtժ5~ӎD#x<:=2MSJ}z(:o6fϚ>Ų-Zhug?sIaU J(sI:RY t_zu=F{ ܊;av Pr8b(LvQMO;@{:x'%ХLiqvڣ*GQ#ܣli4mDya5k]1k:A@ :A@ ¬۪ޒ~- 7[ [BbeLyα,ҀD]hZtY:JUX$.,,L$=؟Ե{"cԩKw$I}|V\at^43{_.N%e˼m3gVQ4$IJGO?W?~Sb*P7 ->|X ?_|~d*}){ڲuңDRU!m\OXXzrr6oP~im2MSEtӍ7h{-lϔ$5nXKڷfeOSxD:>3tI "qIjӺ~߹k~q Xek|%iSkv} x>izg}5c9sUW]ƍ+;;F{YY0Am۵WN5yGQY)gbj׶sw% èS6M2/հKY5iEFFf8WX8̝Ç)""BC ќstWy'MK֭ӌR,ZT]9[﫸Ӻugִ'~\ڽ[[m;kUUUzTvH99TDD Ѝ7ߪf[0}{hժU1ldEL2M/*~N P={нwlAY(`}hz@?o>/Lqq|zozaQջo:t9YYzoJ^FZ ::*Wyr8oO>*//⥋eT^~<jwe-˦_w5kNnĴ?i>e{֭iúݻsS\ږ> Fեkz(yst8VTT/RYsg`w{?hlUTTsNlZfI yot բE Ǎ~I-_!ƌ)S*7;*WvHgmx 6w=gL-tMO4jej8;o>% {NmZ/.T͙;WI^YyDk֮U~~~>[Y'`Yq}f>Y3?Tii7o~BIgnkW{ ?g_Y'{[=Ž!b;^wt/9N).0]N`eNe @q8ugd6I@Lܜ\9_IMMզM*** t9 TjJe @+5%@:A@ XBfK%%%r!6.N%''[v :.4@Ҡ[\Xҙ!X}UW~U\߲ #bnL3U4Ln۲ HA!ȔueY=BR}ը=k9Bi2dXBi~2 WxDwO?9M|ry<Mo.4xTUU`Z@G<ĉt5lu2JMMU4mZb/h%&&͛k-T\?^(y!? *,,LcF>ZguImo_TK4m=v} + =Riie'I4.[N3OWLLK}h~Ӕ3?ҴiOh+ѠV!@( *?92i5^.p$gzGj}oV? ǰ;ϗ4e*+= )Syx tE=6Ą]1v zӢ$`:X_K`~:p)FOLSiH:kɦFFcnj dx<ʚ+Ǝ}=A4N@tbou<1M dR)<<\V}s>N)ӹS'l6\F+W*9a:xޝ>C1bxVQ#@zݻoǏSȑJEF6-7߬{Сڰq[PtloA7Y6nҁ6nܨ;n{|fʼ4Sx1ed Utt jUO  4N>+*#ƛnVu 7j㦍'3|ygyJÇekSϔT-[.Y6mk3nn3Sb’4\sђ%K9bT' )##mg#ejv.И1c~ͦٳf)chzgwAxߋ7Uk_;Y/: S#|t0LlvVr%.0t:-BPNK={&fI g0 A@Jjj6mڬoׯWQQQiNNRSR,BP^.' ] Џ!@@ tfK*t] Zp(99ٲk}d%ǣvo˕kB:G5$@q>[زG>+ @@ ta#̻uM㮼ZWs_+RKgS{k=z'} *,A4q߾M;֭5WCԩ_֮]U_X-Z7.*,ztt%Im44=]IIIw9bj]uEԽ[7y[, =}6f(uJjQmwwLԁ%I}^}u͚=G-r$)>!IK-$ŵI_{^V GHQǟԪI9kWdt:v)>$IPJ>zٿZBW@zӦM5`ڲoܫ_իm4eTI?_yIƍum/5J$rMz7w>y<3x PFz$Iڶ$vx_}mN)`}߾oXޱz%]{x Ði?1#Ghv.;k/_ M7\N;U@9rD+WR HwO8*Owv>|fu^4=̳>6+p~sU+<<\+WzfiX楚5GoUVVfI=^ZZǽ{_)S5G}F-[$EEE鷷ݪ;'ݣkש\6lܬy>lWUU?Pۭ"*22R2 C7|fΚZ8 /]I:mN-/Oq<l~sm~s6l8y:SEEViйdeVXX*++zmY-V+-YLcƎUgW_23f4{,e MuD@ t @@V.`1+G֮^2YzNeYsINN˕ϖ-Vr>*6VNS={Zv $@w @@ t,YXFFi6\*))5d8 IDAT\qqr(9ٺwa#FymrJ/NcE ߺ5ʱLqSR\ieӫo*.o߅t1n[*&mYߖ_:l}O>+>!IsN?E6"iu>3̻5Vrjo:0 y<|}L>AĄ}rIRyy.TwuwAǧ=VвşT8+S W/$CgW3Oז[5g#j$I妛oj}jDo/}dQ[EE//$%'ԃS$ڽ[Yۼkt^y}g#tŸm;fr{n+ihzsozM񞊋Kx PFz$IVn׌wߩ^V9s(66Vsѭz-'&U#Gh2 x<߯)wl!IܵwagcH\F۪~fzY^FTYY*++k]s(8RyD]:w__n^w=I9iǎڸq^y%MnP˖-]V)gr,|*ޱG:IҁnUI~R2UU:mKJLT֭kvWjȐxQvWB6nghƻ(/?_IS<[DVM3g?|]uUjܸk Զ]{uY'ժ=Л&wYy1""%UOU?.]fiڵlСõrժd+>Wmuםս[7h}4? NfiX楚5GoUVVz>w 2DsΩ>i]5ct͟Ivެ4U+պ-/OeeeZsmٺU(uAooڽ[~~]o_tŸCilo{TT~{ۭs=Zv˵aF͚W[W^~>3ݚ_Ï?z㕛]wkmgJ n)''Wa[5sz ,Y={j*6L2M ԢEUVZ&??\=z޻s,4]^yq0%LΝ;%I[oi? 6*9>{qڴQԩkw͜5[m<l~sm~s6lX'ʫQZ TSm^m_o[C箚[aaak-/^$::*Wyr8oO>*//⥋eTjwe-[Z_ce3vkK?]-YD#F y8fϞ].Ӭۭg3QZ|I*?r* ͸T~ܣ͛{#Z 8 zfk,36kY^j lL]TT/RYsg`w{?hlUTTsN녭Y{}ݦi=c|@׷f/X-Zh/?nXOl 94fhM:UߩP?G:kc9segnA'@h2}͝Q.Sa5wA}ܬ,I^sjӺ~q%jܹJJL#Zvkٚ:ag?p9n/|ތݞϖyۢ5k*--qym%icl gy럭=7Uk_ϓ-V^tA1{Lq;:N Ôfa.A2 SNӲk 8Գglv$[K r PnNtjӦvztt*5%Ųk p{ t @@ !h˥ ]J'GL-BfKUUU|qzKiP֭F.WRR LqSR\ieӫo*.o߅t1n[*&mYtE$C@dJ,힀!jԞ4A2!4MnÆ+Yi?i̙iڴ'hPT} `L}~}ᇚ?/K PԳG=#57~cXBPuK2nʔ|y<pS??APtx0<'?R~#~eAtn??Tm;ufӚ5kO:t=A83!(Ջ;:V3zLxCڷ׊ˏvz\\nvޣҥ߾Adyҭ{mn\I^sjӺ~q%jܹJJLq#GPll+ 1q*+{XtE~~Zg(TttfPj޼Io󾏏U+O:j6A@RdddP%YtExzG@}naw`w1Ž_r:2 S6]an0L9NˮA@p8RϞɲml.)ș2LC9rM6UTTr)өԔˮA@ԫWjK . t @@ ;.JJJ.(t) Bl\11JNNt1].UUUi.AY\9JI&3BLIqɫNxeF ĸnfhne} ЃB)f{:Q{s:&d=Ȱ; 4e&-uk]qڹsgkN'Nmڶuta]{B@g;֢(Ij۶|IBj*5l@Y:~х<Lԁ5aDQIm Tr6 zgOV6|l٪yD=z&+>ozTJj/)}hV\oܸ+t-Ԩ)jԨkUO0 +Vgt)ǣ^>233+4uq^} }՗ڶmN}ۇѦ͛ //о} p8p~_zԂ )裏)Ґ/%fjqZ0[2MSa(++KcǍU=t@`f7IO=E6Wh:11[oJݻvi=Ą%&$hҝwj5ʫ4 ԭK]>2uEW۵%ֵ6nܤbϫ~r:zԻyzW$ԈUQQ˗K6hϞ=jWO=~p:cz΄;n}ݫZ>AS>2ej;$I=SQc*(/IJHH~\{BbTѶ<.sM8p\.LjK5w< 9YY:4]-cZjks=k:zRbB;V C=miRT,ٯ%UzDe?8a#'\G)4$ǵdS#{#ch2<eec־ A'@:i:}_ZtLTU9c\|ܩl6V\U+yL׼γ<2u͵שgJ-TK,R657nnƎSco` [Z_aest.?ے%K9bT ,S͞=[Ga~a^@cƌ96fϚU /:"T}!|gv^wLqS뎀%)0eYɽv Ôt1C9.,&蒂)4++ڴi]^EEE.Ap:r8JMItAz@@ t@rD@ dˮA@RUU_Ruk˕kB:S ĔoZۯ[waBiv[7B = !l6K'@Hgm<'@h2MFЃ @2M3`[SxD Zߙ6+1!~pm:A!(OV$Myp.pb5hZfMkBPCi5j={v'k~_1R%%%t94Up_uŕ:T;uԳO?͚iG3\5X$B)9e7b4$IFGӏ{^]K[m P> #§BiӦj}HQUUUv|TA{~_ʒa5ϙ>}zWg|zEE6nܤ:;ԅ.&jmIӦ={kkԵhb]_fiOIDFz8N@dy;v>}T^+55Eg}_ Rw~54CE^8_MDvFfZذ3޽>|Xu^F<1bcw޾p``utE .N:׎xp :Ys a{$1p. KNSaf0@ v)i5brr]3Y6M-%9Si(7'W:WRSSif}~]Nt:p:b5zJ t 8=:A@ VqRII.A#&Fɖ]!f˥* 8=Х4(|#+G))քt@)).Vߴ2yշ_:- t o:"z!@2%Bz]lvO@T}_5jxN@dVqdOaĻuEW\yfϯJtAV׉'(?omիx*=]~uCLqDtT%IIIIJOOWRR&O_#WTTT+ n@ȲY:a]&nEcRR{)6.NC3rJo=ZSlt &סc'͘LTD=8hb39EܠF |\3I4i /P[l{`}GzהriK3}v.7oެ~A/a2MS~ yLӔі[5u2dƏ<Lӷ[ulkӺ#Joo?p葇R_LS#Gжmy˓LS fF.7׻oAj٢FԠTbBni޽|Ok 7)GYPP62ej[^ E L4p@};2:^UOQ?XGZC\EWc]6jqWy6#G|D@PS?RQU|]J)4$IjɦFFϗljӦMrE`>]N#8Nt@1O/>q|Mq2MS;ufʕjʕJNJLLqS{-C1k|> lJڻwL;4|ʫzO-dR˖1t}Cz׵qF[ޚwﮤ$=GiJڥzYߞ\qt_}G+1!&㥗ե[wuCv`>S>,S\{zjٲOt"i=fF]6Rn[2333/վ}4rĈ{ZVWXtƌk{,Y]db sU nW3f4{,e MuLqzk*95<*c{1{Lq;:N Ôfg%Z2 SNӲk 8Գglv$[K r PnNt' A(55U6mַ׫(4NSS))]~t X^.'@@ (toc8f) ]T`[vծ6$zUP]Ehhiwm)hitED# VwA3~s3t|sm67逾fQ74[\hY ~aÆujݙ DZI> :>םA IDATTI K7i ԪU+[Y(%3Cl]Z):$@#sWedffS;]J풌n&YN[4cp7VM~w(wk_().Q]dx9x{W;sK42p8qMi9ՏkUX/Ȱ>%՞K9/uIA~@@?׍albٜC@7DHwAX,9?9j8Αi\t~fЯC@vq7ܽʕiགྷ[OXZګjzMO3eYŊW͂եs'=52AӦ]ފԴvڝ :j0:IIZJM2EӧOٳguVw=\M =X1+;[dž ̳9on]>Wc|;RR4r-~5Ek] '%>HN֊+}6W{^{ŇY\[6M[*((H>xIrats֜l=!3n+rGZ^Nj.^mL{x99j4mzF ]ڼys츑S'!Jݖ1\t~aT{tw]]lXKNVNa;v2OOYίջwo dјGtkڷ/Mc!c8|r妍ںe8 &^=[ic<;S]Fbz|``=IeeFYYZnwF&$hDi˕><[AޝdQpppn,DFo$g^1mO^wa2^t~j"44T6M9jҬv));4cW*..ɓt37KRy,亁$Iy9*>~LǏDq,)du΋Kԩg*)moڶm[1[ȣw*<"B :yJKKݵt~=j℧5ɭ/7E EFFATd|V-[JMMMxxn&m۶'ɲ&7D-Z@.;7\En1=ds_ԯߠ1:zeB,:Q}Oqqӳ,]46Ub Vff>b?Qnn#3v22ɓifPza4F_}SN֮{aŵ׫*-={5 }]XzoӵqFw^+lK}pou씩zy|egg{~gf"=7{֬y_}V^z޿PCCսG8v[.7`ZEքu($fT\\?=0E롡k}'OxCIInWUw;yvcz%Uxv/ԾC + ʿ7ݯv9}Wwh_\YRjݮ}>wWyA*+c\Q]gնg9܇] Wg.od :> h;3 YIENDB`cppcheck-2.7/man/images/gui-results.png000066400000000000000000003163261417746362400202040ustar00rootroot00000000000000PNG  IHDR|MsBIT|dtEXtSoftwaregnome-screenshot> IDATxux߬]I ZܭBO]vگN[~Nj@{p'!!$ɆPZו v}fvvQ(M;toy9**`+B!B!4MfSRRNz뭯ܹs-p3g|nlQUUUjB!B!pNCӡvmO@̙35~UU4j+B!B'ihoӦMl\\\ܹs7+;w{ݺu_XV/B!B\CF֭[{&GEEE_퀄B!BQ|^w!B!l6Nv B!B!2 B!BqmRtW;!B!B\Y !B!8IB!Bk$B!B!5pBǰX,|;s۶o'qz= Կ?#g`Zh8_}͗_}xE c`4a9&{|mx/;sҸQc<==j_~QU՗^nÇYZj&-=NK?͞<ÇkHKO컙tB!_!ǜ:}q s7:]Yݯlfמ=]nfz@Ff&n%˖1yң(Jݞo?{}Z'Ͽģp8);wG^^Sǻ֯BkW>.~ӚUXmZ_ۯ~~~d=uC!edfb*bcb{UJ\]]ҩ{ౣFrI=C ;f^rNQC V^Cٳ;H?w_'Ea(BQq1~ lfӱ};]TM_fƍ~@%ij`"||n,<=p}?~=ǟzmۙ6m=SPPcGno^9!R"6e<:y >0iSMN&I:W!Ɲ8qn]:_4lEKg쨑Z~ ! 8GƫokoݚASTTyӿ__V]}OoӚ{Cpq;ox ߯͜/cFKl\F۶4h4bXҥS'ڴ Mx៯51>z4"_'մikV;z6;w1nh"4!A(Vɨl7oח51;j$lJGa&TUeЀWuj"צ[kPߦ<R)(B4`Է|\Iw.#Zm,6 @^X|#͛zZU**qq_3hܨ޳wnjɓl޺222j<6@|4i܈gRm7'OU}xW{=}*=<{c.zNg}]6_M9ƷiMX~33cu޵+AA]R3nn]NauhrrܵYBqu |0=4q%Bk\xXΈFד~GзWZ0hԨ8/s嗹V.-=_|:vNn./1<<=9~eve7wU˖5ˋhzqAE{ (/{m|/M[еs'&nS8+_ʜϊBh0{h$ɿB\F [a#_F77W\\L[O д=9"/o/ƏM޽l׸a#x]\\_7f4>>lץS'&ʩ<]mUy~(:z_O>%;'omF e+V(e-Ƀ͵rϊB#;'G‰'cO:ţiU*ݝ!ɿB\6x0 ,3q%Bp7l$..Fr1 헟 ④G:m:![ 7|1 ПWФѽx{3nKEn]Y~Ͼ2׵jI~~>i?ku&s]wb.-e_o=ƍ]KzI͛ѩc6n?_{Ȧ7t:wz_OFЎO>c)h ݁O5 q1F~F6oʐA1U{N>+BQ?d϶GaԷΘ䧟hrr+ɿB >o%qۇ;MЩC*]8yV$<o>* m]l=O?MdӦ,]/>>۷V1ҋ|ٰi۶'ҨaCP5 xlo//t;wyM|;oVWvEW_Ͽ`æMl޺ؘW2Z9Ak&&2w+B!ol!BTQT>`u[n=EEE _z ! !\>  8eկBg!ɿBڻo+VUYdvfgRSٹ{7QDG!eBg"cB!Bk !B!8IB!Bk$B!B!5N!B!'ɿB!Bq3B!B! d1!B! 2_B!Bq-cB!B!fiW;!B!B\92ۿB!Bq_!B!I/B!B\$B!B!q !B!8n?8삟V*?'Oݬ% -"A&~}}ذq1nnq׽ծ{bS4l7 0z=uoq7oWnx7j}? !B!n{8ggݻa29xЕ阆R~,Z&IO!EkZn3ϽXm7r>7m?8=\V>݋ WYp&ѤI膛/8i\ОǞxʲ%/s.+v(koqc1 ,[?h( #iۡ WtͷIhߙ #hݶ}exǫ,[j5ͯK`ϞXd)pʜ!YK^|z@Èh ]B!GN'%ddd8~kć'~/a$;K?MLuOy-۶W_2hԨaq<wJnfׯFΒe˰l@YrpF^FC fm:DZZ:+VB4Ovv6_ٱ_AJ^=j]%9%7ow<$TU~};GN;3Ͻc3nh,Zbu,<ƎI||r3?<468r]pO-*%gxXf w#lB!>Yxj'5,/a{S #<<т{Ώm.2{ί?mtDx:x0lٺ #G8¨&5]|,c)),]oGnnG$9 ??ˊ;-- 8|*JJJߐAt֕0ne99dddеKg"6͍Ç͡Ç/(kȠl6VYbeE3v/dƴ=Ua&6ua1\߷Qk3yB!BT1Sx֩}*foӮcuRnӾ]z)Y~ك ӣ{7.^B|D6mz5]Pڷk˲+y(.[=B^^>+Vyf,[o .ZL@? BC ! \ÇVOxx8kY ;ٱs7`Z/ݝC3y 85kW=SU'}0,^0nCI222x7֛xyy9_{p}ӲEO!B!XɎ;)5ocSϰkJKKٷ -@Fs/̑#IL*/4$#II%XJ@yi}]+ݮ6רY85l;Ah0ޝ6aC\v.ׇݻUY>f( YaC &q֭@fVMAz9Q#K$,AD^[. :JWpX̬,yŗpuseԨ{!B!D}b=˷de]_ ]OӘq׽ۿ^ʪnry˺O]ҵgZ't .:>t ܽ?8jƨ9{65$x5]#EQ6t0YY .`ddd0>/XC1UG6mJB|u>{أs]гOoڱ{&KKKg1.R=g.K-'81A Ԡo=TmCu皑ŀAHhߙ,mnhB!B%73~fH+**m\4[6]}kAt|0}=B!JC&3IosM'B!Bqm6s֏3j!B!$Cm7?k1U)6KIa[vJ޷ !B!*6cB!B+/B!B\$B!B!qB!B! RZ,cB!BkAYv B!B!d̿B!Bq3I!B!Zf_!Ǿ'//G-[e:||2v(B!5'(8_?5`b*̕_!.Ӿ(.*""" N+TjT[vmʺM;8K\"^ڣJW!⏑u 6???Oŵ=z~ncӦv7FD@|=,Y4b;@fqˮ{Qٕg`/cujy2AOǥiVղ)ߦ+lcڜ팬c򟕕ż0 7"/7SiiT B.]TNmV sMx0^Vyk֮c[bE4h\"3i-zCٱG[qtYW6oϏǏ3k9ؘKVphf}${3q6WMP5:l]#J#k{]['wfMG`g>ΗmZJ _Y qqsEgBSwn+Rj?j_vʹ,SŦR,%+ Ȱ*ԦR,]c&aX***/ҒSNͬi׉.}偫Rr9rWnJLU !ۿi<<34 g„5U9.0$ԹlYiA1{si@O>Ǿ4;?==] vS)3gf-%II ބr zw+5A6mȳZY5nظንW1q(6MElX,zJ9]} n:ZxA4 fpB}ZUZrL!>e9\ 1I<CE*`"4PX\bl6׸X-V 7?jS pI~UƦ<܋PcQUf t؉sf1vXob#.G!-&[wн[WVK_+A@DD'EС0+KNLz$G4b$@.f_@TT&?M&Ic1KSb]}5UێqI>5uL QY)w^ G].d3ޟFӈ5ëC FVs-XȮ>ɔɄxe{)k?H Gr`JZ]}=d'gh\\| j3UAQ4͊ Ch8+4\"~Z7ɩ3xI>Yz6&Gݦ ' yCe"]$=qww*/8A\d+y2gNČ{޽1Ǐ3fL#.`ʓ ޔKLJCbv{Y&:{$bhvrMHL~A뽦jh֌p0`@˫_s;mO3oX zz:s)ܞ— ݘ=v&7kNL{H<'%ٍE-i4װ!bc;}4?6;y-K8ŗ |}iߺ54o^P_b!55VViUղΩmAaa:}q~~iF r漸4h@TdU8x*Gsӷ%e;9te:!7 '!.MUg(5t@q:˖G8dZS\\hޜl _2;zD`Yt>9Sm'ubr3}myp} 3ڴiCN4EQ\f&!UM>[6yc8}4o,*ffmlF7X[5fhӀ`O6;iE,|y>iOg,Z:8XYO?c=7[`/(DʿT USJK%͝},2j\=/Gaf&sDKm-؎ F9K$(j>fՊYTNw1c[!ZʈK}Ϡh~\NwBC#w)(2f4PMthŦ-x~BvT]u7te0:etޮSm%%CoэIy4 u`P zA0bk9 ?oY]uhƎ;8piiiиqc뇧g{iƺuhӦ vԩ9͛xo zDGGW)\'B!j5kyW=Rc*cE?HѦuk, ..[6џXeX_:͆jTjج9?+%俢B~s&z.x871`%|nnz&L@Uˀj :ڵ -?]CG{Nԭ%\\\$i*6{RTTLRRnӵKYnłjW РWeW?F&vvm/ߚ@ʙl8{&ӫCmcCI6athѠnWqKGHKOw$c9Q5@`` f8r$UVӯ_-[_$I<o`O\?Vo>wl"vΝT \\\hٲGR}<ěoC^|{mdG7rrw}\/Qڅ&gC<o?;ڝ'pu12k,C7slb ÕƁz=lw.5;WdZϫq>g+MzG-[P *~_|2"]h١#O#pĈz9g޽ލBф tLH`onn_^ADw͒Nj.bifPKQUT{)z/;a>;]vt䝙|g JPqD5)*5[*,tBH7 vbH&N膢3Hڶ +9BET4 fѳu?wnnndEQ+eOt1WxZuUh!χsUng?'/p)((`Ϟ=|'z뭄֪lΝ; 1wVΝ޽{e5vZN8wY!uQm߽[j_[J ￑ti1q*O OnҥyD50k]^YrZB=oOF[I*%Z[QL}WS'BCCr#^|AA,]2vоq;fu.ٵ򮳗GS߻;Iӯoo:vhϚukIM=Kp"g/(("Ik??_Eao(,,[tۓI*!qqMq47)v7Ң//҆L{}S:X/ "{g̨'PV ,.,;XeFѫgw\\\X$UU[xǹjUVN`)dh_a->=ۻ-Z.ne\ rygyy{H/sϴl:=0it}l&q~EӯS cWW#.F4`d>Ef~ }E2a@+ϫc|P9]ҵn#1s&M#"^PP^iRW}Rq1n4#cLm"Mp0~< ܽ@77{*̧4wPsek/Eѹً@iج]]LRJv`;fBoo\y BS5 zޞnK^n7HDP\ɾC+ʓ2%첮btC32JAb$WPYRtE[9y&GOMPOnA!ޮt ťcPEjcix`A^ ~Nɓ'8q"^^eİd.\]wUjx≺ TJ)B7CuY-j1n叐+oWe񕏭i!G w.k.2&|ټu6:vo v!1F77VX+ ju^XʪU8: ׀0GXP|vN:U+P5'%\qi\+FE6eС$'#44q`BB~ nٰi3+V;vС]Z]bQiQx9ü{` ~ow#'MQͮ95dE"ǣt:vӮm:v`mJpqqauUQ\\B``d^ΔuM e"KLӄC3ػhMGG^`w_-t,ܹ3_ߏ< x晧h =N'}Am+^$3_ko9ͮ9ohc40 lwƑ:<]f7v,㛲')]t ^f-qؓ%f*J-9*,g iҘ5k/:y̔&N$!! V.X@QtЁ܂oǼX٪5ϠXK T+΄T7T+hfTk1(f4͆Z,vkJ>(-Sm_]ٯ:"7 ޭ(*PRblYͦ#nzI&Old%%B!n~o㺆qC>߱Y&FX!dAD7DٌK6:ʜVT .5?쨦dgѢ?6Uxxyb0| `_2h89Y~=ݺus$EwL>SNAII SN塇"$$'NW_+eǏOTT65kpAJJJ cU/Cs\fe3g$)) W^yz!f3K.%)) Mh֬ĭwԩS[X|9O$$$УGBuj[%[ج*0#&S>e]=Baxg((H =#*[ƩRt:LRK5\ڱ91{}%QV=]*EEgYmӚ!C&WD&_հk1FOPXXXvIsrxjo+n_8uۻ6[b}KQpcH:Du6x]x;7lG= u!juuUdžfZGNU&GoNC:&Za=G@IY 8}r8lhr[I&$|1`]8̪/-:NBUIX7ߢIa|0},fJ?>F3PW$ 8r*Wֱ -*Z\\y`dm:W-'pws`ա8yѽr&b{Ay=u.b>^jpaRS޳'ډz+^^^xyy( F?ЩS'y܃_xݺtz5Nj/lmeIf͖͊(iv Ш 6S͕%5 '3sY%dfRXTdEt#Hsqlj'OeOh Ȯ5 EGsM$4S 5ӱ 콕CCQ+QESWՈ;ѵgO2Mxy8=Xzx-_"699G4/7nM&v;aÆTR*?3FѣG_i˖-lٲ:((Gy9|0'Ov #ҥ ,ݠAs+O!F۬bȬ̙>4j@7\תQQܵIHIf4hfd.aͮw@)f9fHKC۶YkCSPn z] /FU5J,v,v_W#6;.~mW\|_}%0hU[1|vʺߟiVsLzXNcʖO?Q%PvΦtY۷oϙ3*¸VSj@`/zIVr]=o@lMzz$Z96ǾΖWM{SIS'йiT̊(v~t4iҞ[gO}Ցf#˯.0{:Ě飹enxypww+u38uz z1th vEF#f(`4PW1Tp{kiiyS*}m-/2w5϶!:6}j*=RCQD䝩NKAP hvK&v{՞^fܹ\_zǂvt'bw>79WN 3ť*fFZZ^u6 ȸXYC{%:^9ˡm!~Vt^|,?4agadJَѠC M'1q3&Zֆ3䷸%%%4j|Dvv69^L.xzE~|3fp,L ~j~o^UbLuO!>~4C7ÉšCߠ_{ LLӃo%'c9<x&_SR3FNyikP5U6 WtR ?erRw߁Qyǿ37KAS~kA*!NwzPK d&KBn !xϱeyyv>3O) N!x 8S Okm4cL( Ij)9aDd${ IDATF g]IM8p< fiv٨ .Y i!i*p5,&#NƎFT%uMC)qVo^fVYK^^,Zũ"=QTFay]>ѵΜ qbrmj:H]h۩;[~+[i f/d2exk:kFj)^h5^Ƚca$%9Yf1{l bcNi<',߶`P1^0"ο6%a2P hjN>J&ѕ.`BVLIn6ܸqk::guttE9a9iim%=رrhhۿ( I}]u~ ynI5L(9ݾEQM圽=\d_=;KFFSL/{ԫW@e+XVx̙K!(߫?&v{gK|B~O-Nvf.jϣ=Mn^︕vY/:'V-qťwp|PF<F2=d;}؋6ϾdP8Gx5:ҥOgZСwUt#tC?3a0LFL&hrq >#QghȂбE=v]uChh(\ҭ}%%Ln]4?I9k^-+]ױoqtzӟvrqHu)tWť}[wa5WW`IIL~}0*222 z2A0~ >MPUV]@''XpRj>!y9ɄPFkPZwʒ/Ѧk]Vx }̈́FD1K?qpY\ymQCS5+7lWYe=2UU8y" c]:zgfa`dDTƌq4Ĭ} &s8eUULRRzY7 ѳ TE':zǑ=۷/+]talXgDD"%322%*ĉ%&+4|pڷoѣGYlY T"#""0 9rRs!UQjU@lY 2L MVv6@d ȓm>άþ Vҫg`4t{pFR "U\7]K+̬iРi9]+)'Zp?&&țotѼ^c_"+V/1N;rQvyyۿvQDF֭X6ͿMfHJN 3Gw{4%8P{OnDz>q{':=l=NB)J`Riddd$+; C|\))ٻoI&;x?+m۴jq FgHIMBF-`6iNjŒboMDģ_Rw 7Me{t2Iw~x .\{̞=Yfq*D/tko[1;߀h ֫!&܎*,]GWw6F.j˼+jdPeE]׉l7͛cг|83?oHb!j%5I+7mZgv6bc1{x2d,Y_UNֿEHv5ki9B"x5?EלhZn#z8bO0-"n"&An UQص8[bdKPUl'|WɄd -#\RrhotcocX0ZFT'bѲxF35 f@5AU3~8 @xh8&';U~Otݻ7o "11|V+. Mff&X,"##ٴiqqq:uM6n碋.bҥL8Pp8 +¶mСCl6s1ZlhppI222x>Z?gntȺa^2mDz8~r!S^X&+MX"`"ɣOsZRb 0g.N1zn_þ0:έؒDX-FVңC& ž|w` RU-װ_sǚwAUŨ=4߅^f)=ص!6ƎYSN&'rWУG֭[Ǽyzy4oޜE8q")))k{mرswߑQb;ʐ!Cpblذ?_֭ _g…x^FA֭xgOIII)3N˱cHKB!.dN:w^\'SycwfW1!Ln~gVx׹0uH>fi}ߓ5z #N7Èj(1] N6Temi[ҤIFԬ 0(2nX/33˅rrW} S_kۖwQLFƍ˼e2п_>wEQsѮ~8N NӯsC.iȆ)w)SH:M8b"Bǭ t= n޼m~:سw/k7hPd@+8[MӰd{3k5gn:a1M8!}Qe7#G6f<;w%VX$#.8 nz}=fϞͽwM 8p;\כ'o^M{N`*׸.ڞp ˉd*.tYyw(.M""0իoFhd޳'hmt1wLݛ 9Q㛭2o"ܱ'ЧP6I'50nv7VmQ~OWу=J3f g&lܸqY{:3`ƌfs\v]vk҇z\{mձcG:v<\qhh(S ưaJ,h42mڴr B&1"]t{ŗyd oQUpw<$42 0?ޘKB|MFs4NNԠoSPw(LvWy{0a`'>'I! f ++ ś8 <^<) P`=R{ v;q{ywfр( 5iBw?[.Bز D".x^\.+ db' hElbRT\.'99y8IG|1ch4I1k6ZGqJNtiۖu._=p'iGCvwgňZQՂQ3b 5Bc'V4c&:Ml\οW䁱]bƧh`ʠ^tW 肪@='G5ؘ غ ;W\u8xg@22tr*ߎ9-K=ٳo;ŶijK|Oʣ(.!BTM7лW/.geyɹ0qlS "ca gA򯢸v,z#E4ܓEۋA)}22) ^[!%?ԋsoLSk g+c  q$&$r2ܹ3;wOW#$;w:]׋UU-1L<4ī8vD޽ie(QzvʫʩScet.KӇ3a{Wt˯]wClL<1Ux^[EϞ txGYna\l,\֛ܼ< -x<36j3@9z;==Qԏu HOOt2h n7NboDQaw-}r{}=&<ǟ}]qx0o׎?6nS|xƠ6>1vϧ}N3O),Hq:63t3sH'p: ==^/Z4'==vҢy 4h\ t l!",.:siA ;(|͞8}"4b/ b&Nƚ>`Ϟ(#,38&SPeߟ~#- FcӶ3 GE ,o4:Bhܰ!V їnb^+Ҷ!}4Sx&l #ض??Tf_ӎ BGi FLhh(y *qc9,7Oo%+ѡ#ābPDpeGYI+?NpEXNhˤKN`oCh F(xN4ywN LbFHڅEX-5s*zY{ >Md};R2SCQ k`ZRҎS e=ҰaÀ>"//ܼn0LlVT{H*:^ FU0+mOo-.=>t{S@1}3u`Ӡ?#+/%'+D. {nӾ}[{:f3-w; #mZhr:P Ɍbb`X;BArq:=L""# `,ej(v""9LxXQ71Aq56it,VeFT%Dfjaوp`0>/e~@^n6+wiBLD9/:3}a`M0%rOOOtnX5;RXd/oS1(^-B5=gV*I$$Y'Tԋ208xx=x0jҜ&f߹j4|C :濌/7ֽ4l%//ˍFtTU)Hׄf **JrFrs7v, OXp9 RZp2{;( ,+[Gй YL﬐ T݆fb]*7Xii[,5ln'++̌bðUh#LXxDG>A]v{0m4i؟U '˅%: F #.5di*A![<8pO<$I)ǜH *sG)/iq=8撓CnN.6jv;v{Vں{3=9ЫgSR*.0c=wq=n<W70 &L&#&P6AIF %7'lzXf6v{6Պj< X4Mvn@||tah".h2b6Z}kLEb~xr"6 7gVDD6̣Ml"''Mӱ٬p8p`۱ر~.ݍjb6:R]+hUɿYr:&wfUK( fDAT8]Dr 8L&LF#Ʉ^*:a0io=xŌAU08. 7HgӳЂF,?eMXBzJ(twb)9쨴F0vp^5 IDAToP7aa$&&ҶMknW'lV,KP*BQw_M0j2Zj^TUն ШQC5* k Tr 6fP=j`-8ؘjkQ?MDZ( ˦UP'\xZ]x'`0TC]4=ӯh܈ KkWA0EtLtmgA! 7XBFLpY F#v]zIeBQ=֭]Mxx O!ADXIfݨkj$|R1PիWb;vV|]VkBH: *Ǝy˿RԡVMK[u|/ ?pn^o !DM>t뇏a]tAPQSOnwLIV4lҜۦAFfFqI ABuE#]v]yu\ 7\l>*|uڵk:|$1ۿ_oNxiXNKz+nXzM[r+T}MxW7p01񉴻˾.2=嶩wШi lͽ( BNg>zP!ƌcƍ9ӡc3dV^],_4iil۾ݿ˗csp!EDt-۴+W 6Rorl6_['2j8vÏ:P!wMMVjfn*L,_1KQUCWaE^{^/vq:Mw^Pqt~}5C ;'AByss`xܾ{E&Mu|[lߺ0l8X-;sW]Ů[1|xZV&Rowle6^yEV.'NУW?v">xޚ6K.C a7 I:rgz{{]%뮽Vu $U|-Zᣊ_GxR^}O~DzK~{V$HPbϯ*~[3Itы+}߯=z$N_ټy3ǒ?vt<\FAƍPUD>=j$^?wE2y9)zƍM_?뮽4?~T~9/{mLR heO>՗_k.1t`HO?/=%ݺ3Sǎ[U?yShAa ЯvU/튎L=hղǎE3N7jȰaChղ%l}zY&l&NGXh(=cF: FYxǺs#3 ݷ6;4 ]׋+EU g/^Ǵk׎u+rq|5ºn3|P~oaCGDD;uuM+. //G5_WݻzҹdͻNu:tMԱck׮=hF=קO/mQ޷}Oz5k/5f1 6z%So&M:TޮJ+DŽDԫ3YjV֭煗^f ddfK߾h.u!D]( e zȵ ȿ:@X2BRQ}յ[W>=~<ӌ52K'CsϲhkZP\Yy*z'On|'_~&] RugiHv}{(YBws} )UAre巬U?țoΧ{< ~WmZ2vEvvl|Geq̙;qtѻguu!Dݑo)Rm{"j_2Ҋ2y_4u7jlf57अHբy3,]p]5r0N/`9"z<ԛhoe|tqP},ʰ{Y >go޴aWBwsYl߶N:y(šk۷UVӳg*ߵ>;zvmڜ_6dÆ apÍ7s7aۋwT؊]E1WXI n(Jk^BQM:\54]G ~+ꚿ^YbE@>_Pke]) a 98\ݵ}lc#yl޲K $Tgfq{?bǹk!Ø5{6C &,<<})%_E_c=v=& ÏR/1ڞ2!ĉbvwyU!f<0avEn^~}y PҶс(Lf<}eؽw/\sU}l޲L^xe6oʭ\uJkܸ;/.ߟ|_rd*iرs'v\"U [YXgqq_~]ΉHNI)7ڬ%Hp~t|i%*O gdC|G}̝FH-زekqI AB5»Z~M w'; tDNL ㋽^ᾥթEѣFC}:un tɗ]Jrr2]vo9,7xm {-ҏk[K/WБK;1 i?JׁA@r}YuZߟk~E ڭ%Hp>\a*׬GϞ5!nk.ZlYY 4k5ӢyIW0p\' E.;mVۿNVۿ﹦&SU+VУ%_1BQ蜳5ѹS'Z4kVsS+;BBqs' I._#!Dt{Rո| .XR˫_:.2?B )S!O?ߺiYyo=^^u=#Թwaׅ'3 !yAQ*1i&L9p}\˫_:.2?gB.XI!x P]/qsܓ2BpŊ5!Q))ɵ:W]Ͽ89uI !.4j4/kMڤfj@zmHL"B!DVsĄuê퇸cΆW?6Shղ |g,B!*5OjE4^?hhdڴiAŧk|L&deeAFFdeet:}r?k+/o+[hԨ!.]€q8B+99.墋:׿^Y0/AWh`̇ѱEw߽}tN8΢E߰dbܹUYpك0޻? *w9TZxw>.zC9sfh7ҋX޽{1ed>qsӸ[^M~/B!DmRe˖lb[,^y:t\f "J_Ze*~:,'SN1B*l+%K=jtq7Фq6ŋW~ q=}R/S&O!9) %wOHbB"7F~xzT+?< Jڬ6yy||g,fZLH'>A]wӡ}b># < . Rϸ۾zB!W1̝;|ۍdbΜ94i$74h{?N3؏yԩ#nyy&N8СC= ۱>Jk}?ظ忳ݧOo֚\< ,?{eʔ)X@0^^z2edf̸o.]:oy*Vw@ٵkWFCg,-&/B!.l2_;w.?馛ԩSz>N\\Ev"##+x#ѷ{pKЯ__Bx\QU_|:;vlb_yq'%_c\{5t1x}BC^8bo8;>EX*\ӟB!BԎ*u/SN\r%όbnc !$$Ahh(6j%iNsΒmrBa1T:.miӺMW6Шa#LFS3f옱\~l6W/:lټ{xe*bI6iumn:ZjpYg*B!jkW]~EVV¿tͭ`e""?a<<''7Wξѱmlc|.}v%c谡ǥ?JjZ*~ 1+|?bʿ^C3bMm6},|Ơ3iۿo}Sangύ7ȭʘcp{Uʓ=N 8t?+v11޳cIط̇ض}Yٶ}p}eռyם,Y?>GfֿB!НWԫW;\(r']v\qtڍ9pQg' b,]>p\z2 'Яo??OdW~Eq}Q NNj:r-}]Bøyɧ+7yo#;+; :,y7{m[1h z)Zj`;|p:d({90dnfz___fy6māg,B!8 ^hF~GڶmdFQ4MCQ$`2g}V R,oVm璶MlTUnOΝyg1bD?뮵;0gΆB!eZMנ`"|M+|:zsMM* +VGW˄!,,={va6=A\\.'WV#$$xtL'B!:oڶdI&++ln7^j%""A|\< m!B!w4m6;6rj!BQ fu al/K!B!(TB!B!DtwKs|d:f4TtCvB!Bo=Wa0a04NŤFB!D5Xj%A%=PB;so-a6 j6~ d!b%'}#V݊uİpB4MgŶ_ldggW9*ϺwM4 [`ωxޟK:E۷sӤIBCC2dGx^/3gΤ^z8 Ǝ;?C=DfͰX,4hЀ &vZ+-C( KL6 EQ?~+""ŋX̠eA`/w|S߇ 2l0GU>3g0o޼Ru~#GOdd$ݺuᣏ>*5E9|X\mGd2Ѻu2^N:8^z%̬Ty]>tMWux LDD:t[nĉŶ+<ҸiѢ 4RPΝ;]p(3߻w/6oǏɼyhѢEʥXl IDAT?<<,[{seASM6lNhtbA1(j:&@A= Gk:GtGNZelٲ_|GpTkz̙Ð!Cj;<"D.C/0?S?̡C޽;#F 55տ͋/țooۉgɵqHԩSY`/[nO>m۶8N6-+ ̙3ZR̛7Z-aaAQj;p!z~e/8y<&Lc=ƤIXd +V`ƌ`4;m49R,\z饕8ڲկ_ۙ>}:vk@WTϞ=d{=>OfΜ9 ,, \yOXlO?}MӸ+x'kX|9>,tҥDۦ,׏0;O\\\ <8:?-Z3mڴb7:w̍7<34nܸXSLAZZZ;j`0Z޽{KoxS[ o$_•+Wggg|H<<}gpss?᜾ի1tPL4 _}A0TK,޽{vZlݺؿ?~wD" qխ`߾}?+VOUwPQ 'P)2V@ȆZU *U)kP*0lP5T"kkkrJ HII'N~?B4$˖-Ð!C J1rH%!C~~~ZzVTT`pssCnreבWWWaPoHnDD||| }Ś5kSQQ8իWNԩS~z$&&8J%V^ ___888`8w\MNN3<dff6Ak1yb'/_@w BHHVǏG:kUUUptt4+H$5***s۷'ptt$˜L-[`ĈJѣGMN=ۃ\.GXXwwww aA*z˧Ϗ5Q.\0Z}>K_u,^Vz-bؽ{w:C&!**  T*E`` ӱb x{{ -IR߇?:2?䓘;w.,,,pM6aZD"i|^u7o<==yfܼy_|m[Sݐj ƍ|ra__H̙3Fٳs1g$ +|8w.\ 6O>&E߾}i&d裏aܻwOիWիmۦ%锓b )xu.T͇TP) -FUe}T$VNT*}qaΝ;Izb„ 0c޽?x'1n8nLD"͛aÆ!$$DP+Å 7nh dMAEENDFFn:pk6lX,G:ÈD@@H,]{Ş={p-;Z{8j̝;vZMf¼y󐕕$n61g߿JRo>̚5 b|cQ gϞŀ`kkaÆٳ\v wA@@pnҥHKK /~ .-[hgSh4HOO͛quW_PP{_gϞE\\~xzzj_~="##5iCkze,|Pa4hASZKʐ#G̳>UUU ;\[Pȑ#8}4 fRc~xp|۶mغu+,X_~C6^lʟ8DGGW^ƍzQd0`I+nݺ hWxǢE|r'ʤ˗cرҥ /^ll 4{>c=fل RlݺHOOS0Ǐsy%g,>Br@NgNP}qMw.TWnqq1bJJJ.hZ㉈yݻw7IZ&WWWڱcpse*..l@999Bx\\ud8q" 6r9QDD $)::&5Saa!+WN|8-]T8edžf3Gzz:4 Duu5ѡC6ZLiGᔒBoߦדT*Z`>}hdccC5ܹs'yyy m۶ѠAڮ y&pBvI>>>f_C%&>SnذpppX"zOѐZ"JI*e-*jFN yɫ++ʨ*ʊ9hҵs>W+Ҡ2*G,PՖlϻZE>"K젬տIFFx7^}.^BVV>soq5\z={ĀZev}h CX{̄Fٖ3x`/8<fΪ%K'Nٳѯ_Fq?<<<= [7 7Lί%V0Zsvmtav`?|=@& :t(jjj?xNH$ݻw4.\6cl$sժUZ6Vc„ &6{ٺ~}СD׮]2_h4o $'' ׾O듟hZC,--!J/b/ya| g}&:›oe˖믿l"ݻwŋ1i$8pvvvFRR4rW 兛7ojswwއ߳g$܌ GZZ$I͛7cŊ m]7mڄ;w`Z糳1c !!C=ZM]tɿBeF[ac?uW;T4Dְ +APP+]C@x͒URT~KKFۙ-[z K, .](lݺ.]jNVV3''Go_ՊsY.̩cF8&MѣG{nZJ_!HpA8pg GBQ[?K/aΝ߿VXMs{6zCT?МړPo c0gcŸ~:|}}uE;톴E0b$&&_~zjGСCj˗cӦM~\]~jZK6oެucƍ۷/ƌcTfPP;u?Xx1HVgaРAP>IIIƎ; rJh4lܸE Xf 󉉉Dxx87:_.֠.,mzq82XX{Bb +X8vT6.P~\\\0w\[@uu5eRɍvLKKCvvy*SdcPPPB,YZ7"[X`.\/8pz֬Ytݻר\J0,]+V@pp0BCCR=7 '' ok׮!''7n܀3BCCpBQQQHMMڅ!<㥗^Blll1yj|w(((@QQ Æmxm̙xw5qG7|CII rss www` ŬYɰĬY|r\|YX?6S@sjOC׮]/bɒ%vr9>,ȮĘ1csN\t 7n@tt4{=,X@͚[c>˔PG?L>pvv̙3ju899ᣏ>BDDrA[nEEEi-[Ӯ] ???X Gn#$$* ۶mԩS1c _ .QTߐaSnjSTT]v={āo>aٞ6j5 [n KKK?~\k֭[τ/gggDEE!&&F0X{2իbccѳgO=ꫯ0qD :;v?݄Ys`g1ZTWWcذaƭ[|%돝t>PG?㏘1cF#_メC ˆb;v ᩧ´ipI8pcǎJ駟Mзnj1t3nҹ!&&dZԕÆ Ï?'NGxx8oߎÇ7߄H$Bjjj/Qzz:zCa޽@TTۇQF![!..֭ג_~:˯O.,--gi=7n""" Fee% b= WZgE.`imORPT?` 'a >U1Db[pb[%ΰ Q-Wý28u;Ű39 ԤpFLJGYYttT+_ +8 R{VLU %66CeF&NN]p)%4 `'r<؅S {7Q]Y[Z!HAbi .#!u}7XZZ] Aver:V`0 鴓VVV gx`0 Щ'3EEE gt|X?\' `0S`0 `0 /`0 `0N#PUt je!w`0 `0J+k /WX\ <TaΞ3:) `ΟEEEYi1lӭ`0kp밴v Ģ*"d8T7V6;?8p<6l؀[niILL _S$y`t mco)j5ۇaÆ8ڵKgbԨQdx$9u޽a-Tq8u`ܸq8qVףa_wm{Ԩ(o_so0ӧ~Pwƶ/@PPos="t]7={HMLDؾ};Ǝ '''_GAAAm o9_ۓN;רp%pb88!砛׳V:"\h\rE|\\&O Tڢ1|Vс`mtJKK_bҥpwwԩSxWb puu!͒SGYY}];Eґyu<쳭oþ(`ohgΜMЫW/0y/ŋqE$''G-G}ںi4-?]7u3fHM-:Zһ`egx=]^4 p@Ր:YT_T6EJ:hֻwoxyy|.CكӧO#55ݻw 0'Nŋh">}ǵhME_[`>)+0Nn#RdB*J eTRSȡTAMa8 k^[[+WNNN DJJθ...`Ο?bL2Ũ,,[ C T*ȑ#q-ن!C~~~ZzVTT`pssCnreבWWW[Ψ܈w 5k4K"pW qO:%8۩SbHLLq(J^pppѣqܹFgy666ld֬cF󈍍amm OOO,_6ǏG=t.3^%JKKq L>]8gaaI&5iy˗_>3ydL&Ö-[0bHRѣ&מA.#,, ݻw;MGsSck)X FKҜM}BCCcA*t_~xw̙3ǂ 憤$ӧ! գS[yƍX|08DFF"%%gΜi8P"~[hH}5Ę K/i{w0qDit64F֦SN5-չPއ6{P)B,U QSZy8Q Ç;w ""& &$YwT<7nL 7&["`HOOǰa"+Å 7nh5xSlPQQS" F[aڵ 6@,c-.>H'tRݻ{[0vXhV1w\DDDڵk:k:f4"̚5 CVV%Ĝ9s~(JAƾ}0k,FGuO>6KXh/_H^sh4HOO͛quW_FjOٳ/OOFyTUU!??_8 [l>XS|esl`Ks7ꫯc̙7Lao4h^/oE>}/5d;C7]?g?ROma皚ܸqCY2  @ZZҷcD]2}5X[4TfÇqa60mޜu(-#c#a,/\^IK(/}>ϧнkk/нS)'Y2n_xn&{ӝc)3y>'[\\Lbtd2ڿx""y<==iMVՕva4\);;PNNGݺu3Y~&NHÆ #\NDdT.QBBb&+++:Ii TXXHʕ+¹'Owdd$KJJH, uGNK.9::jٱ!Ynj摞N(--Mgx6Q]]Mvvvt!""%'''֯+SzgW^_kC&___ܹm64hQLB;wн{7Ԟ"Ŕ6l:]xyyy;Q\SL&RRp...&)..{g]v ~5 CX{̄Fٖj*|M-˖diԗ?њ3i :j2dΐ734~j.挟Zu߿_kg1ܱSCۨn}ɓ'c0a~G?B=khz{)5"" |吗_Dy1T&A^jkrRV@,Z]DGuu^uڔ9bccsɩɲj% Ûg.]Cvj.ZvvvKhHyy9\$TZ{ؔ>L`OyǬYLN\1:_F#Ӱ@+*p"k (UY[;!66/YT*Q7Wv}`iihcs޲e [oaɒ% "rҥK[ҥKA}6xCwP___p_U+ٳgMtaN3q4i=ݻwcժUZ7:C"8pfϞ^Mi sjǎ /`ŋ Zg}AAVT?МړPogsf7i)_`-;kD"L}>CvjJ=?{=l޼%%%6n܈}b̘1M~[J.LŔz:u*˱{ndddU_nzk)'tAe5wai6!+wXXy]G{0 v ,l/_qqqܹs[o!##HLLHR$''CPhKKKCvvndcPPPB,YZ7"[X`.\/®ܦvuuYػwQ* aaaXt)VX`BR5K'{{{xyyoANN7h׮]CNNnܸgggb…HMMEyy9jpNK/!66111~:]vaDM+csdLGT)S`Ŋ8qz-rk^ڜθ_aa!]>ݼAoms(l+tktBXAoTUH7/7 wwwJO?LDD~-ЫJDmj*0aYd2=ERƍGBZcd?$GD63{wˋLJ"##${ǎԳgO6(7**߿Oδqf딘H~~~dccCO>$}'daa!ͥ~T*!RI /_N{&GGG;vlM?ndP(WйthԵkWAѱcDŽp]m`>-y ڙxI,X,&qb֊~zꩧٙ^xFO*>nGd4999n߾m4d=҄ 8SkÿUVZ )]}fM a/ng! 7#MPТE{$W^63vt׵o ɘ.OmegFC~)=o߾k5)TcelL}ص!ldJٳG뼱 E{]uqyF_H&Ln'4-JBQa.-\cϬ0OviUO8lou CLFF~WĴ* `(<裏P^^'|ԓf3:>.|}};7[ h?x*ͣ0~zh: `0 `3 `0 0kjjPXT^1)H,BWme `0 x0k\KKK+(,,!Of `0 `SpԩϬɓ!JƑ刎݆ڵ. `0 QcM8D-H "v,@`4ya˖-IRdeeV `0 xiֲ7|'O{f#>غu+Ñި,EEE5k͛,$%%aZqbcc1a888F۷oǥKp} <2 _СCBw?Fjj*|I7eeefÈ;wX^™]7oFzz: F+o `0 CHia;9H((( ?~pL>n߾7.Nܼ\ˣSJJ ]xQtSII QefggQn7!b1EGG?^9::pttC ϝ;G޽{:vppX""dt@iii:@޽͖w=<<~H$DS^_絞d3!!! òe0bĈf<=5D]ٔJ% 99QQQP^^jTF3f`ԨQ7o,YŋôiLoLdž_:tnX,uX0f ӧOGpp01sLn݊ Y999·yt ?8BBBR k ƍCDDaĐ!C?_}x a׮]GϞ=q۷ P9r$SNںހz;v`РA3f \]]tRdeepf* 222Z `0 cÕ}a= 0yZ*rЯ_XZX8<M8_Yy]1^ Űk\\\vP8K`0 `0ӧ?O<ߍy<迿yzW㐔Í~2:[GddҴJe-ZYVC`0 `0hxQXTEUUUDUUT*4 b1Tݺյf0 `0 a&66٫`0 `0 a&W[b f`0 yvg0 `0 a>l`0 `0 F'Xw*XDjV`0 `0VO5y\B,.Xb jPx0gD `0Z Ϣܬ46V҈`0 teYyuXZ;bQDTikRqP+K[]hZ=CfILL y6l[Nwkb1sYZ}aذa8v q:^xA+nqq1BCCѧO899a̘1oҴ-yM7nNٳE3(o_so09p{9G8&kM=n8^ysau9ډuܔ<8éSZ,hGꦰAAA+V^ "j͡N5*m e=XNdDj9zd IDAT,(>OKɓ'C*#+Wf KaR|Xt)JݻcرB2=矑w}ݺuk-~mh3fh1 m:C@.̙3B޴iz'OުŜ1Agu9 юX_ᅬ8q~)E]^z= +F@Rg79Kʆ -['JFZ?. t'Ng ())ٳspss?\G<5ho5Vw򂛛pilLԵFnrss1f1#F֭[ޢyB|򯨺d8J *E6Ԫ"hePJN^#Rq5a䆂̯R6zEEϟ777t aaa(//"">>>GB}b͚5:u*֯_DpK]3dزe FT =zTHzjx{{z¶m'665<==|rϣSL1Ed2DEEaJ Dzz:VXooo899aѢEy^Hl2 2R#Gą TP[[+WNNN DJJIy q] &&C -;tՙǏG=t.2V |6m\\\s{9s$OTbFsi1ƛc}Sl1r9BCCѽ{wz_ٳ"77ǎåKguOia;9HUŗN**Tvo ;H%Yb*ȜO)')2/ҫe?4eɡ=z4Quu5вeˈߧ EFFR@@ۛ:Dn"FCFN?>F!"ӧOSff&r%tu""d~""*,,$HD| SVV?oҥ4}td23g%%%שdmmM6m۷oӱc8p@HLǏ z7JKKm,X@oQii)?~\K 3K}d2͙3);;<<}TXXHk׮% yXo6u]LSW>xb|!ܹso߾tz*'tIc4G'~>jw:vI>>>ΛOO/1ҪUt\7&0:h(88{) xWuڻSj,t.,,$tAɓ' V /Y~YMWCgSx>4 Իwo;wꌗxxjUJR)kVQC5rRȫH^]IՕT]QF%TQVLU%tAEr_@w.%>SW޵Qթ,IJ/'HDqHHiqٸZXiFaGGG"z<DS---^{54h) "?7㚇YPL4gflGfsfe}/KM]{*э7_~$H襗^"RMST7]>VFu/xTz{AF!joFQ{wCmMjJ+H숺Z㛄ح[7bbDEEXgĈ:u*bcc1w\ BBB;ٵkL:h@"`ĉ6l|}}vY_6L{N  44r mR+dMDRPPZm^9hHNNF߾}aoo2nI|kOؿ1w>T\X9=,i6l3g555=ztm.* B5EP֕CQ#xZe]TuP!KVkPUUe^777@ee%T*T*4M4Bn #F 55o߆Bسg зo_[N.0j(omT*D"QV/{V|2?bIয়~BMM 0i$lq4!ˑ~QZq s>>>}6gl_ z_Mza03挣BٳwFVV֞;< ~n׮]2x̟?lSOS>x޽{ѡClٲRHMMŧ~(aĈVr_Z-w@kp(!ީ?lNZzw΍(.\-[`ݺuq􆃜O}撞___̛7pqqi4 @ѣGصk,Y*d2&NSX~kSWW7۔z?g-cL/:L? ؿ?tvC~M0G9믿ߟΞ={B ̙3:O>͹9OC_vfիb :9hÆ ܹs`QpsO9sf&etGf[\YW\ΦeP|l z=tIFΝ{nXHII{^C=rUQ[l%Rtl!ås4$?A6u`|3f`֬YBuu5rrrP*E\\/^hL6M蓧'^ \ׯ<==QPPTcոs6]" 33555PTQ\\R\rEnn. 1~xmYȑ#(..FII ,X;;;"ܦɓ'cܹGUUuOj+pF)S ɬr͍>ΣGGsa"w1>}t>}Q^^={ !!3gӦMìYr[999vW6f)ogiFVWWC*Ƃ pUTUUʷq Xl]={X͇ 77Y?g)M ݻwΝ;!ˑ;wpбcGL8Q 

K9r$-Z_ǣG0`mի_c̙(--E||<еkW$%%!!!b2 aaa:BqÐ!C|ήw?mЯ_?1^'W[\L/ӕJ%Ν;|\ssc UoFLLΝ si㭷Ҿw-/F?׵kW8p{A.]n:$$$ॗ^2)͛1vXDFF)))8sLSƀj3r/l(l"˖-x ???8pd} Ɠk֬EP( dž? N5w?g)M9>>999 ŋw\a۶mFPP еkWTҿ)9[8V w3 99x`;8v[L<fm)%wMy<>% J%wP:8;;sHsje"D+j*N*]W7H$c;v`„ W:KVy!p5իaXƉHÕ@ 1|  >\Fث$=iݻ:u޽{Zo=_!uH^e5 &Mk/ N<> x瞈dh'Oc{-bVP(ZU}?eX훢ͩ'uM׿,dff['SB۷-lG%1Y[ p(E͢h 6pH$uV I00TVVٙ3Oǎq1bY$|wAnݴ lm޼y1sL<|Vm]t~'N '';w^1{l8q"3`wwwX,Duݻ:X;5vm r 4k4YNpCIPg ddf}븢ӧO:uX흛E! @kOMM ze˖aرXr%!d>*xaooHRlذD"A@@>-tR~~~ؼydxgagg___|:(++Ø1cLN̥R)֭[`H$ڵkXx1ٳgk၅ bH$2dΟ?oQ?@mm-># Cvv6bZؽ{7 jڄc…ڿ>}9ooo|wǕ%T.VTT && Œ3SN5ZJ4[\_WWKgϞpvvưapY\?aL:U'rr2Vb |: |ҷ=]'OFp!rlNP 66;w͛g MΝ;W^6m ]|v_@ +'Oj{ cp }bmiӰm6$&&B"W^h=%wԑ|h?=ܻrCuhD1oAy7Ra?GS/""ƌCrr9 6bcch…DDS>}VXA#"rqq!:pݼyj5Pll,\.ӧj"":qB LF(//R)۷JJJH(ƍnݺE?|qqq4~x^R)h厉SNQ^^ۗhoё#G%%%iFGGSrr2Ӝ9sٙz1cѹsSҙ^!Rbbb(%% 7ߤ.]F1%ٺu+Kwnn. B.\@b߿ODʒqo\y_u߿??iNlj(,,ϟOwEEE'OlB @@YYYF1x`ڴiI{LΝcG5LO۷oFMsmTl9y!hɒ% uTXgqc[KESes|soi׿E oIg=Jׯ_3gUTTPII K.i?~h6t-cDjyy9Qttѹ(}Ι3x :2%N'ZM*T:RRmM5V+FQIGT*ңT*SbQ^T*:i%SΨQTM'Nӧo99t1ï?wgϞ%tmr;;;L&#"݁t5@i4]vʯi(%KgH+Wl$m۶5J72DFe(pRRRߒܹs׉h͚5[o]|V\I#G4ZGqe8岋yI(,Je[W4o<Φ~V\I޽k09.ۿ>D"m_=!!!=eUUUZruu 0я|2d{㿜 IDAT.nJTYYSΘ͕H$TzFGGG"z*GΝ;FN.[.\cq\[17fS#07L] ^̙6lǜ9q9 nh*T*cǎ74_z?#k4h R('ſs^r\Kx :"778;;bo/ _}w~7nݺA,C,#**Jgg#F`ԩܹs1x&b1 233 H$TTT@T6*ףGDFF⥗^¼y֚wbܸqQW.]heG$vZ4SzχF>-W[ҙ^oBBBB:tQQQ Crr܄ eL+s)߼h40`y1{l|xЭ[7,[ ݺuCbbbaÆi _5ZgCwpp@DD)))pssCHHt@h7`2K J兢" 88zmNV#$$S%KlXeSۺwѺk0QIC6V̍b).;mCX:7;Dֈ SA,_~~ebL7|ˑ}`K=vk pF~<~뺚Ⳛ75%\7E]v1|βPT{tgbϞ=ؽ{7 汶 [:EzSv^{ B?@F7~?pw$ct6661\p[lu… : 'C|3tb޼y K#Cj80FÇc׮]Xd @Dd8qNYcM]]rrrt&r[=g e,z['""'N@BBɓd8p v ߸j.!Mnިŋb_\߳gOӧ97!ҷW_}bGRR&Mͻ|r_Rʐ:w׬YV߶6׫W/6 ?pFik׮E޽1be9(}bT#Xsop]XgG=K}5h a`o2WM̙3}Z-s=x"`kk \3V7l؀h̝; ,0#mҹGiI1=Sp2-'ӏԉt9ygOӧy 2Μ1⿮l݊j5{͠AD D@@B!"_xݻÛhNNN1cf͚,TWW#''IIIRXaňƴiӴxzzի~,HMMEqq1V^3H$DMM T*{W\#lmmBO#GP\\,XvvvEvCM'Oܹs*<W:ӋtF)S@&Y[>}+WԾѵkWb:W͉;y,XW^B}ZdΜ9HHHqL6 >>>/j4.wwwǴi0k,䠼֭CNNNƇ GGI&a0`oߎ˗/ŋꫯ,`\ ٿ?\]]1|po***jR_;vĉulUVV6z\Ro|UUU7}] Sϗ_~qlق^xÁtʊD"۷֭[9s@("''{.wa\bOF9hb)Cڵk2 Xⳬq\{Ds A7o\{||]fۿO"=H'?~S h@3WÛI"ҴExat Ѩ+bzRܚlct F@} R555ZA(B,١o~lm[[6 -PRR- "@y>IBve hM ~J%*++QUUh4NNN# am򐖖q3u~2E`0 lW(J-`FArr2֬Yr>f~k`0 |iTTUL1 aG%%BOAuEEʠhV[M}<<<8,==4GO?͛7*A,az-4]?$ ^{5Ϳk.߿l :uPd2VZ@۷[>@4ȑ#!~-[n+WWWBEE9zia ]zaL֢qc\RRFK|h.l6k8xuZ6ږO?1TB*QJ ~?sEeXjZMkpH$y v؁K.d R]4f{nopwwGdd@C|xWZAR!"̙3f˜1cC!22݋N:a޽V:Gdd$&Mڵk'Og}rEZba6uy*U(pqvр$&"k!VQ4 a|nj!ZFS`vKUUoߎ#G`Ȑ!͛7YYYik~F3én~~( BY` :> :ܸ,̬oWTT`BNsh"hA޽l2;+WDzz:L<~l$33/2QPPT 6`H$Çe.] 7o6z=2 >,>@'=##eee3f8v@*bݺuD"AXX]ŋ={Xp! D!C>={+ͫ->0vލZ=6Xpo"/Os}qe8շ ;0c xyyԩSͦD.CѠ_~sR=RRRt/M60:,]={3 g &`ԩ: Z+V?Y7X|+?i=qSrr:44US>ۘ~qy\|Y[gZZN5v ֌a666G"`СZroLFSp$_?S.:'{WPtvPnӟ(7(oF6gh*EDDИ1cH.\.aÆQll,UUUQ@@-\>cӧъ+(44w}DD...O7oZ&44}t ZMDD'NR($T*}QII Bڸq#ѭ[֑/..Ə+T*111t)ˣ}_~7:r$mhJNN|3g9;;ӃGo3f̠ :w=xu2W:Ӌq4D*RLL Paa!ԥKh4$[n^zi%P#߅ H,{\Y2N+믿NSvv6_ɓ'[LgIMM D"JKK9?e={oZMC ˗͛_~ٳgS=(##JJJhԡCqㆶ<BSN Puu5 BP|#!#":t(}'DD4n8S)2e ݛNlj(,,ϟOwEEE'OlB @@YYYF1x`ڴiI{LΝcG5LO۷oFMsmTl9y%zGDѼdTXC.Z7,\:7CE?vܻwХKy?Nx]c!Gׯ̙3͍***Ĥdԇ6񥏾%xܺIߠCyŢvWTtj&JQ ;&NHӧOrrbx8ɞ{kARXXHH.k%&&RNH$;v-eddhO}...:FXڿϞ=Kvvv&LFDF}5@i4]vʯi(%KgH+Wl$m۶5J72Dt)rmP:ӋtS*++3%sׯњ5k護"|2\Fiʒqe P(Y+v'zX L*--gҀhʔ)<۷oݻB ".ۿ>D"m_=!!!=eUUUZruu 0я|2d{.nJTYYSΘ͕H$TztGGG"z*GΝ;FN.[.\cq\[17fS#0s|ڏ?H 4HWM͙6lӜy5~s¥sS1oxmϦƃ5c1uT*cǎ74_z /} 驹rW^N pqqX*[oKBW_ݻ&ߙ[nňٙxĈ:u*bcc1wF漢[[3ԿUWWDdd$||| HPQQR٨\=^z kxVV޽qoJFǠt颕YHڵkL-??\mJgzᗮ&^^^ > }!DEE!,, s&LЖ15|@`]W_}*l2PVVoشiͪӆ ='_h HHH !!!ҥ ܹc|ҷ$HRxyyDuu5=j=}S qɒ% "QSۺwѺk0QIC6V̍b)c;/_dWW-SwWKԚ~%di;KԼ5p$E,_~~E`&ǐvk pF}tȑx! a;`CJ~PTPTh4 pqq[9"44#F@jj*n߾ oooyB!go߾Xn6]&aԨQML6* "1[l-CL/28x JKK03ɸwΟ?0o\5| >|"%%ڵ+ĕa„ y󐛛 X}\o,M ~ 555HHHI ?~2(޽{QVVxyyi''|wG̥^vcotϊr\t Fۨ}sQF\[1'fr9Bsسgvލ,37僥:4Fk;uAR5mr$}_X27o*v'/> ?ؽ*&##b''8 oo~qL{4… زe ֭[?.\Io8SC`` \\\TA"0zh>|v’%KPUU"L&ĉuomꐓ3۔z?e-cL/:8q0|p888 <<'OL& O>㪹 X,6b{#??烈QJM6_~PTZ6g>zi W_X,I&i._ׯ7ya|T2]5kշmˤo?ܿQڵkѻwo1`نo8hk+|bf}5#֞?;6l؀h̝; ,ژj ~eǔ~Lkkk ^6u]\; IDATŋ 2)!g\ч1=5'r_WS_nEg{f BD"" BB/MPWSc^'''̘1fBVV$RDll,xbDGGcڴiG===qUr\~d}鉂W֙pJ$dff* =QZZ+WEaa'ȑ#(..FII ,X;;;"ܦɓ'cܹGUUuOj+pF)S ɬr-A>}닕+Wj_ڵ+zjGM,XWBP`߾}-vKl2=zFTT&O &e6mf͚cݺui=ATT}(6i$? qe\x_}4ՐÇ9o&ձcGL8Q<(_ee%tjHRUUUHLL)CHHvލ̛7[nEHH̙P 8oީrG1s\\~a\s?;\]]f,Z sS>XC.Z7,0CA`` /^`Xu ֊a A7o)6/}5DSfZ:s?ll`#x<BB\MuȐV-PQQHGGi;vйZmq3, n`kkSN_lmq Çk׮W^-(]#899Y\t=`{~a 4 {5$b0i磇?67֨+bzCFܚlct F@} R555ZA(B,١o~lm[[6 -PRR- ") e71jI\]p!;2 }>}.NNT*QYY*@prrf0 k47gyC|| 틁!C[[`~WBT*mmq l4 f矇L&{b?Ǚ3g{`0-'v?`0 `0O`0 `0  `0 `s;p,U] H` R[[<`0 `0v ,x|"QDbh4@ue)JnD?7aW`0 +p>4***AC^l& 0M~쿶nd;gC$Coo$FL4<jIIIfIOO=*++h4OqͦhKnn^i˲ܱ$_k[AV?7$ F_Q㥗^T*şgr^ӶoiiiM&LqeB6LVBXXXcmDb4B;pҥuazi<۷o˗\%vVm>uo(4:!4Z@]@ P$^(4*.@"غ14 a|nj!Zú=zӦM}PZZ ڵkh"ӳ_~%z~ 111&{k#/"00Ea0Z={ofkb՘舿Xz5Fe:4o{iy3&QQ W!P@eM!TRB|Шa@YSYwEEO///t (/(ߢE USS޽{cٲe;v,V\txNff&^~eۣR6l!HÇk,]6olzd2}Y|NzFF0f=<BϞ=ꊰ0dggj,L/eݻwcppp@``;->34V'LSONNGL1}o}pqqcǎi:`&g4n5M-U(EΝyG;m4l۶ H$իn3m͛7cܹAxx8.\M#"b:缽wc>Wފ AAA1cp)ܹsqi\x3f}5KPwX~2E]].]={Æ ٳgup̙c|-c kD'ٺD"СCuO,#Zt'JMwXSyPrLɇӓDe={ *}ݗ7۟у?[K|*.NE&""͛7IV RZZr>}:yxxZ&"'NPAA) dH*Ҿ}B!mܸ֭[?GǏ_*RJJV:uQ߾}Ύ֯_O9rPRRltt4%''S~~>͙3棷3fPPP;wd-cX?&ϫ=Jׯ_3gUTTH$ǏQ3b2#!*m".cC"F$cLl1osLubG;/5ƸZ!EKV$J 25nn1(Y.s}L=J7_w ymw oRzkUNJTJfSYl*6Jr'Wx*yn_znK7 k6Ue[TTDۗL^^^$hdggGΝmTO2Tp9yiii߿o\zrrr\]]mI?0 S[[K'O NUUU\?Hm۳go,!1=}rD"kds>iL.Q8C6Xs[b6!j; уCDO;#isu*_W)R2jP]/lV*hBsc p$"Z/Y200bbqqq(//93gDBB͛CvHʊ)|}}YYYE^ HPSSB޽{#66?<,Xf% .\}s:^鬏H$ Aצ\CVcȐ!t3u.C]紐K111HJJ m ]y#77W\µ񟚚nMbG/5 R `'uzc3݅2xyyA(lMg}||G=8DEE!557qDNeiƷnAAj5looom,mڹNWIM~6bn@ @dd$7r8KQ=%y(61X,qu#Z_BVB(@sS-hjqVFCU47CReK5FPԨ7* @JJjϰHR.Fg EEEȑ# B$''oEAAD\&a7w6J"~sW t\Y/﮴ߣ III:uqm466Ê+`oo0>|~~~زe 7n9ƍ(z[SWsO>|ҒGOcqT*Z1118rr9rrrh޽{8'[K}nx{{B'?$ pttĸqp)d2</g* .+cmtflu"Nj̙3:s ř,$s.chIʧM ܌_Mȑ3κzrjUhn[@8H# 񀍽?v`c [0 S! +hlqcQx1g\pVV(HHHŋl2L<f^Fnn.quڃ7 rlڴ w% Roz*`gg鬬m~gpq-=7\yzzbڴi7o erZӧC&u]>@ӗ8{իMK[[[L2>._=Q׿X-_HMMEyy9,Y{{{SSK1׷o=kaѢEECC96⺥zkS>`tG郦&7 _ ~~~ æMt^tlu&x뭷tb3222`ooFot?Ν;1p@9Xx:r. ~w]>34VJJJs~`?DŽ 0qD#11@dd$W_W_} 6O>s~888oO-Tߚ;wDϞ=d2 )4'R=M1g0#x駑8q"1~xntt4kLv؁#F_Τh 3g`̘1Fef,w1 Y"K}v?@ZZΜ9JΒ_KH2Pm cȐ!͛7v5441kʊRdrcǙ]P(WEW! u)T!9B$vZPyo \<o;D"899=yzzb:wչLvy8tW憼<7 KV cxIK5f\iåKV---ӧ1|p?ׯ_ͣV,:,N>>b̝;[l'0vGN* ۶m?6'-lwz<<2yp UojP(.n q l`0Lw^êa>Ώg#Q/RǗ_~M] +puuG}>}OFSS|GUЙRĕ+WVك;=v~egg;;?{=ju cL~~>Μ9`D"AaaC֪}X-֭{q L6(`0 `0(:ܹ7n-Gh}/ wwwxzzG􄧧'\\\ xˬٳ///$$$sK"$$ &<3Xz5Ə#33fkgee#PPP|':t($ BBBp1UV!88ۍ#0`Nsp}DGG'OM83gԑ={>0v߿?$ e˖!88xwVjMh# aoo8aaa `0 a)/ZؚܷMVC,c#MMACCjkkQ]]jԠͭ3fϟǥKpun¼n:YqFD"Z Gڵk "•+WRiӦaҥEpp0T*e\v QQQx7رcJl۶ ,@^^^9Lz 8}4ƍSG&aرpqq1j6tOJJ”)S~zm믿/ŋq= 4HOO?O|8|Ѿ&[۷iX,Ɩ-[!C`̙mނa0 `0 >th;wAg>3,oeD"|_ڿy//|}}h":t]vO?ŗ_~[bϞ=k< [n}HOr9#F@HH0i$>xjFSO=:m8^n J%N8hii1uT^m7m4 >aaa8q"~i,]x饗:Y-v-^ӧOG>}L֝5/pO^zA" q{Fll,y,X*.\@ii)^y竛#bbbHKK"""Xvmjyohyfbǎ`0 `0,SVׯ6mڄYlJC @.(//GYYt4JJj[R+#G"==%%%ٳBoQPP~!11+d=z4\]]ۭ>Ԅ$L:U mg)dCa˖-HNNK`0 `0 ibΝ2dH ;;;8::NNNprrD"3xO΂`kkgsEرضm.^Sy ,XaaaJm>kо /رcطoV\zd2^{6!ƌXC!%%SN̵h&[4\zӧOǮ]Я_f0 `0 KGD @ ܂UUUgggۘ3g.\Fdgg#%%P(ŋcٲe}:d2Y`0 `0Vs^^^}֭[1aL<Cvv6`۶m~O?Eqq1111B߾}1sL( èQtRL4  Bmm-̕oڴ _~%yrݻCJJ !wwwlx >>b,ENk>#ܼy}VX,{/v`0 `0!(5z/1v\^O?O~i]8PVC @(.'BD*^TDžD"899u쪪*!//O?tǚla0 `0 >1|ej j"^?7,CE<=}ZZ!vbp;]L&ó>&d `0 `toW uuuE]] T*D" DV`޽:=X- `0 n3wpp_VG~~>Μ9} 33yyy "L:UGNFF|M,[ +1f444<4[ϟb-66S73DsbΜ9FZZ=XTW?Ob̺8[1:Nwg}T*лwolܸG]]W{aРA5k+V***®]`ooovvvXp!;fLѴ< Ze)ŋW^վ^y0} @@-Z Fx1X.HPXXjb˺ut޼y3k8qӦM{4J1FǮ]pq 6 }v… :t(`ѢE:ǭXC AMM \\\~!.]W^y׿< {?{\\\ۥKII@n!C:& qoŚ5km6_!mƌؿ?;hn t9s&駟bٲecX;n CCW=zɓ' XۨȂ0( T<J*(@-MX}>(/ &OOkjj0{l K"$$ԄgyW~zdffB  <<ܬ<5, O>CD"AHH;j*ؾ}Q{d2 {{{)?w߿h6xzzrДn'N̙3u䧦gϞFnbb"D(aٲe+}]jZ-Bۣd`pttDXXg瓊17+uDqq1j5=Ν;pppֻ'OĄ :666xy*h.n`ժU FgTZc˒%K0x`H$ 6 ϟ7*O;577c +p%zbڵkLcd _?ٳg1glܸ{9 .ʕ+k}AA?<<͝;\\\+!XoSxx8/TYYI:imcc# B26|l7~')VZEsϑB)+W҃Ņ""ziʔ)DDBvjs!4eJ?P||y:u]r?駟7syH{r`}L/~i :yE"2}Ҕ7O l=SaI&ѠAt% :u***flL=J7_w ymw oRzkUNJTJfSYl*6Jr'Wx*yn_znK7 k6Ue[TTDۗL^^^$hdggGΝ6hdjOV__ONNN\yss3rmd\ GXX_f6jƌCormes[}'iӦQ)++r9={LӧOשWYYIщ'lmm޳gx~D"nZx1/**O999qƽT*M;w4XE'HDO6hJƮ DvSgxӶ\"ea]nM>| sMNdo99BhΜ9mʴ'֭P&Xo䟈23[sY?1!I$Qzz94\,Cs`mLMŻw~:m޼f̘AtJCӨQcLۛ6P(SNq œɿUR6Bceʏ_ BTB\45^-47IEKDb'4_DJe`` b1b1tV9r$fΜ̛7LX 555P(mݻ7bcccmVpJKKkۃnARR -- nnn`Yyחk}m6oތBرݶ<}}6|7 uuu;w.,Y Ν3ZwѢEx饗0uT">|8x ̛7JRϘ(((J2xm:7w/b ADD= 8z(ѣG1qD޶y9жjVWcJZ MePTQ[y WX EK -P*!{@Rި\777]TBTBVYU*.g EEEȑ#`]Pd|(((@~ȕd2=Gx|hjjBRRN} ߶kEáCe$''s 1a a5tŸ~quuŞ={PUU*~~~m BaÆ >|8=nܸSƍ~O@Hd,KƽR4+ߘn||7O?Y,3,v>et>r"S~?b˖-Xlf͚et~TkB*b͚52۷o/gLh&&[{9),.'&&G\.GNNTܻwϟYQcoh~*Q{lu5V9oRAe8B[6B{88@(]Ь3*7((&x"v؁Dl۶ /^)|YJff&`A*AZ _Ʊcǰo>\ "d2kmw!:C3bBJJOyk;KiMhz*O]v_~b5~F~~>._l##** ʕJ%?W_}CB ̙3:,FdK}KK ѿnZPߟ 5k?6lt>cGxװh"梡Gdtd}Lӷo_c5aaaشiɛͿۃ%zKg,>%%%k;KѶӚl>7og}}s'{h/ ??]{0q0@>q8CDxgac5j٣S'((k֬A`` |}}o>67;wgϞG@@d2ܮ,cS1EL8?~`c #j j"^%nmllëll֠7(-D"'Pˠh P7u( \@N-Ú}/ =jv~bgg;;?{_qǙ3gpGJ&[a`0 dV͛7=d2cت'GhhCyšla7#G`0 **Wg0 `0 ;l`0 `0 D7_n` 5 R 1X.HPXXjb˺ut޼y3k8qӦM{4J1FǮ]pq 6 }v… :t(:?`֬Y0a`\.C]zO߿ 2j2ρ ,Z~-֬Ym۶!-- į m3fq1DGGs7l؀h:www.Oڶ2"ѣN< XbE~ұ'MuQSWaP4@xES 9T-UP(*jUZn\}P^M`񁗗K.EHHwW < V^c̄@ @xxYy@k2YYY|  D;v;fժU=}vd2 0￯S~9ܿFm)&N3gOMMEϞ=m;DQQQòe WWW&ݚlF(555&8p#8;T!0߆^37$V1p@n? --wׯnݺ=-T*Y=͖Z pqq#pYv@klYd  DaÆFiǢf,_puuETT.]SXL[v->>|l7~'׶C ʕ+̌ oc`|2tet_>r"S9HC>}b1K|8{,̙7w:xzzb…Xr%w/((}(jjSyF{AKr\S=qoܸqXd 7g׳gO߿iZt}j7 \SSx#<<o6|||pir}ci>o.7[SYQJԣqE}i%oo!C*JLw>+I~s!̦T?t`[HWLʎh*..b1b%$$Q}}=В%KhŊԷo_jjj""kRdd$oyDDRtMRT$J)!!222fϞMRjhh LFڵkDDA$"  uV*++Bu[x1M0ې kL$J;v̙h"m'J)>>N>M׮]~=}t-:~86vZ-\zA񔖖FEEE믓/jX3|_7L7Q>27$H$QFFӧӻ˻۷)44&NHYYY4l0ڰa/=w}zMΝ Zf Ѝ78QQQpB*--嶲2"2=<I;Q^cXNdoGXjs=G B,**V\I< o_)SQHHڵ1صky1Cq13X3 z駹srrH(ŋI,ӃHS<4 +L!'MD ӥK?&t)07|| t2(ݸ~))IEoPCEV9*MۗSY"*͛Mey *ɝDws_+8}Yu/ ۗ_TUiTnQQbn_rr2yyyqH$ݻw;w+ |I6?TJ>{,zL&#"݋b^^ǩj}A#S{lLzrrrʛՕk#Sm/wʕ4`=hm6[Q4fz7 kۥ-#--Mg}}0g\?iL6OYYY$ٳ4x`>}EujkkiDbéH$qculEDDŋ}QQQ@gsrrM{1T*ۛvi\߿O"N>m.S ύ]񤑧mD\;L\5?|3暜Ȕߚis9rМ9sڔiO׭[GMbY[Maijc*fg:ݽ{׉h4c rtt+WZ_Fg<_mзB!:uۧP(,>|_lHƚ˨Cu@Z ͍%hj(½[hn*CK Nhi6f@bbH=rH̜9 7oj{B}WKK ++ ի$ jjjP(׻woǂ ڬ,}W^1y~9::"&&III4!""emuk___ &[ټy3 cǎv$b1Osa̘1 իҢ>:둛+Wo׫c ??j#F"##ʕ+ADܦ%^$aРA3F| @wv}@kY:4ғo;Xb%z0|r"S~^:̝;K,W_}sh"rK:u*cm榖5T`qϴ>>>ѣGGE\\8q"o[<Ԝoh\PPZwM)7ɿR%ZP2(ZP}㨭<ګhn,ʖj(=RQ__oTT*T*V۬*J@gqȳ"DFFbȑHOOGII ziP(Drr2[_~HLLe2F{ZGSS0uTgX-:-[ 99...]rkŜ\kq={ UUUHKKxillw}+Vaaa8|0e˖Dɲd+Jw?-=R,mSF?c*'2/lقe˖a֬Yhnn6XW*bڵJXf oyk{io?X"GS1=ȑ#ATT{K|CS)7*.<*e Hݢ) yEfQAAA5ŋc$&&b۶mxN"n|YJff&`A* ڎ&/رcطoV\zd2^{6!ƌXC!%%SN̵h&[4\zӧOǮ]Я_~R1}TQWkk#??{wE;3܁ Bb89vE Pxp , DUEUXcPDQE A$1! 3G833'4<ꮪw1XS=p|iFm9J¾},,&+rƝv_QQTtA@Dau]p!^~eߖWmOּ:rW>\tmqVU%gc"GYe޽Xf VZZs碲˗/ԩSqiDGGz\Vڪyp's { 6 | pwƷ~D|Gj ݉ww]KcCEJ/߶ g(<}" ޭ}QІm(_t1i$L<Fii)RSSe^DŽ 0}t̙3FB||間p:u :?j#<<صkrrrtRVEJJ `0i& //'ODfǏ#33ƧzepF3fUVc.r*,@U3tPc57xᇑX}vݰwszcΝ(((ѣG1rH7QQQ.ѣGñcLw`|'5>qWHH1yd+V@jj*LbnQQ-^.;v ''6m|||LJZjaaa7ny{z;f :=zիq |xn:nOmړ5kׯNî]~z>\tqV˶hL:Ӷ">>g6}(UVᥗ^r<{l==nڞwұ`\Νŋ@LL .]7pnUӦMéSPRRO?Դٹq׵46Td *0x»-򉀗o{h=o~?Ghˁi JЖ^r%QF!$$#GDjj*^Caa駝@ә~aذahѢ:wC;L6 ٳgDqUY.]{O>$n:tزe D"$$"}[epرcq;c.r*,K/ܹs?izn3fPu1?x jد7Pw:v%K 22F€vZwyC+V`ƍ߿obȐ!1bڴid۷moZliڼyKŋ۷/ݻw@:dnժU[q]w!<<ӧOʶJڵkrJ|Ǧ"?#hZnOmۓu!551113guOOO}Ե\tq>%Gc"GY^MGA||< Cnq6gPڨyp'[ST>|8rrr0dqqqưan[G5o322O۷uz-w}7"##_X,wvnu UYN6H9x DiM#/Wd~ 1fR jT?hF]\YPUf _IOP1l8{쁯/jh4_Ĺs;ubJ(˩S0zhműcJs`0 !!zJºuluVq <Ngܕz]^^^xQQQj{PEaPT5^|A6CE9 Clܸ?3BBB0bĈFp\틬׳UoEO?4&O8$''c۶m1b 'mm6 ߭ݵPUVV:t/g}EEE3f@oz̓XիW_Ǥ!XN)ŕ뢻\\O{{/ۿ5B#è9`Th,*TBR mHKE@ڳi3~1ؼy3^}U˵Z-2229W /[ ׯ?7i2E7͛/̛7:˗/ٳqx t}Ǝ y...իcoM68|0@%֭[7>\\Yo˖-mۚիW]Iܶm[k0m4|GXp!^{5$''O?}Q|ؾ};L/Yqqq8~Ic0?Db,8zoˊΣ07A-B_2e0PYqz}>XY(-A->e=-,,ĉѲeKh&L0}:{loIdYY:vC ŋgT*tiz@խ))))____#44*Vcm,X(M6x7'11r |||{bp%-CXXQ81~xpM7a֬Yv]u+V@nݠj3g`Μ9BPPz)îSAaa6n܈=z111rި}[uv}#Ǘ_~C};z`4q뭷 G$'';͇~ ,@tt4Я_?߿r:e̙ѣZ-CMϼ/*//ܹs ѣ-ZYfY\)N{ηСCPT8q)ݻwCRwÕrٺҵ˕lјZN0k,{=;ؿ?&O_|:t&,, SNMtlذgwglή[mhS:a͝~}>x9soAdd$&NhM7݄?Д"cbGuV 1vXk>lgm[zRt鈜?:_ο"y矗˺r«rI/r|;7Ur'J1;'@L6lʼnNN' &Hqqo^fΜ)""͓Ν;KYY,ZHrz""%;wN*++%00P&L wN''N0oFӥD>}ZDDBCCO?\QղrJΖ ꫯ,7}t:to[e dS~Jyyi˴iӜ@;vݻWN>-]ty;vٲeKr*,N<)*JN:ewP;v$''Kff}X`j֬ivo ˪Ul..]$Fk\9޵AD\.n{NϼΎ>)Wa\"?093_|ɓ'Xfȿ -a!۸;:u4u7wgJpxۜ}>_O?ȲeG???9q ŋˀL۸vLjoͼnX9''Gj|"" +JC (-;^Ѡ{8s|V6n* ])u@s=UsvmOW;r't\϶9PTT~3gᅬ]wڴiýދ1cƠSNn۸;cGXWwWێ/ѲeK۶ml۶ #GDll,L >Xk1a^tFѣ"CFjPeC_Q(ہ{Qr$KuWPQPTVQ\\l7`U``h1[k`` XLRܑ㮻®]ptM6Uؼy3>#K.Xbiybb"Zرcg 3fD;w),նn݊W^y7oF@@@C}[j ѮgPT8{gϞEddd렠 ]W\+W oooDDD)|T*h4r ˛+eNߝצ=P甮 gkDm]wn ̙x\700-B`` .\r5VkX:N:_생6l ㈍E\\pE:tb^N0X\LJ5njT ! @%Tjww[Qa@nڵI9z +Vk#GX,7͕ܵgDFFbʔ)A```g٪T*w}ؾ};6l؀磸"DW'eРA֭[e3ƴٱsWu9Tj'O?իWK.uJFeW79jv}= All,njz`0`ǎx,u^رcuRo>; qWTT 55ݺu3gk \=Q?Aj]o.\_~7Uvwۓ5okNܕ`*][jm٘QuVmٻw/֬YUVAVcܹn3uT>}Ѯ 뢾uUKp's/ 6 | pwƷ~DQoNxxx8>)2D0Wxo`oh}:̙QF!>>tGxx8N:N~iztڵ 999XtA"%%eee0 شirrr'OYfǑY42 #GČ3ЪU+ٱsy9T|2x=F?0kخ7`y]_Ν+V )) 9999s&|||,qVΝ;QPPGbȑ7nۮ A||<&OT`ŊHMMŔ)S,-**Bvvūԥvc 77ӦMCIv^-,, ƍ3<4cϞ=6UonjCGXz5N8o6֭[VM{vatصk֯_qG}=3`ٖ[guږRc٦/j*K.gG7J.ufNm;XsvsΈŋMㆈ`ҥzwP5y1m4:u?Ta(/w[/ {~6ЖӬ-rJ :FBHHFTkBO;N3aТE tǏ^w^m 0gƃ>ݻիϐ,]|Iaݺuڵ+"""e$$$றc… ;v0g]TRYॗ^¹s윇f̘Ç"--V8a|5Q}k٣>jx'ֹۛcɒ%ԩ~ܹukAǎdDFFbԨQ0`֮][/x71d1mڴArr2WV7x-[xm޼٥vEQQQHKKݻf2j*z뭸뮻ӧc[VRaڵXr%>cӭ}Z[eںu됚̙3ݺuK>Z.Rے1zN /@Djx# >>e9sQx}u>lq;xۚ}c* ÇGNN bz?..6lm޸21nz wvܥrMCgp'Jklzy"CPx/1 KPkAOT!(.1U 󀨚KѠYf+W3go),Դx°f͚:?@׆=ݜѣ4rDDDJh~W`QND'dThޢ<="&deJjRЗ4r4V𤤲P&" ƑGކ))2~B!4,Oj /Ci@V} j xyy1o$֭1YJIe!xzSSgؽ{w~U gDՏ4۷7nlԙB|Q}3HJJ²e˰nͪݗDGG7On4%k,5sΦ_jݗ"g'""""""1'""""""Rg7( c /]#""""""rgjCEiJ.FxCh.A(-C/pZ񇂈ߡmѫ #"""}yq| j 5@X("c4QYYq$,,4___(hċ/swV^9PSNah۶-cǎ9n[`@BBzJu\o֭;x::.]B||<:t耠 u]6-ļypM7Abh5 0*>suվz=G6!!FXXZh#11vEc\ZXXnsT?޲e tŋիWٳg}YT*Yހ`[K1b[DII :t_|>,Lg̘ݻ#>>/͛H+33W[y233vZ;Mǣ =>GRk[\˕13EEE8~gMt#=- ?!vә3HOOǹtW|oQ ,P H1!-w;]:mhϦ5h4bxWm.jh\ՎR /Xl2_0nܸݰ7o/0o<,_gx7бcG|g;v,`͚5hٲ%^uvHgΝСCsE׮]z/-hݺuվȕlق?h۶^z5DZm۶EvӦMG} ^Crr2>S|PGŇ~۷#..%KǏ~)"" @DDL!Cc爨ZC]9j~a# @ F# #"@{u)}E~_Vt)jt/˄Aʊ+TGi j/siaa!&N-[E0aٳg}OбcG<2d/^={@Rk׮Nn7III_"==xWѧOhZo۷ ,@TT|||ЦMv˓[n>>>s=gt!,,Tm?~EIII馛0k,Ǯ:+V[njř3g0gDEE!((O=aWRY̩jz7nD=燘S9oT꾭|ۺuY/4V>A=]t?l,?? rgfEE,Xh_~ؿtU}̙3ѣGhZy_T^^s"::MVIDATAAAѣG-ֳק-ZfͲRvgϝTokۡCRp SwJ2m8ۇ+uk+ٚ11QN:a֬Y{xw~L</":t`MXXN目ذaCkkPظq=g&G}5GV+㱺QGw5Gz9soAdd$&NhM7݄?Дk(gaI~ _Fj`%^I۶9tE?/uʅWʅ_wnO3cDwOr1}*'=l0N':N'&Lbi߾̜9SDD͛';w2YhDD%**J>s9wTVVJ``L0Av-:N&N(aaaRYY)""|󍤧KII$&& 9}ʧ~*""Veʕ-W_Yo2tP߶*ɦ_ rӶǏiӦ9=v2vXٻw>}Zt">>>/?,;ve˖TbY̝|H߾}eɒ%.YSOI+ .OOO9{)X:udee^" QFIRRO?-oZn^I&I׮]/IIIUg]oKKKEVKJJcJ?wSuz׶/ L|`k/wns:v~}sKimgdoL޺2>rd#vze2||ȦMDD䡇ѣGHe5ѲÇS"ίMWvh}g㱺Q8զr7>~jy˗/khڎml_&mce z1+D_Q.eR^Z"e%ERR|UHqZpY \˒mK"EWvcs%;md(g&JG©SȅCDwN9|Mr%{t333t:{7o-Z޵kh4Yfx{{ˁLˬ++?7~ .\ILL̙3@?ns;(aeN<`bi֬iyyycg[nGLL,^F9VsFQ $<2O#99YȥKnTupv͏+Zl]+ڵk%::ZD'4i|7!6l___ٺիWeԨQҮ];]ʕ+W\ʛ|eh4/Rնz-ӧO7٫Yfڽu4 .V/th4ٻwr9Ts{q쎎:=:L:;p\>\s>8zL:|@&O\cy /HttC z)9r86׸r\E\kW;G刻#W+oy`^@~'Yl<''NT}xb0`iWڎ] 5t+ eTmGA'xh0@_~PVY?,eyWB ' m۶F:wuƏ &gA>}Pu#yUTTRRR0bj Z5СF;SL1ÇezN6aÆ!!!`{Ǯu5nnݺXSRY-[ x뭷j]o6jQF]+,,4=Vׯ"###dž L<(..ƩSp DDD[nUۼ h4_~* kϷ|b/wڽFAq[minݺ5T*~WRv봁n{f+wN>Rm7&rTok[ɺ~i̜9>8`wiӦ!//{/ƌN:U6g.^htٵ6liݮ\Z>ݭ#y`8-[D޽m6m0rH"))].?[ s}0aDL8 & &=IONj j=BQ^ }EJ  oEՓ(/A_QCE RhQY_@JC!XaJ_nv+* ({M]vr8 ԑ#G[oaŊxp2كHL2111 ј̃J۷odž 0|CD5f lݺ[l1cL˜;w_ДRj'O?իWK.uJFeW79jv}= All,njz`0`ǎxL >;vDTT/_6-h4h۶mPTطo}Ť?θ+**nݺ޳QDau]p!^~eߖWmOּ:rW>\tmqVU%gc"GYe޽Xf VZZs碲˗/ԩSqiDGGz\ T]\icWkohʶ\N: 6 | pwƷ~D.1ت-\`~~IPh_`\-Dy/m x'ޭ^~1im=ߏ _xEM&Mɓqa"554c^DŽ 0}t̙3FB||鶞p:u :?j#<<صkrrrtRVEJJ `0i& //'ODfǏ#33ƧcepF3fUVc.r*,@U3tPc57xᇑX}vݰwsz7w\XIII̙3cĉwwEAA>#$$$'PuG{ǎ3݁' ]!!!ɓX2eźEEEζxw؁bڴi1}(iΫaܸqxgbٳ1c0tPWƉ'ƺu*=iOѮ];_:v8X>EglKDꭳ:mKii)1{lӇXj^z:f^o3gٳtǣk6ql˵?ߜڹΝ;#22/61"""K:忮qAmتUxzz_vTrerlxEѝ+?_N>*zB.,is%/5)G~^~iG{e֬YҮ];ˢEDDdŊ%""rE ˗HD:uV+󟥢az"5'N?h9sHPPlRfϞ-={4&G,4h4o\|||[o;vH$tsO*IVzRY;tyҥE'1/""s QբhLIQ$::ڴ?@j/bn>"5ϑv$<J%F,OO<`sw-ZVw^cb"5kDFFO>ĥ|:keeesI۶m%00P"ٚ?qCCCeذaҮ];j2`IOO7m]"y'UVjܹzꭈ|  ~rڴ'[׶={HLLm&+WOOOrG>jS.Wq¿>%Gc"GYe-5=ҫW/'fk?[}?PcYVFήMژUfrX](Gj[9+5Gz̙@~{sέ1IkgWDlՍOAԔpOz=rE+^c*A#(+BP\b@.3Q5FAf=+W 88gΜ7\[SRYi|Sma͚5uh_o;z6GE #""RvnQo 7(oTۡVw^|EENq-Zs!rmBVP4hAV( }A}y}0MCN WMcOJ* 5-o"`9zm" B49\AqY j<ڠ6Fnݺ:?{PRY9o"Y ""rbj6Kooox{G $̎xҰo>lܸRgJ* 9MDDDDGU*@T*vZ|+^ZZP#""ϯADDDnRk(**Vu{މ^^QQ=^3=q{;""_D~./ݞ]VK9xȥ\EngBjZbԴwIU9ݎBDDD48x{aB|-zۓ ,^4qUVMB@DDtCcODDtn~,Z&pw}bϘ bARU}ac;"""Y}d#d3Wkl7nO~)5؎Hxrd5zܚ0[n툈~ODDt][vrosfдܜ UގcODDt}߀، `ī/eˑVCY*uS瀈f,j@\ݚ7\ *^3wqpnÄ'ƍpw<#0Z-SI#"""Ko'xFl1/SَF3ʦ VmVoV툈nd"=*ΉKắr<#DDDDDDD>{9]ǥ_=!"""""""cODDPۧ'""o'""RརN'"""""""""mDDDDDDD J?""""""k+?R N!""""""s.?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD ;ސ """"""͒5ODDDDDDp ?1'""""""R8DDDDDDD H)"""""""i`QIENDB`cppcheck-2.7/man/manual-ja.docbook000066400000000000000000002517431417746362400171560ustar00rootroot00000000000000 Cppcheck 1.87 2018-04-23 イントロダクション Cppcheck は C/C++の静的解析ツールです。C/C++ コンパイラやその他の解析ツールとは異なり、シンタックスエラーを検出しません。 その代わりに、Cppcheckは、コンパイラが通常、検出に失敗するような種類のバグを検出します。このプロジェクトのゴールは、擬陽性 0 です。 サポートしているプログラムのソースコードとプラットフォーム: さまざまなコンパイラの拡張構文や、インラインアセンブル等を含む、非標準的なソースコードをチェックできます。 Cppcheck は 最新のC++規格をサポートしている、あらゆるC++コンパイラでコンパイルできるようにしています。 Cppcheck は 十分なCPUパワーとメモリーのある、あらゆるプラットフォームで動作するようにしています。 Cppcheckに限界があることをご理解ください。Cppcheckの報告しているエラーに稀に間違いのあることがあります。また、Cppcheck が検出しないバグが残っていることもあります。 ソフトウェアを注意深くテストすれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。ソフトウェアを注意深く実装すれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。しかし、あなたのソフトウェアを実装するときやテストするときに見逃したバグのいくつかを Cppcheckが検出できるでしょう。 GUIでのはじめ方 GUIの起動
新しいプロジェクト(New Project) 新しプロジェクトのファイルの作成は必要ではありませんが、最初のステップに最適です。ファイル(File)と新しいプロジェクトファイル(New project file)を通じて学べます。
新しいプロジェクト(New Project) - パス(Paths)と定義(Defines) あなたのプロジェクトはどのようなプロジェクトでしょうか。あなたのプロジェクトがVisual Studioのプロジェクトの場合、または(cmake/qbs/等の)コンパイルデータベースqが精製できる場合、あなたはプロジェクトをインポート(import)できます。 そうではない場合には、そのプロジェクトのパスと定義をマニュアルで調整します。次の図は、Visual Studio のプロジェクトファイルをインポートした場合のスクリーンショットです。
新しいプロジェクト(New Project) - プロジェクト(Project) プロジェクトタブ(Project tab)では、ビルドディレクトリ(Cppcheck build dir)を設定しましょう。これはCppcheckが様々な分析する情報を保管するために使用します。プログラム全体の解析、インクリメンタル解析、統計などです。それぞれのプロジェクトは、それぞれのビルドディレクトリを持ちます。次のスクリーンショットはビルドディレクトリをcppcheck-build-dirと設定しています。このパスはプロジェクトファイルからの相対パスです。 あなたは、あなた使用する全てのライブラリーを選択すべきです。次のスクリーンショットではmicrosoft_sal と windowsライブラリーを選択しています。ライブラリーについてはこのマニュアルを参照してして下さい。
新しいプロジェクト(New Project) - アドオン(Addons) ここでは 除外タブ(Exclude)と抑制タブ(Suppressions)をスキップします。これは結果をあとで微調整するために使います。 アドオンタブ(Addons)であなたは別の分析を追加できます。このアドオンにはpythonが必要です。
解析(Analyze) ダイアログのOKボタンをクリックします。解析がすぐに始まります。 全ての警告が有効になり、やや賑やかになります。あなたが注意しない様々な警告があり得ます。これは簡単に修正できます。メッセージを右クリックして、隠す(Hide)または抑制( Suppress)を選びます。メッセージの隠しは永久ではありません。これは次の解析でまた表示されます。メッセージの抑制は、永久です。抑制されたidはプロジェクトファイルに保存されるので、これらは二度と表示されません。
コマンドラインでの始め方
最初のテスト これは単純なソースコードです。 int main() { char a[10]; a[10] = 0; return 0; } このソースコードをfile1.cに保存して次のコマンドを実行します。 cppcheck file1.c cppcheck は次のように出力するでしょう。 Checking file1.c... [file1.c:4]: (error) Array 'a[10]' index 10 out of bounds
フォルダ内の全てのファイルをチェックする 通常、プログラムは多くのソースファイルから構成されます。そして、それら全てをチェックしたいでしょう。Cppcheck は一つのディレクトリ以下の全てのソースファイルをチェックできます。 cppcheck path ここで"path"はディレクトリのパスです。このようにすれば cppcheck はディレクトリ以下の全てのファイルを再帰的にチェックします。 Checking path/file1.cpp... 1/2 files checked 50% done Checking path/file2.cpp... 2/2 files checked 100% done
マニュアルでファイルをチェックまたはプロジェクトファイルの使用 Cppcheckでは、ファイルやパスを指定する事でファイルチェックを指定できます。一方ででプロジェクトファイル(cmake/visual studio)を使用できます。 プロジェクトファイルの使用は早急に始められます、というのもあなたが設定してする項目が少なくなるからです。 マニュアルでのファイルチェックは、解析をより細かく制御できます。 どちらのアプローチが良い結果になるかはわかりません。両方の方法を試して下さい。両方のアプローチを使用するとより多くののバグを見つけられる結果が得られるかもしれません。 以降の章でより詳細を説明します。
チェックからファイルやフォルダを除外する ファイルやフォルダをチェック対象から除外する方法は二つあります。最初の方法は、あなたがチェックしたいファイルやフォルダだけをcppcheckに指定することです。 cppcheck src/a src/b src/asrc/b 以下の全てのファイルだけをチェックします。 第二の方法は、-iオプションと共に除外したいファイルやフォルダを指定することです。次のコマンドではsrc/c以下のファイルをチェックしません。 cppcheck -isrc/c src このオプションは現在--projectオプションと同時に使用できません。また、このオプションが有効なのは、インプットディレクトリが提供するされたときです。複数のディレクトリを無視するためには、-iを複数回使用します。次のコマンドではsrc/b と src/c 以下のファイルをチェックしません。 cppcheck -isrc/b -isrc/c
Severities(厳格度) メッセージのseverities(厳格度)には次のものがあります。: error(エラー) バグが検出されたときに使用します。 warning(警告) 防衛的プログラミングでバグを避けるための提案です。 style コードの可読性の向上に関連した、スタイル関連の指摘(未使用関数、冗長なコードなど) performance コードの高速化のための提案。これらの提案は、一般的な知識に基づいたものでしかありません。このメッセージの修正によって計測できるほど処理速度が向上するかどうかはわかりません。 portability 移植性についての警告。64 bit CPUへの移植性。コンパイラ依存(独自拡張)ソースコードについての警告など。 information 設定上の問題設定を変更している間だけ有効にすることをお勧めします。
メッセージの表示 デフォルトではerrorのメッセージだけを表示します。--enableを使用すると他のチェックを有効にできます。 # warning のメッセージを有効にします。 cppcheck --enable=warning file.c # performanceのメッセージを有効にします。 cppcheck --enable=performance file.c # informationのメッセージを有効にします。 cppcheck --enable=information file.c # 歴史的な理由により --enable=style を指定すると warning, performance, # portability と styleのメッセージを有効にします。古いxml形式を使用しているときには、これらの厳格度を"style"として報告されます。 cppcheck --enable=style file.c # warning と performance のメッセージを有効にします。 cppcheck --enable=warning,performance file.c # unusedFunction のチェックを有効にします。今回は --enable=styleでは有効にできない。 # というのは、これではライブラリではうまく動作しないからです。 cppcheck --enable=unusedFunction file.c # 全てのメッセージを有効にします。 cppcheck --enable=all --enable=unusedFunctionはプログラム全体をチェックするときにだけ有効にしてください。また、--enable=allもプログラム全体をチェックするときにだけ有効にしてください。というのは、unusedFunction チェックは、関数が呼び出されなかったときに警告するチェックだからです。関数呼び出しがチェック範囲にみつからなかったという可能性のノイズになります。
疑いのあるチェック Cppcheckはデフォルトで解析に疑いのない場合にだけエラーメッセージを表示します。しかし、--inconclusiveオプションを使用すると、解析に疑いのある場合であってもエラーメッセージを表示します。 cppcheck --inconclusive path これは、もちろん、実際に問題がないものに対しても、警告することになります。このオプションは、疑いのある警告を表示してもよい場合に限り、使用してください。
結果をファイルに保存 多くの場合、チェックの結果をファイルに保存したいと考えるでしょう。通常のシェルのリダイレクション機能を使って、エラー出力をファイルに保存することができます。 cppcheck file1.c 2> err.txt
マルチスレッドチェック オプションの-j を使用してスレッド数を指定することができます。例えば、4スレッドを使ってフォルダ以下の全てのファイルをチェックする場合は次のように実行します。 cppcheck -j 4 path このチェックでは未使用関数の検出(unusedFunction checking)は無効になることに注意してください。
プラットフォーム あなたがターゲットとするプラットフォームの設定を使用すべきです。 デフォルトで、Cppcheckはネイティブのプラットフォームの設定を使用しますので、あなたのソースコードがローカルの環境でコンパイルし実行する場合には正常に動作するでしょう。 Cppcheck にはビルトインのプラットフォーム設定として、unixwindowsをターゲットにしたものがあります。コマンドラインオプションの--platformを使ってプラットフォーム設定を指定できます。 XMLファイルで自身のプラットフォームにあった設定ファイルを作成することもできます。ここに例をあげます。: <?xml version="1"?> <platform> <char_bit>8</char_bit> <default-sign>signed</default-sign> <sizeof> <short>2</short> <int>4</int> <long>4</long> <long-long>8</long-long> <float>4</float> <double>8</double> <long-double>12</long-double> <pointer>4</pointer> <size_t>4</size_t> <wchar_t>2</wchar_t> </sizeof> </platform>
Project(プロジェクト) CMakeやVisual Studioを使っているとき、あなたは--projectを使ってプロジェクトを解析できます。 これでカンタンにチェックでき、結果も得られます。あなたに必要な設定項目はありません。しかしこれが最も良い結果を得る方法とは限りません。私たちは、このプロジェクトファイルを利用する方法と、--projectを利用しない方法を試してよいオプションを選ぶようにお勧めします。
CMake Cppcheckはコンパイルデータベースを理解します。あなたはこれをCMakeで生成できます。 例: $ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . compile_commands.jsonファイルが現在のディレクトリに生成されます。 それからCppcheckをこのように実行します。: $ cppcheck --project=compile_commands.json
Visual Studio あなたは個々にのプロジェクトファイル(*.vcxproj)でCppcheckを実行できますし、ソルーション全体(*.sln)でも実行できます。 # run cppcheck on a whole solution $ cppcheck --project=foobar.sln # run cppcheck on a individual project $ cppcheck --project=foobar.vcxproj Visual Studio内でcppcheckを実行するための、Visual Studioプラグインもあります。
プリプロセッサの設定 あなたが --projectを使用した場合、Cppcheckはプロジェクトファイルからプリプロセッサーの設定を読み取ります。 そうでなければ、あなたはインクルードパスやディレクティブを設定したくなるでしょう。
ディレクティブ ここに2つの設定があるファイルがあります(Aが定義された場合と定義されていない場合): #ifdef A x = y; #else x = z; #endif Cppcheckはデフォルトでプリプロセッサのデファインのコンパイルスイッチ設定の組み合わせを全てチェックします。(ただし、これらのうち #error を除く)そのため上のコードは、Aが定義された場合とAが定義されていない場合の両方を解析します。 これを変更するには -D を使います。また -D を使用した場合、cppcheckは与えられたコンパイルスイッチだけが有効でその他は設定されていないとしてチェックします。これは、コンパイラのように動作します。また、 --force--max-configs を使用すると、コンパイルスイッチの組み合わせの上限を上書きしてチェックすることができます。 # 全てのコンパイルスイッチの組み合わせをチェックする。 cppcheck file.c # Aのコンパイルスイッチが有効になっている場合の組み合わせをチェックする cppcheck -DA file.c # check all configurations when macro A is defined cppcheck -DA --force file.c また、もう一つのオプションに-U があります。これはシンボルのundefとなります。使用例: cppcheck -UX file.c これはXが定義されていないことを意味します。Cppcheck は Xが定義されている組み合わせをチェックしません。
インクルードパス指定 インクルードパスを追加するには-Iオプションに続けてパスを指定します。 Cppcheckのプリプロセッサは基本的に他のプリプロセッサと同様にインクルードを扱います。しかし、その他のプリプロセッサはヘッダファイルが見つからない場合に停止するのとは違って、cppcheckはただ単に、メッセージ情報を表示してソースコードの解析を続けます。 cppcheckは常にソースコード全体を確認する必要がないので、このような仕様になっています。実際に、全てのインクルードパスを与えないことを推奨しています。もちろん、クラスのメンバーの実装を確認した上でクラスの宣言をCppcheckでチェックするのは有用ではありますが、標準ライブラリのヘッダーをCppcheckに確認させるのは有用ではありません。というのは、チェックにかかる時間が長くなり、あまりよくない結果が表示されるからです。そのような場合、.cfg ファイル (後述します)によってcppcheckに関数や型の実装の情報を提供する方がよいでしょう。
XML出力 Cppcheckは出力をXML形式に変更できます。--xml オプションでフォーマットを指定します。 ファイルをチェックし、XML形式で出力するコマンドのサンプルです。: cppcheck --xml file1.cpp これが出力例です。: <?xml version="1.0" encoding="UTF-8"?> <results version="2"> <cppcheck version="1.66"> <errors> <error id="someError" severity="error" msg="short error text" verbose="long error text" inconclusive="true" cwe="312"> <location file0="file.c" file="file.h" line="1"/> </error> </errors> </results>
<error> 要素 それぞれのエラーは<error>要素に記載されます。属性: id エラーのidこれは、妥当なシンボル名です。 severity 以下のいずれかです: error, warning, style, performance, portability, information msg 短い形式のエラーメッセージ verbose 長い形式のエラーメッセージ inconclusive この属性は、メッセージに疑いのある場合にのみ使用されます。 cwe メッセージのCWE ID。この属性は、メッセージのCWE IDが判明している場合のみ使用されます。
<location>要素 エラーに関連する全ての位置情報は<location> 要素内にリストアップされます。主要な位置は、リストの最初の要素になります。 属性: file ファイル名相対パスまたは絶対パスのどちらかです。 file0 ソースファイルの名前(オプション) line info オプションの、それぞれの位置につiての短い情報
出力の形式の変更 もし、テンプレートを使用して、出力の形式を変更することができます。
事前定義した出力フォーマット Visual Studioに互換性のある形式が必要な場合には、--template=vsを使用します。 cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. gccに互換性のある出力が必要な場合には、--template=gccを使用します。: cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[2] = 0; ^
ユーザー定義出力形式(1行) 自分で自身でパターンを作成できます。例えば古いgcc のよuな出力フォーマットで警告メッセージを出力してほしい場合次のように指定します。: cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. コンマ区切りフォーマット: cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds.
ユーザー定義出力形式(複数行) 多くの警告は、複数の位置を指定します。サンプルコード: void f(int *p) { *p = 3; // line 3 } int main() { int *p = 0; // line 8 f(p); // line 9 return 0; } 3行目でヌルポインタのデリファレンスの可能性があります。Cppcheckは追加の位置情報を表示してその結論がどこから発生したかを示すことができます。そのためには、コマンドラインで--template--template-locationの両方を使用する必要があります。 サンプルコマンド: cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c cppcheck は次のように出力します。 Checking multiline.c ... multiline.c:3: warning: Possible null pointer dereference: p *p = 3; ^ multiline.c:8: note: Assignment 'p=0', assigned value is 0 int *p = 0; ^ multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 f(p); ^ multiline.c:3: note: Null pointer dereference *p = 3; ^ この警告の最初の行は--template で指定したフォーマットです。 この警告の残りの行は--template-locationで指定したフォーマットです。
--templateで指定するフォーマット --template では以下の要素が利用できます。: {file} ファイル名 {line} 行数 {column} カラム番号 {callstack} 全ての位置。それぞれの位置は[{file}:{line}]のフォーマットで記載され、また->で位置を区切ります。例えば次のようになります。: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] {inconclusive:text} 警告が確定的でない場合のメッセージを表示します。このメッセージは含まれていない場合もある、任意のテキストです。サンプル: {inconclusive:inconclusive,} {severity} エラー/警告/スタイル/性能/移植性/情報 {message} 警告メッセージ {id} 警告id {code} 実際のコード \t タブ \n 改行 \r キャリッジリターン
--template-location で指定するフォーマット --template-locationでは以下の要素が利用できます。: {file} ファイル名 {line} 行数 {column} カラム番号 {info} 現在位置についての情報メッセージ {code} 実際のコード \t タブ \t 改行 \r キャリッジリターン
Misra CppcheckはMISRA C 2012 向けのチェッカのアドオンを持っています。
要求事項 必要なもの: Python (2系 または 3系) MISRA C 2012の PDFこのPDFはhttp://www.misra.org.ukで購入できます (15-20 ポンド)
MISRA テキストファイル MISRAルールテキストの公開は禁止されています。そのためMISRAルールテキストはこのアドオンから直接利用できません。代わりにこのアドオンはテキストファイルからルールのテキストを読み込みます。MISRA PDFの ”Appendix A Summary of guidelines"のテキストをコピーペーストした場合、それがルールのテキストになります。 もしあなたがxpdfを持っているなら、テキストファイルはコマンドラインから簡単に生成できます。 (pdftotextxpdfに含まれています。): pdftotext misra-c-2012.pdf output.txt この出力は100%完璧であるとは限りません。少し手直しする必要があることもあります。 その他のpdfからtextに変換するソフトでもうまくいくでしょう。 テキストファイルをマニュアルで作成してするには、MISRA PDFの Appendix A "Summary of guidelines" をコピーペーストします。フォーマット: Appendix A Summary of guidelines Rule 1.1 Rule text Rule 1.2 Rule text ... あなたが無効にしたいルールは、ルールテキストがなくても構いません。ルールテキストのないルールはアドオンによって抑制されます。
出力の抑制 ある種のエラーをフィルタリングしたい場合、出力を抑制することができます。
プレーンテキスト抑制 エラーの種類によって出力を抑制することができます。つぎのいずれかの形式で出力を抑制します。: [error id]:[filename]:[line] [error id]:[filename2] [error id] このerror id は抑制したいエラーのidです。このエラーのIDを簡単に調べるには、--xmlオプションをコマンドラインで与えます。そのXML出力から、idの文字列が取得できます。このエラーのIDに*を指定して全ての種類のメッセージを抑制することができます。(これは指定したファイルに限ることができます。) またfilenameにはワイルドキャラクターである、* または ?を含めることができます。前者には全ての文字列にマッチし、後者は任意の一文字にマッチします。またWindowsを含む全てのOSで、パス区切りに"/" を使うことをお勧めします。
コマンドライン抑制 --suppress=のコマンドラインオプションを使用して、コマンドラインで抑制を指定することができます。例: cppcheck --suppress=memleak:src/file1.cpp src/
ファイルで抑制リストを指定 また、抑制ファイルを作成することもできます。例: // src/file1.cppのmemleak と exceptNew の エラーを抑制 memleak:src/file1.cpp exceptNew:src/file1.cpp // 全てのファイルのuninitvarエラーを抑制する。 uninitvar 空行やコメント行を抑制ファイルに記載することができます。 そして、この抑制ファイルは次のようにして使用します。: cppcheck --suppressions-list=suppressions.txt src/
XML 抑制 XMLファイルで抑制を指定できます。サンプルファイル: <?xml version="1.0"?> <suppressions> <suppression> <id>uninitvar</id> <fileName>src/file1.c</fileName> <lineNumber>10</lineNumber> <symbolName>var</symbolName> </suppression> </suppressions> このXMLフォーマットは拡張可能であり、将来さらなる属性を加えるかもしれません。
インライン出力抑制 エラー出力の抑制をソースコード中に直接、コメントの形で記載することもできます。このコメントには特別なキーワードを含めて記載します。ただし、インライン出力を抑制するコメントをソースコードに追加すると、ソースコードの可読性が少し悪くなってしまうかもしれません。 このソースコードは通常エラメッセージを出力する例です。: void f() { char arr[5]; arr[10] = 0; } 前のソースコードに対する出力は次のようになります。: # cppcheck test.c Checking test.c... [test.c:3]: (error) Array 'arr[5]' index 10 out of bounds このエラーメッセージを抑制するには次のようなコメントを追加します。: void f() { char arr[5]; // cppcheck-suppress arrayIndexOutOfBounds arr[10] = 0; } これで、--inline-suppr オプションの準備ができました。次のようにcppcheckを起動するとエラーが抑制されます。: cppcheck --inline-suppr test.c 特定のシンボルにのみ適用するインライン抑制を指定できます。: // cppcheck-suppress arrayIndexOutOfBounds symbolName=arr 抑制のためにコメントを書きます。; や // を使って開始点を指定できます。 // cppcheck-suppress arrayIndexOutOfBounds ; some comment // cppcheck-suppress arrayIndexOutOfBounds // some comment
ライブラリ設定 WinAPI, POSIX, gtk, Qtなど他の外部のライブラリを使用した場合、Cppcheckは外部の関数がどのようなものであるかがわかりません。Cppcheck はそのため、メモリリークやバッファオーバーフロー、ヌルポインタのデリファレンスの可能性といったさまざまな問題が検出できません。これを解決するには設定ファイル(.cfg file)を使用します。 Cppcheckはいくつかのライブラリ用の設定を持っています。これらは次のようにしてロードできます。cppcheckは C または C++言語用の標準ライブラリの設定 std.cfgはいつもロードします。ご注意ください。もしあなたが有名なライブラリの設定ファイルを作成した場合や更新した場合には、私達のサイトにアップロードしてくれると非常に助かります。
カスタム設定ファイル(.cfg file)の使用 あなたのプロジェクト専用の設定ファイルを作成し、使用することができます。そのためには、--check-library--enable=information を使用して設定のためのヒントを入手します。 設定ファイルの編集に、Library Editorの使用をお勧めします、これはCppcheck GUIに含まれています。これはViewメニューで使用できます。すべての設定がこのマニュアルに載っていません。 この設定ファイル.cfgのフォーマットに質問がある場合、フォーラム(http://sourceforge.net/p/cppcheck/discussion/)で質問してください。 コマンドラインのcppcheck はカスタマイズした設定ファイル(.cfg files)を作業パスから読み込もうとします。作業パスはcppcheckを実行しているパスですでそこに設定ファイルがあると考えます。 GUIのcppcheckはプロジェクトのファイルパスから設定ファイルを読み込もうとします。カスタマイズした設定ファイル(.cfg file)は プロジェクトファイルの編集 ダイアログで確認できます。このタイアログを表示させるにはファイル メニューから開いてください。
メモリーリソースのリーク Cppcheck はリークのチェックが調整できます。言い換えれば、あなたはメモリーやリソースを割り当てる関数またはその割り当てを回収する関数を指定できます。
alloc と dealloc ここにサンプルのプログラムがあります。: void test() { HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); } 上のコード例はリソースリークの欠陥があります。 - CreatePen() は WinAPI 関数でpenを作成します。しかし、Cppcheckは関数からの返り値が解放されていなければならないと仮定しません。そのためエラーメッセージは表示されません。: # cppcheck pen1.c Checking pen1.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: # cppcheck --library=windows.cfg pen1.c Checking pen1.c... [pen1.c:3]: (error) Resource leak: pen これが最小限のwindows.cfg ファイルです: <?xml version="1.0"?> <def> <resource> <alloc>CreatePen</alloc> <dealloc>DeleteObject</dealloc> </resource> </def> このアロケート関数とデアロケート関数はグループにまとめられています。それぞれのグループは<resource><memory> タグ中で定義されており、その<dealloc>関数によって特定されます。これは、<dealloc>タグでオーバーラップしたグループはマージされます。
leak-ignore とuse しばしば、割り当てられたポインタを関数に渡すことがあります。例: void test() { char *p = malloc(100); dostuff(p); } もし設定ファイルがなく、Cppcheckがdostuffの仕様を把握していなければ、Cppcheckはdostuffがメモリーについて配慮しており、メモリーリークは発生しないと仮定します。 dostuffがメモリーについて配慮せず、解放などを行なっていないことを指定するためには、leak-ignore<function> タグ中で使います。: <?xml version="1.0"?> <def> <function name="dostuff"> <leak-ignore/> <arg nr="1"/> </function> </def> これとは逆にdostuffがメモリーについて配慮している場合には次のように設定します。: <?xml version="1.0"?> <def> <memory> <dealloc>free</dealloc> <use>dostuff</use> </memory> </def> なお、この<use>の設定は論理的に全く無意味です。この設定がない場合でも同じエラーが表示されます。これは--check-libraryのinformationメッセージを減らすために使用します。
関数の動作 関数の動作や関数の使用方法を指定するのに、<function>タグが使えます。関数は、その名前によって特定されます。この名前は、name 属性とその引数によって指定されます。この名前はコンマで区切られた関数名のリストです。名前空間やクラス中の関数の場合には、完全修飾名で指定されます。例: <function name="memcpy,std::memcpy">もしテンプレート関数がある場合、インスタンス化した名前を提供してします。<function name="dostuff<int>">.
関数引き数 関数がとる引数は、<arg>タグで指定できます。引数のそれぞれは、引数の順番(1始まり)をnr属性で示します。nr="any" は任意の引き数を表します。また、nr="variadic"は可変長引数を表します。オプション引数は、デフォルト値で指定します。: default="value". それぞれの引数に対する設定は、全ての引数に対する指定を上書きします。
非ブール ここで誤った比較のあるサンプルプログラムがあります。: void test() { if (MemCmp(buffer1, buffer2, 1024==0)) {} } Cppcheckは、この関数にブール値を渡してよいと仮定します。: # cppcheck notbool.c Checking notbool.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: # cppcheck --library=notbool.cfg notbool.c Checking notbool.c... [notbool.c:5]: (error) Invalid MemCmp() argument nr 3. 非ブール値が求められています。 ここで最小のnotbool.cfgを用意しました。 <?xml version="1.0"?> <def> <function name="MemCmp"> <arg nr="1"/> <arg nr="2"/> <arg nr="3"> <not-bool/> </arg> </function> </def>
未初期化メモリ ここにサンプルのプログラムがあります。: void test() { char buffer1[1024]; char buffer2[1024]; CopyMemory(buffer1, buffer2, 1024); } このプログラムのバグは buffer2 が初期化されていないことです。CopyMemory 関数の第二引数は初期化されている必要があります。しかし、Cppcheckは関数に未初期化の変数を渡してもよいと仮定しています。: # cppcheck uninit.c Checking uninit.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: # cppcheck --library=windows.cfg uninit.c Checking uninit.c... [uninit.c:5]: (error) Uninitialized variable: buffer2 注意:これは、ポインタが示すメモリ領域が初期化されていなければならないことを意味しています。 これが最小限のwindows.cfgファイルです。: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="1"/> <arg nr="2"> <not-uninit/> </arg> <arg nr="3"/> </function> </def>
ヌルポインタ Cppcheckは、関数にヌルポインタを渡してもよいと仮定しています。ここにサンプルのプログラムがあります。: void test() { CopyMemory(NULL, NULL, 1024); } MSDNの文書はこれが問題あるかないかを明らかにしていません。しかし、ここでは問題ありと仮定します。Cppcheck は関数にヌルポインタを渡してもよいと仮定していますので、エラーを出力しません。: # cppcheck null.c Checking null.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: cppcheck --library=windows.cfg null.c Checking null.c... [null.c:3]: (error) Null pointer dereference 注意:<not-uninit>は値について意味しています。初期化されていないメモリが関数に渡されています。 これが最小限のwindows.cfg ファイルです: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="1"> <not-null/> </arg> <arg nr="2"/> <arg nr="3"/> </function> </def>
フォーマット文字列 フォーマット文字列を扱う関数を定義できます。例: void test() { do_something("%i %i\n", 1024); } これについてもエラーは報告されません。: # cppcheck formatstring.c Checking formatstring.c... 引数がフォーマット文字列であることを出力する設定ファイルが作成できます。設定ファイルの例です。: <?xml version="1.0"?> <def> <function name="do_something"> <formatstr type="printf"/> <arg nr="1"> <formatstr/> </arg> </function> </def>これで、Cppcheckはエラーを報告できるようになりました。: cppcheck --library=test.cfg formatstring.c Checking formatstring.c... [formatstring.c:3]: (error) do_something format string requires 2 parameters but only 1 is given. このフォーマット文字列のtype属性は次のどちらかになります。: printf - printf のルールに従うフォーマット文字列 scanf - scanf のルールに従うフォーマット文字列
値の範囲 有効な値の範囲が定義できます。想像してください。: void test() { do_something(1024); } これについてもエラーは報告されません。: # cppcheck valuerange.c Checking valuerange.c... 1024 が 範囲外の値であることを出力する設定ファイルが作成できます。設定ファイルの例です。: <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <valid>0:1023</valid> </arg> </function> </def>これで、Cppcheckはエラーを報告できるようになりました。: cppcheck --library=test.cfg range.c Checking range.c... [range.c:3]: (error) Invalid do_something() argument nr 1. この値は1024ですが、妥当な値は0から1023までです。 validの要素で次のような表現が利用できます。: 0,3,5 => 0, 3 それに 5 だけが有効な値です。 -10:20 => -10 から 20 までの値(両端含む)が有効な値です。 :0 => 0または0未満の値が有効な値です。 0: => 0または0以上の値が有効な値です。 0,2:32 => 0 または2から32までの値(両端含む)が有効な値です。 -1.5:5.6 => -1.5 から 5.6 までの値(両端含む)が有効な値です。
最小サイズ いくつかの関数はバッファーを引数にとります。バッファの最小サイズを指定することができます。(要素数ではなくバイト数です。)想像してください。: void test() { char str[5]; do_something(str,"12345"); } これについてもエラーは報告されません。: # cppcheck minsize.c Checking minsize.c... 設定ファイルで、例えば、引数1のバッファのサイズが引数2の文字列長より大きくなればならないと警告するような設定ファイルを作成できます。例を挙げます。: <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <minsize type="strlen" arg="2"/> </arg> <arg nr="2"/> </function> </def>これで、Cppcheckはこのエラーを報告できるようになりました。: cppcheck --library=1.cfg minsize.c Checking minsize.c... [minsize.c:4]: (error) Buffer is accessed out of bounds: str minsizes はいくつかの種類があります。: strlen バッファーのサイズが、その他の引数の文字列長より大きくなければなりません。例: std.cfg のstrcpyの設定を参照してください。 argvalue バッファーのサイズがその他の引数の値より大きくなればなりません。例: std.cfg のmemsetの設定を参照してください。 sizeof バッファーのサイズがその他の引数のバッファーのサイズより大きくなればなりません。例:posix.cfgのmemccpyの設定をみてください。 mul バッファーのサイズがその他の2つの引数の値の積より大きくなればなりません。典型的な使用例としては、一つの引数が構造体などの要素のサイズを指定し、もうひとつの引数が要素の個数を定義するような場合です。例: std.cfg のfreadの設定を参照してください
strz これを指定すると、数が0終端文字列でなければならないということができます。 <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <strz/> </arg> </function> </def>
noreturn Cppcheck はこの関数がいつも値を返すとは仮定していません。ここにサンプルのプログラムがあります。: void test(int x) { int data, buffer[1024]; if (x == 1) data = 123; else ZeroMemory(buffer, sizeof(buffer)); buffer[0] = data; // <- error: xが1でないとき初期化されていない } 理屈の上では、ZeroMemoryがプログラムを終了させてもバグはありません。そのため Cppcheckはエラーを報告しません。: # cppcheck noreturn.c Checking noreturn.c... しかし、--check-library--enable=informationをつかうとエラーが出力されます。: # cppcheck --check-library --enable=information noreturn.c Checking noreturn.c... [noreturn.c:7]: (information) --check-library: Function ZeroMemory() should have <noreturn> configuration もし適切な windows.cfg が提供されていましたら、このバグは検出されます。: # cppcheck --library=windows.cfg noreturn.c Checking noreturn.c... [noreturn.c:8]: (error) Uninitialized variable: data これが最小限のwindows.cfg ファイルです: <?xml version="1.0"?> <def> <function name="ZeroMemory"> <noreturn>false</noreturn> <arg nr="1"/> <arg nr="2"/> </function> </def>
use-retval 他になにも指定されていない限り、cppcheckは関数が返り値を無視していても問題ないと仮定します。: bool test(const char* a, const char* b) { strcmp(a, b); // <- bug: strcmp の呼び出しは副作用を持ちませんが返り値を無視している。 return true; } strcmp が副作用を持つ場合、パラメータが関数に渡されている結果を無視しても問題はなく、このような仮定は正しいといえます。: # cppcheck useretval.c Checking useretval.c... もし適切なlib.cfg が提供されていましたら、このバグは検出されます。: # cppcheck --library=lib.cfg --enable=warning useretval.c Checking useretval.c... [useretval.c:3]: (warning) Return value of function strcmp() is not used. これが最小限のlib.cfg ファイルです。: <?xml version="1.0"?> <def> <function name="strcmp"> <use-retval/> <arg nr="1"/> <arg nr="2"/> </function> </def>
pure関数(pure)とconst関数 これらは、GCC関数属性のpureとconstに対応します。 pure関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数とグローバル変数によってのみ決まります。 const関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数によってのみ決まります。 ここにサンプルのプログラムがあります。: void f(int x) { if (calculate(x) == 213) { } else if (calculate(x) == 213) { // 到達不能コード } } もしcalculate() がconst関数であれば、calculate(x)は両方の条件で同じ値を返します。というのも、同じパラメータを引数にしているからです。 Cppcheck は通常、その結果が異なると仮定するため、Cppcheckはこのコード例に警告を出しません。: # cppcheck const.c Checking const.c... もし適切なconst.cfg が提供されていましたら、このバグは検出されます。: # cppcheck --enable=style --library=const const.c Checking const.c... [const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. これが最小限のconst.cfg ファイルです。: <?xml version="1.0"?> <def> <function name="calculate"> <const/> <arg nr="1"/> </function> </def>
関数strcpyの設定例 標準関数のstrcpyのための適切な設定は次のようになる。: <function name="strcpy"> <leak-ignore/> <noreturn>false</noreturn> <arg nr="1"> <not-null/> </arg> <arg nr="2"> <not-null/> <not-uninit/> <strz/> </arg> </function> この<leak-ignore/> は、リークチェック中に関数呼び出しを無視するように、Cppcheckに伝えます。この関数は、割り当てられたメモリを解放しないことを意味しています。 この<noreturn> は、この関数が、返り値を返すかどうかをCppchecに伝えます。 この関数は第一引数にポインタを取ります。しかしこのポインタは、ヌルポインタであってはなりません。というのは<not-null>が使用されているからです。 この関数は第二引数にポインタを取ります。このポインタはヌルポインタであってはなりません。また、このポインタは初期化されたデータを指していなければなりません。<not-null><not-uninit> は正しく使用されています。さらにいえば、このポインタは0終端文字列(zero-terminated string)でなければなりません。そのため<strz>が使用されています。
define ライブラリはマクロプリプロセッサのdefineを使用することができます。例: <?xml version="1.0"?> <def> <define name="NULL_VALUE" value="0"/> </def> プリプロセッサの段階でソースコード中に "NULL_VALUE" が現れるごとに、"0"で置き換えます。
podtype 多くのソースコードで、プラットフォームに依存しない型をtypedefによって定義しています。"podtype"のタグによって、cppcheckがこれらをサポートするために必要な情報を提供できます。このような情報のない場合、cppcheckは次の例でみるような "uint16_t" 型を理解できません。 void test() { uint16_t a; } そのため、未使用変数である、'a'が未使用であるとのメッセージが表示されません。 # cppcheck --enable=style unusedvar.cpp Checking unusedvar.cpp... もし uint16_t が以下のように定義されていた場合、結果にメッセージが反映されます。 <?xml version="1.0"?> <def> <podtype name="uint16_t" sign="u" size="2"/> </def> 型のサイズはバイトサイズで指定します。符号の "sign" 属性は 符号ありの "s" か 符号無し "u" のどちらかです。これらの属性はオプションです。このライブラリを使用しますと、cppcheckはメッセージを表示できるようになります。 # cppcheck --library=lib.cfg --enable=style unusedvar.cpp Checking unusedvar.cpp... [unusedvar.cpp:2]: (style) Unused variable: a
コンテナ(container) C++ ライブラリの多くや STL 自身は、非常によく似た機能性をもつコンテナを提供します。ライブラリによってその動作をcppcheckに伝えられます。それぞれのコンテナの設定にはユニークなIDが必要とします。コンテナの設定には、startPatternを加えることができます(オプション)。この startPatternはToken::Match パターンとendPattern に有効でなけばなりません。また、このendPatternはリンクしているトークンと比較されるものです。オブション属性の"inherits"は事前に定義されたコンテナのIDをとります。 <container>タグの内部で、<size>、<access>、<other>を選択して使用して関数を定義できます。これらのタグはそれぞれ、"resize" やその結果を与えるような動作を指定することができます。その例 "end-iterator"を示します。 次の例は、std::vectorの為の定義を示しています。std::vectorは"stdContainer"の定義に基づいていますが、ここには表示していません。: <?xml version="1.0"?> <def> <container id="stdVector" startPattern="std :: vector &lt;" inherits="stdContainer"> <size> <function name="push_back" action="push"/> <function name="pop_back" action="pop"/> </size> <access indexOperator="array-like"> <function name="at" yields="at_index"/> <function name="front" yields="item"/> <function name="back" yields="item"/> </access> </container> </def>
ルール(Rules) 正規表現を使用して、ユーザーがルール(rule)を定義することができます。 これらのカスタムルールは、ソースコードを高度に分析した結果を使用することができません。しかしソースコード中の非常にシンプルなパターンについて簡単にルールを作成することができます。 ルールの作成を始めるには次の関連記事を参照してください。: http://sourceforge.net/projects/cppcheck/files/Articles/ ルールのファイルフォーマットは次のとおりです。: <?xml version="1.0"?> <rule> <tokenlist>LIST</tokenlist> <pattern>PATTERN</pattern> <message> <id>ID</id> <severity>SEVERITY</severity> <summary>SUMMARY</summary> </message> </rule> patternタグ中にCDATAを含めた場合、XMLに干渉する可能性がありますので使用時はご注意ください。: <![CDATA[some<strange>pattern]]>
<tokenlist> この<tokenlist> 要素はオプションです。この要素がある場合、どのトークンをチェックするかを指示することができます。このLISTdefine, raw, normal , simpleのいずれかです。 define #define プリプロセッサの記述をチェックするために使用します。 raw プリプロセッサの出力をチェックするために使用します。 normal normal のトークンリストをチェックするために使用します。ソースコードをある程度、単純化した結果をチェックすることになります。 simple 単純なトークンリストをチェックするために使用します。ソースコードを完全に単純化した結果をチェックすることになります。ほとんどの Cppcheckのチェックには、この 単純ばトークンリストを使用します。 もし<tokenlist>要素を省略した場合、simple が使用されます。
<pattern> このPATTERN にはPerlの正規表現と互換性のある正規表現 PCREを指定します。
<id> この ID にはユーザーが定義した message idを指定します。
<severity> このSEVERITYにはCppcheck の厳格度(severities)である、次のいずれかを指定します。: information, performance, portability, style, warning,error
<summary> オプションです。メッセージのサマリーです。もしこのsummaryトークンが指定されていなければ、マッチしたトークンが出力されます。
Cppcheck アドオン Cppcheckのアドオンは、個別のスクリプトや個別のプログラムとして実装されています。Cppcheckのアドオンを使用すると次のような利点があります。 洗練された分析の結果を使用した個別の、外部チェックを追加できます。 ソースコードが可視化できます。 その他
Cppcheckアドオンの使用方法 現在、アドオンを使用するには2段階の操作が必要です。: Cppcheckを実行し、ダンプファイルを生成します。 アドオンでダンプファイルを処理します。 --dumpフラグを使用するとダンプファイルを生成できます。foo/ フォルダ以下の全てのソースファイルからダンプファイルを生成するには次のようにします。 cppcheck --dump foo/ foo/ フォルダ以下の全てのダンプファイルをアドオンで処理するには次のようにします。 python addon.py foo/*.dump
Cppcheckアドオンの見つけ方 ダウンロードできる、アドオンがいくつかあります。 Cppcheck プロジェクトはいくつかのアドオンを以下の場所で提供しています。: http://github.com/danmar/cppcheck/blob/master/addons ublinterは規格で定義されていない未定義動作に注力した"lint"です。: http://github.com/danmar/ublinter あなたのアドオンの情報をご紹介ください。(商用、フリーを問いません。)
Cppcheck アドオンの作成 Cppcheck は XML形式でダンプファイルを生成できます。このファイルには以下のようなものが含まれています。: トークンリスト(Token list) シンタックスツリー(Syntax trees) シンボルデータベース(関数、クラス、変数、スコープ) 既知の値(value flow analysis) Cppcheckはアドオンを直接実行することはできません。直接実行するためにインターフェースはありません。これは、次のような制限がないことを意味します。: アドオンを作成しリリースする際に、どのようなライセンスでも適用できます。 アドオンの作成に、どのようなスクリプト言語やプログラミング言語で作成できます。 アドオン作成者がユーザーインターフェースと出力を決定できます。 警告の生成以外の目的にもアドオン使用できます。 アドオン作成者の利便性のために、Cppcheck プロジェクトは PythonからCppcheckのデータにアクセスするための cppcheckdata.pyを提供しています。cppcheckdata.pyの使用はオプションです。
使用例1 - 全トークンの表示 Script: import sys import cppcheckdata def printtokens(data): for token in data.tokenlist: print(token.str) for arg in sys.argv[1:]: printtokens(cppcheckdata.parse(arg))
使用例2 - 全関数リストアップ Script: import sys import cppcheckdata def printfunctions(data): for scope in data.scopes: if scope.type == 'Function': print(scope.className) for arg in sys.argv[1:]: printfunctions(cppcheckdata.parse(arg))
使用例 3 - 全クラスリストアップ Script: import sys import cppcheckdata def printclasses(data): for scope in data.scopes: if scope.type == 'Class': print(scope.className) for arg in sys.argv[1:]: printfunctions(cppcheckdata.parse(arg))
HTML 形式での報告 cppcheckのXML出力をHTML形式に変更できます。これを利用するには、Python と pygments module (http://pygments.org/) が必要です。Cppcheckのソースツリーにhtmlreportというフォルダがあります。このフォルダには、CppcheckのXMLファイルをHTML出力に変換するスクリプトがあります。 このコマンドでヘルプ画面を生成するには次のように実行します。 htmlreport/cppcheck-htmlreport -h 出力画面には次の内容が表示されます。: Usage: cppcheck-htmlreport [options] Options: -h, --help show this help message and exit --file=FILE The cppcheck xml output file to read defects from. 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. 使用例: ./cppcheck gui/test.cpp --xml 2> err.xml htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. グラフィカルインターフェースGUI
イントロダクション Cppcheck GUIが利用できます。 メイン画面は、このソフトを起動時に表示されます。
ソースコードのチェック Checkメニューを使用します。
結果の確認 結果はリスト表示されます。 View メニューを操作して、メッセージの種類毎に表示/非表示を切り替えできます。 結果をXML ファイルに保存して、後で確認できます。Save results to fileOpen XMLを参照してください。
設定 Languageメニューからいつでも使用言語を変更できます。 設定は、 Edit Preferences で変更できます。
プロジェクトファイル プロジェクトファイルは、プロジェクト固有の設定を保存するのに使用します。固有の設定には次のものがあります。: インクルードパス プリプロセッサのdefine このマニュアルの3 章にあるように、全てのコンパイルスイッチの組み合わせをチェックします。コンパイルスイッチの組み合わせを制限したい場合にだけ、プリプロセッサのdefineを指定してください。
cppcheck-2.7/man/manual-style.tex000066400000000000000000000013051417746362400170670ustar00rootroot00000000000000\usepackage{titlesec} \usepackage{fancyvrb,newverbs,xcolor} \usepackage{xcolor} \titleformat{\chapter}[display] {\normalfont\huge\bfseries}{\chaptertitlename\ \thechapter}{20pt}{\Huge} \titlespacing*{\chapter}{0pt}{-20pt}{20pt} \definecolor{Light}{HTML}{EEEEEE} \let\oldtexttt\texttt \renewcommand{\texttt}[1]{ \colorbox{Light}{\oldtexttt{#1}} } \lstset{ basicstyle=\ttfamily, backgroundcolor=\color{Light}, xleftmargin=10pt, frame=lrtb, framesep=10pt, framerule=0pt, showspaces=false, showstringspaces=false, showtabs=false, tabsize=2, captionpos=b, breaklines=true, breakatwhitespace=true, breakautoindent=true, linewidth=\textwidth } cppcheck-2.7/man/manual.css000066400000000000000000000061271417746362400157300ustar00rootroot00000000000000/* stylelint-disable */ html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none} /* stylelint-enable */ html { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; } body { background-color: #fff; color: #333; font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; font-size: 16px; font-weight: 400; line-height: 1.5; max-width: 1012px; margin: 20px auto; border: 1px solid #eaecef; padding: 20px 100px; overflow-x: hidden; } a { color: #0366d6; text-decoration: none; } a:hover, a:focus { text-decoration: underline; } h1, h2, h3, h4, h5, h6 { font-weight: 600; line-height: 1.25; margin-top: 24px; margin-bottom: 16px; } h1 { font-size: 2em; } h2 { font-size: 1.5em; } h3 { font-size: 1.25em; } h4, h5, h6 { font-size: 1.1em; } h1, h2 { padding-bottom: 0.3em; border-bottom: 1px solid #eaecef; } pre { padding: 16px; background-color: #f6f8fa; border-radius: 6px; line-height: 1.45; font-size: 85%; margin-top: 0; margin-bottom: 16px; overflow: auto; } code { font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace; background-color: #f6f8fa; font-size: 100%; word-break: normal; white-space: pre; } ol, ul { padding-left: 2em; margin-top: 0; margin-bottom: 16px; } cppcheck-2.7/man/manual.md000066400000000000000000001050421417746362400155340ustar00rootroot00000000000000--- title: Cppcheck manual subtitle: Version 2.7 author: Cppcheck team lang: en documentclass: report --- # Introduction Cppcheck is an analysis tool for C/C++ code. It provides unique code analysis to detect bugs and focuses on detecting undefined behaviour and dangerous coding constructs. The goal is to detect only real errors in the code, and generate as few false positives (wrongly reported warnings) as possible. Cppcheck is designed to analyze your C/C++ code even if it has non-standard syntax, as is common in for example embedded projects. Supported code and platforms: - Cppcheck checks non-standard code that contains various compiler extensions, inline assembly code, etc. - Cppcheck should be compilable by any compiler that supports C++11 or later. - Cppcheck is cross platform and is used in various posix/windows/etc environments. The checks in Cppcheck are not perfect. There are bugs that should be found, that Cppcheck fails to detect. ## About static analysis The kinds of bugs that you can find with static analysis are: - Undefined behavior - Using dangerous code patterns - Coding style There are many bugs that you can not find with static analysis. Static analysis tools do not have human knowledge about what your program is intended to do. If the output from your program is valid but unexpected then in most cases this is not detected by static analysis tools. For instance, if your small program writes "Helo" on the screen instead of "Hello" it is unlikely that any tool will complain about that. Static analysis should be used as a complement in your quality assurance. It does not replace any of; - Careful design - Testing - Dynamic analysis - Fuzzing # Getting started ## GUI It is not required but creating a new project file is a good first step. There are a few options you can tweak to get good results. In the project settings dialog, the first option you see is "Import project". It is recommended that you use this feature if you can. Cppcheck can import: - Visual studio solution / project - Compile database, which can be generated from CMake/qbs/etc build files - Borland C++ Builder 6 When you have filled out the project settings and clicked on OK, the Cppcheck analysis will start. ## Command line ### First test Here is some simple code: int main() { char a[10]; a[10] = 0; return 0; } If you save that into file1.c and execute: cppcheck file1.c The output from Cppcheck will then be: Checking file1.c... [file1.c:4]: (error) Array 'a[10]' index 10 out of bounds ### Checking all files in a folder Normally a program has many source files. Cppcheck can check all source files in a directory: cppcheck path If "path" is a folder, then Cppcheck will recursively check all source files in this folder: Checking path/file1.cpp... 1/2 files checked 50% done Checking path/file2.cpp... 2/2 files checked 100% done ### Check files manually or use project file With Cppcheck you can check files manually by specifying files/paths to check and settings. Or you can use a build environment, such as CMake or Visual Studio. We don't know which approach (project file or manual configuration) will give you the best results. It is recommended that you try both. It is possible that you will get different results so that to find the largest amount of bugs you need to use both approaches. Later chapters will describe this in more detail. ### Check files matching a given file filter With `--file-filter=` you can set a file filter and only those files matching the filter will be checked. For example: if you want to check only those files and folders starting from a subfolder src/ that start with "test" you have to type: cppcheck src/ --file-filter=src/test* Cppcheck first collects all files in src/ and will apply the filter after that. So the filter must start with the given start folder. ### Excluding a file or folder from checking To exclude a file or folder, there are two options. The first option is to only provide the paths and files you want to check: cppcheck src/a src/b All files under src/a and src/b are then checked. The second option is to use -i, which specifies the files/paths to ignore. With this command no files in src/c are checked: cppcheck -isrc/c src This option is only valid when supplying an input directory. To ignore multiple directories supply the -i flag for each directory individually. The following command ignores both the src/b and src/c directories: cppcheck -isrc/b -isrc/c ### Clang parser (experimental) By default Cppcheck uses an internal C/C++ parser. However there is an experimental option to use the Clang parser instead. Install `clang`. Then use Cppcheck option `--clang`. Technically, Cppcheck will execute `clang` with its `-ast-dump` option. The Clang output is then imported and converted into the normal Cppcheck format. And then normal Cppcheck analysis is performed on that. You can also pass a custom Clang executable to the option by using for example `--clang=clang-10`. You can also pass it with a path. On Windows it will append the `.exe` extension unless you use a path. ## Severities The possible severities for messages are: **error** when code is executed there is either undefined behavior or other error, such as a memory leak or resource leak **warning** when code is executed there might be undefined behavior **style** stylistic issues, such as unused functions, redundant code, constness, operator precedence, possible mistakes. **performance** run time performance suggestions based on common knowledge, though it is not certain any measurable speed difference will be achieved by fixing these messages. **portability** portability warnings. Implementation defined behavior. 64-bit portability. Some undefined behavior that probably works "as you want", etc. **information** configuration problems, which does not relate to the syntactical correctness, but the used Cppcheck configuration could be improved. ## Possible speedup analysis of template code Cppcheck instantiates the templates in your code. If your templates are recursive this can lead to slow analysis that uses a lot of memory. Cppcheck will write information messages when there are potential problems. Example code: template void a() { a(); } void foo() { a<0>(); } Cppcheck output: test.cpp:4:5: information: TemplateSimplifier: max template recursion (100) reached for template 'a<101>'. You might want to limit Cppcheck recursion. [templateRecursion] a(); ^ As you can see Cppcheck has instantiated `a` until `a<101>` was reached and then it bails out. To limit template recursion you can: - add template specialisation - configure Cppcheck, which can be done in the GUI project file dialog Example code with template specialisation: template void a() { a(); } void foo() { a<0>(); } #ifdef __cppcheck__ template<> void a<3>() {} #endif You can pass `-D__cppcheck__` when checking this code. # Cppcheck build folder Using a Cppcheck build folder is not mandatory but it is recommended. Cppcheck save analyzer information in that folder. The advantages are; - It speeds up the analysis as it makes incremental analysis possible. Only changed files are analyzed when you recheck. - Whole program analysis also when multiple threads are used. On the command line you configure that through `--cppcheck-build-dir=path`. Example: mkdir b cppcheck --cppcheck-build-dir=b src # <- All files are analyzed cppcheck --cppcheck-build-dir=b src # <- Faster! Results of unchanged files are reused In the GUI it is configured in the project settings. # Importing a project You can import some project files and build configurations into Cppcheck. ## Cppcheck GUI project You can import and use Cppcheck GUI project files in the command line tool: cppcheck --project=foobar.cppcheck The Cppcheck GUI has a few options that are not available in the command line directly. To use these options you can import a GUI project file. The command line tool usage is kept intentionally simple and the options are therefore limited. To ignore certain folders in the project you can use `-i`. This will skip the analysis of source files in the `foo` folder. cppcheck --project=foobar.cppcheck -ifoo ## CMake Generate a compile database: cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . The file `compile_commands.json` is created in the current folder. Now run Cppcheck like this: cppcheck --project=compile_commands.json To ignore certain folders you can use `-i`. This will skip analysis of source files in the `foo` folder. cppcheck --project=compile_commands.json -ifoo ## Visual Studio You can run Cppcheck on individual project files (\*.vcxproj) or on a whole solution (\*.sln) Running Cppcheck on an entire Visual Studio solution: cppcheck --project=foobar.sln Running Cppcheck on a Visual Studio project: cppcheck --project=foobar.vcxproj Both options will analyze all available configurations in the project(s). Limiting on a single configuration: cppcheck --project=foobar.sln "--project-configuration=Release|Win32" In the `Cppcheck GUI` you have the option to only analyze a single debug configuration. If you want to use this option on the command line, then create a `Cppcheck GUI` project with this activated and then import the GUI project file on the command line. To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. cppcheck --project=foobar.vcxproj -ifoo ## C++ Builder 6 Running Cppcheck on a C++ Builder 6 project: cppcheck --project=foobar.bpr To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. cppcheck --project=foobar.bpr -ifoo ## Other If you can generate a compile database, then it is possible to import that in Cppcheck. In Linux you can use for instance the `bear` (build ear) utility to generate a compile database from arbitrary build tools: bear make # Preprocessor Settings If you use `--project` then Cppcheck will automatically use the preprocessor settings in the imported project file and likely you don't have to configure anything extra. If you don't use `--project` then a bit of manual preprocessor configuration might be required. However Cppcheck has automatic configuration of defines. ## Automatic configuration of preprocessor defines Cppcheck automatically test different combinations of preprocessor defines to achieve as high coverage in the analysis as possible. Here is a file that has 3 bugs (when x,y,z are assigned). #ifdef A x=100/0; #ifdef B y=100/0; #endif #else z=100/0; #endif #ifndef C #error C must be defined #endif The flag `-D` tells Cppcheck that a name is defined. There will be no Cppcheck analysis without this define. The flag `-U` tells Cppcheck that a name is not defined. There will be no Cppcheck analysis with this define. The flag `--force` and `--max-configs` is used to control how many combinations are checked. When `-D` is used, Cppcheck will only check 1 configuration unless these are used. Example: cppcheck test.c => test all configurations => all bugs are found cppcheck -DA test.c => only test configuration "-DA" => No bug is found (#error) cppcheck -DA -DC test.c => only test configuration "-DA -DC" => The first bug is found cppcheck -UA test.c => The configuration "-DC" is tested => The last bug is found cppcheck --force -DA test.c => All configurations with "-DA" are tested => The two first bugs are found ## Include paths To add an include path, use `-I`, followed by the path. Cppcheck's preprocessor basically handles includes like any other preprocessor. However, while other preprocessors stop working when they encounter a missing header, Cppcheck will just print an information message and continues parsing the code. The purpose of this behaviour is that Cppcheck is meant to work without necessarily seeing the entire code. Actually, it is recommended to not give all include paths. While it is useful for Cppcheck to see the declaration of a class when checking the implementation of its members, passing standard library headers is discouraged, because the analysis will not wor fully and lead to a longer checking time. For such cases, .cfg files are the preferred way to provide information about the implementation of functions and types to Cppcheck, see below for more information. # Platform You should use a platform configuration that matches your target environment. By default Cppcheck uses native platform configuration that works well if your code is compiled and executed locally. Cppcheck has builtin configurations for Unix and Windows targets. You can easily use these with the `--platform` command line flag. You can also create your own custom platform configuration in a XML file. Here is an example: 8 signed 2 4 4 8 4 8 12 4 4 2 # C/C++ Standard Use `--std` on the command line to specify a C/C++ standard. Cppcheck assumes that the code is compatible with the latest C/C++ standard, but it is possible to override this. The available options are: - c89: C code is C89 compatible - c99: C code is C99 compatible - c11: C code is C11 compatible (default) - c++03: C++ code is C++03 compatible - c++11: C++ code is C++11 compatible - c++14: C++ code is C++14 compatible - c++17: C++ code is C++17 compatible - c++20: C++ code is C++20 compatible (default) # Cppcheck build dir It's a good idea to use a Cppcheck build dir. On the command line use `--cppcheck-build-dir`. In the GUI, the build dir is configured in the project options. Rechecking code will be much faster. Cppcheck does not analyse unchanged code. The old warnings are loaded from the build dir and reported again. Whole program analysis does not work when multiple threads are used; unless you use a cppcheck build dir. For instance, the unusedFunction warnings require whole program analysis. # Suppressions If you want to filter out certain errors from being generated, then it is possible to suppress these. If you encounter a false positive, then please report it to the Cppcheck team so that it can be fixed. ## Plain text suppressions The format for an error suppression is one of: [error id]:[filename]:[line] [error id]:[filename2] [error id] The `error id` is the id that you want to suppress. The easiest way to get it is to use the --template=gcc command line flag. The id is shown in brackets. The filename may include the wildcard characters \* or ?, which matches any sequence of characters or any single character respectively. It is recommended to use "/" as path separator on all operating systems. The filename must match the filename in the reported warning exactly. For instance, if the warning contains a relative path, then the suppression must match that relative path. ## Command line suppression The `--suppress=` command line option is used to specify suppressions on the command line. Example: cppcheck --suppress=memleak:src/file1.cpp src/ ## Suppressions in a file You can create a suppressions file for example as follows: // suppress memleak and exceptNew errors in the file src/file1.cpp memleak:src/file1.cpp exceptNew:src/file1.cpp uninitvar // suppress all uninitvar errors in all files Note that you may add empty lines and comments in the suppressions file. Comments must start with `#` or `//` and be at the start of the line, or after the suppression line. The usage of the suppressions file is as follows: cppcheck --suppressions-list=suppressions.txt src/ ## XML suppressions You can specify suppressions in a XML file, for example as follows: uninitvar src/file1.c 10 var The XML format is extensible and may be extended with further attributes in the future. The usage of the suppressions file is as follows: cppcheck --suppress-xml=suppressions.xml src/ ## Inline suppressions Suppressions can also be added directly in the code by adding comments that contain special keywords. Note that adding comments sacrifices the readability of the code somewhat. This code will normally generate an error message: void f() { char arr[5]; arr[10] = 0; } The output is: cppcheck test.c [test.c:3]: (error) Array 'arr[5]' index 10 out of bounds To activate inline suppressions: cppcheck --inline-suppr test.c ### Format You can suppress a warning `aaaa` with: // cppcheck-suppress aaaa Suppressing multiple ids in one comment by using []: // cppcheck-suppress [aaaa, bbbb] ### Comment before code or on same line The comment can be put before the code or at the same line as the code. Before the code: void f() { char arr[5]; // cppcheck-suppress arrayIndexOutOfBounds arr[10] = 0; } Or at the same line as the code: void f() { char arr[5]; arr[10] = 0; // cppcheck-suppress arrayIndexOutOfBounds } In this example there are 2 lines with code and 1 suppression comment. The suppression comment only applies to 1 line: `a = b + c;`. void f() { a = b + c; // cppcheck-suppress abc d = e + f; } As a special case for backwards compatibility, if you have a `{` on its own line and a suppression comment after that, then that will suppress warnings for both the current and next line. This example will suppress `abc` warnings both for `{` and for `a = b + c;`: void f() { // cppcheck-suppress abc a = b + c; } ### Multiple suppressions For a line of code there might be several warnings you want to suppress. There are several options; Using 2 suppression comments before code: void f() { char arr[5]; // cppcheck-suppress arrayIndexOutOfBounds // cppcheck-suppress zerodiv arr[10] = arr[10] / 0; } Using 1 suppression comment before the code: void f() { char arr[5]; // cppcheck-suppress[arrayIndexOutOfBounds,zerodiv] arr[10] = arr[10] / 0; } Suppression comment on the same line as the code: void f() { char arr[5]; arr[10] = arr[10] / 0; // cppcheck-suppress[arrayIndexOutOfBounds,zerodiv] } ### Symbol name You can specify that the inline suppression only applies to a specific symbol: // cppcheck-suppress aaaa symbolName=arr Or: // cppcheck-suppress[aaaa symbolName=arr, bbbb] ### Comment about suppression You can write comments about a suppression as follows: // cppcheck-suppress[warningid] some comment // cppcheck-suppress warningid ; some comment // cppcheck-suppress warningid // some comment # XML output Cppcheck can generate output in XML format. Use `--xml` to enable this format. A sample command to check a file and output errors in the XML format: cppcheck --xml file1.cpp Here is a sample report: ## The `` element Each error is reported in a `` element. Attributes: **id** id of error, and which are valid symbolnames **severity** error/warning/style/performance/portability/information **msg** the error message in short format **verbose** the error message in long format **inconclusive** this attribute is only used when the error message is inconclusive **cwe** CWE ID for the problem; note that this attribute is only used when the CWE ID for the message is known ## The `` element All locations related to an error are listed with `` elements. The primary location is listed first. Attributes: **file** filename, both relative and absolute paths are possible **file0** name of the source file (optional) **line** line number **info** short information for each location (optional) # Reformatting the text output If you want to reformat the output so that it looks different, then you can use templates. ## Predefined output formats To get Visual Studio compatible output you can use --template=vs: cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c This output will look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. To get gcc compatible output you can use --template=gcc: cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c The output will look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[2] = 0; ^ ## User defined output format (single line) You can write your own pattern. For instance, to get warning messages that are formatted like traditional gcc, then the following format can be used: cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c The output will then look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. A comma separated format: cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c The output will look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds. ## User defined output format (multi line) Many warnings have multiple locations. Example code: void f(int *p) { *p = 3; // line 3 } int main() { int *p = 0; // line 8 f(p); // line 9 return 0; } There is a possible null pointer dereference at line 3. Cppcheck can show how it came to that conclusion by showing extra location information. You need to use both --template and --template-location at the command line, for example: cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c The output from Cppcheck is: Checking multiline.c ... multiline.c:3: warning: Possible null pointer dereference: p *p = 3; ^ multiline.c:8: note: Assignment 'p=0', assigned value is 0 int *p = 0; ^ multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 f(p); ^ multiline.c:3: note: Null pointer dereference *p = 3; ^ The first line in the warning is formatted by the --template format. The other lines in the warning are formatted by the --template-location format. ### Format specifiers for --template The available specifiers for --template are: **{file}** File name **{line}** Line number **{column}** Column number **{callstack}** Write all locations. Each location is written in [{file}:{line}] format and the locations are separated by ->. For instance it might look like: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] **{inconclusive:text}** If warning is inconclusive, then the given text is written. The given text can be any text that does not contain }. Example: {inconclusive:inconclusive,} **{severity}** error/warning/style/performance/portability/information **{message}** The warning message **{id}** Warning id **{code}** The real code **\\t** Tab **\\n** Newline **\\r** Carriage return ### Format specifiers for --template-location The available specifiers for `--template-location` are: **{file}** File name **{line}** Line number **{column}** Column number **{info}** Information message about the current location **{code}** The real code **\\t** Tab **\\n** Newline **\\r** Carriage return # Addons Addons are scripts that analyse Cppcheck dump files to check compatibility with secure coding standards and to locate issues. Cppcheck is distributed with a few addons which are listed below. ## Supported addons ### cert.py [cert.py](https://github.com/danmar/cppcheck/blob/main/addons/cert.py) checks for compliance with the safe programming standard [SEI CERT](http://www.cert.org/secure-coding/). ### misra.py [misra.py](https://github.com/danmar/cppcheck/blob/main/addons/misra.py) is used to verify compliance with MISRA C 2012, a proprietary set of guidelines to avoid questionable code, developed for embedded systems. This standard is proprietary, and open source tools are not allowed to distribute the Misra rule texts. Therefore Cppcheck is not allowed to write the rule texts directly. Cppcheck is allowed to distribute the rules and display the id of each violated rule (for example, [c2012-21.3]). The corresponding rule text can also be written however you need to provide that. To get the rule texts, please buy the PDF from MISRA (https://www.misra.org.uk). If you copy the rule texts from "Appendix A - Summary of guidelines" in the PDF and write those in a text file, then by using that text file Cppcheck can write the proper warning messages. To see how the text file can be formatted, take a look at the files listed here: https://github.com/danmar/cppcheck/blob/main/addons/test/misra/. You can use the option `--rule-texts` to specify your rules text file. The full list of supported rules is available on [Cppcheck](https://cppcheck.sourceforge.io/misra.php) home page. ### y2038.py [y2038.py](https://github.com/danmar/cppcheck/blob/main/addons/y2038.py) checks Linux systems for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/main/addons/doc/y2038.txt). ### threadsafety.py [threadsafety.py](https://github.com/danmar/cppcheck/blob/main/addons/threadsafety.py) analyses Cppcheck dump files to locate thread safety issues like static local objects used by multiple threads. ## Running Addons Addons could be run through Cppcheck command line utility as follows: cppcheck --addon=misra.py somefile.c This will launch all Cppcheck checks and additionally calls specific checks provided by selected addon. Some addons need extra arguments. You can configure how you want to execute an addon in a json file. For example put this in misra.json: { "script": "misra.py", "args": [ "--rule-texts=misra.txt" ] } And then the configuration can be executed on the Cppcheck command line: cppcheck --addon=misra.json somefile.c By default Cppcheck would search addon at the standard path which was specified during the installation process. You also can set this path directly, for example: cppcheck --addon=/opt/cppcheck/configurations/my_misra.json somefile.c This allows you to create and manage multiple configuration files for different projects. # Library configuration When external libraries are used, such as WinAPI, POSIX, gtk, Qt, etc, Cppcheck doesn't know how the external functions behave. Cppcheck then fails to detect various problems such as memory leaks, buffer overflows, possible null pointer dereferences, etc. But this can be fixed with configuration files. Cppcheck already contains configurations for several libraries. They can be loaded as described below. Note that the configuration for the standard libraries of C and C++, std.cfg, is always loaded by cppcheck. If you create or update a configuration file for a popular library, we would appreciate if you upload it to us. ## Using your own custom .cfg file You can create and use your own .cfg files for your projects. Use `--check-library` and `--enable=information` to get hints about what you should configure. You can use the `Library Editor` in the `Cppcheck GUI` to edit configuration files. It is available in the `View` menu. The .cfg file format is documented in the `Reference: Cppcheck .cfg format` (https://cppcheck.sourceforge.io/reference-cfg-format.pdf) document. # HTML Report You can convert the XML output from Cppcheck into a HTML report. You'll need Python and the pygments module ( for this to work. In the Cppcheck source tree there is a folder htmlreport that contains a script that transforms a Cppcheck XML file into HTML output. This command generates the help screen: htmlreport/cppcheck-htmlreport -h The output screen says: Usage: cppcheck-htmlreport [options] Options: -h, --help show this help message and exit --file=FILE The cppcheck xml output file to read defects from. 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. Example usage: ./cppcheck gui/test.cpp --xml 2> err.xml htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. # Bug hunting If you want to detect most bugs and can accept false alarms, then Cppcheck has analysis for that. This analysis is soundy; it should diagnose most bugs reported in CVEs and from dynamic analysis. You have to expect false alarms. However Cppcheck tries to limit false alarms. The purpose of the data flow analysis is to limit false alarms. Some possible use cases; - you are writing new code and want to ensure it is safe. - you are reviewing code and want to get hints about possible UB. - you need extra help troubleshooting a weird bug. - you want to check if a release candidate is safe. The intention is that this will be used primarily in the GUI. ## Activate this analysis On the command line you can use `--bug-hunting`. In the GUI go to the project dialog. In the `Analysis` tab there is a check box for `Bug hunting`. ## Contracts To handle false alarms and improve the analysis you are encouraged to use contracts. To provide contracts, you can either annotate your code or configure the contracts in the GUI. There exists various annotations for C and C++ code. gcc has attributes, there are SAL annotations, and then there are standard C++ annotations. It is our goal to handle various types of annotations, if you can reuse those annotations in Cppcheck analysis that will be an extra benefit. ### Function contracts Here is an example code: int foo(int x) { return 100 / x; } The bug hunting analysis will warn about a division by zero. Right now, it can't be proven that x can't be 0 here. A function contract can be used to tell Cppcheck what input "foo(x)" expects. #### Annotation You can use "C++ function contracts" syntax both in C and C++. For C++ code you can write: int foo(int x) [[expects: x > 0]] { return 100 / x; // No division by zero } void bar() { foo(-10); // Warning: Contract is violated! } For C code you can write (works in C++ too): #ifdef __cppcheck__ #define Expects(EXPR) [[expects: EXPR]] #else #define Expects(EXPR) #endif int foo(int x) Expects(x > 0) { return 100 / x; } void bar() { foo(-10); // Warning: Contract is violated! } #### Configuration in gui You can configure contracts in the GUI. Example code: int foo(int x) { return 100 / x; } If you run bug hunting analysis on this code, then because Cppcheck can't prove that x can't be 0, you will get a warning about division by zero. Either: - Right click on that warning and select "Edit contract..". - Open the "Functions" tab at the bottom and lookup the "foo(x)" function. Then double click on that. A dialog box is shown where you can configure the contract for function "foo(x)". A textbox allows you to edit the "Expects" expression. Enter the expression "x > 0" in the dialog box and click OK. Now if you run analysis the division by zero warning will be gone. As for annotations, if the contract is violated somewhere then you will get a warning. ### Variable contracts Here is an example code: int x; int foo() { return 100 / x; } The bug hunting analysis will warn about a division by zero. It can't be proven that x can't be 0. A variable contract specify the allowed values for a variable. Cppcheck use variable contracts both when a variable is read and written: - When a variable is read, Cppcheck will assume that the contract is met. This means you can avoid false positives for impossible variable values. - When a variable is written, Cppcheck will ensure that its contract is not violated. If it can't be determined that the contract is met you will get a warning. #### Annotation You can use Cppcheck attributes `__cppcheck_low__(value)` and `__cppcheck_high__(value)` to configure min and max values for variables and types. Example code: __cppcheck_low__(1) int x; int foo() { return 100 / x; // No division by zero } Tip: You can create an integer type with a limited value range. For instance here is an unsigned integer type that can only have the values 0-100: typedef __cppcheck_high__(100) unsigned int percent_t; percent_t x; x = 110; // <- Cppcheck will warn about this assignment #### GUI To configure variable contracts in the GUI, open the "Variables" tab at the bottom. Lookup the variable you want to configure and double click on that. A dialog box is shown for the variable, where you can configure the min and max values. ## Incomplete analysis The data flow analysis can analyze simple functions completely but complex functions are not analyzed completely (yet). The data flow analysis will be continuously improved in the future but it will never be perfect. It is likely that you will get false alarms caused by incomplete data flow analysis. Unfortunately it is unlikely that such false alarms can be fixed by contracts. cppcheck-2.7/man/reference-cfg-format.md000066400000000000000000000464441417746362400202520ustar00rootroot00000000000000--- title: Cppcheck .cfg format subtitle: Version 2.7 author: Cppcheck team lang: en documentclass: report --- # Introduction This is a reference for the .cfg file format that Cppcheck uses. # Memory and resource leaks Cppcheck has configurable checking for leaks, e.g. you can specify which functions allocate and free memory or resources and which functions do not affect the allocation at all. ## ``, `` and `` Here is an example program: void test() { HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); } The code example above has a resource leak - CreatePen() is a WinAPI function that creates a pen. However, Cppcheck doesn't assume that return values from functions must be freed. There is no error message: $ cppcheck pen1.c Checking pen1.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=windows.cfg pen1.c Checking pen1.c... [pen1.c:3]: (error) Resource leak: pen Here is a minimal windows.cfg file: CreatePen DeleteObject Functions that reallocate memory can be configured using a `` tag. The input argument which points to the memory that shall be reallocated can also be configured (the default is the first argument). As an example, here is a configuration file for the fopen, freopen and fclose functions from the c standard library: fopen freopen fclose The allocation and deallocation functions are organized in groups. Each group is defined in a `` or `` tag and is identified by its `` functions. This means, groups with overlapping `` tags are merged. ## `` and `` Often the allocated pointer is passed to functions. Example: void test() { char *p = malloc(100); dostuff(p); } If Cppcheck doesn't know what `dostuff` does, without configuration it will assume that `dostuff` takes care of the memory so there is no memory leak. To specify that `dostuff` doesn't take care of the memory in any way, use `` in the `` tag (see next section): If instead `dostuff` takes care of the memory then this can be configured with: free dostuff The `` configuration has no logical purpose. You will get the same warnings without it. Use it to silence --check-library information messages. # Function behavior To specify the behaviour of functions and how they should be used, `` tags can be used. Functions are identified by their name, specified in the name attribute and their number of arguments. The name is a comma-separated list of function names. For functions in namespaces or classes, just provide their fully qualified name. For example: ``. If you have template functions then provide their instantiated names ``. ## Function arguments The arguments a function takes can be specified by `` tags. Each of them takes the number of the argument (starting from 1) in the nr attribute, `nr="any"` for arbitrary arguments, or `nr="variadic"` for variadic arguments. Optional arguments can be specified by providing a default value: `default="value"`. The specifications for individual arguments override this setting. You can specify if an argument is an input or output argument. For example ``. The allowed directions are `in`, `out` and `inout`. ### Not bool Here is an example program with misplaced comparison: void test() { if (MemCmp(buffer1, buffer2, 1024==0)) {} } Cppcheck assumes that it is fine to pass boolean values to functions: $ cppcheck notbool.c Checking notbool.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=notbool.cfg notbool.c Checking notbool.c... [notbool.c:5]: (error) Invalid MemCmp() argument nr 3. A non-boolean value is required. Here is the minimal notbool.cfg ### Uninitialized memory Here is an example program: void test() { char buffer1[1024]; char buffer2[1024]; CopyMemory(buffer1, buffer2, 1024); } The bug here is that buffer2 is uninitialized. The second argument for CopyMemory needs to be initialized. However, Cppcheck assumes that it is fine to pass uninitialized variables to functions: $ cppcheck uninit.c Checking uninit.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=windows.cfg uninit.c Checking uninit.c... [uninit.c:5]: (error) Uninitialized variable: buffer2 Below windows.cfg is shown: Version 1: Version 2: Version 1: If `indirect` attribute is not used then the level of indirection is determined automatically. The `` tells Cppcheck that the pointer must be initialized. The `` tells Cppcheck to check 1 extra level. This configuration means that both the pointer and the data must be initialized. Version 2: The `indirect` attribute can be set to explicitly control the level of indirection used in checking. Setting `indirect` to `0` means no uninitialized memory is allowed. Setting it to `1` allows a pointer to uninitialized memory. Setting it to `2` allows a pointer to pointer to uninitialized memory. ### Null pointers Cppcheck assumes it's ok to pass NULL pointers to functions. Here is an example program: void test() { CopyMemory(NULL, NULL, 1024); } The MSDN documentation is not clear if that is ok or not. But let's assume it's bad. Cppcheck assumes that it's ok to pass NULL to functions so no error is reported: $ cppcheck null.c Checking null.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=windows.cfg null.c Checking null.c... [null.c:3]: (error) Null pointer dereference Note that this implies `` as far as values are concerned. Uninitialized memory might still be passed to the function. Here is a minimal windows.cfg file: ### Format string You can define that a function takes a format string. Example: void test() { do_something("%i %i\n", 1024); } No error is reported for that: $ cppcheck formatstring.c Checking formatstring.c... A configuration file can be created that says that the string is a format string. For instance: Now Cppcheck will report an error: $ cppcheck --library=test.cfg formatstring.c Checking formatstring.c... [formatstring.c:3]: (error) do_something format string requires 2 parameters but only 1 is given. The type attribute can be either: printf - format string follows the printf rules scanf - format string follows the scanf rules ### Container inputs If this is a free function for containers(like for `std::size` or `std::erase_if`) then the `` tag can be used to specify the `yield` or `action`. Here is an example of `std::size`: false ### Value range The valid values can be defined. Imagine: void test() { do_something(1024); } No error is reported for that: $ cppcheck valuerange.c Checking valuerange.c... A configuration file can be created that says that 1024 is out of bounds. For instance: 0:1023 Now Cppcheck will report an error: $ cppcheck --library=test.cfg range.c Checking range.c... [range.c:3]: (error) Invalid do_something() argument nr 1. The value is 1024 but the valid values are '0-1023'. Some example expressions you can use in the valid element: 0,3,5 => only values 0, 3 and 5 are valid -10:20 => all values between -10 and 20 are valid :0 => all values that are less or equal to 0 are valid 0: => all values that are greater or equal to 0 are valid 0,2:32 => the value 0 and all values between 2 and 32 are valid -1.5:5.6 => all values between -1.5 and 5.6 are valid ### `` Some function arguments take a buffer. With minsize you can configure the min size of the buffer (in bytes, not elements). Imagine: void test() { char str[5]; do_something(str,"12345"); } No error is reported for that: $ cppcheck minsize.c Checking minsize.c... A configuration file can for instance be created that says that the size of the buffer in argument 1 must be larger than the strlen of argument 2. For instance: Now Cppcheck will report this error: $ cppcheck --library=1.cfg minsize.c Checking minsize.c... [minsize.c:4]: (error) Buffer is accessed out of bounds: str There are different types of minsizes: strlen buffer size must be larger than other arguments string length. Example: see strcpy configuration in std.cfg argvalue buffer size must be larger than value in other argument. Example: see memset configuration in std.cfg sizeof buffer size must be larger than other argument buffer size. Example: see memcpy configuration in posix.cfg mul buffer size must be larger than multiplication result when multiplying values given in two other arguments. Typically one argument defines the element size and another element defines the number of elements. Example: see fread configuration in std.cfg strz With this you can say that an argument must be a zero-terminated string. ### `` Cppcheck doesn't assume that functions always return. Here is an example code: void test(int x) { int data, buffer[1024]; if (x == 1) data = 123; else ZeroMemory(buffer, sizeof(buffer)); buffer[0] = data; // <- error: data is uninitialized if x is not 1 } In theory, if ZeroMemory terminates the program then there is no bug. Cppcheck therefore reports no error: $ cppcheck noreturn.c Checking noreturn.c... However if you use `--check-library` and `--enable=information` you'll get this: $ cppcheck --check-library --enable=information noreturn.c Checking noreturn.c... [noreturn.c:7]: (information) --check-library: Function ZeroMemory() should have configuration If a proper windows.cfg is provided, the bug is detected: $ cppcheck --library=windows.cfg noreturn.c Checking noreturn.c... [noreturn.c:8]: (error) Uninitialized variable: data Here is a minimal windows.cfg file: false ### `` As long as nothing else is specified, cppcheck assumes that ignoring the return value of a function is ok: bool test(const char* a, const char* b) { strcmp(a, b); // <- bug: The call of strcmp does not have side-effects, but the return value is ignored. return true; } In case strcmp has side effects, such as assigning the result to one of the parameters passed to it, nothing bad would happen: $ cppcheck useretval.c Checking useretval.c... If a proper lib.cfg is provided, the bug is detected: $ cppcheck --library=lib.cfg --enable=warning useretval.c Checking useretval.c... [useretval.c:3]: (warning) Return value of function strcmp() is not used. Here is a minimal lib.cfg file: ### `` and `` These correspond to the GCC function attributes `` and ``. A pure function has no effects except to return a value, and its return value depends only on the parameters and global variables. A const function has no effects except to return a value, and its return value depends only on the parameters. Here is an example code: void f(int x) { if (calculate(x) == 213) { } else if (calculate(x) == 213) { // unreachable code } } If calculate() is a const function then the result of calculate(x) will be the same in both conditions, since the same parameter value is used. Cppcheck normally assumes that the result might be different, and reports no warning for the code: $ cppcheck const.c Checking const.c... If a proper const.cfg is provided, the unreachable code is detected: $ cppcheck --enable=style --library=const const.c Checking const.c... [const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. Here is a minimal const.cfg file: ### Example configuration for strcpy() The proper configuration for the standard strcpy() function would be: false The `` tells Cppcheck to ignore this function call in the leaks checking. Passing allocated memory to this function won't mean it will be deallocated. The `` tells Cppcheck if this function returns or not. The first argument that the function takes is a pointer. It must not be a null pointer, therefore `` is used. The second argument the function takes is a pointer. It must not be null. And it must point at initialized data. Using `` and `` is correct. Moreover it must point at a zero-terminated string so `` is also used. # ``; check or suppress The ``configuration tells Cppcheck to show or suppress warnings for a certain type. Example: foo bar In the `unusedvar` checking the `foo` type will be checked. Warnings for `bar` type variables will be suppressed. # `` Libraries can be used to define preprocessor macros as well. For example: Each occurrence of "NULL_VALUE" in the code would then be replaced by "0" at preprocessor stage. # `` Use this for integer/float/bool/pointer types. Not for structs/unions. Lots of code relies on typedefs providing platform independent types. "podtype"-tags can be used to provide necessary information to cppcheck to support them. Without further information, cppcheck does not understand the type "uint16_t" in the following example: void test() { uint16_t a; } No message about variable 'a' being unused is printed: $ cppcheck --enable=style unusedvar.cpp Checking unusedvar.cpp... If uint16_t is defined in a library as follows, the result improves: The size of the type is specified in bytes. Possible values for the "sign" attribute are "s" (signed) and "u" (unsigned). Both attributes are optional. Using this library, cppcheck prints: $ cppcheck --library=lib.cfg --enable=style unusedvar.cpp Checking unusedvar.cpp... [unusedvar.cpp:2]: (style) Unused variable: a # `` A lot of C++ libraries, among those the STL itself, provide containers with very similar functionality. Libraries can be used to tell cppcheck about their behaviour. Each container needs a unique ID. It can optionally have a startPattern, which must be a valid Token::Match pattern and an endPattern that is compared to the linked token of the first token with such a link. The optional attribute "inherits" takes an ID from a previously defined container. The `hasInitializerListConstructor` attribute can be set when the container has a constructor taking an initializer list. The `view` attribute can be set when the container is a view, which means it borrows the lifetime of another container. Inside the `` tag, functions can be defined inside of the tags ``, `` and `` (on your choice). Each of them can specify an action like "resize" and/or the result it yields, for example "end-iterator". The following example provides a definition for std::vector, based on the definition of "stdContainer" (not shown): The tag `` can be added as well to provide more information about the type of container. Here is some of the attributes that can be set: * `string='std-like'` can be set for containers that match `std::string` interfaces. * `associative='std-like'` can be set for containers that match C++'s `AssociativeContainer` interfaces. # `` Specify that a class is a smart pointer by using ``. cppcheck-2.7/man/writing-rules-1.docbook000066400000000000000000000106071417746362400202520ustar00rootroot00000000000000
Part 1 - Getting started
Introduction This is a short and simple guide that describes how rules are written for Cppcheck. The patterns are defined with regular expressions. It is required that you know how regular expressions work.
Data representation of the source code The data used by the rules are not the raw source code. Cppcheck will read the source code and process it before the rules are used. Cppcheck is designed to find bugs and dangerous code. Stylistic information (such as indentation, comments, etc) are filtered out at an early state. You don't need to worry about such stylistic information when you write rules. Between each token in the code there is always a space. For instance the raw code "1+f()" is processed into "1 + f ( )" . The code is simplified in many ways.
Creating a simple rule When creating a rule there are two steps: Create the regular expression Create a XML based rule file
Step 1 - Creating the regular expression Cppcheck uses the PCRE library to handle regular expressions. PCRE stands for "Perl Compatible Regular Expressions". The homepage for PCRE is http://www.pcre.org/. Let's create a regular expression that checks for code such as: if (p) free(p); For such code the condition is often redundant (on most implementations it is valid to free a NULL pointer). The regular expression must be written for the simplified code. To see what the simplified code looks like you can create a source file with the code: void f() { if (p) free(p); } Save that code as dealloc.cpp and then use cppcheck --rule=".+" dealloc.cpp: $ ./cppcheck --rule=".+" dealloc.cpp Checking dealloc.cpp... [dealloc.cpp:1]: (style) found ' void f ( ) { if ( p ) { free ( p ) ; } }' The regular expression .+ matches everything and the matching text is shown on the screen. From that output we can see that the simplified code is: void f ( ) { if ( p ) { free ( p ) ; } } Now that we know how the simplified code looks. We can create a regular expression that matches it properly: $ cppcheck --rule="if \( p \) { free \( p \) ; }" dealloc.cpp Checking dealloc.cpp... [dealloc.cpp:2]: (style) found 'if ( p ) { free ( p ) ; }'
Step 2 - Create rule file A rule file is a simple XML file that contains: a pattern to search for an error message that is reported when pattern is found Here is a simple example: <?xml version="1.0"?> <rule version="1"> <pattern>if \( p \) { free \( p \) ; }</pattern> <message> <id>redundantCondition</id> <severity>style</severity> <summary>Redundant condition. It is valid to free a NULL pointer.</summary> </message> </rule> If you save that xml data in dealloc.rule you can test this rule: $ cppcheck --rule-file=dealloc.rule dealloc.cpp Checking dealloc.cpp... [dealloc.cpp:2]: (style) Redundant condition. It is valid to free a NULL pointer.
cppcheck-2.7/man/writing-rules-2.docbook000066400000000000000000000201041417746362400202440ustar00rootroot00000000000000
Part 2 - The Cppcheck data representation
Introduction In this article I will discuss the data representation that Cppcheck uses. The data representation that Cppcheck uses is specifically designed for static analysis. It is not intended to be generic and useful for other tasks.
See the data There are two ways to look at the data representation at runtime. Using --rule=.+ is one way. All tokens are written on a line: int a ; int b ; Using --debug is another way. The tokens are line separated in the same way as the original code: 1: int a@1 ; 2: int b@2 ; In the --debug output there are "@1" and "@2" shown. These are the variable ids (Cppcheck gives each variable a unique id). You can ignore these if you only plan to write rules with regular expressions, you can't use variable ids with regular expressions. In general, I will use the --rule=.+ output in this article because it is more compact.
Some of the simplifications The data is simplified in many ways.
Preprocessing The Cppcheck data is preprocessed. There are no comments, #define, #include, etc. Original source code: #define SIZE 123 char a[SIZE]; The Cppcheck data for that is: char a [ 123 ] ;
typedef The typedefs are simplified. typedef char s8; s8 x; The Cppcheck data for that is: ; char x ;
Calculations Calculations are simplified. int a[10 + 4]; The Cppcheck data for that is: int a [ 14 ] ;
Variables
Variable declarations Variable declarations are simplified. Only one variable can be declared at a time. The initialization is also broken out into a separate statement. int *a=0, b=2; The Cppcheck data for that is: int * a ; a = 0 ; int b ; b = 2 ; This is even done in the global scope. Even though that is invalid in C/C++.
Known variable values Known variable values are simplified. void f() { int x = 0; x++; array[x + 2] = 0; } The --debug output for that is: 1: void f ( ) 2: { 3: ; ; 4: ; 5: array [ 3 ] = 0 ; 6: } The variable x is removed because it is not used after the simplification. It is therefore redundant. The "known values" doesn't have to be numeric. Variable aliases, pointer aliases, strings, etc should be handled too. Example code: void f() { char *a = strdup("hello"); char *b = a; free(b); } The --debug output for that is: 1: void f ( ) 2: { 3: char * a@1 ; a@1 = strdup ( "hello" ) ; 4: ; ; 5: free ( a@1 ) ; 6: }
if/for/while
Braces in if/for/while-body Cppcheck makes sure that there are always braces in if/for/while bodies. if (x) f1(); The Cppcheck data for that is: if ( x ) { f1 ( ) ; }
No else if The simplified data representation doesn't have "else if". void f(int x) { if (x == 1) f1(); else if (x == 2) f2(); } The --debug output: 1: void f ( int x@1 ) 2: { 3: if ( x@1 == 1 ) { 4: f1 ( ) ; } 5: else { if ( x@1 == 2 ) { 6: f2 ( ) ; } } 7: }
Condition is always true / false Conditions that are always true / false are simplified. void f() { if (true) { f1(); } } The Cppcheck data is: void f ( ) { { f1 ( ) ; } } Another example: void f() { if (false) { f1(); } } The debug output: void f ( ) { }
Assignments Assignments within conditions are broken out from the condition. void f() { int x; if ((x = f1()) == 12) { f2(); } } The x=f1() is broken out. The --debug output: 1: void f ( ) 2: { 3: int x@1 ; 4: x@1 = f1 ( ) ; if ( x@1 == 12 ) { 5: f2 ( ) ; 6: } 7: } Replacing the "if" with "while" in the above example: void f() { int x; while ((x = f1()) == 12) { f2(); } } The x=f1() is broken out twice. The --debug output: 1: void f ( ) 2: { 3: int x@1 ; 4: x@1 = f1 ( ) ; while ( x@1 == 12 ) { 5: f2 ( ) ; x@1 = f1 ( ) ; 5: 6: } 7: }
Comparison with > Comparisons are simplified. The two conditions in this example are logically the same: void f() { if (x < 2); if (2 > x); } Cppcheck data doesn't use > for comparisons. It is converted into < instead. In the Cppcheck data there is no difference for 2>x and x<2. 1: 2: void f ( ) 3: { 4: if ( x < 2 ) { ; } 5: if ( x < 2 ) { ; } 6: } A similar conversion happens when >= is used.
if (x) and if (!x) If possible a condition will be reduced to x or !x. Here is an example code: void f() { if (!x); if (NULL == x); if (x == 0); if (x); if (NULL != x); if (x != 0); } The --debug output is: 1: void f ( ) 2: { 3: if ( ! x ) { ; } 4: if ( ! x ) { ; } 5: if ( ! x ) { ; } 6: 7: if ( x ) { ; } 8: if ( x ) { ; } 9: if ( x ) { ; } 10: }
cppcheck-2.7/man/writing-rules-3.docbook000066400000000000000000000164751417746362400202650ustar00rootroot00000000000000
Part 3 - Introduction to writing rules with C++
Introduction The goal for this article is to introduce how Cppcheck rules are written with C++. With C++ it is possible to write more complex rules than is possible with regular expressions.
Basics A C++ rule is written in a C++ function. Rules are organized into Check classes. For instance there is a class with the name CheckStl that contains various stl rules. The CheckOther can always be used if no other class suits you. When you have added your rule you must recompile Cppcheck before you can test it.
Division by zero This simple regular expression will check for division by zero: cppcheck --rule="/ 0" Here is the corresponding C++ check: // Detect division by zero void CheckOther::divisionByZero() { // Loop through all tokens for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // check if there is a division by zero if (Token::Match(tok, "/ 0")) { // report error divisionByZeroError(tok); } } } // Report error void CheckOther::divisionByZeroError() { reportError(tok, // location Severity::error, // severity "divisionByZero", // id "Division by zero"); // message } The Token::Match matches tokens against expressions. A few rules about Token::Match expressions are: tokens are either completely matched or not matched at all. The token "abc" is not matched by "ab". Spaces are used as separators. With normal regular expressions there are special meanings for + * ? ( ). These are just normal characters in Token::Match patterns.
Condition before deallocation In the first Writing rules part I described a rule that looks for redundant conditions. Here is the regular expression that was shown: if \( p \) { free \( p \) ; } The corresponding Token::Match expression is: if ( %var% ) { free ( %var% ) ; } The %var% pattern match any variable name. Here is a C++ function: // Find redundant condition before deallocation void CheckOther::dealloc() { // Loop through all tokens for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Is there a condition and a deallocation? if (Token::Match(tok, "if ( %var% ) { free ( %var% ) ; }")) { // Get variable name used in condition: const std::string varname1 = tok->strAt(2); // Get variable name used in deallocation: const std::string varname2 = tok->strAt(7); // Is the same variable used? if (varname1 == varname2) { // report warning deallocWarning(tok); } } } } // Report warning void CheckOther::deallocWarning() { reportError(tok, // location Severity::warning, // severity "dealloc", // id "Redundant condition"); // message } The strAt function is used to fetch strings from the token list. The parameter specifies the token offset. The result for "tok->tokAt(1)" is the same as for "tok->next()".
Validate function parameters Sometimes it is known that a function can't handle certain parameters. Here is an example rule that checks that the parameters for strtol or strtoul are valid: //--------------------------------------------------------------------------- // strtol(str, 0, radix) <- radix must be 0 or 2-36 //--------------------------------------------------------------------------- void CheckOther::invalidFunctionUsage() { // Loop through all tokens for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Is there a function call for strtol or strtoul? if (!Token::Match(tok, "strtol|strtoul (")) continue; // Locate the third parameter of the function call.. // Counter that counts the parameters. int param = 1; // Scan the function call tokens. The "tok->tokAt(2)" returns // the token after the "(" for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { // If a "(" is found then jump to the corresponding ")" if (tok2->str() == "(") tok2 = tok2->link(); // End of function call. else if (tok2->str() == ")") break; // Found a ",". increment param counter else if (tok2->str() == ",") { ++param; // If the param is 3 then check if the parameter is valid if (param == 3) { if (Token::Match(tok2, ", %num% )")) { // convert next token into a number MathLib::bigint radix; radix = MathLib::toLongNumber(tok2->strAt(1)); // invalid radix? if (!(radix == 0 || (radix >= 2 && radix <= 36))) { dangerousUsageStrtolError(tok2); } } break; } } } } } void CheckOther::dangerousUsageStrtolError(const Token *tok) { reportError(tok, // location Severity::error, // severity "dangerousUsageStrtol", // id "Invalid radix"); // message } The link() member function is used to find the corresponding ( ) [ ] or { } token. The inner loop is not necessary if you just want to get the last parameter. This code will check if the last parameter is numerical.. .. // Is there a function call? if (!Token::Match(tok, "do_something (")) continue; if (Token::Match(tok->next()->link()->tokAt(-2), "(|, %num% )")) ... The pattern (|, can also be written as [(,].
cppcheck-2.7/naming.json000066400000000000000000000003011417746362400153160ustar00rootroot00000000000000{ "script": "addons/naming.py", "args": [ "--private-member-variable=m[A-Z].*", "--var=[_a-z].*", "--const=[_a-zA-Z].*", "--function=[a-zA-Z].*" ] } cppcheck-2.7/oss-fuzz/000077500000000000000000000000001417746362400147605ustar00rootroot00000000000000cppcheck-2.7/oss-fuzz/CMakeLists.txt000066400000000000000000000017721417746362400175270ustar00rootroot00000000000000if (ENABLE_OSS_FUZZ AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_executable(fuzz-client EXCLUDE_FROM_ALL main.cpp type2.cpp $ $ $) target_include_directories(fuzz-client PRIVATE ${CMAKE_SOURCE_DIR}/lib ${CMAKE_SOURCE_DIR}/externals/simplecpp ${CMAKE_SOURCE_DIR}/externals/tinyxml2 ${CMAKE_SOURCE_DIR}/externals) target_compile_options(fuzz-client PRIVATE -fsanitize=fuzzer) # TODO: target_link_options() requires CMake >= 3.13 #target_link_options(fuzz-client PRIVATE -fsanitize=fuzzer) target_link_libraries(fuzz-client PRIVATE -fsanitize=fuzzer) if (HAVE_RULES) target_link_libraries(fuzz-client PRIVATE ${PCRE_LIBRARY}) endif() if (USE_Z3) target_link_libraries(fuzz-client PRIVATE ${Z3_LIBRARIES}) endif() add_executable(translate EXCLUDE_FROM_ALL translate.cpp type2.cpp) endif() cppcheck-2.7/oss-fuzz/Makefile000066400000000000000000000017101417746362400164170ustar00rootroot00000000000000# # fuzzer clients # ==================== # # Local libfuzzer client: # make CXX=clang++-6.0 CXXFLAGS="-fsanitize=address" fuzz-client CPPCHECK_DIR=.. INCLUDE_DIR=-I ${CPPCHECK_DIR}/lib -I ${CPPCHECK_DIR}/externals/picojson -I ${CPPCHECK_DIR}/externals/simplecpp -I ${CPPCHECK_DIR}/externals/tinyxml2 -I ${CPPCHECK_DIR}/externals SRC_FILES=main.cpp type2.cpp ${CPPCHECK_DIR}/externals/simplecpp/simplecpp.cpp ${CPPCHECK_DIR}/externals/tinyxml2/tinyxml2.cpp ${CPPCHECK_DIR}/lib/*.cpp all: oss-fuzz-client translate oss-fuzz-client: main.cpp type2.cpp type2.h ${CXX} -std=c++11 -g ${CXXFLAGS} -o oss-fuzz-client ${INCLUDE_DIR} ${SRC_FILES} ${LIB_FUZZING_ENGINE} fuzz-client: main.cpp type2.cpp type2.h ${CXX} -std=c++11 -g ${CXXFLAGS} -o fuzz-client ${INCLUDE_DIR} ${SRC_FILES} -fsanitize=fuzzer translate: translate.cpp type2.cpp type2.h ${CXX} -std=c++11 -g ${CXXFLAGS} -o translate type2.cpp translate.cpp clean: rm -f oss-fuzz-client fuzz-client translate cppcheck-2.7/oss-fuzz/main.cpp000066400000000000000000000040271417746362400164130ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "color.h" #include "cppcheck.h" #include "type2.h" class CppcheckExecutor : public ErrorLogger { private: CppCheck cppcheck; public: CppcheckExecutor() : ErrorLogger() , cppcheck(*this, false, nullptr) { cppcheck.settings().addEnabled("all"); cppcheck.settings().certainty.setEnabled(Certainty::inconclusive, true); } void run(const std::string &code) { cppcheck.check("test.cpp", code); } void reportOut(const std::string &outmsg, Color) OVERRIDE { (void)outmsg; } void reportErr(const ErrorMessage &msg) OVERRIDE { (void)msg; } void reportProgress(const std::string& filename, const char stage[], const std::size_t value) OVERRIDE { (void)filename; (void)stage; (void)value; } void bughuntingReport(const std::string &str) OVERRIDE { (void)str; } }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) { if (dataSize < 10000) { const std::string code = generateCode2(data, dataSize); //std::ofstream fout("code.cpp"); //fout << code; //fout.close(); CppcheckExecutor cppcheckExecutor; cppcheckExecutor.run(code); } return 0; } cppcheck-2.7/oss-fuzz/translate.cpp000066400000000000000000000011661417746362400174650ustar00rootroot00000000000000 #include #include #include "type2.h" int main(int argc, char **argv) { const char *filename = argc==2 ? argv[1] : nullptr; if (!filename) { std::cout << "Invalid args, no filename\n"; return 1; } std::ifstream f(filename); if (!f.is_open()) { std::cout << "failed to open file:" << filename << "\n"; return 1; } std::string str((std::istreambuf_iterator(f)), std::istreambuf_iterator()); std::cout << generateCode2(reinterpret_cast(str.data()), str.size()) << std::endl; return 0; } cppcheck-2.7/oss-fuzz/type2.cpp000066400000000000000000000156061417746362400165370ustar00rootroot00000000000000 #include #include "type2.h" static int getValue(const uint8_t *data, size_t dataSize, uint8_t maxValue, bool *done = nullptr) { static size_t pos; // current "data" position static int dataValue; // value extracted from data static int ones; // ones. This variable tracks if we need to add more stuff in "dataValue". // Shift more bits from "data" into "dataValue" if needed while (pos < dataSize && ones < 0xFFFF) { ones = (ones << 8) | 0xff; dataValue = (dataValue << 8) | data[pos]; pos++; } if (done) *done = (ones == 0); if (maxValue == 0) return 0; // Shift out info from "dataValue" using % . Using & and >> would work but then we are limited to "power of 2" max value. const int ret = dataValue % maxValue; ones /= maxValue; dataValue /= maxValue; return ret; } static std::string generateExpression2_lvalue(const uint8_t *data, size_t dataSize) { return "var" + std::to_string(1 + getValue(data, dataSize, 5)); } static std::string generateExpression2_Op(const uint8_t *data, size_t dataSize, uint8_t numberOfGlobalConstants) { std::ostringstream code; switch (getValue(data, dataSize, 3)) { case 0: code << generateExpression2_lvalue(data, dataSize); break; case 1: code << "globalconstant" << (1 + getValue(data, dataSize, numberOfGlobalConstants)); break; case 2: code << (getValue(data, dataSize, 0x80) * 0x80 + getValue(data, dataSize, 0x80)); break; } return code.str(); } static std::string generateExpression2_Expr(const uint8_t *data, size_t dataSize, uint8_t numberOfGlobalConstants, int depth=0) { ++depth; const int type = (depth > 3) ? 0 : getValue(data, dataSize, 3); const char binop[] = "=<>+-*/%&|^"; const char *unop[] = {"++","--","()","~"}; switch (type) { case 0: return generateExpression2_Op(data, dataSize, numberOfGlobalConstants); case 1: { const char op = binop[getValue(data,dataSize,sizeof(binop)-1)]; const std::string lhs = (op == '=') ? generateExpression2_lvalue(data, dataSize) : generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); std::string ret = lhs + op + rhs; if (depth > 1 && op == '=') ret = "(" + ret + ")"; return ret; } case 2: { const char *u = unop[getValue(data,dataSize,sizeof(unop)/sizeof(*unop))]; if (u == std::string("()")) return "(" + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth) + ")"; else if (u == std::string("++") || u == std::string("--")) return u + generateExpression2_lvalue(data, dataSize); return u + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); } default: break; } return "0"; } static std::string generateExpression2_Cond(const uint8_t *data, size_t dataSize, uint8_t numberOfGlobalConstants) { const char *comp[] = {"==", "!=", "<", "<=", ">", ">="}; const int i = getValue(data, dataSize, 6); const std::string lhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); return lhs + comp[i] + rhs; } static std::string functionStart() { static int functionNumber; return "int f" + std::to_string(++functionNumber) + "()\n" "{\n"; } static std::string generateExpression2_conditionalCode(const std::string &indent, const uint8_t *data, size_t dataSize, uint8_t numberOfGlobalConstants) { std::ostringstream code; if (indent.empty()) code << functionStart(); else code << indent << "{\n"; for (int line = 0; line < 4 || indent.empty(); ++line) { bool done = false; const int type1 = getValue(data, dataSize, 8, &done); if (done) break; const int mostLikelyType = (line >= 2) ? 4 : 0; // should var assignment or return be more likely? const int type2 = (indent.size() >= 12) ? mostLikelyType : // max indentation, no inner conditions ((type1 >= 5) ? mostLikelyType : type1); if (type2 == 0) { code << indent << " var" << getValue(data, dataSize, 5) << "=" << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; } else if (type2 == 1) { code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); } else if (type2 == 2) { code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); code << indent << " else\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); } else if (type2 == 3) { code << indent << " while (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); } else if (type2 == 4) { code << indent << " return " << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; if (indent.empty()) code << "}\n\n" << functionStart(); else break; } } if (!indent.empty()) code << indent << "}\n"; else code << " return 0;\n}\n"; return code.str(); } std::string generateCode2(const uint8_t *data, size_t dataSize) { std::ostringstream code; // create global constants constexpr uint8_t numberOfGlobalConstants = 0; /* const int numberOfGlobalConstants = getValue(data, dataSize, 5); for (int nr = 1; nr <= numberOfGlobalConstants; nr++) { const char *types[4] = {"char", "int", "long long", "float"}; code << "const " << types[getValue(data, dataSize, 4)] << " globalconstant" << nr << " = " << generateExpression2_Expr(data, dataSize, nr - 1) << ";\n"; } */ code << "int var1 = 1;\n" "int var2 = 0;\n" "int var3 = 1;\n" "int var4 = 0;\n" "int var5 = -1;\n\n"; code << generateExpression2_conditionalCode("", data, dataSize, numberOfGlobalConstants); return code.str(); } cppcheck-2.7/oss-fuzz/type2.h000066400000000000000000000001671417746362400162000ustar00rootroot00000000000000 #pragma once #include #include std::string generateCode2(const uint8_t *data, size_t dataSize); cppcheck-2.7/philosophy.md000066400000000000000000000053551417746362400157100ustar00rootroot00000000000000 # Cppcheck Philosophy It is important that everybody in the Cppcheck team has a consistent idea about how this tool should work. This is a static analyzer tool. ## Normal analysis - No false positives A fundamental goal is "no false positives". It is not possible to achieve "no false positives" completely. One case where false positives are OK is when the code is garbage. If the code is written as it is by design, then our goal is to not warn. If it is not known if there is a problem, then in general we need to bailout. We can only warn when we see that there is a problem. Stylistic checks are much more prone to false positives and therefore we should avoid writing stylistic checks mostly. Reporting issues in Trac: - If you see a false negative; report that as an enhancement. - If you see a false positive; report that as a defect. ### Inconclusive messages Inconclusive messages will be created if cppcheck cannot be sure there is an issue to warn but 50-50 probability. User shall enable inconclusive messages if they are willing to spend substantially more time on message verification in order to find more issues within a high false positive rate. Inconclusive messages shall not be used for new checks which are just being developed. There `settings.experimental` can be used. ## Bug hunting - Soundy analysis The goal is to detect nearly all bugs. It will not be possible to detect ALL bugs. For instance if the code is garbage or if the bug happens in a inline assembler code block. It will not be possible to avoid false alarms completely but we can not be sloppy about false alarms. There are tools that are too noisy. A handful of false alarms for a project is totally fine in this analysis. But 1000's of false alarms for the average project would not be ok. We want to detect UB. But we will not add checkers that are too noisy. Reporting issues in Trac: - If you see a false negative; report that as a defect. - If you see a false positive; report it as an enhancement. We should try hard to fix false positives however we can't have heuristics that we know will cause important false negatives. ## No configuration We want that a user can run Cppcheck without explicit -D and -I configuration. When this happens the false positives should be avoided. The user can reduce false negatives with configuration. ## Allow compiler extensions This is not just a tool for mainstream gcc/msvc c/c++ developers. If you can compile the code with a C/C++ compiler then our goal is that Cppcheck can check it. ## C++ language Our goal is to be highly portable. Users must be able to compile Cppcheck with GCC 4.6 or MSVS 2013. No C++14 is allowed. A subset of the C++11 is allowed. ## Avoid dependencies We are very careful about dependencies. cppcheck-2.7/platforms/000077500000000000000000000000001417746362400151675ustar00rootroot00000000000000cppcheck-2.7/platforms/aix_ppc64.xml000066400000000000000000000005611417746362400175100ustar00rootroot00000000000000 8 unsigned 2 4 8 8 4 8 8 8 8 2 cppcheck-2.7/platforms/arm32-wchar_t2.xml000066400000000000000000000005611417746362400203460ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 4 4 2 cppcheck-2.7/platforms/arm32-wchar_t4.xml000066400000000000000000000005611417746362400203500ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 4 4 4 cppcheck-2.7/platforms/arm64-wchar_t2.xml000066400000000000000000000005611417746362400203530ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 8 4 2 cppcheck-2.7/platforms/arm64-wchar_t4.xml000066400000000000000000000005611417746362400203550ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 8 4 4 cppcheck-2.7/platforms/avr8.xml000066400000000000000000000006041417746362400165710ustar00rootroot00000000000000 8 unsigned 1 2 2 4 8 4 4 4 2 2 2 cppcheck-2.7/platforms/cppcheck-platforms.rng000066400000000000000000000024571417746362400214740ustar00rootroot00000000000000 cppcheck-2.7/platforms/cray_sv1.xml000066400000000000000000000005621417746362400174430ustar00rootroot00000000000000 8 unsigned 2 8 8 8 8 8 16 8 8 8 cppcheck-2.7/platforms/elbrus-e1cp.xml000066400000000000000000000006051417746362400200340ustar00rootroot00000000000000 8 unsigned 1 2 4 8 8 4 8 16 8 8 4 cppcheck-2.7/platforms/mips32.xml000066400000000000000000000006021417746362400170240ustar00rootroot00000000000000 8 signed 1 2 4 4 8 4 8 8 4 4 4 cppcheck-2.7/platforms/msp430_eabi_large_datamodel.xml000066400000000000000000000005571417746362400231120ustar00rootroot00000000000000 8 signed 2 2 4 8 4 8 8 4 4 2 cppcheck-2.7/platforms/pic16.xml000066400000000000000000000006041417746362400166330ustar00rootroot00000000000000 8 unsigned 1 2 2 4 8 4 4 8 2 2 2 cppcheck-2.7/platforms/pic8-enhanced.xml000066400000000000000000000006041417746362400203170ustar00rootroot00000000000000 8 unsigned 1 2 2 4 8 4 4 4 2 2 2 cppcheck-2.7/platforms/pic8.xml000066400000000000000000000006041417746362400165540ustar00rootroot00000000000000 8 unsigned 1 2 2 4 4 4 4 4 2 2 2 cppcheck-2.7/platforms/unix32-unsigned.xml000066400000000000000000000005621417746362400206560ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 12 4 4 2 cppcheck-2.7/platforms/unix64-unsigned.xml000066400000000000000000000005621417746362400206630ustar00rootroot00000000000000 8 unsigned 2 4 8 8 4 8 16 8 8 4 cppcheck-2.7/pylintrc_travis000066400000000000000000000012561417746362400163430ustar00rootroot00000000000000[MESSAGES CONTROL] disable=C,R,W enable=mixed-indentation, trailing-whitespace, print-statement, literal-comparison, unnecessary-semicolon, mixed-line-endings, bad-open-mode, redundant-unittest-assert, boolean-datetime, deprecated-method, anomalous-unicode-escape-in-string, anomalous-backslash-in-string, bad-indentation [REPORTS] reports=no [TYPECHECK] # See https://stackoverflow.com/questions/10300082/how-to-prevent-python-pylint-complaining-about-socket-class-sendall-method ignored-classes=SQLObject,_socketobject [MASTER] init-hook='import sys; sys.path.append("./addons")' suggestion-mode=yes cppcheck-2.7/readme.md000066400000000000000000000172041417746362400147430ustar00rootroot00000000000000# **Cppcheck** |GitHub Actions|Linux Build Status|Windows Build Status|OSS-Fuzz|Coverity Scan Build Status|License| |:-:|:--:|:--:|:--:|:--:|:-:| |[![Github Action Status](https://github.com/danmar/cppcheck/workflows/CI/badge.svg)](https://github.com/danmar/cppcheck/actions?query=workflow%3ACI)|[![Linux Build Status](https://img.shields.io/travis/danmar/cppcheck/main.svg?label=Linux%20build)](https://travis-ci.org/danmar/cppcheck)|[![Windows Build Status](https://img.shields.io/appveyor/ci/danmar/cppcheck/main.svg?label=Windows%20build)](https://ci.appveyor.com/project/danmar/cppcheck/branch/main)|[![OSS-Fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/cppcheck.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:cppcheck)|[![Coverity Scan Build Status](https://img.shields.io/coverity/scan/512.svg)](https://scan.coverity.com/projects/512)|[![License](https://img.shields.io/badge/license-GPL3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0) ## About the name The original name of this program was "C++check", but it was later changed to "Cppcheck". Despite the name, Cppcheck is designed for both C and C++. ## Manual A manual is available [online](https://cppcheck.sourceforge.io/manual.pdf). ## Donate CPU Cppcheck is a hobby project with limited resources. You can help us by donating CPU (1 core or as many as you like). It is simple: 1. Download (and extract) Cppcheck source code. 2. Run script: python cppcheck/tools/donate-cpu.py. The script will analyse debian source code and upload the results to a cppcheck server. We need these results both to improve Cppcheck and to detect regressions. You can stop the script whenever you like with Ctrl C. ## Compiling Any C++11 compiler should work. For compilers with partial C++11 support it may work. If your compiler has the C++11 features that are available in Visual Studio 2013 / GCC 4.6 then it will work. To build the GUI, you need Qt. When building the command line tool, [PCRE](http://www.pcre.org/) is optional. It is used if you build with rules. There are multiple compilation choices: * qmake - cross platform build tool * cmake - cross platform build tool * Windows: Visual Studio (VS 2013 and above) * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (or later) * clang++ ### cmake Example, compiling Cppcheck with cmake: ```shell mkdir build cd build cmake .. cmake --build . ``` If you want to compile the GUI you can use the flag. -DBUILD_GUI=ON For rules support (requires pcre) use the flag. -DHAVE_RULES=ON For release builds it is recommended that you use: -DUSE_MATCHCOMPILER=ON Using cmake you can generate project files for Visual Studio,XCode,etc. ### qmake You can use the gui/gui.pro file to build the GUI. ```shell cd gui qmake make ``` ### Visual Studio Use the cppcheck.sln file. The file is configured for Visual Studio 2019, but the platform toolset can be changed easily to older or newer versions. The solution contains platform targets for both x86 and x64. To compile with rules, select "Release-PCRE" or "Debug-PCRE" configuration. pcre.lib (pcre64.lib for x64 builds) and pcre.h are expected to be in /externals then. A current version of PCRE for Visual Studio can be obtained using [vcpkg](https://github.com/microsoft/vcpkg). ### Visual Studio (from command line) If you do not wish to use the Visual Studio IDE, you can compile cppcheck from the command line the following command. ```shell msbuild cppcheck.sln ``` ### VS Code (on Windows) Install MSYS2 to get GNU toolchain with g++ and gdb (https://www.msys2.org/). Create a settings.json file in the .vscode folder with the following content (adjust path as necessary): ``` { "terminal.integrated.shell.windows": "C:\\msys64\\usr\\bin\\bash.exe", "terminal.integrated.shellArgs.windows": [ "--login", ], "terminal.integrated.env.windows": { "CHERE_INVOKING": "1", "MSYSTEM": "MINGW64", } } ``` Run "make" in the terminal to build cppcheck. For debugging create a launch.json file in the .vscode folder with the following content, which covers configuration for debugging cppcheck and misra.py: ``` { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "cppcheck", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/cppcheck.exe", "args": [ "--dump", "${workspaceFolder}/addons/test/misra/misra-test.c" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": true, "MIMode": "gdb", "miDebuggerPath": "C:/msys64/mingw64/bin/gdb.exe", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ] }, { "name": "misra.py", "type": "python", "request": "launch", "program": "${workspaceFolder}/addons/misra.py", "console": "integratedTerminal", "args": [ "${workspaceFolder}/addons/test/misra/misra-test.c.dump" ] } ] } ``` ### Qt Creator + MinGW The PCRE dll is needed to build the CLI. It can be downloaded here: http://software-download.name/pcre-library-windows/ ### GNU make Simple, unoptimized build (no dependencies): ```shell make ``` The recommended release build is: ```shell make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" ``` Flags: 1. `MATCHCOMPILER=yes` Python is used to optimise cppcheck. The Token::Match patterns are converted into C++ code at compile time. 2. `FILESDIR=/usr/share/cppcheck` Specify folder where cppcheck files are installed (addons, cfg, platform) 3. `HAVE_RULES=yes` Enable rules (PCRE is required if this is used) 4. `CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function"` Enables most compiler optimizations, disables cppcheck-internal debugging code and enables basic compiler warnings. ### g++ (for experts) If you just want to build Cppcheck without dependencies then you can use this command: ```shell g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Iexternals/picojson -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp ``` If you want to use `--rule` and `--rule-file` then dependencies are needed: ```shell g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Ilib -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp ``` ### MinGW ```shell mingw32-make LDFLAGS=-lshlwapi ``` ### Other Compiler/IDE 1. Create an empty project file / makefile. 2. Add all cpp files in the cppcheck cli and lib folders to the project file / makefile. 3. Add all cpp files in the externals folders to the project file / makefile. 4. Compile. ### Cross compiling Win32 (CLI) version of Cppcheck in Linux ```shell sudo apt-get install mingw32 make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" RDYNAMIC="" mv cppcheck cppcheck.exe ``` ## Packages You can install Cppcheck with yum/apt/brew/etc. The official rpms are built with these files: https://src.fedoraproject.org/rpms/cppcheck/tree/master ## Webpage https://cppcheck.sourceforge.io/ cppcheck-2.7/readme.txt000066400000000000000000000107061417746362400151620ustar00rootroot00000000000000========= Cppcheck ========= About The original name of this program is "C++check" but it was later changed to "cppcheck". Manual A manual is available online: https://cppcheck.sourceforge.io/manual.pdf Compiling Any C++11 compiler should work. For compilers with partial C++11 support it may work. If your compiler has the C++11 features that are available in Visual Studio 2013 / GCC 4.6 then it will work. To build the GUI, you need Qt. While building the command line tool, PCRE is optional. It is used if you build with rules. For "bug hunting" you need Z3. Installing Z3: * debian: "sudo apt-get install libz3-dev * windows: 32-bit: https://github.com/Z3Prover/z3/releases/download/z3-4.8.7/z3-4.8.7-x86-win.zip 64-bit: https://github.com/Z3Prover/z3/releases/download/z3-4.8.7/z3-4.8.7-x64-win.zip If you do not want to install z3 in some "system" include/lib paths you can put the files in cppcheck/externals/z3/include and cppcheck/externals/z3/bin There are multiple compilation choices: * qmake - cross platform build tool * cmake - cross platform build tool * Windows: Visual Studio * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (or later) * clang++ cmake ===== Example, compiling Cppcheck with cmake: mkdir build cd build cmake .. cmake --build . If you want to compile the GUI you can use the flag -DBUILD_GUI=ON For rules support (requires pcre) use the flag -DHAVE_RULES=ON For release builds it is recommended that you use: -DUSE_MATCHCOMPILER=ON qmake ===== You can use the gui/gui.pro file to build the GUI. cd gui qmake make Visual Studio ============= Use the cppcheck.sln file. The file is configured for Visual Studio 2019, but the platform toolset can be changed easily to older or newer versions. The solution contains platform targets for both x86 and x64. To compile with rules, select "Release-PCRE" or "Debug-PCRE" configuration. pcre.lib (pcre64.lib for x64 builds) and pcre.h are expected to be in /externals then. A current version of PCRE for Visual Studio can be obtained using vcpkg: https://github.com/microsoft/vcpkg Qt Creator + mingw ================== The PCRE dll is needed to build the CLI. It can be downloaded here: http://software-download.name/pcre-library-windows/ gnu make ======== Simple build (no dependencies): make The recommended release build is: make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes Flags: MATCHCOMPILER=yes : Python is used to optimise cppcheck at compile time FILESDIR=/usr/share/cppcheck : Specify folder where cppcheck files are installed HAVE_RULES=yes : Enable rules (pcre is required if this is used) g++ (for experts) ================= If you just want to build Cppcheck without dependencies then you can use this command: g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp If you want to use --rule and --rule-file then dependencies are needed: g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Ilib -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp mingw ===== The "LDFLAGS=-lshlwapi" is needed when building with mingw mingw32-make LDFLAGS=-lshlwapi other compilers/ide =================== 1. Create a empty project file / makefile. 2. Add all cpp files in the cppcheck cli and lib folders to the project file / makefile. 3. Add all cpp files in the externals folders to the project file / makefile. 4. Compile. Cross compiling Win32 (CLI) version of Cppcheck in Linux sudo apt-get install mingw32 make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" mv cppcheck cppcheck.exe Packages You can install Cppcheck with yum/apt/brew/etc. The official rpms are built with these files: https://src.fedoraproject.org/rpms/cppcheck/tree/master Webpage https://cppcheck.sourceforge.io/ cppcheck-2.7/readmeja.md000066400000000000000000000130371417746362400152560ustar00rootroot00000000000000# Cppcheck | Linux ビルド状態 | Windows ビルド状態 | Coverity Scan Build 状態 | |:--:|:--:|:--:| | [![Linux ビルド状態](https://img.shields.io/travis/danmar/cppcheck/master.svg?label=Linux%20build)](https://travis-ci.org/danmar/cppcheck) | [![Windows ビルド状態](https://img.shields.io/appveyor/ci/danmar/cppcheck/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/danmar/cppcheck/branch/master) | [![Coverity Scan Build 状態](https://img.shields.io/coverity/scan/512.svg)](https://scan.coverity.com/projects/512) | ## 名前について このプログラムは元々、"C++check"という名前でしたが後に"Cppcheck"に変更されました。 このような名前ですが、Cppcheckは CとC++の両方に対して設計されています。 ## マニュアル マニュアルは[オンライン上に](https://cppcheck.sourceforge.io/manual.pdf)あります。 ## ビルド C++11に対応したコンパイラが利用できます。部分的にC++11にサポートしたコンパイラも利用できるかもしれません。もし、あなたのコンパイラがVisual Studio 2013や GCC 4.6で利用できるC++11機能がサポートされているなら、そのコンパイラが利用できます。 GUIも利用する場合、Qtライブラリが必要です。 コマンドラインツールをビルドする場合、[PCRE](http://www.pcre.org/)はオプションです。これはルールを作成するために利用します。 コンパイル上の選択肢がいくつかあります。 * qmake - クロスプラットフォームのビルドツール * cmake - クロスプラットフォームのビルドツール * Windows: Visual Studio (VS 2013 またはそれ以上) * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (またはそれ以上) * clang++ ### cmake cmakeでCppcheckをコンパイルする例 ```shell mkdir build cd build cmake .. cmake --build . ``` C++標準を指定する必要がある場合次のオプションを指定します。 -DCMAKE_CXX_STANDARD=11 CppcheckのGUIが必要な場合次のフラグを指定します。 -DBUILD_GUI=ON pcreが必要になりますが、正規表現のルールサポートが必要な場合次のフラグを指定します。 -DHAVE_RULES=ON ### qmake GUIをビルドするには、gui/gui.proファイルが利用できます。 ```shell cd gui qmake make ``` ### Visual Studio cppcheck.slnファイルが利用できます。このファイルは、Visual Studio 2019向けです。しかし、このプラットフォームツールセットはこれより新しいバージョンまたは古いバージョン向けに変更できます。このソルーションには、プラットフォームターゲットとしてx86とx64があります。 ルールをコンパイルするためには、"Release-PCRE" または "Debug-PCRE" 設定を選択してください。pcre.lib (または pcre64.lib x64ビルド向け) と pcre.h を /externals にコピーしてください。Visual Studio のための PCRE の最新バージョンは [vcpkg](https://github.com/microsoft/vcpkg) から取得できます。 ### Qt Creator + MinGW コマンドラインツールをビルドするには、PCRE.dllが必要です。これは以下のURLからダウンロードできます。: http://software-download.name/pcre-library-windows/ ### GNU make 単純で最適化しないビルド(依存関係なし): ```shell make ``` 推奨するリリースビルド方法: ```shell make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" ``` フラグ: 1. `MATCHCOMPILER=yes` cppcheckの最適化にPythonを使用します。Token::Match パターンはコンパイル時にlC++コードに変換されます。 2. `FILESDIR=/usr/share/cppcheck` cppcheckの設定ファイル(addon や cfg や platform)を置くディレクトリを指定します。 3. `HAVE_RULES=yes` ルール機能の有効化 (ルール機能には PCRE が必要です)設定です。 4. `CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function"` ほとんどのコンパイラの最適化オプション、cppcheckの内部デバッグコードの無効化、基本的なコンパイラ警告の有効化 ### g++ (エキスパート向け) 依存関係なく Cppcheckをビルドしたい場合、次のコマンドを利用できます。 ```shell g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp ``` `--rule` や `--rule-file` を利用する場合、依存ライブラリが必要です。 ```shell g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp ``` ### MinGW ```shell mingw32-make LDFLAGS=-lshlwapi ``` ### その他のコンパイラ/IDE 1. 空のプロジェクトファイル /makefileの作成 2. cppcheck cli それに lib ディレクトリに含まれる全てのcppファイルをそのプロジェクトファイルまたはmakefileに加えます。 3. externalsフォルダの全てのcppファイルをプロジェクトファイル / makefileに追加します。 4. ビルド ### Linux で Win32 コマンドラインバージョンをクロスコンパイル ```shell sudo apt-get install mingw32 make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" RDYNAMIC="" mv cppcheck cppcheck.exe ``` ## Webページ https://cppcheck.sourceforge.io/ cppcheck-2.7/releasenotes.txt000066400000000000000000000004151417746362400164120ustar00rootroot00000000000000release notes for cppcheck-2.7 Add support for container views. The `view` attribute has been added to the `` library tag to specify the class is a view. The lifetime analysis has been updated to use this new attribute to find dangling lifetime containers. cppcheck-2.7/requirements.txt000066400000000000000000000001421417746362400164410ustar00rootroot00000000000000pcre,pfultz2/pcre@8.45 -H sha256:d6f7182602a775a7d500a0cedca6449af0400c6493951513046d17615ed0bf11 cppcheck-2.7/rules/000077500000000000000000000000001417746362400143125ustar00rootroot00000000000000cppcheck-2.7/rules/empty-catch-block.xml000066400000000000000000000003541417746362400203440ustar00rootroot00000000000000 normal style Empty catch block found. cppcheck-2.7/rules/error-reporting.xml000066400000000000000000000004631417746362400201770ustar00rootroot00000000000000 Severity :: fromString \( "\w+" \) ConstantSeverityFromString style Constant severity lookups should be done via Severity::constant. cppcheck-2.7/rules/show-all-defines.rule000066400000000000000000000003511417746362400203430ustar00rootroot00000000000000 define .* showalldefines information cppcheck-2.7/rules/stl.xml000066400000000000000000000005541417746362400156420ustar00rootroot00000000000000 \. find \( "[^"]+?" \) == \d+ UselessSTDStringFind performance When looking for a string at a fixed position compare is faster. cppcheck-2.7/rules/strlen-empty-str.xml000066400000000000000000000005001417746362400203000ustar00rootroot00000000000000 if \( ([!] )*?(strlen) \( \w+? \) ([>] [0] )*?\) { StrlenEmptyString performance Using strlen() to check if a string is empty is not efficient. cppcheck-2.7/rules/suggest_nullptr.xml000066400000000000000000000006111417746362400202730ustar00rootroot00000000000000 raw modernizeUseNullPtr style Prefer to use a 'nullptr' instead of initializing a pointer with 0. cppcheck-2.7/rules/token-matching.xml000066400000000000000000000023271417746362400177500ustar00rootroot00000000000000 Token :: (?:findm|(?:simple|)M)atch \([^,]+,\s+"(?:\s+|[^"]+?\s+") TokenMatchSpacing style Useless extra spacing for Token::*Match. (?U)Token :: Match \([^,]+,\s+"[^%|!\[\]]+" UseTokensimpleMatch error Token::simpleMatch should be used to match tokens without special pattern requirements. \b[\w_]+ \. tokAt \( 0 \) TokentokAt0 error tok->tokAt(0) is a slow way to say tok. \b[\w_]+ \. strAt \( 0 \) TokenstrAt0 error tok->strAt(0) is a slow way to say tok->str() cppcheck-2.7/rules/unused-deref.xml000066400000000000000000000004221417746362400174200ustar00rootroot00000000000000 [;{}] [*] \w+? (\+\+|\-\-) ; UnusedDeref style Redundant * found, "*p++" is the same as "*(p++)". cppcheck-2.7/runformat000077500000000000000000000025531417746362400151300ustar00rootroot00000000000000#!/bin/bash # # uncrustify-0.72 is used to format cppcheck source code. # source code: https://github.com/uncrustify/uncrustify/archive/refs/tags/uncrustify-0.72.0.zip # building in Linux: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make # building in Windows: mkdir build && cd build && cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release .. && nmake UNCRUSTIFY_VERSION="0.72.0" UNCRUSTIFY="${UNCRUSTIFY-uncrustify}" DETECTED_VERSION=$("$UNCRUSTIFY" --version 2>&1 | grep -o -E '[0-9.]+') if [ "$DETECTED_VERSION" != "${UNCRUSTIFY_VERSION}" ]; then echo "You should use version: ${UNCRUSTIFY_VERSION}" echo "Detected version: ${DETECTED_VERSION}" exit 1 fi # OS variables [ $(uname -s) = "Darwin" ] && export OSX=1 && export UNIX=1 [ $(uname -s) = "Linux" ] && export LINUX=1 && export UNIX=1 uname -s | grep -q "_NT-" && export WINDOWS=1 if [ $OSX ] then export CPUCOUNT=$(sysctl -n hw.ncpu) elif [ $LINUX ] then export CPUCOUNT=$(nproc) else export CPUCOUNT="1" fi function formatCplusplus { find $1 -iname '*.h' \ -o -iname '*.c' \ -o -iname '*.cpp' \ | xargs -n 1 -P $CPUCOUNT -I{} -t $UNCRUSTIFY -c .uncrustify.cfg --no-backup {} } formatCplusplus cli/ formatCplusplus democlient/ formatCplusplus gui/ formatCplusplus lib/ formatCplusplus oss-fuzz/ formatCplusplus test/ formatCplusplus tools/ formatCplusplus samples/ cppcheck-2.7/samples/000077500000000000000000000000001417746362400146245ustar00rootroot00000000000000cppcheck-2.7/samples/AssignmentAddressToInteger/000077500000000000000000000000001417746362400220635ustar00rootroot00000000000000cppcheck-2.7/samples/AssignmentAddressToInteger/bad.c000066400000000000000000000001561417746362400227570ustar00rootroot00000000000000int foo(int *p) { int a = p; return a + 4; } int main() { int i[10]; foo(i); return 0; } cppcheck-2.7/samples/AssignmentAddressToInteger/good.c000066400000000000000000000001401417746362400231520ustar00rootroot00000000000000int* foo(int *p) { return p + 4; } int main() { int i[10]; foo(i); return 0; } cppcheck-2.7/samples/AssignmentAddressToInteger/out.txt000066400000000000000000000002471417746362400234360ustar00rootroot00000000000000samples\AssignmentAddressToInteger\bad.c:3:11: portability: Assigning a pointer to an integer is not portable. [AssignmentAddressToInteger] int a = p; ^ cppcheck-2.7/samples/arrayIndexOutOfBounds/000077500000000000000000000000001417746362400210625ustar00rootroot00000000000000cppcheck-2.7/samples/arrayIndexOutOfBounds/bad.c000066400000000000000000000001251417746362400217520ustar00rootroot00000000000000int a[2]; int main() { a[0] = 0; a[1] = 0; a[2] = 0; return a[0]; } cppcheck-2.7/samples/arrayIndexOutOfBounds/good.c000066400000000000000000000001221417746362400221510ustar00rootroot00000000000000int a[3]; int main() { a[0] = 0; a[1] = 0; a[2] = 0; return 0; } cppcheck-2.7/samples/arrayIndexOutOfBounds/out.txt000066400000000000000000000002271417746362400224330ustar00rootroot00000000000000samples\arrayIndexOutOfBounds\bad.c:7:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[2] = 0; ^ cppcheck-2.7/samples/autoVariables/000077500000000000000000000000001417746362400174255ustar00rootroot00000000000000cppcheck-2.7/samples/autoVariables/bad.c000066400000000000000000000001511417746362400203140ustar00rootroot00000000000000void foo(int **a) { int b = 1; *a = &b; } int main() { int *c; foo(&c); return 0; } cppcheck-2.7/samples/autoVariables/good.c000066400000000000000000000001711417746362400205200ustar00rootroot00000000000000void foo(int **a) { int b = 1; **a = b; } int main() { int b; int *c = &b; foo(&c); return 0; } cppcheck-2.7/samples/autoVariables/out.txt000066400000000000000000000002141417746362400207720ustar00rootroot00000000000000samples\autoVariables\bad.c:4:5: error: Address of local auto-variable assigned to a function parameter. [autoVariables] *a = &b; ^ cppcheck-2.7/samples/bufferAccessOutOfBounds/000077500000000000000000000000001417746362400213475ustar00rootroot00000000000000cppcheck-2.7/samples/bufferAccessOutOfBounds/bad.c000066400000000000000000000001471417746362400222430ustar00rootroot00000000000000int main() { int a[2]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-2.7/samples/bufferAccessOutOfBounds/good.c000066400000000000000000000001471417746362400224450ustar00rootroot00000000000000int main() { int a[3]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-2.7/samples/bufferAccessOutOfBounds/out.txt000066400000000000000000000002421417746362400227150ustar00rootroot00000000000000samples\bufferAccessOutOfBounds\bad.c:6:10: error: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[i] = 0; ^ cppcheck-2.7/samples/erase/000077500000000000000000000000001417746362400157235ustar00rootroot00000000000000cppcheck-2.7/samples/erase/bad.cpp000066400000000000000000000004611417746362400171560ustar00rootroot00000000000000#include int main() { std::vector items; items.push_back(1); items.push_back(2); items.push_back(3); std::vector::iterator iter; for (iter = items.begin(); iter != items.end(); ++iter) { if (*iter == 2) { items.erase(iter); } } } cppcheck-2.7/samples/erase/good.cpp000066400000000000000000000005261417746362400173620ustar00rootroot00000000000000#include int main() { std::vector items; items.push_back(1); items.push_back(2); items.push_back(3); std::vector::iterator iter; for (iter = items.begin(); iter != items.end();) { if (*iter == 2) { iter = items.erase(iter); } else { ++iter; } } } cppcheck-2.7/samples/erase/out.txt000066400000000000000000000022751417746362400173010ustar00rootroot00000000000000samples\erase\bad.cpp:9:32: error: Using iterator to local container 'items' that may be invalid. [invalidContainer] for (iter = items.begin(); iter != items.end(); ++iter) { ^ samples\erase\bad.cpp:9:17: note: Iterator to container is created here. for (iter = items.begin(); iter != items.end(); ++iter) { ^ samples\erase\bad.cpp:10:19: note: Assuming condition is true. if (*iter == 2) { ^ samples\erase\bad.cpp:10:19: note: Assuming condition is true. if (*iter == 2) { ^ samples\erase\bad.cpp:9:37: note: Assuming condition is true. for (iter = items.begin(); iter != items.end(); ++iter) { ^ samples\erase\bad.cpp:11:13: note: After calling 'erase', iterators or references to the container's data may be invalid . items.erase(iter); ^ samples\erase\bad.cpp:4:22: note: Variable created here. std::vector items; ^ samples\erase\bad.cpp:9:32: note: Using iterator to local container 'items' that may be invalid. for (iter = items.begin(); iter != items.end(); ++iter) { ^ cppcheck-2.7/samples/memleak/000077500000000000000000000000001417746362400162375ustar00rootroot00000000000000cppcheck-2.7/samples/memleak/bad.c000066400000000000000000000002011417746362400171220ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; return result; } cppcheck-2.7/samples/memleak/good.c000066400000000000000000000002161417746362400173320ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; free(a); return result; } cppcheck-2.7/samples/memleak/out.txt000066400000000000000000000001241417746362400176040ustar00rootroot00000000000000samples/memleak/bad.c:8:5: error: Memory leak: a [memleak] return result; ^ cppcheck-2.7/samples/outOfBounds/000077500000000000000000000000001417746362400170735ustar00rootroot00000000000000cppcheck-2.7/samples/outOfBounds/bad.c000066400000000000000000000001461417746362400177660ustar00rootroot00000000000000#include int main() { char str[5]; strcpy(str, "0123456789abcdef"); return 0; } cppcheck-2.7/samples/outOfBounds/good.c000066400000000000000000000001461417746362400201700ustar00rootroot00000000000000#include int main() { char str[10]; snprintf(str, 10, "%s", "abc"); return 0; } cppcheck-2.7/samples/outOfBounds/out.txt000066400000000000000000000002311417746362400204370ustar00rootroot00000000000000samples\outOfBounds\bad.c:5:12: error: Buffer is accessed out of bounds: str [bufferAccessOutOfBounds] strcpy(str, "0123456789abcdef"); ^ cppcheck-2.7/samples/resourceLeak/000077500000000000000000000000001417746362400172505ustar00rootroot00000000000000cppcheck-2.7/samples/resourceLeak/bad.c000066400000000000000000000001631417746362400201420ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; return 0; } cppcheck-2.7/samples/resourceLeak/good.c000066400000000000000000000002011417746362400203350ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; fclose(a); return 0; } cppcheck-2.7/samples/resourceLeak/out.txt000066400000000000000000000001331417746362400206150ustar00rootroot00000000000000samples\resourceLeak\bad.c:8:5: error: Resource leak: a [resourceLeak] return 0; ^ cppcheck-2.7/samples/syntaxError/000077500000000000000000000000001417746362400171645ustar00rootroot00000000000000cppcheck-2.7/samples/syntaxError/bad.c000066400000000000000000000000551417746362400200560ustar00rootroot00000000000000int main() { return 0; #ifdef A } #endif cppcheck-2.7/samples/syntaxError/good.c000066400000000000000000000000561417746362400202610ustar00rootroot00000000000000int main() { #ifndef A #endif return 0; } cppcheck-2.7/samples/syntaxError/out.txt000066400000000000000000000001321417746362400205300ustar00rootroot00000000000000samples\syntaxError\bad.c:2:1: error: Unmatched '{'. Configuration: ''. [syntaxError] { ^ cppcheck-2.7/snap/000077500000000000000000000000001417746362400141215ustar00rootroot00000000000000cppcheck-2.7/snap/gui/000077500000000000000000000000001417746362400147055ustar00rootroot00000000000000cppcheck-2.7/snap/gui/cppcheck-gui.desktop000066400000000000000000000003331417746362400206410ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=Cppcheck Comment=A tool for static C/C++ code analysis Exec=cppcheck-gui Icon=${SNAP}/meta/gui/cppcheck-gui.png Terminal=true StartupNotify=true Categories=Development;Debugger;Qt; cppcheck-2.7/snap/gui/cppcheck-gui.png000066400000000000000000000047571417746362400177720ustar00rootroot00000000000000PNG  IHDRAAE IDATxt$񎝬m۶mڶm\۶m۾kS{*'әAMwuUX^V x/1ovb :n*m⃟ `.A 11V>2`vIJzFfH/S6o>#S=oW2iҫj-n[Ixx.1B<$((X/g";w^]c۷_3ޗWH2Mh3(m 'QSAX Ԭ[. ΧҲ)QYo=Z Iɒ s煲l>X?wA4C@T0;]uGj.npaUTl3W!_Leow8Wo2G"N` ZP]C'!8Fg;ZLTFQN@zZi\EsE\ͻzBȍ.]ޣ:[ȓntƍ{L(P/\n? @6j`e9!|Ѩ1m;~Y!,Xdyvb3d o,jdxVP ,tU!Gg!n^ǔH5}u مЬYԯ?4]ХPE!t 5Hmo͚`=q˒{=Hdd<3nCHJzZ7< ~n #f6ȰނzO. +QN+=^A#$6}8BFߣ<ҥ qwim:iwG4Uk*a읖~ĒY'>Gh QqUjƺ'd;\4F+wmJH,W`X~+#&ԁ/P=0NrSijrȨt_iےUnK$"*5xᶪJJ'%4'5)mVc G`ǎR׍řuXn d},ݒ]- K=ƫ EᲚ$27.ѣ\ siQ""f}wH|.K&'9,ך/#&GU_Ԫ՗0vCanݖLr~FEhOC]u)HѲrzAKj65Q.+q|YG^mUNO|mS,q1kUgsS7YRd_:DqIlԚ?RZy5DX: { ] pK:Uu' `(_~6TuBHD%gݖf݈_71K0jH|L{&3I=荀8)Uɒv2;?\ ?k!CQO]d !~D T(KPiN< /OAC O0>g,-I>z#~6d=vbFߡ;V}-tzoNC, M=_P~pjQc|x t:!jf`:^]_<>jVQsLmï{!Ӽ?X=eJN͂=U[a{%)Am˜ƬU/[V0 AY\xwTٶoP  PťjIƚzD ˪4@OЏL5^-0_BRWr[-XA1  jta3_X Pc==W,cTP7UC==YyGԓ?#u`4vaj}ʧ(lQA!Ju4\.e r{h,9VѰpc_پ \@ۍOU脀WϥIENDB`cppcheck-2.7/snap/snapcraft.yaml000066400000000000000000000030671417746362400167740ustar00rootroot00000000000000name: cppcheckgui version: '1.81.99' summary: A tool for static C/C++ code analysis description: | A tool for static C/C++ code analysis grade: stable confinement: strict icon: snap/gui/cppcheck-gui.png type: app apps: cppcheckgui: command: desktop-launch ${SNAP}/bin/cppcheck-gui plugs: [home, unity7, x11, network-bind, network-control] parts: cppcheckgui: source-type: git plugin: cmake configflags: - -DBUILD_GUI=ON after: [desktop-qt5] build-packages: # A list of Ubuntu packages to be installed on the host to aid in building the part. # These packages will not go into the final snap. - build-essential - qt5-default - qtbase5-dev - dpkg-dev # For Qt5LinguistTools - qttools5-dev - qttools5-dev-tools desktop-qt5: stage-packages: # A set of Ubuntu packages to be downloaded and unpacked to join the part before it’s built. # Note that these packages are not installed on the host. # Like the rest of the part, all files from these packages will make it into the final snap unless filtered out via the prime keyword. - libqt5gui5 - libqt5svg5 # for loading icon themes which are svg - libtiff5-dev - libjpeg8-dev - libxkbcommon0 - ttf-ubuntu-font-family - dmz-cursor-theme - light-themes - shared-mime-info - libgdk-pixbuf2.0-0 - locales-all - xcb - libxcb1 cppcheck-2.7/test/000077500000000000000000000000001417746362400141375ustar00rootroot00000000000000cppcheck-2.7/test/CMakeLists.txt000066400000000000000000000155041417746362400167040ustar00rootroot00000000000000if (BUILD_TESTS) file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") list(APPEND testrunner_SOURCES ${hdrs} ${srcs} $ $ $) if(USE_BUNDLED_TINYXML2) list(APPEND testrunner_SOURCES $) endif() add_executable(testrunner ${testrunner_SOURCES}) target_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/lib/ ${PROJECT_SOURCE_DIR}/cli/) if(USE_BUNDLED_TINYXML2) target_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2) endif() target_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/) if (HAVE_RULES) target_link_libraries(testrunner ${PCRE_LIBRARY}) endif() if (USE_Z3) target_link_libraries(testrunner ${Z3_LIBRARIES}) endif() if (WIN32 AND NOT BORLAND) if(NOT MINGW) target_link_libraries(testrunner Shlwapi.lib) else() target_link_libraries(testrunner shlwapi) endif() endif() if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) target_link_libraries(testrunner tinyxml2) endif() if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) target_precompile_headers(testrunner PRIVATE precompiled.h) endif() add_dependencies(testrunner copy_cfg) add_dependencies(testrunner copy_addons) if (LIBXML2_XMLLINT_EXECUTABLE) # TODO: get rid of the copy add_custom_target(checkcfg ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cfg/runtests.sh DEPENDS cppcheck validateCFG WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cfg) endif() if (REGISTER_TESTS) cmake_minimum_required(VERSION 3.4) # cmake_policy(SET CMP0064 NEW) cmake_policy(SET CMP0064 NEW) cmake_policy(SET CMP0057 NEW) include(CTest) find_package(Threads REQUIRED) include(ProcessorCount) ProcessorCount(N) set(CTEST_PARALLEL_LEVEL ${N} CACHE STRING "CTest parallel level") set(CTEST_TIMEOUT 90 CACHE STRING "CTest timeout") add_custom_target(check ${CMAKE_CTEST_COMMAND} --output-on-failure -j ${CTEST_PARALLEL_LEVEL} -C ${CMAKE_CFG_INTDIR} --timeout ${CTEST_TIMEOUT} DEPENDS testrunner cppcheck) set(SKIP_TESTS "" CACHE STRING "A list of tests to skip") function(add_fixture NAME) set(options) set(oneValueArgs WORKING_DIRECTORY) set(multiValueArgs) cmake_parse_arguments(PARSE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if (${NAME} IN_LIST SKIP_TESTS) elseif(TEST ${NAME}) else() set(WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) if (PARSE_WORKING_DIRECTORY) set(WORKING_DIRECTORY ${PARSE_WORKING_DIRECTORY}) endif() add_test(NAME ${NAME} COMMAND testrunner ${NAME} WORKING_DIRECTORY ${WORKING_DIRECTORY}) endif() endfunction() function(fixture_cost NAME COST) if(TEST ${NAME}) set_tests_properties(${NAME} PROPERTIES COST ${COST}) endif() endfunction() # TODO: what is this? add_fixture(TestSamples WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) foreach(SRC ${srcs}) file(STRINGS ${SRC} FIXTURE_LINE REGEX "TestFixture\\(" LIMIT_COUNT 1) if(FIXTURE_LINE MATCHES "TestFixture\\(\"([a-zA-z0-9]+)\"\\)") add_fixture(${CMAKE_MATCH_1}) endif() endforeach() # TODO: parse files for REGISTER_TEST macro add_fixture(TestLeakAutoVarStrcpy) add_fixture(TestLeakAutoVarWindows) add_fixture(TestMemleakInFunction) add_fixture(TestMemleakInClass) add_fixture(TestMemleakStructMember) add_fixture(TestMemleakNoVar) function(add_cfg CFG_TEST) set(options INCONCLUSIVE) set(oneValueArgs PLATFORM NAME) set(multiValueArgs LIBRARY) cmake_parse_arguments(PARSE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(PARSE_LIBRARY) string(REPLACE ";" "," LIBRARY "${PARSE_LIBRARY}") else() get_filename_component(LIBRARY ${CFG_TEST} NAME_WE) endif() set(PLATFORM unix64) if(PARSE_PLATFORM) set(PLATFORM ${PARSE_PLATFORM}) endif() if(PARSE_NAME) set(TEST_NAME ${PARSE_NAME}) else() string(MAKE_C_IDENTIFIER ${CFG_TEST} TEST_NAME) endif() set(INCONCLUSIVE) if(PARSE_INCONCLUSIVE) set(INCONCLUSIVE "--inconclusive") endif() if (${TEST_NAME} IN_LIST SKIP_TESTS) else() # TODO: add syntax check add_test(NAME cfg-${TEST_NAME} COMMAND $ --check-library --platform=${PLATFORM} --library=${LIBRARY} --enable=information --enable=style --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr --template="{file}:{line}:{severity}:{id}:{message}" ${INCONCLUSIVE} ${CMAKE_CURRENT_SOURCE_DIR}/cfg/${CFG_TEST} ) endif() endfunction() add_cfg(posix.c) add_cfg(gnu.c LIBRARY posix;gnu) add_cfg(qt.cpp INCONCLUSIVE) add_cfg(bsd.c) add_cfg(std.c INCONCLUSIVE) add_cfg(std.cpp INCONCLUSIVE) add_cfg(windows.cpp INCONCLUSIVE NAME windows32A PLATFORM win32A) add_cfg(windows.cpp INCONCLUSIVE NAME windows32W PLATFORM win32W) add_cfg(windows.cpp INCONCLUSIVE NAME windows64 PLATFORM win64) add_cfg(wxwidgets.cpp INCONCLUSIVE) add_cfg(gtk.c INCONCLUSIVE) add_cfg(boost.cpp INCONCLUSIVE) add_cfg(sqlite3.c INCONCLUSIVE) add_cfg(openmp.c) add_cfg(python.c) add_cfg(lua.c) add_cfg(libcurl.c) add_cfg(cairo.c) add_cfg(googletest.cpp INCONCLUSIVE) add_cfg(kde.cpp INCONCLUSIVE) add_cfg(libsigc++.cpp) add_cfg(openssl.c) add_cfg(opencv2.cpp) # Set cost of the more expensive tests to help improve parallel scheduling # of tests fixture_cost(TestIO 20) fixture_cost(cfg-std_c 8) fixture_cost(TestThreadExecutor 5) fixture_cost(TestLeakAutoVarRecursiveCountLimit 4) fixture_cost(TestTokenizer 4) endif() endif() cppcheck-2.7/test/bug-hunting/000077500000000000000000000000001417746362400163665ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve.py000066400000000000000000000040171417746362400175170ustar00rootroot00000000000000# Test if --bug-hunting works using cve tests import glob import logging import os import sys import subprocess if sys.argv[0] in ('test/bug-hunting/cve.py', './test/bug-hunting/cve.py'): CPPCHECK_PATH = './cppcheck' TEST_SUITE = 'test/bug-hunting/cve' else: CPPCHECK_PATH = '../../cppcheck' TEST_SUITE = 'cve' slow = '--slow' in sys.argv logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', datefmt='%H:%M:%S') def test(test_folder): logging.info(test_folder) cmd_file = os.path.join(test_folder, 'cmd.txt') expected_file = os.path.join(test_folder, 'expected.txt') cmd = ['nice', CPPCHECK_PATH, '-D__GNUC__', '--bug-hunting', '--inconclusive', '--platform=unix64', '--template={file}:{line}:{id}', '-rp=' + test_folder] if os.path.isfile(cmd_file): for line in open(cmd_file, 'rt'): if len(line) > 1: cmd.append(line.strip()) cmd.append(test_folder) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() stdout = comm[0].decode(encoding='utf-8', errors='ignore') stderr = comm[1].decode(encoding='utf-8', errors='ignore') with open(expected_file, 'rt') as f: for expected in f.readlines(): if expected.strip() not in stderr.split('\n'): print('FAILED. Expected result not found: ' + expected) print('Command:') print(' '.join(cmd)) print('Output:') print(stderr) sys.exit(1) if (slow is False) and len(sys.argv) > 1: test(sys.argv[1]) sys.exit(0) SLOW = [] for test_folder in sorted(glob.glob(TEST_SUITE + '/CVE*')): if slow is False: check = False for s in SLOW: if s in test_folder: check = True if check is True: logging.info('skipping %s', test_folder) continue test(test_folder) cppcheck-2.7/test/bug-hunting/cve/000077500000000000000000000000001417746362400171435ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2018-19872/000077500000000000000000000000001417746362400206405ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2018-19872/README000066400000000000000000000001071417746362400215160ustar00rootroot00000000000000Project: Qt Details: https://nvd.nist.gov/vuln/detail/CVE-2018-19872 cppcheck-2.7/test/bug-hunting/cve/CVE-2018-19872/expected.txt000066400000000000000000000001201417746362400231730ustar00rootroot00000000000000qppmhandler.cpp:223:bughuntingDivByZero qppmhandler.cpp:255:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2018-19872/qppmhandler.cpp000066400000000000000000000434021417746362400236620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "private/qppmhandler_p.h" #ifndef QT_NO_IMAGEFORMAT_PPM #include #include #include #include #include QT_BEGIN_NAMESPACE /***************************************************************************** PBM/PGM/PPM (ASCII and RAW) image read/write functions *****************************************************************************/ static void discard_pbm_line(QIODevice *d) { const int buflen = 100; char buf[buflen]; int res = 0; do { res = d->readLine(buf, buflen); } while (res > 0 && buf[res-1] != '\n'); } static int read_pbm_int(QIODevice *d) { char c; int val = -1; bool digit; for (;;) { if (!d->getChar(&c)) // end of file break; digit = isdigit((uchar) c); if (val != -1) { if (digit) { val = 10*val + c - '0'; continue; } else { if (c == '#') // comment discard_pbm_line(d); break; } } if (digit) // first digit val = c - '0'; else if (isspace((uchar) c)) continue; else if (c == '#') discard_pbm_line(d); else break; } return val; } static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc) { char buf[3]; if (device->read(buf, 3) != 3) // read P[1-6] return false; if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2]))) return false; type = buf[1]; if (type < '1' || type > '6') return false; w = read_pbm_int(device); // get image width h = read_pbm_int(device); // get image height if (type == '1' || type == '4') mcc = 1; // ignore max color component else mcc = read_pbm_int(device); // get max color component if (w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0) return false; // weird P.M image return true; } static inline QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv) { return QRgba64::fromRgba64((rv * 0xffff) / mx, (gv * 0xffff) / mx, (bv * 0xffff) / mx, 0xffff).toArgb32(); } static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage) { int nbits, y; int pbm_bpl; bool raw; QImage::Format format; switch (type) { case '1': // ascii PBM case '4': // raw PBM nbits = 1; format = QImage::Format_Mono; break; case '2': // ascii PGM case '5': // raw PGM nbits = 8; format = QImage::Format_Grayscale8; break; case '3': // ascii PPM case '6': // raw PPM nbits = 32; format = QImage::Format_RGB32; break; default: return false; } raw = type >= '4'; if (outImage->size() != QSize(w, h) || outImage->format() != format) { *outImage = QImage(w, h, format); if (outImage->isNull()) return false; } pbm_bpl = (nbits*w+7)/8; // bytes per scanline in PBM if (raw) { // read raw data if (nbits == 32) { // type 6 pbm_bpl = mcc < 256 ? 3*w : 6*w; uchar *buf24 = new uchar[pbm_bpl], *b; QRgb *p; QRgb *end; for (y=0; yread((char *)buf24, pbm_bpl) != pbm_bpl) { delete[] buf24; return false; } p = (QRgb *)outImage->scanLine(y); end = p + w; b = buf24; while (p < end) { if (mcc < 256) { if (mcc == 255) *p++ = qRgb(b[0],b[1],b[2]); else *p++ = scale_pbm_color(mcc, b[0], b[1], b[2]); b += 3; } else { quint16 rv = b[0] << 8 | b[1]; quint16 gv = b[2] << 8 | b[3]; quint16 bv = b[4] << 8 | b[5]; if (mcc == 0xffff) *p++ = QRgba64::fromRgba64(rv, gv, bv, 0xffff).toArgb32(); else *p++ = scale_pbm_color(mcc, rv, gv, bv); b += 6; } } } delete[] buf24; } else if (nbits == 8 && mcc > 255) { // type 5 16bit pbm_bpl = 2*w; uchar *buf16 = new uchar[pbm_bpl]; for (y=0; yread((char *)buf16, pbm_bpl) != pbm_bpl) { delete[] buf16; return false; } uchar *p = outImage->scanLine(y); uchar *end = p + w; uchar *b = buf16; while (p < end) { *p++ = (b[0] << 8 | b[1]) * 255 / mcc; b += 2; } } delete[] buf16; } else { // type 4,5 for (y=0; yscanLine(y); if (device->read((char *)p, pbm_bpl) != pbm_bpl) return false; if (nbits == 8 && mcc < 255) { for (int i = 0; i < pbm_bpl; i++) p[i] = (p[i] * 255) / mcc; } } } } else { // read ascii data uchar *p; int n; char buf; for (y = 0; (y < h) && (device->peek(&buf, 1) == 1); y++) { p = outImage->scanLine(y); n = pbm_bpl; if (nbits == 1) { int b; int bitsLeft = w; while (n--) { b = 0; for (int i=0; i<8; i++) { if (i < bitsLeft) b = (b << 1) | (read_pbm_int(device) & 1); else b = (b << 1) | (0 & 1); // pad it our self if we need to } bitsLeft -= 8; *p++ = b; } } else if (nbits == 8) { if (mcc == 255) { while (n--) { *p++ = read_pbm_int(device); } } else { while (n--) { *p++ = read_pbm_int(device) * 255 / mcc; } } } else { // 32 bits n /= 4; int r, g, b; if (mcc == 255) { while (n--) { r = read_pbm_int(device); g = read_pbm_int(device); b = read_pbm_int(device); *((QRgb*)p) = qRgb(r, g, b); p += 4; } } else { while (n--) { r = read_pbm_int(device); g = read_pbm_int(device); b = read_pbm_int(device); *((QRgb*)p) = scale_pbm_color(mcc, r, g, b); p += 4; } } } } } if (format == QImage::Format_Mono) { outImage->setColorCount(2); outImage->setColor(0, qRgb(255,255,255)); // white outImage->setColor(1, qRgb(0,0,0)); // black } return true; } static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat) { QByteArray str; QImage image = sourceImage; QByteArray format = sourceFormat; format = format.left(3); // ignore RAW part bool gray = format == "pgm"; if (format == "pbm") { image = image.convertToFormat(QImage::Format_Mono); } else if (gray) { image = image.convertToFormat(QImage::Format_Grayscale8); } else { switch (image.format()) { case QImage::Format_Mono: case QImage::Format_MonoLSB: image = image.convertToFormat(QImage::Format_Indexed8); break; case QImage::Format_Indexed8: case QImage::Format_RGB32: case QImage::Format_ARGB32: break; default: if (image.hasAlphaChannel()) image = image.convertToFormat(QImage::Format_ARGB32); else image = image.convertToFormat(QImage::Format_RGB32); break; } } if (image.depth() == 1 && image.colorCount() == 2) { if (qGray(image.color(0)) < qGray(image.color(1))) { // 0=dark/black, 1=light/white - invert image.detach(); for (int y=0; ywrite(str, str.length()) != str.length()) return false; w = (w+7)/8; for (uint y=0; ywrite((char*)line, w)) return false; } } break; case 8: { str.insert(1, gray ? '5' : '6'); str.append("255\n"); if (out->write(str, str.length()) != str.length()) return false; uint bpl = w * (gray ? 1 : 3); uchar *buf = new uchar[bpl]; if (image.format() == QImage::Format_Indexed8) { QVector color = image.colorTable(); for (uint y=0; ywrite((char*)buf, bpl)) return false; } } else { for (uint y=0; ywrite((char*)buf, bpl)) return false; } } delete[] buf; break; } case 32: { str.insert(1, '6'); str.append("255\n"); if (out->write(str, str.length()) != str.length()) return false; uint bpl = w * 3; uchar *buf = new uchar[bpl]; for (uint y=0; y(image.constScanLine(y)); uchar *p = buf; uchar *end = buf+bpl; while (p < end) { QRgb rgb = *b++; *p++ = qRed(rgb); *p++ = qGreen(rgb); *p++ = qBlue(rgb); } if (bpl != (uint)out->write((char*)buf, bpl)) return false; } delete[] buf; break; } default: return false; } return true; } QPpmHandler::QPpmHandler() : state(Ready) {} bool QPpmHandler::readHeader() { state = Error; if (!read_pbm_header(device(), type, width, height, mcc)) return false; state = ReadHeader; return true; } bool QPpmHandler::canRead() const { if (state == Ready && !canRead(device(), &subType)) return false; if (state != Error) { setFormat(subType); return true; } return false; } bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType) { if (!device) { qWarning("QPpmHandler::canRead() called with no device"); return false; } char head[2]; if (device->peek(head, sizeof(head)) != sizeof(head)) return false; if (head[0] != 'P') return false; if (head[1] == '1' || head[1] == '4') { if (subType) *subType = "pbm"; } else if (head[1] == '2' || head[1] == '5') { if (subType) *subType = "pgm"; } else if (head[1] == '3' || head[1] == '6') { if (subType) *subType = "ppm"; } else { return false; } return true; } bool QPpmHandler::read(QImage *image) { if (state == Error) return false; if (state == Ready && !readHeader()) { state = Error; return false; } if (!read_pbm_body(device(), type, width, height, mcc, image)) { state = Error; return false; } state = Ready; return true; } bool QPpmHandler::write(const QImage &image) { return write_pbm_image(device(), image, subType); } bool QPpmHandler::supportsOption(ImageOption option) const { return option == SubType || option == Size || option == ImageFormat; } QVariant QPpmHandler::option(ImageOption option) const { if (option == SubType) { return subType; } else if (option == Size) { if (state == Error) return QVariant(); if (state == Ready && !const_cast(this)->readHeader()) return QVariant(); return QSize(width, height); } else if (option == ImageFormat) { if (state == Error) return QVariant(); if (state == Ready && !const_cast(this)->readHeader()) return QVariant(); QImage::Format format = QImage::Format_Invalid; switch (type) { case '1': // ascii PBM case '4': // raw PBM format = QImage::Format_Mono; break; case '2': // ascii PGM case '5': // raw PGM format = QImage::Format_Grayscale8; break; case '3': // ascii PPM case '6': // raw PPM format = QImage::Format_RGB32; break; default: break; } return format; } return QVariant(); } void QPpmHandler::setOption(ImageOption option, const QVariant &value) { if (option == SubType) subType = value.toByteArray().toLower(); } QByteArray QPpmHandler::name() const { return subType.isEmpty() ? QByteArray("ppm") : subType; } QT_END_NAMESPACE #endif // QT_NO_IMAGEFORMAT_PPM cppcheck-2.7/test/bug-hunting/cve/CVE-2018-20845/000077500000000000000000000000001417746362400206305ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2018-20845/expected.txt000066400000000000000000000000731417746362400231720ustar00rootroot00000000000000pi.c:426:bughuntingDivByZero pi.c:430:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2018-20845/pi.c000066400000000000000000001232641417746362400214140ustar00rootroot00000000000000/* * The copyright in this software is being made available under the 2-clauses * BSD License, included below. This software may be subject to other third * party and contributor rights, including patent rights, and no such rights * are granted under this license. * * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium * Copyright (c) 2002-2014, Professor Benoit Macq * Copyright (c) 2001-2003, David Janssens * Copyright (c) 2002-2003, Yannick Verschueren * Copyright (c) 2003-2007, Francois-Olivier Devaux * Copyright (c) 2003-2014, Antonin Descampe * Copyright (c) 2005, Herve Drolon, FreeImage Team * Copyright (c) 2006-2007, Parvatha Elangovan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "opj_includes.h" /** @defgroup PI PI - Implementation of a packet iterator */ /*@{*/ /** @name Local static functions */ /*@{*/ /** Get next packet in layer-resolution-component-precinct order. @param pi packet iterator to modify @return returns false if pi pointed to the last packet or else returns true */ static opj_bool pi_next_lrcp(opj_pi_iterator_t * pi); /** Get next packet in resolution-layer-component-precinct order. @param pi packet iterator to modify @return returns false if pi pointed to the last packet or else returns true */ static opj_bool pi_next_rlcp(opj_pi_iterator_t * pi); /** Get next packet in resolution-precinct-component-layer order. @param pi packet iterator to modify @return returns false if pi pointed to the last packet or else returns true */ static opj_bool pi_next_rpcl(opj_pi_iterator_t * pi); /** Get next packet in precinct-component-resolution-layer order. @param pi packet iterator to modify @return returns false if pi pointed to the last packet or else returns true */ static opj_bool pi_next_pcrl(opj_pi_iterator_t * pi); /** Get next packet in component-precinct-resolution-layer order. @param pi packet iterator to modify @return returns false if pi pointed to the last packet or else returns true */ static opj_bool pi_next_cprl(opj_pi_iterator_t * pi); /*@}*/ /*@}*/ /* ========================================================== local functions ========================================================== */ static opj_bool pi_next_lrcp(opj_pi_iterator_t * pi) { opj_pi_comp_t *comp = NULL; opj_pi_resolution_t *res = NULL; long index = 0; if (!pi->first) { comp = &pi->comps[pi->compno]; res = &comp->resolutions[pi->resno]; goto LABEL_SKIP; } else { pi->first = 0; } for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) { for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { comp = &pi->comps[pi->compno]; if (pi->resno >= comp->numresolutions) { continue; } res = &comp->resolutions[pi->resno]; if (!pi->tp_on) { pi->poc.precno1 = res->pw * res->ph; } for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) { index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * pi->step_c + pi->precno * pi->step_p; if (!pi->include[index]) { pi->include[index] = 1; return OPJ_TRUE; } LABEL_SKIP: ; } } } } return OPJ_FALSE; } static opj_bool pi_next_rlcp(opj_pi_iterator_t * pi) { opj_pi_comp_t *comp = NULL; opj_pi_resolution_t *res = NULL; long index = 0; if (!pi->first) { comp = &pi->comps[pi->compno]; res = &comp->resolutions[pi->resno]; goto LABEL_SKIP; } else { pi->first = 0; } for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) { for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { comp = &pi->comps[pi->compno]; if (pi->resno >= comp->numresolutions) { continue; } res = &comp->resolutions[pi->resno]; if (!pi->tp_on) { pi->poc.precno1 = res->pw * res->ph; } for (pi->precno = pi->poc.precno0; pi->precno < pi->poc.precno1; pi->precno++) { index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * pi->step_c + pi->precno * pi->step_p; if (!pi->include[index]) { pi->include[index] = 1; return OPJ_TRUE; } LABEL_SKIP: ; } } } } return OPJ_FALSE; } static opj_bool pi_next_rpcl(opj_pi_iterator_t * pi) { opj_pi_comp_t *comp = NULL; opj_pi_resolution_t *res = NULL; long index = 0; if (!pi->first) { goto LABEL_SKIP; } else { int compno, resno; pi->first = 0; pi->dx = 0; pi->dy = 0; for (compno = 0; compno < pi->numcomps; compno++) { comp = &pi->comps[compno]; for (resno = 0; resno < comp->numresolutions; resno++) { int dx, dy; res = &comp->resolutions[resno]; dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); pi->dx = !pi->dx ? dx : int_min(pi->dx, dx); pi->dy = !pi->dy ? dy : int_min(pi->dy, dy); } } } if (!pi->tp_on) { pi->poc.ty0 = pi->ty0; pi->poc.tx0 = pi->tx0; pi->poc.ty1 = pi->ty1; pi->poc.tx1 = pi->tx1; } for (pi->resno = pi->poc.resno0; pi->resno < pi->poc.resno1; pi->resno++) { for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1; pi->y += pi->dy - (pi->y % pi->dy)) { for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1; pi->x += pi->dx - (pi->x % pi->dx)) { for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { int levelno; int trx0, try0; int trx1, try1; int rpx, rpy; int prci, prcj; comp = &pi->comps[pi->compno]; if (pi->resno >= comp->numresolutions) { continue; } res = &comp->resolutions[pi->resno]; levelno = comp->numresolutions - 1 - pi->resno; trx0 = int_ceildiv(pi->tx0, comp->dx << levelno); try0 = int_ceildiv(pi->ty0, comp->dy << levelno); trx1 = int_ceildiv(pi->tx1, comp->dx << levelno); try1 = int_ceildiv(pi->ty1, comp->dy << levelno); rpx = res->pdx + levelno; rpy = res->pdy + levelno; /* To avoid divisions by zero / undefined behaviour on shift */ if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx || rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) { continue; } if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) && ((try0 << levelno) % (1 << rpy))))) { continue; } if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) && ((trx0 << levelno) % (1 << rpx))))) { continue; } if ((res->pw == 0) || (res->ph == 0)) { continue; } if ((trx0 == trx1) || (try0 == try1)) { continue; } prci = int_floordivpow2(int_ceildiv(pi->x, comp->dx << levelno), res->pdx) - int_floordivpow2(trx0, res->pdx); prcj = int_floordivpow2(int_ceildiv(pi->y, comp->dy << levelno), res->pdy) - int_floordivpow2(try0, res->pdy); pi->precno = prci + prcj * res->pw; for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * pi->step_c + pi->precno * pi->step_p; if (!pi->include[index]) { pi->include[index] = 1; return OPJ_TRUE; } LABEL_SKIP: ; } } } } } return OPJ_FALSE; } static opj_bool pi_next_pcrl(opj_pi_iterator_t * pi) { opj_pi_comp_t *comp = NULL; opj_pi_resolution_t *res = NULL; long index = 0; if (!pi->first) { comp = &pi->comps[pi->compno]; goto LABEL_SKIP; } else { int compno, resno; pi->first = 0; pi->dx = 0; pi->dy = 0; for (compno = 0; compno < pi->numcomps; compno++) { comp = &pi->comps[compno]; for (resno = 0; resno < comp->numresolutions; resno++) { int dx, dy; res = &comp->resolutions[resno]; dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); pi->dx = !pi->dx ? dx : int_min(pi->dx, dx); pi->dy = !pi->dy ? dy : int_min(pi->dy, dy); } } } if (!pi->tp_on) { pi->poc.ty0 = pi->ty0; pi->poc.tx0 = pi->tx0; pi->poc.ty1 = pi->ty1; pi->poc.tx1 = pi->tx1; } for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1; pi->y += pi->dy - (pi->y % pi->dy)) { for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1; pi->x += pi->dx - (pi->x % pi->dx)) { for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { comp = &pi->comps[pi->compno]; for (pi->resno = pi->poc.resno0; pi->resno < int_min(pi->poc.resno1, comp->numresolutions); pi->resno++) { int levelno; int trx0, try0; int trx1, try1; int rpx, rpy; int prci, prcj; res = &comp->resolutions[pi->resno]; levelno = comp->numresolutions - 1 - pi->resno; trx0 = int_ceildiv(pi->tx0, comp->dx << levelno); try0 = int_ceildiv(pi->ty0, comp->dy << levelno); trx1 = int_ceildiv(pi->tx1, comp->dx << levelno); try1 = int_ceildiv(pi->ty1, comp->dy << levelno); rpx = res->pdx + levelno; rpy = res->pdy + levelno; /* To avoid divisions by zero / undefined behaviour on shift */ if (rpx >= 31 || ((comp->dx << rpx) >> rpx) != comp->dx || rpy >= 31 || ((comp->dy << rpy) >> rpy) != comp->dy) { continue; } if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) && ((try0 << levelno) % (1 << rpy))))) { continue; } if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) && ((trx0 << levelno) % (1 << rpx))))) { continue; } if ((res->pw == 0) || (res->ph == 0)) { continue; } if ((trx0 == trx1) || (try0 == try1)) { continue; } prci = int_floordivpow2(int_ceildiv(pi->x, comp->dx << levelno), res->pdx) - int_floordivpow2(trx0, res->pdx); prcj = int_floordivpow2(int_ceildiv(pi->y, comp->dy << levelno), res->pdy) - int_floordivpow2(try0, res->pdy); pi->precno = prci + prcj * res->pw; for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * pi->step_c + pi->precno * pi->step_p; if (!pi->include[index]) { pi->include[index] = 1; return OPJ_TRUE; } LABEL_SKIP: ; } } } } } return OPJ_FALSE; } static opj_bool pi_next_cprl(opj_pi_iterator_t * pi) { opj_pi_comp_t *comp = NULL; opj_pi_resolution_t *res = NULL; long index = 0; if (!pi->first) { comp = &pi->comps[pi->compno]; goto LABEL_SKIP; } else { pi->first = 0; } for (pi->compno = pi->poc.compno0; pi->compno < pi->poc.compno1; pi->compno++) { int resno; comp = &pi->comps[pi->compno]; pi->dx = 0; pi->dy = 0; for (resno = 0; resno < comp->numresolutions; resno++) { int dx, dy; res = &comp->resolutions[resno]; dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); pi->dx = !pi->dx ? dx : int_min(pi->dx, dx); pi->dy = !pi->dy ? dy : int_min(pi->dy, dy); } if (!pi->tp_on) { pi->poc.ty0 = pi->ty0; pi->poc.tx0 = pi->tx0; pi->poc.ty1 = pi->ty1; pi->poc.tx1 = pi->tx1; } for (pi->y = pi->poc.ty0; pi->y < pi->poc.ty1; pi->y += pi->dy - (pi->y % pi->dy)) { for (pi->x = pi->poc.tx0; pi->x < pi->poc.tx1; pi->x += pi->dx - (pi->x % pi->dx)) { for (pi->resno = pi->poc.resno0; pi->resno < int_min(pi->poc.resno1, comp->numresolutions); pi->resno++) { int levelno; int trx0, try0; int trx1, try1; int rpx, rpy; int prci, prcj; res = &comp->resolutions[pi->resno]; levelno = comp->numresolutions - 1 - pi->resno; trx0 = int_ceildiv(pi->tx0, comp->dx << levelno); try0 = int_ceildiv(pi->ty0, comp->dy << levelno); trx1 = int_ceildiv(pi->tx1, comp->dx << levelno); try1 = int_ceildiv(pi->ty1, comp->dy << levelno); rpx = res->pdx + levelno; rpy = res->pdy + levelno; if (!((pi->y % (comp->dy << rpy) == 0) || ((pi->y == pi->ty0) && ((try0 << levelno) % (1 << rpy))))) { continue; } if (!((pi->x % (comp->dx << rpx) == 0) || ((pi->x == pi->tx0) && ((trx0 << levelno) % (1 << rpx))))) { continue; } if ((res->pw == 0) || (res->ph == 0)) { continue; } if ((trx0 == trx1) || (try0 == try1)) { continue; } prci = int_floordivpow2(int_ceildiv(pi->x, comp->dx << levelno), res->pdx) - int_floordivpow2(trx0, res->pdx); prcj = int_floordivpow2(int_ceildiv(pi->y, comp->dy << levelno), res->pdy) - int_floordivpow2(try0, res->pdy); pi->precno = prci + prcj * res->pw; for (pi->layno = pi->poc.layno0; pi->layno < pi->poc.layno1; pi->layno++) { index = pi->layno * pi->step_l + pi->resno * pi->step_r + pi->compno * pi->step_c + pi->precno * pi->step_p; if (!pi->include[index]) { pi->include[index] = 1; return OPJ_TRUE; } LABEL_SKIP: ; } } } } } return OPJ_FALSE; } /* ========================================================== Packet iterator interface ========================================================== */ opj_pi_iterator_t *pi_create_decode(opj_image_t *image, opj_cp_t *cp, int tileno) { int p, q; int compno, resno, pino; opj_pi_iterator_t *pi = NULL; opj_tcp_t *tcp = NULL; opj_tccp_t *tccp = NULL; tcp = &cp->tcps[tileno]; pi = (opj_pi_iterator_t*) opj_calloc((tcp->numpocs + 1), sizeof(opj_pi_iterator_t)); if (!pi) { /* TODO: throw an error */ return NULL; } for (pino = 0; pino < tcp->numpocs + 1; pino++) { /* change */ int maxres = 0; int maxprec = 0; p = tileno % cp->tw; q = tileno / cp->tw; pi[pino].tx0 = int_max(cp->tx0 + p * cp->tdx, image->x0); pi[pino].ty0 = int_max(cp->ty0 + q * cp->tdy, image->y0); pi[pino].tx1 = int_min(cp->tx0 + (p + 1) * cp->tdx, image->x1); pi[pino].ty1 = int_min(cp->ty0 + (q + 1) * cp->tdy, image->y1); pi[pino].numcomps = image->numcomps; pi[pino].comps = (opj_pi_comp_t*) opj_calloc(image->numcomps, sizeof(opj_pi_comp_t)); if (!pi[pino].comps) { /* TODO: throw an error */ pi_destroy(pi, cp, tileno); return NULL; } for (compno = 0; compno < pi->numcomps; compno++) { int tcx0, tcy0, tcx1, tcy1; opj_pi_comp_t *comp = &pi[pino].comps[compno]; tccp = &tcp->tccps[compno]; comp->dx = image->comps[compno].dx; comp->dy = image->comps[compno].dy; comp->numresolutions = tccp->numresolutions; comp->resolutions = (opj_pi_resolution_t*) opj_calloc(comp->numresolutions, sizeof(opj_pi_resolution_t)); if (!comp->resolutions) { /* TODO: throw an error */ pi_destroy(pi, cp, tileno); return NULL; } tcx0 = int_ceildiv(pi->tx0, comp->dx); tcy0 = int_ceildiv(pi->ty0, comp->dy); tcx1 = int_ceildiv(pi->tx1, comp->dx); tcy1 = int_ceildiv(pi->ty1, comp->dy); if (comp->numresolutions > maxres) { maxres = comp->numresolutions; } for (resno = 0; resno < comp->numresolutions; resno++) { int levelno; int rx0, ry0, rx1, ry1; int px0, py0, px1, py1; opj_pi_resolution_t *res = &comp->resolutions[resno]; if (tccp->csty & J2K_CCP_CSTY_PRT) { res->pdx = tccp->prcw[resno]; res->pdy = tccp->prch[resno]; } else { res->pdx = 15; res->pdy = 15; } levelno = comp->numresolutions - 1 - resno; rx0 = int_ceildivpow2(tcx0, levelno); ry0 = int_ceildivpow2(tcy0, levelno); rx1 = int_ceildivpow2(tcx1, levelno); ry1 = int_ceildivpow2(tcy1, levelno); px0 = int_floordivpow2(rx0, res->pdx) << res->pdx; py0 = int_floordivpow2(ry0, res->pdy) << res->pdy; px1 = int_ceildivpow2(rx1, res->pdx) << res->pdx; py1 = int_ceildivpow2(ry1, res->pdy) << res->pdy; res->pw = (rx0 == rx1) ? 0 : ((px1 - px0) >> res->pdx); res->ph = (ry0 == ry1) ? 0 : ((py1 - py0) >> res->pdy); if (res->pw * res->ph > maxprec) { maxprec = res->pw * res->ph; } } } tccp = &tcp->tccps[0]; pi[pino].step_p = 1; pi[pino].step_c = maxprec * pi[pino].step_p; pi[pino].step_r = image->numcomps * pi[pino].step_c; pi[pino].step_l = maxres * pi[pino].step_r; if (pino == 0) { pi[pino].include = (short int*) opj_calloc(image->numcomps * maxres * tcp->numlayers * maxprec, sizeof(short int)); if (!pi[pino].include) { /* TODO: throw an error */ pi_destroy(pi, cp, tileno); return NULL; } } else { pi[pino].include = pi[pino - 1].include; } if (tcp->POC == 0) { pi[pino].first = 1; pi[pino].poc.resno0 = 0; pi[pino].poc.compno0 = 0; pi[pino].poc.layno1 = tcp->numlayers; pi[pino].poc.resno1 = maxres; pi[pino].poc.compno1 = image->numcomps; pi[pino].poc.prg = tcp->prg; } else { pi[pino].first = 1; pi[pino].poc.resno0 = tcp->pocs[pino].resno0; pi[pino].poc.compno0 = tcp->pocs[pino].compno0; pi[pino].poc.layno1 = tcp->pocs[pino].layno1; pi[pino].poc.resno1 = tcp->pocs[pino].resno1; pi[pino].poc.compno1 = tcp->pocs[pino].compno1; pi[pino].poc.prg = tcp->pocs[pino].prg; } pi[pino].poc.layno0 = 0; pi[pino].poc.precno0 = 0; pi[pino].poc.precno1 = maxprec; } return pi; } opj_pi_iterator_t *pi_initialise_encode(opj_image_t *image, opj_cp_t *cp, int tileno, J2K_T2_MODE t2_mode) { int p, q, pino; int compno, resno; int maxres = 0; int maxprec = 0; opj_pi_iterator_t *pi = NULL; opj_tcp_t *tcp = NULL; opj_tccp_t *tccp = NULL; tcp = &cp->tcps[tileno]; pi = (opj_pi_iterator_t*) opj_calloc((tcp->numpocs + 1), sizeof(opj_pi_iterator_t)); if (!pi) { return NULL; } pi->tp_on = cp->tp_on; for (pino = 0; pino < tcp->numpocs + 1; pino++) { p = tileno % cp->tw; q = tileno / cp->tw; pi[pino].tx0 = int_max(cp->tx0 + p * cp->tdx, image->x0); pi[pino].ty0 = int_max(cp->ty0 + q * cp->tdy, image->y0); pi[pino].tx1 = int_min(cp->tx0 + (p + 1) * cp->tdx, image->x1); pi[pino].ty1 = int_min(cp->ty0 + (q + 1) * cp->tdy, image->y1); pi[pino].numcomps = image->numcomps; pi[pino].comps = (opj_pi_comp_t*) opj_calloc(image->numcomps, sizeof(opj_pi_comp_t)); if (!pi[pino].comps) { pi_destroy(pi, cp, tileno); return NULL; } for (compno = 0; compno < pi[pino].numcomps; compno++) { int tcx0, tcy0, tcx1, tcy1; opj_pi_comp_t *comp = &pi[pino].comps[compno]; tccp = &tcp->tccps[compno]; comp->dx = image->comps[compno].dx; comp->dy = image->comps[compno].dy; comp->numresolutions = tccp->numresolutions; comp->resolutions = (opj_pi_resolution_t*) opj_malloc(comp->numresolutions * sizeof(opj_pi_resolution_t)); if (!comp->resolutions) { pi_destroy(pi, cp, tileno); return NULL; } tcx0 = int_ceildiv(pi[pino].tx0, comp->dx); tcy0 = int_ceildiv(pi[pino].ty0, comp->dy); tcx1 = int_ceildiv(pi[pino].tx1, comp->dx); tcy1 = int_ceildiv(pi[pino].ty1, comp->dy); if (comp->numresolutions > maxres) { maxres = comp->numresolutions; } for (resno = 0; resno < comp->numresolutions; resno++) { int levelno; int rx0, ry0, rx1, ry1; int px0, py0, px1, py1; opj_pi_resolution_t *res = &comp->resolutions[resno]; if (tccp->csty & J2K_CCP_CSTY_PRT) { res->pdx = tccp->prcw[resno]; res->pdy = tccp->prch[resno]; } else { res->pdx = 15; res->pdy = 15; } levelno = comp->numresolutions - 1 - resno; rx0 = int_ceildivpow2(tcx0, levelno); ry0 = int_ceildivpow2(tcy0, levelno); rx1 = int_ceildivpow2(tcx1, levelno); ry1 = int_ceildivpow2(tcy1, levelno); px0 = int_floordivpow2(rx0, res->pdx) << res->pdx; py0 = int_floordivpow2(ry0, res->pdy) << res->pdy; px1 = int_ceildivpow2(rx1, res->pdx) << res->pdx; py1 = int_ceildivpow2(ry1, res->pdy) << res->pdy; res->pw = (rx0 == rx1) ? 0 : ((px1 - px0) >> res->pdx); res->ph = (ry0 == ry1) ? 0 : ((py1 - py0) >> res->pdy); if (res->pw * res->ph > maxprec) { maxprec = res->pw * res->ph; } } } tccp = &tcp->tccps[0]; pi[pino].step_p = 1; pi[pino].step_c = maxprec * pi[pino].step_p; pi[pino].step_r = image->numcomps * pi[pino].step_c; pi[pino].step_l = maxres * pi[pino].step_r; for (compno = 0; compno < pi->numcomps; compno++) { opj_pi_comp_t *comp = &pi->comps[compno]; for (resno = 0; resno < comp->numresolutions; resno++) { int dx, dy; opj_pi_resolution_t *res = &comp->resolutions[resno]; dx = comp->dx * (1 << (res->pdx + comp->numresolutions - 1 - resno)); dy = comp->dy * (1 << (res->pdy + comp->numresolutions - 1 - resno)); pi[pino].dx = !pi->dx ? dx : int_min(pi->dx, dx); pi[pino].dy = !pi->dy ? dy : int_min(pi->dy, dy); } } if (pino == 0) { pi[pino].include = (short int*) opj_calloc(tcp->numlayers * pi[pino].step_l, sizeof(short int)); if (!pi[pino].include) { pi_destroy(pi, cp, tileno); return NULL; } } else { pi[pino].include = pi[pino - 1].include; } /* Generation of boundaries for each prog flag*/ if (tcp->POC && (cp->cinema || ((!cp->cinema) && (t2_mode == FINAL_PASS)))) { tcp->pocs[pino].compS = tcp->pocs[pino].compno0; tcp->pocs[pino].compE = tcp->pocs[pino].compno1; tcp->pocs[pino].resS = tcp->pocs[pino].resno0; tcp->pocs[pino].resE = tcp->pocs[pino].resno1; tcp->pocs[pino].layE = tcp->pocs[pino].layno1; tcp->pocs[pino].prg = tcp->pocs[pino].prg1; if (pino > 0) { tcp->pocs[pino].layS = (tcp->pocs[pino].layE > tcp->pocs[pino - 1].layE) ? tcp->pocs[pino - 1].layE : 0; } } else { tcp->pocs[pino].compS = 0; tcp->pocs[pino].compE = image->numcomps; tcp->pocs[pino].resS = 0; tcp->pocs[pino].resE = maxres; tcp->pocs[pino].layS = 0; tcp->pocs[pino].layE = tcp->numlayers; tcp->pocs[pino].prg = tcp->prg; } tcp->pocs[pino].prcS = 0; tcp->pocs[pino].prcE = maxprec;; tcp->pocs[pino].txS = pi[pino].tx0; tcp->pocs[pino].txE = pi[pino].tx1; tcp->pocs[pino].tyS = pi[pino].ty0; tcp->pocs[pino].tyE = pi[pino].ty1; tcp->pocs[pino].dx = pi[pino].dx; tcp->pocs[pino].dy = pi[pino].dy; } return pi; } void pi_destroy(opj_pi_iterator_t *pi, opj_cp_t *cp, int tileno) { int compno, pino; opj_tcp_t *tcp = &cp->tcps[tileno]; if (pi) { for (pino = 0; pino < tcp->numpocs + 1; pino++) { if (pi[pino].comps) { for (compno = 0; compno < pi->numcomps; compno++) { opj_pi_comp_t *comp = &pi[pino].comps[compno]; if (comp->resolutions) { opj_free(comp->resolutions); } } opj_free(pi[pino].comps); } } if (pi->include) { opj_free(pi->include); } opj_free(pi); } } opj_bool pi_next(opj_pi_iterator_t * pi) { switch (pi->poc.prg) { case LRCP: return pi_next_lrcp(pi); case RLCP: return pi_next_rlcp(pi); case RPCL: return pi_next_rpcl(pi); case PCRL: return pi_next_pcrl(pi); case CPRL: return pi_next_cprl(pi); case PROG_UNKNOWN: return OPJ_FALSE; } return OPJ_FALSE; } opj_bool pi_create_encode(opj_pi_iterator_t *pi, opj_cp_t *cp, int tileno, int pino, int tpnum, int tppos, J2K_T2_MODE t2_mode, int cur_totnum_tp) { char prog[4]; int i; int incr_top = 1, resetX = 0; opj_tcp_t *tcps = &cp->tcps[tileno]; opj_poc_t *tcp = &tcps->pocs[pino]; pi[pino].first = 1; pi[pino].poc.prg = tcp->prg; switch (tcp->prg) { case CPRL: strncpy(prog, "CPRL", 4); break; case LRCP: strncpy(prog, "LRCP", 4); break; case PCRL: strncpy(prog, "PCRL", 4); break; case RLCP: strncpy(prog, "RLCP", 4); break; case RPCL: strncpy(prog, "RPCL", 4); break; case PROG_UNKNOWN: return OPJ_TRUE; } if (!(cp->tp_on && ((!cp->cinema && (t2_mode == FINAL_PASS)) || cp->cinema))) { pi[pino].poc.resno0 = tcp->resS; pi[pino].poc.resno1 = tcp->resE; pi[pino].poc.compno0 = tcp->compS; pi[pino].poc.compno1 = tcp->compE; pi[pino].poc.layno0 = tcp->layS; pi[pino].poc.layno1 = tcp->layE; pi[pino].poc.precno0 = tcp->prcS; pi[pino].poc.precno1 = tcp->prcE; pi[pino].poc.tx0 = tcp->txS; pi[pino].poc.ty0 = tcp->tyS; pi[pino].poc.tx1 = tcp->txE; pi[pino].poc.ty1 = tcp->tyE; } else { if (tpnum < cur_totnum_tp) { for (i = 3; i >= 0; i--) { switch (prog[i]) { case 'C': if (i > tppos) { pi[pino].poc.compno0 = tcp->compS; pi[pino].poc.compno1 = tcp->compE; } else { if (tpnum == 0) { tcp->comp_t = tcp->compS; pi[pino].poc.compno0 = tcp->comp_t; pi[pino].poc.compno1 = tcp->comp_t + 1; tcp->comp_t += 1; } else { if (incr_top == 1) { if (tcp->comp_t == tcp->compE) { tcp->comp_t = tcp->compS; pi[pino].poc.compno0 = tcp->comp_t; pi[pino].poc.compno1 = tcp->comp_t + 1; tcp->comp_t += 1; incr_top = 1; } else { pi[pino].poc.compno0 = tcp->comp_t; pi[pino].poc.compno1 = tcp->comp_t + 1; tcp->comp_t += 1; incr_top = 0; } } else { pi[pino].poc.compno0 = tcp->comp_t - 1; pi[pino].poc.compno1 = tcp->comp_t; } } } break; case 'R': if (i > tppos) { pi[pino].poc.resno0 = tcp->resS; pi[pino].poc.resno1 = tcp->resE; } else { if (tpnum == 0) { tcp->res_t = tcp->resS; pi[pino].poc.resno0 = tcp->res_t; pi[pino].poc.resno1 = tcp->res_t + 1; tcp->res_t += 1; } else { if (incr_top == 1) { if (tcp->res_t == tcp->resE) { tcp->res_t = tcp->resS; pi[pino].poc.resno0 = tcp->res_t; pi[pino].poc.resno1 = tcp->res_t + 1; tcp->res_t += 1; incr_top = 1; } else { pi[pino].poc.resno0 = tcp->res_t; pi[pino].poc.resno1 = tcp->res_t + 1; tcp->res_t += 1; incr_top = 0; } } else { pi[pino].poc.resno0 = tcp->res_t - 1; pi[pino].poc.resno1 = tcp->res_t; } } } break; case 'L': if (i > tppos) { pi[pino].poc.layno0 = tcp->layS; pi[pino].poc.layno1 = tcp->layE; } else { if (tpnum == 0) { tcp->lay_t = tcp->layS; pi[pino].poc.layno0 = tcp->lay_t; pi[pino].poc.layno1 = tcp->lay_t + 1; tcp->lay_t += 1; } else { if (incr_top == 1) { if (tcp->lay_t == tcp->layE) { tcp->lay_t = tcp->layS; pi[pino].poc.layno0 = tcp->lay_t; pi[pino].poc.layno1 = tcp->lay_t + 1; tcp->lay_t += 1; incr_top = 1; } else { pi[pino].poc.layno0 = tcp->lay_t; pi[pino].poc.layno1 = tcp->lay_t + 1; tcp->lay_t += 1; incr_top = 0; } } else { pi[pino].poc.layno0 = tcp->lay_t - 1; pi[pino].poc.layno1 = tcp->lay_t; } } } break; case 'P': switch (tcp->prg) { case LRCP: case RLCP: if (i > tppos) { pi[pino].poc.precno0 = tcp->prcS; pi[pino].poc.precno1 = tcp->prcE; } else { if (tpnum == 0) { tcp->prc_t = tcp->prcS; pi[pino].poc.precno0 = tcp->prc_t; pi[pino].poc.precno1 = tcp->prc_t + 1; tcp->prc_t += 1; } else { if (incr_top == 1) { if (tcp->prc_t == tcp->prcE) { tcp->prc_t = tcp->prcS; pi[pino].poc.precno0 = tcp->prc_t; pi[pino].poc.precno1 = tcp->prc_t + 1; tcp->prc_t += 1; incr_top = 1; } else { pi[pino].poc.precno0 = tcp->prc_t; pi[pino].poc.precno1 = tcp->prc_t + 1; tcp->prc_t += 1; incr_top = 0; } } else { pi[pino].poc.precno0 = tcp->prc_t - 1; pi[pino].poc.precno1 = tcp->prc_t; } } } break; default: if (i > tppos) { pi[pino].poc.tx0 = tcp->txS; pi[pino].poc.ty0 = tcp->tyS; pi[pino].poc.tx1 = tcp->txE; pi[pino].poc.ty1 = tcp->tyE; } else { if (tpnum == 0) { tcp->tx0_t = tcp->txS; tcp->ty0_t = tcp->tyS; pi[pino].poc.tx0 = tcp->tx0_t; pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx); pi[pino].poc.ty0 = tcp->ty0_t; pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy); tcp->tx0_t = pi[pino].poc.tx1; tcp->ty0_t = pi[pino].poc.ty1; } else { if (incr_top == 1) { if (tcp->tx0_t >= tcp->txE) { if (tcp->ty0_t >= tcp->tyE) { tcp->ty0_t = tcp->tyS; pi[pino].poc.ty0 = tcp->ty0_t; pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy); tcp->ty0_t = pi[pino].poc.ty1; incr_top = 1; resetX = 1; } else { pi[pino].poc.ty0 = tcp->ty0_t; pi[pino].poc.ty1 = tcp->ty0_t + tcp->dy - (tcp->ty0_t % tcp->dy); tcp->ty0_t = pi[pino].poc.ty1; incr_top = 0; resetX = 1; } if (resetX == 1) { tcp->tx0_t = tcp->txS; pi[pino].poc.tx0 = tcp->tx0_t; pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx); tcp->tx0_t = pi[pino].poc.tx1; } } else { pi[pino].poc.tx0 = tcp->tx0_t; pi[pino].poc.tx1 = tcp->tx0_t + tcp->dx - (tcp->tx0_t % tcp->dx); tcp->tx0_t = pi[pino].poc.tx1; pi[pino].poc.ty0 = tcp->ty0_t - tcp->dy - (tcp->ty0_t % tcp->dy); pi[pino].poc.ty1 = tcp->ty0_t; incr_top = 0; } } else { pi[pino].poc.tx0 = tcp->tx0_t - tcp->dx - (tcp->tx0_t % tcp->dx); pi[pino].poc.tx1 = tcp->tx0_t; pi[pino].poc.ty0 = tcp->ty0_t - tcp->dy - (tcp->ty0_t % tcp->dy); pi[pino].poc.ty1 = tcp->ty0_t; } } } break; } break; } } } } return OPJ_FALSE; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10018/000077500000000000000000000000001417746362400206205ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10018/Function.cc000066400000000000000000001055171417746362400227250ustar00rootroot00000000000000//======================================================================== // // Function.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "gmempp.h" #include "GList.h" #include "Object.h" #include "Dict.h" #include "Stream.h" #include "Error.h" #include "Function.h" //------------------------------------------------------------------------ // Max depth of nested functions. This is used to catch infinite // loops in the function object structure. #define recursionLimit 8 //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ Function::Function() { } Function::~Function() { } Function *Function::parse(Object *funcObj, int recursion) { Function *func; Dict *dict; int funcType; Object obj1; if (recursion > recursionLimit) { error(errSyntaxError, -1, "Loop detected in function objects"); return NULL; } if (funcObj->isStream()) { dict = funcObj->streamGetDict(); } else if (funcObj->isDict()) { dict = funcObj->getDict(); } else if (funcObj->isName("Identity")) { return new IdentityFunction(); } else { error(errSyntaxError, -1, "Expected function dictionary or stream"); return NULL; } if (!dict->lookup("FunctionType", &obj1)->isInt()) { error(errSyntaxError, -1, "Function type is missing or wrong type"); obj1.free(); return NULL; } funcType = obj1.getInt(); obj1.free(); if (funcType == 0) { func = new SampledFunction(funcObj, dict); } else if (funcType == 2) { func = new ExponentialFunction(funcObj, dict); } else if (funcType == 3) { func = new StitchingFunction(funcObj, dict, recursion); } else if (funcType == 4) { func = new PostScriptFunction(funcObj, dict); } else { error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); return NULL; } if (!func->isOk()) { delete func; return NULL; } return func; } GBool Function::init(Dict *dict) { Object obj1, obj2; int i; //----- Domain if (!dict->lookup("Domain", &obj1)->isArray()) { error(errSyntaxError, -1, "Function is missing domain"); goto err2; } m = obj1.arrayGetLength() / 2; if (m > funcMaxInputs) { error(errSyntaxError, -1, "Functions with more than {0:d} inputs are unsupported", funcMaxInputs); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][1] = obj2.getNum(); obj2.free(); } obj1.free(); //----- Range hasRange = gFalse; n = 0; if (dict->lookup("Range", &obj1)->isArray()) { hasRange = gTrue; n = obj1.arrayGetLength() / 2; if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][1] = obj2.getNum(); obj2.free(); } } obj1.free(); return gTrue; err1: obj2.free(); err2: obj1.free(); return gFalse; } //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ IdentityFunction::IdentityFunction() { int i; // fill these in with arbitrary values just in case they get used // somewhere m = funcMaxInputs; n = funcMaxOutputs; for (i = 0; i < funcMaxInputs; ++i) { domain[i][0] = 0; domain[i][1] = 1; } hasRange = gFalse; } IdentityFunction::~IdentityFunction() { } void IdentityFunction::transform(double *in, double *out) { int i; for (i = 0; i < funcMaxOutputs; ++i) { out[i] = in[i]; } } //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { Stream *str; int sampleBits; double sampleMul; Object obj1, obj2; Guint buf, bitMask; int bits; Guint s; double in[funcMaxInputs]; int i, j, t, bit, idx; idxOffset = NULL; samples = NULL; sBuf = NULL; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 0 function is missing range"); goto err1; } if (m > sampledFuncMaxInputs) { error(errSyntaxError, -1, "Sampled functions with more than {0:d} inputs are unsupported", sampledFuncMaxInputs); goto err1; } //----- buffer sBuf = (double *)gmallocn(1 << m, sizeof(double)); //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 0 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- Size if (!dict->lookup("Size", &obj1)->isArray() || obj1.arrayGetLength() != m) { error(errSyntaxError, -1, "Function has missing or invalid size array"); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isInt()) { error(errSyntaxError, -1, "Illegal value in function size array"); goto err3; } sampleSize[i] = obj2.getInt(); if (sampleSize[i] <= 0) { error(errSyntaxError, -1, "Illegal non-positive value in function size array"); goto err3; } obj2.free(); } obj1.free(); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); for (i = 0; i < (1<= 1; --j, t <<= 1) { if (sampleSize[j] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idx = (idx + bit) * sampleSize[j-1]; } if (sampleSize[0] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idxOffset[i] = (idx + bit) * n; } //----- BitsPerSample if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); goto err2; } sampleBits = obj1.getInt(); sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); obj1.free(); //----- Encode if (dict->lookup("Encode", &obj1)->isArray() && obj1.arrayGetLength() == 2*m) { for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < m; ++i) { encode[i][0] = 0; encode[i][1] = sampleSize[i] - 1; } } obj1.free(); for (i = 0; i < m; ++i) { inputMul[i] = (encode[i][1] - encode[i][0]) / (domain[i][1] - domain[i][0]); } //----- Decode if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() == 2*n) { for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < n; ++i) { decode[i][0] = range[i][0]; decode[i][1] = range[i][1]; } } obj1.free(); //----- samples nSamples = n; for (i = 0; i < m; ++i) nSamples *= sampleSize[i]; samples = (double *)gmallocn(nSamples, sizeof(double)); buf = 0; bits = 0; bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; str->reset(); for (i = 0; i < nSamples; ++i) { if (sampleBits == 8) { s = str->getChar(); } else if (sampleBits == 16) { s = str->getChar(); s = (s << 8) + str->getChar(); } else if (sampleBits == 32) { s = str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); } else { while (bits < sampleBits) { buf = (buf << 8) | (str->getChar() & 0xff); bits += 8; } s = (buf >> (bits - sampleBits)) & bitMask; bits -= sampleBits; } samples[i] = (double)s * sampleMul; } str->close(); // set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } SampledFunction::~SampledFunction() { if (idxOffset) { gfree(idxOffset); } if (samples) { gfree(samples); } if (sBuf) { gfree(sBuf); } } SampledFunction::SampledFunction(SampledFunction *func) { memcpy((void *)this, (void *)func, sizeof(SampledFunction)); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); samples = (double *)gmallocn(nSamples, sizeof(double)); memcpy(samples, func->samples, nSamples * sizeof(double)); sBuf = (double *)gmallocn(1 << m, sizeof(double)); } void SampledFunction::transform(double *in, double *out) { double x; int e[funcMaxInputs]; double efrac0[funcMaxInputs]; double efrac1[funcMaxInputs]; int i, j, k, idx0, t; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } // map input values into sample array for (i = 0; i < m; ++i) { x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) x = 0; } else if (x > sampleSize[i] - 1) { x = sampleSize[i] - 1; } e[i] = (int)x; if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { // this happens if in[i] = domain[i][1] e[i] = sampleSize[i] - 2; } efrac1[i] = x - e[i]; efrac0[i] = 1 - efrac1[i]; } // compute index for the first sample to be used idx0 = 0; for (k = m - 1; k >= 1; --k) { idx0 = (idx0 + e[k]) * sampleSize[k-1]; } idx0 = (idx0 + e[0]) * n; // for each output, do m-linear interpolation for (i = 0; i < n; ++i) { // pull 2^m values out of the sample array for (j = 0; j < (1<>= 1) { for (k = 0; k < t; k += 2) { sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; } } // map output value to range out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { Object obj1, obj2; int i; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Exponential function with more than one input"); goto err1; } //----- C0 if (dict->lookup("C0", &obj1)->isArray()) { if (hasRange && obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = obj1.arrayGetLength(); if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C0 array"); goto err3; } c0[i] = obj2.getNum(); obj2.free(); } } else { if (hasRange && n != 1) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = 1; c0[0] = 0; } obj1.free(); //----- C1 if (dict->lookup("C1", &obj1)->isArray()) { if (obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C1 array"); goto err3; } c1[i] = obj2.getNum(); obj2.free(); } } else { if (n != 1) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } c1[0] = 1; } obj1.free(); //----- N (exponent) if (!dict->lookup("N", &obj1)->isNum()) { error(errSyntaxError, -1, "Function has missing or invalid N"); goto err2; } e = obj1.getNum(); obj1.free(); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } ExponentialFunction::~ExponentialFunction() { } ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { memcpy((void *)this, (void *)func, sizeof(ExponentialFunction)); } void ExponentialFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < n; ++i) { out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); if (hasRange) { if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } } return; } //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, int recursion) { Object obj1, obj2; int i; ok = gFalse; funcs = NULL; bounds = NULL; encode = NULL; scale = NULL; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Stitching function with more than one input"); goto err1; } //----- Functions if (!dict->lookup("Functions", &obj1)->isArray()) { error(errSyntaxError, -1, "Missing 'Functions' entry in stitching function"); goto err1; } k = obj1.arrayGetLength(); funcs = (Function **)gmallocn(k, sizeof(Function *)); bounds = (double *)gmallocn(k + 1, sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); for (i = 0; i < k; ++i) { funcs[i] = NULL; } for (i = 0; i < k; ++i) { if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), recursion + 1))) { goto err2; } if (funcs[i]->getInputSize() != 1 || (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { error(errSyntaxError, -1, "Incompatible subfunctions in stitching function"); goto err2; } obj2.free(); } obj1.free(); //----- Bounds if (!dict->lookup("Bounds", &obj1)->isArray() || obj1.arrayGetLength() != k - 1) { error(errSyntaxError, -1, "Missing or invalid 'Bounds' entry in stitching function"); goto err1; } bounds[0] = domain[0][0]; for (i = 1; i < k; ++i) { if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Bounds' array in stitching function"); goto err2; } bounds[i] = obj2.getNum(); obj2.free(); } bounds[k] = domain[0][1]; obj1.free(); //----- Encode if (!dict->lookup("Encode", &obj1)->isArray() || obj1.arrayGetLength() != 2 * k) { error(errSyntaxError, -1, "Missing or invalid 'Encode' entry in stitching function"); goto err1; } for (i = 0; i < 2 * k; ++i) { if (!obj1.arrayGet(i, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Encode' array in stitching function"); goto err2; } encode[i] = obj2.getNum(); obj2.free(); } obj1.free(); //----- pre-compute the scale factors for (i = 0; i < k; ++i) { if (bounds[i] == bounds[i+1]) { // avoid a divide-by-zero -- in this situation, function i will // never be used anyway scale[i] = 0; } else { scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); } } ok = gTrue; return; err2: obj2.free(); err1: obj1.free(); } StitchingFunction::StitchingFunction(StitchingFunction *func) { int i; memcpy((void *)this, (void *)func, sizeof(StitchingFunction)); funcs = (Function **)gmallocn(k, sizeof(Function *)); for (i = 0; i < k; ++i) { funcs[i] = func->funcs[i]->copy(); } bounds = (double *)gmallocn(k + 1, sizeof(double)); memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); memcpy(encode, func->encode, 2 * k * sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); memcpy(scale, func->scale, k * sizeof(double)); ok = gTrue; } StitchingFunction::~StitchingFunction() { int i; if (funcs) { for (i = 0; i < k; ++i) { if (funcs[i]) { delete funcs[i]; } } } gfree(funcs); gfree(bounds); gfree(encode); gfree(scale); } void StitchingFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < k - 1; ++i) { if (x < bounds[i+1]) { break; } } x = encode[2*i] + (x - bounds[i]) * scale[i]; funcs[i]->transform(&x, out); } //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ // This is not an enum, because we can't foreward-declare the enum // type in Function.h // // NB: This must be kept in sync with psOpNames[] below. #define psOpAbs 0 #define psOpAdd 1 #define psOpAnd 2 #define psOpAtan 3 #define psOpBitshift 4 #define psOpCeiling 5 #define psOpCopy 6 #define psOpCos 7 #define psOpCvi 8 #define psOpCvr 9 #define psOpDiv 10 #define psOpDup 11 #define psOpEq 12 #define psOpExch 13 #define psOpExp 14 #define psOpFalse 15 #define psOpFloor 16 #define psOpGe 17 #define psOpGt 18 #define psOpIdiv 19 #define psOpIndex 20 #define psOpLe 21 #define psOpLn 22 #define psOpLog 23 #define psOpLt 24 #define psOpMod 25 #define psOpMul 26 #define psOpNe 27 #define psOpNeg 28 #define psOpNot 29 #define psOpOr 30 #define psOpPop 31 #define psOpRoll 32 #define psOpRound 33 #define psOpSin 34 #define psOpSqrt 35 #define psOpSub 36 #define psOpTrue 37 #define psOpTruncate 38 #define psOpXor 39 // the push/j/jz ops are used internally (and are not listed in psOpNames[]) #define psOpPush 40 #define psOpJ 41 #define psOpJz 42 #define nPSOps (sizeof(psOpNames) / sizeof(const char *)) // Note: 'if' and 'ifelse' are parsed separately. // The rest are listed here in alphabetical order. // // NB: This must be kept in sync with the psOpXXX defines above. static const char *psOpNames[] = { "abs", "add", "and", "atan", "bitshift", "ceiling", "copy", "cos", "cvi", "cvr", "div", "dup", "eq", "exch", "exp", "false", "floor", "ge", "gt", "idiv", "index", "le", "ln", "log", "lt", "mod", "mul", "ne", "neg", "not", "or", "pop", "roll", "round", "sin", "sqrt", "sub", "true", "truncate", "xor" }; struct PSCode { int op; union { double d; int i; } val; }; #define psStackSize 100 PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { Stream *str; GList *tokens; GString *tok; double in[funcMaxInputs]; int tokPtr, codePtr, i; codeString = NULL; code = NULL; codeSize = 0; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 4 function is missing range"); goto err1; } //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 4 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- tokenize the function codeString = new GString(); tokens = new GList(); str->reset(); while ((tok = getToken(str))) { tokens->append(tok); } str->close(); //----- parse the function if (tokens->getLength() < 1 || ((GString *)tokens->get(0))->cmp("{")) { error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); goto err2; } tokPtr = 1; codePtr = 0; if (!parseCode(tokens, &tokPtr, &codePtr)) { goto err2; } codeLen = codePtr; //----- set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; err2: deleteGList(tokens, GString); err1: return; } PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { memcpy((void *)this, (void *)func, sizeof(PostScriptFunction)); codeString = func->codeString->copy(); code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); memcpy(code, func->code, codeSize * sizeof(PSCode)); } PostScriptFunction::~PostScriptFunction() { gfree(code); if (codeString) { delete codeString; } } void PostScriptFunction::transform(double *in, double *out) { double stack[psStackSize]; double x; int sp, i; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } for (i = 0; i < m; ++i) { stack[psStackSize - 1 - i] = in[i]; } sp = exec(stack, psStackSize - m); // if (sp < psStackSize - n) { // error(errSyntaxWarning, -1, // "Extra values on stack at end of PostScript function"); // } if (sp > psStackSize - n) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); sp = psStackSize - n; } for (i = 0; i < n; ++i) { x = stack[sp + n - 1 - i]; if (x < range[i][0]) { out[i] = range[i][0]; } else if (x > range[i][1]) { out[i] = range[i][1]; } else { out[i] = x; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { GString *tok; char *p; int a, b, mid, cmp; int codePtr0, codePtr1; while (1) { if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); p = tok->getCString(); if (isdigit(*p) || *p == '.' || *p == '-') { addCodeD(codePtr, psOpPush, atof(tok->getCString())); } else if (!tok->cmp("{")) { codePtr0 = *codePtr; addCodeI(codePtr, psOpJz, 0); if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("if")) { code[codePtr0].val.i = *codePtr; } else if (!tok->cmp("{")) { codePtr1 = *codePtr; addCodeI(codePtr, psOpJ, 0); code[codePtr0].val.i = *codePtr; if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("ifelse")) { code[codePtr1].val.i = *codePtr; } else { error(errSyntaxError, -1, "Expected 'ifelse' in PostScript function stream"); return gFalse; } } else { error(errSyntaxError, -1, "Expected 'if' in PostScript function stream"); return gFalse; } } else if (!tok->cmp("}")) { break; } else if (!tok->cmp("if")) { error(errSyntaxError, -1, "Unexpected 'if' in PostScript function stream"); return gFalse; } else if (!tok->cmp("ifelse")) { error(errSyntaxError, -1, "Unexpected 'ifelse' in PostScript function stream"); return gFalse; } else { a = -1; b = nPSOps; cmp = 0; // make gcc happy // invariant: psOpNames[a] < tok < psOpNames[b] while (b - a > 1) { mid = (a + b) / 2; cmp = tok->cmp(psOpNames[mid]); if (cmp > 0) { a = mid; } else if (cmp < 0) { b = mid; } else { a = b = mid; } } if (cmp != 0) { error(errSyntaxError, -1, "Unknown operator '{0:t}' in PostScript function", tok); return gFalse; } addCode(codePtr, a); } } return gTrue; } void PostScriptFunction::addCode(int *codePtr, int op) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; ++(*codePtr); } void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.i = x; ++(*codePtr); } void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.d = x; ++(*codePtr); } GString *PostScriptFunction::getToken(Stream *str) { GString *s; int c; GBool comment; s = new GString(); comment = gFalse; while (1) { if ((c = str->getChar()) == EOF) { delete s; return NULL; } codeString->append((char)c); if (comment) { if (c == '\x0a' || c == '\x0d') { comment = gFalse; } } else if (c == '%') { comment = gTrue; } else if (!isspace(c)) { break; } } if (c == '{' || c == '}') { s->append((char)c); } else if (isdigit(c) || c == '.' || c == '-') { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { break; } str->getChar(); codeString->append((char)c); } } else { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !isalnum(c)) { break; } str->getChar(); codeString->append((char)c); } } return s; } int PostScriptFunction::exec(double *stack, int sp0) { PSCode *c; double tmp[psStackSize]; double t; int sp, ip, nn, k, i; sp = sp0; ip = 0; while (ip < codeLen) { c = &code[ip++]; switch(c->op) { case psOpAbs: if (sp >= psStackSize) { goto underflow; } stack[sp] = fabs(stack[sp]); break; case psOpAdd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] + stack[sp]; ++sp; break; case psOpAnd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; ++sp; break; case psOpAtan: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); ++sp; break; case psOpBitshift: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp + 1]; nn = (int)stack[sp]; if (nn > 0) { stack[sp + 1] = k << nn; } else if (nn < 0) { stack[sp + 1] = k >> -nn; } else { stack[sp + 1] = k; } ++sp; break; case psOpCeiling: if (sp >= psStackSize) { goto underflow; } stack[sp] = ceil(stack[sp]); break; case psOpCopy: if (sp >= psStackSize) { goto underflow; } nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (sp - nn < 0) { goto overflow; } for (i = 0; i < nn; ++i) { stack[sp - nn + i] = stack[sp + i]; } sp -= nn; break; case psOpCos: if (sp >= psStackSize) { goto underflow; } stack[sp] = cos(stack[sp]); break; case psOpCvi: if (sp >= psStackSize) { goto underflow; } stack[sp] = (int)stack[sp]; break; case psOpCvr: if (sp >= psStackSize) { goto underflow; } break; case psOpDiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] / stack[sp]; ++sp; break; case psOpDup: if (sp >= psStackSize) { goto underflow; } if (sp < 1) { goto overflow; } stack[sp - 1] = stack[sp]; --sp; break; case psOpEq: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; ++sp; break; case psOpExch: if (sp + 1 >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = stack[sp + 1]; stack[sp + 1] = t; break; case psOpExp: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = pow(stack[sp + 1], stack[sp]); ++sp; break; case psOpFalse: if (sp < 1) { goto overflow; } stack[sp - 1] = 0; --sp; break; case psOpFloor: if (sp >= psStackSize) { goto underflow; } stack[sp] = floor(stack[sp]); break; case psOpGe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; ++sp; break; case psOpGt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; ++sp; break; case psOpIdiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; ++sp; break; case psOpIndex: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp]; if (k < 0) { goto invalidArg; } if (sp + 1 + k >= psStackSize) { goto underflow; } stack[sp] = stack[sp + 1 + k]; break; case psOpLe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; ++sp; break; case psOpLn: if (sp >= psStackSize) { goto underflow; } stack[sp] = log(stack[sp]); break; case psOpLog: if (sp >= psStackSize) { goto underflow; } stack[sp] = log10(stack[sp]); break; case psOpLt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; ++sp; break; case psOpMod: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; ++sp; break; case psOpMul: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] * stack[sp]; ++sp; break; case psOpNe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; ++sp; break; case psOpNeg: if (sp >= psStackSize) { goto underflow; } stack[sp] = -stack[sp]; break; case psOpNot: if (sp >= psStackSize) { goto underflow; } stack[sp] = stack[sp] == 0 ? 1 : 0; break; case psOpOr: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; ++sp; break; case psOpPop: if (sp >= psStackSize) { goto underflow; } ++sp; break; case psOpRoll: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp++]; nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (k >= 0) { k %= nn; } else { k = -k % nn; if (k) { k = nn - k; } } for (i = 0; i < nn; ++i) { tmp[i] = stack[sp + i]; } for (i = 0; i < nn; ++i) { stack[sp + i] = tmp[(i + k) % nn]; } break; case psOpRound: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); break; case psOpSin: if (sp >= psStackSize) { goto underflow; } stack[sp] = sin(stack[sp]); break; case psOpSqrt: if (sp >= psStackSize) { goto underflow; } stack[sp] = sqrt(stack[sp]); break; case psOpSub: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] - stack[sp]; ++sp; break; case psOpTrue: if (sp < 1) { goto overflow; } stack[sp - 1] = 1; --sp; break; case psOpTruncate: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t) : ceil(t); break; case psOpXor: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; ++sp; break; case psOpPush: if (sp < 1) { goto overflow; } stack[--sp] = c->val.d; break; case psOpJ: ip = c->val.i; break; case psOpJz: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp++]; if (k == 0) { ip = c->val.i; } break; } } return sp; underflow: error(errSyntaxError, -1, "Stack underflow in PostScript function"); return sp; overflow: error(errSyntaxError, -1, "Stack overflow in PostScript function"); return sp; invalidArg: error(errSyntaxError, -1, "Invalid arg in PostScript function"); return sp; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10018/Function.h000066400000000000000000000161301417746362400225570ustar00rootroot00000000000000//======================================================================== // // Function.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef FUNCTION_H #define FUNCTION_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" class GList; class Dict; class Stream; struct PSCode; //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ #define funcMaxInputs 32 #define funcMaxOutputs 32 #define sampledFuncMaxInputs 16 class Function { public: Function(); virtual ~Function(); // Construct a function. Returns NULL if unsuccessful. static Function *parse(Object *funcObj, int recursion = 0); // Initialize the entries common to all function types. GBool init(Dict *dict); virtual Function *copy() = 0; // Return the function type: // -1 : identity // 0 : sampled // 2 : exponential // 3 : stitching // 4 : PostScript virtual int getType() = 0; // Return size of input and output tuples. int getInputSize() { return m; } int getOutputSize() { return n; } double getDomainMin(int i) { return domain[i][0]; } double getDomainMax(int i) { return domain[i][1]; } double getRangeMin(int i) { return range[i][0]; } double getRangeMax(int i) { return range[i][1]; } GBool getHasRange() { return hasRange; } // Transform an input tuple into an output tuple. virtual void transform(double *in, double *out) = 0; virtual GBool isOk() = 0; protected: int m, n; // size of input and output tuples double // min and max values for function domain domain[funcMaxInputs][2]; double // min and max values for function range range[funcMaxOutputs][2]; GBool hasRange; // set if range is defined }; //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ class IdentityFunction : public Function { public: IdentityFunction(); virtual ~IdentityFunction(); virtual Function *copy() { return new IdentityFunction(); } virtual int getType() { return -1; } virtual void transform(double *in, double *out); virtual GBool isOk() { return gTrue; } private: }; //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ class SampledFunction : public Function { public: SampledFunction(Object *funcObj, Dict *dict); virtual ~SampledFunction(); virtual Function *copy() { return new SampledFunction(this); } virtual int getType() { return 0; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getSampleSize(int i) { return sampleSize[i]; } double getEncodeMin(int i) { return encode[i][0]; } double getEncodeMax(int i) { return encode[i][1]; } double getDecodeMin(int i) { return decode[i][0]; } double getDecodeMax(int i) { return decode[i][1]; } double *getSamples() { return samples; } private: SampledFunction(SampledFunction *func); int // number of samples for each domain element sampleSize[funcMaxInputs]; double // min and max values for domain encoder encode[funcMaxInputs][2]; double // min and max values for range decoder decode[funcMaxOutputs][2]; double // input multipliers inputMul[funcMaxInputs]; int *idxOffset; double *samples; // the samples int nSamples; // size of the samples array double *sBuf; // buffer for the transform function double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ class ExponentialFunction : public Function { public: ExponentialFunction(Object *funcObj, Dict *dict); virtual ~ExponentialFunction(); virtual Function *copy() { return new ExponentialFunction(this); } virtual int getType() { return 2; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } double *getC0() { return c0; } double *getC1() { return c1; } double getE() { return e; } private: ExponentialFunction(ExponentialFunction *func); double c0[funcMaxOutputs]; double c1[funcMaxOutputs]; double e; GBool ok; }; //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ class StitchingFunction : public Function { public: StitchingFunction(Object *funcObj, Dict *dict, int recursion); virtual ~StitchingFunction(); virtual Function *copy() { return new StitchingFunction(this); } virtual int getType() { return 3; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getNumFuncs() { return k; } Function *getFunc(int i) { return funcs[i]; } double *getBounds() { return bounds; } double *getEncode() { return encode; } double *getScale() { return scale; } private: StitchingFunction(StitchingFunction *func); int k; Function **funcs; double *bounds; double *encode; double *scale; GBool ok; }; //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ class PostScriptFunction : public Function { public: PostScriptFunction(Object *funcObj, Dict *dict); virtual ~PostScriptFunction(); virtual Function *copy() { return new PostScriptFunction(this); } virtual int getType() { return 4; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } GString *getCodeString() { return codeString; } private: PostScriptFunction(PostScriptFunction *func); GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); void addCode(int *codePtr, int op); void addCodeI(int *codePtr, int op, int x); void addCodeD(int *codePtr, int op, double x); GString *getToken(Stream *str); int exec(double *stack, int sp0); GString *codeString; PSCode *code; int codeLen; int codeSize; double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10018/expected.txt000066400000000000000000000000461417746362400231620ustar00rootroot00000000000000Function.cc:1374:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10019/000077500000000000000000000000001417746362400206215ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.cc000066400000000000000000006770031417746362400233470ustar00rootroot00000000000000//======================================================================== // // PSOutputDev.cc // // Copyright 1996-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include "gmempp.h" #include "GString.h" #include "GList.h" #include "GHash.h" #include "config.h" #include "GlobalParams.h" #include "Object.h" #include "Error.h" #include "Function.h" #include "Gfx.h" #include "GfxState.h" #include "GfxFont.h" #include "UnicodeMap.h" #include "FoFiType1C.h" #include "FoFiTrueType.h" #include "Catalog.h" #include "Page.h" #include "Stream.h" #include "Annot.h" #include "PDFDoc.h" #include "XRef.h" #include "PreScanOutputDev.h" #include "CharCodeToUnicode.h" #include "Form.h" #include "TextString.h" #if HAVE_SPLASH # include "Splash.h" # include "SplashBitmap.h" # include "SplashOutputDev.h" #endif #include "PSOutputDev.h" // the MSVC math.h doesn't define this #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // PostScript prolog and setup //------------------------------------------------------------------------ // The '~' escapes mark prolog code that is emitted only in certain // levels: // // ~[123][ngs] // ^ ^----- n=psLevel_, g=psLevel_Gray, s=psLevel_Sep // +----- 1=psLevel1__, 2=psLevel2__, 3=psLevel3__ static const char *prolog[] = { "/xpdf 75 dict def xpdf begin", "% PDF special state", "/pdfDictSize 15 def", "~1ns", "/pdfStates 64 array def", " 0 1 63 {", " pdfStates exch pdfDictSize dict", " dup /pdfStateIdx 3 index put", " put", " } for", "~123ngs", "/pdfSetup {", " /pdfDuplex exch def", " /setpagedevice where {", " pop 2 dict begin", " /Policies 1 dict dup begin /PageSize 6 def end def", " pdfDuplex { /Duplex true def } if", " currentdict end setpagedevice", " } if", " /pdfPageW 0 def", " /pdfPageH 0 def", "} def", "/pdfSetupPaper {", " 2 copy pdfPageH ne exch pdfPageW ne or {", " /pdfPageH exch def", " /pdfPageW exch def", " /setpagedevice where {", " pop 3 dict begin", " /PageSize [pdfPageW pdfPageH] def", " pdfDuplex { /Duplex true def } if", " /ImagingBBox null def", " currentdict end setpagedevice", " } if", " } {", " pop pop", " } ifelse", "} def", "~1ns", "/pdfOpNames [", " /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke", " /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender", " /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath", "] def", "~123ngs", "/pdfStartPage {", "~1ns", " pdfStates 0 get begin", "~23ngs", " pdfDictSize dict begin", "~23n", " /pdfFillCS [] def", " /pdfFillXform {} def", " /pdfStrokeCS [] def", " /pdfStrokeXform {} def", "~1n", " /pdfFill 0 def", " /pdfStroke 0 def", "~1s", " /pdfFill [0 0 0 1] def", " /pdfStroke [0 0 0 1] def", "~23g", " /pdfFill 0 def", " /pdfStroke 0 def", "~23ns", " /pdfFill [0] def", " /pdfStroke [0] def", " /pdfFillOP false def", " /pdfStrokeOP false def", "~123ngs", " /pdfLastFill false def", " /pdfLastStroke false def", " /pdfTextMat [1 0 0 1 0 0] def", " /pdfFontSize 0 def", " /pdfCharSpacing 0 def", " /pdfTextRender 0 def", " /pdfTextRise 0 def", " /pdfWordSpacing 0 def", " /pdfHorizScaling 1 def", " /pdfTextClipPath [] def", "} def", "/pdfEndPage { end } def", "~23s", "% separation convention operators", "/findcmykcustomcolor where {", " pop", "}{", " /findcmykcustomcolor { 5 array astore } def", "} ifelse", "/setcustomcolor where {", " pop", "}{", " /setcustomcolor {", " exch", " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", " 0 4 getinterval cvx", " [ exch /dup load exch { mul exch dup } /forall load", " /pop load dup ] cvx", " ] setcolorspace setcolor", " } def", "} ifelse", "/customcolorimage where {", " pop", "}{", " /customcolorimage {", " gsave", " [ exch /Separation exch dup 4 get exch /DeviceCMYK exch", " 0 4 getinterval", " [ exch /dup load exch { mul exch dup } /forall load", " /pop load dup ] cvx", " ] setcolorspace", " 10 dict begin", " /ImageType 1 def", " /DataSource exch def", " /ImageMatrix exch def", " /BitsPerComponent exch def", " /Height exch def", " /Width exch def", " /Decode [1 0] def", " currentdict end", " image", " grestore", " } def", "} ifelse", "~123ngs", "% PDF color state", "~1n", "/g { dup /pdfFill exch def setgray", " /pdfLastFill true def /pdfLastStroke false def } def", "/G { dup /pdfStroke exch def setgray", " /pdfLastStroke true def /pdfLastFill false def } def", "/fCol {", " pdfLastFill not {", " pdfFill setgray", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke setgray", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~1s", "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/fCol {", " pdfLastFill not {", " pdfFill aload pop setcmykcolor", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke aload pop setcmykcolor", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~23n", "/cs { /pdfFillXform exch def dup /pdfFillCS exch def", " setcolorspace } def", "/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def", " setcolorspace } def", "/sc { pdfLastFill not {", " pdfFillCS setcolorspace pdfFillOP setoverprint", " } if", " dup /pdfFill exch def aload pop pdfFillXform setcolor", " /pdfLastFill true def /pdfLastStroke false def } def", "/SC { pdfLastStroke not {", " pdfStrokeCS setcolorspace pdfStrokeOP setoverprint", " } if", " dup /pdfStroke exch def aload pop pdfStrokeXform setcolor", " /pdfLastStroke true def /pdfLastFill false def } def", "/op { /pdfFillOP exch def", " pdfLastFill { pdfFillOP setoverprint } if } def", "/OP { /pdfStrokeOP exch def", " pdfLastStroke { pdfStrokeOP setoverprint } if } def", "/fCol {", " pdfLastFill not {", " pdfFillCS setcolorspace", " pdfFill aload pop pdfFillXform setcolor", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStrokeCS setcolorspace", " pdfStroke aload pop pdfStrokeXform setcolor", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~23g", "/g { dup /pdfFill exch def setgray", " /pdfLastFill true def /pdfLastStroke false def } def", "/G { dup /pdfStroke exch def setgray", " /pdfLastStroke true def /pdfLastFill false def } def", "/fCol {", " pdfLastFill not {", " pdfFill setgray", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke setgray", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~23s", "/k { 4 copy 4 array astore /pdfFill exch def setcmykcolor", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def } def", "/K { 4 copy 4 array astore /pdfStroke exch def setcmykcolor", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def } def", "/ck { 6 copy 6 array astore /pdfFill exch def", " findcmykcustomcolor exch setcustomcolor", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def } def", "/CK { 6 copy 6 array astore /pdfStroke exch def", " findcmykcustomcolor exch setcustomcolor", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def } def", "/op { /pdfFillOP exch def", " pdfLastFill { pdfFillOP setoverprint } if } def", "/OP { /pdfStrokeOP exch def", " pdfLastStroke { pdfStrokeOP setoverprint } if } def", "/fCol {", " pdfLastFill not {", " pdfFill aload length 4 eq {", " setcmykcolor", " }{", " findcmykcustomcolor exch setcustomcolor", " } ifelse", " pdfFillOP setoverprint", " /pdfLastFill true def /pdfLastStroke false def", " } if", "} def", "/sCol {", " pdfLastStroke not {", " pdfStroke aload length 4 eq {", " setcmykcolor", " }{", " findcmykcustomcolor exch setcustomcolor", " } ifelse", " pdfStrokeOP setoverprint", " /pdfLastStroke true def /pdfLastFill false def", " } if", "} def", "~3ns", "/opm {", " /setoverprintmode where { pop setoverprintmode } { pop } ifelse", "} def", "~123ngs", "% build a font", "/pdfMakeFont {", " 4 3 roll findfont", " 4 2 roll matrix scale makefont", " dup length dict begin", " { 1 index /FID ne { def } { pop pop } ifelse } forall", " /Encoding exch def", " currentdict", " end", " definefont pop", "} def", "/pdfMakeFont16 {", " exch findfont", " dup length dict begin", " { 1 index /FID ne { def } { pop pop } ifelse } forall", " /WMode exch def", " currentdict", " end", " definefont pop", "} def", "~3ngs", "/pdfMakeFont16L3 {", " 1 index /CIDFont resourcestatus {", " pop pop 1 index /CIDFont findresource /CIDFontType known", " } {", " false", " } ifelse", " {", " 0 eq { /Identity-H } { /Identity-V } ifelse", " exch 1 array astore composefont pop", " } {", " pdfMakeFont16", " } ifelse", "} def", "~123ngs", "% graphics state operators", "~1ns", "/q {", " gsave", " pdfOpNames length 1 sub -1 0 { pdfOpNames exch get load } for", " pdfStates pdfStateIdx 1 add get begin", " pdfOpNames { exch def } forall", "} def", "/Q { end grestore } def", "~23ngs", "/q { gsave pdfDictSize dict begin } def", "/Q {", " end grestore", "} def", "~123ngs", "/cm { concat } def", "/d { setdash } def", "/i { setflat } def", "/j { setlinejoin } def", "/J { setlinecap } def", "/M { setmiterlimit } def", "/w { setlinewidth } def", "% path segment operators", "/m { moveto } def", "/l { lineto } def", "/c { curveto } def", "/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto", " neg 0 rlineto closepath } def", "/h { closepath } def", "% path painting operators", "/S { sCol stroke } def", "/Sf { fCol stroke } def", "/f { fCol fill } def", "/f* { fCol eofill } def", "% clipping operators", "/W { clip newpath } def", "/W* { eoclip newpath } def", "/Ws { strokepath clip newpath } def", "% text state operators", "/Tc { /pdfCharSpacing exch def } def", "/Tf { dup /pdfFontSize exch def", " dup pdfHorizScaling mul exch matrix scale", " pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put", " exch findfont exch makefont setfont } def", "/Tr { /pdfTextRender exch def } def", "/Ts { /pdfTextRise exch def } def", "/Tw { /pdfWordSpacing exch def } def", "/Tz { /pdfHorizScaling exch def } def", "% text positioning operators", "/Td { pdfTextMat transform moveto } def", "/Tm { /pdfTextMat exch def } def", "% text string operators", "/xyshow where {", " pop", " /xyshow2 {", " dup length array", " 0 2 2 index length 1 sub {", " 2 index 1 index 2 copy get 3 1 roll 1 add get", " pdfTextMat dtransform", " 4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put", " } for", " exch pop", " xyshow", " } def", "}{", " /xyshow2 {", " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval show moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval show moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/cshow where {", " pop", " /xycp {", // xycharpath " 0 3 2 roll", " {", " pop pop currentpoint 3 2 roll", " 1 string dup 0 4 3 roll put false charpath moveto", " 2 copy get 2 index 2 index 1 add get", " pdfTextMat dtransform rmoveto", " 2 add", " } exch cshow", " pop pop", " } def", "}{", " /xycp {", // xycharpath " currentfont /FontType get 0 eq {", " 0 2 3 index length 1 sub {", " currentpoint 4 index 3 index 2 getinterval false charpath moveto", " 2 copy get 2 index 3 2 roll 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } {", " 0 1 3 index length 1 sub {", " currentpoint 4 index 3 index 1 getinterval false charpath moveto", " 2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get", " pdfTextMat dtransform rmoveto", " } for", " } ifelse", " pop pop", " } def", "} ifelse", "/Tj {", " fCol", // because stringwidth has to draw Type 3 chars " 0 pdfTextRise pdfTextMat dtransform rmoveto", " currentpoint 4 2 roll", " pdfTextRender 1 and 0 eq {", " 2 copy xyshow2", " } if", " pdfTextRender 3 and dup 1 eq exch 2 eq or {", " 3 index 3 index moveto", " 2 copy", " currentfont /FontType get 3 eq { fCol } { sCol } ifelse", " xycp currentpoint stroke moveto", " } if", " pdfTextRender 4 and 0 ne {", " 4 2 roll moveto xycp", " /pdfTextClipPath [ pdfTextClipPath aload pop", " {/moveto cvx}", " {/lineto cvx}", " {/curveto cvx}", " {/closepath cvx}", " pathforall ] def", " currentpoint newpath moveto", " } {", " pop pop pop pop", " } ifelse", " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", "} def", "/Tj3 {", " pdfTextRender 3 and 3 ne {" " fCol", // because stringwidth has to draw Type 3 chars " 0 pdfTextRise pdfTextMat dtransform rmoveto", " xyshow2", " 0 pdfTextRise neg pdfTextMat dtransform rmoveto", " } {", " pop pop", " } ifelse", "} def", "/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0", " pdfTextMat dtransform rmoveto } def", "/TJmV { 0.001 mul pdfFontSize mul neg 0 exch", " pdfTextMat dtransform rmoveto } def", "/Tclip { pdfTextClipPath cvx exec clip newpath", " /pdfTextClipPath [] def } def", "~1ns", "% Level 1 image operators", "~1n", "/pdfIm1 {", " /pdfImBuf1 4 index string def", " { currentfile pdfImBuf1 readhexstring pop } image", "} def", "~1s", "/pdfIm1Sep {", " /pdfImBuf1 4 index string def", " /pdfImBuf2 4 index string def", " /pdfImBuf3 4 index string def", " /pdfImBuf4 4 index string def", " { currentfile pdfImBuf1 readhexstring pop }", " { currentfile pdfImBuf2 readhexstring pop }", " { currentfile pdfImBuf3 readhexstring pop }", " { currentfile pdfImBuf4 readhexstring pop }", " true 4 colorimage", "} def", "~1ns", "/pdfImM1 {", " fCol /pdfImBuf1 4 index 7 add 8 idiv string def", " { currentfile pdfImBuf1 readhexstring pop } imagemask", "} def", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/pdfImM1a {", " { pdfImStr } imagemask", " pop pop", "} def", "~23ngs", "% Level 2/3 image operators", "/pdfImBuf 100 string def", "/pdfImStr {", " 2 copy exch length lt {", " 2 copy get exch 1 add exch", " } {", " ()", " } ifelse", "} def", "/skipEOD {", " { currentfile pdfImBuf readline", " not { pop exit } if", " (%-EOD-) eq { exit } if } loop", "} def", "/pdfIm { image skipEOD } def", "~3ngs", "/pdfMask {", " /ReusableStreamDecode filter", " skipEOD", " /maskStream exch def", "} def", "/pdfMaskEnd { maskStream closefile } def", "/pdfMaskInit {", " /maskArray exch def", " /maskIdx 0 def", "} def", "/pdfMaskSrc {", " maskIdx maskArray length lt {", " maskArray maskIdx get", " /maskIdx maskIdx 1 add def", " } {", " ()", " } ifelse", "} def", "~23s", "/pdfImSep {", " findcmykcustomcolor exch", " dup /Width get /pdfImBuf1 exch string def", " dup /Decode get aload pop 1 index sub /pdfImDecodeRange exch def", " /pdfImDecodeLow exch def", " begin Width Height BitsPerComponent ImageMatrix DataSource end", " /pdfImData exch def", " { pdfImData pdfImBuf1 readstring pop", " 0 1 2 index length 1 sub {", " 1 index exch 2 copy get", " pdfImDecodeRange mul 255 div pdfImDecodeLow add round cvi", " 255 exch sub put", " } for }", " 6 5 roll customcolorimage", " skipEOD", "} def", "~23ngs", "/pdfImM { fCol imagemask skipEOD } def", "/pr {", " 4 2 roll exch 5 index div exch 4 index div moveto", " exch 3 index div dup 0 rlineto", " exch 2 index div 0 exch rlineto", " neg 0 rlineto", " closepath", "} def", "/pdfImClip { gsave clip } def", "/pdfImClipEnd { grestore } def", "~23ns", "% shading operators", "/colordelta {", " false 0 1 3 index length 1 sub {", " dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {", " pop true", " } if", " } for", " exch pop exch pop", "} def", "/funcCol { func n array astore } def", "/funcSH {", " dup 0 eq {", " true", " } {", " dup 6 eq {", " false", " } {", " 4 index 4 index funcCol dup", " 6 index 4 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " 5 index 5 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " 6 index 8 index funcCol dup", " 3 1 roll colordelta 3 1 roll", " colordelta or or or", " } ifelse", " } ifelse", " {", " 1 add", " 4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch", " 6 index 6 index 4 index 4 index 4 index funcSH", " 2 index 6 index 6 index 4 index 4 index funcSH", " 6 index 2 index 4 index 6 index 4 index funcSH", " 5 3 roll 3 2 roll funcSH pop pop", " } {", " pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul", "~23n", " funcCol sc", "~23s", " funcCol aload pop k", "~23ns", " dup 4 index exch mat transform m", " 3 index 3 index mat transform l", " 1 index 3 index mat transform l", " mat transform l pop pop h f*", " } ifelse", "} def", "/axialCol {", " dup 0 lt {", " pop t0", " } {", " dup 1 gt {", " pop t1", " } {", " dt mul t0 add", " } ifelse", " } ifelse", " func n array astore", "} def", "/axialSH {", " dup 2 lt {", " true", " } {", " dup 8 eq {", " false", " } {", " 2 index axialCol 2 index axialCol colordelta", " } ifelse", " } ifelse", " {", " 1 add 3 1 roll 2 copy add 0.5 mul", " dup 4 3 roll exch 4 index axialSH", " exch 3 2 roll axialSH", " } {", " pop 2 copy add 0.5 mul", "~23n", " axialCol sc", "~23s", " axialCol aload pop k", "~23ns", " exch dup dx mul x0 add exch dy mul y0 add", " 3 2 roll dup dx mul x0 add exch dy mul y0 add", " dx abs dy abs ge {", " 2 copy yMin sub dy mul dx div add yMin m", " yMax sub dy mul dx div add yMax l", " 2 copy yMax sub dy mul dx div add yMax l", " yMin sub dy mul dx div add yMin l", " h f*", " } {", " exch 2 copy xMin sub dx mul dy div add xMin exch m", " xMax sub dx mul dy div add xMax exch l", " exch 2 copy xMax sub dx mul dy div add xMax exch l", " xMin sub dx mul dy div add xMin exch l", " h f*", " } ifelse", " } ifelse", "} def", "/radialCol {", " dup t0 lt {", " pop t0", " } {", " dup t1 gt {", " pop t1", " } if", " } ifelse", " func n array astore", "} def", "/radialSH {", " dup 0 eq {", " true", " } {", " dup 8 eq {", " false", " } {", " 2 index dt mul t0 add radialCol", " 2 index dt mul t0 add radialCol colordelta", " } ifelse", " } ifelse", " {", " 1 add 3 1 roll 2 copy add 0.5 mul", " dup 4 3 roll exch 4 index radialSH", " exch 3 2 roll radialSH", " } {", " pop 2 copy add 0.5 mul dt mul t0 add", "~23n", " radialCol sc", "~23s", " radialCol aload pop k", "~23ns", " encl {", " exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " 0 360 arc h", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " 360 0 arcn h f", " } {", " 2 copy", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a1 a2 arcn", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a2 a1 arcn h", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a1 a2 arc", " dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add", " a2 a1 arc h f", " } ifelse", " } ifelse", "} def", "~123ngs", "end", NULL }; static const char *minLineWidthProlog[] = { "/pdfDist { dup dtransform dup mul exch dup mul add 0.5 mul sqrt } def", "/pdfIDist { dup idtransform dup mul exch dup mul add 0.5 mul sqrt } def", "/pdfMinLineDist pdfMinLineWidth pdfDist def", "/setlinewidth {", " dup pdfDist pdfMinLineDist lt {", " pop pdfMinLineDist pdfIDist", " } if", " setlinewidth", "} bind def", NULL }; static const char *cmapProlog[] = { "/CIDInit /ProcSet findresource begin", "10 dict begin", " begincmap", " /CMapType 1 def", " /CMapName /Identity-H def", " /CIDSystemInfo 3 dict dup begin", " /Registry (Adobe) def", " /Ordering (Identity) def", " /Supplement 0 def", " end def", " 1 begincodespacerange", " <0000> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 0", " endcidrange", " endcmap", " currentdict CMapName exch /CMap defineresource pop", "end", "10 dict begin", " begincmap", " /CMapType 1 def", " /CMapName /Identity-V def", " /CIDSystemInfo 3 dict dup begin", " /Registry (Adobe) def", " /Ordering (Identity) def", " /Supplement 0 def", " end def", " /WMode 1 def", " 1 begincodespacerange", " <0000> ", " endcodespacerange", " 0 usefont", " 1 begincidrange", " <0000> 0", " endcidrange", " endcmap", " currentdict CMapName exch /CMap defineresource pop", "end", "end", NULL }; //------------------------------------------------------------------------ // Fonts //------------------------------------------------------------------------ struct PSSubstFont { const char *psName; // PostScript name double mWidth; // width of 'm' character }; // NB: must be in same order as base14SubstFonts in GfxFont.cc static PSSubstFont psBase14SubstFonts[14] = { {"Courier", 0.600}, {"Courier-Oblique", 0.600}, {"Courier-Bold", 0.600}, {"Courier-BoldOblique", 0.600}, {"Helvetica", 0.833}, {"Helvetica-Oblique", 0.833}, {"Helvetica-Bold", 0.889}, {"Helvetica-BoldOblique", 0.889}, {"Times-Roman", 0.788}, {"Times-Italic", 0.722}, {"Times-Bold", 0.833}, {"Times-BoldItalic", 0.778}, // the last two are never used for substitution {"Symbol", 0}, {"ZapfDingbats", 0} }; class PSFontInfo { public: PSFontInfo(Ref fontIDA) { fontID = fontIDA; ff = NULL; } Ref fontID; PSFontFileInfo *ff; // pointer to font file info; NULL indicates // font mapping failed }; enum PSFontFileLocation { psFontFileResident, psFontFileEmbedded, psFontFileExternal }; class PSFontFileInfo { public: PSFontFileInfo(GString *psNameA, GfxFontType typeA, PSFontFileLocation locA); ~PSFontFileInfo(); GString *psName; // name under which font is defined GfxFontType type; // font type PSFontFileLocation loc; // font location Ref embFontID; // object ID for the embedded font file // (for all embedded fonts) GString *extFileName; // external font file path // (for all external fonts) GString *encoding; // encoding name (for resident CID fonts) int *codeToGID; // mapping from code/CID to GID // (for TrueType, OpenType-TrueType, and // CID OpenType-CFF fonts) int codeToGIDLen; // length of codeToGID array }; PSFontFileInfo::PSFontFileInfo(GString *psNameA, GfxFontType typeA, PSFontFileLocation locA) { psName = psNameA; type = typeA; loc = locA; embFontID.num = embFontID.gen = -1; extFileName = NULL; encoding = NULL; codeToGID = NULL; codeToGIDLen = 0; } PSFontFileInfo::~PSFontFileInfo() { delete psName; if (extFileName) { delete extFileName; } if (encoding) { delete encoding; } if (codeToGID) { gfree(codeToGID); } } //------------------------------------------------------------------------ // process colors //------------------------------------------------------------------------ #define psProcessCyan 1 #define psProcessMagenta 2 #define psProcessYellow 4 #define psProcessBlack 8 #define psProcessCMYK 15 //------------------------------------------------------------------------ // PSOutCustomColor //------------------------------------------------------------------------ class PSOutCustomColor { public: PSOutCustomColor(double cA, double mA, double yA, double kA, GString *nameA); ~PSOutCustomColor(); double c, m, y, k; GString *name; PSOutCustomColor *next; }; PSOutCustomColor::PSOutCustomColor(double cA, double mA, double yA, double kA, GString *nameA) { c = cA; m = mA; y = yA; k = kA; name = nameA; next = NULL; } PSOutCustomColor::~PSOutCustomColor() { delete name; } //------------------------------------------------------------------------ struct PSOutImgClipRect { int x0, x1, y0, y1; }; //------------------------------------------------------------------------ struct PSOutPaperSize { PSOutPaperSize(int wA, int hA) { w = wA; h = hA; } int w, h; }; //------------------------------------------------------------------------ // DeviceNRecoder //------------------------------------------------------------------------ class DeviceNRecoder: public FilterStream { public: DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA); virtual ~DeviceNRecoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: GBool fillBuf(); int width, height; GfxImageColorMap *colorMap; Function *func; ImageStream *imgStr; int buf[gfxColorMaxComps]; int pixelIdx; int bufIdx; int bufSize; }; DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA): FilterStream(strA) { width = widthA; height = heightA; colorMap = colorMapA; imgStr = NULL; pixelIdx = 0; bufIdx = gfxColorMaxComps; bufSize = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> getAlt()->getNComps(); func = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> getTintTransformFunc(); } DeviceNRecoder::~DeviceNRecoder() { if (str->isEncoder()) { delete str; } } Stream *DeviceNRecoder::copy() { error(errInternal, -1, "Called copy() on DeviceNRecoder"); return NULL; } void DeviceNRecoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } void DeviceNRecoder::close() { delete imgStr; imgStr = NULL; str->close(); } GBool DeviceNRecoder::fillBuf() { Guchar pixBuf[gfxColorMaxComps]; GfxColor color; double x[gfxColorMaxComps], y[gfxColorMaxComps]; int i; if (pixelIdx >= width * height) { return gFalse; } imgStr->getPixel(pixBuf); colorMap->getColor(pixBuf, &color); for (i = 0; i < ((GfxDeviceNColorSpace *)colorMap->getColorSpace())->getNComps(); ++i) { x[i] = colToDbl(color.c[i]); } func->transform(x, y); for (i = 0; i < bufSize; ++i) { buf[i] = (int)(y[i] * 255 + 0.5); } bufIdx = 0; ++pixelIdx; return gTrue; } //------------------------------------------------------------------------ // GrayRecoder //------------------------------------------------------------------------ class GrayRecoder: public FilterStream { public: GrayRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA); virtual ~GrayRecoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= width && !fillBuf()) ? EOF : buf[bufIdx]; } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: GBool fillBuf(); int width, height; GfxImageColorMap *colorMap; ImageStream *imgStr; Guchar *buf; int bufIdx; }; GrayRecoder::GrayRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA): FilterStream(strA) { width = widthA; height = heightA; colorMap = colorMapA; imgStr = NULL; buf = (Guchar *)gmalloc(width); bufIdx = width; } GrayRecoder::~GrayRecoder() { gfree(buf); if (str->isEncoder()) { delete str; } } Stream *GrayRecoder::copy() { error(errInternal, -1, "Called copy() on GrayRecoder"); return NULL; } void GrayRecoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } void GrayRecoder::close() { delete imgStr; imgStr = NULL; str->close(); } GBool GrayRecoder::fillBuf() { Guchar *line; if (!(line = imgStr->getLine())) { bufIdx = width; return gFalse; } //~ this should probably use the rendering intent from the image //~ dict, or from the content stream colorMap->getGrayByteLine(line, buf, width, gfxRenderingIntentRelativeColorimetric); bufIdx = 0; return gTrue; } //------------------------------------------------------------------------ // ColorKeyToMaskEncoder //------------------------------------------------------------------------ class ColorKeyToMaskEncoder: public FilterStream { public: ColorKeyToMaskEncoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA, int *maskColorsA); virtual ~ColorKeyToMaskEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } virtual int lookChar() { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: GBool fillBuf(); int width, height; GfxImageColorMap *colorMap; int numComps; int *maskColors; ImageStream *imgStr; Guchar *buf; int bufIdx; int bufSize; }; ColorKeyToMaskEncoder::ColorKeyToMaskEncoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA, int *maskColorsA): FilterStream(strA) { width = widthA; height = heightA; colorMap = colorMapA; numComps = colorMap->getNumPixelComps(); maskColors = maskColorsA; imgStr = NULL; bufSize = (width + 7) / 8; buf = (Guchar *)gmalloc(bufSize); bufIdx = width; } ColorKeyToMaskEncoder::~ColorKeyToMaskEncoder() { gfree(buf); if (str->isEncoder()) { delete str; } } Stream *ColorKeyToMaskEncoder::copy() { error(errInternal, -1, "Called copy() on ColorKeyToMaskEncoder"); return NULL; } void ColorKeyToMaskEncoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); } void ColorKeyToMaskEncoder::close() { delete imgStr; imgStr = NULL; str->close(); } GBool ColorKeyToMaskEncoder::fillBuf() { Guchar *line, *linePtr, *bufPtr; Guchar byte; int x, xx, i; if (!(line = imgStr->getLine())) { bufIdx = width; return gFalse; } linePtr = line; bufPtr = buf; for (x = 0; x < width; x += 8) { byte = 0; for (xx = 0; xx < 8; ++xx) { byte = (Guchar)(byte << 1); if (x + xx < width) { for (i = 0; i < numComps; ++i) { if (linePtr[i] < maskColors[2 * i] || linePtr[i] > maskColors[2 * i + 1]) { break; } } if (i >= numComps) { byte |= 1; } linePtr += numComps; } else { byte |= 1; } } *bufPtr++ = byte; } bufIdx = 0; return gTrue; } //------------------------------------------------------------------------ // PSOutputDev //------------------------------------------------------------------------ extern "C" { typedef void (*SignalFunc)(int); } static void outputToFile(void *stream, const char *data, int len) { fwrite(data, 1, len, (FILE *)stream); } PSOutputDev::PSOutputDev(char *fileName, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, GBool honorUserUnitA) { FILE *f; PSFileType fileTypeA; underlayCbk = NULL; underlayCbkData = NULL; overlayCbk = NULL; overlayCbkData = NULL; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; rasterizePage = NULL; fontInfo = new GList(); fontFileInfo = new GHash(); imgIDs = NULL; formIDs = NULL; visitedResources = NULL; saveStack = NULL; paperSizes = NULL; embFontList = NULL; customColors = NULL; haveTextClip = gFalse; t3String = NULL; // open file or pipe if (!strcmp(fileName, "-")) { fileTypeA = psStdout; f = stdout; } else if (fileName[0] == '|') { fileTypeA = psPipe; #ifdef HAVE_POPEN #ifndef _WIN32 signal(SIGPIPE, (SignalFunc)SIG_IGN); #endif if (!(f = popen(fileName + 1, "w"))) { error(errIO, -1, "Couldn't run print command '{0:s}'", fileName); ok = gFalse; return; } #else error(errIO, -1, "Print commands are not supported ('{0:s}')", fileName); ok = gFalse; return; #endif } else { fileTypeA = psFile; if (!(f = fopen(fileName, "w"))) { error(errIO, -1, "Couldn't open PostScript file '{0:s}'", fileName); ok = gFalse; return; } } init(outputToFile, f, fileTypeA, docA, firstPageA, lastPageA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA); } PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, PSOutCustomCodeCbk customCodeCbkA, void *customCodeCbkDataA, GBool honorUserUnitA) { underlayCbk = NULL; underlayCbkData = NULL; overlayCbk = NULL; overlayCbkData = NULL; customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; rasterizePage = NULL; fontInfo = new GList(); fontFileInfo = new GHash(); imgIDs = NULL; formIDs = NULL; visitedResources = NULL; saveStack = NULL; paperSizes = NULL; embFontList = NULL; customColors = NULL; haveTextClip = gFalse; t3String = NULL; init(outputFuncA, outputStreamA, psGeneric, docA, firstPageA, lastPageA, modeA, imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA, honorUserUnitA); } void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, GBool honorUserUnitA) { Catalog *catalog; Page *page; PDFRectangle *box; PSOutPaperSize *size; PSFontFileInfo *ff; GList *names; double userUnit; int pg, w, h, i; // initialize ok = gTrue; outputFunc = outputFuncA; outputStream = outputStreamA; fileType = fileTypeA; doc = docA; xref = doc->getXRef(); catalog = doc->getCatalog(); if ((firstPage = firstPageA) < 1) { firstPage = 1; } if ((lastPage = lastPageA) > doc->getNumPages()) { lastPage = doc->getNumPages(); } level = globalParams->getPSLevel(); mode = modeA; honorUserUnit = honorUserUnitA; paperWidth = globalParams->getPSPaperWidth(); paperHeight = globalParams->getPSPaperHeight(); imgLLX = imgLLXA; imgLLY = imgLLYA; imgURX = imgURXA; imgURY = imgURYA; if (imgLLX == 0 && imgURX == 0 && imgLLY == 0 && imgURY == 0) { globalParams->getPSImageableArea(&imgLLX, &imgLLY, &imgURX, &imgURY); } if (paperWidth < 0 || paperHeight < 0) { paperMatch = gTrue; paperSizes = new GList(); paperWidth = paperHeight = 1; // in case the document has zero pages for (pg = firstPage; pg <= lastPage; ++pg) { page = catalog->getPage(pg); if (honorUserUnit) { userUnit = page->getUserUnit(); } else { userUnit = 1; } if (globalParams->getPSUseCropBoxAsPage()) { w = (int)ceil(page->getCropWidth() * userUnit); h = (int)ceil(page->getCropHeight() * userUnit); } else { w = (int)ceil(page->getMediaWidth() * userUnit); h = (int)ceil(page->getMediaHeight() * userUnit); } for (i = 0; i < paperSizes->getLength(); ++i) { size = (PSOutPaperSize *)paperSizes->get(i); if (size->w == w && size->h == h) { break; } } if (i == paperSizes->getLength()) { paperSizes->append(new PSOutPaperSize(w, h)); } if (w > paperWidth) { paperWidth = w; } if (h > paperHeight) { paperHeight = h; } } // NB: img{LLX,LLY,URX,URY} will be set by startPage() } else { paperMatch = gFalse; } preload = globalParams->getPSPreload(); manualCtrl = manualCtrlA; if (mode == psModeForm) { lastPage = firstPage; } processColors = 0; inType3Char = gFalse; #if OPI_SUPPORT // initialize OPI nesting levels opi13Nest = 0; opi20Nest = 0; #endif tx0 = ty0 = -1; xScale0 = yScale0 = 0; rotate0 = -1; clipLLX0 = clipLLY0 = 0; clipURX0 = clipURY0 = -1; // initialize font lists, etc. for (i = 0; i < 14; ++i) { ff = new PSFontFileInfo(new GString(psBase14SubstFonts[i].psName), fontType1, psFontFileResident); fontFileInfo->add(ff->psName, ff); } names = globalParams->getPSResidentFonts(); for (i = 0; i < names->getLength(); ++i) { if (!fontFileInfo->lookup((GString *)names->get(i))) { ff = new PSFontFileInfo((GString *)names->get(i), fontType1, psFontFileResident); fontFileInfo->add(ff->psName, ff); } } delete names; imgIDLen = 0; imgIDSize = 0; formIDLen = 0; formIDSize = 0; noStateChanges = gFalse; saveStack = new GList(); numTilingPatterns = 0; nextFunc = 0; // initialize embedded font resource comment list embFontList = new GString(); if (!manualCtrl) { // this check is needed in case the document has zero pages if (firstPage <= catalog->getNumPages()) { writeHeader(catalog->getPage(firstPage)->getMediaBox(), catalog->getPage(firstPage)->getCropBox(), catalog->getPage(firstPage)->getRotate()); } else { box = new PDFRectangle(0, 0, 1, 1); writeHeader(box, box, 0); delete box; } if (mode != psModeForm) { writePS("%%BeginProlog\n"); } writeXpdfProcset(); if (mode != psModeForm) { writePS("%%EndProlog\n"); writePS("%%BeginSetup\n"); } writeDocSetup(catalog); if (mode != psModeForm) { writePS("%%EndSetup\n"); } } // initialize sequential page number seqPage = 1; } PSOutputDev::~PSOutputDev() { PSOutCustomColor *cc; if (ok) { if (!manualCtrl) { writePS("%%Trailer\n"); writeTrailer(); if (mode != psModeForm) { writePS("%%EOF\n"); } } if (fileType == psFile) { fclose((FILE *)outputStream); } #ifdef HAVE_POPEN else if (fileType == psPipe) { pclose((FILE *)outputStream); #ifndef _WIN32 signal(SIGPIPE, (SignalFunc)SIG_DFL); #endif } #endif } gfree(rasterizePage); if (paperSizes) { deleteGList(paperSizes, PSOutPaperSize); } if (embFontList) { delete embFontList; } deleteGList(fontInfo, PSFontInfo); deleteGHash(fontFileInfo, PSFontFileInfo); gfree(imgIDs); gfree(formIDs); if (saveStack) { delete saveStack; } while (customColors) { cc = customColors; customColors = cc->next; delete cc; } } GBool PSOutputDev::checkIO() { if (fileType == psFile || fileType == psPipe || fileType == psStdout) { if (ferror((FILE *)outputStream)) { error(errIO, -1, "Error writing to PostScript file"); return gFalse; } } return gTrue; } void PSOutputDev::writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox, int pageRotate) { Object info, obj1; PSOutPaperSize *size; double x1, y1, x2, y2; int i; switch (mode) { case psModePS: writePS("%!PS-Adobe-3.0\n"); break; case psModeEPS: writePS("%!PS-Adobe-3.0 EPSF-3.0\n"); break; case psModeForm: writePS("%!PS-Adobe-3.0 Resource-Form\n"); break; } writePSFmt("%XpdfVersion: {0:s}\n", xpdfVersion); xref->getDocInfo(&info); if (info.isDict() && info.dictLookup("Creator", &obj1)->isString()) { writePS("%%Creator: "); writePSTextLine(obj1.getString()); } obj1.free(); if (info.isDict() && info.dictLookup("Title", &obj1)->isString()) { writePS("%%Title: "); writePSTextLine(obj1.getString()); } obj1.free(); info.free(); writePSFmt("%%LanguageLevel: {0:d}\n", level >= psLevel3 ? 3 : level >= psLevel2 ? 2 : 1); if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { writePS("%%DocumentProcessColors: (atend)\n"); writePS("%%DocumentCustomColors: (atend)\n"); } writePS("%%DocumentSuppliedResources: (atend)\n"); switch (mode) { case psModePS: if (paperMatch) { for (i = 0; i < paperSizes->getLength(); ++i) { size = (PSOutPaperSize *)paperSizes->get(i); writePSFmt("%%{0:s} {1:d}x{2:d} {1:d} {2:d} 0 () ()\n", i==0 ? "DocumentMedia:" : "+", size->w, size->h); } } else { writePSFmt("%%DocumentMedia: plain {0:d} {1:d} 0 () ()\n", paperWidth, paperHeight); } writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); writePSFmt("%%Pages: {0:d}\n", lastPage - firstPage + 1); writePS("%%EndComments\n"); if (!paperMatch) { writePS("%%BeginDefaults\n"); writePS("%%PageMedia: plain\n"); writePS("%%EndDefaults\n"); } break; case psModeEPS: epsX1 = cropBox->x1; epsY1 = cropBox->y1; epsX2 = cropBox->x2; epsY2 = cropBox->y2; if (pageRotate == 0 || pageRotate == 180) { x1 = epsX1; y1 = epsY1; x2 = epsX2; y2 = epsY2; } else { // pageRotate == 90 || pageRotate == 270 x1 = 0; y1 = 0; x2 = epsY2 - epsY1; y2 = epsX2 - epsX1; } writePSFmt("%%BoundingBox: {0:d} {1:d} {2:d} {3:d}\n", (int)floor(x1), (int)floor(y1), (int)ceil(x2), (int)ceil(y2)); if (floor(x1) != ceil(x1) || floor(y1) != ceil(y1) || floor(x2) != ceil(x2) || floor(y2) != ceil(y2)) { writePSFmt("%%HiResBoundingBox: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", x1, y1, x2, y2); } writePS("%%EndComments\n"); break; case psModeForm: writePS("%%EndComments\n"); writePS("32 dict dup begin\n"); writePSFmt("/BBox [{0:d} {1:d} {2:d} {3:d}] def\n", (int)floor(mediaBox->x1), (int)floor(mediaBox->y1), (int)ceil(mediaBox->x2), (int)ceil(mediaBox->y2)); writePS("/FormType 1 def\n"); writePS("/Matrix [1 0 0 1 0 0] def\n"); break; } } void PSOutputDev::writeXpdfProcset() { GBool lev1, lev2, lev3, nonSep, gray, sep; const char **p; const char *q; double w; writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion); writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright); lev1 = lev2 = lev3 = nonSep = gray = sep = gTrue; for (p = prolog; *p; ++p) { if ((*p)[0] == '~') { lev1 = lev2 = lev3 = nonSep = gray = sep = gFalse; for (q = *p + 1; *q; ++q) { switch (*q) { case '1': lev1 = gTrue; break; case '2': lev2 = gTrue; break; case '3': lev3 = gTrue; break; case 'g': gray = gTrue; break; case 'n': nonSep = gTrue; break; case 's': sep = gTrue; break; } } } else if ((level == psLevel1 && lev1 && nonSep) || (level == psLevel1Sep && lev1 && sep) || (level == psLevel2 && lev2 && nonSep) || (level == psLevel2Gray && lev2 && gray) || (level == psLevel2Sep && lev2 && sep) || (level == psLevel3 && lev3 && nonSep) || (level == psLevel3Gray && lev3 && gray) || (level == psLevel3Sep && lev3 && sep)) { writePSFmt("{0:s}\n", *p); } } if ((w = globalParams->getPSMinLineWidth()) > 0) { writePSFmt("/pdfMinLineWidth {0:.4g} def\n", w); for (p = minLineWidthProlog; *p; ++p) { writePSFmt("{0:s}\n", *p); } } writePS("%%EndResource\n"); if (level >= psLevel3) { for (p = cmapProlog; *p; ++p) { writePSFmt("{0:s}\n", *p); } } } void PSOutputDev::writeDocSetup(Catalog *catalog) { Page *page; Dict *resDict; Annots *annots; Form *form; Object obj1, obj2, obj3; GString *s; GBool needDefaultFont; int pg, i, j; // check to see which pages will be rasterized if (firstPage <= lastPage) { rasterizePage = (char *)gmalloc(lastPage - firstPage + 1); for (pg = firstPage; pg <= lastPage; ++pg) { rasterizePage[pg - firstPage] = (char)checkIfPageNeedsToBeRasterized(pg); } } else { rasterizePage = NULL; } visitedResources = (char *)gmalloc(xref->getNumObjects()); memset(visitedResources, 0, xref->getNumObjects()); if (mode == psModeForm) { // swap the form and xpdf dicts writePS("xpdf end begin dup begin\n"); } else { writePS("xpdf begin\n"); } needDefaultFont = gFalse; for (pg = firstPage; pg <= lastPage; ++pg) { if (rasterizePage[pg - firstPage]) { continue; } page = catalog->getPage(pg); if ((resDict = page->getResourceDict())) { setupResources(resDict); } annots = new Annots(doc, page->getAnnots(&obj1)); obj1.free(); if (annots->getNumAnnots()) { needDefaultFont = gTrue; } for (i = 0; i < annots->getNumAnnots(); ++i) { if (annots->getAnnot(i)->getAppearance(&obj1)->isStream()) { obj1.streamGetDict()->lookup("Resources", &obj2); if (obj2.isDict()) { setupResources(obj2.getDict()); } obj2.free(); } obj1.free(); } delete annots; } if ((form = catalog->getForm())) { if (form->getNumFields() > 0) { needDefaultFont = gTrue; } for (i = 0; i < form->getNumFields(); ++i) { form->getField(i)->getResources(&obj1); if (obj1.isArray()) { for (j = 0; j < obj1.arrayGetLength(); ++j) { obj1.arrayGet(j, &obj2); if (obj2.isDict()) { setupResources(obj2.getDict()); } obj2.free(); } } else if (obj1.isDict()) { setupResources(obj1.getDict()); } obj1.free(); } } if (needDefaultFont) { setupDefaultFont(); } if (mode != psModeForm) { if (mode != psModeEPS && !manualCtrl) { writePSFmt("{0:s} pdfSetup\n", globalParams->getPSDuplex() ? "true" : "false"); if (!paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", paperWidth, paperHeight); } } #if OPI_SUPPORT if (globalParams->getPSOPI()) { writePS("/opiMatrix matrix currentmatrix def\n"); } #endif } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomDocSetup, 0, customCodeCbkData))) { writePS(s->getCString()); delete s; } } if (mode != psModeForm) { writePS("end\n"); } gfree(visitedResources); visitedResources = NULL; } void PSOutputDev::writePageTrailer() { if (mode != psModeForm) { writePS("pdfEndPage\n"); } } void PSOutputDev::writeTrailer() { PSOutCustomColor *cc; if (mode == psModeForm) { writePS("/Foo exch /Form defineresource pop\n"); } else { writePS("%%DocumentSuppliedResources:\n"); writePS(embFontList->getCString()); if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) { writePS("%%DocumentProcessColors:"); if (processColors & psProcessCyan) { writePS(" Cyan"); } if (processColors & psProcessMagenta) { writePS(" Magenta"); } if (processColors & psProcessYellow) { writePS(" Yellow"); } if (processColors & psProcessBlack) { writePS(" Black"); } writePS("\n"); writePS("%%DocumentCustomColors:"); for (cc = customColors; cc; cc = cc->next) { writePS(" "); writePSString(cc->name); } writePS("\n"); writePS("%%CMYKCustomColor:\n"); for (cc = customColors; cc; cc = cc->next) { writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", cc->c, cc->m, cc->y, cc->k); writePSString(cc->name); writePS("\n"); } } } } GBool PSOutputDev::checkIfPageNeedsToBeRasterized(int pg) { PreScanOutputDev *scan; GBool rasterize; if (globalParams->getPSAlwaysRasterize()) { rasterize = gTrue; } else { scan = new PreScanOutputDev(); //~ this could depend on the printing flag, e.g., if an annotation //~ uses transparency --> need to pass the printing flag into //~ constructor, init, writeDocSetup doc->getCatalog()->getPage(pg)->display(scan, 72, 72, 0, gTrue, gTrue, gTrue); rasterize = scan->usesTransparency() || scan->usesPatternImageMask(); delete scan; if (rasterize && globalParams->getPSNeverRasterize()) { error(errSyntaxWarning, -1, "PDF page uses transparency and the psNeverRasterize option is " "set - output may not be correct"); rasterize = gFalse; } } return rasterize; } void PSOutputDev::setupResources(Dict *resDict) { Object xObjDict, xObjRef, xObj, patDict, patRef, pat; Object gsDict, gsRef, gs, smask, smaskGroup, resObj; Ref ref0; GBool skip; int i; setupFonts(resDict); setupImages(resDict); //----- recursively scan XObjects resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { // check for an already-visited XObject skip = gFalse; if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) { ref0 = xObjRef.getRef(); if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { skip = gTrue; } else { skip = (GBool)visitedResources[ref0.num]; visitedResources[ref0.num] = 1; } } if (!skip) { // process the XObject's resource dictionary xObjDict.dictGetVal(i, &xObj); if (xObj.isStream()) { xObj.streamGetDict()->lookup("Resources", &resObj); if (resObj.isDict()) { setupResources(resObj.getDict()); } resObj.free(); } xObj.free(); } xObjRef.free(); } } xObjDict.free(); //----- recursively scan Patterns resDict->lookup("Pattern", &patDict); if (patDict.isDict()) { inType3Char = gTrue; for (i = 0; i < patDict.dictGetLength(); ++i) { // check for an already-visited Pattern skip = gFalse; if ((patDict.dictGetValNF(i, &patRef)->isRef())) { ref0 = patRef.getRef(); if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { skip = gTrue; } else { skip = (GBool)visitedResources[ref0.num]; visitedResources[ref0.num] = 1; } } if (!skip) { // process the Pattern's resource dictionary patDict.dictGetVal(i, &pat); if (pat.isStream()) { pat.streamGetDict()->lookup("Resources", &resObj); if (resObj.isDict()) { setupResources(resObj.getDict()); } resObj.free(); } pat.free(); } patRef.free(); } inType3Char = gFalse; } patDict.free(); //----- recursively scan SMask transparency groups in ExtGState dicts resDict->lookup("ExtGState", &gsDict); if (gsDict.isDict()) { for (i = 0; i < gsDict.dictGetLength(); ++i) { // check for an already-visited ExtGState skip = gFalse; if ((gsDict.dictGetValNF(i, &gsRef)->isRef())) { ref0 = gsRef.getRef(); if (ref0.num < 0 || ref0.num >= xref->getNumObjects()) { skip = gTrue; } else { skip = (GBool)visitedResources[ref0.num]; visitedResources[ref0.num] = 1; } } if (!skip) { // process the ExtGState's SMask's transparency group's resource dict if (gsDict.dictGetVal(i, &gs)->isDict()) { if (gs.dictLookup("SMask", &smask)->isDict()) { if (smask.dictLookup("G", &smaskGroup)->isStream()) { smaskGroup.streamGetDict()->lookup("Resources", &resObj); if (resObj.isDict()) { setupResources(resObj.getDict()); } resObj.free(); } smaskGroup.free(); } smask.free(); } gs.free(); } gsRef.free(); } } gsDict.free(); setupForms(resDict); } void PSOutputDev::setupFonts(Dict *resDict) { Object obj1, obj2; Ref r; GfxFontDict *gfxFontDict; GfxFont *font; int i; gfxFontDict = NULL; resDict->lookupNF("Font", &obj1); if (obj1.isRef()) { obj1.fetch(xref, &obj2); if (obj2.isDict()) { r = obj1.getRef(); gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict()); } obj2.free(); } else if (obj1.isDict()) { gfxFontDict = new GfxFontDict(xref, NULL, obj1.getDict()); } if (gfxFontDict) { for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { if ((font = gfxFontDict->getFont(i))) { setupFont(font, resDict); } } delete gfxFontDict; } obj1.free(); } void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { PSFontInfo *fi; GfxFontLoc *fontLoc; GBool subst; char buf[16]; UnicodeMap *uMap; char *charName; double xs, ys; int code; double w1, w2; int i, j; // check if font is already set up for (i = 0; i < fontInfo->getLength(); ++i) { fi = (PSFontInfo *)fontInfo->get(i); if (fi->fontID.num == font->getID()->num && fi->fontID.gen == font->getID()->gen) { return; } } // add fontInfo entry fi = new PSFontInfo(*font->getID()); fontInfo->append(fi); xs = ys = 1; subst = gFalse; if (font->getType() == fontType3) { fi->ff = setupType3Font(font, parentResDict); } else { if ((fontLoc = font->locateFont(xref, gTrue))) { switch (fontLoc->locType) { case gfxFontLocEmbedded: switch (fontLoc->fontType) { case fontType1: fi->ff = setupEmbeddedType1Font(font, &fontLoc->embFontID); break; case fontType1C: fi->ff = setupEmbeddedType1CFont(font, &fontLoc->embFontID); break; case fontType1COT: fi->ff = setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID); break; case fontTrueType: case fontTrueTypeOT: fi->ff = setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID); break; case fontCIDType0C: fi->ff = setupEmbeddedCIDType0Font(font, &fontLoc->embFontID); break; case fontCIDType2: case fontCIDType2OT: //~ should check to see if font actually uses vertical mode fi->ff = setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, gTrue); break; case fontCIDType0COT: fi->ff = setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID); break; default: break; } break; case gfxFontLocExternal: //~ add cases for other external 16-bit fonts switch (fontLoc->fontType) { case fontType1: fi->ff = setupExternalType1Font(font, fontLoc->path); break; case fontTrueType: case fontTrueTypeOT: fi->ff = setupExternalTrueTypeFont(font, fontLoc->path, fontLoc->fontNum); break; case fontCIDType2: case fontCIDType2OT: //~ should check to see if font actually uses vertical mode fi->ff = setupExternalCIDTrueTypeFont(font, fontLoc->path, fontLoc->fontNum, gTrue); break; case fontCIDType0COT: fi->ff = setupExternalOpenTypeCFFFont(font, fontLoc->path); break; default: break; } break; case gfxFontLocResident: if (!(fi->ff = (PSFontFileInfo *)fontFileInfo->lookup(fontLoc->path))) { // handle psFontPassthrough fi->ff = new PSFontFileInfo(fontLoc->path->copy(), fontLoc->fontType, psFontFileResident); fontFileInfo->add(fi->ff->psName, fi->ff); } break; } } if (!fi->ff) { if (font->isCIDFont()) { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}' ('{1:s}' character collection)", font->getName() ? font->getName()->getCString() : "(unnamed)", ((GfxCIDFont *)font)->getCollection() ? ((GfxCIDFont *)font)->getCollection()->getCString() : "(unknown)"); } else { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); } delete fontLoc; return; } // scale substituted 8-bit fonts if (fontLoc->locType == gfxFontLocResident && fontLoc->substIdx >= 0) { subst = gTrue; for (code = 0; code < 256; ++code) { if ((charName = ((Gfx8BitFont *)font)->getCharName(code)) && charName[0] == 'm' && charName[1] == '\0') { break; } } if (code < 256) { w1 = ((Gfx8BitFont *)font)->getWidth((Guchar)code); } else { w1 = 0; } w2 = psBase14SubstFonts[fontLoc->substIdx].mWidth; xs = w1 / w2; if (xs < 0.1) { xs = 1; } } // handle encodings for substituted CID fonts if (fontLoc->locType == gfxFontLocResident && fontLoc->fontType >= fontCIDType0) { subst = gTrue; if ((uMap = globalParams->getUnicodeMap(fontLoc->encoding))) { fi->ff->encoding = fontLoc->encoding->copy(); uMap->decRefCnt(); } else { error(errSyntaxError, -1, "Couldn't find Unicode map for 16-bit font encoding '{0:t}'", fontLoc->encoding); } } delete fontLoc; } // generate PostScript code to set up the font if (font->isCIDFont()) { if (level >= psLevel3) { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", font->getID()->num, font->getID()->gen, fi->ff->psName, font->getWMode()); } else { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", font->getID()->num, font->getID()->gen, fi->ff->psName, font->getWMode()); } } else { writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", font->getID()->num, font->getID()->gen, fi->ff->psName, xs, ys); for (i = 0; i < 256; i += 8) { writePS((char *)((i == 0) ? "[ " : " ")); for (j = 0; j < 8; ++j) { if (font->getType() == fontTrueType && !subst && !((Gfx8BitFont *)font)->getHasEncoding()) { sprintf(buf, "c%02x", i+j); charName = buf; } else { charName = ((Gfx8BitFont *)font)->getCharName(i+j); } writePS("/"); writePSName(charName ? charName : (char *)".notdef"); // the empty name is legal in PDF and PostScript, but PostScript // uses a double-slash (//...) for "immediately evaluated names", // so we need to add a space character here if (charName && !charName[0]) { writePS(" "); } } writePS((i == 256-8) ? (char *)"]\n" : (char *)"\n"); } writePS("pdfMakeFont\n"); } } PSFontFileInfo *PSOutputDev::setupEmbeddedType1Font(GfxFont *font, Ref *id) { GString *psName, *origFont, *cleanFont; PSFontFileInfo *ff; Object refObj, strObj, obj1, obj2; Dict *dict; char buf[4096]; GBool rename; int length1, length2, n; // check if font is already embedded if (!font->getEmbeddedFontName()) { rename = gTrue; } else if ((ff = (PSFontFileInfo *) fontFileInfo->lookup(font->getEmbeddedFontName()))) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { return ff; } rename = gTrue; } else { rename = gFalse; } // generate name // (this assumes that the PS font name matches the PDF font name) if (rename) { psName = makePSFontName(font, id); } else { psName = font->getEmbeddedFontName()->copy(); } // get the font stream and info refObj.initRef(id->num, id->gen); refObj.fetch(xref, &strObj); refObj.free(); if (!strObj.isStream()) { error(errSyntaxError, -1, "Embedded font file object is not a stream"); goto err1; } if (!(dict = strObj.streamGetDict())) { error(errSyntaxError, -1, "Embedded font stream is missing its dictionary"); goto err1; } dict->lookup("Length1", &obj1); dict->lookup("Length2", &obj2); if (!obj1.isInt() || !obj2.isInt()) { error(errSyntaxError, -1, "Missing length fields in embedded font stream dictionary"); obj1.free(); obj2.free(); goto err1; } length1 = obj1.getInt(); length2 = obj2.getInt(); obj1.free(); obj2.free(); // read the font file origFont = new GString(); strObj.streamReset(); while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) { origFont->append(buf, n); } strObj.streamClose(); strObj.free(); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // clean up the font file cleanFont = fixType1Font(origFont, length1, length2); if (rename) { renameType1Font(cleanFont, psName); } writePSBlock(cleanFont->getCString(), cleanFont->getLength()); delete cleanFont; delete origFont; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; err1: strObj.free(); delete psName; return NULL; } PSFontFileInfo *PSOutputDev::setupExternalType1Font(GfxFont *font, GString *fileName) { static char hexChar[17] = "0123456789abcdef"; GString *psName; PSFontFileInfo *ff; FILE *fontFile; int buf[6]; int c, n, i; if (font->getName()) { // check if font is already embedded if ((ff = (PSFontFileInfo *)fontFileInfo->lookup(font->getName()))) { return ff; } // this assumes that the PS font name matches the PDF font name psName = font->getName()->copy(); } else { // generate name //~ this won't work -- the PS font name won't match psName = makePSFontName(font, font->getID()); } // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // open the font file if (!(fontFile = fopen(fileName->getCString(), "rb"))) { error(errIO, -1, "Couldn't open external font file"); return NULL; } // check for PFB format buf[0] = fgetc(fontFile); buf[1] = fgetc(fontFile); if (buf[0] == 0x80 && buf[1] == 0x01) { while (1) { for (i = 2; i < 6; ++i) { buf[i] = fgetc(fontFile); } if (buf[2] == EOF || buf[3] == EOF || buf[4] == EOF || buf[5] == EOF) { break; } n = buf[2] + (buf[3] << 8) + (buf[4] << 16) + (buf[5] << 24); if (buf[1] == 0x01) { for (i = 0; i < n; ++i) { if ((c = fgetc(fontFile)) == EOF) { break; } writePSChar((char)c); } } else { for (i = 0; i < n; ++i) { if ((c = fgetc(fontFile)) == EOF) { break; } writePSChar(hexChar[(c >> 4) & 0x0f]); writePSChar(hexChar[c & 0x0f]); if (i % 32 == 31) { writePSChar('\n'); } } } buf[0] = fgetc(fontFile); buf[1] = fgetc(fontFile); if (buf[0] == EOF || buf[1] == EOF || (buf[0] == 0x80 && buf[1] == 0x03)) { break; } else if (!(buf[0] == 0x80 && (buf[1] == 0x01 || buf[1] == 0x02))) { error(errSyntaxError, -1, "Invalid PFB header in external font file"); break; } } writePSChar('\n'); // plain text (PFA) format } else { writePSChar((char)buf[0]); writePSChar((char)buf[1]); while ((c = fgetc(fontFile)) != EOF) { writePSChar((char)c); } } fclose(fontFile); // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiType1C *ffT1C; GHashIter *iter; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 1 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { ffT1C->convertToType1(psName->getCString(), NULL, gTrue, outputFunc, outputStream); delete ffT1C; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; GHashIter *iter; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 1 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) { if (ffTT->isOpenTypeCFF()) { ffTT->convertToType1(psName->getCString(), NULL, gTrue, outputFunc, outputStream); } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; int *codeToGID; GHashIter *iter; // get the code-to-GID mapping if (!(fontBuf = font->readEmbFontFile(xref, &fontLen))) { return NULL; } if (!(ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { gfree(fontBuf); return NULL; } codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->type == font->getType() && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen && ff->codeToGIDLen == 256 && !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; gfree(fontBuf); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 42 font ffTT->convertToType42(psName->getCString(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : (char **)NULL, codeToGID, outputFunc, outputStream); delete ffTT; gfree(fontBuf); // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; ff->codeToGID = codeToGID; ff->codeToGIDLen = 256; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *fileName, int fontNum) { GString *psName; PSFontFileInfo *ff; FoFiTrueType *ffTT; int *codeToGID; GHashIter *iter; // get the code-to-GID mapping if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) { return NULL; } codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileExternal && ff->type == font->getType() && !ff->extFileName->cmp(fileName) && ff->codeToGIDLen == 256 && !memcmp(ff->codeToGID, codeToGID, 256 * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; return ff; } } // generate name psName = makePSFontName(font, font->getID()); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 42 font ffTT->convertToType42(psName->getCString(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : (char **)NULL, codeToGID, outputFunc, outputStream); delete ffTT; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); ff->codeToGID = codeToGID; ff->codeToGIDLen = 256; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiType1C *ffT1C; GHashIter *iter; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffT1C = FoFiType1C::make(fontBuf, fontLen))) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffT1C->convertToCIDType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffT1C->convertToType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } delete ffT1C; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedCIDTrueTypeFont( GfxFont *font, Ref *id, GBool needVerticalMetrics) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; int *codeToGID; int codeToGIDLen; GHashIter *iter; // get the code-to-GID mapping codeToGID = ((GfxCIDFont *)font)->getCIDToGID(); codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen(); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->type == font->getType() && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen && ff->codeToGIDLen == codeToGIDLen && ((!ff->codeToGID && !codeToGID) || (ff->codeToGID && codeToGID && !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))))) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0))) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType2(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; if (codeToGIDLen) { ff->codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); memcpy(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int)); ff->codeToGIDLen = codeToGIDLen; } fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupExternalCIDTrueTypeFont( GfxFont *font, GString *fileName, int fontNum, GBool needVerticalMetrics) { GString *psName; PSFontFileInfo *ff; FoFiTrueType *ffTT; int *codeToGID; int codeToGIDLen; CharCodeToUnicode *ctu; Unicode uBuf[8]; int cmap, cmapPlatform, cmapEncoding, code; GHashIter *iter; // create a code-to-GID mapping, via Unicode if (!(ffTT = FoFiTrueType::load(fileName->getCString(), fontNum))) { return NULL; } if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) { error(errSyntaxError, -1, "Couldn't find a mapping to Unicode for font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); delete ffTT; return NULL; } // look for a Unicode cmap for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { cmapPlatform = ffTT->getCmapPlatform(cmap); cmapEncoding = ffTT->getCmapEncoding(cmap); if ((cmapPlatform == 3 && cmapEncoding == 1) || (cmapPlatform == 0 && cmapEncoding <= 4)) { break; } } if (cmap >= ffTT->getNumCmaps()) { error(errSyntaxError, -1, "Couldn't find a Unicode cmap in font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); ctu->decRefCnt(); delete ffTT; return NULL; } // map CID -> Unicode -> GID if (ctu->isIdentity()) { codeToGIDLen = 65536; } else { codeToGIDLen = ctu->getLength(); } codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); for (code = 0; code < codeToGIDLen; ++code) { if (ctu->mapToUnicode(code, uBuf, 8) > 0) { codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); } else { codeToGID[code] = 0; } } ctu->decRefCnt(); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileExternal && ff->type == font->getType() && !ff->extFileName->cmp(fileName) && ff->codeToGIDLen == codeToGIDLen && ff->codeToGID && !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; return ff; } } // check for embedding permission if (ffTT->getEmbeddingRights() < 1) { error(errSyntaxError, -1, "TrueType font '{0:s}' does not allow embedding", font->getName() ? font->getName()->getCString() : "(unnamed)"); gfree(codeToGID); delete ffTT; return NULL; } // generate name psName = makePSFontName(font, font->getID()); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font //~ this should use fontNum to load the correct font if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType2(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); } delete ffTT; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); ff->codeToGID = codeToGID; ff->codeToGIDLen = codeToGIDLen; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id) { GString *psName; PSFontFileInfo *ff; char *fontBuf; int fontLen; FoFiTrueType *ffTT; GHashIter *iter; int n; // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileEmbedded && ff->embFontID.num == id->num && ff->embFontID.gen == id->gen) { fontFileInfo->killIter(&iter); return ff; } } // generate name psName = makePSFontName(font, id); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font if ((fontBuf = font->readEmbFontFile(xref, &fontLen))) { if ((ffTT = FoFiTrueType::make(fontBuf, fontLen, 0, gTrue))) { if (ffTT->isOpenTypeCFF()) { if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); } } delete ffTT; } gfree(fontBuf); } // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); ff->embFontID = *id; if ((n = ((GfxCIDFont *)font)->getCIDToGIDLen())) { ff->codeToGID = (int *)gmallocn(n, sizeof(int)); memcpy(ff->codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), n * sizeof(int)); ff->codeToGIDLen = n; } fontFileInfo->add(ff->psName, ff); return ff; } // This assumes an OpenType CFF font that has a Unicode cmap (in the // OpenType section), and a CFF blob that uses an identity CID-to-GID // mapping. PSFontFileInfo *PSOutputDev::setupExternalOpenTypeCFFFont(GfxFont *font, GString *fileName) { GString *psName; PSFontFileInfo *ff; FoFiTrueType *ffTT; GHashIter *iter; CharCodeToUnicode *ctu; Unicode uBuf[8]; int *codeToGID; int codeToGIDLen; int cmap, cmapPlatform, cmapEncoding, code; // create a code-to-GID mapping, via Unicode if (!(ffTT = FoFiTrueType::load(fileName->getCString(), 0, gTrue))) { return NULL; } if (!ffTT->isOpenTypeCFF()) { delete ffTT; return NULL; } if (!(ctu = ((GfxCIDFont *)font)->getToUnicode())) { error(errSyntaxError, -1, "Couldn't find a mapping to Unicode for font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); delete ffTT; return NULL; } // look for a Unicode cmap for (cmap = 0; cmap < ffTT->getNumCmaps(); ++cmap) { cmapPlatform = ffTT->getCmapPlatform(cmap); cmapEncoding = ffTT->getCmapEncoding(cmap); if ((cmapPlatform == 3 && cmapEncoding == 1) || (cmapPlatform == 0 && cmapEncoding <= 4)) { break; } } if (cmap >= ffTT->getNumCmaps()) { error(errSyntaxError, -1, "Couldn't find a Unicode cmap in font '{0:s}'", font->getName() ? font->getName()->getCString() : "(unnamed)"); ctu->decRefCnt(); delete ffTT; return NULL; } // map CID -> Unicode -> GID if (ctu->isIdentity()) { codeToGIDLen = 65536; } else { codeToGIDLen = ctu->getLength(); } codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); for (code = 0; code < codeToGIDLen; ++code) { if (ctu->mapToUnicode(code, uBuf, 8) > 0) { codeToGID[code] = ffTT->mapCodeToGID(cmap, uBuf[0]); } else { codeToGID[code] = 0; } } ctu->decRefCnt(); // check if font is already embedded fontFileInfo->startIter(&iter); while (fontFileInfo->getNext(&iter, &psName, (void **)&ff)) { if (ff->loc == psFontFileExternal && ff->type == font->getType() && !ff->extFileName->cmp(fileName) && ff->codeToGIDLen == codeToGIDLen && ff->codeToGID && !memcmp(ff->codeToGID, codeToGID, codeToGIDLen * sizeof(int))) { fontFileInfo->killIter(&iter); gfree(codeToGID); delete ffTT; return ff; } } // generate name psName = makePSFontName(font, font->getID()); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // convert it to a Type 0 font if (globalParams->getPSLevel() >= psLevel3) { // Level 3: use a CID font ffTT->convertToCIDType0(psName->getCString(), codeToGID, codeToGIDLen, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font ffTT->convertToType0(psName->getCString(), codeToGID, codeToGIDLen, outputFunc, outputStream); } delete ffTT; // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileExternal); ff->extFileName = fileName->copy(); ff->codeToGID = codeToGID; ff->codeToGIDLen = codeToGIDLen; fontFileInfo->add(ff->psName, ff); return ff; } PSFontFileInfo *PSOutputDev::setupType3Font(GfxFont *font, Dict *parentResDict) { PSFontFileInfo *ff; GString *psName; Dict *resDict; Dict *charProcs; Object charProc; Gfx *gfx; PDFRectangle box; double *m; GString *buf; int i; // generate name psName = GString::format("T3_{0:d}_{1:d}", font->getID()->num, font->getID()->gen); // set up resources used by font if ((resDict = ((Gfx8BitFont *)font)->getResources())) { inType3Char = gTrue; setupResources(resDict); inType3Char = gFalse; } else { resDict = parentResDict; } // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->getCString()); embFontList->append("\n"); // font dictionary writePS("8 dict begin\n"); writePS("/FontType 3 def\n"); m = font->getFontMatrix(); writePSFmt("/FontMatrix [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", m[0], m[1], m[2], m[3], m[4], m[5]); m = font->getFontBBox(); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", m[0], m[1], m[2], m[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); if ((charProcs = ((Gfx8BitFont *)font)->getCharProcs())) { writePSFmt("/CharProcs {0:d} dict def\n", charProcs->getLength()); writePS("CharProcs begin\n"); box.x1 = m[0]; box.y1 = m[1]; box.x2 = m[2]; box.y2 = m[3]; gfx = new Gfx(doc, this, resDict, &box, NULL); inType3Char = gTrue; for (i = 0; i < charProcs->getLength(); ++i) { t3FillColorOnly = gFalse; t3Cacheable = gFalse; t3NeedsRestore = gFalse; writePS("/"); writePSName(charProcs->getKey(i)); writePS(" {\n"); gfx->display(charProcs->getValNF(i, &charProc)); charProc.free(); if (t3String) { if (t3Cacheable) { buf = GString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); } else { buf = GString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY); } (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); delete buf; (*outputFunc)(outputStream, t3String->getCString(), t3String->getLength()); delete t3String; t3String = NULL; } if (t3NeedsRestore) { (*outputFunc)(outputStream, "Q\n", 2); } writePS("} def\n"); } inType3Char = gFalse; delete gfx; writePS("end\n"); } writePS("currentdict end\n"); writePSFmt("/{0:t} exch definefont pop\n", psName); // ending comment writePS("%%EndResource\n"); ff = new PSFontFileInfo(psName, font->getType(), psFontFileEmbedded); fontFileInfo->add(ff->psName, ff); return ff; } // Make a unique PS font name, based on the names given in the PDF // font object, and an object ID (font file object for GString *PSOutputDev::makePSFontName(GfxFont *font, Ref *id) { GString *psName, *s; if ((s = font->getEmbeddedFontName())) { psName = filterPSName(s); if (!fontFileInfo->lookup(psName)) { return psName; } delete psName; } if ((s = font->getName())) { psName = filterPSName(s); if (!fontFileInfo->lookup(psName)) { return psName; } delete psName; } psName = GString::format("FF{0:d}_{1:d}", id->num, id->gen); if ((s = font->getEmbeddedFontName())) { s = filterPSName(s); psName->append('_')->append(s); delete s; } else if ((s = font->getName())) { s = filterPSName(s); psName->append('_')->append(s); delete s; } return psName; } GString *PSOutputDev::fixType1Font(GString *font, int length1, int length2) { Guchar *fontData; GString *out, *binSection; GBool pfb; int fontSize, i; fontData = (Guchar *)font->getCString(); fontSize = font->getLength(); // check for PFB pfb = fontSize >= 6 && fontData[0] == 0x80 && fontData[1] == 0x01; out = new GString(); binSection = new GString(); if (pfb) { if (!splitType1PFB(fontData, fontSize, out, binSection)) { delete out; delete binSection; return copyType1PFB(fontData, fontSize); } } else { if (!splitType1PFA(fontData, fontSize, length1, length2, out, binSection)) { delete out; delete binSection; return copyType1PFA(fontData, fontSize); } } out->append('\n'); binSection = asciiHexDecodeType1EexecSection(binSection); if (!fixType1EexecSection(binSection, out)) { delete out; delete binSection; return pfb ? copyType1PFB(fontData, fontSize) : copyType1PFA(fontData, fontSize); } delete binSection; for (i = 0; i < 8; ++i) { out->append("0000000000000000000000000000000000000000000000000000000000000000\n"); } out->append("cleartomark\n"); return out; } // Split a Type 1 font in PFA format into a text section and a binary // section. GBool PSOutputDev::splitType1PFA(Guchar *font, int fontSize, int length1, int length2, GString *textSection, GString *binSection) { int textLength, binStart, binLength, lastSpace, i; //--- extract the text section // Length1 is correct, and the text section ends with whitespace if (length1 <= fontSize && length1 >= 18 && !memcmp(font + length1 - 18, "currentfile eexec", 17)) { textLength = length1 - 1; // Length1 is correct, but the trailing whitespace is missing } else if (length1 <= fontSize && length1 >= 17 && !memcmp(font + length1 - 17, "currentfile eexec", 17)) { textLength = length1; // Length1 is incorrect } else { for (textLength = 17; textLength <= fontSize; ++textLength) { if (!memcmp(font + textLength - 17, "currentfile eexec", 17)) { break; } } if (textLength > fontSize) { return gFalse; } } textSection->append((char *)font, textLength); //--- skip whitespace between the text section and the binary section for (i = 0, binStart = textLength; i < 8 && binStart < fontSize; ++i, ++binStart) { if (font[binStart] != ' ' && font[binStart] != '\t' && font[binStart] != '\n' && font[binStart] != '\r') { break; } } if (i == 8) { return gFalse; } //--- extract binary section // if we see "0000", assume Length2 is correct // (if Length2 is too long, it will be corrected by fixType1EexecSection) if (length2 > 0 && length2 < INT_MAX - 4 && binStart <= fontSize - length2 - 4 && !memcmp(font + binStart + length2, "0000", 4)) { binLength = length2; } else { // look for "0000" near the end of the font (note that there can // be intervening "\n", "\r\n", etc.), then search backward if (fontSize - binStart < 512) { return gFalse; } if (!memcmp(font + fontSize - 256, "0000", 4) || !memcmp(font + fontSize - 255, "0000", 4) || !memcmp(font + fontSize - 254, "0000", 4) || !memcmp(font + fontSize - 253, "0000", 4) || !memcmp(font + fontSize - 252, "0000", 4) || !memcmp(font + fontSize - 251, "0000", 4)) { i = fontSize - 252; lastSpace = -1; while (i >= binStart) { if (font[i] == ' ' || font[i] == '\t' || font[i] == '\n' || font[i] == '\r') { lastSpace = i; --i; } else if (font[i] == '0') { --i; } else { break; } } if (lastSpace < 0) { return gFalse; } // check for the case where the newline/space is missing between // the binary section and the first set of 64 '0' chars if (lastSpace - binStart > 64 && !memcmp(font + lastSpace - 64, "0000000000000000000000000000000000000000000000000000000000000000", 64)) { binLength = lastSpace - 64 - binStart; } else { binLength = lastSpace - binStart; } // couldn't find zeros after binary section -- assume they're // missing and the binary section extends to the end of the file } else { binLength = fontSize - binStart; } } binSection->append((char *)(font + binStart), binLength); return gTrue; } // Split a Type 1 font in PFB format into a text section and a binary // section. GBool PSOutputDev::splitType1PFB(Guchar *font, int fontSize, GString *textSection, GString *binSection) { Guchar *p; int state, remain, len, n; // states: // 0: text section // 1: binary section // 2: trailer section // 3: eof state = 0; p = font; remain = fontSize; while (remain >= 2) { if (p[0] != 0x80) { return gFalse; } switch (state) { case 0: if (p[1] == 0x02) { state = 1; } else if (p[1] != 0x01) { return gFalse; } break; case 1: if (p[1] == 0x01) { state = 2; } else if (p[1] != 0x02) { return gFalse; } break; case 2: if (p[1] == 0x03) { state = 3; } else if (p[1] != 0x01) { return gFalse; } break; default: // shouldn't happen return gFalse; } if (state == 3) { break; } if (remain < 6) { break; } len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24); if (len < 0 || len > remain - 6) { return gFalse; } switch (state) { case 0: textSection->append((char *)(p + 6), len); break; case 1: binSection->append((char *)(p + 6), len); break; case 2: // we don't use the trailer break; default: // shouldn't happen return gFalse; } p += len + 6; remain -= len + 6; } if (state != 3) { return gFalse; } n = textSection->getLength(); if (n >= 18 && !memcmp(textSection->getCString() + n - 18, "currentfile eexec", 17)) { // remove the trailing whitespace textSection->del(n - 1, 1); } else if (n >= 17 && !memcmp(textSection->getCString() + n - 17, "currentfile eexec", 17)) { // missing whitespace at end -- leave as-is } else { return gFalse; } return gTrue; } // If is ASCIIHex-encoded, decode it, delete , and return the // binary version. Else return unchanged. GString *PSOutputDev::asciiHexDecodeType1EexecSection(GString *in) { GString *out; char c; Guchar byte; int state, i; out = new GString(); state = 0; byte = 0; for (i = 0; i < in->getLength(); ++i) { c = in->getChar(i); if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { continue; } if (c >= '0' && c <= '9') { byte = (Guchar)(byte + (c - '0')); } else if (c >= 'A' && c <= 'F') { byte = (Guchar)(byte + (c - 'A' + 10)); } else if (c >= 'a' && c <= 'f') { byte = (Guchar)(byte + (c - 'a' + 10)); } else { delete out; return in; } if (state == 0) { byte = (Guchar)(byte << 4); state = 1; } else { out->append((char)byte); state = 0; byte = 0; } } delete in; return out; } GBool PSOutputDev::fixType1EexecSection(GString *binSection, GString *out) { static char hexChars[17] = "0123456789abcdef"; Guchar buf[16], buf2[16]; Guchar byte; int r, i, j; // eexec-decode the binary section, keeping the last 16 bytes r = 55665; for (i = 0; i < binSection->getLength(); ++i) { byte = (Guchar)binSection->getChar(i); buf[i & 15] = byte ^ (Guchar)(r >> 8); r = ((r + byte) * 52845 + 22719) & 0xffff; } for (j = 0; j < 16; ++j) { buf2[j] = buf[(i + j) & 15]; } // look for 'closefile' for (i = 0; i <= 16 - 9; ++i) { if (!memcmp(buf2 + i, "closefile", 9)) { break; } } if (i > 16 - 9) { return gFalse; } // three cases: // - short: missing space after "closefile" (i == 16 - 9) // - correct: exactly one space after "closefile" (i == 16 - 10) // - long: extra chars after "closefile" (i < 16 - 10) if (i == 16 - 9) { binSection->append((char)((Guchar)'\n' ^ (Guchar)(r >> 8))); } else if (i < 16 - 10) { binSection->del(binSection->getLength() - (16 - 10 - i), 16 - 10 - i); } // ASCIIHex encode for (i = 0; i < binSection->getLength(); i += 32) { for (j = 0; j < 32 && i+j < binSection->getLength(); ++j) { byte = (Guchar)binSection->getChar(i+j); out->append(hexChars[(byte >> 4) & 0x0f]); out->append(hexChars[byte & 0x0f]); } out->append('\n'); } return gTrue; } // The Type 1 cleanup code failed -- assume it's a valid PFA-format // font and copy it to the output. GString *PSOutputDev::copyType1PFA(Guchar *font, int fontSize) { GString *out; error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 font"); out = new GString((char *)font, fontSize); // append a newline to avoid problems where the original font // doesn't end with one out->append('\n'); return out; } // The Type 1 cleanup code failed -- assume it's a valid PFB-format // font, decode the PFB blocks, and copy them to the output. GString *PSOutputDev::copyType1PFB(Guchar *font, int fontSize) { static char hexChars[17] = "0123456789abcdef"; GString *out; Guchar *p; int remain, len, i, j; error(errSyntaxWarning, -1, "Couldn't parse embedded Type 1 (PFB) font"); out = new GString(); p = font; remain = fontSize; while (remain >= 6 && p[0] == 0x80 && (p[1] == 0x01 || p[1] == 0x02)) { len = p[2] + (p[3] << 8) + (p[4] << 16) + (p[5] << 24); if (len > remain - 6) { break; } if (p[1] == 0x01) { out->append((char *)(p + 6), len); } else { for (i = 0; i < len; i += 32) { for (j = 0; j < 32 && i+j < len; ++j) { out->append(hexChars[(p[6+i+j] >> 4) & 0x0f]); out->append(hexChars[p[6+i+j] & 0x0f]); } out->append('\n'); } } p += len + 6; remain -= len + 6; } // append a newline to avoid problems where the original font // doesn't end with one out->append('\n'); return out; } void PSOutputDev::renameType1Font(GString *font, GString *name) { char *p1, *p2; int i; if (!(p1 = strstr(font->getCString(), "\n/FontName")) && !(p1 = strstr(font->getCString(), "\r/FontName"))) { return; } p1 += 10; while (*p1 == ' ' || *p1 == '\t' || *p1 == '\n' || *p1 == '\r') { ++p1; } if (*p1 != '/') { return; } ++p1; p2 = p1; while (*p2 && *p2 != ' ' && *p2 != '\t' && *p2 != '\n' && *p2 != '\r') { ++p2; } if (!*p2) { return; } i = (int)(p1 - font->getCString()); font->del(i, (int)(p2 - p1)); font->insert(i, name); } void PSOutputDev::setupDefaultFont() { writePS("/xpdf_default_font /Helvetica 1 1 ISOLatin1Encoding pdfMakeFont\n"); } void PSOutputDev::setupImages(Dict *resDict) { Object xObjDict, xObj, xObjRef, subtypeObj, maskObj, maskRef; Ref imgID; int i, j; if (!(mode == psModeForm || inType3Char || preload)) { return; } resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { xObjDict.dictGetValNF(i, &xObjRef); xObjDict.dictGetVal(i, &xObj); if (xObj.isStream()) { xObj.streamGetDict()->lookup("Subtype", &subtypeObj); if (subtypeObj.isName("Image")) { if (xObjRef.isRef()) { imgID = xObjRef.getRef(); for (j = 0; j < imgIDLen; ++j) { if (imgIDs[j].num == imgID.num && imgIDs[j].gen == imgID.gen) { break; } } if (j == imgIDLen) { if (imgIDLen >= imgIDSize) { if (imgIDSize == 0) { imgIDSize = 64; } else { imgIDSize *= 2; } imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref)); } imgIDs[imgIDLen++] = imgID; setupImage(imgID, xObj.getStream(), gFalse, NULL); if (level >= psLevel3) { xObj.streamGetDict()->lookup("Mask", &maskObj); if (maskObj.isStream()) { setupImage(imgID, maskObj.getStream(), gTrue, NULL); } else if (level == psLevel3Gray && maskObj.isArray()) { setupImage(imgID, xObj.getStream(), gFalse, maskObj.getArray()); } maskObj.free(); } } } else { error(errSyntaxError, -1, "Image in resource dict is not an indirect reference"); } } subtypeObj.free(); } xObj.free(); xObjRef.free(); } } xObjDict.free(); } void PSOutputDev::setupImage(Ref id, Stream *str, GBool mask, Array *colorKeyMask) { StreamColorSpaceMode csMode; GfxColorSpace *colorSpace; GfxImageColorMap *colorMap; int maskColors[2*gfxColorMaxComps]; Object obj1; GBool imageMask, useLZW, useRLE, useCompressed, useASCIIHex; GString *s; int c, width, height, bits, size, line, col, i; // check for mask str->getDict()->lookup("ImageMask", &obj1); if (obj1.isBool()) { imageMask = obj1.getBool(); } else { imageMask = gFalse; } obj1.free(); // get image size str->getDict()->lookup("Width", &obj1); if (!obj1.isInt() || obj1.getInt() <= 0) { error(errSyntaxError, -1, "Invalid Width in image"); obj1.free(); return; } width = obj1.getInt(); obj1.free(); str->getDict()->lookup("Height", &obj1); if (!obj1.isInt() || obj1.getInt() <= 0) { error(errSyntaxError, -1, "Invalid Height in image"); obj1.free(); return; } height = obj1.getInt(); obj1.free(); // build the color map if (mask || imageMask) { colorMap = NULL; } else { bits = 0; csMode = streamCSNone; str->getImageParams(&bits, &csMode); if (bits == 0) { str->getDict()->lookup("BitsPerComponent", &obj1); if (!obj1.isInt()) { error(errSyntaxError, -1, "Invalid BitsPerComponent in image"); obj1.free(); return; } bits = obj1.getInt(); obj1.free(); } str->getDict()->lookup("ColorSpace", &obj1); if (!obj1.isNull()) { colorSpace = GfxColorSpace::parse(&obj1 ); } else if (csMode == streamCSDeviceGray) { colorSpace = GfxColorSpace::create(csDeviceGray); } else if (csMode == streamCSDeviceRGB) { colorSpace = GfxColorSpace::create(csDeviceRGB); } else if (csMode == streamCSDeviceCMYK) { colorSpace = GfxColorSpace::create(csDeviceCMYK); } else { colorSpace = NULL; } obj1.free(); if (!colorSpace) { error(errSyntaxError, -1, "Invalid ColorSpace in image"); return; } str->getDict()->lookup("Decode", &obj1); colorMap = new GfxImageColorMap(bits, &obj1, colorSpace); obj1.free(); } // filters if (level < psLevel2) { useLZW = useRLE = gFalse; useCompressed = gFalse; useASCIIHex = gTrue; } else { if (colorKeyMask) { if (globalParams->getPSUncompressPreloadedImages()) { useLZW = useRLE = gFalse; } else if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useCompressed = gFalse; } else if (colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || level == psLevel2Gray || level == psLevel3Gray)) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useCompressed = gFalse; } else if (globalParams->getPSUncompressPreloadedImages()) { useLZW = useRLE = gFalse; useCompressed = gFalse; } else { s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); if (s) { useLZW = useRLE = gFalse; useCompressed = gTrue; delete s; } else { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useCompressed = gFalse; } } useASCIIHex = globalParams->getPSASCIIHex(); } if (useCompressed) { str = str->getUndecodedStream(); } if (colorKeyMask) { memset(maskColors, 0, sizeof(maskColors)); for (i = 0; i < colorKeyMask->getLength() && i < 2*gfxColorMaxComps; ++i) { colorKeyMask->get(i, &obj1); if (obj1.isInt()) { maskColors[i] = obj1.getInt(); } obj1.free(); } str = new ColorKeyToMaskEncoder(str, width, height, colorMap, maskColors); } else if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { str = new GrayRecoder(str, width, height, colorMap); } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { str = new DeviceNRecoder(str, width, height, colorMap); } if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } // compute image data size str->reset(); col = size = 0; do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { ++col; } else { ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } ++col; } } if (col > 225) { ++size; col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); // add one entry for the final line of data; add another entry // because the LZWDecode/RunLengthDecode filter may read past the end ++size; if (useLZW || useRLE) { ++size; } writePSFmt("{0:d} array dup /{1:s}Data_{2:d}_{3:d} exch def\n", size, (mask || colorKeyMask) ? "Mask" : "Im", id.num, id.gen); str->close(); // write the data into the array str->reset(); line = col = 0; writePS((char *)(useASCIIHex ? "dup 0 <" : "dup 0 <~")); do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar((char)c); ++col; } else { writePSChar((char)c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar((char)c); ++col; } } // each line is: "dup nnnnn <~...data...~> put" // so max data length = 255 - 20 = 235 // chunks are 1 or 4 bytes each, so we have to stop at 232 // but make it 225 just to be safe if (col > 225) { writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); ++line; writePSFmt((char *)(useASCIIHex ? "dup {0:d} <" : "dup {0:d} <~"), line); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? "> put\n" : "~> put\n")); if (useLZW || useRLE) { ++line; writePSFmt("{0:d} <> put\n", line); } else { writePS("pop\n"); } str->close(); delete str; if (colorMap) { delete colorMap; } } void PSOutputDev::setupForms(Dict *resDict) { Object xObjDict, xObj, xObjRef, subtypeObj; int i; if (!preload) { return; } resDict->lookup("XObject", &xObjDict); if (xObjDict.isDict()) { for (i = 0; i < xObjDict.dictGetLength(); ++i) { xObjDict.dictGetValNF(i, &xObjRef); xObjDict.dictGetVal(i, &xObj); if (xObj.isStream()) { xObj.streamGetDict()->lookup("Subtype", &subtypeObj); if (subtypeObj.isName("Form")) { if (xObjRef.isRef()) { setupForm(&xObjRef, &xObj); } else { error(errSyntaxError, -1, "Form in resource dict is not an indirect reference"); } } subtypeObj.free(); } xObj.free(); xObjRef.free(); } } xObjDict.free(); } void PSOutputDev::setupForm(Object *strRef, Object *strObj) { Dict *dict, *resDict; Object matrixObj, bboxObj, resObj, obj1; double m[6], bbox[4]; PDFRectangle box; Gfx *gfx; int i; // check if form is already defined for (i = 0; i < formIDLen; ++i) { if (formIDs[i].num == strRef->getRefNum() && formIDs[i].gen == strRef->getRefGen()) { return; } } // add entry to formIDs list if (formIDLen >= formIDSize) { if (formIDSize == 0) { formIDSize = 64; } else { formIDSize *= 2; } formIDs = (Ref *)greallocn(formIDs, formIDSize, sizeof(Ref)); } formIDs[formIDLen++] = strRef->getRef(); dict = strObj->streamGetDict(); // get bounding box dict->lookup("BBox", &bboxObj); if (!bboxObj.isArray()) { bboxObj.free(); error(errSyntaxError, -1, "Bad form bounding box"); return; } for (i = 0; i < 4; ++i) { bboxObj.arrayGet(i, &obj1); bbox[i] = obj1.getNum(); obj1.free(); } bboxObj.free(); // get matrix dict->lookup("Matrix", &matrixObj); if (matrixObj.isArray()) { for (i = 0; i < 6; ++i) { matrixObj.arrayGet(i, &obj1); m[i] = obj1.getNum(); obj1.free(); } } else { m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 1; m[4] = 0; m[5] = 0; } matrixObj.free(); // get resources dict->lookup("Resources", &resObj); resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL; writePSFmt("/f_{0:d}_{1:d} {{\n", strRef->getRefNum(), strRef->getRefGen()); writePS("q\n"); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", m[0], m[1], m[2], m[3], m[4], m[5]); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, &box); gfx->display(strRef); delete gfx; writePS("Q\n"); writePS("} def\n"); resObj.free(); } GBool PSOutputDev::checkPageSlice(Page *page, double hDPI, double vDPI, int rotateA, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, GBool printing, GBool (*abortCheckCbk)(void *data), void *abortCheckCbkData) { int pg; #if HAVE_SPLASH GBool mono; GBool useLZW; double dpi; SplashOutputDev *splashOut; SplashColor paperColor; PDFRectangle box; GfxState *state; SplashBitmap *bitmap; Stream *str0, *str; Object obj; Guchar *p; Guchar col[4]; char buf[4096]; double userUnit, hDPI2, vDPI2; double m0, m1, m2, m3, m4, m5; int nStripes, stripeH, stripeY; int w, h, x, y, comp, i, n; #endif pg = page->getNum(); if (!(pg >= firstPage && pg <= lastPage && rasterizePage[pg - firstPage])) { return gTrue; } #if HAVE_SPLASH // get the rasterization parameters dpi = globalParams->getPSRasterResolution(); mono = globalParams->getPSRasterMono() || level == psLevel1 || level == psLevel2Gray || level == psLevel3Gray; useLZW = globalParams->getPSLZW(); // get the UserUnit if (honorUserUnit) { userUnit = page->getUserUnit(); } else { userUnit = 1; } // start the PS page page->makeBox(userUnit * dpi, userUnit * dpi, rotateA, useMediaBox, gFalse, sliceX, sliceY, sliceW, sliceH, &box, &crop); rotateA += page->getRotate(); if (rotateA >= 360) { rotateA -= 360; } else if (rotateA < 0) { rotateA += 360; } state = new GfxState(dpi, dpi, &box, rotateA, gFalse); startPage(page->getNum(), state); delete state; // set up the SplashOutputDev if (mono) { paperColor[0] = 0xff; splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); #if SPLASH_CMYK } else if (level == psLevel1Sep) { paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0; splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); #endif } else { paperColor[0] = paperColor[1] = paperColor[2] = 0xff; splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse, paperColor, gFalse, globalParams->getAntialiasPrinting()); } splashOut->startDoc(xref); // break the page into stripes // NB: startPage() has already multiplied xScale and yScale by UserUnit hDPI2 = xScale * dpi; vDPI2 = yScale * dpi; if (sliceW < 0 || sliceH < 0) { if (useMediaBox) { box = *page->getMediaBox(); } else { box = *page->getCropBox(); } sliceX = sliceY = 0; sliceW = (int)((box.x2 - box.x1) * hDPI2 / 72.0); sliceH = (int)((box.y2 - box.y1) * vDPI2 / 72.0); } nStripes = (int)ceil(((double)sliceW * (double)sliceH) / (double)globalParams->getPSRasterSliceSize()); stripeH = (sliceH + nStripes - 1) / nStripes; // render the stripes for (stripeY = sliceY; stripeY < sliceH; stripeY += stripeH) { // rasterize a stripe page->makeBox(hDPI2, vDPI2, 0, useMediaBox, gFalse, sliceX, stripeY, sliceW, stripeH, &box, &crop); m0 = box.x2 - box.x1; m1 = 0; m2 = 0; m3 = box.y2 - box.y1; m4 = box.x1; m5 = box.y1; page->displaySlice(splashOut, hDPI2, vDPI2, (360 - page->getRotate()) % 360, useMediaBox, crop, sliceX, stripeY, sliceW, stripeH, printing, abortCheckCbk, abortCheckCbkData); // draw the rasterized image bitmap = splashOut->getBitmap(); w = bitmap->getWidth(); h = bitmap->getHeight(); writePS("gsave\n"); writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", m0, m1, m2, m3, m4, m5); switch (level) { case psLevel1: writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", w, h, w, -h, h); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); i = 0; for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { writePSFmt("{0:02x}", *p++); if (++i == 32) { writePSChar('\n'); i = 0; } } } if (i != 0) { writePSChar('\n'); } break; case psLevel1Sep: writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", w, h, w, -h, h); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); i = 0; col[0] = col[1] = col[2] = col[3] = 0; for (y = 0; y < h; ++y) { for (comp = 0; comp < 4; ++comp) { for (x = 0; x < w; ++x) { writePSFmt("{0:02x}", p[4*x + comp]); col[comp] |= p[4*x + comp]; if (++i == 32) { writePSChar('\n'); i = 0; } } } p -= bitmap->getRowSize(); } if (i != 0) { writePSChar('\n'); } if (col[0]) { processColors |= psProcessCyan; } if (col[1]) { processColors |= psProcessMagenta; } if (col[2]) { processColors |= psProcessYellow; } if (col[3]) { processColors |= psProcessBlack; } break; case psLevel2: case psLevel2Gray: case psLevel2Sep: case psLevel3: case psLevel3Gray: case psLevel3Sep: if (mono) { writePS("/DeviceGray setcolorspace\n"); } else { writePS("/DeviceRGB setcolorspace\n"); } writePS("<<\n /ImageType 1\n"); writePSFmt(" /Width {0:d}\n", bitmap->getWidth()); writePSFmt(" /Height {0:d}\n", bitmap->getHeight()); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", w, -h, h); writePS(" /BitsPerComponent 8\n"); if (mono) { writePS(" /Decode [0 1]\n"); } else { writePS(" /Decode [0 1 0 1 0 1]\n"); } writePS(" /DataSource currentfile\n"); if (globalParams->getPSASCIIHex()) { writePS(" /ASCIIHexDecode filter\n"); } else { writePS(" /ASCII85Decode filter\n"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else { writePS(" /RunLengthDecode filter\n"); } writePS(">>\n"); writePS("image\n"); obj.initNull(); p = bitmap->getDataPtr() + (h - 1) * bitmap->getRowSize(); str0 = new MemStream((char *)p, 0, w * h * (mono ? 1 : 3), &obj); if (useLZW) { str = new LZWEncoder(str0); } else { str = new RunLengthEncoder(str0); } if (globalParams->getPSASCIIHex()) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); delete str; delete str0; writePSChar('\n'); processColors |= mono ? psProcessBlack : psProcessCMYK; break; } writePS("grestore\n"); } delete splashOut; // finish the PS page endPage(); return gFalse; #else // HAVE_SPLASH error(errSyntaxWarning, -1, "PDF page uses transparency and PSOutputDev was built without" " the Splash rasterizer - output may not be correct"); return gTrue; #endif // HAVE_SPLASH } void PSOutputDev::startPage(int pageNum, GfxState *state) { Page *page; double userUnit; int x1, y1, x2, y2, width, height, t; int imgWidth, imgHeight, imgWidth2, imgHeight2; GBool landscape; GString *s; page = doc->getCatalog()->getPage(pageNum); if (honorUserUnit) { userUnit = page->getUserUnit(); } else { userUnit = 1; } if (mode == psModePS) { writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage); if (paperMatch) { imgLLX = imgLLY = 0; if (globalParams->getPSUseCropBoxAsPage()) { imgURX = (int)ceil(page->getCropWidth() * userUnit); imgURY = (int)ceil(page->getCropHeight() * userUnit); } else { imgURX = (int)ceil(page->getMediaWidth() * userUnit); imgURY = (int)ceil(page->getMediaHeight() * userUnit); } if (state->getRotate() == 90 || state->getRotate() == 270) { t = imgURX; imgURX = imgURY; imgURY = t; } writePSFmt("%%PageMedia: {0:d}x{1:d}\n", imgURX, imgURY); writePSFmt("%%PageBoundingBox: 0 0 {0:d} {1:d}\n", imgURX, imgURY); } writePS("%%BeginPageSetup\n"); } if (mode != psModeForm) { writePS("xpdf begin\n"); } // set up paper size for paper=match mode // NB: this must be done *before* the saveState() for overlays. if (mode == psModePS && paperMatch) { writePSFmt("{0:d} {1:d} pdfSetupPaper\n", imgURX, imgURY); } // underlays if (underlayCbk) { (*underlayCbk)(this, underlayCbkData); } if (overlayCbk) { saveState(NULL); } switch (mode) { case psModePS: // rotate, translate, and scale page imgWidth = imgURX - imgLLX; imgHeight = imgURY - imgLLY; x1 = (int)floor(state->getX1()); y1 = (int)floor(state->getY1()); x2 = (int)ceil(state->getX2()); y2 = (int)ceil(state->getY2()); width = x2 - x1; height = y2 - y1; tx = ty = 0; // rotation and portrait/landscape mode if (paperMatch) { rotate = (360 - state->getRotate()) % 360; landscape = gFalse; } else if (rotate0 >= 0) { rotate = (360 - rotate0) % 360; landscape = gFalse; } else { rotate = (360 - state->getRotate()) % 360; if (rotate == 0 || rotate == 180) { if ((width < height && imgWidth > imgHeight && height > imgHeight) || (width > height && imgWidth < imgHeight && width > imgWidth)) { rotate += 90; landscape = gTrue; } else { landscape = gFalse; } } else { // rotate == 90 || rotate == 270 if ((height < width && imgWidth > imgHeight && width > imgHeight) || (height > width && imgWidth < imgHeight && height > imgWidth)) { rotate = 270 - rotate; landscape = gTrue; } else { landscape = gFalse; } } } writePSFmt("%%PageOrientation: {0:s}\n", landscape ? "Landscape" : "Portrait"); writePS("pdfStartPage\n"); if (rotate == 0) { imgWidth2 = imgWidth; imgHeight2 = imgHeight; } else if (rotate == 90) { writePS("90 rotate\n"); ty = -imgWidth; imgWidth2 = imgHeight; imgHeight2 = imgWidth; } else if (rotate == 180) { writePS("180 rotate\n"); imgWidth2 = imgWidth; imgHeight2 = imgHeight; tx = -imgWidth; ty = -imgHeight; } else { // rotate == 270 writePS("270 rotate\n"); tx = -imgHeight; imgWidth2 = imgHeight; imgHeight2 = imgWidth; } // shrink or expand if (xScale0 > 0 && yScale0 > 0) { xScale = xScale0 * userUnit; yScale = yScale0 * userUnit; } else if ((globalParams->getPSShrinkLarger() && (width * userUnit > imgWidth2 || height * userUnit > imgHeight2)) || (globalParams->getPSExpandSmaller() && (width * userUnit < imgWidth2 && height * userUnit < imgHeight2))) { xScale = (double)imgWidth2 / (double)width; yScale = (double)imgHeight2 / (double)height; if (yScale < xScale) { xScale = yScale; } else { yScale = xScale; } } else { xScale = yScale = userUnit; } // deal with odd bounding boxes or clipping if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { tx -= xScale * clipLLX0; ty -= yScale * clipLLY0; } else { tx -= xScale * x1; ty -= yScale * y1; } // center if (tx0 >= 0 && ty0 >= 0) { tx += (rotate == 0 || rotate == 180) ? tx0 : ty0; ty += (rotate == 0 || rotate == 180) ? ty0 : -tx0; } else if (globalParams->getPSCenter()) { if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { tx += (imgWidth2 - xScale * (clipURX0 - clipLLX0)) / 2; ty += (imgHeight2 - yScale * (clipURY0 - clipLLY0)) / 2; } else { tx += (imgWidth2 - xScale * width) / 2; ty += (imgHeight2 - yScale * height) / 2; } } tx += (rotate == 0 || rotate == 180) ? imgLLX : imgLLY; ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } if (xScale != 1 || yScale != 1) { writePSFmt("{0:.4f} {1:.4f} scale\n", xScale, yScale); } if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re W\n", clipLLX0, clipLLY0, clipURX0 - clipLLX0, clipURY0 - clipLLY0); } else { writePSFmt("{0:d} {1:d} {2:d} {3:d} re W\n", x1, y1, x2 - x1, y2 - y1); } ++seqPage; break; case psModeEPS: writePS("pdfStartPage\n"); tx = ty = 0; rotate = (360 - state->getRotate()) % 360; if (rotate == 0) { } else if (rotate == 90) { writePS("90 rotate\n"); tx = -epsX1; ty = -epsY2; } else if (rotate == 180) { writePS("180 rotate\n"); tx = -(epsX1 + epsX2); ty = -(epsY1 + epsY2); } else { // rotate == 270 writePS("270 rotate\n"); tx = -epsX2; ty = -epsY1; } if (tx != 0 || ty != 0) { writePSFmt("{0:.6g} {1:.6g} translate\n", tx, ty); } xScale = yScale = 1; break; case psModeForm: writePS("/PaintProc {\n"); writePS("begin xpdf begin\n"); writePS("pdfStartPage\n"); tx = ty = 0; xScale = yScale = 1; rotate = 0; break; } if (level == psLevel2Gray || level == psLevel3Gray) { writePS("/DeviceGray setcolorspace\n"); } if (customCodeCbk) { if ((s = (*customCodeCbk)(this, psOutCustomPageSetup, pageNum, customCodeCbkData))) { writePS(s->getCString()); delete s; } } if (mode == psModePS) { writePS("%%EndPageSetup\n"); } noStateChanges = gFalse; } void PSOutputDev::endPage() { if (overlayCbk) { restoreState(NULL); (*overlayCbk)(this, overlayCbkData); } if (mode == psModeForm) { writePS("pdfEndPage\n"); writePS("end end\n"); writePS("} def\n"); writePS("end end\n"); } else { if (!manualCtrl) { writePS("showpage\n"); } writePS("%%PageTrailer\n"); writePageTrailer(); writePS("end\n"); } } void PSOutputDev::saveState(GfxState *state) { // The noStateChanges and saveStack fields are used to implement an // optimization to reduce gsave/grestore nesting. The idea is to // look for sequences like this: // q q AAA Q BBB Q (where AAA and BBB are sequences of operations) // and transform them to: // q AAA Q q BBB Q if (noStateChanges) { // any non-NULL pointer will work here saveStack->append(this); } else { saveStack->append((PSOutputDev *)NULL); writePS("q\n"); noStateChanges = gTrue; } } void PSOutputDev::restoreState(GfxState *state) { if (saveStack->getLength()) { writePS("Q\n"); if (saveStack->del(saveStack->getLength() - 1)) { writePS("q\n"); noStateChanges = gTrue; } else { noStateChanges = gFalse; } } } void PSOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { if (m11 == 1 && m12 == 0 && m21 == 0 && m22 == 1 && m31 == 0 && m32 == 0) { return; } if (fabs(m11 * m22 - m12 * m21) < 1e-10) { // avoid a singular (or close-to-singular) matrix writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] cm\n", m31, m32); } else { writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] cm\n", m11, m12, m21, m22, m31, m32); } noStateChanges = gFalse; } void PSOutputDev::updateLineDash(GfxState *state) { double *dash; double start; int length, i; state->getLineDash(&dash, &length, &start); writePS("["); for (i = 0; i < length; ++i) { writePSFmt("{0:.6g}{1:w}", dash[i] < 0 ? 0 : dash[i], (i == length-1) ? 0 : 1); } writePSFmt("] {0:.6g} d\n", start); noStateChanges = gFalse; } void PSOutputDev::updateFlatness(GfxState *state) { writePSFmt("{0:.4g} i\n", state->getFlatness()); noStateChanges = gFalse; } void PSOutputDev::updateLineJoin(GfxState *state) { writePSFmt("{0:d} j\n", state->getLineJoin()); noStateChanges = gFalse; } void PSOutputDev::updateLineCap(GfxState *state) { writePSFmt("{0:d} J\n", state->getLineCap()); noStateChanges = gFalse; } void PSOutputDev::updateMiterLimit(GfxState *state) { writePSFmt("{0:.4g} M\n", state->getMiterLimit()); noStateChanges = gFalse; } void PSOutputDev::updateLineWidth(GfxState *state) { writePSFmt("{0:.6g} w\n", state->getLineWidth()); noStateChanges = gFalse; } void PSOutputDev::updateFillColorSpace(GfxState *state) { switch (level) { case psLevel1: case psLevel1Sep: break; case psLevel2: case psLevel3: if (state->getFillColorSpace()->getMode() != csPattern) { dumpColorSpaceL2(state, state->getFillColorSpace(), gTrue, gFalse, gFalse); writePS(" cs\n"); noStateChanges = gFalse; } break; case psLevel2Gray: case psLevel3Gray: case psLevel2Sep: case psLevel3Sep: break; } } void PSOutputDev::updateStrokeColorSpace(GfxState *state) { switch (level) { case psLevel1: case psLevel1Sep: break; case psLevel2: case psLevel3: if (state->getStrokeColorSpace()->getMode() != csPattern) { dumpColorSpaceL2(state, state->getStrokeColorSpace(), gTrue, gFalse, gFalse); writePS(" CS\n"); noStateChanges = gFalse; } break; case psLevel2Gray: case psLevel3Gray: case psLevel2Sep: case psLevel3Sep: break; } } void PSOutputDev::updateFillColor(GfxState *state) { GfxColor color; GfxColor *colorPtr; GfxGray gray; GfxCMYK cmyk; GfxSeparationColorSpace *sepCS; double c, m, y, k; int i; switch (level) { case psLevel1: case psLevel2Gray: case psLevel3Gray: state->getFillGray(&gray); writePSFmt("{0:.4g} g\n", colToDbl(gray)); break; case psLevel1Sep: state->getFillCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); addProcessColor(c, m, y, k); break; case psLevel2: case psLevel3: if (state->getFillColorSpace()->getMode() != csPattern) { colorPtr = state->getFillColor(); writePS("["); for (i = 0; i < state->getFillColorSpace()->getNComps(); ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); } writePS("] sc\n"); } break; case psLevel2Sep: case psLevel3Sep: if (state->getFillColorSpace()->getMode() == csSeparation) { sepCS = (GfxSeparationColorSpace *)state->getFillColorSpace(); color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) ck\n", colToDbl(state->getFillColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); addCustomColor(state, sepCS); } else { state->getFillCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} k\n", c, m, y, k); addProcessColor(c, m, y, k); } break; } t3Cacheable = gFalse; noStateChanges = gFalse; } void PSOutputDev::updateStrokeColor(GfxState *state) { GfxColor color; GfxColor *colorPtr; GfxGray gray; GfxCMYK cmyk; GfxSeparationColorSpace *sepCS; double c, m, y, k; int i; switch (level) { case psLevel1: case psLevel2Gray: case psLevel3Gray: state->getStrokeGray(&gray); writePSFmt("{0:.4g} G\n", colToDbl(gray)); break; case psLevel1Sep: state->getStrokeCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); addProcessColor(c, m, y, k); break; case psLevel2: case psLevel3: if (state->getStrokeColorSpace()->getMode() != csPattern) { colorPtr = state->getStrokeColor(); writePS("["); for (i = 0; i < state->getStrokeColorSpace()->getNComps(); ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g}", colToDbl(colorPtr->c[i])); } writePS("] SC\n"); } break; case psLevel2Sep: case psLevel3Sep: if (state->getStrokeColorSpace()->getMode() == csSeparation) { sepCS = (GfxSeparationColorSpace *)state->getStrokeColorSpace(); color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} ({5:t}) CK\n", colToDbl(state->getStrokeColor()->c[0]), colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); addCustomColor(state, sepCS); } else { state->getStrokeCMYK(&cmyk); c = colToDbl(cmyk.c); m = colToDbl(cmyk.m); y = colToDbl(cmyk.y); k = colToDbl(cmyk.k); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} K\n", c, m, y, k); addProcessColor(c, m, y, k); } break; } t3Cacheable = gFalse; noStateChanges = gFalse; } void PSOutputDev::addProcessColor(double c, double m, double y, double k) { if (c > 0) { processColors |= psProcessCyan; } if (m > 0) { processColors |= psProcessMagenta; } if (y > 0) { processColors |= psProcessYellow; } if (k > 0) { processColors |= psProcessBlack; } } void PSOutputDev::addCustomColor(GfxState *state, GfxSeparationColorSpace *sepCS) { PSOutCustomColor *cc; GfxColor color; GfxCMYK cmyk; for (cc = customColors; cc; cc = cc->next) { if (!cc->name->cmp(sepCS->getName())) { return; } } color.c[0] = gfxColorComp1; sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()->copy()); cc->next = customColors; customColors = cc; } void PSOutputDev::addCustomColors(GfxState *state, GfxDeviceNColorSpace *devnCS) { PSOutCustomColor *cc; GfxColor color; GfxCMYK cmyk; int i; for (i = 0; i < devnCS->getNComps(); ++i) { color.c[i] = 0; } for (i = 0; i < devnCS->getNComps(); ++i) { for (cc = customColors; cc; cc = cc->next) { if (!cc->name->cmp(devnCS->getColorantName(i))) { break; } } if (cc) { continue; } color.c[i] = gfxColorComp1; devnCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); color.c[i] = 0; cc = new PSOutCustomColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), devnCS->getColorantName(i)->copy()); cc->next = customColors; customColors = cc; } } void PSOutputDev::updateFillOverprint(GfxState *state) { if (level == psLevel2 || level == psLevel2Sep || level == psLevel3 || level == psLevel3Sep) { writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false"); noStateChanges = gFalse; } } void PSOutputDev::updateStrokeOverprint(GfxState *state) { if (level == psLevel2 || level == psLevel2Sep || level == psLevel3 || level == psLevel3Sep) { writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false"); noStateChanges = gFalse; } } void PSOutputDev::updateOverprintMode(GfxState *state) { if (level == psLevel3 || level == psLevel3Sep) { writePSFmt("{0:s} opm\n", state->getOverprintMode() ? "true" : "false"); noStateChanges = gFalse; } } void PSOutputDev::updateTransfer(GfxState *state) { Function **funcs; int i; funcs = state->getTransfer(); if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) { if (level == psLevel2 || level == psLevel2Sep || level == psLevel3 || level == psLevel3Sep) { for (i = 0; i < 4; ++i) { cvtFunction(funcs[i]); } writePS("setcolortransfer\n"); } else { cvtFunction(funcs[3]); writePS("settransfer\n"); } } else if (funcs[0]) { cvtFunction(funcs[0]); writePS("settransfer\n"); } else { writePS("{} settransfer\n"); } noStateChanges = gFalse; } void PSOutputDev::updateFont(GfxState *state) { if (state->getFont()) { if (state->getFont()->getTag() && !state->getFont()->getTag()->cmp("xpdf_default_font")) { writePSFmt("/xpdf_default_font {0:.6g} Tf\n", fabs(state->getFontSize()) < 0.0001 ? 0.0001 : state->getFontSize()); } else { writePSFmt("/F{0:d}_{1:d} {2:.6g} Tf\n", state->getFont()->getID()->num, state->getFont()->getID()->gen, fabs(state->getFontSize()) < 0.0001 ? 0.0001 : state->getFontSize()); } noStateChanges = gFalse; } } void PSOutputDev::updateTextMat(GfxState *state) { double *mat; mat = state->getTextMat(); if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 1e-10) { // avoid a singular (or close-to-singular) matrix writePSFmt("[0.00001 0 0 0.00001 {0:.6g} {1:.6g}] Tm\n", mat[4], mat[5]); } else { writePSFmt("[{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] Tm\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); } noStateChanges = gFalse; } void PSOutputDev::updateCharSpace(GfxState *state) { writePSFmt("{0:.6g} Tc\n", state->getCharSpace()); noStateChanges = gFalse; } void PSOutputDev::updateRender(GfxState *state) { int rm; rm = state->getRender(); writePSFmt("{0:d} Tr\n", rm); rm &= 3; if (rm != 0 && rm != 3) { t3Cacheable = gFalse; } noStateChanges = gFalse; } void PSOutputDev::updateRise(GfxState *state) { writePSFmt("{0:.6g} Ts\n", state->getRise()); noStateChanges = gFalse; } void PSOutputDev::updateWordSpace(GfxState *state) { writePSFmt("{0:.6g} Tw\n", state->getWordSpace()); noStateChanges = gFalse; } void PSOutputDev::updateHorizScaling(GfxState *state) { double h; h = state->getHorizScaling(); if (fabs(h) < 0.01) { h = 0.01; } writePSFmt("{0:.6g} Tz\n", h); noStateChanges = gFalse; } void PSOutputDev::updateTextPos(GfxState *state) { writePSFmt("{0:.6g} {1:.6g} Td\n", state->getLineX(), state->getLineY()); noStateChanges = gFalse; } void PSOutputDev::updateTextShift(GfxState *state, double shift) { if (state->getFont()->getWMode()) { writePSFmt("{0:.6g} TJmV\n", shift); } else { writePSFmt("{0:.6g} TJm\n", shift); } noStateChanges = gFalse; } void PSOutputDev::saveTextPos(GfxState *state) { writePS("currentpoint\n"); noStateChanges = gFalse; } void PSOutputDev::restoreTextPos(GfxState *state) { writePS("m\n"); noStateChanges = gFalse; } void PSOutputDev::stroke(GfxState *state) { doPath(state->getPath()); if (inType3Char && t3FillColorOnly) { // if we're constructing a cacheable Type 3 glyph, we need to do // everything in the fill color writePS("Sf\n"); } else { writePS("S\n"); } noStateChanges = gFalse; } void PSOutputDev::fill(GfxState *state) { doPath(state->getPath()); writePS("f\n"); noStateChanges = gFalse; } void PSOutputDev::eoFill(GfxState *state) { doPath(state->getPath()); writePS("f*\n"); noStateChanges = gFalse; } void PSOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { if (level <= psLevel1Sep) { tilingPatternFillL1(state, gfx, strRef, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); } else { tilingPatternFillL2(state, gfx, strRef, paintType, tilingType, resDict, mat, bbox, x0, y0, x1, y1, xStep, yStep); } } void PSOutputDev::tilingPatternFillL1(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx2; // define a Type 3 font writePS("8 dict begin\n"); writePS("/FontType 3 def\n"); writePS("/FontMatrix [1 0 0 1 0 0] def\n"); writePSFmt("/FontBBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); writePS("/Encoding 256 array def\n"); writePS(" 0 1 255 { Encoding exch /.notdef put } for\n"); writePS(" Encoding 120 /x put\n"); writePS("/BuildGlyph {\n"); writePS(" exch /CharProcs get exch\n"); writePS(" 2 copy known not { pop /.notdef } if\n"); writePS(" get exec\n"); writePS("} bind def\n"); writePS("/BuildChar {\n"); writePS(" 1 index /Encoding get exch get\n"); writePS(" 1 index /BuildGlyph get exec\n"); writePS("} bind def\n"); writePS("/CharProcs 1 dict def\n"); writePS("CharProcs begin\n"); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx2 = new Gfx(doc, this, resDict, &box, NULL); gfx2->takeContentStreamStack(gfx); writePS("/x {\n"); if (paintType == 2) { writePSFmt("{0:.6g} 0 {1:.6g} {2:.6g} {3:.6g} {4:.6g} setcachedevice\n", xStep, bbox[0], bbox[1], bbox[2], bbox[3]); t3FillColorOnly = gTrue; } else { if (x1 - 1 <= x0) { writePS("1 0 setcharwidth\n"); } else { writePSFmt("{0:.6g} 0 setcharwidth\n", xStep); } t3FillColorOnly = gFalse; } inType3Char = gTrue; ++numTilingPatterns; gfx2->display(strRef); --numTilingPatterns; inType3Char = gFalse; writePS("} def\n"); delete gfx2; writePS("end\n"); writePS("currentdict end\n"); writePSFmt("/xpdfTile{0:d} exch definefont pop\n", numTilingPatterns); // draw the tiles writePSFmt("/xpdfTile{0:d} findfont setfont\n", numTilingPatterns); writePS("fCol\n"); writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("{0:d} 1 {1:d} {{ {2:.6g} exch {3:.6g} mul m {4:d} 1 {5:d} {{ pop (x) show }} for }} for\n", y0, y1 - 1, x0 * xStep, yStep, x0, x1 - 1); writePS("grestore\n"); noStateChanges = gFalse; } void PSOutputDev::tilingPatternFillL2(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx2; // switch to pattern space writePSFmt("gsave [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] concat\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); // define a pattern writePSFmt("/xpdfTile{0:d}\n", numTilingPatterns); writePS("<<\n"); writePS(" /PatternType 1\n"); writePSFmt(" /PaintType {0:d}\n", paintType); writePSFmt(" /TilingType {0:d}\n", tilingType); writePSFmt(" /BBox [{0:.6g} {1:.6g} {2:.6g} {3:.6g}]\n", bbox[0], bbox[1], bbox[2], bbox[3]); writePSFmt(" /XStep {0:.6g}\n", xStep); writePSFmt(" /YStep {0:.6g}\n", yStep); writePS(" /PaintProc {\n"); writePS(" pop\n"); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx2 = new Gfx(doc, this, resDict, &box, NULL); gfx2->takeContentStreamStack(gfx); t3FillColorOnly = paintType == 2; inType3Char = gTrue; ++numTilingPatterns; gfx2->display(strRef); --numTilingPatterns; inType3Char = gFalse; delete gfx2; writePS(" }\n"); writePS(">> matrix makepattern def\n"); // set the pattern if (paintType == 2) { writePS("currentcolor "); } writePSFmt("xpdfTile{0:d} setpattern\n", numTilingPatterns); // fill with the pattern writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} rectfill\n", x0 * xStep + bbox[0], y0 * yStep + bbox[1], (x1 - x0) * xStep + bbox[2], (y1 - y0) * yStep + bbox[3]); writePS("grestore\n"); noStateChanges = gFalse; } GBool PSOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { double x0, y0, x1, y1; double *mat; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return gFalse; } processColors |= psProcessCMYK; } shading->getDomain(&x0, &y0, &x1, &y1); mat = shading->getMatrix(); writePSFmt("/mat [{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g}] def\n", mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("2 copy\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("3 1 roll\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} 0 funcSH\n", x0, y0, x1, y1); noStateChanges = gFalse; return gTrue; } GBool PSOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, x1, y1, dx, dy, mul; double tMin, tMax, t, t0, t1; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return gFalse; } processColors |= psProcessCMYK; } // get the clip region bbox state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); // compute min and max t values, based on the four corners of the // clip region bbox shading->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; if (fabs(dx) < 0.01 && fabs(dy) < 0.01) { return gTrue; } else { mul = 1 / (dx * dx + dy * dy); tMin = tMax = ((xMin - x0) * dx + (yMin - y0) * dy) * mul; t = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } t = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; if (t < tMin) { tMin = t; } else if (t > tMax) { tMax = t; } if (tMin < 0 && !shading->getExtend0()) { tMin = 0; } if (tMax > 1 && !shading->getExtend1()) { tMax = 1; } } // get the function domain t0 = shading->getDomain0(); t1 = shading->getDomain1(); // generate the PS code writePSFmt("/t0 {0:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/xMin {0:.6g} def\n", xMin); writePSFmt("/yMin {0:.6g} def\n", yMin); writePSFmt("/xMax {0:.6g} def\n", xMax); writePSFmt("/yMax {0:.6g} def\n", yMax); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 axialSH\n", tMin, tMax); noStateChanges = gFalse; return gTrue; } GBool PSOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading) { double xMin, yMin, xMax, yMax; double x0, y0, r0, x1, y1, r1, t0, t1; double xa, ya, ra; double sMin, sMax, h, ta; double sLeft, sRight, sTop, sBottom, sZero, sDiag; GBool haveSLeft, haveSRight, haveSTop, haveSBottom, haveSZero; GBool haveSMin, haveSMax; double theta, alpha, a1, a2; GBool enclosed; int i; if (level == psLevel2Sep || level == psLevel3Sep) { if (shading->getColorSpace()->getMode() != csDeviceCMYK) { return gFalse; } processColors |= psProcessCMYK; } // get the shading info shading->getCoords(&x0, &y0, &r0, &x1, &y1, &r1); t0 = shading->getDomain0(); t1 = shading->getDomain1(); // Compute the point at which r(s) = 0; check for the enclosed // circles case; and compute the angles for the tangent lines. h = sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)); if (h == 0) { enclosed = gTrue; theta = 0; // make gcc happy } else if (r1 - r0 == 0) { enclosed = gFalse; theta = 0; } else if (fabs(r1 - r0) >= h) { enclosed = gTrue; theta = 0; // make gcc happy } else { enclosed = gFalse; theta = asin((r1 - r0) / h); } if (enclosed) { a1 = 0; a2 = 360; } else { alpha = atan2(y1 - y0, x1 - x0); a1 = (180 / M_PI) * (alpha + theta) + 90; a2 = (180 / M_PI) * (alpha - theta) - 90; while (a2 < a1) { a2 += 360; } } // compute the (possibly extended) s range state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); if (enclosed) { sMin = 0; sMax = 1; } else { // solve x(sLeft) + r(sLeft) = xMin if ((haveSLeft = fabs((x1 + r1) - (x0 + r0)) > 0.000001)) { sLeft = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0)); } else { sLeft = 0; // make gcc happy } // solve x(sRight) - r(sRight) = xMax if ((haveSRight = fabs((x1 - r1) - (x0 - r0)) > 0.000001)) { sRight = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0)); } else { sRight = 0; // make gcc happy } // solve y(sBottom) + r(sBottom) = yMin if ((haveSBottom = fabs((y1 + r1) - (y0 + r0)) > 0.000001)) { sBottom = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0)); } else { sBottom = 0; // make gcc happy } // solve y(sTop) - r(sTop) = yMax if ((haveSTop = fabs((y1 - r1) - (y0 - r0)) > 0.000001)) { sTop = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0)); } else { sTop = 0; // make gcc happy } // solve r(sZero) = 0 if ((haveSZero = fabs(r1 - r0) > 0.000001)) { sZero = -r0 / (r1 - r0); } else { sZero = 0; // make gcc happy } // solve r(sDiag) = sqrt((xMax-xMin)^2 + (yMax-yMin)^2) if (haveSZero) { sDiag = (sqrt((xMax - xMin) * (xMax - xMin) + (yMax - yMin) * (yMax - yMin)) - r0) / (r1 - r0); } else { sDiag = 0; // make gcc happy } // compute sMin if (shading->getExtend0()) { sMin = 0; haveSMin = gFalse; if (x0 < x1 && haveSLeft && sLeft < 0) { sMin = sLeft; haveSMin = gTrue; } else if (x0 > x1 && haveSRight && sRight < 0) { sMin = sRight; haveSMin = gTrue; } if (y0 < y1 && haveSBottom && sBottom < 0) { if (!haveSMin || sBottom > sMin) { sMin = sBottom; haveSMin = gTrue; } } else if (y0 > y1 && haveSTop && sTop < 0) { if (!haveSMin || sTop > sMin) { sMin = sTop; haveSMin = gTrue; } } if (haveSZero && sZero < 0) { if (!haveSMin || sZero > sMin) { sMin = sZero; } } } else { sMin = 0; } // compute sMax if (shading->getExtend1()) { sMax = 1; haveSMax = gFalse; if (x1 < x0 && haveSLeft && sLeft > 1) { sMax = sLeft; haveSMax = gTrue; } else if (x1 > x0 && haveSRight && sRight > 1) { sMax = sRight; haveSMax = gTrue; } if (y1 < y0 && haveSBottom && sBottom > 1) { if (!haveSMax || sBottom < sMax) { sMax = sBottom; haveSMax = gTrue; } } else if (y1 > y0 && haveSTop && sTop > 1) { if (!haveSMax || sTop < sMax) { sMax = sTop; haveSMax = gTrue; } } if (haveSZero && sDiag > 1) { if (!haveSMax || sDiag < sMax) { sMax = sDiag; } } } else { sMax = 1; } } // generate the PS code writePSFmt("/x0 {0:.6g} def\n", x0); writePSFmt("/x1 {0:.6g} def\n", x1); writePSFmt("/dx {0:.6g} def\n", x1 - x0); writePSFmt("/y0 {0:.6g} def\n", y0); writePSFmt("/y1 {0:.6g} def\n", y1); writePSFmt("/dy {0:.6g} def\n", y1 - y0); writePSFmt("/r0 {0:.6g} def\n", r0); writePSFmt("/r1 {0:.6g} def\n", r1); writePSFmt("/dr {0:.6g} def\n", r1 - r0); writePSFmt("/t0 {0:.6g} def\n", t0); writePSFmt("/t1 {0:.6g} def\n", t1); writePSFmt("/dt {0:.6g} def\n", t1 - t0); writePSFmt("/n {0:d} def\n", shading->getColorSpace()->getNComps()); writePSFmt("/encl {0:s} def\n", enclosed ? "true" : "false"); writePSFmt("/a1 {0:.6g} def\n", a1); writePSFmt("/a2 {0:.6g} def\n", a2); if (shading->getNFuncs() == 1) { writePS("/func "); cvtFunction(shading->getFunc(0)); writePS("def\n"); } else { writePS("/func {\n"); for (i = 0; i < shading->getNFuncs(); ++i) { if (i < shading->getNFuncs() - 1) { writePS("dup\n"); } cvtFunction(shading->getFunc(i)); writePS("exec\n"); if (i < shading->getNFuncs() - 1) { writePS("exch\n"); } } writePS("} def\n"); } writePSFmt("{0:.6g} {1:.6g} 0 radialSH\n", sMin, sMax); // extend the 'enclosed' case if (enclosed) { // extend the smaller circle if ((shading->getExtend0() && r0 <= r1) || (shading->getExtend1() && r1 < r0)) { if (r0 <= r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h f*\n", xa, ya, ra); } // extend the larger circle if ((shading->getExtend0() && r0 > r1) || (shading->getExtend1() && r1 >= r0)) { if (r0 > r1) { ta = t0; ra = r0; xa = x0; ya = y0; } else { ta = t1; ra = r1; xa = x1; ya = y1; } if (level == psLevel2Sep || level == psLevel3Sep) { writePSFmt("{0:.6g} radialCol aload pop k\n", ta); } else { writePSFmt("{0:.6g} radialCol sc\n", ta); } writePSFmt("{0:.6g} {1:.6g} {2:.6g} 0 360 arc h\n", xa, ya, ra); writePSFmt("{0:.6g} {1:.6g} m {2:.6g} {3:.6g} l {4:.6g} {5:.6g} l {6:.6g} {7:.6g} l h f*\n", xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin); } } noStateChanges = gFalse; return gTrue; } void PSOutputDev::clip(GfxState *state) { doPath(state->getPath()); writePS("W\n"); noStateChanges = gFalse; } void PSOutputDev::eoClip(GfxState *state) { doPath(state->getPath()); writePS("W*\n"); noStateChanges = gFalse; } void PSOutputDev::clipToStrokePath(GfxState *state) { doPath(state->getPath()); writePS("Ws\n"); noStateChanges = gFalse; } void PSOutputDev::doPath(GfxPath *path) { GfxSubpath *subpath; double x0, y0, x1, y1, x2, y2, x3, y3, x4, y4; int n, m, i, j; n = path->getNumSubpaths(); if (n == 1 && path->getSubpath(0)->getNumPoints() == 5) { subpath = path->getSubpath(0); x0 = subpath->getX(0); y0 = subpath->getY(0); x4 = subpath->getX(4); y4 = subpath->getY(4); if (x4 == x0 && y4 == y0) { x1 = subpath->getX(1); y1 = subpath->getY(1); x2 = subpath->getX(2); y2 = subpath->getY(2); x3 = subpath->getX(3); y3 = subpath->getY(3); if (x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x2 ? x0 : x2, y0 < y1 ? y0 : y1, fabs(x2 - x0), fabs(y1 - y0)); return; } else if (x0 == x3 && x1 == x2 && y0 == y1 && y2 == y3) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} re\n", x0 < x1 ? x0 : x1, y0 < y2 ? y0 : y2, fabs(x1 - x0), fabs(y2 - y0)); return; } } } for (i = 0; i < n; ++i) { subpath = path->getSubpath(i); m = subpath->getNumPoints(); writePSFmt("{0:.6g} {1:.6g} m\n", subpath->getX(0), subpath->getY(0)); j = 1; while (j < m) { if (subpath->getCurve(j)) { writePSFmt("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} c\n", subpath->getX(j), subpath->getY(j), subpath->getX(j+1), subpath->getY(j+1), subpath->getX(j+2), subpath->getY(j+2)); j += 3; } else { writePSFmt("{0:.6g} {1:.6g} l\n", subpath->getX(j), subpath->getY(j)); ++j; } } if (subpath->isClosed()) { writePS("h\n"); } } } void PSOutputDev::drawString(GfxState *state, GString *s) { GfxFont *font; int wMode; int *codeToGID; GString *s2; double dx, dy, originX, originY, originX0, originY0, tOriginX0, tOriginY0; char *p; PSFontInfo *fi; UnicodeMap *uMap; CharCode code; Unicode u[8]; char buf[8]; double *dxdy; int dxdySize, len, nChars, uLen, n, m, i, j; // check for invisible text -- this is used by Acrobat Capture if (state->getRender() == 3) { return; } // ignore empty strings if (s->getLength() == 0) { return; } // get the font if (!(font = state->getFont())) { return; } wMode = font->getWMode(); fi = NULL; for (i = 0; i < fontInfo->getLength(); ++i) { fi = (PSFontInfo *)fontInfo->get(i); if (fi->fontID.num == font->getID()->num && fi->fontID.gen == font->getID()->gen) { break; } fi = NULL; } // check for a subtitute 16-bit font uMap = NULL; codeToGID = NULL; if (font->isCIDFont()) { if (!(fi && fi->ff)) { // font substitution failed, so don't output any text return; } if (fi->ff->encoding) { uMap = globalParams->getUnicodeMap(fi->ff->encoding); } // check for an 8-bit code-to-GID map } else { if (fi && fi->ff) { codeToGID = fi->ff->codeToGID; } } // compute the positioning (dx, dy) for each char in the string nChars = 0; p = s->getCString(); len = s->getLength(); s2 = new GString(); dxdySize = font->isCIDFont() ? 8 : s->getLength(); dxdy = (double *)gmallocn(2 * dxdySize, sizeof(double)); originX0 = originY0 = 0; // make gcc happy while (len > 0) { n = font->getNextChar(p, len, &code, u, (int)(sizeof(u) / sizeof(Unicode)), &uLen, &dx, &dy, &originX, &originY); //~ this doesn't handle the case where the origin offset changes //~ within a string of characters -- which could be fixed by //~ modifying dx,dy as needed for each character if (p == s->getCString()) { originX0 = originX; originY0 = originY; } dx *= state->getFontSize(); dy *= state->getFontSize(); if (wMode) { dy += state->getCharSpace(); if (n == 1 && *p == ' ') { dy += state->getWordSpace(); } } else { dx += state->getCharSpace(); if (n == 1 && *p == ' ') { dx += state->getWordSpace(); } } dx *= state->getHorizScaling(); if (font->isCIDFont()) { if (uMap) { if (nChars + uLen > dxdySize) { do { dxdySize *= 2; } while (nChars + uLen > dxdySize); dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } for (i = 0; i < uLen; ++i) { m = uMap->mapUnicode(u[i], buf, (int)sizeof(buf)); for (j = 0; j < m; ++j) { s2->append(buf[j]); } //~ this really needs to get the number of chars in the target //~ encoding - which may be more than the number of Unicode //~ chars dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else { if (nChars + 1 > dxdySize) { dxdySize *= 2; dxdy = (double *)greallocn(dxdy, 2 * dxdySize, sizeof(double)); } s2->append((char)((code >> 8) & 0xff)); s2->append((char)(code & 0xff)); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } else { if (!codeToGID || codeToGID[code] >= 0) { s2->append((char)code); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; ++nChars; } } p += n; len -= n; } if (uMap) { uMap->decRefCnt(); } originX0 *= state->getFontSize(); originY0 *= state->getFontSize(); state->textTransformDelta(originX0, originY0, &tOriginX0, &tOriginY0); if (nChars > 0) { if (wMode) { writePSFmt("{0:.6g} {1:.6g} rmoveto\n", -tOriginX0, -tOriginY0); } writePSString(s2); writePS("\n["); for (i = 0; i < 2 * nChars; ++i) { if (i > 0) { writePS("\n"); } writePSFmt("{0:.6g}", dxdy[i]); } if (font->getType() == fontType3) { writePS("] Tj3\n"); } else { writePS("] Tj\n"); } if (wMode) { writePSFmt("{0:.6g} {1:.6g} rmoveto\n", tOriginX0, tOriginY0); } } gfree(dxdy); delete s2; if ((state->getRender() & 4) && font->getType() != fontType3) { haveTextClip = gTrue; } noStateChanges = gFalse; } void PSOutputDev::endTextObject(GfxState *state) { if (haveTextClip) { writePS("Tclip\n"); haveTextClip = gFalse; noStateChanges = gFalse; } } void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate) { int len; len = height * ((width + 7) / 8); switch (level) { case psLevel1: case psLevel1Sep: doImageL1(ref, state, NULL, invert, inlineImg, str, width, height, len); break; case psLevel2: case psLevel2Gray: case psLevel2Sep: doImageL2(ref, state, NULL, invert, inlineImg, str, width, height, len, NULL, NULL, 0, 0, gFalse); break; case psLevel3: case psLevel3Gray: case psLevel3Sep: doImageL3(ref, state, NULL, invert, inlineImg, str, width, height, len, NULL, NULL, 0, 0, gFalse); break; } noStateChanges = gFalse; } void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg, GBool interpolate) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, state, colorMap, gFalse, inlineImg, str, width, height, len); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(state, colorMap, gFalse, inlineImg, str, width, height, len); break; case psLevel2: case psLevel2Gray: case psLevel2Sep: doImageL2(ref, state, colorMap, gFalse, inlineImg, str, width, height, len, maskColors, NULL, 0, 0, gFalse); break; case psLevel3: case psLevel3Gray: case psLevel3Sep: doImageL3(ref, state, colorMap, gFalse, inlineImg, str, width, height, len, maskColors, NULL, 0, 0, gFalse); break; } t3Cacheable = gFalse; noStateChanges = gFalse; } void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate) { int len; len = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); switch (level) { case psLevel1: doImageL1(ref, state, colorMap, gFalse, gFalse, str, width, height, len); break; case psLevel1Sep: //~ handle indexed, separation, ... color spaces doImageL1Sep(state, colorMap, gFalse, gFalse, str, width, height, len); break; case psLevel2: case psLevel2Gray: case psLevel2Sep: doImageL2(ref, state, colorMap, gFalse, gFalse, str, width, height, len, NULL, maskStr, maskWidth, maskHeight, maskInvert); break; case psLevel3: case psLevel3Gray: case psLevel3Sep: doImageL3(ref, state, colorMap, gFalse, gFalse, str, width, height, len, NULL, maskStr, maskWidth, maskHeight, maskInvert); break; } t3Cacheable = gFalse; noStateChanges = gFalse; } void PSOutputDev::doImageL1(Object *ref, GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len) { ImageStream *imgStr; Guchar pixBuf[gfxColorMaxComps]; GfxGray gray; int col, x, y, c, i; if ((inType3Char || preload) && !colorMap) { if (inlineImg) { // create an array str = new FixedLengthEncoder(str, len); str = new ASCIIHexEncoder(str); str->reset(); col = 0; writePS("[<"); do { do { c = str->getChar(); } while (c == '\n' || c == '\r'); if (c == '>' || c == EOF) { break; } writePSChar((char)c); ++col; // each line is: "<...data...>" // so max data length = 255 - 4 = 251 // but make it 240 just to be safe // chunks are 2 bytes each, so we need to stop on an even col number if (col == 240) { writePS(">\n<"); col = 0; } } while (c != '>' && c != EOF); writePS(">]\n"); writePS("0\n"); str->close(); delete str; } else { // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); } } // image/imagemask command if ((inType3Char || preload) && !colorMap) { writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1a\n", width, height, invert ? "true" : "false", width, -height, height); } else if (colorMap) { writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1\n", width, height, width, -height, height); } else { writePSFmt("{0:d} {1:d} {2:s} [{3:d} 0 0 {4:d} 0 {5:d}] pdfImM1\n", width, height, invert ? "true" : "false", width, -height, height); } // image data if (!((inType3Char || preload) && !colorMap)) { if (colorMap) { // set up to process the data stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // process the data stream i = 0; for (y = 0; y < height; ++y) { // write the line for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getGray(pixBuf, &gray, state->getRenderingIntent()); writePSFmt("{0:02x}", colToByte(gray)); if (++i == 32) { writePSChar('\n'); i = 0; } } } if (i != 0) { writePSChar('\n'); } str->close(); delete imgStr; // imagemask } else { str->reset(); i = 0; for (y = 0; y < height; ++y) { for (x = 0; x < width; x += 8) { writePSFmt("{0:02x}", str->getChar() & 0xff); if (++i == 32) { writePSChar('\n'); i = 0; } } } if (i != 0) { writePSChar('\n'); } str->close(); } } } void PSOutputDev::doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len) { ImageStream *imgStr; Guchar *lineBuf; Guchar pixBuf[gfxColorMaxComps]; GfxCMYK cmyk; int x, y, i, comp; // width, height, matrix, bits per component writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1Sep\n", width, height, width, -height, height); // allocate a line buffer lineBuf = (Guchar *)gmallocn(width, 4); // set up to process the data stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // process the data stream i = 0; for (y = 0; y < height; ++y) { // read the line for (x = 0; x < width; ++x) { imgStr->getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk, state->getRenderingIntent()); lineBuf[4*x+0] = colToByte(cmyk.c); lineBuf[4*x+1] = colToByte(cmyk.m); lineBuf[4*x+2] = colToByte(cmyk.y); lineBuf[4*x+3] = colToByte(cmyk.k); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } // write one line of each color component for (comp = 0; comp < 4; ++comp) { for (x = 0; x < width; ++x) { writePSFmt("{0:02x}", lineBuf[4*x + comp]); if (++i == 32) { writePSChar('\n'); i = 0; } } } } if (i != 0) { writePSChar('\n'); } str->close(); delete imgStr; gfree(lineBuf); } void PSOutputDev::doImageL2(Object *ref, GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert) { Stream *str2; GString *s; int n, numComps; GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed; GfxSeparationColorSpace *sepCS; GfxColor color; GfxCMYK cmyk; char buf[4096]; int c, col, i; // color key masking if (maskColors && colorMap && !inlineImg) { // can't read the stream twice for inline images -- but masking // isn't allowed with inline images anyway convertColorKeyMaskToClipRects(colorMap, str, width, height, maskColors); // explicit masking } else if (maskStr) { convertExplicitMaskToClipRects(maskStr, maskWidth, maskHeight, maskInvert); } // color space if (colorMap && !(level == psLevel2Gray || level == psLevel3Gray)) { dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse); writePS(" setcolorspace\n"); } useASCIIHex = globalParams->getPSASCIIHex(); // set up the image data if (mode == psModeForm || inType3Char || preload) { if (inlineImg) { // create an array str2 = new FixedLengthEncoder(str, len); if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { str2 = new GrayRecoder(str2, width, height, colorMap); } if (globalParams->getPSLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar((char)c); ++col; } else { writePSChar((char)c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar((char)c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the LZWDecode/RunLengthDecode // filter may read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); } } // image dictionary writePS("<<\n /ImageType 1\n"); // width, height, matrix, bits per component writePSFmt(" /Width {0:d}\n", width); writePSFmt(" /Height {0:d}\n", height); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height); if (colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || level == psLevel2Gray || level == psLevel3Gray)) { writePS(" /BitsPerComponent 8\n"); } else { writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1); } // decode if (colorMap) { writePS(" /Decode ["); if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap->getColorSpace()->getMode() == csSeparation) { // this matches up with the code in the pdfImSep operator n = (1 << colorMap->getBits()) - 1; writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n); } else if (level == psLevel2Gray || level == psLevel3Gray) { writePS("0 1"); } else if (colorMap->getColorSpace()->getMode() == csDeviceN) { numComps = ((GfxDeviceNColorSpace *)colorMap->getColorSpace())-> getAlt()->getNComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePS("0 1"); } } else { numComps = colorMap->getNumPixelComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); } } writePS("]\n"); } else { writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); } // data source if (mode == psModeForm || inType3Char || preload) { writePS(" /DataSource { pdfImStr }\n"); } else { writePS(" /DataSource currentfile\n"); } // filters if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; useLZW = useRLE = gFalse; useCompressed = gFalse; useASCII = gFalse; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " "); if ((colorMap && (colorMap->getColorSpace()->getMode() == csDeviceN || level == psLevel2Gray || level == psLevel3Gray)) || inlineImg || !s) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useASCII = !(mode == psModeForm || inType3Char || preload); useCompressed = gFalse; } else { useLZW = useRLE = gFalse; useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preload); useCompressed = gTrue; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->getCString()); } if (s) { delete s; } if (mode == psModeForm || inType3Char || preload) { // end of image dictionary writePSFmt(">>\n{0:s}\n", colorMap ? "image" : "imagemask"); // get rid of the array and index writePS("pop pop\n"); } else { // cut off inline image streams at appropriate length if (inlineImg) { str = new FixedLengthEncoder(str, len); } else if (useCompressed) { str = str->getUndecodedStream(); } // recode to grayscale if (colorMap && (level == psLevel2Gray || level == psLevel3Gray)) { str = new GrayRecoder(str, width, height, colorMap); // recode DeviceN data } else if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) { str = new DeviceNRecoder(str, width, height, colorMap); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // end of image dictionary writePS(">>\n"); #if OPI_SUPPORT if (opi13Nest) { if (inlineImg) { // this can't happen -- OPI dictionaries are in XObjects error(errSyntaxError, -1, "OPI in inline image"); n = 0; } else { // need to read the stream to count characters -- the length // is data-dependent (because of ASCII and LZW/RunLength // filters) str->reset(); n = 0; do { i = str->discardChars(4096); n += i; } while (i == 4096); str->close(); } // +6/7 for "pdfIm\n" / "pdfImM\n" // +8 for newline + trailer n += colorMap ? 14 : 15; writePSFmt("%%BeginData: {0:d} Hex Bytes\n", n); } #endif if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap && colorMap->getColorSpace()->getMode() == csSeparation) { color.c[0] = gfxColorComp1; sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); } else { writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); } // copy the stream data str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); // add newline and trailer to the end writePSChar('\n'); writePS("%-EOD-\n"); #if OPI_SUPPORT if (opi13Nest) { writePS("%%EndData\n"); } #endif // delete encoders if (useLZW || useRLE || useASCII || inlineImg) { delete str; } } if ((maskColors && colorMap && !inlineImg) || maskStr) { writePS("pdfImClipEnd\n"); } } // Convert color key masking to a clipping region consisting of a // sequence of clip rectangles. void PSOutputDev::convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap, Stream *str, int width, int height, int *maskColors) { ImageStream *imgStr; Guchar *line; PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; GBool emitRect, addRect, extendRect; int numComps, i, j, x0, x1, y; numComps = colorMap->getNumPixelComps(); imgStr = new ImageStream(str, width, numComps, colorMap->getBits()); imgStr->reset(); rects0Len = rects1Len = rectsOutLen = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); for (y = 0; y < height; ++y) { if (!(line = imgStr->getLine())) { break; } i = 0; rects1Len = 0; for (x0 = 0; x0 < width; ++x0) { for (j = 0; j < numComps; ++j) { if (line[x0*numComps+j] < maskColors[2*j] || line[x0*numComps+j] > maskColors[2*j+1]) { break; } } if (j < numComps) { break; } } for (x1 = x0; x1 < width; ++x1) { for (j = 0; j < numComps; ++j) { if (line[x1*numComps+j] < maskColors[2*j] || line[x1*numComps+j] > maskColors[2*j+1]) { break; } } if (j == numComps) { break; } } while (x0 < width || i < rects0Len) { emitRect = addRect = extendRect = gFalse; if (x0 >= width) { emitRect = gTrue; } else if (i >= rects0Len) { addRect = gTrue; } else if (rects0[i].x0 < x0) { emitRect = gTrue; } else if (x0 < rects0[i].x0) { addRect = gTrue; } else if (rects0[i].x1 == x1) { extendRect = gTrue; } else { emitRect = addRect = gTrue; } if (emitRect) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = height - y - 1; rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; ++rectsOutLen; ++i; } if (addRect || extendRect) { if (rects1Len == rectsSize) { rectsSize *= 2; rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect)); } rects1[rects1Len].x0 = x0; rects1[rects1Len].x1 = x1; if (addRect) { rects1[rects1Len].y0 = y; } if (extendRect) { rects1[rects1Len].y0 = rects0[i].y0; ++i; } ++rects1Len; for (x0 = x1; x0 < width; ++x0) { for (j = 0; j < numComps; ++j) { if (line[x0*numComps+j] < maskColors[2*j] || line[x0*numComps+j] > maskColors[2*j+1]) { break; } } if (j < numComps) { break; } } for (x1 = x0; x1 < width; ++x1) { for (j = 0; j < numComps; ++j) { if (line[x1*numComps+j] < maskColors[2*j] || line[x1*numComps+j] > maskColors[2*j+1]) { break; } } if (j == numComps) { break; } } } } rectsTmp = rects0; rects0 = rects1; rects1 = rectsTmp; i = rects0Len; rects0Len = rects1Len; rects1Len = i; } for (i = 0; i < rects0Len; ++i) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = height - y - 1; rectsOut[rectsOutLen].y1 = height - rects0[i].y0 - 1; ++rectsOutLen; } writePSFmt("{0:d} {1:d}\n", width, height); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0); } writePS("pop pop pdfImClip\n"); gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; str->close(); } // Convert an explicit mask image to a clipping region consisting of a // sequence of clip rectangles. void PSOutputDev::convertExplicitMaskToClipRects(Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert) { ImageStream *imgStr; Guchar *line; PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; GBool emitRect, addRect, extendRect; int i, x0, x1, y, maskXor; imgStr = new ImageStream(maskStr, maskWidth, 1, 1); imgStr->reset(); rects0Len = rects1Len = rectsOutLen = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); maskXor = maskInvert ? 1 : 0; for (y = 0; y < maskHeight; ++y) { if (!(line = imgStr->getLine())) { break; } i = 0; rects1Len = 0; for (x0 = 0; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; while (x0 < maskWidth || i < rects0Len) { emitRect = addRect = extendRect = gFalse; if (x0 >= maskWidth) { emitRect = gTrue; } else if (i >= rects0Len) { addRect = gTrue; } else if (rects0[i].x0 < x0) { emitRect = gTrue; } else if (x0 < rects0[i].x0) { addRect = gTrue; } else if (rects0[i].x1 == x1) { extendRect = gTrue; } else { emitRect = addRect = gTrue; } if (emitRect) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = maskHeight - y - 1; rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; ++rectsOutLen; ++i; } if (addRect || extendRect) { if (rects1Len == rectsSize) { rectsSize *= 2; rects0 = (PSOutImgClipRect *)greallocn(rects0, rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)greallocn(rects1, rectsSize, sizeof(PSOutImgClipRect)); } rects1[rects1Len].x0 = x0; rects1[rects1Len].x1 = x1; if (addRect) { rects1[rects1Len].y0 = y; } if (extendRect) { rects1[rects1Len].y0 = rects0[i].y0; ++i; } ++rects1Len; for (x0 = x1; x0 < maskWidth && (line[x0] ^ maskXor); ++x0) ; for (x1 = x0; x1 < maskWidth && !(line[x1] ^ maskXor); ++x1) ; } } rectsTmp = rects0; rects0 = rects1; rects1 = rectsTmp; i = rects0Len; rects0Len = rects1Len; rects1Len = i; } for (i = 0; i < rects0Len; ++i) { if (rectsOutLen == rectsOutSize) { rectsOutSize *= 2; rectsOut = (PSOutImgClipRect *)greallocn(rectsOut, rectsOutSize, sizeof(PSOutImgClipRect)); } rectsOut[rectsOutLen].x0 = rects0[i].x0; rectsOut[rectsOutLen].x1 = rects0[i].x1; rectsOut[rectsOutLen].y0 = maskHeight - y - 1; rectsOut[rectsOutLen].y1 = maskHeight - rects0[i].y0 - 1; ++rectsOutLen; } writePSFmt("{0:d} {1:d}\n", maskWidth, maskHeight); for (i = 0; i < rectsOutLen; ++i) { writePSFmt("{0:d} {1:d} {2:d} {3:d} pr\n", rectsOut[i].x0, rectsOut[i].y0, rectsOut[i].x1 - rectsOut[i].x0, rectsOut[i].y1 - rectsOut[i].y0); } writePS("pop pop pdfImClip\n"); gfree(rectsOut); gfree(rects0); gfree(rects1); delete imgStr; maskStr->close(); } //~ this doesn't currently support OPI void PSOutputDev::doImageL3(Object *ref, GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert) { Stream *str2; GString *s; int n, numComps; GBool useLZW, useRLE, useASCII, useASCIIHex, useCompressed; GBool maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed; GString *maskFilters; GfxSeparationColorSpace *sepCS; GfxColor color; GfxCMYK cmyk; char buf[4096]; int c; int col, i; useASCIIHex = globalParams->getPSASCIIHex(); useLZW = useRLE = useASCII = useCompressed = gFalse; // make gcc happy maskUseLZW = maskUseRLE = maskUseASCII = gFalse; // make gcc happy maskUseCompressed = gFalse; // make gcc happy maskFilters = NULL; // make gcc happy // explicit masking // -- this also converts color key masking in grayscale mode if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { // mask data source if (maskColors && colorMap && level == psLevel3Gray) { s = NULL; if (mode == psModeForm || inType3Char || preload) { if (globalParams->getPSUncompressPreloadedImages()) { maskUseLZW = maskUseRLE = gFalse; } else if (globalParams->getPSLZW()) { maskUseLZW = gTrue; maskUseRLE = gFalse; } else { maskUseRLE = gTrue; maskUseLZW = gFalse; } maskUseASCII = gFalse; maskUseCompressed = gFalse; } else { if (globalParams->getPSLZW()) { maskUseLZW = gTrue; maskUseRLE = gFalse; } else { maskUseRLE = gTrue; maskUseLZW = gFalse; } maskUseASCII = gTrue; } maskUseCompressed = gFalse; maskWidth = width; maskHeight = height; maskInvert = gFalse; } else if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; maskUseLZW = maskUseRLE = gFalse; maskUseCompressed = gFalse; maskUseASCII = gFalse; } else { s = maskStr->getPSFilter(3, " "); if (!s) { if (globalParams->getPSLZW()) { maskUseLZW = gTrue; maskUseRLE = gFalse; } else { maskUseRLE = gTrue; maskUseLZW = gFalse; } maskUseASCII = !(mode == psModeForm || inType3Char || preload); maskUseCompressed = gFalse; } else { maskUseLZW = maskUseRLE = gFalse; maskUseASCII = maskStr->isBinary() && !(mode == psModeForm || inType3Char || preload); maskUseCompressed = gTrue; } } maskFilters = new GString(); if (maskUseASCII) { maskFilters->appendf(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (maskUseLZW) { maskFilters->append(" /LZWDecode filter\n"); } else if (maskUseRLE) { maskFilters->append(" /RunLengthDecode filter\n"); } if (maskUseCompressed) { maskFilters->append(s); } if (s) { delete s; } if (mode == psModeForm || inType3Char || preload) { writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", ref->getRefNum(), ref->getRefGen()); } else { writePS("currentfile\n"); writePS(maskFilters->getCString()); writePS("pdfMask\n"); // add the ColorKeyToMask filter if (maskColors && colorMap && level == psLevel3Gray) { maskStr = new ColorKeyToMaskEncoder(str, width, height, colorMap, maskColors); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (maskUseCompressed) { maskStr = maskStr->getUndecodedStream(); } if (maskUseLZW) { maskStr = new LZWEncoder(maskStr); } else if (maskUseRLE) { maskStr = new RunLengthEncoder(maskStr); } if (maskUseASCII) { if (useASCIIHex) { maskStr = new ASCIIHexEncoder(maskStr); } else { maskStr = new ASCII85Encoder(maskStr); } } // copy the stream data maskStr->reset(); while ((n = maskStr->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } maskStr->close(); writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (maskUseLZW || maskUseRLE || maskUseASCII) { delete maskStr; } } } // color space if (colorMap && level != psLevel3Gray) { dumpColorSpaceL2(state, colorMap->getColorSpace(), gFalse, gTrue, gFalse); writePS(" setcolorspace\n"); } // set up the image data if (mode == psModeForm || inType3Char || preload) { if (inlineImg) { // create an array str2 = new FixedLengthEncoder(str, len); if (colorMap && level == psLevel3Gray) { str2 = new GrayRecoder(str2, width, height, colorMap); } if (globalParams->getPSLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); } if (useASCIIHex) { str2 = new ASCIIHexEncoder(str2); } else { str2 = new ASCII85Encoder(str2); } str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } if (c == 'z') { writePSChar((char)c); ++col; } else { writePSChar((char)c); ++col; for (i = 1; i <= (useASCIIHex ? 1 : 4); ++i) { do { c = str2->getChar(); } while (c == '\n' || c == '\r'); if (c == (useASCIIHex ? '>' : '~') || c == EOF) { break; } writePSChar((char)c); ++col; } } // each line is: "<~...data...~>" // so max data length = 255 - 6 = 249 // chunks are 1 or 5 bytes each, so we have to stop at 245 // but make it 240 just to be safe if (col > 240) { writePS((char *)(useASCIIHex ? ">\n<" : "~>\n<~")); col = 0; } } while (c != (useASCIIHex ? '>' : '~') && c != EOF); writePS((char *)(useASCIIHex ? ">\n" : "~>\n")); // add an extra entry because the LZWDecode/RunLengthDecode // filter may read past the end writePS("<>]\n"); writePS("0\n"); str2->close(); delete str2; } else { // set up to use the array already created by setupImages() writePSFmt("ImData_{0:d}_{1:d} 0\n", ref->getRefNum(), ref->getRefGen()); } } // explicit masking if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { writePS("<<\n /ImageType 3\n"); writePS(" /InterleaveType 3\n"); writePS(" /DataDict\n"); } // image (data) dictionary writePSFmt("<<\n /ImageType {0:d}\n", (maskColors && colorMap && level != psLevel3Gray) ? 4 : 1); // color key masking if (maskColors && colorMap && level != psLevel3Gray) { writePS(" /MaskColor [\n"); numComps = colorMap->getNumPixelComps(); for (i = 0; i < 2 * numComps; i += 2) { writePSFmt(" {0:d} {1:d}\n", maskColors[i], maskColors[i+1]); } writePS(" ]\n"); } // width, height, matrix, bits per component writePSFmt(" /Width {0:d}\n", width); writePSFmt(" /Height {0:d}\n", height); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", width, -height, height); if (colorMap && level == psLevel3Gray) { writePS(" /BitsPerComponent 8\n"); } else { writePSFmt(" /BitsPerComponent {0:d}\n", colorMap ? colorMap->getBits() : 1); } // decode if (colorMap) { writePS(" /Decode ["); if (level == psLevel3Sep && colorMap->getColorSpace()->getMode() == csSeparation) { // this matches up with the code in the pdfImSep operator n = (1 << colorMap->getBits()) - 1; writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(0) * n, colorMap->getDecodeHigh(0) * n); } else if (level == psLevel3Gray) { writePS("0 1"); } else { numComps = colorMap->getNumPixelComps(); for (i = 0; i < numComps; ++i) { if (i > 0) { writePS(" "); } writePSFmt("{0:.4g} {1:.4g}", colorMap->getDecodeLow(i), colorMap->getDecodeHigh(i)); } } writePS("]\n"); } else { writePSFmt(" /Decode [{0:d} {1:d}]\n", invert ? 1 : 0, invert ? 0 : 1); } // data source if (mode == psModeForm || inType3Char || preload) { writePS(" /DataSource { pdfImStr }\n"); } else { writePS(" /DataSource currentfile\n"); } // filters if ((mode == psModeForm || inType3Char || preload) && globalParams->getPSUncompressPreloadedImages()) { s = NULL; useLZW = useRLE = gFalse; useCompressed = gFalse; useASCII = gFalse; } else { s = str->getPSFilter(3, " "); if ((colorMap && level == psLevel3Gray) || inlineImg || !s) { if (globalParams->getPSLZW()) { useLZW = gTrue; useRLE = gFalse; } else { useRLE = gTrue; useLZW = gFalse; } useASCII = !(mode == psModeForm || inType3Char || preload); useCompressed = gFalse; } else { useLZW = useRLE = gFalse; useASCII = str->isBinary() && !(mode == psModeForm || inType3Char || preload); useCompressed = gTrue; } } if (useASCII) { writePSFmt(" /ASCII{0:s}Decode filter\n", useASCIIHex ? "Hex" : "85"); } if (useLZW) { writePS(" /LZWDecode filter\n"); } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } if (useCompressed) { writePS(s->getCString()); } if (s) { delete s; } // end of image (data) dictionary writePS(">>\n"); // explicit masking if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { writePS(" /MaskDict\n"); writePS("<<\n"); writePS(" /ImageType 1\n"); writePSFmt(" /Width {0:d}\n", maskWidth); writePSFmt(" /Height {0:d}\n", maskHeight); writePSFmt(" /ImageMatrix [{0:d} 0 0 {1:d} 0 {2:d}]\n", maskWidth, -maskHeight, maskHeight); writePS(" /BitsPerComponent 1\n"); writePSFmt(" /Decode [{0:d} {1:d}]\n", maskInvert ? 1 : 0, maskInvert ? 0 : 1); // mask data source if (mode == psModeForm || inType3Char || preload) { writePS(" /DataSource {pdfMaskSrc}\n"); writePS(maskFilters->getCString()); } else { writePS(" /DataSource maskStream\n"); } delete maskFilters; writePS(">>\n"); writePS(">>\n"); } if (mode == psModeForm || inType3Char || preload) { // image command writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask"); } else { if (level == psLevel3Sep && colorMap && colorMap->getColorSpace()->getMode() == csSeparation) { color.c[0] = gfxColorComp1; sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace(); sepCS->getCMYK(&color, &cmyk, state->getRenderingIntent()); writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t}) pdfImSep\n", colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k), sepCS->getName()); } else { writePSFmt("{0:s}\n", colorMap ? "pdfIm" : "pdfImM"); } } // get rid of the array and index if (mode == psModeForm || inType3Char || preload) { writePS("pop pop\n"); // image data } else { // cut off inline image streams at appropriate length if (inlineImg) { str = new FixedLengthEncoder(str, len); } else if (useCompressed) { str = str->getUndecodedStream(); } // recode to grayscale if (colorMap && level == psLevel3Gray) { str = new GrayRecoder(str, width, height, colorMap); } // add LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); } if (useASCII) { if (useASCIIHex) { str = new ASCIIHexEncoder(str); } else { str = new ASCII85Encoder(str); } } // copy the stream data str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); // add newline and trailer to the end writePSChar('\n'); writePS("%-EOD-\n"); // delete encoders if (useLZW || useRLE || useASCII || inlineImg) { delete str; } } // close the mask stream if (maskStr || (maskColors && colorMap && level == psLevel3Gray)) { if (!(mode == psModeForm || inType3Char || preload)) { writePS("pdfMaskEnd\n"); } } } void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, GBool genXform, GBool updateColors, GBool map01) { switch (colorSpace->getMode()) { case csDeviceGray: dumpDeviceGrayColorSpace((GfxDeviceGrayColorSpace *)colorSpace, genXform, updateColors, map01); break; case csCalGray: dumpCalGrayColorSpace((GfxCalGrayColorSpace *)colorSpace, genXform, updateColors, map01); break; case csDeviceRGB: dumpDeviceRGBColorSpace((GfxDeviceRGBColorSpace *)colorSpace, genXform, updateColors, map01); break; case csCalRGB: dumpCalRGBColorSpace((GfxCalRGBColorSpace *)colorSpace, genXform, updateColors, map01); break; case csDeviceCMYK: dumpDeviceCMYKColorSpace((GfxDeviceCMYKColorSpace *)colorSpace, genXform, updateColors, map01); break; case csLab: dumpLabColorSpace((GfxLabColorSpace *)colorSpace, genXform, updateColors, map01); break; case csICCBased: dumpICCBasedColorSpace(state, (GfxICCBasedColorSpace *)colorSpace, genXform, updateColors, map01); break; case csIndexed: dumpIndexedColorSpace(state, (GfxIndexedColorSpace *)colorSpace, genXform, updateColors, map01); break; case csSeparation: dumpSeparationColorSpace(state, (GfxSeparationColorSpace *)colorSpace, genXform, updateColors, map01); break; case csDeviceN: if (level >= psLevel3) { dumpDeviceNColorSpaceL3(state, (GfxDeviceNColorSpace *)colorSpace, genXform, updateColors, map01); } else { dumpDeviceNColorSpaceL2(state, (GfxDeviceNColorSpace *)colorSpace, genXform, updateColors, map01); } break; case csPattern: //~ unimplemented break; } } void PSOutputDev::dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("/DeviceGray"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } } void PSOutputDev::dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("[/CIEBasedA <<\n"); writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", cs->getGamma()); writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessBlack; } } void PSOutputDev::dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("/DeviceRGB"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("[/CIEBasedABC <<\n"); writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n", cs->getGammaR(), cs->getGammaG(), cs->getGammaB()); writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n", cs->getMatrix()[0], cs->getMatrix()[1], cs->getMatrix()[2], cs->getMatrix()[3], cs->getMatrix()[4], cs->getMatrix()[5], cs->getMatrix()[6], cs->getMatrix()[7], cs->getMatrix()[8]); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("/DeviceCMYK"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpLabColorSpace(GfxLabColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("[/CIEBasedABC <<\n"); if (map01) { writePS(" /RangeABC [0 1 0 1 0 1]\n"); writePSFmt(" /DecodeABC [{{100 mul 16 add 116 div}} bind {{{0:.4g} mul {1:.4g} add}} bind {{{2:.4g} mul {3:.4g} add}} bind]\n", (cs->getAMax() - cs->getAMin()) / 500.0, cs->getAMin() / 500.0, (cs->getBMax() - cs->getBMin()) / 200.0, cs->getBMin() / 200.0); } else { writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n", cs->getAMin(), cs->getAMax(), cs->getBMin(), cs->getBMax()); writePS(" /DecodeABC [{16 add 116 div} bind {500 div} bind {200 div} bind]\n"); } writePS(" /MatrixABC [1 1 1 1 0 0 0 0 -1]\n"); writePS(" /DecodeLMN\n"); writePS(" [{dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", cs->getWhiteX()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind\n", cs->getWhiteY()); writePS(" {dup 6 29 div ge {dup dup mul mul}\n"); writePSFmt(" {{4 29 div sub 108 841 div mul }} ifelse {0:.4g} mul}} bind]\n", cs->getWhiteZ()); writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getWhiteX(), cs->getWhiteY(), cs->getWhiteZ()); writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n", cs->getBlackX(), cs->getBlackY(), cs->getBlackZ()); writePS(">>]"); if (genXform) { writePS(" {}"); } if (updateColors) { processColors |= psProcessCMYK; } } void PSOutputDev::dumpICCBasedColorSpace(GfxState *state, GfxICCBasedColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { // there is no transform function to the alternate color space, so // we can use it directly dumpColorSpaceL2(state, cs->getAlt(), genXform, updateColors, gFalse); } void PSOutputDev::dumpIndexedColorSpace(GfxState *state, GfxIndexedColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { GfxColorSpace *baseCS; GfxLabColorSpace *labCS; Guchar *lookup, *p; double x[gfxColorMaxComps], y[gfxColorMaxComps]; double low[gfxColorMaxComps], range[gfxColorMaxComps]; GfxColor color; GfxCMYK cmyk; Function *func; int n, numComps, numAltComps; int byte; int i, j, k; baseCS = cs->getBase(); writePS("[/Indexed "); dumpColorSpaceL2(state, baseCS, gFalse, updateColors, gTrue); n = cs->getIndexHigh(); numComps = baseCS->getNComps(); lookup = cs->getLookup(); writePSFmt(" {0:d} <\n", n); if (baseCS->getMode() == csDeviceN && level < psLevel3) { func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc(); baseCS->getDefaultRanges(low, range, cs->getIndexHigh()); if (((GfxDeviceNColorSpace *)baseCS)->getAlt()->getMode() == csLab) { labCS = (GfxLabColorSpace *)((GfxDeviceNColorSpace *)baseCS)->getAlt(); } else { labCS = NULL; } numAltComps = ((GfxDeviceNColorSpace *)baseCS)->getAlt()->getNComps(); p = lookup; for (i = 0; i <= n; i += 8) { writePS(" "); for (j = i; j < i+8 && j <= n; ++j) { for (k = 0; k < numComps; ++k) { x[k] = low[k] + (*p++ / 255.0) * range[k]; } func->transform(x, y); if (labCS) { y[0] /= 100.0; y[1] = (y[1] - labCS->getAMin()) / (labCS->getAMax() - labCS->getAMin()); y[2] = (y[2] - labCS->getBMin()) / (labCS->getBMax() - labCS->getBMin()); } for (k = 0; k < numAltComps; ++k) { byte = (int)(y[k] * 255 + 0.5); if (byte < 0) { byte = 0; } else if (byte > 255) { byte = 255; } writePSFmt("{0:02x}", byte); } if (updateColors) { color.c[0] = dblToCol(j); cs->getCMYK(&color, &cmyk, state->getRenderingIntent()); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } writePS("\n"); } } else { for (i = 0; i <= n; i += 8) { writePS(" "); for (j = i; j < i+8 && j <= n; ++j) { for (k = 0; k < numComps; ++k) { writePSFmt("{0:02x}", lookup[j * numComps + k]); } if (updateColors) { color.c[0] = dblToCol(j); cs->getCMYK(&color, &cmyk, state->getRenderingIntent()); addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m), colToDbl(cmyk.y), colToDbl(cmyk.k)); } } writePS("\n"); } } writePS(">]"); if (genXform) { writePS(" {}"); } } void PSOutputDev::dumpSeparationColorSpace(GfxState *state, GfxSeparationColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { writePS("[/Separation "); writePSString(cs->getName()); writePS(" "); dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse); writePS("\n"); cvtFunction(cs->getFunc()); writePS("]"); if (genXform) { writePS(" {}"); } if (updateColors) { addCustomColor(state, cs); } } void PSOutputDev::dumpDeviceNColorSpaceL2(GfxState *state, GfxDeviceNColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { dumpColorSpaceL2(state, cs->getAlt(), gFalse, updateColors, map01); if (genXform) { writePS(" "); cvtFunction(cs->getTintTransformFunc()); } } void PSOutputDev::dumpDeviceNColorSpaceL3(GfxState *state, GfxDeviceNColorSpace *cs, GBool genXform, GBool updateColors, GBool map01) { GString *tint; int i; writePS("[/DeviceN [\n"); for (i = 0; i < cs->getNComps(); ++i) { writePSString(cs->getColorantName(i)); writePS("\n"); } writePS("]\n"); if ((tint = createDeviceNTintFunc(cs))) { writePS("/DeviceCMYK\n"); writePS(tint->getCString()); delete tint; } else { dumpColorSpaceL2(state, cs->getAlt(), gFalse, gFalse, gFalse); writePS("\n"); cvtFunction(cs->getTintTransformFunc()); } writePS("]"); if (genXform) { writePS(" {}"); } if (updateColors) { addCustomColors(state, cs); } } // If the DeviceN color space has a Colorants dictionary, and all of // the colorants are one of: "None", "Cyan", "Magenta", "Yellow", // "Black", or have an entry in the Colorants dict that maps to // DeviceCMYK, then build a new tint function; else use the existing // tint function. GString *PSOutputDev::createDeviceNTintFunc(GfxDeviceNColorSpace *cs) { Object *attrs; Object colorants, sepCSObj, funcObj, obj1; GString *name; Function *func; double sepIn; double cmyk[gfxColorMaxComps][4]; GString *tint; GBool first; int i, j; attrs = cs->getAttrs(); if (!attrs->isDict()) { return NULL; } if (!attrs->dictLookup("Colorants", &colorants)->isDict()) { colorants.free(); return NULL; } for (i = 0; i < cs->getNComps(); ++i) { name = cs->getColorantName(i); if (!name->cmp("None")) { cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0; } else if (!name->cmp("Cyan")) { cmyk[i][1] = cmyk[i][2] = cmyk[i][3] = 0; cmyk[i][0] = 1; } else if (!name->cmp("Magenta")) { cmyk[i][0] = cmyk[i][2] = cmyk[i][3] = 0; cmyk[i][1] = 1; } else if (!name->cmp("Yellow")) { cmyk[i][0] = cmyk[i][1] = cmyk[i][3] = 0; cmyk[i][2] = 1; } else if (!name->cmp("Black")) { cmyk[i][0] = cmyk[i][1] = cmyk[i][2] = 0; cmyk[i][3] = 1; } else { colorants.dictLookup(name->getCString(), &sepCSObj); if (!sepCSObj.isArray() || sepCSObj.arrayGetLength() != 4) { sepCSObj.free(); colorants.free(); return NULL; } if (!sepCSObj.arrayGet(0, &obj1)->isName("Separation")) { obj1.free(); sepCSObj.free(); colorants.free(); return NULL; } obj1.free(); if (!sepCSObj.arrayGet(2, &obj1)->isName("DeviceCMYK")) { obj1.free(); sepCSObj.free(); colorants.free(); return NULL; } obj1.free(); sepCSObj.arrayGet(3, &funcObj); if (!(func = Function::parse(&funcObj))) { funcObj.free(); sepCSObj.free(); colorants.free(); return NULL; } funcObj.free(); if (func->getInputSize() != 1 || func->getOutputSize() != 4) { delete func; sepCSObj.free(); colorants.free(); return NULL; } sepIn = 1; func->transform(&sepIn, cmyk[i]); delete func; sepCSObj.free(); } } colorants.free(); tint = new GString(); tint->append("{\n"); for (j = 0; j < 4; ++j) { // C, M, Y, K first = gTrue; for (i = 0; i < cs->getNComps(); ++i) { if (cmyk[i][j] != 0) { tint->appendf("{0:d} index {1:.4f} mul{2:s}\n", j + cs->getNComps() - 1 - i, cmyk[i][j], first ? "" : " add"); first = gFalse; } } if (first) { tint->append("0\n"); } } tint->appendf("{0:d} 4 roll\n", cs->getNComps() + 4); for (i = 0; i < cs->getNComps(); ++i) { tint->append("pop\n"); } tint->append("}\n"); return tint; } #if OPI_SUPPORT void PSOutputDev::opiBegin(GfxState *state, Dict *opiDict) { Object dict; if (globalParams->getPSOPI()) { opiDict->lookup("2.0", &dict); if (dict.isDict()) { opiBegin20(state, dict.getDict()); dict.free(); } else { dict.free(); opiDict->lookup("1.3", &dict); if (dict.isDict()) { opiBegin13(state, dict.getDict()); } dict.free(); } } } void PSOutputDev::opiBegin20(GfxState *state, Dict *dict) { Object obj1, obj2, obj3, obj4; double width, height, left, right, top, bottom; int w, h; int i; writePS("%%BeginOPI: 2.0\n"); writePS("%%Distilled\n"); dict->lookup("F", &obj1); if (getFileSpec(&obj1, &obj2)) { writePSFmt("%%ImageFileName: {0:t}\n", obj2.getString()); obj2.free(); } obj1.free(); dict->lookup("MainImage", &obj1); if (obj1.isString()) { writePSFmt("%%MainImage: {0:t}\n", obj1.getString()); } obj1.free(); //~ ignoring 'Tags' entry //~ need to use writePSString() and deal with >255-char lines dict->lookup("Size", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); width = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); height = obj2.getNum(); obj2.free(); writePSFmt("%%ImageDimensions: {0:.6g} {1:.6g}\n", width, height); } obj1.free(); dict->lookup("CropRect", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 4) { obj1.arrayGet(0, &obj2); left = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); top = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); right = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); bottom = obj2.getNum(); obj2.free(); writePSFmt("%%ImageCropRect: {0:.6g} {1:.6g} {2:.6g} {3:.6g}\n", left, top, right, bottom); } obj1.free(); dict->lookup("Overprint", &obj1); if (obj1.isBool()) { writePSFmt("%%ImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1.free(); dict->lookup("Inks", &obj1); if (obj1.isName()) { writePSFmt("%%ImageInks: {0:s}\n", obj1.getName()); } else if (obj1.isArray() && obj1.arrayGetLength() >= 1) { obj1.arrayGet(0, &obj2); if (obj2.isName()) { writePSFmt("%%ImageInks: {0:s} {1:d}", obj2.getName(), (obj1.arrayGetLength() - 1) / 2); for (i = 1; i+1 < obj1.arrayGetLength(); i += 2) { obj1.arrayGet(i, &obj3); obj1.arrayGet(i+1, &obj4); if (obj3.isString() && obj4.isNum()) { writePS(" "); writePSString(obj3.getString()); writePSFmt(" {0:.4g}", obj4.getNum()); } obj3.free(); obj4.free(); } writePS("\n"); } obj2.free(); } obj1.free(); writePS("gsave\n"); writePS("%%BeginIncludedImage\n"); dict->lookup("IncludedImageDimensions", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); w = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); h = obj2.getInt(); obj2.free(); writePSFmt("%%IncludedImageDimensions: {0:d} {1:d}\n", w, h); } obj1.free(); dict->lookup("IncludedImageQuality", &obj1); if (obj1.isNum()) { writePSFmt("%%IncludedImageQuality: {0:.4g}\n", obj1.getNum()); } obj1.free(); ++opi20Nest; } void PSOutputDev::opiBegin13(GfxState *state, Dict *dict) { Object obj1, obj2; int left, right, top, bottom, samples, bits, width, height; double c, m, y, k; double llx, lly, ulx, uly, urx, ury, lrx, lry; double tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry; double horiz, vert; int i, j; writePS("save\n"); writePS("/opiMatrix2 matrix currentmatrix def\n"); writePS("opiMatrix setmatrix\n"); dict->lookup("F", &obj1); if (getFileSpec(&obj1, &obj2)) { writePSFmt("%ALDImageFileName: {0:t}\n", obj2.getString()); obj2.free(); } obj1.free(); dict->lookup("CropRect", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 4) { obj1.arrayGet(0, &obj2); left = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); top = obj2.getInt(); obj2.free(); obj1.arrayGet(2, &obj2); right = obj2.getInt(); obj2.free(); obj1.arrayGet(3, &obj2); bottom = obj2.getInt(); obj2.free(); writePSFmt("%ALDImageCropRect: {0:d} {1:d} {2:d} {3:d}\n", left, top, right, bottom); } obj1.free(); dict->lookup("Color", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 5) { obj1.arrayGet(0, &obj2); c = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); m = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); y = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); k = obj2.getNum(); obj2.free(); obj1.arrayGet(4, &obj2); if (obj2.isString()) { writePSFmt("%ALDImageColor: {0:.4g} {1:.4g} {2:.4g} {3:.4g} ", c, m, y, k); writePSString(obj2.getString()); writePS("\n"); } obj2.free(); } obj1.free(); dict->lookup("ColorType", &obj1); if (obj1.isName()) { writePSFmt("%ALDImageColorType: {0:s}\n", obj1.getName()); } obj1.free(); //~ ignores 'Comments' entry //~ need to handle multiple lines dict->lookup("CropFixed", &obj1); if (obj1.isArray()) { obj1.arrayGet(0, &obj2); ulx = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); uly = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); lrx = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); lry = obj2.getNum(); obj2.free(); writePSFmt("%ALDImageCropFixed: {0:.4g} {1:.4g} {2:.4g} {3:.4g}\n", ulx, uly, lrx, lry); } obj1.free(); dict->lookup("GrayMap", &obj1); if (obj1.isArray()) { writePS("%ALDImageGrayMap:"); for (i = 0; i < obj1.arrayGetLength(); i += 16) { if (i > 0) { writePS("\n%%+"); } for (j = 0; j < 16 && i+j < obj1.arrayGetLength(); ++j) { obj1.arrayGet(i+j, &obj2); writePSFmt(" {0:d}", obj2.getInt()); obj2.free(); } } writePS("\n"); } obj1.free(); dict->lookup("ID", &obj1); if (obj1.isString()) { writePSFmt("%ALDImageID: {0:t}\n", obj1.getString()); } obj1.free(); dict->lookup("ImageType", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); samples = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); bits = obj2.getInt(); obj2.free(); writePSFmt("%ALDImageType: {0:d} {1:d}\n", samples, bits); } obj1.free(); dict->lookup("Overprint", &obj1); if (obj1.isBool()) { writePSFmt("%ALDImageOverprint: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1.free(); dict->lookup("Position", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 8) { obj1.arrayGet(0, &obj2); llx = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); lly = obj2.getNum(); obj2.free(); obj1.arrayGet(2, &obj2); ulx = obj2.getNum(); obj2.free(); obj1.arrayGet(3, &obj2); uly = obj2.getNum(); obj2.free(); obj1.arrayGet(4, &obj2); urx = obj2.getNum(); obj2.free(); obj1.arrayGet(5, &obj2); ury = obj2.getNum(); obj2.free(); obj1.arrayGet(6, &obj2); lrx = obj2.getNum(); obj2.free(); obj1.arrayGet(7, &obj2); lry = obj2.getNum(); obj2.free(); opiTransform(state, llx, lly, &tllx, &tlly); opiTransform(state, ulx, uly, &tulx, &tuly); opiTransform(state, urx, ury, &turx, &tury); opiTransform(state, lrx, lry, &tlrx, &tlry); writePSFmt("%ALDImagePosition: {0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g}\n", tllx, tlly, tulx, tuly, turx, tury, tlrx, tlry); obj2.free(); } obj1.free(); dict->lookup("Resolution", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); horiz = obj2.getNum(); obj2.free(); obj1.arrayGet(1, &obj2); vert = obj2.getNum(); obj2.free(); writePSFmt("%ALDImageResoution: {0:.4g} {1:.4g}\n", horiz, vert); obj2.free(); } obj1.free(); dict->lookup("Size", &obj1); if (obj1.isArray() && obj1.arrayGetLength() == 2) { obj1.arrayGet(0, &obj2); width = obj2.getInt(); obj2.free(); obj1.arrayGet(1, &obj2); height = obj2.getInt(); obj2.free(); writePSFmt("%ALDImageDimensions: {0:d} {1:d}\n", width, height); } obj1.free(); //~ ignoring 'Tags' entry //~ need to use writePSString() and deal with >255-char lines dict->lookup("Tint", &obj1); if (obj1.isNum()) { writePSFmt("%ALDImageTint: {0:.4g}\n", obj1.getNum()); } obj1.free(); dict->lookup("Transparency", &obj1); if (obj1.isBool()) { writePSFmt("%ALDImageTransparency: {0:s}\n", obj1.getBool() ? "true" : "false"); } obj1.free(); writePS("%%BeginObject: image\n"); writePS("opiMatrix2 setmatrix\n"); ++opi13Nest; } // Convert PDF user space coordinates to PostScript default user space // coordinates. This has to account for both the PDF CTM and the // PSOutputDev page-fitting transform. void PSOutputDev::opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1) { double t; state->transform(x0, y0, x1, y1); *x1 += tx; *y1 += ty; if (rotate == 90) { t = *x1; *x1 = -*y1; *y1 = t; } else if (rotate == 180) { *x1 = -*x1; *y1 = -*y1; } else if (rotate == 270) { t = *x1; *x1 = *y1; *y1 = -t; } *x1 *= xScale; *y1 *= yScale; } void PSOutputDev::opiEnd(GfxState *state, Dict *opiDict) { Object dict; if (globalParams->getPSOPI()) { opiDict->lookup("2.0", &dict); if (dict.isDict()) { writePS("%%EndIncludedImage\n"); writePS("%%EndOPI\n"); writePS("grestore\n"); --opi20Nest; dict.free(); } else { dict.free(); opiDict->lookup("1.3", &dict); if (dict.isDict()) { writePS("%%EndObject\n"); writePS("restore\n"); --opi13Nest; } dict.free(); } } } GBool PSOutputDev::getFileSpec(Object *fileSpec, Object *fileName) { if (fileSpec->isString()) { fileSpec->copy(fileName); return gTrue; } if (fileSpec->isDict()) { fileSpec->dictLookup("DOS", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); fileSpec->dictLookup("Mac", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); fileSpec->dictLookup("Unix", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); fileSpec->dictLookup("F", fileName); if (fileName->isString()) { return gTrue; } fileName->free(); } return gFalse; } #endif // OPI_SUPPORT void PSOutputDev::type3D0(GfxState *state, double wx, double wy) { writePSFmt("{0:.6g} {1:.6g} setcharwidth\n", wx, wy); writePS("q\n"); t3NeedsRestore = gTrue; noStateChanges = gFalse; } void PSOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { if (t3String) { error(errSyntaxError, -1, "Multiple 'd1' operators in Type 3 CharProc"); return; } t3WX = wx; t3WY = wy; t3LLX = llx; t3LLY = lly; t3URX = urx; t3URY = ury; t3String = new GString(); writePS("q\n"); t3FillColorOnly = gTrue; t3Cacheable = gTrue; t3NeedsRestore = gTrue; noStateChanges = gFalse; } void PSOutputDev::drawForm(Ref id) { writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen); noStateChanges = gFalse; } void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) { Stream *str; char buf[4096]; int n; if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) { str = level1Stream; } else { str = psStream; } str->reset(); while ((n = str->getBlock(buf, sizeof(buf))) > 0) { writePSBlock(buf, n); } str->close(); noStateChanges = gFalse; } //~ can nextFunc be reset to 0 -- maybe at the start of each page? //~ or maybe at the start of each color space / pattern? void PSOutputDev::cvtFunction(Function *func) { SampledFunction *func0; ExponentialFunction *func2; StitchingFunction *func3; PostScriptFunction *func4; int thisFunc, m, n, nSamples, i, j, k; switch (func->getType()) { case -1: // identity writePS("{}\n"); break; case 0: // sampled func0 = (SampledFunction *)func; thisFunc = nextFunc++; m = func0->getInputSize(); n = func0->getOutputSize(); nSamples = n; for (i = 0; i < m; ++i) { nSamples *= func0->getSampleSize(i); } writePSFmt("/xpdfSamples{0:d} [\n", thisFunc); for (i = 0; i < nSamples; ++i) { writePSFmt("{0:.6g}\n", func0->getSamples()[i]); } writePS("] def\n"); writePSFmt("{{ {0:d} array {1:d} array {2:d} 2 roll\n", 2*m, m, m+2); // [e01] [efrac] x0 x1 ... xm-1 for (i = m-1; i >= 0; --i) { // [e01] [efrac] x0 x1 ... xi writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add\n", func0->getDomainMin(i), (func0->getEncodeMax(i) - func0->getEncodeMin(i)) / (func0->getDomainMax(i) - func0->getDomainMin(i)), func0->getEncodeMin(i)); // [e01] [efrac] x0 x1 ... xi-1 xi' writePSFmt("dup 0 lt {{ pop 0 }} {{ dup {0:d} gt {{ pop {1:d} }} if }} ifelse\n", func0->getSampleSize(i) - 1, func0->getSampleSize(i) - 1); // [e01] [efrac] x0 x1 ... xi-1 xi' writePS("dup floor cvi exch dup ceiling cvi exch 2 index sub\n"); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') xi'-floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, i); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') ceiling(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+3, 2*i+1); // [e01] [efrac] x0 x1 ... xi-1 floor(xi') writePSFmt("{0:d} index {1:d} 3 2 roll put\n", i+2, 2*i); // [e01] [efrac] x0 x1 ... xi-1 } // [e01] [efrac] for (i = 0; i < n; ++i) { // [e01] [efrac] y(0) ... y(i-1) for (j = 0; j < (1<> k) & 1)); for (k = m - 2; k >= 0; --k) { writePSFmt("{0:d} mul {1:d} index {2:d} get add\n", func0->getSampleSize(k), i + j + 3, 2 * k + ((j >> k) & 1)); } if (n > 1) { writePSFmt("{0:d} mul {1:d} add ", n, i); } writePS("get\n"); } // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^m-1) for (j = 0; j < m; ++j) { // [e01] [efrac] y(0) ... y(i-1) s(0) s(1) ... s(2^(m-j)-1) for (k = 0; k < (1 << (m - j)); k += 2) { // [e01] [efrac] y(0) ... y(i-1) <2^(m-j)-k s values> writePSFmt("{0:d} index {1:d} get dup\n", i + k/2 + (1 << (m-j)) - k, j); writePS("3 2 roll mul exch 1 exch sub 3 2 roll mul add\n"); writePSFmt("{0:d} 1 roll\n", k/2 + (1 << (m-j)) - k - 1); } // [e01] [efrac] s'(0) s'(1) ... s(2^(m-j-1)-1) } // [e01] [efrac] y(0) ... y(i-1) s writePSFmt("{0:.6g} mul {1:.6g} add\n", func0->getDecodeMax(i) - func0->getDecodeMin(i), func0->getDecodeMin(i)); writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func0->getRangeMin(i), func0->getRangeMin(i), func0->getRangeMax(i), func0->getRangeMax(i)); // [e01] [efrac] y(0) ... y(i-1) y(i) } // [e01] [efrac] y(0) ... y(n-1) writePSFmt("{0:d} {1:d} roll pop pop }}\n", n+2, n); break; case 2: // exponential func2 = (ExponentialFunction *)func; n = func2->getOutputSize(); writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getDomainMin(0), func2->getDomainMin(0), func2->getDomainMax(0), func2->getDomainMax(0)); // x for (i = 0; i < n; ++i) { // x y(0) .. y(i-1) writePSFmt("{0:d} index {1:.6g} exp {2:.6g} mul {3:.6g} add\n", i, func2->getE(), func2->getC1()[i] - func2->getC0()[i], func2->getC0()[i]); if (func2->getHasRange()) { writePSFmt("dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getRangeMin(i), func2->getRangeMin(i), func2->getRangeMax(i), func2->getRangeMax(i)); } } // x y(0) .. y(n-1) writePSFmt("{0:d} {1:d} roll pop }}\n", n+1, n); break; case 3: // stitching func3 = (StitchingFunction *)func; thisFunc = nextFunc++; for (i = 0; i < func3->getNumFuncs(); ++i) { cvtFunction(func3->getFunc(i)); writePSFmt("/xpdfFunc{0:d}_{1:d} exch def\n", thisFunc, i); } writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func3->getDomainMin(0), func3->getDomainMin(0), func3->getDomainMax(0), func3->getDomainMax(0)); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePSFmt("dup {0:.6g} lt {{ {1:.6g} sub {2:.6g} mul {3:.6g} add xpdfFunc{4:d}_{5:d} }} {{\n", func3->getBounds()[i+1], func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2*i], thisFunc, i); } writePSFmt("{0:.6g} sub {1:.6g} mul {2:.6g} add xpdfFunc{3:d}_{4:d}\n", func3->getBounds()[i], func3->getScale()[i], func3->getEncode()[2*i], thisFunc, i); for (i = 0; i < func3->getNumFuncs() - 1; ++i) { writePS("} ifelse\n"); } writePS("}\n"); break; case 4: // PostScript func4 = (PostScriptFunction *)func; writePS(func4->getCodeString()->getCString()); writePS("\n"); break; } } void PSOutputDev::writePSChar(char c) { if (t3String) { t3String->append(c); } else { (*outputFunc)(outputStream, &c, 1); } } void PSOutputDev::writePSBlock(char *s, int len) { if (t3String) { t3String->append(s, len); } else { (*outputFunc)(outputStream, s, len); } } void PSOutputDev::writePS(const char *s) { if (t3String) { t3String->append(s); } else { (*outputFunc)(outputStream, s, (int)strlen(s)); } } void PSOutputDev::writePSFmt(const char *fmt, ...) { va_list args; GString *buf; va_start(args, fmt); if (t3String) { t3String->appendfv((char *)fmt, args); } else { buf = GString::formatv((char *)fmt, args); (*outputFunc)(outputStream, buf->getCString(), buf->getLength()); delete buf; } va_end(args); } void PSOutputDev::writePSString(GString *s) { Guchar *p; int n, line; char buf[8]; writePSChar('('); line = 1; for (p = (Guchar *)s->getCString(), n = s->getLength(); n; ++p, --n) { if (line >= 64) { writePSChar('\\'); writePSChar('\n'); line = 0; } if (*p == '(' || *p == ')' || *p == '\\') { writePSChar('\\'); writePSChar((char)*p); line += 2; } else if (*p < 0x20 || *p >= 0x80) { sprintf(buf, "\\%03o", *p); writePS(buf); line += 4; } else { writePSChar((char)*p); ++line; } } writePSChar(')'); } void PSOutputDev::writePSName(const char *s) { const char *p; char c; p = s; while ((c = *p++)) { if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') { writePSFmt("#{0:02x}", c & 0xff); } else { writePSChar(c); } } } GString *PSOutputDev::filterPSName(GString *name) { GString *name2; char buf[8]; int i; char c; name2 = new GString(); // ghostscript chokes on names that begin with out-of-limits // numbers, e.g., 1e4foo is handled correctly (as a name), but // 1e999foo generates a limitcheck error c = name->getChar(0); if (c >= '0' && c <= '9') { name2->append('f'); } for (i = 0; i < name->getLength(); ++i) { c = name->getChar(i); if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') { sprintf(buf, "#%02x", c & 0xff); name2->append(buf); } else { name2->append(c); } } return name2; } // Write a DSC-compliant . void PSOutputDev::writePSTextLine(GString *s) { TextString *ts; Unicode *u; int i, j; int c; // - DSC comments must be printable ASCII; control chars and // backslashes have to be escaped (we do cheap Unicode-to-ASCII // conversion by simply ignoring the high byte) // - lines are limited to 255 chars (we limit to 200 here to allow // for the keyword, which was emitted by the caller) // - lines that start with a left paren are treated as // instead of , so we escape a leading paren ts = new TextString(s); u = ts->getUnicode(); for (i = 0, j = 0; i < ts->getLength() && j < 200; ++i) { c = u[i] & 0xff; if (c == '\\') { writePS("\\\\"); j += 2; } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) { writePSFmt("\\{0:03o}", c); j += 4; } else { writePSChar((char)c); ++j; } } writePS("\n"); delete ts; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10019/PSOutputDev.h000066400000000000000000000534411417746362400232030ustar00rootroot00000000000000//======================================================================== // // PSOutputDev.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef PSOUTPUTDEV_H #define PSOUTPUTDEV_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #include "config.h" #include "Object.h" #include "GlobalParams.h" #include "OutputDev.h" class GHash; class PDFDoc; class XRef; class Function; class GfxPath; class GfxFont; class GfxColorSpace; class GfxDeviceGrayColorSpace; class GfxCalGrayColorSpace; class GfxDeviceRGBColorSpace; class GfxCalRGBColorSpace; class GfxDeviceCMYKColorSpace; class GfxLabColorSpace; class GfxICCBasedColorSpace; class GfxIndexedColorSpace; class GfxSeparationColorSpace; class GfxDeviceNColorSpace; class PDFRectangle; class PSOutCustomColor; class PSOutputDev; class PSFontFileInfo; //------------------------------------------------------------------------ // PSOutputDev //------------------------------------------------------------------------ enum PSOutMode { psModePS, psModeEPS, psModeForm }; enum PSFileType { psFile, // write to file psPipe, // write to pipe psStdout, // write to stdout psGeneric // write to a generic stream }; enum PSOutCustomCodeLocation { psOutCustomDocSetup, psOutCustomPageSetup }; typedef void (*PSOutputFunc)(void *stream, const char *data, int len); typedef GString *(*PSOutCustomCodeCbk)(PSOutputDev *psOut, PSOutCustomCodeLocation loc, int n, void *data); class PSOutputDev : public OutputDev { public: // Open a PostScript output file, and write the prolog. PSOutputDev(char *fileName, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA = 0, int imgLLYA = 0, int imgURXA = 0, int imgURYA = 0, GBool manualCtrlA = gFalse, PSOutCustomCodeCbk customCodeCbkA = NULL, void *customCodeCbkDataA = NULL, GBool honorUserUnitA = gFalse); // Open a PSOutputDev that will write to a generic stream. PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA = 0, int imgLLYA = 0, int imgURXA = 0, int imgURYA = 0, GBool manualCtrlA = gFalse, PSOutCustomCodeCbk customCodeCbkA = NULL, void *customCodeCbkDataA = NULL, GBool honorUserUnitA = gFalse); // Destructor -- writes the trailer and closes the file. virtual ~PSOutputDev(); // Check if file was successfully created. virtual GBool isOk() { return ok; } // Returns false if there have been any errors on the output stream. GBool checkIO(); //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual GBool upsideDown() { return gFalse; } // Does this device use drawChar() or drawString()? virtual GBool useDrawChar() { return gFalse; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. virtual GBool useTilingPatternFill() { return gTrue; } // Does this device use functionShadedFill(), axialShadedFill(), and // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. virtual GBool useShadedFills() { return level == psLevel2 || level == psLevel2Sep || level == psLevel3 || level == psLevel3Sep; } // Does this device use drawForm()? If this returns false, // form-type XObjects will be interpreted (i.e., unrolled). virtual GBool useDrawForm() { return preload; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual GBool interpretType3Chars() { return gFalse; } //----- header/trailer (used only if manualCtrl is true) // Write the document-level header. void writeHeader(PDFRectangle *mediaBox, PDFRectangle *cropBox, int pageRotate); // Write the Xpdf procset. void writeXpdfProcset(); // Write the document-level setup. void writeDocSetup(Catalog *catalog); // Write the trailer for the current page. void writePageTrailer(); // Write the document trailer. void writeTrailer(); //----- initialization and control // Check to see if a page slice should be displayed. If this // returns false, the page display is aborted. Typically, an // OutputDev will use some alternate means to display the page // before returning false. virtual GBool checkPageSlice(Page *page, double hDPI, double vDPI, int rotate, GBool useMediaBox, GBool crop, int sliceX, int sliceY, int sliceW, int sliceH, GBool printing, GBool (*abortCheckCbk)(void *data) = NULL, void *abortCheckCbkData = NULL); // Start a page. virtual void startPage(int pageNum, GfxState *state); // End a page. virtual void endPage(); //----- save/restore graphics state virtual void saveState(GfxState *state); virtual void restoreState(GfxState *state); //----- update graphics state virtual void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32); virtual void updateLineDash(GfxState *state); virtual void updateFlatness(GfxState *state); virtual void updateLineJoin(GfxState *state); virtual void updateLineCap(GfxState *state); virtual void updateMiterLimit(GfxState *state); virtual void updateLineWidth(GfxState *state); virtual void updateFillColorSpace(GfxState *state); virtual void updateStrokeColorSpace(GfxState *state); virtual void updateFillColor(GfxState *state); virtual void updateStrokeColor(GfxState *state); virtual void updateFillOverprint(GfxState *state); virtual void updateStrokeOverprint(GfxState *state); virtual void updateOverprintMode(GfxState *state); virtual void updateTransfer(GfxState *state); //----- update text state virtual void updateFont(GfxState *state); virtual void updateTextMat(GfxState *state); virtual void updateCharSpace(GfxState *state); virtual void updateRender(GfxState *state); virtual void updateRise(GfxState *state); virtual void updateWordSpace(GfxState *state); virtual void updateHorizScaling(GfxState *state); virtual void updateTextPos(GfxState *state); virtual void updateTextShift(GfxState *state, double shift); virtual void saveTextPos(GfxState *state); virtual void restoreTextPos(GfxState *state); //----- path painting virtual void stroke(GfxState *state); virtual void fill(GfxState *state); virtual void eoFill(GfxState *state); virtual void tilingPatternFill(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); virtual GBool functionShadedFill(GfxState *state, GfxFunctionShading *shading); virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading); virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading); //----- path clipping virtual void clip(GfxState *state); virtual void eoClip(GfxState *state); virtual void clipToStrokePath(GfxState *state); //----- text drawing virtual void drawString(GfxState *state, GString *s); virtual void endTextObject(GfxState *state); //----- image drawing virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool inlineImg, GBool interpolate); virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, int *maskColors, GBool inlineImg, GBool interpolate); virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert, GBool interpolate); #if OPI_SUPPORT //----- OPI functions virtual void opiBegin(GfxState *state, Dict *opiDict); virtual void opiEnd(GfxState *state, Dict *opiDict); #endif //----- Type 3 font operators virtual void type3D0(GfxState *state, double wx, double wy); virtual void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury); //----- form XObjects virtual void drawForm(Ref ref); //----- PostScript XObjects virtual void psXObject(Stream *psStream, Stream *level1Stream); //----- miscellaneous void setImageableArea(int imgLLXA, int imgLLYA, int imgURXA, int imgURYA) { imgLLX = imgLLXA; imgLLY = imgLLYA; imgURX = imgURXA; imgURY = imgURYA; } void setOffset(double x, double y) { tx0 = x; ty0 = y; } void setScale(double x, double y) { xScale0 = x; yScale0 = y; } void setRotate(int rotateA) { rotate0 = rotateA; } void setClip(double llx, double lly, double urx, double ury) { clipLLX0 = llx; clipLLY0 = lly; clipURX0 = urx; clipURY0 = ury; } void setUnderlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), void *data) { underlayCbk = cbk; underlayCbkData = data; } void setOverlayCbk(void (*cbk)(PSOutputDev *psOut, void *data), void *data) { overlayCbk = cbk; overlayCbkData = data; } void writePSChar(char c); void writePSBlock(char *s, int len); void writePS(const char *s); void writePSFmt(const char *fmt, ...); void writePSString(GString *s); void writePSName(const char *s); private: void init(PSOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, PDFDoc *docA, int firstPageA, int lastPageA, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, GBool manualCtrlA, GBool honorUserUnitA); GBool checkIfPageNeedsToBeRasterized(int pg); void setupResources(Dict *resDict); void setupFonts(Dict *resDict); void setupFont(GfxFont *font, Dict *parentResDict); PSFontFileInfo *setupEmbeddedType1Font(GfxFont *font, Ref *id); PSFontFileInfo *setupExternalType1Font(GfxFont *font, GString *fileName); PSFontFileInfo *setupEmbeddedType1CFont(GfxFont *font, Ref *id); PSFontFileInfo *setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id); PSFontFileInfo *setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id); PSFontFileInfo *setupExternalTrueTypeFont(GfxFont *font, GString *fileName, int fontNum); PSFontFileInfo *setupEmbeddedCIDType0Font(GfxFont *font, Ref *id); PSFontFileInfo *setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GBool needVerticalMetrics); PSFontFileInfo *setupExternalCIDTrueTypeFont(GfxFont *font, GString *fileName, int fontNum, GBool needVerticalMetrics); PSFontFileInfo *setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id); PSFontFileInfo *setupExternalOpenTypeCFFFont(GfxFont *font, GString *fileName); PSFontFileInfo *setupType3Font(GfxFont *font, Dict *parentResDict); GString *makePSFontName(GfxFont *font, Ref *id); GString *fixType1Font(GString *font, int length1, int length2); GBool splitType1PFA(Guchar *font, int fontSize, int length1, int length2, GString *textSection, GString *binSection); GBool splitType1PFB(Guchar *font, int fontSize, GString *textSection, GString *binSection); GString *asciiHexDecodeType1EexecSection(GString *in); GBool fixType1EexecSection(GString *binSection, GString *out); GString *copyType1PFA(Guchar *font, int fontSize); GString *copyType1PFB(Guchar *font, int fontSize); void renameType1Font(GString *font, GString *name); void setupDefaultFont(); void setupImages(Dict *resDict); void setupImage(Ref id, Stream *str, GBool mask, Array *colorKeyMask); void setupForms(Dict *resDict); void setupForm(Object *strRef, Object *strObj); void addProcessColor(double c, double m, double y, double k); void addCustomColor(GfxState *state, GfxSeparationColorSpace *sepCS); void addCustomColors(GfxState *state, GfxDeviceNColorSpace *devnCS); void tilingPatternFillL1(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); void tilingPatternFillL2(GfxState *state, Gfx *gfx, Object *strRef, int paintType, int tilingType, Dict *resDict, double *mat, double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep); void doPath(GfxPath *path); void doImageL1(Object *ref, GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len); void doImageL1Sep(GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len); void doImageL2(Object *ref, GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert); void convertColorKeyMaskToClipRects(GfxImageColorMap *colorMap, Stream *str, int width, int height, int *maskColors); void convertExplicitMaskToClipRects(Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert); void doImageL3(Object *ref, GfxState *state, GfxImageColorMap *colorMap, GBool invert, GBool inlineImg, Stream *str, int width, int height, int len, int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, GBool maskInvert); void dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, GBool genXform, GBool updateColors, GBool map01); void dumpDeviceGrayColorSpace(GfxDeviceGrayColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpCalGrayColorSpace(GfxCalGrayColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpDeviceRGBColorSpace(GfxDeviceRGBColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpCalRGBColorSpace(GfxCalRGBColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpDeviceCMYKColorSpace(GfxDeviceCMYKColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpLabColorSpace(GfxLabColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpICCBasedColorSpace(GfxState *state, GfxICCBasedColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpIndexedColorSpace(GfxState *state, GfxIndexedColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpSeparationColorSpace(GfxState *state, GfxSeparationColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpDeviceNColorSpaceL2(GfxState *state, GfxDeviceNColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); void dumpDeviceNColorSpaceL3(GfxState *state, GfxDeviceNColorSpace *cs, GBool genXform, GBool updateColors, GBool map01); GString *createDeviceNTintFunc(GfxDeviceNColorSpace *cs); #if OPI_SUPPORT void opiBegin20(GfxState *state, Dict *dict); void opiBegin13(GfxState *state, Dict *dict); void opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1); GBool getFileSpec(Object *fileSpec, Object *fileName); #endif void cvtFunction(Function *func); GString *filterPSName(GString *name); void writePSTextLine(GString *s); PSLevel level; // PostScript level PSOutMode mode; // PostScript mode (PS, EPS, form) int paperWidth; // width of paper, in pts int paperHeight; // height of paper, in pts GBool paperMatch; // true if paper size is set to match each page int imgLLX, imgLLY, // imageable area, in pts imgURX, imgURY; GBool preload; // load all images into memory, and // predefine forms PSOutputFunc outputFunc; void *outputStream; PSFileType fileType; // file / pipe / stdout GBool manualCtrl; int seqPage; // current sequential page number void (*underlayCbk)(PSOutputDev *psOut, void *data); void *underlayCbkData; void (*overlayCbk)(PSOutputDev *psOut, void *data); void *overlayCbkData; GString *(*customCodeCbk)(PSOutputDev *psOut, PSOutCustomCodeLocation loc, int n, void *data); void *customCodeCbkData; GBool honorUserUnit; PDFDoc *doc; XRef *xref; // the xref table for this PDF file int firstPage; // first output page int lastPage; // last output page char *rasterizePage; // boolean for each page - true if page // needs to be rasterized GList *fontInfo; // info for each font [PSFontInfo] GHash *fontFileInfo; // info for each font file [PSFontFileInfo] Ref *imgIDs; // list of image IDs for in-memory images int imgIDLen; // number of entries in imgIDs array int imgIDSize; // size of imgIDs array Ref *formIDs; // list of IDs for predefined forms int formIDLen; // number of entries in formIDs array int formIDSize; // size of formIDs array char *visitedResources; // vector of resource objects already visited GBool noStateChanges; // true if there have been no state changes // since the last save GList *saveStack; // "no state changes" flag for each // pending save int numTilingPatterns; // current number of nested tiling patterns int nextFunc; // next unique number to use for a function GList *paperSizes; // list of used paper sizes, if paperMatch // is true [PSOutPaperSize] double tx0, ty0; // global translation double xScale0, yScale0; // global scaling int rotate0; // rotation angle (0, 90, 180, 270) double clipLLX0, clipLLY0, clipURX0, clipURY0; double tx, ty; // global translation for current page double xScale, yScale; // global scaling for current page int rotate; // rotation angle for current page double epsX1, epsY1, // EPS bounding box (unrotated) epsX2, epsY2; GString *embFontList; // resource comments for embedded fonts int processColors; // used process colors PSOutCustomColor // used custom colors *customColors; GBool haveTextClip; // set if text has been drawn with a // clipping render mode GBool inType3Char; // inside a Type 3 CharProc GString *t3String; // Type 3 content string double t3WX, t3WY, // Type 3 character parameters t3LLX, t3LLY, t3URX, t3URY; GBool t3FillColorOnly; // operators should only use the fill color GBool t3Cacheable; // cleared if char is not cacheable GBool t3NeedsRestore; // set if a 'q' operator was issued #if OPI_SUPPORT int opi13Nest; // nesting level of OPI 1.3 objects int opi20Nest; // nesting level of OPI 2.0 objects #endif GBool ok; // set up ok? friend class WinPDFPrinter; }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10019/cmd.txt000066400000000000000000000000201417746362400221150ustar00rootroot00000000000000-DHAVE_SPLASH=1 cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10019/expected.txt000066400000000000000000000000501417746362400231560ustar00rootroot00000000000000PSOutputDev.cc:4198:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10020/000077500000000000000000000000001417746362400206115ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10020/Splash.cc000066400000000000000000005731461417746362400223720ustar00rootroot00000000000000//======================================================================== // // Splash.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "gmempp.h" #include "SplashErrorCodes.h" #include "SplashMath.h" #include "SplashBitmap.h" #include "SplashState.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashFont.h" #include "SplashGlyphBitmap.h" #include "Splash.h" // the MSVC math.h doesn't define this #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle ((SplashCoord)0.55228475) #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline Guchar div255(int x) { return (Guchar)((x + (x >> 8) + 0x80) >> 8); } // Clip x to lie in [0, 255]. static inline Guchar clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x; } // Used by drawImage and fillImageMask to divide the target // quadrilateral into sections. struct ImageSection { int y0, y1; // actual y range int ia0, ia1; // vertex indices for edge A int ib0, ib1; // vertex indices for edge B SplashCoord xa0, ya0, xa1, ya1; // edge A SplashCoord dxdya; // slope of edge A SplashCoord xb0, yb0, xb1, yb1; // edge B SplashCoord dxdyb; // slope of edge B }; //------------------------------------------------------------------------ // SplashPipe //------------------------------------------------------------------------ #define splashPipeMaxStages 9 struct SplashPipe { // source pattern SplashPattern *pattern; // source alpha and color Guchar aInput; SplashColor cSrcVal; // special cases and result color GBool noTransparency; GBool shapeOnly; SplashPipeResultColorCtrl resultColorCtrl; // non-isolated group correction // (this is only used when Splash::composite() is called to composite // a non-isolated group onto the backdrop) GBool nonIsolatedGroup; // the "run" function void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); }; SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorNoAlphaBlendCMYK #endif }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaNoBlendCMYK #endif }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaBlendCMYK #endif }; //------------------------------------------------------------------------ // modified region //------------------------------------------------------------------------ void Splash::clearModRegion() { modXMin = bitmap->width; modYMin = bitmap->height; modXMax = -1; modYMax = -1; } inline void Splash::updateModX(int x) { if (x < modXMin) { modXMin = x; } if (x > modXMax) { modXMax = x; } } inline void Splash::updateModY(int y) { if (y < modYMin) { modYMin = y; } if (y > modYMax) { modYMax = y; } } //------------------------------------------------------------------------ // pipeline //------------------------------------------------------------------------ inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup) { SplashColorMode mode; mode = bitmap->mode; pipe->pattern = NULL; // source color if (pattern && pattern->isStatic()) { pattern->getColor(0, 0, pipe->cSrcVal); pipe->pattern = NULL; } else { pipe->pattern = pattern; } // source alpha pipe->aInput = aInput; // special cases pipe->noTransparency = aInput == 255 && !state->softMask && !usesShape && !state->inNonIsolatedGroup && !state->inKnockoutGroup && !nonIsolatedGroup && state->overprintMask == 0xffffffff; pipe->shapeOnly = aInput == 255 && !state->softMask && usesShape && !state->inNonIsolatedGroup && !state->inKnockoutGroup && !nonIsolatedGroup && state->overprintMask == 0xffffffff; // result color if (pipe->noTransparency) { // the !state->blendFunc case is handled separately in pipeRun pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode]; } else if (!state->blendFunc) { pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode]; } else { pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode]; } // non-isolated group correction pipe->nonIsolatedGroup = nonIsolatedGroup; // select the 'run' function pipe->run = &Splash::pipeRun; if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { if (mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono1; } else if (mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono8; } else if (mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleRGB8; } else if (mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleBGR8; #if SPLASH_CMYK } else if (mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleCMYK8; #endif } } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) { if (mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunShapeMono1; } else if (mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeMono8; } else if (mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeRGB8; } else if (mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeBGR8; #if SPLASH_CMYK } else if (mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeCMYK8; #endif } } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && usesShape && !(state->inNonIsolatedGroup && groupBackBitmap->alpha) && !state->inKnockoutGroup && !state->blendFunc && !pipe->nonIsolatedGroup) { if (mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono1; } else if (mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono8; } else if (mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAARGB8; } else if (mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAABGR8; #if SPLASH_CMYK } else if (mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAACMYK8; #endif } } } // general case void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar *shapePtr2; Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; SplashColor cSrc, cDest, cBlend; Guchar shapeVal, cResult0, cResult1, cResult2, cResult3; int cSrcStride, shapeStride, x, lastX, t; SplashColorPtr destColorPtr; Guchar destColorMask; Guchar *destAlphaPtr; SplashColorPtr color0Ptr; Guchar color0Mask; Guchar *alpha0Ptr; SplashColorPtr softMaskPtr; #if SPLASH_CMYK SplashColor cSrc2, cDest2; #endif if (cSrcPtr && !pipe->pattern) { cSrcStride = bitmapComps; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (shapePtr) { shapePtr2 = shapePtr; shapeStride = 1; for (; x0 <= x1; ++x0) { if (*shapePtr2) { break; } cSrcPtr += cSrcStride; ++shapePtr2; } } else { shapeVal = 0xff; shapePtr2 = &shapeVal; shapeStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; if (bitmap->mode == splashModeMono1) { destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); } else { destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps]; destColorMask = 0; // make gcc happy } if (bitmap->alpha) { destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; } else { destAlphaPtr = NULL; } if (state->softMask) { softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0]; } else { softMaskPtr = NULL; } if (state->inKnockoutGroup) { if (bitmap->mode == splashModeMono1) { color0Ptr = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + ((groupBackX + x0) >> 3)]; color0Mask = (Guchar)(0x80 >> ((groupBackX + x0) & 7)); } else { color0Ptr = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + (groupBackX + x0) * bitmapComps]; color0Mask = 0; // make gcc happy } } else { color0Ptr = NULL; color0Mask = 0; // make gcc happy } if (state->inNonIsolatedGroup && groupBackBitmap->alpha) { alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y) * groupBackBitmap->alphaRowSize + (groupBackX + x0)]; } else { alpha0Ptr = NULL; } for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr2; if (!shape) { if (bitmap->mode == splashModeMono1) { destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); } else { destColorPtr += bitmapComps; } if (destAlphaPtr) { ++destAlphaPtr; } if (softMaskPtr) { ++softMaskPtr; } if (color0Ptr) { if (bitmap->mode == splashModeMono1) { color0Ptr += color0Mask & 1; color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); } else { color0Ptr += bitmapComps; } } if (alpha0Ptr) { ++alpha0Ptr; } cSrcPtr += cSrcStride; shapePtr2 += shapeStride; continue; } lastX = x; //----- source color // static pattern: handled in pipeInit // fixed color: handled in pipeInit // dynamic pattern if (pipe->pattern) { pipe->pattern->getColor(x, y, pipe->cSrcVal); } cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy if (pipe->noTransparency && !state->blendFunc) { //----- result color switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: cResult0 = state->grayTransfer[cSrcPtr[0]]; break; case splashModeRGB8: case splashModeBGR8: cResult0 = state->rgbTransferR[cSrcPtr[0]]; cResult1 = state->rgbTransferG[cSrcPtr[1]]; cResult2 = state->rgbTransferB[cSrcPtr[2]]; break; #if SPLASH_CMYK case splashModeCMYK8: cResult0 = state->cmykTransferC[cSrcPtr[0]]; cResult1 = state->cmykTransferM[cSrcPtr[1]]; cResult2 = state->cmykTransferY[cSrcPtr[2]]; cResult3 = state->cmykTransferK[cSrcPtr[3]]; break; #endif } aResult = 255; } else { // if (noTransparency && !blendFunc) //----- read destination pixel // (or backdrop color, for knockout groups) if (color0Ptr) { switch (bitmap->mode) { case splashModeMono1: cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00; color0Ptr += color0Mask & 1; color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); break; case splashModeMono8: cDest[0] = *color0Ptr++; break; case splashModeRGB8: cDest[0] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[2] = color0Ptr[2]; color0Ptr += 3; break; case splashModeBGR8: cDest[2] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[0] = color0Ptr[2]; color0Ptr += 3; break; #if SPLASH_CMYK case splashModeCMYK8: cDest[0] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[2] = color0Ptr[2]; cDest[3] = color0Ptr[3]; color0Ptr += 4; break; #endif } } else { switch (bitmap->mode) { case splashModeMono1: cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00; break; case splashModeMono8: cDest[0] = *destColorPtr; break; case splashModeRGB8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; break; case splashModeBGR8: cDest[0] = destColorPtr[2]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[0]; break; #if SPLASH_CMYK case splashModeCMYK8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; cDest[3] = destColorPtr[3]; break; #endif } } if (destAlphaPtr) { aDest = *destAlphaPtr; } else { aDest = 0xff; } //----- read source color; handle overprint switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: cSrc[0] = state->grayTransfer[cSrcPtr[0]]; break; case splashModeRGB8: case splashModeBGR8: cSrc[0] = state->rgbTransferR[cSrcPtr[0]]; cSrc[1] = state->rgbTransferG[cSrcPtr[1]]; cSrc[2] = state->rgbTransferB[cSrcPtr[2]]; break; #if SPLASH_CMYK case splashModeCMYK8: if (state->overprintMask & 0x01) { cSrc[0] = state->cmykTransferC[cSrcPtr[0]]; } else { cSrc[0] = div255(aDest * cDest[0]); } if (state->overprintMask & 0x02) { cSrc[1] = state->cmykTransferM[cSrcPtr[1]]; } else { cSrc[1] = div255(aDest * cDest[1]); } if (state->overprintMask & 0x04) { cSrc[2] = state->cmykTransferY[cSrcPtr[2]]; } else { cSrc[2] = div255(aDest * cDest[2]); } if (state->overprintMask & 0x08) { cSrc[3] = state->cmykTransferK[cSrcPtr[3]]; } else { cSrc[3] = div255(aDest * cDest[3]); } break; #endif } //----- source alpha if (softMaskPtr) { if (shapePtr) { aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape); } else { aSrc = div255(pipe->aInput * *softMaskPtr++); } } else if (shapePtr) { aSrc = div255(pipe->aInput * shape); } else { aSrc = pipe->aInput; } //----- non-isolated group correction if (pipe->nonIsolatedGroup) { // This path is only used when Splash::composite() is called to // composite a non-isolated group onto the backdrop. In this // case, shape is the source (group) alpha. t = (aDest * 255) / shape - aDest; switch (bitmap->mode) { #if SPLASH_CMYK case splashModeCMYK8: cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255); #endif case splashModeRGB8: case splashModeBGR8: cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255); cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255); case splashModeMono1: case splashModeMono8: cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255); break; } } //----- blend function if (state->blendFunc) { #if SPLASH_CMYK if (bitmap->mode == splashModeCMYK8) { // convert colors to additive cSrc2[0] = (Guchar)(0xff - cSrc[0]); cSrc2[1] = (Guchar)(0xff - cSrc[1]); cSrc2[2] = (Guchar)(0xff - cSrc[2]); cSrc2[3] = (Guchar)(0xff - cSrc[3]); cDest2[0] = (Guchar)(0xff - cDest[0]); cDest2[1] = (Guchar)(0xff - cDest[1]); cDest2[2] = (Guchar)(0xff - cDest[2]); cDest2[3] = (Guchar)(0xff - cDest[3]); (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode); // convert result back to subtractive cBlend[0] = (Guchar)(0xff - cBlend[0]); cBlend[1] = (Guchar)(0xff - cBlend[1]); cBlend[2] = (Guchar)(0xff - cBlend[2]); cBlend[3] = (Guchar)(0xff - cBlend[3]); } else #endif (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); } //----- result alpha and non-isolated group element correction // alphaI = alpha_i // alphaIm1 = alpha_(i-1) if (pipe->noTransparency) { alphaI = alphaIm1 = aResult = 255; } else if (alpha0Ptr) { if (color0Ptr) { // non-isolated, knockout aResult = aSrc; alpha0 = *alpha0Ptr++; alphaI = (Guchar)(aSrc + alpha0 - div255(aSrc * alpha0)); alphaIm1 = alpha0; } else { // non-isolated, non-knockout aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alpha0 = *alpha0Ptr++; alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0)); alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest)); } } else { if (color0Ptr) { // isolated, knockout aResult = aSrc; alphaI = aSrc; alphaIm1 = 0; } else { // isolated, non-knockout aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; alphaIm1 = aDest; } } //----- result color switch (pipe->resultColorCtrl) { case splashPipeResultColorNoAlphaBlendMono: cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); break; case splashPipeResultColorNoAlphaBlendRGB: cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); break; #if SPLASH_CMYK case splashPipeResultColorNoAlphaBlendCMYK: cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]); break; #endif case splashPipeResultColorAlphaNoBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI); } break; case splashPipeResultColorAlphaNoBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI); } break; #if SPLASH_CMYK case splashPipeResultColorAlphaNoBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3]) / alphaI); } break; #endif case splashPipeResultColorAlphaBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI); } break; case splashPipeResultColorAlphaBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI); } break; #if SPLASH_CMYK case splashPipeResultColorAlphaBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * ((255 - alphaIm1) * cSrc[3] + alphaIm1 * cBlend[3]) / 255) / alphaI); } break; #endif } } // if (noTransparency && !blendFunc) //----- write destination pixel switch (bitmap->mode) { case splashModeMono1: if (state->screen->test(x, y, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); break; case splashModeMono8: *destColorPtr++ = cResult0; break; case splashModeRGB8: destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; break; case splashModeBGR8: destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; break; #if SPLASH_CMYK case splashModeCMYK8: destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; break; #endif } if (destAlphaPtr) { *destAlphaPtr++ = aResult; } cSrcPtr += cSrcStride; shapePtr2 += shapeStride; } // for (x ...) updateModX(lastX); } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono1 && !bitmap->alpha) { void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; SplashScreenCursor screenCursor; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); screenCursor = state->screen->getTestCursor(y); for (x = x0; x <= x1; ++x) { //----- write destination pixel cResult0 = state->grayTransfer[cSrcPtr[0]]; if (state->screen->testWithCursor(screenCursor, x, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono8 && bitmap->alpha) { void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeRGB8 && bitmap->alpha) { void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr += 3; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeBGR8 && bitmap->alpha) { void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr += 3; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } #if SPLASH_CMYK // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeCMYK8 && bitmap->alpha) { void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; destColorPtr += 4; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } #endif // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeMono1 && !bitmap->alpha void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; SplashScreenCursor screenCursor; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); screenCursor = state->screen->getTestCursor(y); for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { cResult0 = cSrc0; } else { //----- read destination pixel cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; //----- result color // note: aDest = alphaI = aResult = 0xff cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); } //----- write destination pixel if (state->screen->testWithCursor(screenCursor, x, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeMono8 && bitmap->alpha void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { ++destColorPtr; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; } else { //----- read destination alpha aDest = *destAlphaPtr; //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; } else { //----- read destination pixel cDest0 = *destColorPtr; //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); } } //----- write destination pixel *destColorPtr++ = cResult0; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeRGB8 && bitmap->alpha void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination alpha aDest = *destAlphaPtr; //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeBGR8 && bitmap->alpha void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination alpha aDest = *destAlphaPtr; //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination pixel cDest0 = destColorPtr[2]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[0]; //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } } //----- write destination pixel destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #if SPLASH_CMYK // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeCMYK8 && bitmap->alpha void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2, cSrc3; Guchar cDest0, cDest1, cDest2, cDest3; Guchar cResult0, cResult1, cResult2, cResult3; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 4; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; cDest3 = destColorPtr[3]; aDest = *destAlphaPtr; //----- overprint if (state->overprintMask & 1) { cSrc0 = state->cmykTransferC[cSrcPtr[0]]; } else { cSrc0 = div255(aDest * cDest0); } if (state->overprintMask & 2) { cSrc1 = state->cmykTransferM[cSrcPtr[1]]; } else { cSrc1 = div255(aDest * cDest1); } if (state->overprintMask & 4) { cSrc2 = state->cmykTransferY[cSrcPtr[2]]; } else { cSrc2 = div255(aDest * cDest2); } if (state->overprintMask & 8) { cSrc3 = state->cmykTransferK[cSrcPtr[3]]; } else { cSrc3 = div255(aDest * cDest3); } //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; cResult3 = cSrc3; } else { //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; cResult3 = cSrc3; } else { //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); } } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #endif // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono1 && !bitmap->alpha void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; SplashScreenCursor screenCursor; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); screenCursor = state->screen->getTestCursor(y); for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result color // note: aDest = alphaI = aResult = 0xff cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); //----- write destination pixel if (state->screen->testWithCursor(screenCursor, x, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono8 && bitmap->alpha void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { ++destColorPtr; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = *destColorPtr; aDest = *destAlphaPtr; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); } //----- write destination pixel *destColorPtr++ = cResult0; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeRGB8 && bitmap->alpha void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; aDest = *destAlphaPtr; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeBGR8 && bitmap->alpha void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[2]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[0]; aDest = *destAlphaPtr; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } //----- write destination pixel destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #if SPLASH_CMYK // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeCMYK8 && bitmap->alpha void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2, cSrc3; Guchar cDest0, cDest1, cDest2, cDest3; Guchar cResult0, cResult1, cResult2, cResult3; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 4; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; cDest3 = destColorPtr[3]; aDest = *destAlphaPtr; //----- overprint if (state->overprintMask & 1) { cSrc0 = state->cmykTransferC[cSrcPtr[0]]; } else { cSrc0 = div255(aDest * cDest0); } if (state->overprintMask & 2) { cSrc1 = state->cmykTransferM[cSrcPtr[1]]; } else { cSrc1 = div255(aDest * cDest1); } if (state->overprintMask & 4) { cSrc2 = state->cmykTransferY[cSrcPtr[2]]; } else { cSrc2 = div255(aDest * cDest2); } if (state->overprintMask & 8) { cSrc3 = state->cmykTransferK[cSrcPtr[3]]; } else { cSrc3 = div255(aDest * cDest3); } //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #endif //------------------------------------------------------------------------ // Transform a point from user space to device space. inline void Splash::transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo) { // [ m[0] m[1] 0 ] // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] // [ m[4] m[5] 1 ] *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; } //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreenParams *screenParams) { bitmap = bitmapA; bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenParams); scanBuf = (Guchar *)gmalloc(bitmap->width); if (bitmap->mode == splashModeMono1) { scanBuf2 = (Guchar *)gmalloc(bitmap->width); } else { scanBuf2 = NULL; } groupBackBitmap = NULL; minLineWidth = 0; clearModRegion(); debugMode = gFalse; } Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreen *screenA) { bitmap = bitmapA; bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenA); scanBuf = (Guchar *)gmalloc(bitmap->width); if (bitmap->mode == splashModeMono1) { scanBuf2 = (Guchar *)gmalloc(bitmap->width); } else { scanBuf2 = NULL; } groupBackBitmap = NULL; minLineWidth = 0; clearModRegion(); debugMode = gFalse; } Splash::~Splash() { while (state->next) { restoreState(); } delete state; gfree(scanBuf); gfree(scanBuf2); } //------------------------------------------------------------------------ // state read //------------------------------------------------------------------------ SplashCoord *Splash::getMatrix() { return state->matrix; } SplashPattern *Splash::getStrokePattern() { return state->strokePattern; } SplashPattern *Splash::getFillPattern() { return state->fillPattern; } SplashScreen *Splash::getScreen() { return state->screen; } SplashBlendFunc Splash::getBlendFunc() { return state->blendFunc; } SplashCoord Splash::getStrokeAlpha() { return state->strokeAlpha; } SplashCoord Splash::getFillAlpha() { return state->fillAlpha; } SplashCoord Splash::getLineWidth() { return state->lineWidth; } int Splash::getLineCap() { return state->lineCap; } int Splash::getLineJoin() { return state->lineJoin; } SplashCoord Splash::getMiterLimit() { return state->miterLimit; } SplashCoord Splash::getFlatness() { return state->flatness; } SplashCoord *Splash::getLineDash() { return state->lineDash; } int Splash::getLineDashLength() { return state->lineDashLength; } SplashCoord Splash::getLineDashPhase() { return state->lineDashPhase; } SplashStrokeAdjustMode Splash::getStrokeAdjust() { return state->strokeAdjust; } SplashClip *Splash::getClip() { return state->clip; } SplashBitmap *Splash::getSoftMask() { return state->softMask; } GBool Splash::getInNonIsolatedGroup() { return state->inNonIsolatedGroup; } GBool Splash::getInKnockoutGroup() { return state->inKnockoutGroup; } //------------------------------------------------------------------------ // state write //------------------------------------------------------------------------ void Splash::setMatrix(SplashCoord *matrix) { memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); } void Splash::setStrokePattern(SplashPattern *strokePattern) { state->setStrokePattern(strokePattern); } void Splash::setFillPattern(SplashPattern *fillPattern) { state->setFillPattern(fillPattern); } void Splash::setScreen(SplashScreen *screen) { state->setScreen(screen); } void Splash::setBlendFunc(SplashBlendFunc func) { state->blendFunc = func; } void Splash::setStrokeAlpha(SplashCoord alpha) { state->strokeAlpha = alpha; } void Splash::setFillAlpha(SplashCoord alpha) { state->fillAlpha = alpha; } void Splash::setLineWidth(SplashCoord lineWidth) { state->lineWidth = lineWidth; } void Splash::setLineCap(int lineCap) { if (lineCap >= 0 && lineCap <= 2) { state->lineCap = lineCap; } else { state->lineCap = 0; } } void Splash::setLineJoin(int lineJoin) { if (lineJoin >= 0 && lineJoin <= 2) { state->lineJoin = lineJoin; } else { state->lineJoin = 0; } } void Splash::setMiterLimit(SplashCoord miterLimit) { state->miterLimit = miterLimit; } void Splash::setFlatness(SplashCoord flatness) { if (flatness < 1) { state->flatness = 1; } else { state->flatness = flatness; } } void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase) { state->setLineDash(lineDash, lineDashLength, lineDashPhase); } void Splash::setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust) { state->strokeAdjust = strokeAdjust; } void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { state->clipResetToRect(x0, y0, x1, y1); } SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { return state->clipToRect(x0, y0, x1, y1); } SplashError Splash::clipToPath(SplashPath *path, GBool eo) { return state->clipToPath(path, eo); } void Splash::setSoftMask(SplashBitmap *softMask) { state->setSoftMask(softMask); } void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA, int groupBackXA, int groupBackYA, GBool nonIsolated, GBool knockout) { groupBackBitmap = groupBackBitmapA; groupBackX = groupBackXA; groupBackY = groupBackYA; state->inNonIsolatedGroup = nonIsolated; state->inKnockoutGroup = knockout; } void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray) { state->setTransfer(red, green, blue, gray); } void Splash::setOverprintMask(Guint overprintMask) { state->overprintMask = overprintMask; } void Splash::setEnablePathSimplification(GBool en) { state->enablePathSimplification = en; } //------------------------------------------------------------------------ // state save/restore //------------------------------------------------------------------------ void Splash::saveState() { SplashState *newState; newState = state->copy(); newState->next = state; state = newState; } SplashError Splash::restoreState() { SplashState *oldState; if (!state->next) { return splashErrNoSave; } oldState = state; state = state->next; delete oldState; return splashOk; } //------------------------------------------------------------------------ // drawing operations //------------------------------------------------------------------------ void Splash::clear(SplashColorPtr color, Guchar alpha) { SplashColorPtr row, p; Guchar mono; int x, y; switch (bitmap->mode) { case splashModeMono1: mono = (color[0] & 0x80) ? 0xff : 0x00; if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), mono, -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); } break; case splashModeMono8: if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } break; case splashModeRGB8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; } row += bitmap->rowSize; } } break; case splashModeBGR8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[2]; *p++ = color[1]; *p++ = color[0]; } row += bitmap->rowSize; } } break; #if SPLASH_CMYK case splashModeCMYK8: if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; *p++ = color[3]; } row += bitmap->rowSize; } } break; #endif } if (bitmap->alpha) { memset(bitmap->alpha, alpha, bitmap->alphaRowSize * bitmap->height); } updateModX(0); updateModY(0); updateModX(bitmap->width - 1); updateModY(bitmap->height - 1); } SplashError Splash::stroke(SplashPath *path) { SplashPath *path2, *dPath; SplashCoord t0, t1, t2, t3, w, w2, lineDashMax, lineDashTotal; int lineCap, lineJoin, i; if (debugMode) { printf("stroke [dash:%d] [width:%.2f]:\n", state->lineDashLength, (double)state->lineWidth); dumpPath(path); } opClipRes = splashClipAllOutside; if (path->length == 0) { return splashErrEmptyPath; } path2 = flattenPath(path, state->matrix, state->flatness); // Compute an approximation of the transformed line width. // Given a CTM of [m0 m1], // [m2 m3] // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else // use min{|m1|,|m2|}. // This handles the common cases -- [s 0 ] and [0 s] -- // [0 +/-s] [+/-s 0] // well, and still does something reasonable for the uncommon // case transforms. t0 = splashAbs(state->matrix[0]); t1 = splashAbs(state->matrix[1]); t2 = splashAbs(state->matrix[2]); t3 = splashAbs(state->matrix[3]); if (t0 * t3 >= t1 * t2) { w = (t0 < t3) ? t0 : t3; } else { w = (t1 < t2) ? t1 : t2; } w2 = w * state->lineWidth; // construct the dashed path if (state->lineDashLength > 0) { // check the maximum transformed dash element length (using the // same approximation as for line width) -- if it's less than 0.1 // pixel, don't apply the dash pattern; this avoids a huge // performance/memory hit with PDF files that use absurd dash // patterns like [0.0007 0.0003] lineDashTotal = 0; lineDashMax = 0; for (i = 0; i < state->lineDashLength; ++i) { lineDashTotal += state->lineDash[i]; if (state->lineDash[i] > lineDashMax) { lineDashMax = state->lineDash[i]; } } // Acrobat simply draws nothing if the dash array is [0] if (lineDashTotal == 0) { delete path2; return splashOk; } if (w * lineDashMax > 0.1) { dPath = makeDashedPath(path2); delete path2; path2 = dPath; if (path2->length == 0) { delete path2; return splashErrEmptyPath; } } } // round caps on narrow lines look bad, and can't be // stroke-adjusted, so use projecting caps instead (but we can't do // this if there are zero-length dashes or segments, because those // turn into round dots) lineCap = state->lineCap; lineJoin = state->lineJoin; if (state->strokeAdjust == splashStrokeAdjustCAD && w2 < 3.5) { if (lineCap == splashLineCapRound && !state->lineDashContainsZeroLengthDashes() && !path->containsZeroLengthSubpaths()) { lineCap = splashLineCapProjecting; } if (lineJoin == splashLineJoinRound) { lineJoin = splashLineJoinBevel; } } // if there is a min line width set, and the transformed line width // is smaller, use the min line width if (w > 0 && w2 < minLineWidth) { strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel); } else if (bitmap->mode == splashModeMono1 || !vectorAntialias) { // in monochrome mode or if antialiasing is disabled, use 0-width // lines for any transformed line width <= 1 -- lines less than 1 // pixel wide look too fat without antialiasing if (w2 < 1.001) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth, lineCap, lineJoin); } } else { // in gray and color modes, only use 0-width lines if the line // width is explicitly set to 0 if (state->lineWidth == 0) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth, lineCap, lineJoin); } } delete path2; return splashOk; } void Splash::strokeNarrow(SplashPath *path) { SplashPipe pipe; SplashXPath *xPath; SplashXPathSeg *seg; int x0, x1, y0, y1, xa, xb, y; SplashCoord dxdy; SplashClipResult clipRes; int nClipRes[3]; int i; nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse, state->enablePathSimplification, state->strokeAdjust); pipeInit(&pipe, state->strokePattern, (Guchar)splashRound(state->strokeAlpha * 255), gTrue, gFalse); for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { if (seg->y0 <= seg->y1) { y0 = splashFloor(seg->y0); y1 = splashFloor(seg->y1); x0 = splashFloor(seg->x0); x1 = splashFloor(seg->x1); } else { y0 = splashFloor(seg->y1); y1 = splashFloor(seg->y0); x0 = splashFloor(seg->x1); x1 = splashFloor(seg->x0); } if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, x0 <= x1 ? x1 : x0, y1, state->strokeAdjust)) != splashClipAllOutside) { if (y0 == y1) { if (x0 <= x1) { drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); } } else { dxdy = seg->dxdy; y = state->clip->getYMinI(state->strokeAdjust); if (y0 < y) { y0 = y; x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); } y = state->clip->getYMaxI(state->strokeAdjust); if (y1 > y) { y1 = y; x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy); } if (x0 <= x1) { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 + 1; } if (xa == xb) { drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside); } xa = xb; } } else { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 - 1; } if (xa == xb) { drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside); } xa = xb; } } } } ++nClipRes[clipRes]; } if (nClipRes[splashClipPartial] || (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { opClipRes = splashClipPartial; } else if (nClipRes[splashClipAllInside]) { opClipRes = splashClipAllInside; } else { opClipRes = splashClipAllOutside; } delete xPath; } void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip) { int x; x = state->clip->getXMinI(state->strokeAdjust); if (x > x0) { x0 = x; } x = state->clip->getXMaxI(state->strokeAdjust); if (x < x1) { x1 = x; } if (x0 > x1) { return; } for (x = x0; x <= x1; ++x) { scanBuf[x] = 0xff; } if (!noClip) { if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) { return; } } (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL); } void Splash::strokeWide(SplashPath *path, SplashCoord w, int lineCap, int lineJoin) { SplashPath *path2; path2 = makeStrokePath(path, w, lineCap, lineJoin, gFalse); fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); delete path2; } SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness) { SplashPath *fPath; SplashCoord flatness2; Guchar flag; int i; fPath = new SplashPath(); #if USE_FIXEDPOINT flatness2 = flatness; #else flatness2 = flatness * flatness; #endif i = 0; while (i < path->length) { flag = path->flags[i]; if (flag & splashPathFirst) { fPath->moveTo(path->pts[i].x, path->pts[i].y); ++i; } else { if (flag & splashPathCurve) { flattenCurve(path->pts[i-1].x, path->pts[i-1].y, path->pts[i ].x, path->pts[i ].y, path->pts[i+1].x, path->pts[i+1].y, path->pts[i+2].x, path->pts[i+2].y, matrix, flatness2, fPath); i += 3; } else { fPath->lineTo(path->pts[i].x, path->pts[i].y); ++i; } if (path->flags[i-1] & splashPathClosed) { fPath->close(); } } } return fPath; } void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath) { SplashCoord cx[splashMaxCurveSplits + 1][3]; SplashCoord cy[splashMaxCurveSplits + 1][3]; int cNext[splashMaxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, tx, ty, d1, d2; int p1, p2, p3; // initial segment p1 = 0; p2 = splashMaxCurveSplits; cx[p1][0] = x0; cy[p1][0] = y0; cx[p1][1] = x1; cy[p1][1] = y1; cx[p1][2] = x2; cy[p1][2] = y2; cx[p2][0] = x3; cy[p2][0] = y3; cNext[p1] = p2; while (p1 < splashMaxCurveSplits) { // get the next segment xl0 = cx[p1][0]; yl0 = cy[p1][0]; xx1 = cx[p1][1]; yy1 = cy[p1][1]; xx2 = cx[p1][2]; yy2 = cy[p1][2]; p2 = cNext[p1]; xr3 = cx[p2][0]; yr3 = cy[p2][0]; // compute the distances (in device space) from the control points // to the midpoint of the straight line (this is a bit of a hack, // but it's much faster than computing the actual distances to the // line) transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); transform(matrix, xx1, yy1, &tx, &ty); #if USE_FIXEDPOINT d1 = splashDist(tx, ty, mx, my); #else dx = tx - mx; dy = ty - my; d1 = dx*dx + dy*dy; #endif transform(matrix, xx2, yy2, &tx, &ty); #if USE_FIXEDPOINT d2 = splashDist(tx, ty, mx, my); #else dx = tx - mx; dy = ty - my; d2 = dx*dx + dy*dy; #endif // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { fPath->lineTo(xr3, yr3); p1 = p2; // otherwise, subdivide the curve } else { xl1 = splashAvg(xl0, xx1); yl1 = splashAvg(yl0, yy1); xh = splashAvg(xx1, xx2); yh = splashAvg(yy1, yy2); xl2 = splashAvg(xl1, xh); yl2 = splashAvg(yl1, yh); xr2 = splashAvg(xx2, xr3); yr2 = splashAvg(yy2, yr3); xr1 = splashAvg(xh, xr2); yr1 = splashAvg(yh, yr2); xr0 = splashAvg(xl2, xr1); yr0 = splashAvg(yl2, yr1); // add the new subdivision points p3 = (p1 + p2) / 2; cx[p1][1] = xl1; cy[p1][1] = yl1; cx[p1][2] = xl2; cy[p1][2] = yl2; cNext[p1] = p3; cx[p3][0] = xr0; cy[p3][0] = yr0; cx[p3][1] = xr1; cy[p3][1] = yr1; cx[p3][2] = xr2; cy[p3][2] = yr2; cNext[p3] = p2; } } } SplashPath *Splash::makeDashedPath(SplashPath *path) { SplashPath *dPath; SplashCoord lineDashTotal; SplashCoord lineDashStartPhase, lineDashDist, segLen; SplashCoord x0, y0, x1, y1, xa, ya; GBool lineDashStartOn, lineDashEndOn, lineDashOn, newPath; int lineDashStartIdx, lineDashIdx, subpathStart, nDashes; int i, j, k; lineDashTotal = 0; for (i = 0; i < state->lineDashLength; ++i) { lineDashTotal += state->lineDash[i]; } // Acrobat simply draws nothing if the dash array is [0] if (lineDashTotal == 0) { return new SplashPath(); } lineDashStartPhase = state->lineDashPhase; if (lineDashStartPhase > lineDashTotal * 2) { i = splashFloor(lineDashStartPhase / (lineDashTotal * 2)); lineDashStartPhase -= lineDashTotal * i * 2; } else if (lineDashStartPhase < 0) { i = splashCeil(-lineDashStartPhase / (lineDashTotal * 2)); lineDashStartPhase += lineDashTotal * i * 2; } i = splashFloor(lineDashStartPhase / lineDashTotal); lineDashStartPhase -= (SplashCoord)i * lineDashTotal; lineDashStartOn = gTrue; lineDashStartIdx = 0; if (lineDashStartPhase > 0) { while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { lineDashStartOn = !lineDashStartOn; lineDashStartPhase -= state->lineDash[lineDashStartIdx]; if (++lineDashStartIdx == state->lineDashLength) { lineDashStartIdx = 0; } } } dPath = new SplashPath(); // process each subpath i = 0; while (i < path->length) { // find the end of the subpath for (j = i; j < path->length - 1 && !(path->flags[j] & splashPathLast); ++j) ; // initialize the dash parameters lineDashOn = lineDashStartOn; lineDashEndOn = lineDashStartOn; lineDashIdx = lineDashStartIdx; lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; subpathStart = dPath->length; nDashes = 0; // process each segment of the subpath newPath = gTrue; for (k = i; k < j; ++k) { // grab the segment x0 = path->pts[k].x; y0 = path->pts[k].y; x1 = path->pts[k+1].x; y1 = path->pts[k+1].y; segLen = splashDist(x0, y0, x1, y1); // process the segment while (segLen > 0) { // Special case for zero-length dash segments: draw a very // short -- but not zero-length -- segment. This ensures that // we get the correct behavior with butt and projecting line // caps. The PS/PDF specs imply that zero-length segments are // not drawn unless the line cap is round, but Acrobat and // Ghostscript both draw very short segments (for butt caps) // and squares (for projecting caps). if (lineDashDist == 0) { if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; ++nDashes; } xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0); ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0); dPath->lineTo(xa, ya); } } else if (lineDashDist >= segLen) { if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; ++nDashes; } dPath->lineTo(x1, y1); } lineDashDist -= segLen; segLen = 0; } else { xa = x0 + (lineDashDist / segLen) * (x1 - x0); ya = y0 + (lineDashDist / segLen) * (y1 - y0); if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; ++nDashes; } dPath->lineTo(xa, ya); } x0 = xa; y0 = ya; segLen -= lineDashDist; lineDashDist = 0; } lineDashEndOn = lineDashOn; // get the next entry in the dash array if (lineDashDist <= 0) { lineDashOn = !lineDashOn; if (++lineDashIdx == state->lineDashLength) { lineDashIdx = 0; } lineDashDist = state->lineDash[lineDashIdx]; newPath = gTrue; } } } // in a closed subpath, where the dash pattern is "on" at both the // start and end of the subpath, we need to merge the start and // end to get a proper line join if ((path->flags[j] & splashPathClosed) && lineDashStartOn && lineDashEndOn) { if (nDashes == 1) { dPath->close(); } else if (nDashes > 1) { k = subpathStart; do { ++k; dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y); } while (!(dPath->flags[k] & splashPathLast)); ++k; memmove(&dPath->pts[subpathStart], &dPath->pts[k], (dPath->length - k) * sizeof(SplashPathPoint)); memmove(&dPath->flags[subpathStart], &dPath->flags[k], (dPath->length - k) * sizeof(Guchar)); dPath->length -= k - subpathStart; dPath->curSubpath -= k - subpathStart; } } i = j + 1; } return dPath; } SplashError Splash::fill(SplashPath *path, GBool eo) { if (debugMode) { printf("fill [eo:%d]:\n", eo); dumpPath(path); } return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha); } SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha) { SplashPipe pipe; SplashPath *path2; SplashXPath *xPath; SplashXPathScanner *scanner; int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t; SplashClipResult clipRes; if (path->length == 0) { return splashErrEmptyPath; } if (pathAllOutside(path)) { opClipRes = splashClipAllOutside; return splashOk; } path2 = tweakFillPath(path); xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue, state->enablePathSimplification, state->strokeAdjust); if (path2 != path) { delete path2; } xMin = xPath->getXMin(); yMin = xPath->getYMin(); xMax = xPath->getXMax(); yMax = xPath->getYMax(); if (xMin > xMax || yMin > yMax) { delete xPath; return splashOk; } scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); // check clipping if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, state->strokeAdjust)) != splashClipAllOutside) { if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { yMax = t; } if (xMin > xMax || yMin > yMax) { delete scanner; delete xPath; return splashOk; } pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255), gTrue, gFalse); // draw the spans if (vectorAntialias && !inShading) { for (y = yMin; y <= yMax; ++y) { scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2); if (xMin2 <= xMax2) { if (clipRes != splashClipAllInside) { state->clip->clipSpan(scanBuf, y, xMin2, xMax2, state->strokeAdjust); } (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); } } } else { for (y = yMin; y <= yMax; ++y) { scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2); if (xMin2 <= xMax2) { if (clipRes != splashClipAllInside) { state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2, state->strokeAdjust); } (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); } } } } opClipRes = clipRes; delete scanner; delete xPath; return splashOk; } // Applies various tweaks to a fill path: // (1) add stroke adjust hints to a filled rectangle // (2) applies a minimum width to a zero-width filled rectangle (so // stroke adjustment works correctly // (3) convert a degenerate fill ('moveto lineto fill' and 'moveto // lineto closepath fill') to a minimum-width filled rectangle // // These tweaks only apply to paths with a single subpath. // // Returns either the unchanged input path or a new path (in which // case the returned path must be deleted by the caller). SplashPath *Splash::tweakFillPath(SplashPath *path) { SplashPath *path2; SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w; int n; if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) { return path; } n = path->getLength(); if (!((n == 2) || (n == 3 && path->flags[1] == 0) || (n == 4 && path->flags[1] == 0 && path->flags[2] == 0) || (n == 5 && path->flags[1] == 0 && path->flags[2] == 0 && path->flags[3] == 0))) { return path; } path2 = path; // degenerate fill (2 or 3 points) or rectangle of (nearly) zero // width --> replace with a min-width rectangle and hint if (n == 2 || (n == 3 && (path->flags[0] & splashPathClosed)) || (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) || ((n == 4 || (n == 5 && (path->flags[0] & splashPathClosed))) && ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 && splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 && splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) || (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 && splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 && splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) { wx = state->matrix[0] + state->matrix[2]; wy = state->matrix[1] + state->matrix[3]; w = splashSqrt(wx*wx + wy*wy); if (w < 0.001) { w = 0; } else { // min width is 0.1 -- this constant is minWidth * sqrt(2) w = (SplashCoord)0.1414 / w; } xx0 = path->pts[0].x; yy0 = path->pts[0].y; if (n <= 3) { xx1 = path->pts[1].x; yy1 = path->pts[1].y; } else { xx1 = path->pts[2].x; yy1 = path->pts[2].y; } dx = xx1 - xx0; dy = yy1 - yy0; d = splashSqrt(dx * dx + dy * dy); if (d < 0.001) { d = 0; } else { d = w / d; } dx *= d; dy *= d; path2 = new SplashPath(); path2->moveTo(xx0 + dy, yy0 - dx); path2->lineTo(xx1 + dy, yy1 - dx); path2->lineTo(xx1 - dy, yy1 + dx); path2->lineTo(xx0 - dy, yy0 + dx); path2->close(gTrue); path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); // unclosed rectangle --> close and hint } else if (n == 4 && !(path->flags[0] & splashPathClosed)) { path2->close(gTrue); path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); // closed rectangle --> hint } else if (n == 5 && (path->flags[0] & splashPathClosed)) { path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); } return path2; } GBool Splash::pathAllOutside(SplashPath *path) { SplashCoord xMin1, yMin1, xMax1, yMax1; SplashCoord xMin2, yMin2, xMax2, yMax2; SplashCoord x, y; int xMinI, yMinI, xMaxI, yMaxI; int i; xMin1 = xMax1 = path->pts[0].x; yMin1 = yMax1 = path->pts[0].y; for (i = 1; i < path->length; ++i) { if (path->pts[i].x < xMin1) { xMin1 = path->pts[i].x; } else if (path->pts[i].x > xMax1) { xMax1 = path->pts[i].x; } if (path->pts[i].y < yMin1) { yMin1 = path->pts[i].y; } else if (path->pts[i].y > yMax1) { yMax1 = path->pts[i].y; } } transform(state->matrix, xMin1, yMin1, &x, &y); xMin2 = xMax2 = x; yMin2 = yMax2 = y; transform(state->matrix, xMin1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMin1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } // sanity-check the coordinates - xMinI/yMinI/xMaxI/yMaxI are // 32-bit integers, so coords need to be < 2^31 SplashXPath::clampCoords(&xMin2, &yMin2); SplashXPath::clampCoords(&xMax2, &yMax2); xMinI = splashFloor(xMin2); yMinI = splashFloor(yMin2); xMaxI = splashFloor(xMax2); yMaxI = splashFloor(yMax2); return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI, state->strokeAdjust) == splashClipAllOutside; } SplashError Splash::fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font) { SplashGlyphBitmap glyph; SplashCoord xt, yt; int x0, y0, xFrac, yFrac; SplashError err; if (debugMode) { printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", (double)x, (double)y, c, c, c); } transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); xFrac = splashFloor((xt - x0) * splashFontFraction); y0 = splashFloor(yt); yFrac = splashFloor((yt - y0) * splashFontFraction); if (!font->getGlyph(c, xFrac, yFrac, &glyph)) { return splashErrNoGlyph; } err = fillGlyph2(x0, y0, &glyph); if (glyph.freeData) { gfree(glyph.data); } return err; } SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph) { SplashCoord xt, yt; int x0, y0; transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); y0 = splashFloor(yt); return fillGlyph2(x0, y0, glyph); } SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { SplashPipe pipe; SplashClipResult clipRes; Guchar alpha; Guchar *p; int xMin, yMin, xMax, yMax; int x, y, xg, yg, xx, t; xg = x0 - glyph->x; yg = y0 - glyph->y; xMin = xg; xMax = xg + glyph->w - 1; yMin = yg; yMax = yg + glyph->h - 1; if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, state->strokeAdjust)) != splashClipAllOutside) { pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (clipRes == splashClipAllInside) { if (glyph->aa) { p = glyph->data; for (y = yMin; y <= yMax; ++y) { (this->*pipe.run)(&pipe, xMin, xMax, y, glyph->data + (y - yMin) * glyph->w, NULL); } } else { p = glyph->data; for (y = yMin; y <= yMax; ++y) { for (x = xMin; x <= xMax; x += 8) { alpha = *p++; for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00; alpha = (Guchar)(alpha << 1); } } (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } else { if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { yMax = t; } if (xMin <= xMax && yMin <= yMax) { if (glyph->aa) { for (y = yMin; y <= yMax; ++y) { p = glyph->data + (y - yg) * glyph->w + (xMin - xg); memcpy(scanBuf + xMin, p, xMax - xMin + 1); state->clip->clipSpan(scanBuf, y, xMin, xMax, state->strokeAdjust); (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } else { for (y = yMin; y <= yMax; ++y) { p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3) + ((xMin - xg) >> 3); alpha = *p++; xx = (xMin - xg) & 7; alpha = (Guchar)(alpha << xx); for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) { scanBuf[x] = (alpha & 0x80) ? 255 : 0; alpha = (Guchar)(alpha << 1); } for (; x <= xMax; x += 8) { alpha = *p++; for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0; alpha = (Guchar)(alpha << 1); } } state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, state->strokeAdjust); (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } } } opClipRes = clipRes; return splashOk; } void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax, int *xyMinI, int *xyMaxI) { if (state->strokeAdjust == splashStrokeAdjustOff) { *xyMinI = splashFloor(xyMin); *xyMaxI = splashFloor(xyMax); if (*xyMaxI <= *xyMinI) { *xyMaxI = *xyMinI + 1; } } else { splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust); } } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashBitmap *scaledMask; SplashClipResult clipRes; GBool minorAxisZero; SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; if (debugMode) { printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; // rough estimate of size of scaled mask t0 = splashAbs(mat[0]); t1 = splashAbs(mat[1]); wSize = t0 > t1 ? t0 : t1; t0 = splashAbs(mat[2]); t1 = splashAbs(mat[3]); hSize = t0 > t1 ? t0 : t1; // stream-mode upscaling -- this is slower, so we only use it if the // upscaled mask is large (in which case clipping should remove many // pixels) #if USE_FIXEDPOINT if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { #else if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || (wSize > w && hSize > h && wSize * hSize > 10000000) || ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate); #endif // scaling only } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus horizontal flip } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus horizontal and vertical flips } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // all other cases } else { arbitraryTransformMask(src, srcData, w, h, mat, glyphMode, interpolate); } return splashOk; } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. void Splash::upscaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashClipResult clipRes; SplashPipe pipe; Guchar *unscaledImage, *p; SplashCoord xMin, yMin, xMax, yMax, t; SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; SplashCoord ix, iy, sx, sy, pix0, pix1; int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt; // compute the bbox of the target quadrilateral xMin = xMax = mat[4]; t = mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } getImageBounds(xMin, xMax, &xMinI, &xMaxI); yMin = yMax = mat[5]; t = mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } getImageBounds(yMin, yMax, &yMinI, &yMaxI); // clipping clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } if (clipRes != splashClipAllInside) { if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { xMinI = tt; } if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { xMaxI = tt; } if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { yMinI = tt; } if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { yMaxI = tt; } } // invert the matrix det = mat[0] * mat[3] - mat[1] * mat[2]; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } det = (SplashCoord)1 / det; mi0 = det * mat[3] * srcWidth; mi1 = -det * mat[1] * srcHeight; mi2 = -det * mat[2] * srcWidth; mi3 = det * mat[0] * srcHeight; mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; // grab the image unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight); for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) { (*src)(srcData, p); for (x = 0; x < srcWidth; ++x) { p[x] = (Guchar)(p[x] * 255); } } // draw it pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (y = yMinI; y < yMaxI; ++y) { for (x = xMinI; x < xMaxI; ++x) { ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; if (interpolate) { if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { x0 = splashFloor(ix - 0.5); x1 = x0 + 1; sx = (ix - 0.5) - x0; y0 = splashFloor(iy - 0.5); y1 = y0 + 1; sy = (iy - 0.5) - y0; if (x0 < 0) { x0 = 0; } if (x1 >= srcWidth) { x1 = srcWidth - 1; } if (y0 < 0) { y0 = 0; } if (y1 >= srcHeight) { y1 = srcHeight - 1; } pix0 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledImage[y0 * srcWidth + x0] + sx * (SplashCoord)unscaledImage[y0 * srcWidth + x1]; pix1 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledImage[y1 * srcWidth + x0] + sx * (SplashCoord)unscaledImage[y1 * srcWidth + x1]; scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } else { scanBuf[x] = 0; } } else { x0 = splashFloor(ix); y0 = splashFloor(iy); if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { scanBuf[x] = unscaledImage[y0 * srcWidth + x0]; } else { scanBuf[x] = 0; } } } if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } } (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL); } gfree(unscaledImage); } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashBitmap *scaledMask; SplashClipResult clipRes; SplashPipe pipe; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int bw, y, xa, xb, x, i, xx, yy; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping xMin = splashRound(vx[0]); xMax = splashRound(vx[0]); yMin = splashRound(vy[0]); yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; } else if (t0 > xMax) { xMax = t0; } t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; } else if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } // compute the scale factors if (mat[0] >= 0) { t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); // construct the three sections i = 0; if (vy[1] < vy[i]) { i = 1; } if (vy[2] < vy[i]) { i = 2; } if (vy[3] < vy[i]) { i = 3; } // NB: if using fixed point, 0.000001 will be truncated to zero, // so these two comparisons must be <=, not < if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && vy[(i-1) & 3] < vy[(i+1) & 3]) { i = (i-1) & 3; } if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { section[0].y0 = splashRound(vy[i]); section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; section[0].ib0 = (i+1) & 3; section[0].ib1 = (i+2) & 3; } else { section[0].ia0 = (i+1) & 3; section[0].ia1 = (i+2) & 3; section[0].ib0 = i; section[0].ib1 = (i+3) & 3; } nSections = 1; } else { section[0].y0 = splashRound(vy[i]); section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i+2) & 3; if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[0].ia1 = section[2].ia0 = (i+1) & 3; section[0].ib1 = section[2].ib0 = (i+3) & 3; } else { section[0].ia1 = section[2].ia0 = (i+3) & 3; section[0].ib1 = section[2].ib0 = (i+1) & 3; } if (vy[(i+1) & 3] < vy[(i+3) & 3]) { section[1].y0 = splashRound(vy[(i+1) & 3]); section[2].y0 = splashRound(vy[(i+3) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = (i+1) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i+3) & 3; section[1].ib0 = (i+1) & 3; section[1].ib1 = (i+2) & 3; } } else { section[1].y0 = splashRound(vy[(i+3) & 3]); section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; section[1].ib0 = (i+3) & 3; section[1].ib1 = (i+2) & 3; } else { section[1].ia0 = (i+3) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } // scan all pixels inside the target region bw = bitmap->width; for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = splashRound(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya); xb = splashRound(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb); if (xa > xb) { continue; } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } // check the scanBuf bounds if (xa >= bw || xb < 0) { continue; } if (xa < 0) { xa = 0; } if (xb > bw) { xb = bw; } // get the scan line for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (xx < 0) { xx = 0; } else if (xx >= scaledWidth) { xx = scaledWidth - 1; } if (yy < 0) { yy = 0; } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } scanBuf[x] = scaledMask->data[yy * scaledWidth + xx]; } // clip the scan line if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, state->strokeAdjust); } } // draw the scan line (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL); } } delete scaledMask; } // Scale an image mask into a SplashBitmap. SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, gFalse); if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { scaleMaskYdXd(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYdXu(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { scaleMaskYuXd(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { if (interpolate) { scaleMaskYuXuI(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYuXu(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } } return dest; } void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint *pixBuf; Guint pix; Guchar *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d0 = (255 << 23) / (yStep * xp); d1 = (255 << 23) / (yStep * (xp + 1)); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += pixBuf[xx++]; } // (255 * pix) / xStep * yStep pix = (pix * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix; } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint *pixBuf; Guint pix; Guchar *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d = (255 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = pixBuf[x]; // (255 * pix) / yStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix; } } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint pix; Guchar *destPtr0, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; destPtr0 = dest->data; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; d0 = (255 << 23) / xp; d1 = (255 << 23) / (xp + 1); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += lineBuf[xx++]; } // (255 * pix) / xStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + i * scaledWidth + x; *destPtr = (Guchar)pix; } } destPtr0 += yStep * scaledWidth; } gfree(lineBuf); } void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guchar pix; Guchar *srcPtr, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; int i; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; // generate one row srcPtr = lineBuf; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = *srcPtr ? 255 : 0; ++srcPtr; // duplicate the pixel horizontally for (i = 0; i < xStep; ++i) { *destPtr++ = pix; } } // duplicate the row vertically for (i = 1 ; i < yStep; ++i) { memcpy(destPtr, destPtr - scaledWidth, scaledWidth); destPtr += scaledWidth; } } gfree(lineBuf); } void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf0, *lineBuf1, *tBuf; Guchar pix; SplashCoord yr, xr, ys, xs, ySrc, xSrc; int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x; Guchar *destPtr; // ratios yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; // allocate buffers lineBuf0 = (Guchar *)gmalloc(scaledWidth); lineBuf1 = (Guchar *)gmalloc(scaledWidth); // read first two rows (*src)(srcData, lineBuf0); if (srcHeight > 1) { (*src)(srcData, lineBuf1); yBuf = 1; } else { memcpy(lineBuf1, lineBuf0, srcWidth); yBuf = 0; } // interpolate first two rows for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } lineBuf0[x] = (Guchar)(int) ((xs * (int)lineBuf0[xSrc0] + ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1]) * 255); lineBuf1[x] = (Guchar)(int) ((xs * (int)lineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); } destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // compute vertical interpolation parameters ySrc = yr * y; ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); ySrc1 = ySrc0 + 1; ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); if (ySrc0 < 0) { ySrc0 = 0; ys = 1; } if (ySrc1 >= srcHeight) { ySrc1 = srcHeight - 1; ys = 0; } // read another row (if necessary) if (ySrc1 > yBuf) { tBuf = lineBuf0; lineBuf0 = lineBuf1; lineBuf1 = tBuf; (*src)(srcData, lineBuf1); // interpolate the row for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } lineBuf1[x] = (Guchar)(int) ((xs * (int)lineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); } ++yBuf; } // do the vertical interpolation for (x = 0; x < scaledWidth; ++x) { pix = (Guchar)(int)(ys * (int)lineBuf0[x] + ((SplashCoord)1 - ys) * (int)lineBuf1[x]); // store the pixel *destPtr++ = pix; } } gfree(lineBuf1); gfree(lineBuf0); } void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; int w, h, x0, x1, y0, y1, y, t; w = src->width; h = src->height; pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (clipRes == splashClipAllInside) { for (y = 0; y < h; ++y) { (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->data + y * (size_t)w, NULL); } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->data + (y - yDest) * (size_t)w + (x0 - xDest), x1 - x0); if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL); } } } } SplashError Splash::drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat, GBool interpolate) { GBool ok; SplashBitmap *scaledImg; SplashClipResult clipRes; GBool minorAxisZero; SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; int nComps; if (debugMode) { printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check color modes ok = gFalse; // make gcc happy nComps = 0; // make gcc happy switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: ok = srcMode == splashModeMono8; nComps = 1; break; case splashModeRGB8: case splashModeBGR8: ok = srcMode == splashModeRGB8; nComps = 3; break; #if SPLASH_CMYK case splashModeCMYK8: ok = srcMode == splashModeCMYK8; nComps = 4; break; #endif default: ok = gFalse; break; } if (!ok) { return splashErrModeMismatch; } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; // rough estimate of size of scaled image t0 = splashAbs(mat[0]); t1 = splashAbs(mat[1]); wSize = t0 > t1 ? t0 : t1; t0 = splashAbs(mat[2]); t1 = splashAbs(mat[3]); hSize = t0 > t1 ? t0 : t1; // stream-mode upscaling -- this is slower, so we only use it if the // upscaled image is large (in which case clipping should remove // many pixels) #if USE_FIXEDPOINT if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { #else if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || (wSize > w && hSize > h && wSize * hSize > 10000000) || ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { #endif upscaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate); // scaling only } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus horizontal flip } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus horizontal and vertical flips } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // all other cases } else { arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate); } return splashOk; } void Splash::upscaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate) { SplashClipResult clipRes; SplashPipe pipe; SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11; Guchar *unscaledAlpha, *alphaPtr; SplashCoord xMin, yMin, xMax, yMax, t; SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; SplashCoord ix, iy, sx, sy, pix0, pix1; SplashBitmapRowSize rowSize; int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i; // compute the bbox of the target quadrilateral xMin = xMax = mat[4]; t = mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } getImageBounds(xMin, xMax, &xMinI, &xMaxI); yMin = yMax = mat[5]; t = mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } getImageBounds(yMin, yMax, &yMinI, &yMaxI); // clipping clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } if (clipRes != splashClipAllInside) { if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { xMinI = tt; } if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { xMaxI = tt; } if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { yMinI = tt; } if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { yMaxI = tt; } } // invert the matrix det = mat[0] * mat[3] - mat[1] * mat[2]; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } det = (SplashCoord)1 / det; mi0 = det * mat[3] * srcWidth; mi1 = -det * mat[1] * srcHeight; mi2 = -det * mat[2] * srcWidth; mi3 = det * mat[0] * srcHeight; mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; // grab the image if (srcWidth > INT_MAX / nComps) { rowSize = -1; } else { rowSize = srcWidth * nComps; } unscaledImage = (SplashColorPtr)gmallocn64(srcHeight, rowSize); if (srcAlpha) { unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth); for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha; y < srcHeight; ++y, p += rowSize, alphaPtr += srcWidth) { (*src)(srcData, p, alphaPtr); } } else { unscaledAlpha = NULL; for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += rowSize) { (*src)(srcData, p, NULL); } } // draw it pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps); pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (y = yMinI; y < yMaxI; ++y) { p = pixelBuf; for (x = xMinI; x < xMaxI; ++x) { ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; if (interpolate) { if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { x0 = splashFloor(ix - 0.5); x1 = x0 + 1; sx = (ix - 0.5) - x0; y0 = splashFloor(iy - 0.5); y1 = y0 + 1; sy = (iy - 0.5) - y0; if (x0 < 0) { x0 = 0; } if (x1 >= srcWidth) { x1 = srcWidth - 1; } if (y0 < 0) { y0 = 0; } if (y1 >= srcHeight) { y1 = srcHeight - 1; } q00 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; q01 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x1 * nComps]; q10 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x0 * nComps]; q11 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x1 * nComps]; for (i = 0; i < nComps; ++i) { pix0 = ((SplashCoord)1 - sx) * (int)*q00++ + sx * (int)*q01++; pix1 = ((SplashCoord)1 - sx) * (int)*q10++ + sx * (int)*q11++; *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } if (srcAlpha) { pix0 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledAlpha[y0 * srcWidth + x0] + sx * (SplashCoord)unscaledAlpha[y0 * srcWidth + x1]; pix1 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledAlpha[y1 * srcWidth + x0] + sx * (SplashCoord)unscaledAlpha[y1 * srcWidth + x1]; scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } else { scanBuf[x] = 0xff; } } else { for (i = 0; i < nComps; ++i) { *p++ = 0; } scanBuf[x] = 0; } } else { x0 = splashFloor(ix); y0 = splashFloor(iy); if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { q = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; for (i = 0; i < nComps; ++i) { *p++ = *q++; } if (srcAlpha) { scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0]; } else { scanBuf[x] = 0xff; } } else { for (i = 0; i < nComps; ++i) { *p++ = 0; } scanBuf[x] = 0; } } } if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } } (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf); } gfree(pixelBuf); gfree(unscaledImage); gfree(unscaledAlpha); } void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate) { SplashBitmap *scaledImg; SplashClipResult clipRes; SplashPipe pipe; SplashColorPtr pixelBuf; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int y, xa, xb, x, i, xx, yy; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping xMin = splashRound(vx[0]); xMax = splashRound(vx[0]); yMin = splashRound(vy[0]); yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; } else if (t0 > xMax) { xMax = t0; } t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; } else if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } // compute the scale factors if (mat[0] >= 0) { t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in drawImage return; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); // construct the three sections i = 0; if (vy[1] < vy[i]) { i = 1; } if (vy[2] < vy[i]) { i = 2; } if (vy[3] < vy[i]) { i = 3; } // NB: if using fixed point, 0.000001 will be truncated to zero, // so these two comparisons must be <=, not < if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && vy[(i-1) & 3] < vy[(i+1) & 3]) { i = (i-1) & 3; } if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { section[0].y0 = splashRound(vy[i]); section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; section[0].ib0 = (i+1) & 3; section[0].ib1 = (i+2) & 3; } else { section[0].ia0 = (i+1) & 3; section[0].ia1 = (i+2) & 3; section[0].ib0 = i; section[0].ib1 = (i+3) & 3; } nSections = 1; } else { section[0].y0 = splashRound(vy[i]); section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i+2) & 3; if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[0].ia1 = section[2].ia0 = (i+1) & 3; section[0].ib1 = section[2].ib0 = (i+3) & 3; } else { section[0].ia1 = section[2].ia0 = (i+3) & 3; section[0].ib1 = section[2].ib0 = (i+1) & 3; } if (vy[(i+1) & 3] < vy[(i+3) & 3]) { section[1].y0 = splashRound(vy[(i+1) & 3]); section[2].y0 = splashRound(vy[(i+3) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = (i+1) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i+3) & 3; section[1].ib0 = (i+1) & 3; section[1].ib1 = (i+2) & 3; } } else { section[1].y0 = splashRound(vy[(i+3) & 3]); section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; section[1].ib0 = (i+3) & 3; section[1].ib1 = (i+2) & 3; } else { section[1].ia0 = (i+3) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps); // scan all pixels inside the target region for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = splashRound(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya); xb = splashRound(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb); if (xa > xb) { continue; } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } // check the scanBuf bounds if (xa >= bitmap->width || xb < 0) { continue; } if (xa < 0) { xa = 0; } if (xb > bitmap->width) { xb = bitmap->width; } // clip the scan line memset(scanBuf + xa, 0xff, xb - xa); if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, state->strokeAdjust); } } // draw the scan line for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (xx < 0) { xx = 0; } else if (xx >= scaledWidth) { xx = scaledWidth - 1; } if (yy < 0) { yy = 0; } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } // get the color scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps); // apply alpha if (srcAlpha) { scanBuf[x] = div255(scanBuf[x] * scaledImg->alpha[yy * scaledWidth + xx]); } } (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf); } } gfree(pixelBuf); delete scaledImg; } // Scale an image into a SplashBitmap. SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { if (interpolate) { scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } } return dest; } void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint *pixBuf, *alphaPixBuf; Guint pix0, pix1, pix2; #if SPLASH_CMYK Guint pix3; #endif Guint alpha; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = NULL; alphaPixBuf = NULL; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d0 = (1 << 23) / (yStep * xp); d1 = (1 << 23) / (yStep * (xp + 1)); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } switch (srcMode) { case splashModeMono8: // compute the final pixel pix0 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx++]; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; break; case splashModeRGB8: // compute the final pixel pix0 = pix1 = pix2 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx+1]; pix2 += pixBuf[xx+2]; xx += 3; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; *destPtr++ = (Guchar)pix1; *destPtr++ = (Guchar)pix2; break; #if SPLASH_CMYK case splashModeCMYK8: // compute the final pixel pix0 = pix1 = pix2 = pix3 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx+1]; pix2 += pixBuf[xx+2]; pix3 += pixBuf[xx+3]; xx += 4; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; pix3 = (pix3 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; *destPtr++ = (Guchar)pix1; *destPtr++ = (Guchar)pix2; *destPtr++ = (Guchar)pix3; break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // bgr8 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaPixBuf[xxa]; } // alpha / xStep * yStep alpha = (alpha * d) >> 23; *destAlphaPtr++ = (Guchar)alpha; } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); } void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint *pixBuf, *alphaPixBuf; Guint pix[splashMaxColorComps]; Guint alpha; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = NULL; alphaPixBuf = NULL; } // make gcc happy pix[0] = pix[1] = pix[2] = 0; #if SPLASH_CMYK pix[3] = 0; #endif // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d = (1 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel for (i = 0; i < nComps; ++i) { // pixBuf[] / yStep pix[i] = (pixBuf[x * nComps + i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; } break; case splashModeRGB8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; } break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[3]; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { // alphaPixBuf[] / yStep alpha = (alphaPixBuf[x] * d) >> 23; for (i = 0; i < xStep; ++i) { *destAlphaPtr++ = (Guchar)alpha; } } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); } void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint pix[splashMaxColorComps]; Guint alpha; Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); } else { alphaLineBuf = NULL; } // make gcc happy pix[0] = pix[1] = pix[2] = 0; #if SPLASH_CMYK pix[3] = 0; #endif // init y scale Bresenham yt = 0; destPtr0 = dest->data; destAlphaPtr0 = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; d0 = (1 << 23) / xp; d1 = (1 << 23) / (xp + 1); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel for (i = 0; i < nComps; ++i) { pix[i] = 0; } for (i = 0; i < xStep; ++i) { for (j = 0; j < nComps; ++j, ++xx) { pix[j] += lineBuf[xx]; } } for (i = 0; i < nComps; ++i) { // pix[] / xStep pix[i] = (pix[i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; } break; case splashModeRGB8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; } break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[3]; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaLineBuf[xxa]; } // alpha / xStep alpha = (alpha * d) >> 23; for (i = 0; i < yStep; ++i) { destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; *destAlphaPtr = (Guchar)alpha; } } } destPtr0 += yStep * scaledWidth * nComps; if (srcAlpha) { destAlphaPtr0 += yStep * scaledWidth; } } gfree(alphaLineBuf); gfree(lineBuf); } void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guchar pix0, pix1, pix2; #if SPLASH_CMYK Guchar pix3; #endif Guchar alpha; Guchar *srcPtr, *srcAlphaPtr; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; int i; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); } else { alphaLineBuf = NULL; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; // generate one row srcPtr = lineBuf; srcAlphaPtr = alphaLineBuf; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // duplicate the pixel horizontally switch (srcMode) { case splashModeMono8: pix0 = *srcPtr++; for (i = 0; i < xStep; ++i) { *destPtr++ = pix0; } break; case splashModeRGB8: pix0 = *srcPtr++; pix1 = *srcPtr++; pix2 = *srcPtr++; for (i = 0; i < xStep; ++i) { *destPtr++ = pix0; *destPtr++ = pix1; *destPtr++ = pix2; } break; #if SPLASH_CMYK case splashModeCMYK8: pix0 = *srcPtr++; pix1 = *srcPtr++; pix2 = *srcPtr++; pix3 = *srcPtr++; for (i = 0; i < xStep; ++i) { *destPtr++ = pix0; *destPtr++ = pix1; *destPtr++ = pix2; *destPtr++ = pix3; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // duplicate the alpha value horizontally if (srcAlpha) { alpha = *srcAlphaPtr++; for (i = 0; i < xStep; ++i) { *destAlphaPtr++ = alpha; } } } // duplicate the row vertically for (i = 1; i < yStep; ++i) { memcpy(destPtr, destPtr - scaledWidth * nComps, scaledWidth * nComps); destPtr += scaledWidth * nComps; } if (srcAlpha) { for (i = 1; i < yStep; ++i) { memcpy(destAlphaPtr, destAlphaPtr - scaledWidth, scaledWidth); destAlphaPtr += scaledWidth; } } } gfree(alphaLineBuf); gfree(lineBuf); } void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf; Guchar pix[splashMaxColorComps]; SplashCoord yr, xr, ys, xs, ySrc, xSrc; int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i; Guchar *destPtr, *destAlphaPtr; // ratios yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; // allocate buffers lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps); lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); if (srcAlpha) { alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth); alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); } else { alphaLineBuf0 = NULL; alphaLineBuf1 = NULL; } // read first two rows (*src)(srcData, lineBuf0, alphaLineBuf0); if (srcHeight > 1) { (*src)(srcData, lineBuf1, alphaLineBuf1); yBuf = 1; } else { memcpy(lineBuf1, lineBuf0, srcWidth * nComps); if (srcAlpha) { memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth); } yBuf = 0; } // interpolate first two rows for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } for (i = 0; i < nComps; ++i) { lineBuf0[x*nComps+i] = (Guchar)(int) (xs * (int)lineBuf0[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1*nComps+i]); lineBuf1[x*nComps+i] = (Guchar)(int) (xs * (int)lineBuf1[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); } if (srcAlpha) { alphaLineBuf0[x] = (Guchar)(int) (xs * (int)alphaLineBuf0[xSrc0] + ((SplashCoord)1 - xs) * (int)alphaLineBuf0[xSrc1]); alphaLineBuf1[x] = (Guchar)(int) (xs * (int)alphaLineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); } } // make gcc happy pix[0] = pix[1] = pix[2] = 0; #if SPLASH_CMYK pix[3] = 0; #endif destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // compute vertical interpolation parameters ySrc = yr * y; ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); ySrc1 = ySrc0 + 1; ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); if (ySrc0 < 0) { ySrc0 = 0; ys = 1; } if (ySrc1 >= srcHeight) { ySrc1 = srcHeight - 1; ys = 0; } // read another row (if necessary) if (ySrc1 > yBuf) { tBuf = lineBuf0; lineBuf0 = lineBuf1; lineBuf1 = tBuf; tBuf = alphaLineBuf0; alphaLineBuf0 = alphaLineBuf1; alphaLineBuf1 = tBuf; (*src)(srcData, lineBuf1, alphaLineBuf1); // interpolate the row for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } for (i = 0; i < nComps; ++i) { lineBuf1[x*nComps+i] = (Guchar)(int) (xs * (int)lineBuf1[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); } if (srcAlpha) { alphaLineBuf1[x] = (Guchar)(int) (xs * (int)alphaLineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); } } ++yBuf; } // do the vertical interpolation for (x = 0; x < scaledWidth; ++x) { for (i = 0; i < nComps; ++i) { pix[i] = (Guchar)(int) (ys * (int)lineBuf0[x*nComps+i] + ((SplashCoord)1 - ys) * (int)lineBuf1[x*nComps+i]); } // store the pixel switch (srcMode) { case splashModeMono8: *destPtr++ = pix[0]; break; case splashModeRGB8: *destPtr++ = pix[0]; *destPtr++ = pix[1]; *destPtr++ = pix[2]; break; #if SPLASH_CMYK case splashModeCMYK8: *destPtr++ = pix[0]; *destPtr++ = pix[1]; *destPtr++ = pix[2]; *destPtr++ = pix[3]; break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { *destAlphaPtr++ = (Guchar)(int) (ys * (int)alphaLineBuf0[x] + ((SplashCoord)1 - ys) * (int)alphaLineBuf1[x]); } } } gfree(alphaLineBuf1); gfree(alphaLineBuf0); gfree(lineBuf1); gfree(lineBuf0); } void Splash::vertFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; Guchar *p0, *p1; int w; w = width * nComps; lineBuf = (Guchar *)gmalloc(w); for (p0 = img->data, p1 = img->data + (height - 1) * (size_t)w; p0 < p1; p0 += w, p1 -= w) { memcpy(lineBuf, p0, w); memcpy(p0, p1, w); memcpy(p1, lineBuf, w); } if (img->alpha) { for (p0 = img->alpha, p1 = img->alpha + (height - 1) * (size_t)width; p0 < p1; p0 += width, p1 -= width) { memcpy(lineBuf, p0, width); memcpy(p0, p1, width); memcpy(p1, lineBuf, width); } } gfree(lineBuf); } void Splash::horizFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; SplashColorPtr p0, p1, p2; int w, x, y, i; w = width * nComps; lineBuf = (Guchar *)gmalloc(w); for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) { memcpy(lineBuf, p0, w); p1 = p0; p2 = lineBuf + (w - nComps); for (x = 0; x < width; ++x) { for (i = 0; i < nComps; ++i) { p1[i] = p2[i]; } p1 += nComps; p2 -= nComps; } } if (img->alpha) { for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) { memcpy(lineBuf, p0, width); p1 = p0; p2 = lineBuf + (width - 1); for (x = 0; x < width; ++x) { *p1++ = *p2--; } } } gfree(lineBuf); } void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; int w, h, x0, y0, x1, y1, y; // split the image into clipped and unclipped regions w = src->width; h = src->height; if (clipRes == splashClipAllInside) { x0 = 0; y0 = 0; x1 = w; y1 = h; } else { if (state->clip->getNumPaths()) { x0 = x1 = w; y0 = y1 = h; } else { if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { x0 = 0; } if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { y0 = 0; } if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { x1 = w; } if (x1 < x0) { x1 = x0; } if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { y1 = h; } if (y1 < y0) { y1 = y0; } } } // draw the unclipped region if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); if (srcAlpha) { for (y = y0; y < y1; ++y) { (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, src->alpha + y * src->alphaRowSize + x0, src->data + y * src->rowSize + x0 * bitmapComps); } } else { for (y = y0; y < y1; ++y) { (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, NULL, src->data + y * src->getRowSize() + x0 * bitmapComps); } } } // draw the clipped regions if (y0 > 0) { blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); } if (y1 < h) { blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); } if (x0 > 0 && y0 < y1) { blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); } if (x1 < w && y0 < y1) { blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, w - x1, y1 - y0); } } void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashPipe pipe; int y; if (xDest < 0) { xSrc -= xDest; w += xDest; xDest = 0; } if (xDest + w > bitmap->width) { w = bitmap->width - xDest; } if (yDest < 0) { ySrc -= yDest; h += yDest; yDest = 0; } if (yDest + h > bitmap->height) { h = bitmap->height - yDest; } if (w <= 0 || h <= 0) { return; } pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (srcAlpha) { for (y = 0; y < h; ++y) { memcpy(scanBuf + xDest, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, w); if (vectorAntialias) { state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, scanBuf + xDest, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } else { for (y = 0; y < h; ++y) { memset(scanBuf + xDest, 0xff, w); if (vectorAntialias) { state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, scanBuf + xDest, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } } SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, GBool noClip, GBool nonIsolated) { SplashPipe pipe; Guchar *mono1Ptr, *lineBuf, *linePtr; Guchar mono1Mask, b; int x0, x1, x, y0, y1, y, t; if (!(src->mode == bitmap->mode || (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) || (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) { return splashErrModeMismatch; } pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), !noClip || src->alpha != NULL, nonIsolated); if (src->mode == splashModeMono1) { // in mono1 mode, pipeRun expects the source to be in mono8 // format, so we need to extract the source color values into // scanBuf, expanding them from mono1 to mono8 if (noClip) { if (src->alpha) { for (y = 0; y < h; ++y) { mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, scanBuf); } } else { for (y = 0; y < h; ++y) { mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, NULL, scanBuf); } } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { if (src->alpha) { for (y = y0; y < y1; ++y) { mono1Ptr = src->data + (ySrc + y - yDest) * src->rowSize + ((xSrc + x0 - xDest) >> 3); mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); for (x = x0; x < x1; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } memcpy(scanBuf2 + x0, src->alpha + (ySrc + y - yDest) * src->alphaRowSize + (xSrc + x0 - xDest), x1 - x0); if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, state->strokeAdjust)) { continue; } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf2 + x0, scanBuf + x0); } } else { for (y = y0; y < y1; ++y) { mono1Ptr = src->data + (ySrc + y - yDest) * src->rowSize + ((xSrc + x0 - xDest) >> 3); mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); for (x = x0; x < x1; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } memset(scanBuf2 + x0, 0xff, x1 - x0); if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, state->strokeAdjust)) { continue; } (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf2 + x0, scanBuf + x0); } } } } } else if (src->mode == splashModeBGR8) { // in BGR8 mode, pipeRun expects the source to be in RGB8 format, // so we need to swap bytes lineBuf = (Guchar *)gmallocn(w, 3); if (noClip) { if (src->alpha) { for (y = 0; y < h; ++y) { memcpy(lineBuf, src->data + (ySrc + y) * src->rowSize + xSrc * 3, w * 3); for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, lineBuf); } } else { for (y = 0; y < h; ++y) { memcpy(lineBuf, src->data + (ySrc + y) * src->rowSize + xSrc * 3, w * 3); for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, NULL, lineBuf); } } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { if (src->alpha) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->alpha + (ySrc + y - yDest) * src->alphaRowSize + (xSrc + x0 - xDest), x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); memcpy(lineBuf, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * 3, (x1 - x0) * 3); for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, lineBuf); } } else { for (y = y0; y < y1; ++y) { memset(scanBuf + x0, 0xff, x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); memcpy(lineBuf, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * 3, (x1 - x0) * 3); for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, scanBuf + x0, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * bitmapComps); } } } } gfree(lineBuf); } else { // src->mode not mono1 or BGR8 if (noClip) { if (src->alpha) { for (y = 0; y < h; ++y) { // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } else { for (y = 0; y < h; ++y) { (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, NULL, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { if (src->alpha) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->alpha + (ySrc + y - yDest) * src->alphaRowSize + (xSrc + x0 - xDest), x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * bitmapComps); } } else { for (y = y0; y < y1; ++y) { memset(scanBuf + x0, 0xff, x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, scanBuf + x0, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * bitmapComps); } } } } } return splashOk; } void Splash::compositeBackground(SplashColorPtr color) { SplashColorPtr p; Guchar *q; Guchar alpha, alpha1, c, color0, color1, color2, mask; #if SPLASH_CMYK Guchar color3; #endif int x, y; switch (bitmap->mode) { case splashModeMono1: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; mask = 0x80; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { if (color0 & 0x80) { *p |= mask; } else { *p &= (Guchar)~mask; } } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); c = (*p & mask) ? 0xff : 0x00; c = div255(alpha1 * color0 + alpha * c); if (c & 0x80) { *p |= mask; } else { *p &= (Guchar)~mask; } } if (!(mask = (Guchar)(mask >> 1))) { mask = 0x80; ++p; } } } break; case splashModeMono8: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); p[0] = div255(alpha1 * color0 + alpha * p[0]); } ++p; } } break; case splashModeRGB8: case splashModeBGR8: color0 = color[0]; color1 = color[1]; color2 = color[2]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; p[1] = color1; p[2] = color2; } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); } p += 3; } } break; #if SPLASH_CMYK case splashModeCMYK8: color0 = color[0]; color1 = color[1]; color2 = color[2]; color3 = color[3]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; p[1] = color1; p[2] = color2; p[3] = color3; } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); p[3] = div255(alpha1 * color3 + alpha * p[3]); } p += 4; } } break; #endif } memset(bitmap->alpha, 255, bitmap->alphaRowSize * bitmap->height); } SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColorPtr p, q; Guchar mask, srcMask; int x, y; if (src->mode != bitmap->mode) { return splashErrModeMismatch; } switch (bitmap->mode) { case splashModeMono1: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; mask = (Guchar)(0x80 >> (xDest & 7)); q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; srcMask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { if (*q & srcMask) { *p |= mask; } else { *p &= (Guchar)~mask; } if (!(mask = (Guchar)(mask >> 1))) { mask = 0x80; ++p; } if (!(srcMask = (Guchar)(srcMask >> 1))) { srcMask = 0x80; ++q; } } } break; case splashModeMono8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; q = &src->data[(ySrc + y) * src->rowSize + xSrc]; memcpy(p, q, w); } break; case splashModeRGB8: case splashModeBGR8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; memcpy(p, q, 3 * w); } break; #if SPLASH_CMYK case splashModeCMYK8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; memcpy(p, q, 4 * w); } break; #endif } if (bitmap->alpha) { for (y = 0; y < h; ++y) { q = &bitmap->alpha[(yDest + y) * bitmap->alphaRowSize + xDest]; memset(q, 0, w); } } return splashOk; } SplashError Splash::blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColorPtr p, q; Guchar *alpha0Ptr; Guchar alpha0, aSrc, mask, srcMask; int x, y; if (bitmap->mode != dest->mode || !bitmap->alpha || !dest->alpha || !groupBackBitmap) { return splashErrModeMismatch; } switch (bitmap->mode) { case splashModeMono1: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + (xDest >> 3)]; mask = (Guchar)(0x80 >> (xDest & 7)); q = &bitmap->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)]; srcMask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { if (*q & srcMask) { *p |= mask; } else { *p &= (Guchar)~mask; } if (!(mask = (Guchar)(mask >> 1))) { mask = 0x80; ++p; } if (!(srcMask = (Guchar)(srcMask >> 1))) { srcMask = 0x80; ++q; } } } break; case splashModeMono8: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + xDest]; q = &bitmap->data[(ySrc + y) * bitmap->rowSize + xSrc]; memcpy(p, q, w); } break; case splashModeRGB8: case splashModeBGR8: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + 3 * xDest]; q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 3 * xSrc]; memcpy(p, q, 3 * w); } break; #if SPLASH_CMYK case splashModeCMYK8: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + 4 * xDest]; q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 4 * xSrc]; memcpy(p, q, 4 * w); } break; #endif } for (y = 0; y < h; ++y) { p = &dest->alpha[(yDest + y) * dest->alphaRowSize + xDest]; q = &bitmap->alpha[(ySrc + y) * bitmap->alphaRowSize + xSrc]; alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + ySrc + y) * groupBackBitmap->alphaRowSize + (groupBackX + xSrc)]; for (x = 0; x < w; ++x) { alpha0 = *alpha0Ptr++; aSrc = *q++; *p++ = (Guchar)(alpha0 + aSrc - div255(alpha0 * aSrc)); } } return splashOk; } SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, int lineCap, int lineJoin, GBool flatten) { SplashPath *pathIn, *dashPath, *pathOut; SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; SplashCoord crossprod, dotprod, miter, m; SplashCoord angle, angleNext, dAngle, xc, yc; SplashCoord dxJoin, dyJoin, dJoin, kappa; SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4; GBool first, last, closed; int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1; int left0, left1, left2, right0, right1, right2, join0, join1, join2; int leftFirst, rightFirst, firstPt; pathOut = new SplashPath(); if (path->length == 0) { return pathOut; } if (flatten) { pathIn = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { dashPath = makeDashedPath(pathIn); delete pathIn; pathIn = dashPath; if (pathIn->length == 0) { delete pathIn; return pathOut; } } } else { pathIn = path; } subpathStart0 = subpathStart1 = 0; // make gcc happy seg = 0; // make gcc happy closed = gFalse; // make gcc happy left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy leftFirst = rightFirst = firstPt = 0; // make gcc happy i0 = 0; for (i1 = i0; !(pathIn->flags[i1] & splashPathLast) && i1 + 1 < pathIn->length && pathIn->pts[i1+1].x == pathIn->pts[i1].x && pathIn->pts[i1+1].y == pathIn->pts[i1].y; ++i1) ; while (i1 < pathIn->length) { if ((first = pathIn->flags[i0] & splashPathFirst)) { subpathStart0 = i0; subpathStart1 = i1; seg = 0; closed = pathIn->flags[i0] & splashPathClosed; } j0 = i1 + 1; if (j0 < pathIn->length) { for (j1 = j0; !(pathIn->flags[j1] & splashPathLast) && j1 + 1 < pathIn->length && pathIn->pts[j1+1].x == pathIn->pts[j1].x && pathIn->pts[j1+1].y == pathIn->pts[j1].y; ++j1) ; } else { j1 = j0; } if (pathIn->flags[i1] & splashPathLast) { if (first && lineCap == splashLineCapRound) { // special case: zero-length subpath with round line caps --> // draw a circle pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y + (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y - (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->close(); } i0 = j0; i1 = j1; continue; } last = pathIn->flags[j1] & splashPathLast; if (last) { k0 = subpathStart1 + 1; } else { k0 = j1 + 1; } for (k1 = k0; !(pathIn->flags[k1] & splashPathLast) && k1 + 1 < pathIn->length && pathIn->pts[k1+1].x == pathIn->pts[k1].x && pathIn->pts[k1+1].y == pathIn->pts[k1].y; ++k1) ; // compute the deltas for segment (i1, j0) #if USE_FIXEDPOINT // the 1/d value can be small, which introduces significant // inaccuracies in fixed point mode d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, pathIn->pts[j0].x, pathIn->pts[j0].y); dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d; dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d; #else d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, pathIn->pts[j0].x, pathIn->pts[j0].y); dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x); dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y); #endif wdx = (SplashCoord)0.5 * w * dx; wdy = (SplashCoord)0.5 * w * dy; // draw the start cap if (i0 == subpathStart0) { firstPt = pathOut->length; } if (first && !closed) { switch (lineCap) { case splashLineCapButt: pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapRound: pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx, pathIn->pts[i0].y + wdx - bezierCircle * wdy, pathIn->pts[i0].x - wdx - bezierCircle * wdy, pathIn->pts[i0].y - wdy + bezierCircle * wdx, pathIn->pts[i0].x - wdx, pathIn->pts[i0].y - wdy); pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy, pathIn->pts[i0].y - wdy - bezierCircle * wdx, pathIn->pts[i0].x + wdy - bezierCircle * wdx, pathIn->pts[i0].y - wdx - bezierCircle * wdy, pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapProjecting: pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy, pathIn->pts[i0].y + wdx - wdy); pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy, pathIn->pts[i0].y - wdx - wdy); break; } } else { pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); } // draw the left side of the segment rectangle and the end cap left2 = pathOut->length - 1; if (last && !closed) { switch (lineCap) { case splashLineCapButt: pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapRound: pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx, pathIn->pts[j0].y - wdx + bezierCircle * wdy, pathIn->pts[j0].x + wdx + bezierCircle * wdy, pathIn->pts[j0].y + wdy - bezierCircle * wdx, pathIn->pts[j0].x + wdx, pathIn->pts[j0].y + wdy); pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy, pathIn->pts[j0].y + wdy + bezierCircle * wdx, pathIn->pts[j0].x - wdy + bezierCircle * wdx, pathIn->pts[j0].y + wdx + bezierCircle * wdy, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapProjecting: pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx, pathIn->pts[j0].y - wdx + wdy); pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx, pathIn->pts[j0].y + wdx + wdy); break; } } else { pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // draw the right side of the segment rectangle // (NB: if stroke adjustment is enabled, the closepath operation MUST // add a segment because this segment is used for a hint) right2 = pathOut->length - 1; pathOut->close(state->strokeAdjust != splashStrokeAdjustOff); // draw the join join2 = pathOut->length; if (!last || closed) { // compute the deltas for segment (j1, k0) #if USE_FIXEDPOINT // the 1/d value can be small, which introduces significant // inaccuracies in fixed point mode d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, pathIn->pts[k0].x, pathIn->pts[k0].y); dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d; dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d; #else d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, pathIn->pts[k0].x, pathIn->pts[k0].y); dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x); dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y); #endif wdxNext = (SplashCoord)0.5 * w * dxNext; wdyNext = (SplashCoord)0.5 * w * dyNext; // compute the join parameters crossprod = dx * dyNext - dy * dxNext; dotprod = -(dx * dxNext + dy * dyNext); if (dotprod > 0.9999) { // avoid a divide-by-zero -- set miter to something arbitrary // such that sqrt(miter) will exceed miterLimit (and m is never // used in that situation) // (note: the comparison value (0.9999) has to be less than // 1-epsilon, where epsilon is the smallest value // representable in the fixed point format) miter = (state->miterLimit + 1) * (state->miterLimit + 1); m = 0; } else { miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); if (miter < 1) { // this can happen because of floating point inaccuracies miter = 1; } m = splashSqrt(miter - 1); } // round join if (lineJoin == splashLineJoinRound) { // join angle < 180 if (crossprod < 0) { angle = atan2((double)dx, (double)-dy); angleNext = atan2((double)dxNext, (double)-dyNext); if (angle < angleNext) { angle += 2 * M_PI; } dAngle = (angle - angleNext) / M_PI; if (dAngle < 0.501) { // span angle is <= 90 degrees -> draw a single arc kappa = dAngle * bezierCircle * w; cx1 = pathIn->pts[j0].x - wdy + kappa * dx; cy1 = pathIn->pts[j0].y + wdx + kappa * dy; cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); pathOut->curveTo(cx2, cy2, cx1, cy1, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } else { // span angle is > 90 degrees -> split into two arcs dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext); if (dJoin > 0) { dxJoin = (-wdyNext + wdy) / dJoin; dyJoin = (wdxNext - wdx) / dJoin; xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos((double)((SplashCoord)0.5 * (angle + angleNext))); yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin((double)((SplashCoord)0.5 * (angle + angleNext))); kappa = dAngle * bezierCircle2 * w; cx1 = pathIn->pts[j0].x - wdy + kappa * dx; cy1 = pathIn->pts[j0].y + wdx + kappa * dy; cx2 = xc - kappa * dxJoin; cy2 = yc - kappa * dyJoin; cx3 = xc + kappa * dxJoin; cy3 = yc + kappa * dyJoin; cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc); pathOut->curveTo(cx2, cy2, cx1, cy1, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } } // join angle >= 180 } else { angle = atan2((double)-dx, (double)dy); angleNext = atan2((double)-dxNext, (double)dyNext); if (angleNext < angle) { angleNext += 2 * M_PI; } dAngle = (angleNext - angle) / M_PI; if (dAngle < 0.501) { // span angle is <= 90 degrees -> draw a single arc kappa = dAngle * bezierCircle * w; cx1 = pathIn->pts[j0].x + wdy + kappa * dx; cy1 = pathIn->pts[j0].y - wdx + kappa * dy; cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(cx1, cy1, cx2, cy2, pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } else { // span angle is > 90 degrees -> split into two arcs dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext); if (dJoin > 0) { dxJoin = (wdyNext - wdy) / dJoin; dyJoin = (-wdxNext + wdx) / dJoin; xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos((double)((SplashCoord)0.5 * (angle + angleNext))); yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin((double)((SplashCoord)0.5 * (angle + angleNext))); kappa = dAngle * bezierCircle2 * w; cx1 = pathIn->pts[j0].x + wdy + kappa * dx; cy1 = pathIn->pts[j0].y - wdx + kappa * dy; cx2 = xc - kappa * dxJoin; cy2 = yc - kappa * dyJoin; cx3 = xc + kappa * dxJoin; cy3 = yc + kappa * dyJoin; cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc); pathOut->curveTo(cx3, cy3, cx4, cy4, pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } } } } else { pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); // join angle < 180 if (crossprod < 0) { pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); // miter join inside limit if (lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m, pathIn->pts[j0].y + wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // join angle >= 180 } else { pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); // miter join inside limit if (lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m, pathIn->pts[j0].y - wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } } } pathOut->close(); } // add stroke adjustment hints if (state->strokeAdjust != splashStrokeAdjustOff) { // subpath with one segment if (seg == 0 && last) { switch (lineCap) { case splashLineCapButt: pathOut->addStrokeAdjustHint(firstPt, left2 + 1, firstPt, pathOut->length - 1); break; case splashLineCapProjecting: pathOut->addStrokeAdjustHint(firstPt, left2 + 1, firstPt, pathOut->length - 1, gTrue); break; case splashLineCapRound: break; } pathOut->addStrokeAdjustHint(left2, right2, firstPt, pathOut->length - 1); } else { // start of subpath if (seg == 1) { // start cap if (!closed) { switch (lineCap) { case splashLineCapButt: pathOut->addStrokeAdjustHint(firstPt, left1 + 1, firstPt, firstPt + 1); pathOut->addStrokeAdjustHint(firstPt, left1 + 1, right1 + 1, right1 + 1); break; case splashLineCapProjecting: pathOut->addStrokeAdjustHint(firstPt, left1 + 1, firstPt, firstPt + 1, gTrue); pathOut->addStrokeAdjustHint(firstPt, left1 + 1, right1 + 1, right1 + 1, gTrue); break; case splashLineCapRound: break; } } // first segment pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); } // middle of subpath if (seg > 1) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, left2); pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); } // end of subpath if (last) { if (closed) { // first segment pathOut->addStrokeAdjustHint(leftFirst, rightFirst, left2 + 1, right2); pathOut->addStrokeAdjustHint(leftFirst, rightFirst, join2, pathOut->length - 1); // last segment pathOut->addStrokeAdjustHint(left2, right2, left1 + 1, right1); pathOut->addStrokeAdjustHint(left2, right2, join1, pathOut->length - 1); pathOut->addStrokeAdjustHint(left2, right2, leftFirst - 1, leftFirst); pathOut->addStrokeAdjustHint(left2, right2, rightFirst + 1, rightFirst + 1); } else { // last segment pathOut->addStrokeAdjustHint(left2, right2, left1 + 1, right1); pathOut->addStrokeAdjustHint(left2, right2, join1, pathOut->length - 1); // end cap switch (lineCap) { case splashLineCapButt: pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, left2 + 1, left2 + 2); break; case splashLineCapProjecting: pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, left2 + 1, left2 + 2, gTrue); break; case splashLineCapRound: break; } } } } left0 = left1; left1 = left2; right0 = right1; right1 = right2; join0 = join1; join1 = join2; if (seg == 0) { leftFirst = left2; rightFirst = right2; } } i0 = j0; i1 = j1; ++seg; } if (pathIn != path) { delete pathIn; } return pathOut; } SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin, int *xMax, int *yMax) { int t; if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) { *xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) { *xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) { *yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) { *yMax = t; } if (*xMin >= *xMax || *yMin >= *yMax) { return splashClipAllOutside; } return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1, state->strokeAdjust); } void Splash::dumpPath(SplashPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n", i, (double)path->pts[i].x, (double)path->pts[i].y, (path->flags[i] & splashPathFirst) ? " first" : "", (path->flags[i] & splashPathLast) ? " last" : "", (path->flags[i] & splashPathClosed) ? " closed" : "", (path->flags[i] & splashPathCurve) ? " curve" : ""); } if (path->hintsLength == 0) { printf(" no hints\n"); } else { for (i = 0; i < path->hintsLength; ++i) { printf(" hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n", i, path->hints[i].ctrl0, path->hints[i].ctrl1, path->hints[i].firstPt, path->hints[i].lastPt); } } } void Splash::dumpXPath(SplashXPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n", i, (double)path->segs[i].x0, (double)path->segs[i].y0, (double)path->segs[i].x1, (double)path->segs[i].y1, path->segs[i].count); } } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10020/Splash.h000066400000000000000000000451351417746362400222240ustar00rootroot00000000000000//======================================================================== // // Splash.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASH_H #define SPLASH_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" #include "SplashClip.h" class Splash; class SplashBitmap; struct SplashGlyphBitmap; class SplashState; class SplashPattern; class SplashScreen; class SplashPath; class SplashXPath; class SplashFont; struct SplashPipe; //------------------------------------------------------------------------ // Retrieves the next line of pixels in an image mask. Normally, // fills in * and returns true. If the image stream is // exhausted, returns false. typedef GBool (*SplashImageMaskSource)(void *data, Guchar *pixel); // Retrieves the next line of pixels in an image. Normally, fills in // * and returns true. If the image stream is exhausted, // returns false. typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine, Guchar *alphaLine); //------------------------------------------------------------------------ enum SplashPipeResultColorCtrl { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, #if SPLASH_CMYK splashPipeResultColorNoAlphaBlendCMYK, #endif splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, #if SPLASH_CMYK splashPipeResultColorAlphaNoBlendCMYK, #endif splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaBlendCMYK #endif }; //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ class Splash { public: // Create a new rasterizer object. Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreenParams *screenParams = NULL); Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreen *screenA); ~Splash(); //----- state read SplashCoord *getMatrix(); SplashPattern *getStrokePattern(); SplashPattern *getFillPattern(); SplashScreen *getScreen(); SplashBlendFunc getBlendFunc(); SplashCoord getStrokeAlpha(); SplashCoord getFillAlpha(); SplashCoord getLineWidth(); int getLineCap(); int getLineJoin(); SplashCoord getMiterLimit(); SplashCoord getFlatness(); SplashCoord *getLineDash(); int getLineDashLength(); SplashCoord getLineDashPhase(); SplashStrokeAdjustMode getStrokeAdjust(); SplashClip *getClip(); SplashBitmap *getSoftMask(); GBool getInNonIsolatedGroup(); GBool getInKnockoutGroup(); //----- state write void setMatrix(SplashCoord *matrix); void setStrokePattern(SplashPattern *strokeColor); void setFillPattern(SplashPattern *fillColor); void setScreen(SplashScreen *screen); void setBlendFunc(SplashBlendFunc func); void setStrokeAlpha(SplashCoord alpha); void setFillAlpha(SplashCoord alpha); void setLineWidth(SplashCoord lineWidth); void setLineCap(int lineCap); void setLineJoin(int lineJoin); void setMiterLimit(SplashCoord miterLimit); void setFlatness(SplashCoord flatness); // the array will be copied void setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase); void setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust); // NB: uses transformed coordinates. void clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses transformed coordinates. SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses untransformed coordinates. SplashError clipToPath(SplashPath *path, GBool eo); void setSoftMask(SplashBitmap *softMask); void setInTransparencyGroup(SplashBitmap *groupBackBitmapA, int groupBackXA, int groupBackYA, GBool nonIsolated, GBool knockout); void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); void setOverprintMask(Guint overprintMask); void setEnablePathSimplification(GBool en); //----- state save/restore void saveState(); SplashError restoreState(); //----- drawing operations // Fill the bitmap with . This is not subject to clipping. void clear(SplashColorPtr color, Guchar alpha = 0x00); // Stroke a path using the current stroke pattern. SplashError stroke(SplashPath *path); // Fill a path using the current fill pattern. SplashError fill(SplashPath *path, GBool eo); // Draw a character, using the current fill pattern. SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font); // Draw a glyph, using the current fill pattern. This function does // not free any data, i.e., it ignores glyph->freeData. SplashError fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph); // Draws an image mask using the fill color. This will read // lines of pixels from , starting with the top line. "1" // pixels will be drawn with the current fill color; "0" pixels are // transparent. The matrix: // [ mat[0] mat[1] 0 ] // [ mat[2] mat[3] 0 ] // [ mat[4] mat[5] 1 ] // maps a unit square to the desired destination for the image, in // PostScript style: // [x' y' 1] = [x y 1] * mat // Note that the Splash y axis points downward, and the image source // is assumed to produce pixels in raster order, starting from the // top line. SplashError fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, GBool glyphMode, GBool interpolate); // Draw an image. This will read lines of pixels from // , starting with the top line. These pixels are assumed to // be in the source mode, . If is true, the // alpha values returned by are used; otherwise they are // ignored. The following combinations of source and target modes // are supported: // source target // ------ ------ // Mono8 Mono1 -- with dithering // Mono8 Mono8 // RGB8 RGB8 // BGR8 RGB8 // CMYK8 CMYK8 // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat, GBool interpolate); // Composite a rectangular region from onto this Splash // object. SplashError composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, GBool noClip, GBool nonIsolated); // Composite this Splash object onto a background color. The // background alpha is assumed to be 1. void compositeBackground(SplashColorPtr color); // Copy a rectangular region from onto the bitmap belonging to // this Splash object. The destination alpha values are all set to // zero. SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h); // Copy a rectangular region from the bitmap belonging to this // Splash object to . The alpha values are corrected for a // non-isolated group. SplashError blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, int xDest, int yDest, int w, int h); //----- misc // Construct a path for a stroke, given the path to be stroked and // the line width . All other stroke parameters are taken from // the current state. If is true, this function will // first flatten the path and handle the linedash. SplashPath *makeStrokePath(SplashPath *path, SplashCoord w, int lineCap, int lineJoin, GBool flatten = gTrue); // Reduce the size of a rectangle as much as possible by moving any // edges that are completely outside the clip region. Returns the // clipping status of the resulting rectangle. SplashClipResult limitRectToClipRect(int *xMin, int *yMin, int *xMax, int *yMax); // Return the associated bitmap. SplashBitmap *getBitmap() { return bitmap; } // Set the minimum line width. void setMinLineWidth(SplashCoord w) { minLineWidth = w; } // Get a bounding box which includes all modifications since the // last call to clearModRegion. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) { *xMin = modXMin; *yMin = modYMin; *xMax = modXMax; *yMax = modYMax; } // Clear the modified region bounding box. void clearModRegion(); // Get clipping status for the last drawing operation subject to // clipping. SplashClipResult getClipRes() { return opClipRes; } // Toggle debug mode on or off. void setDebugMode(GBool debugModeA) { debugMode = debugModeA; } #if 1 //~tmp: turn off anti-aliasing temporarily void setInShading(GBool sh) { inShading = sh; } #endif private: void pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup); void pipeRun(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void updateModX(int x); void updateModY(int y); void strokeNarrow(SplashPath *path); void drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); void strokeWide(SplashPath *path, SplashCoord w, int lineCap, int lineJoin); SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness); void flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath); SplashPath *makeDashedPath(SplashPath *xPath); SplashError fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha); SplashPath *tweakFillPath(SplashPath *path); GBool pathAllOutside(SplashPath *path); SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph); void getImageBounds(SplashCoord xyMin, SplashCoord xyMax, int *xyMinI, int *xyMaxI); void upscaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate); void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate); SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate); void scaleMaskYdXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYdXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes); void upscaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate); void arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate); SplashBitmap *scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate); void scaleImageYdXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYdXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXuI(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void vertFlipImage(SplashBitmap *img, int width, int height, int nComps); void horizFlipImage(SplashBitmap *img, int width, int height, int nComps); void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, SplashClipResult clipRes); void blitImageClipped(SplashBitmap *src, GBool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h); void dumpPath(SplashPath *path); void dumpXPath(SplashXPath *path); static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[]; static int pipeNonIsoGroupCorrection[]; SplashBitmap *bitmap; int bitmapComps; SplashState *state; Guchar *scanBuf; Guchar *scanBuf2; SplashBitmap // for transparency groups, this is the bitmap *groupBackBitmap; // containing the alpha0/color0 values int groupBackX, groupBackY; // offset within groupBackBitmap SplashCoord minLineWidth; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; GBool vectorAntialias; GBool inShading; GBool debugMode; }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10020/expected.txt000066400000000000000000000001061417746362400231500ustar00rootroot00000000000000Splash.cc:5559:bughuntingDivByZero Splash.cc:5560:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10021/000077500000000000000000000000001417746362400206125ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10021/Stream.cc000066400000000000000000003665671417746362400224030ustar00rootroot00000000000000//======================================================================== // // Stream.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include #include #include "gmem.h" #include "gmempp.h" #include "gfile.h" #if MULTITHREADED #include "GMutex.h" #endif #include "config.h" #include "Error.h" #include "Object.h" #include "Lexer.h" #include "GfxState.h" #include "Stream.h" #include "JBIG2Stream.h" #include "JPXStream.h" #include "Stream-CCITT.h" #ifdef __DJGPP__ static GBool setDJSYSFLAGS = gFalse; #endif #ifdef VMS #ifdef __GNUC__ #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #endif //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ Stream::Stream() { } Stream::~Stream() { } void Stream::close() { } int Stream::getRawChar() { error(errInternal, -1, "Called getRawChar() on non-predictor stream"); return EOF; } int Stream::getBlock(char *buf, int size) { int n, c; n = 0; while (n < size) { if ((c = getChar()) == EOF) { break; } buf[n++] = (char)c; } return n; } char *Stream::getLine(char *buf, int size) { int i; int c; if (lookChar() == EOF || size < 0) return NULL; for (i = 0; i < size - 1; ++i) { c = getChar(); if (c == EOF || c == '\n') break; if (c == '\r') { if ((c = lookChar()) == '\n') getChar(); break; } buf[i] = (char)c; } buf[i] = '\0'; return buf; } Guint Stream::discardChars(Guint n) { char buf[4096]; Guint count, i, j; count = 0; while (count < n) { if ((i = n - count) > sizeof(buf)) { i = (Guint)sizeof(buf); } j = (Guint)getBlock(buf, (int)i); count += j; if (j != i) { break; } } return count; } GString *Stream::getPSFilter(int psLevel, const char *indent) { return new GString(); } Stream *Stream::addFilters(Object *dict, int recursion) { Object obj, obj2; Object params, params2; Stream *str; int i; str = this; dict->dictLookup("Filter", &obj); if (obj.isNull()) { obj.free(); dict->dictLookup("F", &obj); } dict->dictLookup("DecodeParms", ¶ms); if (params.isNull()) { params.free(); dict->dictLookup("DP", ¶ms); } if (obj.isName()) { str = makeFilter(obj.getName(), str, ¶ms, recursion); } else if (obj.isArray()) { for (i = 0; i < obj.arrayGetLength(); ++i) { obj.arrayGet(i, &obj2, recursion); if (params.isArray()) params.arrayGet(i, ¶ms2, recursion); else params2.initNull(); if (obj2.isName()) { str = makeFilter(obj2.getName(), str, ¶ms2, recursion); } else { error(errSyntaxError, getPos(), "Bad filter name"); str = new EOFStream(str); } obj2.free(); params2.free(); } } else if (!obj.isNull()) { error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream"); } obj.free(); params.free(); return str; } Stream *Stream::makeFilter(char *name, Stream *str, Object *params, int recursion) { int pred; // parameters int colors; int bits; int early; int encoding; GBool endOfLine, byteAlign, endOfBlock, black; int columns, rows; int colorXform; Object globals, obj; if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) { str = new ASCIIHexStream(str); } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) { str = new ASCII85Stream(str); } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) { pred = 1; columns = 1; colors = 1; bits = 8; early = 1; if (params->isDict()) { params->dictLookup("Predictor", &obj, recursion); if (obj.isInt()) pred = obj.getInt(); obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) columns = obj.getInt(); obj.free(); params->dictLookup("Colors", &obj, recursion); if (obj.isInt()) colors = obj.getInt(); obj.free(); params->dictLookup("BitsPerComponent", &obj, recursion); if (obj.isInt()) bits = obj.getInt(); obj.free(); params->dictLookup("EarlyChange", &obj, recursion); if (obj.isInt()) early = obj.getInt(); obj.free(); } str = new LZWStream(str, pred, columns, colors, bits, early); } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) { str = new RunLengthStream(str); } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) { encoding = 0; endOfLine = gFalse; byteAlign = gFalse; columns = 1728; rows = 0; endOfBlock = gTrue; black = gFalse; if (params->isDict()) { params->dictLookup("K", &obj, recursion); if (obj.isInt()) { encoding = obj.getInt(); } obj.free(); params->dictLookup("EndOfLine", &obj, recursion); if (obj.isBool()) { endOfLine = obj.getBool(); } obj.free(); params->dictLookup("EncodedByteAlign", &obj, recursion); if (obj.isBool()) { byteAlign = obj.getBool(); } obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) { columns = obj.getInt(); } obj.free(); params->dictLookup("Rows", &obj, recursion); if (obj.isInt()) { rows = obj.getInt(); } obj.free(); params->dictLookup("EndOfBlock", &obj, recursion); if (obj.isBool()) { endOfBlock = obj.getBool(); } obj.free(); params->dictLookup("BlackIs1", &obj, recursion); if (obj.isBool()) { black = obj.getBool(); } obj.free(); } str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign, columns, rows, endOfBlock, black); } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) { colorXform = -1; if (params->isDict()) { if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) { colorXform = obj.getInt(); } obj.free(); } str = new DCTStream(str, colorXform); } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) { pred = 1; columns = 1; colors = 1; bits = 8; if (params->isDict()) { params->dictLookup("Predictor", &obj, recursion); if (obj.isInt()) pred = obj.getInt(); obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) columns = obj.getInt(); obj.free(); params->dictLookup("Colors", &obj, recursion); if (obj.isInt()) colors = obj.getInt(); obj.free(); params->dictLookup("BitsPerComponent", &obj, recursion); if (obj.isInt()) bits = obj.getInt(); obj.free(); } str = new FlateStream(str, pred, columns, colors, bits); } else if (!strcmp(name, "JBIG2Decode")) { if (params->isDict()) { params->dictLookup("JBIG2Globals", &globals, recursion); } str = new JBIG2Stream(str, &globals); globals.free(); } else if (!strcmp(name, "JPXDecode")) { str = new JPXStream(str); } else { error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); str = new EOFStream(str); } return str; } //------------------------------------------------------------------------ // BaseStream //------------------------------------------------------------------------ BaseStream::BaseStream(Object *dictA) { dict = *dictA; } BaseStream::~BaseStream() { dict.free(); } //------------------------------------------------------------------------ // FilterStream //------------------------------------------------------------------------ FilterStream::FilterStream(Stream *strA) { str = strA; } FilterStream::~FilterStream() { } void FilterStream::close() { str->close(); } void FilterStream::setPos(GFileOffset pos, int dir) { error(errInternal, -1, "Called setPos() on FilterStream"); } //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) { int imgLineSize; str = strA; width = widthA; nComps = nCompsA; nBits = nBitsA; nVals = width * nComps; inputLineSize = (nVals * nBits + 7) >> 3; if (width > INT_MAX / nComps || nVals > (INT_MAX - 7) / nBits) { // force a call to gmallocn(-1,...), which will throw an exception inputLineSize = -1; } inputLine = (char *)gmallocn(inputLineSize, sizeof(char)); if (nBits == 8) { imgLine = (Guchar *)inputLine; } else { if (nBits == 1) { imgLineSize = (nVals + 7) & ~7; } else { imgLineSize = nVals; } imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar)); } imgIdx = nVals; } ImageStream::~ImageStream() { if (imgLine != (Guchar *)inputLine) { gfree(imgLine); } gfree(inputLine); } void ImageStream::reset() { str->reset(); } void ImageStream::close() { str->close(); } GBool ImageStream::getPixel(Guchar *pix) { int i; if (imgIdx >= nVals) { if (!getLine()) { return gFalse; } imgIdx = 0; } for (i = 0; i < nComps; ++i) { pix[i] = imgLine[imgIdx++]; } return gTrue; } Guchar *ImageStream::getLine() { Gulong buf, bitMask; int bits; int c; int i; char *p; if (str->getBlock(inputLine, inputLineSize) != inputLineSize) { return NULL; } if (nBits == 1) { p = inputLine; for (i = 0; i < nVals; i += 8) { c = *p++; imgLine[i+0] = (Guchar)((c >> 7) & 1); imgLine[i+1] = (Guchar)((c >> 6) & 1); imgLine[i+2] = (Guchar)((c >> 5) & 1); imgLine[i+3] = (Guchar)((c >> 4) & 1); imgLine[i+4] = (Guchar)((c >> 3) & 1); imgLine[i+5] = (Guchar)((c >> 2) & 1); imgLine[i+6] = (Guchar)((c >> 1) & 1); imgLine[i+7] = (Guchar)(c & 1); } } else if (nBits == 8) { // special case: imgLine == inputLine } else if (nBits == 16) { for (i = 0; i < nVals; ++i) { imgLine[i] = (Guchar)inputLine[2*i]; } } else { bitMask = (1 << nBits) - 1; buf = 0; bits = 0; p = inputLine; for (i = 0; i < nVals; ++i) { if (bits < nBits) { buf = (buf << 8) | (*p++ & 0xff); bits += 8; } imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask); bits -= nBits; } } return imgLine; } void ImageStream::skipLine() { str->getBlock(inputLine, inputLineSize); } //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ StreamPredictor::StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA) { str = strA; predictor = predictorA; width = widthA; nComps = nCompsA; nBits = nBitsA; predLine = NULL; ok = gFalse; nVals = width * nComps; pixBytes = (nComps * nBits + 7) >> 3; rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes; if (width <= 0 || nComps <= 0 || nBits <= 0 || nComps > gfxColorMaxComps || nBits > 16 || width >= INT_MAX / nComps || // check for overflow in nVals nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes return; } predLine = (Guchar *)gmalloc(rowBytes); reset(); ok = gTrue; } StreamPredictor::~StreamPredictor() { gfree(predLine); } void StreamPredictor::reset() { memset(predLine, 0, rowBytes); predIdx = rowBytes; } int StreamPredictor::lookChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx]; } int StreamPredictor::getChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx++]; } int StreamPredictor::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (predIdx >= rowBytes) { if (!getNextLine()) { break; } } m = rowBytes - predIdx; if (m > size - n) { m = size - n; } memcpy(blk + n, predLine + predIdx, m); predIdx += m; n += m; } return n; } GBool StreamPredictor::getNextLine() { int curPred; Guchar upLeftBuf[gfxColorMaxComps * 2 + 1]; int left, up, upLeft, p, pa, pb, pc; int c; Gulong inBuf, outBuf, bitMask; int inBits, outBits; int i, j, k, kk; // get PNG optimum predictor number if (predictor >= 10) { if ((curPred = str->getRawChar()) == EOF) { return gFalse; } curPred += 10; } else { curPred = predictor; } // read the raw line, apply PNG (byte) predictor memset(upLeftBuf, 0, pixBytes + 1); for (i = pixBytes; i < rowBytes; ++i) { for (j = pixBytes; j > 0; --j) { upLeftBuf[j] = upLeftBuf[j-1]; } upLeftBuf[0] = predLine[i]; if ((c = str->getRawChar()) == EOF) { if (i > pixBytes) { // this ought to return false, but some (broken) PDF files // contain truncated image data, and Adobe apparently reads the // last partial line break; } return gFalse; } switch (curPred) { case 11: // PNG sub predLine[i] = (Guchar)(predLine[i - pixBytes] + c); break; case 12: // PNG up predLine[i] = (Guchar)(predLine[i] + c); break; case 13: // PNG average predLine[i] = (Guchar)(((predLine[i - pixBytes] + predLine[i]) >> 1) + c); break; case 14: // PNG Paeth left = predLine[i - pixBytes]; up = predLine[i]; upLeft = upLeftBuf[pixBytes]; p = left + up - upLeft; if ((pa = p - left) < 0) pa = -pa; if ((pb = p - up) < 0) pb = -pb; if ((pc = p - upLeft) < 0) pc = -pc; if (pa <= pb && pa <= pc) predLine[i] = (Guchar)(left + c); else if (pb <= pc) predLine[i] = (Guchar)(up + c); else predLine[i] = (Guchar)(upLeft + c); break; case 10: // PNG none default: // no predictor or TIFF predictor predLine[i] = (Guchar)c; break; } } // apply TIFF (component) predictor if (predictor == 2) { if (nBits == 8) { for (i = pixBytes; i < rowBytes; ++i) { predLine[i] = (Guchar)(predLine[i] + predLine[i - nComps]); } } else if (nBits == 16) { for (i = pixBytes; i < rowBytes; i += 2) { c = ((predLine[i] + predLine[i - 2*nComps]) << 8) + predLine[i + 1] + predLine[i + 1 - 2*nComps]; predLine[i] = (Guchar)(c >> 8); predLine[i+1] = (Guchar)(c & 0xff); } } else { memset(upLeftBuf, 0, nComps); bitMask = (1 << nBits) - 1; inBuf = outBuf = 0; inBits = outBits = 0; j = k = pixBytes; for (i = 0; i < width; ++i) { for (kk = 0; kk < nComps; ++kk) { if (inBits < nBits) { inBuf = (inBuf << 8) | (predLine[j++] & 0xff); inBits += 8; } upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] + (inBuf >> (inBits - nBits))) & bitMask); inBits -= nBits; outBuf = (outBuf << nBits) | upLeftBuf[kk]; outBits += nBits; if (outBits >= 8) { predLine[k++] = (Guchar)(outBuf >> (outBits - 8)); outBits -= 8; } } } if (outBits > 0) { predLine[k++] = (Guchar)((outBuf << (8 - outBits)) + (inBuf & ((1 << (8 - outBits)) - 1))); } } } // reset to start of line predIdx = pixBytes; return gTrue; } //------------------------------------------------------------------------ // SharedFile //------------------------------------------------------------------------ class SharedFile { public: SharedFile(FILE *fA); SharedFile *copy(); void free(); int readBlock(char *buf, GFileOffset pos, int size); GFileOffset getSize(); private: ~SharedFile(); FILE *f; int refCnt; #if MULTITHREADED GMutex mutex; #endif }; SharedFile::SharedFile(FILE *fA) { f = fA; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } SharedFile::~SharedFile() { #if MULTITHREADED gDestroyMutex(&mutex); #endif } SharedFile *SharedFile::copy() { #if MULTITHREADED gLockMutex(&mutex); #endif ++refCnt; #if MULTITHREADED gUnlockMutex(&mutex); #endif return this; } void SharedFile::free() { int newCount; #if MULTITHREADED gLockMutex(&mutex); #endif newCount = --refCnt; #if MULTITHREADED gUnlockMutex(&mutex); #endif if (newCount == 0) { delete this; } } int SharedFile::readBlock(char *buf, GFileOffset pos, int size) { int n; #if MULTITHREADED gLockMutex(&mutex); #endif gfseek(f, pos, SEEK_SET); n = (int)fread(buf, 1, size, f); #if MULTITHREADED gUnlockMutex(&mutex); #endif return n; } GFileOffset SharedFile::getSize() { GFileOffset size; #if MULTITHREADED gLockMutex(&mutex); #endif gfseek(f, 0, SEEK_END); size = gftell(f); #if MULTITHREADED gUnlockMutex(&mutex); #endif return size; } //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA): BaseStream(dictA) { f = new SharedFile(fA); start = startA; limited = limitedA; length = lengthA; bufPtr = bufEnd = buf; bufPos = start; } FileStream::FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA): BaseStream(dictA) { f = fA->copy(); start = startA; limited = limitedA; length = lengthA; bufPtr = bufEnd = buf; bufPos = start; } FileStream::~FileStream() { f->free(); } Stream *FileStream::copy() { Object dictA; dict.copy(&dictA); return new FileStream(f, start, limited, length, &dictA); } Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA) { return new FileStream(f, startA, limitedA, lengthA, dictA); } void FileStream::reset() { bufPtr = bufEnd = buf; bufPos = start; } int FileStream::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > size - n) { m = size - n; } memcpy(blk + n, bufPtr, m); bufPtr += m; n += m; } return n; } GBool FileStream::fillBuf() { int n; bufPos += (int)(bufEnd - buf); bufPtr = bufEnd = buf; if (limited && bufPos >= start + length) { return gFalse; } if (limited && bufPos + fileStreamBufSize > start + length) { n = (int)(start + length - bufPos); } else { n = fileStreamBufSize; } n = f->readBlock(buf, bufPos, n); bufEnd = buf + n; if (bufPtr >= bufEnd) { return gFalse; } return gTrue; } void FileStream::setPos(GFileOffset pos, int dir) { GFileOffset size; if (dir >= 0) { bufPos = pos; } else { size = f->getSize(); if (pos <= size) { bufPos = size - pos; } else { bufPos = 0; } } bufPtr = bufEnd = buf; } void FileStream::moveStart(int delta) { start += delta; bufPtr = bufEnd = buf; bufPos = start; } //------------------------------------------------------------------------ // MemStream //------------------------------------------------------------------------ MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA): BaseStream(dictA) { buf = bufA; start = startA; length = lengthA; bufEnd = buf + start + length; bufPtr = buf + start; needFree = gFalse; } MemStream::~MemStream() { if (needFree) { gfree(buf); } } Stream *MemStream::copy() { Object dictA; dict.copy(&dictA); return new MemStream(buf, start, length, &dictA); } Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited, GFileOffset lengthA, Object *dictA) { MemStream *subStr; Guint newStart, newLength; if (startA < start) { newStart = start; } else if (startA > start + length) { newStart = start + (int)length; } else { newStart = (int)startA; } if (!limited || newStart + lengthA > start + length) { newLength = start + length - newStart; } else { newLength = (Guint)lengthA; } subStr = new MemStream(buf, newStart, newLength, dictA); return subStr; } void MemStream::reset() { bufPtr = buf + start; } void MemStream::close() { } int MemStream::getBlock(char *blk, int size) { int n; if (size <= 0) { return 0; } if (bufEnd - bufPtr < size) { n = (int)(bufEnd - bufPtr); } else { n = size; } memcpy(blk, bufPtr, n); bufPtr += n; return n; } void MemStream::setPos(GFileOffset pos, int dir) { Guint i; if (dir >= 0) { i = (Guint)pos; } else { i = (Guint)(start + length - pos); } if (i < start) { i = start; } else if (i > start + length) { i = start + length; } bufPtr = buf + i; } void MemStream::moveStart(int delta) { start += delta; length -= delta; bufPtr = buf + start; } //------------------------------------------------------------------------ // EmbedStream //------------------------------------------------------------------------ EmbedStream::EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA): BaseStream(dictA) { str = strA; limited = limitedA; length = lengthA; } EmbedStream::~EmbedStream() { } Stream *EmbedStream::copy() { Object dictA; dict.copy(&dictA); return new EmbedStream(str, &dictA, limited, length); } Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA, GFileOffset lengthA, Object *dictA) { error(errInternal, -1, "Called makeSubStream() on EmbedStream"); return NULL; } int EmbedStream::getChar() { if (limited && !length) { return EOF; } --length; return str->getChar(); } int EmbedStream::lookChar() { if (limited && !length) { return EOF; } return str->lookChar(); } int EmbedStream::getBlock(char *blk, int size) { if (size <= 0) { return 0; } if (limited && length < (Guint)size) { size = (int)length; } length -= size; return str->getBlock(blk, size); } void EmbedStream::setPos(GFileOffset pos, int dir) { error(errInternal, -1, "Called setPos() on EmbedStream"); } GFileOffset EmbedStream::getStart() { error(errInternal, -1, "Called getStart() on EmbedStream"); return 0; } void EmbedStream::moveStart(int delta) { error(errInternal, -1, "Called moveStart() on EmbedStream"); } //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ ASCIIHexStream::ASCIIHexStream(Stream *strA): FilterStream(strA) { buf = EOF; eof = gFalse; } ASCIIHexStream::~ASCIIHexStream() { delete str; } Stream *ASCIIHexStream::copy() { return new ASCIIHexStream(str->copy()); } void ASCIIHexStream::reset() { str->reset(); buf = EOF; eof = gFalse; } int ASCIIHexStream::lookChar() { int c1, c2, x; if (buf != EOF) return buf; if (eof) { buf = EOF; return EOF; } do { c1 = str->getChar(); } while (isspace(c1)); if (c1 == '>') { eof = gTrue; buf = EOF; return buf; } do { c2 = str->getChar(); } while (isspace(c2)); if (c2 == '>') { eof = gTrue; c2 = '0'; } if (c1 >= '0' && c1 <= '9') { x = (c1 - '0') << 4; } else if (c1 >= 'A' && c1 <= 'F') { x = (c1 - 'A' + 10) << 4; } else if (c1 >= 'a' && c1 <= 'f') { x = (c1 - 'a' + 10) << 4; } else if (c1 == EOF) { eof = gTrue; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c1); x = 0; } if (c2 >= '0' && c2 <= '9') { x += c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { x += c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { x += c2 - 'a' + 10; } else if (c2 == EOF) { eof = gTrue; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c2); } buf = x & 0xff; return buf; } GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/ASCIIHexDecode filter\n"); return s; } GBool ASCIIHexStream::isBinary(GBool last) { return str->isBinary(gFalse); } //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ ASCII85Stream::ASCII85Stream(Stream *strA): FilterStream(strA) { index = n = 0; eof = gFalse; } ASCII85Stream::~ASCII85Stream() { delete str; } Stream *ASCII85Stream::copy() { return new ASCII85Stream(str->copy()); } void ASCII85Stream::reset() { str->reset(); index = n = 0; eof = gFalse; } int ASCII85Stream::lookChar() { int k; Gulong t; if (index >= n) { if (eof) return EOF; index = 0; do { c[0] = str->getChar(); } while (Lexer::isSpace(c[0])); if (c[0] == '~' || c[0] == EOF) { eof = gTrue; n = 0; return EOF; } else if (c[0] == 'z') { b[0] = b[1] = b[2] = b[3] = 0; n = 4; } else { for (k = 1; k < 5; ++k) { do { c[k] = str->getChar(); } while (Lexer::isSpace(c[k])); if (c[k] == '~' || c[k] == EOF) break; } n = k - 1; if (k < 5 && (c[k] == '~' || c[k] == EOF)) { for (++k; k < 5; ++k) c[k] = 0x21 + 84; eof = gTrue; } t = 0; for (k = 0; k < 5; ++k) t = t * 85 + (c[k] - 0x21); for (k = 3; k >= 0; --k) { b[k] = (int)(t & 0xff); t >>= 8; } } } return b[index]; } GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/ASCII85Decode filter\n"); return s; } GBool ASCII85Stream::isBinary(GBool last) { return str->isBinary(gFalse); } //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA): FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = NULL; } } else { pred = NULL; } early = earlyA; eof = gFalse; inputBits = 0; clearTable(); } LZWStream::~LZWStream() { if (pred) { delete pred; } delete str; } Stream *LZWStream::copy() { if (pred) { return new LZWStream(str->copy(), pred->getPredictor(), pred->getWidth(), pred->getNComps(), pred->getNBits(), early); } else { return new LZWStream(str->copy(), 1, 0, 0, 0, early); } } int LZWStream::getChar() { if (pred) { return pred->getChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } int LZWStream::lookChar() { if (pred) { return pred->lookChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex]; } int LZWStream::getRawChar() { if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } int LZWStream::getBlock(char *blk, int size) { int n, m; if (pred) { return pred->getBlock(blk, size); } if (eof) { return 0; } n = 0; while (n < size) { if (seqIndex >= seqLength) { if (!processNextCode()) { break; } } m = seqLength - seqIndex; if (m > size - n) { m = size - n; } memcpy(blk + n, seqBuf + seqIndex, m); seqIndex += m; n += m; } return n; } void LZWStream::reset() { str->reset(); if (pred) { pred->reset(); } eof = gFalse; inputBits = 0; clearTable(); } GBool LZWStream::processNextCode() { int code; int nextLength; int i, j; // check for EOF if (eof) { return gFalse; } // check for eod and clear-table codes start: code = getCode(); if (code == EOF || code == 257) { eof = gTrue; return gFalse; } if (code == 256) { clearTable(); goto start; } if (nextCode >= 4097) { error(errSyntaxError, getPos(), "Bad LZW stream - expected clear-table code"); clearTable(); } // process the next code nextLength = seqLength + 1; if (code < 256) { seqBuf[0] = (Guchar)code; seqLength = 1; } else if (code < nextCode) { seqLength = table[code].length; for (i = seqLength - 1, j = code; i > 0; --i) { seqBuf[i] = table[j].tail; j = table[j].head; } seqBuf[0] = (Guchar)j; } else if (code == nextCode) { seqBuf[seqLength] = (Guchar)newChar; ++seqLength; } else { error(errSyntaxError, getPos(), "Bad LZW stream - unexpected code"); eof = gTrue; return gFalse; } newChar = seqBuf[0]; if (first) { first = gFalse; } else { table[nextCode].length = nextLength; table[nextCode].head = prevCode; table[nextCode].tail = (Guchar)newChar; ++nextCode; if (nextCode + early == 512) nextBits = 10; else if (nextCode + early == 1024) nextBits = 11; else if (nextCode + early == 2048) nextBits = 12; } prevCode = code; // reset buffer seqIndex = 0; return gTrue; } void LZWStream::clearTable() { nextCode = 258; nextBits = 9; seqIndex = seqLength = 0; first = gTrue; } int LZWStream::getCode() { int c; int code; while (inputBits < nextBits) { if ((c = str->getChar()) == EOF) return EOF; inputBuf = (inputBuf << 8) | (c & 0xff); inputBits += 8; } code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1); inputBits -= nextBits; return code; } GString *LZWStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2 || pred) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< "); if (!early) { s->append("/EarlyChange 0 "); } s->append(">> /LZWDecode filter\n"); return s; } GBool LZWStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ RunLengthStream::RunLengthStream(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; eof = gFalse; } RunLengthStream::~RunLengthStream() { delete str; } Stream *RunLengthStream::copy() { return new RunLengthStream(str->copy()); } void RunLengthStream::reset() { str->reset(); bufPtr = bufEnd = buf; eof = gFalse; } int RunLengthStream::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > size - n) { m = size - n; } memcpy(blk + n, bufPtr, m); bufPtr += m; n += m; } return n; } GString *RunLengthStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/RunLengthDecode filter\n"); return s; } GBool RunLengthStream::isBinary(GBool last) { return str->isBinary(gTrue); } GBool RunLengthStream::fillBuf() { int c; int n, i; if (eof) return gFalse; c = str->getChar(); if (c == 0x80 || c == EOF) { eof = gTrue; return gFalse; } if (c < 0x80) { n = c + 1; for (i = 0; i < n; ++i) buf[i] = (char)str->getChar(); } else { n = 0x101 - c; c = str->getChar(); for (i = 0; i < n; ++i) buf[i] = (char)c; } bufPtr = buf; bufEnd = buf + n; return gTrue; } //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, GBool byteAlignA, int columnsA, int rowsA, GBool endOfBlockA, GBool blackA): FilterStream(strA) { encoding = encodingA; endOfLine = endOfLineA; byteAlign = byteAlignA; columns = columnsA; if (columns < 1) { columns = 1; } else if (columns > INT_MAX - 3) { columns = INT_MAX - 3; } rows = rowsA; endOfBlock = endOfBlockA; black = blackA; blackXOR = black ? 0xff : 0x00; // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns // ---> max codingLine size = columns + 1 // refLine has two extra guard entries at the end // ---> max refLine size = columns + 3 codingLine = (int *)gmallocn(columns + 1, sizeof(int)); refLine = (int *)gmallocn(columns + 3, sizeof(int)); eof = gFalse; row = 0; nextLine2D = encoding < 0; inputBits = 0; codingLine[0] = columns; nextCol = columns; a0i = 0; err = gFalse; nErrors = 0; } CCITTFaxStream::~CCITTFaxStream() { delete str; gfree(refLine); gfree(codingLine); } Stream *CCITTFaxStream::copy() { return new CCITTFaxStream(str->copy(), encoding, endOfLine, byteAlign, columns, rows, endOfBlock, black); } void CCITTFaxStream::reset() { int code1; str->reset(); eof = gFalse; row = 0; nextLine2D = encoding < 0; inputBits = 0; codingLine[0] = columns; nextCol = columns; a0i = 0; // skip any initial zero bits and end-of-line marker, and get the 2D // encoding tag while ((code1 = lookBits(12)) == 0) { eatBits(1); } if (code1 == 0x001) { eatBits(12); endOfLine = gTrue; } if (encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } } int CCITTFaxStream::getChar() { int c, bitsNeeded, bitsAvail, bitsUsed; if (nextCol >= columns) { if (eof) { return EOF; } if (!readRow()) { return EOF; } } bitsAvail = codingLine[a0i] - nextCol; if (bitsAvail > 8) { c = (a0i & 1) ? 0x00 : 0xff; } else { c = 0; bitsNeeded = 8; do { bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; c <<= bitsUsed; if (!(a0i & 1)) { c |= 0xff >> (8 - bitsUsed); } bitsAvail -= bitsUsed; bitsNeeded -= bitsUsed; if (bitsAvail == 0) { if (codingLine[a0i] >= columns) { c <<= bitsNeeded; break; } ++a0i; bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; } } while (bitsNeeded > 0); } nextCol += 8; c ^= blackXOR; return c; } int CCITTFaxStream::lookChar() { int c, bitsNeeded, bitsAvail, bitsUsed, i; if (nextCol >= columns) { if (eof) { return EOF; } if (!readRow()) { return EOF; } } bitsAvail = codingLine[a0i] - nextCol; if (bitsAvail >= 8) { c = (a0i & 1) ? 0x00 : 0xff; } else { i = a0i; c = 0; bitsNeeded = 8; do { bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; c <<= bitsUsed; if (!(i & 1)) { c |= 0xff >> (8 - bitsUsed); } bitsAvail -= bitsUsed; bitsNeeded -= bitsUsed; if (bitsAvail == 0) { if (codingLine[i] >= columns) { c <<= bitsNeeded; break; } ++i; bitsAvail = codingLine[i] - codingLine[i - 1]; } } while (bitsNeeded > 0); } c ^= blackXOR; return c; } int CCITTFaxStream::getBlock(char *blk, int size) { int bytesRead, bitsAvail, bitsNeeded, bitsUsed, byte, c; bytesRead = 0; while (bytesRead < size) { if (nextCol >= columns) { if (eof) { break; } if (!readRow()) { break; } } bitsAvail = codingLine[a0i] - nextCol; byte = (a0i & 1) ? 0x00 : 0xff; if (bitsAvail > 8) { c = byte; bitsAvail -= 8; } else { c = 0; bitsNeeded = 8; do { bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; c <<= bitsUsed; c |= byte >> (8 - bitsUsed); bitsAvail -= bitsUsed; bitsNeeded -= bitsUsed; if (bitsAvail == 0) { if (codingLine[a0i] >= columns) { c <<= bitsNeeded; break; } ++a0i; bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; byte ^= 0xff; } } while (bitsNeeded > 0); } nextCol += 8; blk[bytesRead++] = (char)(c ^ blackXOR); } return bytesRead; } inline void CCITTFaxStream::addPixels(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = gTrue; ++nErrors; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } } inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = gTrue; ++nErrors; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } else if (a1 < codingLine[a0i]) { if (a1 < 0) { error(errSyntaxError, getPos(), "Invalid CCITTFax code"); err = gTrue; ++nErrors; a1 = 0; } while (a0i > 0 && a1 <= codingLine[a0i - 1]) { --a0i; } codingLine[a0i] = a1; } } GBool CCITTFaxStream::readRow() { int code1, code2, code3; int b1i, blackPixels, i; GBool gotEOL; // if at eof just return EOF if (eof) { return gFalse; } err = gFalse; // 2-D encoding if (nextLine2D) { for (i = 0; codingLine[i] < columns; ++i) { refLine[i] = codingLine[i]; } refLine[i++] = columns; refLine[i++] = columns; refLine[i] = columns; codingLine[0] = 0; a0i = 0; b1i = 0; blackPixels = 0; // invariant: // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] // <= columns // exception at left edge: // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible // exception at right edge: // refLine[b1i] = refLine[b1i+1] = columns is possible while (codingLine[a0i] < columns) { code1 = getTwoDimCode(); switch (code1) { case twoDimPass: addPixels(refLine[b1i + 1], blackPixels); if (refLine[b1i + 1] < columns) { b1i += 2; } break; case twoDimHoriz: code1 = code2 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); do { code2 += code3 = getWhiteCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); do { code2 += code3 = getBlackCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); if (codingLine[a0i] < columns) { addPixels(codingLine[a0i] + code2, blackPixels ^ 1); } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } break; case twoDimVertR3: addPixels(refLine[b1i] + 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertR2: addPixels(refLine[b1i] + 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertR1: addPixels(refLine[b1i] + 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVert0: addPixels(refLine[b1i], blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL3: addPixelsNeg(refLine[b1i] - 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL2: addPixelsNeg(refLine[b1i] - 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL1: addPixelsNeg(refLine[b1i] - 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case EOF: addPixels(columns, 0); err = gTrue; break; default: error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); addPixels(columns, 0); err = gTrue; ++nErrors; break; } } // 1-D encoding } else { codingLine[0] = 0; a0i = 0; blackPixels = 0; while (codingLine[a0i] < columns) { code1 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); blackPixels ^= 1; } } // check for end-of-line marker, skipping over any extra zero bits // (if EncodedByteAlign is true and EndOfLine is false, there can // be "false" EOL markers -- i.e., if the last n unused bits in // row i are set to zero, and the first 11-n bits in row i+1 // happen to be zero -- so we don't look for EOL markers in this // case) gotEOL = gFalse; if (!endOfBlock && row == rows - 1) { eof = gTrue; } else if (endOfLine || !byteAlign) { code1 = lookBits(12); if (endOfLine) { while (code1 != EOF && code1 != 0x001) { eatBits(1); code1 = lookBits(12); } } else { while (code1 == 0) { eatBits(1); code1 = lookBits(12); } } if (code1 == 0x001) { eatBits(12); gotEOL = gTrue; } } // byte-align the row // (Adobe apparently doesn't do byte alignment after EOL markers // -- I've seen CCITT image data streams in two different formats, // both with the byteAlign flag set: // 1. xx:x0:01:yy:yy // 2. xx:00:1y:yy:yy // where xx is the previous line, yy is the next line, and colons // separate bytes.) if (byteAlign && !gotEOL) { inputBits &= ~7; } // check for end of stream if (lookBits(1) == EOF) { eof = gTrue; } // get 2D encoding tag if (!eof && encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } // check for end-of-block marker if (endOfBlock && !endOfLine && byteAlign) { // in this case, we didn't check for an EOL code above, so we // need to check here code1 = lookBits(24); if (code1 == 0x001001) { eatBits(12); gotEOL = gTrue; } } if (endOfBlock && gotEOL) { code1 = lookBits(12); if (code1 == 0x001) { eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } if (encoding >= 0) { for (i = 0; i < 4; ++i) { code1 = lookBits(12); if (code1 != 0x001) { error(errSyntaxError, getPos(), "Bad RTC code in CCITTFax stream"); ++nErrors; } eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } } } eof = gTrue; } // look for an end-of-line marker after an error -- we only do // this if we know the stream contains end-of-line markers because // the "just plow on" technique tends to work better otherwise } else if (err && endOfLine) { while (1) { code1 = lookBits(13); if (code1 == EOF) { eof = gTrue; return gFalse; } if ((code1 >> 1) == 0x001) { break; } eatBits(1); } eatBits(12); if (encoding > 0) { eatBits(1); nextLine2D = !(code1 & 1); } } // corrupt CCITTFax streams can generate huge data expansion -- we // avoid that case by aborting decode after 1000 errors if (nErrors > 1000) { error(errSyntaxError, getPos(), "Too many errors in CCITTFaxStream - aborting decode"); eof = gTrue; return gFalse; } // set up for output nextCol = 0; a0i = (codingLine[0] > 0) ? 0 : 1; ++row; return gTrue; } short CCITTFaxStream::getTwoDimCode() { int code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { if ((code = lookBits(7)) != EOF) { p = &twoDimTab1[code]; if (p->bits > 0) { eatBits(p->bits); return p->n; } } } else { for (n = 1; n <= 7; ++n) { if ((code = lookBits(n)) == EOF) { break; } if (n < 7) { code <<= 7 - n; } p = &twoDimTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad two dim code ({0:04x}) in CCITTFax stream", code); ++nErrors; return EOF; } short CCITTFaxStream::getWhiteCode() { short code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(12); if (code == EOF) { return 1; } if ((code >> 5) == 0) { p = &whiteTab1[code]; } else { p = &whiteTab2[code >> 3]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 1; n <= 9; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 9) { code = (short)(code << (9 - n)); } p = &whiteTab2[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 11; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code = (short)(code << (12 - n)); } p = &whiteTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad white code ({0:04x}) in CCITTFax stream", code); ++nErrors; // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::getBlackCode() { short code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(13); if (code == EOF) { return 1; } if ((code >> 7) == 0) { p = &blackTab1[code]; } else if ((code >> 9) == 0 && (code >> 7) != 0) { p = &blackTab2[(code >> 1) - 64]; } else { p = &blackTab3[code >> 7]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 2; n <= 6; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 6) { code = (short)(code << (6 - n)); } p = &blackTab3[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 7; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code = (short)(code << (12 - n)); } if (code >= 64) { p = &blackTab2[code - 64]; if (p->bits == n) { eatBits(n); return p->n; } } } for (n = 10; n <= 13; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 13) { code = (short)(code << (13 - n)); } p = &blackTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad black code ({0:04x}) in CCITTFax stream", code); ++nErrors; // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::lookBits(int n) { int c; while (inputBits < n) { if ((c = str->getChar()) == EOF) { if (inputBits == 0) { return EOF; } // near the end of the stream, the caller may ask for more bits // than are available, but there may still be a valid code in // however many bits are available -- we need to return correct // data in this case return (short)((inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n))); } inputBuf = (inputBuf << 8) + c; inputBits += 8; } return (short)((inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n))); } GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { GString *s; char s1[50]; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< "); if (encoding != 0) { sprintf(s1, "/K %d ", encoding); s->append(s1); } if (endOfLine) { s->append("/EndOfLine true "); } if (byteAlign) { s->append("/EncodedByteAlign true "); } sprintf(s1, "/Columns %d ", columns); s->append(s1); if (rows != 0) { sprintf(s1, "/Rows %d ", rows); s->append(s1); } if (!endOfBlock) { s->append("/EndOfBlock false "); } if (black) { s->append("/BlackIs1 true "); } s->append(">> /CCITTFaxDecode filter\n"); return s; } GBool CCITTFaxStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ #if HAVE_JPEGLIB DCTStream::DCTStream(Stream *strA, GBool colorXformA): FilterStream(strA) { colorXform = colorXformA; lineBuf = NULL; inlineImage = str->isEmbedStream(); } DCTStream::~DCTStream() { delete str; } Stream *DCTStream::copy() { return new DCTStream(str->copy(), colorXform); } void DCTStream::reset() { int i; lineBuf = NULL; error = gFalse; str->reset(); // initialize the libjpeg decompression object decomp.err = jpeg_std_error(&errorMgr.err); errorMgr.err.error_exit = &errorExit; errorMgr.err.output_message = &errorMessage; if (setjmp(errorMgr.setjmpBuf)) { error = gTrue; return; } jpeg_create_decompress(&decomp); // set up the data source manager sourceMgr.src.next_input_byte = NULL; sourceMgr.src.bytes_in_buffer = 0; sourceMgr.src.init_source = &initSourceCbk; sourceMgr.src.fill_input_buffer = &fillInputBufferCbk; sourceMgr.src.skip_input_data = &skipInputDataCbk; sourceMgr.src.resync_to_restart = &jpeg_resync_to_restart; sourceMgr.src.term_source = &termSourceCbk; sourceMgr.str = this; decomp.src = &sourceMgr.src; // read the header jpeg_read_header(&decomp, TRUE); jpeg_calc_output_dimensions(&decomp); // set up the color transform if (!decomp.saw_Adobe_marker && colorXform >= 0) { if (decomp.num_components == 3) { decomp.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB; decomp.out_color_space = JCS_RGB; decomp.out_color_components = 3; } else if (decomp.num_components == 4) { decomp.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK; decomp.out_color_space = JCS_CMYK; decomp.out_color_components = 4; } } // allocate a line buffer if ((lineBufHeight = decomp.rec_outbuf_height) > 4) { lineBufHeight = 4; } lineBuf = (char *)gmallocn(lineBufHeight * decomp.out_color_components, decomp.output_width); for (i = 0; i < lineBufHeight; ++i) { lineBufRows[i] = lineBuf + i * decomp.out_color_components * decomp.output_width; } bufPtr = bufEnd = lineBuf; // start up the decompression process jpeg_start_decompress(&decomp); } void DCTStream::close() { // we don't call jpeg_finish_decompress() here because it will report // an error if the full image wasn't read if (setjmp(errorMgr.setjmpBuf)) { goto skip; } jpeg_destroy_decompress(&decomp); skip: gfree(lineBuf); FilterStream::close(); } int DCTStream::getChar() { if (error) { return EOF; } if (bufPtr == bufEnd) { if (!fillBuf()) { return EOF; } } return *bufPtr++ & 0xff; } int DCTStream::lookChar() { if (error) { return EOF; } if (bufPtr == bufEnd) { if (!fillBuf()) { return EOF; } } return *bufPtr & 0xff; } int DCTStream::getBlock(char *blk, int size) { int nRead, nAvail, n; if (error) { return 0; } nRead = 0; while (nRead < size) { if (bufPtr == bufEnd) { if (!fillBuf()) { break; } } nAvail = bufEnd - bufPtr; n = (nAvail < size - nRead) ? nAvail : size - nRead; memcpy(blk + nRead, bufPtr, n); bufPtr += n; nRead += n; } return nRead; } GBool DCTStream::fillBuf() { int nLines; if (setjmp(errorMgr.setjmpBuf)) { error = gTrue; return gFalse; } nLines = jpeg_read_scanlines(&decomp, (JSAMPARRAY)lineBufRows, lineBufHeight); bufPtr = lineBuf; bufEnd = lineBuf + nLines * decomp.out_color_components * decomp.output_width; return nLines > 0; } void DCTStream::errorExit(j_common_ptr d) { DCTErrorMgr *errMgr = (DCTErrorMgr *)d->err; longjmp(errMgr->setjmpBuf, 1); } void DCTStream::errorMessage(j_common_ptr d) { #if 0 // for debugging char buf[JMSG_LENGTH_MAX]; (*d->err->format_message)(d, buf); fprintf(stderr, "%s\n", buf); #endif } void DCTStream::initSourceCbk(j_decompress_ptr d) { DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; sourceMgr->src.next_input_byte = NULL; sourceMgr->src.bytes_in_buffer = 0; } boolean DCTStream::fillInputBufferCbk(j_decompress_ptr d) { DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; int c, n; // for inline images, we need to read one byte at a time so we don't // read past the end of the input data if (sourceMgr->str->inlineImage) { c = sourceMgr->str->str->getChar(); if (c == EOF) { sourceMgr->buf[0] = (char)0xff; sourceMgr->buf[1] = (char)JPEG_EOI; sourceMgr->src.bytes_in_buffer = 2; } else { sourceMgr->buf[0] = (char)c; sourceMgr->src.bytes_in_buffer = 1; } } else { n = sourceMgr->str->str->getBlock(sourceMgr->buf, dctStreamBufSize); if (n > 0) { sourceMgr->src.bytes_in_buffer = (size_t)n; } else { sourceMgr->buf[0] = (char)0xff; sourceMgr->buf[1] = (char)JPEG_EOI; sourceMgr->src.bytes_in_buffer = 2; } } sourceMgr->src.next_input_byte = (JOCTET *)sourceMgr->buf; return TRUE; } void DCTStream::skipInputDataCbk(j_decompress_ptr d, long numBytes) { DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; if (numBytes > 0) { if ((long)sourceMgr->src.bytes_in_buffer < numBytes) { sourceMgr->str->str->discardChars( (Guint)(numBytes - sourceMgr->src.bytes_in_buffer)); sourceMgr->src.bytes_in_buffer = 0; } else { sourceMgr->src.bytes_in_buffer -= numBytes; sourceMgr->src.next_input_byte += numBytes; } } } void DCTStream::termSourceCbk(j_decompress_ptr d) { } #else // HAVE_JPEGLIB #define idctScaleA 1024 #define idctScaleB 1138 #define idctScaleC 1730 #define idctScaleD 1609 #define idctScaleE 1264 #define idctScaleF 1922 #define idctScaleG 1788 #define idctScaleH 2923 #define idctScaleI 2718 #define idctScaleJ 2528 static int idctScaleMat[64] = { idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE, idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE }; // color conversion parameters (16.16 fixed point format) #define dctCrToR 91881 // 1.4020 #define dctCbToG -22553 // -0.3441363 #define dctCrToG -46802 // -0.71413636 #define dctCbToB 116130 // 1.772 // The dctClip function clips signed integers to the [0,255] range. // To handle valid DCT inputs, this must support an input range of at // least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF // files) can result in arbitrary values, so we want to mask those // out. We round the input range size up to a power of 2 (so we can // use a bit mask), which gives us an input range of [-384,639]. The // end result is: // input output // ---------- ------ // <-384 X invalid inputs -> output is "don't care" // -384..-257 0 invalid inputs, clipped // -256..-1 0 valid inputs, need to be clipped // 0..255 0..255 // 256..511 255 valid inputs, need to be clipped // 512..639 255 invalid inputs, clipped // >=512 X invalid inputs -> output is "don't care" #define dctClipOffset 384 #define dctClipMask 1023 static Guchar dctClipData[1024]; static inline void dctClipInit() { static int initDone = 0; int i; if (!initDone) { for (i = -384; i < 0; ++i) { dctClipData[dctClipOffset + i] = 0; } for (i = 0; i < 256; ++i) { dctClipData[dctClipOffset + i] = (Guchar)i; } for (i = 256; i < 639; ++i) { dctClipData[dctClipOffset + i] = 255; } initDone = 1; } } static inline Guchar dctClip(int x) { return dctClipData[(dctClipOffset + x) & dctClipMask]; } // zig zag decode map static int dctZigZag[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; DCTStream::DCTStream(Stream *strA, GBool colorXformA): FilterStream(strA) { int i; colorXform = colorXformA; progressive = interleaved = gFalse; width = height = 0; mcuWidth = mcuHeight = 0; numComps = 0; comp = 0; x = y = 0; for (i = 0; i < 4; ++i) { frameBuf[i] = NULL; } rowBuf = NULL; memset(dcHuffTables, 0, sizeof(dcHuffTables)); memset(acHuffTables, 0, sizeof(acHuffTables)); dctClipInit(); } DCTStream::~DCTStream() { close(); delete str; } Stream *DCTStream::copy() { return new DCTStream(str->copy(), colorXform); } void DCTStream::reset() { int i; str->reset(); progressive = interleaved = gFalse; width = height = 0; numComps = 0; numQuantTables = 0; numDCHuffTables = 0; numACHuffTables = 0; gotJFIFMarker = gFalse; gotAdobeMarker = gFalse; restartInterval = 0; if (!readHeader(gTrue)) { // force an EOF condition progressive = gTrue; y = height; return; } // compute MCU size if (numComps == 1) { compInfo[0].hSample = compInfo[0].vSample = 1; } mcuWidth = compInfo[0].hSample; mcuHeight = compInfo[0].vSample; for (i = 1; i < numComps; ++i) { if (compInfo[i].hSample > mcuWidth) { mcuWidth = compInfo[i].hSample; } if (compInfo[i].vSample > mcuHeight) { mcuHeight = compInfo[i].vSample; } } mcuWidth *= 8; mcuHeight *= 8; // figure out color transform if (colorXform == -1) { if (numComps == 3) { if (gotJFIFMarker) { colorXform = 1; } else if (compInfo[0].id == 82 && compInfo[1].id == 71 && compInfo[2].id == 66) { // ASCII "RGB" colorXform = 0; } else { colorXform = 1; } } else { colorXform = 0; } } if (progressive || !interleaved) { // allocate a buffer for the whole image bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight; if (bufWidth <= 0 || bufHeight <= 0 || bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) { error(errSyntaxError, getPos(), "Invalid image size in DCT stream"); y = height; return; } for (i = 0; i < numComps; ++i) { frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int)); } // read the image data do { restartMarker = 0xd0; restart(); readScan(); } while (readHeader(gFalse)); // decode decodeImage(); // initialize counters comp = 0; x = 0; y = 0; } else { if (scanInfo.numComps != numComps) { error(errSyntaxError, getPos(), "Invalid scan in sequential DCT stream"); y = height; return; } // allocate a buffer for one row of MCUs bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth); rowBufPtr = rowBufEnd = rowBuf; // initialize counters y = -mcuHeight; restartMarker = 0xd0; restart(); } } void DCTStream::close() { int i; for (i = 0; i < 4; ++i) { gfree(frameBuf[i]); frameBuf[i] = NULL; } gfree(rowBuf); rowBuf = NULL; FilterStream::close(); } int DCTStream::getChar() { int c; if (progressive || !interleaved) { if (y >= height) { return EOF; } c = frameBuf[comp][y * bufWidth + x]; if (++comp == numComps) { comp = 0; if (++x == width) { x = 0; ++y; } } } else { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { return EOF; } y += mcuHeight; if (!readMCURow()) { y = height; return EOF; } } c = *rowBufPtr++; } return c; } int DCTStream::lookChar() { if (progressive || !interleaved) { if (y >= height) { return EOF; } return frameBuf[comp][y * bufWidth + x]; } else { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { return EOF; } if (!readMCURow()) { y = height; return EOF; } } return *rowBufPtr; } } int DCTStream::getBlock(char *blk, int size) { int nRead, nAvail, n; if (progressive || !interleaved) { if (y >= height) { return 0; } for (nRead = 0; nRead < size; ++nRead) { blk[nRead] = (char)frameBuf[comp][y * bufWidth + x]; if (++comp == numComps) { comp = 0; if (++x == width) { x = 0; ++y; if (y >= height) { ++nRead; break; } } } } } else { nRead = 0; while (nRead < size) { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { break; } y += mcuHeight; if (!readMCURow()) { y = height; break; } } nAvail = (int)(rowBufEnd - rowBufPtr); n = (nAvail < size - nRead) ? nAvail : size - nRead; memcpy(blk + nRead, rowBufPtr, n); rowBufPtr += n; nRead += n; } } return nRead; } void DCTStream::restart() { int i; inputBits = 0; restartCtr = restartInterval; for (i = 0; i < numComps; ++i) { compInfo[i].prevDC = 0; } eobRun = 0; } // Read one row of MCUs from a sequential JPEG stream. GBool DCTStream::readMCURow() { int data1[64]; Guchar data2[64]; Guchar *p1, *p2; int pY, pCb, pCr, pR, pG, pB; int h, v, horiz, vert, hSub, vSub; int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int c; for (cc = 0; cc < numComps; ++cc) { if (scanInfo.dcHuffTable[cc] >= numDCHuffTables || scanInfo.acHuffTable[cc] >= numACHuffTables) { error(errSyntaxError, getPos(), "Bad DCT data: invalid Huffman table index"); return gFalse; } if (compInfo[cc].quantTable > numQuantTables) { error(errSyntaxError, getPos(), "Bad DCT data: invalid quant table index"); return gFalse; } } for (x1 = 0; x1 < width; x1 += mcuWidth) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return gFalse; } if (++restartMarker == 0xd8) restartMarker = 0xd0; restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data1)) { return gFalse; } transformDataUnit(quantTables[compInfo[cc].quantTable], data1, data2); if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; p1[0] = data2[i]; p1[ numComps] = data2[i+1]; p1[2*numComps] = data2[i+2]; p1[3*numComps] = data2[i+3]; p1[4*numComps] = data2[i+4]; p1[5*numComps] = data2[i+5]; p1[6*numComps] = data2[i+6]; p1[7*numComps] = data2[i+7]; } } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) { for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; p2 = p1 + width * numComps; p1[0] = p1[numComps] = p2[0] = p2[numComps] = data2[i]; p1[2*numComps] = p1[3*numComps] = p2[2*numComps] = p2[3*numComps] = data2[i+1]; p1[4*numComps] = p1[5*numComps] = p2[4*numComps] = p2[5*numComps] = data2[i+2]; p1[6*numComps] = p1[7*numComps] = p2[6*numComps] = p2[7*numComps] = data2[i+3]; p1[8*numComps] = p1[9*numComps] = p2[8*numComps] = p2[9*numComps] = data2[i+4]; p1[10*numComps] = p1[11*numComps] = p2[10*numComps] = p2[11*numComps] = data2[i+5]; p1[12*numComps] = p1[13*numComps] = p2[12*numComps] = p2[13*numComps] = data2[i+6]; p1[14*numComps] = p1[15*numComps] = p2[14*numComps] = p2[15*numComps] = data2[i+7]; } } else { p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc]; i = 0; for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { for (y5 = 0; y5 < vSub; ++y5) { for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) { p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i]; } } ++i; } } } } } } --restartCtr; } // color space conversion if (colorXform) { // convert YCbCr to RGB if (numComps == 3) { for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) { pY = p1[0]; pCb = p1[1] - 128; pCr = p1[2] - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; p1[0] = dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; p1[1] = dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; p1[2] = dctClip(pB); } // convert YCbCrK to CMYK (K is passed through unchanged) } else if (numComps == 4) { for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) { pY = p1[0]; pCb = p1[1] - 128; pCr = p1[2] - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; p1[0] = (Guchar)(255 - dctClip(pR)); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; p1[1] = (Guchar)(255 - dctClip(pG)); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; p1[2] = (Guchar)(255 - dctClip(pB)); } } } rowBufPtr = rowBuf; if (y + mcuHeight <= height) { rowBufEnd = rowBuf + numComps * width * mcuHeight; } else { rowBufEnd = rowBuf + numComps * width * (height - y); } return gTrue; } // Read one scan from a progressive or non-interleaved JPEG stream. void DCTStream::readScan() { int data[64]; int x1, y1, dx1, dy1, x2, y2, y3, cc, i; int h, v, horiz, vert, vSub; int *p1; int c; for (cc = 0; cc < numComps; ++cc) { if (scanInfo.comp[cc] && (scanInfo.dcHuffTable[cc] >= numDCHuffTables || ((!progressive || scanInfo.lastCoeff > 0) && scanInfo.acHuffTable[cc] >= numACHuffTables))) { error(errSyntaxError, getPos(), "Bad DCT data: invalid Huffman table index"); return; } if (compInfo[cc].quantTable > numQuantTables) { error(errSyntaxError, getPos(), "Bad DCT data: invalid quant table index"); return; } } if (scanInfo.numComps == 1) { for (cc = 0; cc < numComps; ++cc) { if (scanInfo.comp[cc]) { break; } } dx1 = mcuWidth / compInfo[cc].hSample; dy1 = mcuHeight / compInfo[cc].vSample; } else { dx1 = mcuWidth; dy1 = mcuHeight; } for (y1 = 0; y1 < height; y1 += dy1) { for (x1 = 0; x1 < width; x1 += dx1) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return; } if (++restartMarker == 0xd8) { restartMarker = 0xd0; } restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { if (!scanInfo.comp[cc]) { continue; } h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; vSub = vert / 8; for (y2 = 0; y2 < dy1; y2 += vert) { for (x2 = 0; x2 < dx1; x2 += horiz) { // pull out the current values p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { data[i] = p1[0]; data[i+1] = p1[1]; data[i+2] = p1[2]; data[i+3] = p1[3]; data[i+4] = p1[4]; data[i+5] = p1[5]; data[i+6] = p1[6]; data[i+7] = p1[7]; p1 += bufWidth * vSub; } // read one data unit if (progressive) { if (!readProgressiveDataUnit( &dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } else { if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } // add the data unit into frameBuf p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = data[i]; p1[1] = data[i+1]; p1[2] = data[i+2]; p1[3] = data[i+3]; p1[4] = data[i+4]; p1[5] = data[i+5]; p1[6] = data[i+6]; p1[7] = data[i+7]; p1 += bufWidth * vSub; } } } } --restartCtr; } } } // Read one data unit from a sequential JPEG stream. GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp; int c; int i, j; if ((size = readHuffSym(dcHuffTable)) == 9999) { return gFalse; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return gFalse; } } else { amp = 0; } data[0] = *prevDC += amp; for (i = 1; i < 64; ++i) { data[i] = 0; } i = 1; while (i < 64) { run = 0; while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) { run += 0x10; } if (c == 9999) { return gFalse; } if (c == 0x00) { break; } else { run += (c >> 4) & 0x0f; size = c & 0x0f; amp = readAmp(size); if (amp == 9999) { return gFalse; } i += run; if (i < 64) { j = dctZigZag[i++]; data[j] = amp; } } } return gTrue; } // Read one data unit from a progressive JPEG stream. GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp, bit, c; int i, j, k; // get the DC coefficient i = scanInfo.firstCoeff; if (i == 0) { if (scanInfo.ah == 0) { if ((size = readHuffSym(dcHuffTable)) == 9999) { return gFalse; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return gFalse; } } else { amp = 0; } data[0] += (*prevDC += amp) << scanInfo.al; } else { if ((bit = readBit()) == 9999) { return gFalse; } if (bit) { data[0] += 1 << scanInfo.al; } } ++i; } if (scanInfo.lastCoeff == 0) { return gTrue; } // check for an EOB run if (eobRun > 0) { while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } --eobRun; return gTrue; } // read the AC coefficients while (i <= scanInfo.lastCoeff) { if ((c = readHuffSym(acHuffTable)) == 9999) { return gFalse; } // ZRL if (c == 0xf0) { k = 0; while (k < 16 && i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] == 0) { ++k; } else { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } // EOB run } else if ((c & 0x0f) == 0x00) { j = c >> 4; eobRun = 0; for (k = 0; k < j; ++k) { if ((bit = readBit()) == EOF) { return gFalse; } eobRun = (eobRun << 1) | bit; } eobRun += 1 << j; while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } --eobRun; break; // zero run and one AC coefficient } else { run = (c >> 4) & 0x0f; size = c & 0x0f; if ((amp = readAmp(size)) == 9999) { return gFalse; } j = 0; // make gcc happy for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) { j = dctZigZag[i++]; while (data[j] != 0 && i <= scanInfo.lastCoeff) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } j = dctZigZag[i++]; } } data[j] = amp << scanInfo.al; } } return gTrue; } // Decode a progressive JPEG image. void DCTStream::decodeImage() { int dataIn[64]; Guchar dataOut[64]; Gushort *quantTable; int pY, pCb, pCr, pR, pG, pB; int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int h, v, horiz, vert, hSub, vSub; int *p0, *p1, *p2; for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) { for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) { for (cc = 0; cc < numComps; ++cc) { quantTable = quantTables[compInfo[cc].quantTable]; h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { // pull out the coded data unit p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { dataIn[i] = p1[0]; dataIn[i+1] = p1[1]; dataIn[i+2] = p1[2]; dataIn[i+3] = p1[3]; dataIn[i+4] = p1[4]; dataIn[i+5] = p1[5]; dataIn[i+6] = p1[6]; dataIn[i+7] = p1[7]; p1 += bufWidth * vSub; } // transform transformDataUnit(quantTable, dataIn, dataOut); // store back into frameBuf, doing replication for // subsampled components p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; if (hSub == 1 && vSub == 1) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = dataOut[i] & 0xff; p1[1] = dataOut[i+1] & 0xff; p1[2] = dataOut[i+2] & 0xff; p1[3] = dataOut[i+3] & 0xff; p1[4] = dataOut[i+4] & 0xff; p1[5] = dataOut[i+5] & 0xff; p1[6] = dataOut[i+6] & 0xff; p1[7] = dataOut[i+7] & 0xff; p1 += bufWidth; } } else if (hSub == 2 && vSub == 2) { p2 = p1 + bufWidth; for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff; p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff; p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff; p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff; p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff; p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff; p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff; p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff; p1 += bufWidth * 2; p2 += bufWidth * 2; } } else { i = 0; for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { p2 = p1 + x4; for (y5 = 0; y5 < vSub; ++y5) { for (x5 = 0; x5 < hSub; ++x5) { p2[x5] = dataOut[i] & 0xff; } p2 += bufWidth; } ++i; } p1 += bufWidth * vSub; } } } } } // color space conversion if (colorXform) { // convert YCbCr to RGB if (numComps == 3) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = dctClip(pB); } } // convert YCbCrK to CMYK (K is passed through unchanged) } else if (numComps == 4) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = 255 - dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = 255 - dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = 255 - dctClip(pB); } } } } } } } // Transform one data unit -- this performs the dequantization and // IDCT steps. This IDCT algorithm is taken from: // Y. A. Reznik, A. T. Hinds, L. Yu, Z. Ni, and C-X. Zhang, // "Efficient fixed-point approximations of the 8x8 inverse discrete // cosine transform" (invited paper), Proc. SPIE Vol. 6696, Sep. 24, // 2007. // which is based on: // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, // "Practical Fast 1-D DCT Algorithms with 11 Multiplications", // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, // 988-991. // The stage numbers mentioned in the comments refer to Figure 1 in the // Loeffler paper. void DCTStream::transformDataUnit(Gushort *quantTable, int dataIn[64], Guchar dataOut[64]) { int v0, v1, v2, v3, v4, v5, v6, v7; int t0, t1, t2, t3, t4, t5, t6, t7; int *p, *scale; Gushort *q; int i; // dequant; inverse DCT on rows for (i = 0; i < 64; i += 8) { p = dataIn + i; q = quantTable + i; scale = idctScaleMat + i; // check for all-zero AC coefficients if (p[1] == 0 && p[2] == 0 && p[3] == 0 && p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) { t0 = p[0] * q[0] * scale[0]; if (i == 0) { t0 += 1 << 12; // rounding bias } p[0] = t0; p[1] = t0; p[2] = t0; p[3] = t0; p[4] = t0; p[5] = t0; p[6] = t0; p[7] = t0; continue; } // stage 4 v0 = p[0] * q[0] * scale[0]; if (i == 0) { v0 += 1 << 12; // rounding bias } v1 = p[4] * q[4] * scale[4]; v2 = p[2] * q[2] * scale[2]; v3 = p[6] * q[6] * scale[6]; t0 = p[1] * q[1] * scale[1]; t1 = p[7] * q[7] * scale[7]; v4 = t0 - t1; v7 = t0 + t1; v5 = p[3] * q[3] * scale[3]; v6 = p[5] * q[5] * scale[5]; // stage 3 t0 = v0 - v1; v0 = v0 + v1; v1 = t0; t0 = v2 + (v2 >> 5); t1 = t0 >> 2; t2 = t1 + (v2 >> 4); // 41/128 * v2 t3 = t0 - t1; // 99/128 * v2 t4 = v3 + (v3 >> 5); t5 = t4 >> 2; t6 = t5 + (v3 >> 4); // 41/128 * v3 t7 = t4 - t5; // 99/128 * v3 v2 = t2 - t7; v3 = t3 + t6; t0 = v4 - v6; v4 = v4 + v6; v6 = t0; t0 = v7 + v5; v5 = v7 - v5; v7 = t0; // stage 2 t0 = v0 - v3; v0 = v0 + v3; v3 = t0; t0 = v1 - v2; v1 = v1 + v2; v2 = t0; t0 = (v4 >> 9) - v4; t1 = v4 >> 1; // 1/2 * v4 t2 = (t0 >> 2) - t0; // 1533/2048 * v4 t3 = (v7 >> 9) - v7; t4 = v7 >> 1; // 1/2 * v7 t5 = (t3 >> 2) - t3; // 1533/2048 * v7 v4 = t2 - t4; v7 = t1 + t5; t0 = (v5 >> 3) - (v5 >> 7); t1 = t0 - (v5 >> 11); t2 = t0 + (t1 >> 1); // 719/4096 * v5 t3 = v5 - t0; // 113/256 * v5 t4 = (v6 >> 3) - (v6 >> 7); t5 = t4 - (v6 >> 11); t6 = t4 + (t5 >> 1); // 719/4096 * v6 t7 = v6 - t4; // 113/256 * v6 v5 = t3 - t6; v6 = t2 + t7; // stage 1 p[0] = v0 + v7; p[7] = v0 - v7; p[1] = v1 + v6; p[6] = v1 - v6; p[2] = v2 + v5; p[5] = v2 - v5; p[3] = v3 + v4; p[4] = v3 - v4; } // inverse DCT on columns for (i = 0; i < 8; ++i) { p = dataIn + i; // check for all-zero AC coefficients if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 && p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) { t0 = p[0*8]; p[1*8] = t0; p[2*8] = t0; p[3*8] = t0; p[4*8] = t0; p[5*8] = t0; p[6*8] = t0; p[7*8] = t0; continue; } // stage 4 v0 = p[0*8]; v1 = p[4*8]; v2 = p[2*8]; v3 = p[6*8]; t0 = p[1*8]; t1 = p[7*8]; v4 = t0 - t1; v7 = t0 + t1; v5 = p[3*8]; v6 = p[5*8]; // stage 3 t0 = v0 - v1; v0 = v0 + v1; v1 = t0; t0 = v2 + (v2 >> 5); t1 = t0 >> 2; t2 = t1 + (v2 >> 4); // 41/128 * v2 t3 = t0 - t1; // 99/128 * v2 t4 = v3 + (v3 >> 5); t5 = t4 >> 2; t6 = t5 + (v3 >> 4); // 41/128 * v3 t7 = t4 - t5; // 99/128 * v3 v2 = t2 - t7; v3 = t3 + t6; t0 = v4 - v6; v4 = v4 + v6; v6 = t0; t0 = v7 + v5; v5 = v7 - v5; v7 = t0; // stage 2 t0 = v0 - v3; v0 = v0 + v3; v3 = t0; t0 = v1 - v2; v1 = v1 + v2; v2 = t0; t0 = (v4 >> 9) - v4; t1 = v4 >> 1; // 1/2 * v4 t2 = (t0 >> 2) - t0; // 1533/2048 * v4 t3 = (v7 >> 9) - v7; t4 = v7 >> 1; // 1/2 * v7 t5 = (t3 >> 2) - t3; // 1533/2048 * v7 v4 = t2 - t4; v7 = t1 + t5; t0 = (v5 >> 3) - (v5 >> 7); t1 = t0 - (v5 >> 11); t2 = t0 + (t1 >> 1); // 719/4096 * v5 t3 = v5 - t0; // 113/256 * v5 t4 = (v6 >> 3) - (v6 >> 7); t5 = t4 - (v6 >> 11); t6 = t4 + (t5 >> 1); // 719/4096 * v6 t7 = v6 - t4; // 113/256 * v6 v5 = t3 - t6; v6 = t2 + t7; // stage 1 p[0*8] = v0 + v7; p[7*8] = v0 - v7; p[1*8] = v1 + v6; p[6*8] = v1 - v6; p[2*8] = v2 + v5; p[5*8] = v2 - v5; p[3*8] = v3 + v4; p[4*8] = v3 - v4; } // convert to 8-bit integers for (i = 0; i < 64; ++i) { dataOut[i] = dctClip(128 + (dataIn[i] >> 13)); } } int DCTStream::readHuffSym(DCTHuffTable *table) { Gushort code; int bit; int codeBits; code = 0; codeBits = 0; do { // add a bit to the code if ((bit = readBit()) == EOF) { return 9999; } code = (Gushort)((code << 1) + bit); ++codeBits; // look up code if (code < table->firstCode[codeBits]) { break; } if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) { code = (Gushort)(code - table->firstCode[codeBits]); return table->sym[table->firstSym[codeBits] + code]; } } while (codeBits < 16); error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream"); return 9999; } int DCTStream::readAmp(int size) { int amp, bit; int bits; amp = 0; for (bits = 0; bits < size; ++bits) { if ((bit = readBit()) == EOF) return 9999; amp = (amp << 1) + bit; } if (amp < (1 << (size - 1))) amp -= (1 << size) - 1; return amp; } int DCTStream::readBit() { int bit; int c, c2; if (inputBits == 0) { if ((c = str->getChar()) == EOF) return EOF; if (c == 0xff) { do { c2 = str->getChar(); } while (c2 == 0xff); if (c2 != 0x00) { error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff"); return EOF; } } inputBuf = c; inputBits = 8; } bit = (inputBuf >> (inputBits - 1)) & 1; --inputBits; return bit; } GBool DCTStream::readHeader(GBool frame) { GBool doScan; int n; int c = 0; // read headers doScan = gFalse; while (!doScan) { c = readMarker(); switch (c) { case 0xc0: // SOF0 (sequential) case 0xc1: // SOF1 (extended sequential) if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readBaselineSOF()) { return gFalse; } break; case 0xc2: // SOF2 (progressive) if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readProgressiveSOF()) { return gFalse; } break; case 0xc4: // DHT if (!readHuffmanTables()) { return gFalse; } break; case 0xd8: // SOI if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } break; case 0xd9: // EOI return gFalse; case 0xda: // SOS if (!readScanInfo()) { return gFalse; } doScan = gTrue; break; case 0xdb: // DQT if (!readQuantTables()) { return gFalse; } break; case 0xdd: // DRI if (!readRestartInterval()) { return gFalse; } break; case 0xe0: // APP0 if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readJFIFMarker()) { return gFalse; } break; case 0xee: // APP14 if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readAdobeMarker()) { return gFalse; } break; case EOF: error(errSyntaxError, getPos(), "Bad DCT header"); return gFalse; default: // skip APPn / COM / etc. if (c >= 0xe0) { n = read16() - 2; str->discardChars(n); } else { error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c); return gFalse; } break; } } return gTrue; } GBool DCTStream::readBaselineSOF() { int prec; int i; int c; read16(); // length prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return gFalse; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return gFalse; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return gFalse; } } progressive = gFalse; return gTrue; } GBool DCTStream::readProgressiveSOF() { int prec; int i; int c; read16(); // length prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return gFalse; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return gFalse; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return gFalse; } } progressive = gTrue; return gTrue; } GBool DCTStream::readScanInfo() { int length; int id, c; int i, j; length = read16() - 2; scanInfo.numComps = str->getChar(); if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); scanInfo.numComps = 0; return gFalse; } --length; if (length != 2 * scanInfo.numComps + 3) { error(errSyntaxError, getPos(), "Bad DCT scan info block"); return gFalse; } interleaved = scanInfo.numComps == numComps; for (j = 0; j < numComps; ++j) { scanInfo.comp[j] = gFalse; } for (i = 0; i < scanInfo.numComps; ++i) { id = str->getChar(); // some (broken) DCT streams reuse ID numbers, but at least they // keep the components in order, so we check compInfo[i] first to // work around the problem if (id == compInfo[i].id) { j = i; } else { for (j = 0; j < numComps; ++j) { if (id == compInfo[j].id) { break; } } if (j == numComps) { error(errSyntaxError, getPos(), "Bad DCT component ID in scan info block"); return gFalse; } } if (scanInfo.comp[j]) { error(errSyntaxError, getPos(), "Invalid DCT component ID in scan info block"); return gFalse; } scanInfo.comp[j] = gTrue; c = str->getChar(); scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f; scanInfo.acHuffTable[j] = c & 0x0f; } scanInfo.firstCoeff = str->getChar(); scanInfo.lastCoeff = str->getChar(); if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 || scanInfo.firstCoeff > scanInfo.lastCoeff) { error(errSyntaxError, getPos(), "Bad DCT coefficient numbers in scan info block"); return gFalse; } c = str->getChar(); scanInfo.ah = (c >> 4) & 0x0f; scanInfo.al = c & 0x0f; return gTrue; } GBool DCTStream::readQuantTables() { int length, prec, i, index; length = read16() - 2; while (length > 0) { index = str->getChar(); prec = (index >> 4) & 0x0f; index &= 0x0f; if (prec > 1 || index >= 4) { error(errSyntaxError, getPos(), "Bad DCT quantization table"); return gFalse; } if (index == numQuantTables) { numQuantTables = index + 1; } for (i = 0; i < 64; ++i) { if (prec) { quantTables[index][dctZigZag[i]] = (Gushort)read16(); } else { quantTables[index][dctZigZag[i]] = (Gushort)str->getChar(); } } if (prec) { length -= 129; } else { length -= 65; } } return gTrue; } GBool DCTStream::readHuffmanTables() { DCTHuffTable *tbl; int length; int index; Gushort code; Guchar sym; int i; int c; length = read16() - 2; while (length > 0) { index = str->getChar(); --length; if ((index & 0x0f) >= 4) { error(errSyntaxError, getPos(), "Bad DCT Huffman table"); return gFalse; } if (index & 0x10) { index &= 0x0f; if (index >= numACHuffTables) numACHuffTables = index+1; tbl = &acHuffTables[index]; } else { index &= 0x0f; if (index >= numDCHuffTables) numDCHuffTables = index+1; tbl = &dcHuffTables[index]; } sym = 0; code = 0; for (i = 1; i <= 16; ++i) { c = str->getChar(); tbl->firstSym[i] = sym; tbl->firstCode[i] = code; tbl->numCodes[i] = (Gushort)c; sym = (Guchar)(sym + c); code = (Gushort)((code + c) << 1); } length -= 16; for (i = 0; i < sym; ++i) tbl->sym[i] = (Guchar)str->getChar(); length -= sym; } return gTrue; } GBool DCTStream::readRestartInterval() { int length; length = read16(); if (length != 4) { error(errSyntaxError, getPos(), "Bad DCT restart interval"); return gFalse; } restartInterval = read16(); return gTrue; } GBool DCTStream::readJFIFMarker() { int length, i; char buf[5]; int c; length = read16(); length -= 2; if (length >= 5) { for (i = 0; i < 5; ++i) { if ((c = str->getChar()) == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return gFalse; } buf[i] = (char)c; } length -= 5; if (!memcmp(buf, "JFIF\0", 5)) { gotJFIFMarker = gTrue; } } while (length > 0) { if (str->getChar() == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return gFalse; } --length; } return gTrue; } GBool DCTStream::readAdobeMarker() { int length, i; char buf[12]; int c; length = read16(); if (length < 14) { goto err; } for (i = 0; i < 12; ++i) { if ((c = str->getChar()) == EOF) { goto err; } buf[i] = (char)c; } if (!strncmp(buf, "Adobe", 5)) { colorXform = buf[11]; gotAdobeMarker = gTrue; } for (i = 14; i < length; ++i) { if (str->getChar() == EOF) { goto err; } } return gTrue; err: error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker"); return gFalse; } GBool DCTStream::readTrailer() { int c; c = readMarker(); if (c != 0xd9) { // EOI error(errSyntaxError, getPos(), "Bad DCT trailer"); return gFalse; } return gTrue; } int DCTStream::readMarker() { int c; do { do { c = str->getChar(); } while (c != 0xff && c != EOF); do { c = str->getChar(); } while (c == 0xff); } while (c == 0x00); return c; } int DCTStream::read16() { int c1, c2; if ((c1 = str->getChar()) == EOF) return EOF; if ((c2 = str->getChar()) == EOF) return EOF; return (c1 << 8) + c2; } #endif // HAVE_JPEGLIB GString *DCTStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< >> /DCTDecode filter\n"); return s; } GBool DCTStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = { {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8}, {0, 9}, {0, 10}, {1, 11}, {1, 13}, {1, 15}, {1, 17}, {2, 19}, {2, 23}, {2, 27}, {2, 31}, {3, 35}, {3, 43}, {3, 51}, {3, 59}, {4, 67}, {4, 83}, {4, 99}, {4, 115}, {5, 131}, {5, 163}, {5, 195}, {5, 227}, {0, 258}, {0, 258}, {0, 258} }; FlateDecode FlateStream::distDecode[flateMaxDistCodes] = { { 0, 1}, { 0, 2}, { 0, 3}, { 0, 4}, { 1, 5}, { 1, 7}, { 2, 9}, { 2, 13}, { 3, 17}, { 3, 25}, { 4, 33}, { 4, 49}, { 5, 65}, { 5, 97}, { 6, 129}, { 6, 193}, { 7, 257}, { 7, 385}, { 8, 513}, { 8, 769}, { 9, 1025}, { 9, 1537}, {10, 2049}, {10, 3073}, {11, 4097}, {11, 6145}, {12, 8193}, {12, 12289}, {13, 16385}, {13, 24577} }; static FlateCode flateFixedLitCodeTabCodes[512] = { {7, 0x0100}, {8, 0x0050}, {8, 0x0010}, {8, 0x0118}, {7, 0x0110}, {8, 0x0070}, {8, 0x0030}, {9, 0x00c0}, {7, 0x0108}, {8, 0x0060}, {8, 0x0020}, {9, 0x00a0}, {8, 0x0000}, {8, 0x0080}, {8, 0x0040}, {9, 0x00e0}, {7, 0x0104}, {8, 0x0058}, {8, 0x0018}, {9, 0x0090}, {7, 0x0114}, {8, 0x0078}, {8, 0x0038}, {9, 0x00d0}, {7, 0x010c}, {8, 0x0068}, {8, 0x0028}, {9, 0x00b0}, {8, 0x0008}, {8, 0x0088}, {8, 0x0048}, {9, 0x00f0}, {7, 0x0102}, {8, 0x0054}, {8, 0x0014}, {8, 0x011c}, {7, 0x0112}, {8, 0x0074}, {8, 0x0034}, {9, 0x00c8}, {7, 0x010a}, {8, 0x0064}, {8, 0x0024}, {9, 0x00a8}, {8, 0x0004}, {8, 0x0084}, {8, 0x0044}, {9, 0x00e8}, {7, 0x0106}, {8, 0x005c}, {8, 0x001c}, {9, 0x0098}, {7, 0x0116}, {8, 0x007c}, {8, 0x003c}, {9, 0x00d8}, {7, 0x010e}, {8, 0x006c}, {8, 0x002c}, {9, 0x00b8}, {8, 0x000c}, {8, 0x008c}, {8, 0x004c}, {9, 0x00f8}, {7, 0x0101}, {8, 0x0052}, {8, 0x0012}, {8, 0x011a}, {7, 0x0111}, {8, 0x0072}, {8, 0x0032}, {9, 0x00c4}, {7, 0x0109}, {8, 0x0062}, {8, 0x0022}, {9, 0x00a4}, {8, 0x0002}, {8, 0x0082}, {8, 0x0042}, {9, 0x00e4}, {7, 0x0105}, {8, 0x005a}, {8, 0x001a}, {9, 0x0094}, {7, 0x0115}, {8, 0x007a}, {8, 0x003a}, {9, 0x00d4}, {7, 0x010d}, {8, 0x006a}, {8, 0x002a}, {9, 0x00b4}, {8, 0x000a}, {8, 0x008a}, {8, 0x004a}, {9, 0x00f4}, {7, 0x0103}, {8, 0x0056}, {8, 0x0016}, {8, 0x011e}, {7, 0x0113}, {8, 0x0076}, {8, 0x0036}, {9, 0x00cc}, {7, 0x010b}, {8, 0x0066}, {8, 0x0026}, {9, 0x00ac}, {8, 0x0006}, {8, 0x0086}, {8, 0x0046}, {9, 0x00ec}, {7, 0x0107}, {8, 0x005e}, {8, 0x001e}, {9, 0x009c}, {7, 0x0117}, {8, 0x007e}, {8, 0x003e}, {9, 0x00dc}, {7, 0x010f}, {8, 0x006e}, {8, 0x002e}, {9, 0x00bc}, {8, 0x000e}, {8, 0x008e}, {8, 0x004e}, {9, 0x00fc}, {7, 0x0100}, {8, 0x0051}, {8, 0x0011}, {8, 0x0119}, {7, 0x0110}, {8, 0x0071}, {8, 0x0031}, {9, 0x00c2}, {7, 0x0108}, {8, 0x0061}, {8, 0x0021}, {9, 0x00a2}, {8, 0x0001}, {8, 0x0081}, {8, 0x0041}, {9, 0x00e2}, {7, 0x0104}, {8, 0x0059}, {8, 0x0019}, {9, 0x0092}, {7, 0x0114}, {8, 0x0079}, {8, 0x0039}, {9, 0x00d2}, {7, 0x010c}, {8, 0x0069}, {8, 0x0029}, {9, 0x00b2}, {8, 0x0009}, {8, 0x0089}, {8, 0x0049}, {9, 0x00f2}, {7, 0x0102}, {8, 0x0055}, {8, 0x0015}, {8, 0x011d}, {7, 0x0112}, {8, 0x0075}, {8, 0x0035}, {9, 0x00ca}, {7, 0x010a}, {8, 0x0065}, {8, 0x0025}, {9, 0x00aa}, {8, 0x0005}, {8, 0x0085}, {8, 0x0045}, {9, 0x00ea}, {7, 0x0106}, {8, 0x005d}, {8, 0x001d}, {9, 0x009a}, {7, 0x0116}, {8, 0x007d}, {8, 0x003d}, {9, 0x00da}, {7, 0x010e}, {8, 0x006d}, {8, 0x002d}, {9, 0x00ba}, {8, 0x000d}, {8, 0x008d}, {8, 0x004d}, {9, 0x00fa}, {7, 0x0101}, {8, 0x0053}, {8, 0x0013}, {8, 0x011b}, {7, 0x0111}, {8, 0x0073}, {8, 0x0033}, {9, 0x00c6}, {7, 0x0109}, {8, 0x0063}, {8, 0x0023}, {9, 0x00a6}, {8, 0x0003}, {8, 0x0083}, {8, 0x0043}, {9, 0x00e6}, {7, 0x0105}, {8, 0x005b}, {8, 0x001b}, {9, 0x0096}, {7, 0x0115}, {8, 0x007b}, {8, 0x003b}, {9, 0x00d6}, {7, 0x010d}, {8, 0x006b}, {8, 0x002b}, {9, 0x00b6}, {8, 0x000b}, {8, 0x008b}, {8, 0x004b}, {9, 0x00f6}, {7, 0x0103}, {8, 0x0057}, {8, 0x0017}, {8, 0x011f}, {7, 0x0113}, {8, 0x0077}, {8, 0x0037}, {9, 0x00ce}, {7, 0x010b}, {8, 0x0067}, {8, 0x0027}, {9, 0x00ae}, {8, 0x0007}, {8, 0x0087}, {8, 0x0047}, {9, 0x00ee}, {7, 0x0107}, {8, 0x005f}, {8, 0x001f}, {9, 0x009e}, {7, 0x0117}, {8, 0x007f}, {8, 0x003f}, {9, 0x00de}, {7, 0x010f}, {8, 0x006f}, {8, 0x002f}, {9, 0x00be}, {8, 0x000f}, {8, 0x008f}, {8, 0x004f}, {9, 0x00fe}, {7, 0x0100}, {8, 0x0050}, {8, 0x0010}, {8, 0x0118}, {7, 0x0110}, {8, 0x0070}, {8, 0x0030}, {9, 0x00c1}, {7, 0x0108}, {8, 0x0060}, {8, 0x0020}, {9, 0x00a1}, {8, 0x0000}, {8, 0x0080}, {8, 0x0040}, {9, 0x00e1}, {7, 0x0104}, {8, 0x0058}, {8, 0x0018}, {9, 0x0091}, {7, 0x0114}, {8, 0x0078}, {8, 0x0038}, {9, 0x00d1}, {7, 0x010c}, {8, 0x0068}, {8, 0x0028}, {9, 0x00b1}, {8, 0x0008}, {8, 0x0088}, {8, 0x0048}, {9, 0x00f1}, {7, 0x0102}, {8, 0x0054}, {8, 0x0014}, {8, 0x011c}, {7, 0x0112}, {8, 0x0074}, {8, 0x0034}, {9, 0x00c9}, {7, 0x010a}, {8, 0x0064}, {8, 0x0024}, {9, 0x00a9}, {8, 0x0004}, {8, 0x0084}, {8, 0x0044}, {9, 0x00e9}, {7, 0x0106}, {8, 0x005c}, {8, 0x001c}, {9, 0x0099}, {7, 0x0116}, {8, 0x007c}, {8, 0x003c}, {9, 0x00d9}, {7, 0x010e}, {8, 0x006c}, {8, 0x002c}, {9, 0x00b9}, {8, 0x000c}, {8, 0x008c}, {8, 0x004c}, {9, 0x00f9}, {7, 0x0101}, {8, 0x0052}, {8, 0x0012}, {8, 0x011a}, {7, 0x0111}, {8, 0x0072}, {8, 0x0032}, {9, 0x00c5}, {7, 0x0109}, {8, 0x0062}, {8, 0x0022}, {9, 0x00a5}, {8, 0x0002}, {8, 0x0082}, {8, 0x0042}, {9, 0x00e5}, {7, 0x0105}, {8, 0x005a}, {8, 0x001a}, {9, 0x0095}, {7, 0x0115}, {8, 0x007a}, {8, 0x003a}, {9, 0x00d5}, {7, 0x010d}, {8, 0x006a}, {8, 0x002a}, {9, 0x00b5}, {8, 0x000a}, {8, 0x008a}, {8, 0x004a}, {9, 0x00f5}, {7, 0x0103}, {8, 0x0056}, {8, 0x0016}, {8, 0x011e}, {7, 0x0113}, {8, 0x0076}, {8, 0x0036}, {9, 0x00cd}, {7, 0x010b}, {8, 0x0066}, {8, 0x0026}, {9, 0x00ad}, {8, 0x0006}, {8, 0x0086}, {8, 0x0046}, {9, 0x00ed}, {7, 0x0107}, {8, 0x005e}, {8, 0x001e}, {9, 0x009d}, {7, 0x0117}, {8, 0x007e}, {8, 0x003e}, {9, 0x00dd}, {7, 0x010f}, {8, 0x006e}, {8, 0x002e}, {9, 0x00bd}, {8, 0x000e}, {8, 0x008e}, {8, 0x004e}, {9, 0x00fd}, {7, 0x0100}, {8, 0x0051}, {8, 0x0011}, {8, 0x0119}, {7, 0x0110}, {8, 0x0071}, {8, 0x0031}, {9, 0x00c3}, {7, 0x0108}, {8, 0x0061}, {8, 0x0021}, {9, 0x00a3}, {8, 0x0001}, {8, 0x0081}, {8, 0x0041}, {9, 0x00e3}, {7, 0x0104}, {8, 0x0059}, {8, 0x0019}, {9, 0x0093}, {7, 0x0114}, {8, 0x0079}, {8, 0x0039}, {9, 0x00d3}, {7, 0x010c}, {8, 0x0069}, {8, 0x0029}, {9, 0x00b3}, {8, 0x0009}, {8, 0x0089}, {8, 0x0049}, {9, 0x00f3}, {7, 0x0102}, {8, 0x0055}, {8, 0x0015}, {8, 0x011d}, {7, 0x0112}, {8, 0x0075}, {8, 0x0035}, {9, 0x00cb}, {7, 0x010a}, {8, 0x0065}, {8, 0x0025}, {9, 0x00ab}, {8, 0x0005}, {8, 0x0085}, {8, 0x0045}, {9, 0x00eb}, {7, 0x0106}, {8, 0x005d}, {8, 0x001d}, {9, 0x009b}, {7, 0x0116}, {8, 0x007d}, {8, 0x003d}, {9, 0x00db}, {7, 0x010e}, {8, 0x006d}, {8, 0x002d}, {9, 0x00bb}, {8, 0x000d}, {8, 0x008d}, {8, 0x004d}, {9, 0x00fb}, {7, 0x0101}, {8, 0x0053}, {8, 0x0013}, {8, 0x011b}, {7, 0x0111}, {8, 0x0073}, {8, 0x0033}, {9, 0x00c7}, {7, 0x0109}, {8, 0x0063}, {8, 0x0023}, {9, 0x00a7}, {8, 0x0003}, {8, 0x0083}, {8, 0x0043}, {9, 0x00e7}, {7, 0x0105}, {8, 0x005b}, {8, 0x001b}, {9, 0x0097}, {7, 0x0115}, {8, 0x007b}, {8, 0x003b}, {9, 0x00d7}, {7, 0x010d}, {8, 0x006b}, {8, 0x002b}, {9, 0x00b7}, {8, 0x000b}, {8, 0x008b}, {8, 0x004b}, {9, 0x00f7}, {7, 0x0103}, {8, 0x0057}, {8, 0x0017}, {8, 0x011f}, {7, 0x0113}, {8, 0x0077}, {8, 0x0037}, {9, 0x00cf}, {7, 0x010b}, {8, 0x0067}, {8, 0x0027}, {9, 0x00af}, {8, 0x0007}, {8, 0x0087}, {8, 0x0047}, {9, 0x00ef}, {7, 0x0107}, {8, 0x005f}, {8, 0x001f}, {9, 0x009f}, {7, 0x0117}, {8, 0x007f}, {8, 0x003f}, {9, 0x00df}, {7, 0x010f}, {8, 0x006f}, {8, 0x002f}, {9, 0x00bf}, {8, 0x000f}, {8, 0x008f}, {8, 0x004f}, {9, 0x00ff} }; FlateHuffmanTab FlateStream::fixedLitCodeTab = { flateFixedLitCodeTabCodes, 9 }; static FlateCode flateFixedDistCodeTabCodes[32] = { {5, 0x0000}, {5, 0x0010}, {5, 0x0008}, {5, 0x0018}, {5, 0x0004}, {5, 0x0014}, {5, 0x000c}, {5, 0x001c}, {5, 0x0002}, {5, 0x0012}, {5, 0x000a}, {5, 0x001a}, {5, 0x0006}, {5, 0x0016}, {5, 0x000e}, {0, 0x0000}, {5, 0x0001}, {5, 0x0011}, {5, 0x0009}, {5, 0x0019}, {5, 0x0005}, {5, 0x0015}, {5, 0x000d}, {5, 0x001d}, {5, 0x0003}, {5, 0x0013}, {5, 0x000b}, {5, 0x001b}, {5, 0x0007}, {5, 0x0017}, {5, 0x000f}, {0, 0x0000} }; FlateHuffmanTab FlateStream::fixedDistCodeTab = { flateFixedDistCodeTabCodes, 5 }; FlateStream::FlateStream(Stream *strA, int predictor, int columns, int colors, int bits): FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = NULL; } } else { pred = NULL; } litCodeTab.codes = NULL; distCodeTab.codes = NULL; memset(buf, 0, flateWindow); } FlateStream::~FlateStream() { if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(litCodeTab.codes); } if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(distCodeTab.codes); } if (pred) { delete pred; } delete str; } Stream *FlateStream::copy() { if (pred) { return new FlateStream(str->copy(), pred->getPredictor(), pred->getWidth(), pred->getNComps(), pred->getNBits()); } else { return new FlateStream(str->copy(), 1, 0, 0, 0); } } void FlateStream::reset() { int cmf, flg; index = 0; remain = 0; codeBuf = 0; codeSize = 0; compressedBlock = gFalse; endOfBlock = gTrue; eof = gTrue; str->reset(); if (pred) { pred->reset(); } // read header //~ need to look at window size? endOfBlock = eof = gTrue; cmf = str->getChar(); flg = str->getChar(); if (cmf == EOF || flg == EOF) return; if ((cmf & 0x0f) != 0x08) { error(errSyntaxError, getPos(), "Unknown compression method in flate stream"); return; } if ((((cmf << 8) + flg) % 31) != 0) { error(errSyntaxError, getPos(), "Bad FCHECK in flate stream"); return; } if (flg & 0x20) { error(errSyntaxError, getPos(), "FDICT bit set in flate stream"); return; } eof = gFalse; } int FlateStream::getChar() { int c; if (pred) { return pred->getChar(); } while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; index = (index + 1) & flateMask; --remain; return c; } int FlateStream::lookChar() { int c; if (pred) { return pred->lookChar(); } while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; return c; } int FlateStream::getRawChar() { int c; while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; index = (index + 1) & flateMask; --remain; return c; } int FlateStream::getBlock(char *blk, int size) { int n; if (pred) { return pred->getBlock(blk, size); } n = 0; while (n < size) { if (remain == 0) { if (endOfBlock && eof) { break; } readSome(); } while (remain && n < size) { blk[n++] = buf[index]; index = (index + 1) & flateMask; --remain; } } return n; } GString *FlateStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 3 || pred) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< >> /FlateDecode filter\n"); return s; } GBool FlateStream::isBinary(GBool last) { return str->isBinary(gTrue); } void FlateStream::readSome() { int code1, code2; int len, dist; int i, j, k; int c; if (endOfBlock) { if (!startBlock()) return; } if (compressedBlock) { if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF) goto err; if (code1 < 256) { buf[index] = (Guchar)code1; remain = 1; } else if (code1 == 256) { endOfBlock = gTrue; remain = 0; } else { code1 -= 257; code2 = lengthDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) goto err; len = lengthDecode[code1].first + code2; if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF) goto err; code2 = distDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) goto err; dist = distDecode[code1].first + code2; i = index; j = (index - dist) & flateMask; for (k = 0; k < len; ++k) { buf[i] = buf[j]; i = (i + 1) & flateMask; j = (j + 1) & flateMask; } remain = len; } } else { len = (blockLen < flateWindow) ? blockLen : flateWindow; for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) { if ((c = str->getChar()) == EOF) { endOfBlock = eof = gTrue; break; } buf[j] = (Guchar)c; } remain = i; blockLen -= len; if (blockLen == 0) endOfBlock = gTrue; } return; err: error(errSyntaxError, getPos(), "Unexpected end of file in flate stream"); endOfBlock = eof = gTrue; remain = 0; } GBool FlateStream::startBlock() { int blockHdr; int c; int check; // free the code tables from the previous block if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(litCodeTab.codes); } litCodeTab.codes = NULL; if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(distCodeTab.codes); } distCodeTab.codes = NULL; // read block header blockHdr = getCodeWord(3); if (blockHdr & 1) eof = gTrue; blockHdr >>= 1; // uncompressed block if (blockHdr == 0) { compressedBlock = gFalse; if ((c = str->getChar()) == EOF) goto err; blockLen = c & 0xff; if ((c = str->getChar()) == EOF) goto err; blockLen |= (c & 0xff) << 8; if ((c = str->getChar()) == EOF) goto err; check = c & 0xff; if ((c = str->getChar()) == EOF) goto err; check |= (c & 0xff) << 8; if (check != (~blockLen & 0xffff)) error(errSyntaxError, getPos(), "Bad uncompressed block length in flate stream"); codeBuf = 0; codeSize = 0; // compressed block with fixed codes } else if (blockHdr == 1) { compressedBlock = gTrue; loadFixedCodes(); // compressed block with dynamic codes } else if (blockHdr == 2) { compressedBlock = gTrue; if (!readDynamicCodes()) { goto err; } // unknown block type } else { goto err; } endOfBlock = gFalse; return gTrue; err: error(errSyntaxError, getPos(), "Bad block header in flate stream"); endOfBlock = eof = gTrue; return gFalse; } void FlateStream::loadFixedCodes() { litCodeTab.codes = fixedLitCodeTab.codes; litCodeTab.maxLen = fixedLitCodeTab.maxLen; distCodeTab.codes = fixedDistCodeTab.codes; distCodeTab.maxLen = fixedDistCodeTab.maxLen; } GBool FlateStream::readDynamicCodes() { int numCodeLenCodes; int numLitCodes; int numDistCodes; int codeLenCodeLengths[flateMaxCodeLenCodes]; FlateHuffmanTab codeLenCodeTab; int len, repeat, code; int i; codeLenCodeTab.codes = NULL; // read lengths if ((numLitCodes = getCodeWord(5)) == EOF) { goto err; } numLitCodes += 257; if ((numDistCodes = getCodeWord(5)) == EOF) { goto err; } numDistCodes += 1; if ((numCodeLenCodes = getCodeWord(4)) == EOF) { goto err; } numCodeLenCodes += 4; if (numLitCodes > flateMaxLitCodes || numDistCodes > flateMaxDistCodes || numCodeLenCodes > flateMaxCodeLenCodes) { goto err; } // build the code length code table for (i = 0; i < flateMaxCodeLenCodes; ++i) { codeLenCodeLengths[i] = 0; } for (i = 0; i < numCodeLenCodes; ++i) { if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) { goto err; } } compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab); // build the literal and distance code tables len = 0; repeat = 0; i = 0; while (i < numLitCodes + numDistCodes) { if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) { goto err; } if (code == 16) { if ((repeat = getCodeWord(2)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } for (; repeat > 0; --repeat) { codeLengths[i++] = len; } } else if (code == 17) { if ((repeat = getCodeWord(3)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else if (code == 18) { if ((repeat = getCodeWord(7)) == EOF) { goto err; } repeat += 11; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else { codeLengths[i++] = len = code; } } compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab); compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab); gfree(codeLenCodeTab.codes); return gTrue; err: error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream"); gfree(codeLenCodeTab.codes); return gFalse; } // Convert an array of lengths, in value order, into a // Huffman code lookup table. void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) { int tabSize, len, code, code2, skip, val, i, t; // find max code length tab->maxLen = 0; for (val = 0; val < n; ++val) { if (lengths[val] > tab->maxLen) { tab->maxLen = lengths[val]; } } // allocate the table tabSize = 1 << tab->maxLen; tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode)); // clear the table for (i = 0; i < tabSize; ++i) { tab->codes[i].len = 0; tab->codes[i].val = 0; } // build the table for (len = 1, code = 0, skip = 2; len <= tab->maxLen; ++len, code <<= 1, skip <<= 1) { for (val = 0; val < n; ++val) { if (lengths[val] == len) { // bit-reverse the code code2 = 0; t = code; for (i = 0; i < len; ++i) { code2 = (code2 << 1) | (t & 1); t >>= 1; } // fill in the table entries for (i = code2; i < tabSize; i += skip) { tab->codes[i].len = (Gushort)len; tab->codes[i].val = (Gushort)val; } ++code; } } } } int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) { FlateCode *code; int c; while (codeSize < tab->maxLen) { if ((c = str->getChar()) == EOF) { break; } codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)]; if (codeSize == 0 || codeSize < code->len || code->len == 0) { return EOF; } codeBuf >>= code->len; codeSize -= code->len; return (int)code->val; } int FlateStream::getCodeWord(int bits) { int c; while (codeSize < bits) { if ((c = str->getChar()) == EOF) return EOF; codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } c = codeBuf & ((1 << bits) - 1); codeBuf >>= bits; codeSize -= bits; return c; } //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ EOFStream::EOFStream(Stream *strA): FilterStream(strA) { } EOFStream::~EOFStream() { delete str; } Stream *EOFStream::copy() { return new EOFStream(str->copy()); } //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ BufStream::BufStream(Stream *strA, int bufSizeA): FilterStream(strA) { bufSize = bufSizeA; buf = (int *)gmallocn(bufSize, sizeof(int)); } BufStream::~BufStream() { gfree(buf); delete str; } Stream *BufStream::copy() { return new BufStream(str->copy(), bufSize); } void BufStream::reset() { int i; str->reset(); for (i = 0; i < bufSize; ++i) { buf[i] = str->getChar(); } } int BufStream::getChar() { int c, i; c = buf[0]; for (i = 1; i < bufSize; ++i) { buf[i-1] = buf[i]; } buf[bufSize - 1] = str->getChar(); return c; } int BufStream::lookChar() { return buf[0]; } int BufStream::lookChar(int idx) { return buf[idx]; } GBool BufStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA): FilterStream(strA) { length = lengthA; count = 0; } FixedLengthEncoder::~FixedLengthEncoder() { if (str->isEncoder()) delete str; } Stream *FixedLengthEncoder::copy() { error(errInternal, -1, "Called copy() on FixedLengthEncoder"); return NULL; } void FixedLengthEncoder::reset() { str->reset(); count = 0; } int FixedLengthEncoder::getChar() { if (length >= 0 && count >= length) return EOF; ++count; return str->getChar(); } int FixedLengthEncoder::lookChar() { if (length >= 0 && count >= length) return EOF; return str->getChar(); } GBool FixedLengthEncoder::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } ASCIIHexEncoder::~ASCIIHexEncoder() { if (str->isEncoder()) { delete str; } } Stream *ASCIIHexEncoder::copy() { error(errInternal, -1, "Called copy() on ASCIIHexEncoder"); return NULL; } void ASCIIHexEncoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } GBool ASCIIHexEncoder::fillBuf() { static const char *hex = "0123456789abcdef"; int c; if (eof) { return gFalse; } bufPtr = bufEnd = buf; if ((c = str->getChar()) == EOF) { *bufEnd++ = '>'; eof = gTrue; } else { if (lineLen >= 64) { *bufEnd++ = '\n'; lineLen = 0; } *bufEnd++ = hex[(c >> 4) & 0x0f]; *bufEnd++ = hex[c & 0x0f]; lineLen += 2; } return gTrue; } //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ ASCII85Encoder::ASCII85Encoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } ASCII85Encoder::~ASCII85Encoder() { if (str->isEncoder()) delete str; } Stream *ASCII85Encoder::copy() { error(errInternal, -1, "Called copy() on ASCII85Encoder"); return NULL; } void ASCII85Encoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } GBool ASCII85Encoder::fillBuf() { Guint t; char buf1[5]; int c0, c1, c2, c3; int n, i; if (eof) { return gFalse; } c0 = str->getChar(); c1 = str->getChar(); c2 = str->getChar(); c3 = str->getChar(); bufPtr = bufEnd = buf; if (c3 == EOF) { if (c0 == EOF) { n = 0; t = 0; } else { if (c1 == EOF) { n = 1; t = c0 << 24; } else if (c2 == EOF) { n = 2; t = (c0 << 24) | (c1 << 16); } else { n = 3; t = (c0 << 24) | (c1 << 16) | (c2 << 8); } for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= n; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } *bufEnd++ = '~'; *bufEnd++ = '>'; eof = gTrue; } else { t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; if (t == 0) { *bufEnd++ = 'z'; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } else { for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= 4; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } } return gTrue; } //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ RunLengthEncoder::RunLengthEncoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = nextEnd = buf; eof = gFalse; } RunLengthEncoder::~RunLengthEncoder() { if (str->isEncoder()) delete str; } Stream *RunLengthEncoder::copy() { error(errInternal, -1, "Called copy() on RunLengthEncoder"); return NULL; } void RunLengthEncoder::reset() { str->reset(); bufPtr = bufEnd = nextEnd = buf; eof = gFalse; } // // When fillBuf finishes, buf[] looks like this: // +-----+--------------+-----------------+-- // + tag | ... data ... | next 0, 1, or 2 | // +-----+--------------+-----------------+-- // ^ ^ ^ // bufPtr bufEnd nextEnd // GBool RunLengthEncoder::fillBuf() { int c, c1, c2; int n; // already hit EOF? if (eof) return gFalse; // grab two bytes if (nextEnd < bufEnd + 1) { if ((c1 = str->getChar()) == EOF) { eof = gTrue; return gFalse; } } else { c1 = bufEnd[0] & 0xff; } if (nextEnd < bufEnd + 2) { if ((c2 = str->getChar()) == EOF) { eof = gTrue; buf[0] = 0; buf[1] = (char)c1; bufPtr = buf; bufEnd = &buf[2]; return gTrue; } } else { c2 = bufEnd[1] & 0xff; } // check for repeat c = 0; // make gcc happy if (c1 == c2) { n = 2; while (n < 128 && (c = str->getChar()) == c1) ++n; buf[0] = (char)(257 - n); buf[1] = (char)c1; bufEnd = &buf[2]; if (c == EOF) { eof = gTrue; } else if (n < 128) { buf[2] = (char)c; nextEnd = &buf[3]; } else { nextEnd = bufEnd; } // get up to 128 chars } else { buf[1] = (char)c1; buf[2] = (char)c2; n = 2; while (n < 128) { if ((c = str->getChar()) == EOF) { eof = gTrue; break; } ++n; buf[n] = (char)c; if (buf[n] == buf[n-1]) break; } if (buf[n] == buf[n-1]) { buf[0] = (char)(n-2-1); bufEnd = &buf[n-1]; nextEnd = &buf[n+1]; } else { buf[0] = (char)(n-1); bufEnd = nextEnd = &buf[n+1]; } } bufPtr = buf; return gTrue; } //------------------------------------------------------------------------ // LZWEncoder //------------------------------------------------------------------------ LZWEncoder::LZWEncoder(Stream *strA): FilterStream(strA) { inBufStart = 0; inBufLen = 0; outBufLen = 0; } LZWEncoder::~LZWEncoder() { if (str->isEncoder()) { delete str; } } Stream *LZWEncoder::copy() { error(errInternal, -1, "Called copy() on LZWEncoder"); return NULL; } void LZWEncoder::reset() { int i; str->reset(); // initialize code table for (i = 0; i < 256; ++i) { table[i].byte = i; table[i].next = NULL; table[i].children = NULL; } nextSeq = 258; codeLen = 9; // initialize input buffer inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf)); inBufStart = 0; // initialize output buffer with a clear-table code outBuf = 256; outBufLen = 9; needEOD = gFalse; } int LZWEncoder::getChar() { int ret; if (inBufLen == 0 && !needEOD && outBufLen == 0) { return EOF; } if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { fillBuf(); } if (outBufLen >= 8) { ret = (outBuf >> (outBufLen - 8)) & 0xff; outBufLen -= 8; } else { ret = (outBuf << (8 - outBufLen)) & 0xff; outBufLen = 0; } return ret; } int LZWEncoder::lookChar() { if (inBufLen == 0 && !needEOD && outBufLen == 0) { return EOF; } if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { fillBuf(); } if (outBufLen >= 8) { return (outBuf >> (outBufLen - 8)) & 0xff; } else { return (outBuf << (8 - outBufLen)) & 0xff; } } // On input, outBufLen < 8. // This function generates, at most, 2 12-bit codes // --> outBufLen < 8 + 12 + 12 = 32 void LZWEncoder::fillBuf() { LZWEncoderNode *p0, *p1; int seqLen, code, i; if (needEOD) { outBuf = (outBuf << codeLen) | 257; outBufLen += codeLen; needEOD = gFalse; return; } // find longest matching sequence (if any) p0 = table + inBuf[inBufStart]; seqLen = 1; while (inBufLen > seqLen) { for (p1 = p0->children; p1; p1 = p1->next) { if (p1->byte == inBuf[inBufStart + seqLen]) { break; } } if (!p1) { break; } p0 = p1; ++seqLen; } code = (int)(p0 - table); // generate an output code outBuf = (outBuf << codeLen) | code; outBufLen += codeLen; // update the table table[nextSeq].byte = seqLen < inBufLen ? inBuf[inBufStart + seqLen] : 0; table[nextSeq].children = NULL; if (table[code].children) { table[nextSeq].next = table[code].children; } else { table[nextSeq].next = NULL; } table[code].children = table + nextSeq; ++nextSeq; // update the input buffer inBufStart += seqLen; inBufLen -= seqLen; if (inBufStart >= 4096 && inBufStart + inBufLen == sizeof(inBuf)) { memcpy(inBuf, inBuf + inBufStart, inBufLen); inBufStart = 0; inBufLen += str->getBlock((char *)inBuf + inBufLen, (int)sizeof(inBuf) - inBufLen); } // increment codeLen; generate clear-table code if (nextSeq == (1 << codeLen)) { ++codeLen; if (codeLen == 13) { outBuf = (outBuf << 12) | 256; outBufLen += 12; for (i = 0; i < 256; ++i) { table[i].next = NULL; table[i].children = NULL; } nextSeq = 258; codeLen = 9; } } // generate EOD next time if (inBufLen == 0) { needEOD = gTrue; } } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10021/Stream.h000066400000000000000000001006511417746362400222210ustar00rootroot00000000000000//======================================================================== // // Stream.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef STREAM_H #define STREAM_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #if HAVE_JPEGLIB #include #include #endif #include "gtypes.h" #include "gfile.h" #include "Object.h" class BaseStream; class SharedFile; //------------------------------------------------------------------------ enum StreamKind { strFile, strASCIIHex, strASCII85, strLZW, strRunLength, strCCITTFax, strDCT, strFlate, strJBIG2, strJPX, strWeird // internal-use stream types }; enum StreamColorSpaceMode { streamCSNone, streamCSDeviceGray, streamCSDeviceRGB, streamCSDeviceCMYK }; //------------------------------------------------------------------------ // This is in Stream.h instead of Decrypt.h to avoid really annoying // include file dependency loops. enum CryptAlgorithm { cryptRC4, cryptAES, cryptAES256 }; //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ class Stream { public: // Constructor. Stream(); // Destructor. virtual ~Stream(); virtual Stream *copy() = 0; // Get kind of stream. virtual StreamKind getKind() = 0; virtual GBool isEmbedStream() { return gFalse; } // Reset stream to beginning. virtual void reset() = 0; // Close down the stream. virtual void close(); // Get next char from stream. virtual int getChar() = 0; // Peek at next char in stream. virtual int lookChar() = 0; // Get next char from stream without using the predictor. // This is only used by StreamPredictor. virtual int getRawChar(); // Get exactly bytes from stream. Returns the number of // bytes read -- the returned count will be less than at EOF. virtual int getBlock(char *blk, int size); // Get next line from stream. virtual char *getLine(char *buf, int size); // Discard the next bytes from stream. Returns the number of // bytes discarded, which will be less than only if EOF is // reached. virtual Guint discardChars(Guint n); // Get current position in file. virtual GFileOffset getPos() = 0; // Go to a position in the stream. If is negative, the // position is from the end of the file; otherwise the position is // from the start of the file. virtual void setPos(GFileOffset pos, int dir = 0) = 0; // Get PostScript command for the filter(s). virtual GString *getPSFilter(int psLevel, const char *indent); // Does this stream type potentially contain non-printable chars? virtual GBool isBinary(GBool last = gTrue) = 0; // Get the BaseStream of this stream. virtual BaseStream *getBaseStream() = 0; // Get the stream after the last decoder (this may be a BaseStream // or a DecryptStream). virtual Stream *getUndecodedStream() = 0; // Get the dictionary associated with this stream. virtual Dict *getDict() = 0; // Is this an encoding filter? virtual GBool isEncoder() { return gFalse; } // Get image parameters which are defined by the stream contents. virtual void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) {} // Return the next stream in the "stack". virtual Stream *getNextStream() { return NULL; } // Add filters to this stream according to the parameters in . // Returns the new stream. Stream *addFilters(Object *dict, int recursion = 0); private: Stream *makeFilter(char *name, Stream *str, Object *params, int recursion); }; //------------------------------------------------------------------------ // BaseStream // // This is the base class for all streams that read directly from a file. //------------------------------------------------------------------------ class BaseStream : public Stream { public: BaseStream(Object *dictA); virtual ~BaseStream(); virtual Stream *makeSubStream(GFileOffset start, GBool limited, GFileOffset length, Object *dict) = 0; virtual void setPos(GFileOffset pos, int dir = 0) = 0; virtual GBool isBinary(GBool last = gTrue) { return last; } virtual BaseStream *getBaseStream() { return this; } virtual Stream *getUndecodedStream() { return this; } virtual Dict *getDict() { return dict.getDict(); } virtual GString *getFileName() { return NULL; } // Get/set position of first byte of stream within the file. virtual GFileOffset getStart() = 0; virtual void moveStart(int delta) = 0; protected: Object dict; }; //------------------------------------------------------------------------ // FilterStream // // This is the base class for all streams that filter another stream. //------------------------------------------------------------------------ class FilterStream : public Stream { public: FilterStream(Stream *strA); virtual ~FilterStream(); virtual void close(); virtual GFileOffset getPos() { return str->getPos(); } virtual void setPos(GFileOffset pos, int dir = 0); virtual BaseStream *getBaseStream() { return str->getBaseStream(); } virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); } virtual Dict *getDict() { return str->getDict(); } virtual Stream *getNextStream() { return str; } protected: Stream *str; }; //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ class ImageStream { public: // Create an image stream object for an image with the specified // parameters. Note that these are the actual image parameters, // which may be different from the predictor parameters. ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA); ~ImageStream(); // Reset the stream. void reset(); // Close down the stream. void close(); // Gets the next pixel from the stream. should be able to hold // at least nComps elements. Returns false at end of file. GBool getPixel(Guchar *pix); // Returns a pointer to the next line of pixels. Returns NULL at // end of file. Guchar *getLine(); // Skip an entire line from the image. void skipLine(); private: Stream *str; // base stream int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int inputLineSize; // input line buffer size char *inputLine; // input line buffer Guchar *imgLine; // line buffer int imgIdx; // current index in imgLine }; //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ class StreamPredictor { public: // Create a predictor object. Note that the parameters are for the // predictor, and may not match the actual image parameters. StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA); ~StreamPredictor(); GBool isOk() { return ok; } void reset(); int lookChar(); int getChar(); int getBlock(char *blk, int size); int getPredictor() { return predictor; } int getWidth() { return width; } int getNComps() { return nComps; } int getNBits() { return nBits; } private: GBool getNextLine(); Stream *str; // base stream int predictor; // predictor int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int pixBytes; // bytes per pixel int rowBytes; // bytes per line Guchar *predLine; // line buffer int predIdx; // current index in predLine GBool ok; }; //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ #define fileStreamBufSize 256 class FileStream : public BaseStream { public: FileStream(FILE *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual ~FileStream(); virtual Stream *copy(); virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return strFile; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getBlock(char *blk, int size); virtual GFileOffset getPos() { return bufPos + (int)(bufPtr - buf); } virtual void setPos(GFileOffset pos, int dir = 0); virtual GFileOffset getStart() { return start; } virtual void moveStart(int delta); private: FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); GBool fillBuf(); SharedFile *f; GFileOffset start; GBool limited; GFileOffset length; char buf[fileStreamBufSize]; char *bufPtr; char *bufEnd; GFileOffset bufPos; }; //------------------------------------------------------------------------ // MemStream //------------------------------------------------------------------------ class MemStream : public BaseStream { public: MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA); virtual ~MemStream(); virtual Stream *copy(); virtual Stream *makeSubStream(GFileOffset start, GBool limited, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; } virtual int lookChar() { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; } virtual int getBlock(char *blk, int size); virtual GFileOffset getPos() { return (GFileOffset)(bufPtr - buf); } virtual void setPos(GFileOffset pos, int dir = 0); virtual GFileOffset getStart() { return start; } virtual void moveStart(int delta); private: char *buf; Guint start; Guint length; char *bufEnd; char *bufPtr; GBool needFree; }; //------------------------------------------------------------------------ // EmbedStream // // This is a special stream type used for embedded streams (inline // images). It reads directly from the base stream -- after the // EmbedStream is deleted, reads from the base stream will proceed where // the BaseStream left off. Note that this is very different behavior // that creating a new FileStream (using makeSubStream). //------------------------------------------------------------------------ class EmbedStream : public BaseStream { public: EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA); virtual ~EmbedStream(); virtual Stream *copy(); virtual Stream *makeSubStream(GFileOffset start, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return str->getKind(); } virtual GBool isEmbedStream() { return gTrue; } virtual void reset() {} virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); virtual GFileOffset getPos() { return str->getPos(); } virtual void setPos(GFileOffset pos, int dir = 0); virtual GFileOffset getStart(); virtual void moveStart(int delta); private: Stream *str; GBool limited; GFileOffset length; }; //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ class ASCIIHexStream : public FilterStream { public: ASCIIHexStream(Stream *strA); virtual ~ASCIIHexStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strASCIIHex; } virtual void reset(); virtual int getChar() { int c = lookChar(); buf = EOF; return c; } virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int buf; GBool eof; }; //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ class ASCII85Stream : public FilterStream { public: ASCII85Stream(Stream *strA); virtual ~ASCII85Stream(); virtual Stream *copy(); virtual StreamKind getKind() { return strASCII85; } virtual void reset(); virtual int getChar() { int ch = lookChar(); ++index; return ch; } virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int c[5]; int b[4]; int index, n; GBool eof; }; //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ class LZWStream : public FilterStream { public: LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA); virtual ~LZWStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strLZW; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: StreamPredictor *pred; // predictor int early; // early parameter GBool eof; // true if at eof int inputBuf; // input buffer int inputBits; // number of bits in input buffer struct { // decoding table int length; int head; Guchar tail; } table[4097]; int nextCode; // next code to be used int nextBits; // number of bits in next code word int prevCode; // previous code used in stream int newChar; // next char to be added to table Guchar seqBuf[4097]; // buffer for current sequence int seqLength; // length of current sequence int seqIndex; // index into current sequence GBool first; // first code after a table clear GBool processNextCode(); void clearTable(); int getCode(); }; //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ class RunLengthStream : public FilterStream { public: RunLengthStream(Stream *strA); virtual ~RunLengthStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strRunLength; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: char buf[128]; // buffer char *bufPtr; // next char to read char *bufEnd; // end of buffer GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ struct CCITTCodeTable; class CCITTFaxStream : public FilterStream { public: CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, GBool byteAlignA, int columnsA, int rowsA, GBool endOfBlockA, GBool blackA); virtual ~CCITTFaxStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strCCITTFax; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int encoding; // 'K' parameter GBool endOfLine; // 'EndOfLine' parameter GBool byteAlign; // 'EncodedByteAlign' parameter int columns; // 'Columns' parameter int rows; // 'Rows' parameter GBool endOfBlock; // 'EndOfBlock' parameter GBool black; // 'BlackIs1' parameter int blackXOR; GBool eof; // true if at eof GBool nextLine2D; // true if next line uses 2D encoding int row; // current row Guint inputBuf; // input buffer int inputBits; // number of bits in input buffer int *codingLine; // coding line changing elements int *refLine; // reference line changing elements int nextCol; // next column to read int a0i; // index into codingLine GBool err; // error on current line int nErrors; // number of errors so far in this stream void addPixels(int a1, int blackPixels); void addPixelsNeg(int a1, int blackPixels); GBool readRow(); short getTwoDimCode(); short getWhiteCode(); short getBlackCode(); short lookBits(int n); void eatBits(int n) { if ((inputBits -= n) < 0) inputBits = 0; } }; //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ #if HAVE_JPEGLIB class DCTStream; #define dctStreamBufSize 4096 struct DCTSourceMgr { jpeg_source_mgr src; DCTStream *str; char buf[dctStreamBufSize]; }; struct DCTErrorMgr { struct jpeg_error_mgr err; jmp_buf setjmpBuf; }; #else // HAVE_JPEGLIB // DCT component info struct DCTCompInfo { int id; // component ID int hSample, vSample; // horiz/vert sampling resolutions int quantTable; // quantization table number int prevDC; // DC coefficient accumulator }; struct DCTScanInfo { GBool comp[4]; // comp[i] is set if component i is // included in this scan int numComps; // number of components in the scan int dcHuffTable[4]; // DC Huffman table numbers int acHuffTable[4]; // AC Huffman table numbers int firstCoeff, lastCoeff; // first and last DCT coefficient int ah, al; // successive approximation parameters }; // DCT Huffman decoding table struct DCTHuffTable { Guchar firstSym[17]; // first symbol for this bit length Gushort firstCode[17]; // first code for this bit length Gushort numCodes[17]; // number of codes of this bit length Guchar sym[256]; // symbols }; #endif // HAVE_JPEGLIB class DCTStream : public FilterStream { public: DCTStream(Stream *strA, int colorXformA); virtual ~DCTStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strDCT; } virtual void reset(); virtual void close(); virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); Stream *getRawStream() { return str; } private: #if HAVE_JPEGLIB int colorXform; // color transform: -1 = unspecified // 0 = none // 1 = YUV/YUVK -> RGB/CMYK jpeg_decompress_struct decomp; DCTErrorMgr errorMgr; DCTSourceMgr sourceMgr; GBool error; char *lineBuf; int lineBufHeight; char *lineBufRows[4]; char *bufPtr; char *bufEnd; GBool inlineImage; GBool fillBuf(); static void errorExit(j_common_ptr d); static void errorMessage(j_common_ptr d); static void initSourceCbk(j_decompress_ptr d); static boolean fillInputBufferCbk(j_decompress_ptr d); static void skipInputDataCbk(j_decompress_ptr d, long numBytes); static void termSourceCbk(j_decompress_ptr d); #else // HAVE_JPEGLIB GBool progressive; // set if in progressive mode GBool interleaved; // set if in interleaved mode int width, height; // image size int mcuWidth, mcuHeight; // size of min coding unit, in data units int bufWidth, bufHeight; // frameBuf size DCTCompInfo compInfo[4]; // info for each component DCTScanInfo scanInfo; // info for the current scan int numComps; // number of components in image int colorXform; // color transform: -1 = unspecified // 0 = none // 1 = YUV/YUVK -> RGB/CMYK GBool gotJFIFMarker; // set if APP0 JFIF marker was present GBool gotAdobeMarker; // set if APP14 Adobe marker was present int restartInterval; // restart interval, in MCUs Gushort quantTables[4][64]; // quantization tables int numQuantTables; // number of quantization tables DCTHuffTable dcHuffTables[4]; // DC Huffman tables DCTHuffTable acHuffTables[4]; // AC Huffman tables int numDCHuffTables; // number of DC Huffman tables int numACHuffTables; // number of AC Huffman tables Guchar *rowBuf; Guchar *rowBufPtr; // current position within rowBuf Guchar *rowBufEnd; // end of valid data in rowBuf int *frameBuf[4]; // buffer for frame (progressive mode) int comp, x, y; // current position within image/MCU int restartCtr; // MCUs left until restart int restartMarker; // next restart marker int eobRun; // number of EOBs left in the current run int inputBuf; // input buffer for variable length codes int inputBits; // number of valid bits in input buffer void restart(); GBool readMCURow(); void readScan(); GBool readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); void decodeImage(); void transformDataUnit(Gushort *quantTable, int dataIn[64], Guchar dataOut[64]); int readHuffSym(DCTHuffTable *table); int readAmp(int size); int readBit(); GBool readHeader(GBool frame); GBool readBaselineSOF(); GBool readProgressiveSOF(); GBool readScanInfo(); GBool readQuantTables(); GBool readHuffmanTables(); GBool readRestartInterval(); GBool readJFIFMarker(); GBool readAdobeMarker(); GBool readTrailer(); int readMarker(); int read16(); #endif // HAVE_JPEGLIB }; //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ #define flateWindow 32768 // buffer size #define flateMask (flateWindow-1) #define flateMaxHuffman 15 // max Huffman code length #define flateMaxCodeLenCodes 19 // max # code length codes #define flateMaxLitCodes 288 // max # literal codes #define flateMaxDistCodes 30 // max # distance codes // Huffman code table entry struct FlateCode { Gushort len; // code length, in bits Gushort val; // value represented by this code }; struct FlateHuffmanTab { FlateCode *codes; int maxLen; }; // Decoding info for length and distance code words struct FlateDecode { int bits; // # extra bits int first; // first length/distance }; class FlateStream : public FilterStream { public: FlateStream(Stream *strA, int predictor, int columns, int colors, int bits); virtual ~FlateStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strFlate; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: StreamPredictor *pred; // predictor Guchar buf[flateWindow]; // output data buffer int index; // current index into output buffer int remain; // number valid bytes in output buffer int codeBuf; // input buffer int codeSize; // number of bits in input buffer int // literal and distance code lengths codeLengths[flateMaxLitCodes + flateMaxDistCodes]; FlateHuffmanTab litCodeTab; // literal code table FlateHuffmanTab distCodeTab; // distance code table GBool compressedBlock; // set if reading a compressed block int blockLen; // remaining length of uncompressed block GBool endOfBlock; // set when end of block is reached GBool eof; // set when end of stream is reached static int // code length code reordering codeLenCodeMap[flateMaxCodeLenCodes]; static FlateDecode // length decoding info lengthDecode[flateMaxLitCodes-257]; static FlateDecode // distance decoding info distDecode[flateMaxDistCodes]; static FlateHuffmanTab // fixed literal code table fixedLitCodeTab; static FlateHuffmanTab // fixed distance code table fixedDistCodeTab; void readSome(); GBool startBlock(); void loadFixedCodes(); GBool readDynamicCodes(); void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab); int getHuffmanCodeWord(FlateHuffmanTab *tab); int getCodeWord(int bits); }; //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ class EOFStream : public FilterStream { public: EOFStream(Stream *strA); virtual ~EOFStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset() {} virtual int getChar() { return EOF; } virtual int lookChar() { return EOF; } virtual int getBlock(char *blk, int size) { return 0; } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } }; //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ class BufStream : public FilterStream { public: BufStream(Stream *strA, int bufSizeA); virtual ~BufStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue); int lookChar(int idx); private: int *buf; int bufSize; }; //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ class FixedLengthEncoder : public FilterStream { public: FixedLengthEncoder(Stream *strA, int lengthA); ~FixedLengthEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue); virtual GBool isEncoder() { return gTrue; } private: int length; int count; }; //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ class ASCIIHexEncoder : public FilterStream { public: ASCIIHexEncoder(Stream *strA); virtual ~ASCIIHexEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } private: char buf[4]; char *bufPtr; char *bufEnd; int lineLen; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ class ASCII85Encoder : public FilterStream { public: ASCII85Encoder(Stream *strA); virtual ~ASCII85Encoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } private: char buf[8]; char *bufPtr; char *bufEnd; int lineLen; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ class RunLengthEncoder : public FilterStream { public: RunLengthEncoder(Stream *strA); virtual ~RunLengthEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: char buf[131]; char *bufPtr; char *bufEnd; char *nextEnd; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // LZWEncoder //------------------------------------------------------------------------ struct LZWEncoderNode { int byte; LZWEncoderNode *next; // next sibling LZWEncoderNode *children; // first child }; class LZWEncoder : public FilterStream { public: LZWEncoder(Stream *strA); virtual ~LZWEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: LZWEncoderNode table[4096]; int nextSeq; int codeLen; Guchar inBuf[8192]; int inBufStart; int inBufLen; int outBuf; int outBufLen; GBool needEOD; void fillBuf(); }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10021/expected.txt000066400000000000000000000000421417746362400231500ustar00rootroot00000000000000Stream.cc:359:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10023/000077500000000000000000000000001417746362400206145ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10023/Function.cc000066400000000000000000001055171417746362400227210ustar00rootroot00000000000000//======================================================================== // // Function.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "gmempp.h" #include "GList.h" #include "Object.h" #include "Dict.h" #include "Stream.h" #include "Error.h" #include "Function.h" //------------------------------------------------------------------------ // Max depth of nested functions. This is used to catch infinite // loops in the function object structure. #define recursionLimit 8 //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ Function::Function() { } Function::~Function() { } Function *Function::parse(Object *funcObj, int recursion) { Function *func; Dict *dict; int funcType; Object obj1; if (recursion > recursionLimit) { error(errSyntaxError, -1, "Loop detected in function objects"); return NULL; } if (funcObj->isStream()) { dict = funcObj->streamGetDict(); } else if (funcObj->isDict()) { dict = funcObj->getDict(); } else if (funcObj->isName("Identity")) { return new IdentityFunction(); } else { error(errSyntaxError, -1, "Expected function dictionary or stream"); return NULL; } if (!dict->lookup("FunctionType", &obj1)->isInt()) { error(errSyntaxError, -1, "Function type is missing or wrong type"); obj1.free(); return NULL; } funcType = obj1.getInt(); obj1.free(); if (funcType == 0) { func = new SampledFunction(funcObj, dict); } else if (funcType == 2) { func = new ExponentialFunction(funcObj, dict); } else if (funcType == 3) { func = new StitchingFunction(funcObj, dict, recursion); } else if (funcType == 4) { func = new PostScriptFunction(funcObj, dict); } else { error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); return NULL; } if (!func->isOk()) { delete func; return NULL; } return func; } GBool Function::init(Dict *dict) { Object obj1, obj2; int i; //----- Domain if (!dict->lookup("Domain", &obj1)->isArray()) { error(errSyntaxError, -1, "Function is missing domain"); goto err2; } m = obj1.arrayGetLength() / 2; if (m > funcMaxInputs) { error(errSyntaxError, -1, "Functions with more than {0:d} inputs are unsupported", funcMaxInputs); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][1] = obj2.getNum(); obj2.free(); } obj1.free(); //----- Range hasRange = gFalse; n = 0; if (dict->lookup("Range", &obj1)->isArray()) { hasRange = gTrue; n = obj1.arrayGetLength() / 2; if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][1] = obj2.getNum(); obj2.free(); } } obj1.free(); return gTrue; err1: obj2.free(); err2: obj1.free(); return gFalse; } //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ IdentityFunction::IdentityFunction() { int i; // fill these in with arbitrary values just in case they get used // somewhere m = funcMaxInputs; n = funcMaxOutputs; for (i = 0; i < funcMaxInputs; ++i) { domain[i][0] = 0; domain[i][1] = 1; } hasRange = gFalse; } IdentityFunction::~IdentityFunction() { } void IdentityFunction::transform(double *in, double *out) { int i; for (i = 0; i < funcMaxOutputs; ++i) { out[i] = in[i]; } } //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { Stream *str; int sampleBits; double sampleMul; Object obj1, obj2; Guint buf, bitMask; int bits; Guint s; double in[funcMaxInputs]; int i, j, t, bit, idx; idxOffset = NULL; samples = NULL; sBuf = NULL; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 0 function is missing range"); goto err1; } if (m > sampledFuncMaxInputs) { error(errSyntaxError, -1, "Sampled functions with more than {0:d} inputs are unsupported", sampledFuncMaxInputs); goto err1; } //----- buffer sBuf = (double *)gmallocn(1 << m, sizeof(double)); //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 0 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- Size if (!dict->lookup("Size", &obj1)->isArray() || obj1.arrayGetLength() != m) { error(errSyntaxError, -1, "Function has missing or invalid size array"); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isInt()) { error(errSyntaxError, -1, "Illegal value in function size array"); goto err3; } sampleSize[i] = obj2.getInt(); if (sampleSize[i] <= 0) { error(errSyntaxError, -1, "Illegal non-positive value in function size array"); goto err3; } obj2.free(); } obj1.free(); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); for (i = 0; i < (1<= 1; --j, t <<= 1) { if (sampleSize[j] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idx = (idx + bit) * sampleSize[j-1]; } if (sampleSize[0] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idxOffset[i] = (idx + bit) * n; } //----- BitsPerSample if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); goto err2; } sampleBits = obj1.getInt(); sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); obj1.free(); //----- Encode if (dict->lookup("Encode", &obj1)->isArray() && obj1.arrayGetLength() == 2*m) { for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < m; ++i) { encode[i][0] = 0; encode[i][1] = sampleSize[i] - 1; } } obj1.free(); for (i = 0; i < m; ++i) { inputMul[i] = (encode[i][1] - encode[i][0]) / (domain[i][1] - domain[i][0]); } //----- Decode if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() == 2*n) { for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < n; ++i) { decode[i][0] = range[i][0]; decode[i][1] = range[i][1]; } } obj1.free(); //----- samples nSamples = n; for (i = 0; i < m; ++i) nSamples *= sampleSize[i]; samples = (double *)gmallocn(nSamples, sizeof(double)); buf = 0; bits = 0; bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; str->reset(); for (i = 0; i < nSamples; ++i) { if (sampleBits == 8) { s = str->getChar(); } else if (sampleBits == 16) { s = str->getChar(); s = (s << 8) + str->getChar(); } else if (sampleBits == 32) { s = str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); } else { while (bits < sampleBits) { buf = (buf << 8) | (str->getChar() & 0xff); bits += 8; } s = (buf >> (bits - sampleBits)) & bitMask; bits -= sampleBits; } samples[i] = (double)s * sampleMul; } str->close(); // set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } SampledFunction::~SampledFunction() { if (idxOffset) { gfree(idxOffset); } if (samples) { gfree(samples); } if (sBuf) { gfree(sBuf); } } SampledFunction::SampledFunction(SampledFunction *func) { memcpy((void *)this, (void *)func, sizeof(SampledFunction)); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); samples = (double *)gmallocn(nSamples, sizeof(double)); memcpy(samples, func->samples, nSamples * sizeof(double)); sBuf = (double *)gmallocn(1 << m, sizeof(double)); } void SampledFunction::transform(double *in, double *out) { double x; int e[funcMaxInputs]; double efrac0[funcMaxInputs]; double efrac1[funcMaxInputs]; int i, j, k, idx0, t; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } // map input values into sample array for (i = 0; i < m; ++i) { x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) x = 0; } else if (x > sampleSize[i] - 1) { x = sampleSize[i] - 1; } e[i] = (int)x; if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { // this happens if in[i] = domain[i][1] e[i] = sampleSize[i] - 2; } efrac1[i] = x - e[i]; efrac0[i] = 1 - efrac1[i]; } // compute index for the first sample to be used idx0 = 0; for (k = m - 1; k >= 1; --k) { idx0 = (idx0 + e[k]) * sampleSize[k-1]; } idx0 = (idx0 + e[0]) * n; // for each output, do m-linear interpolation for (i = 0; i < n; ++i) { // pull 2^m values out of the sample array for (j = 0; j < (1<>= 1) { for (k = 0; k < t; k += 2) { sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; } } // map output value to range out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { Object obj1, obj2; int i; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Exponential function with more than one input"); goto err1; } //----- C0 if (dict->lookup("C0", &obj1)->isArray()) { if (hasRange && obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = obj1.arrayGetLength(); if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C0 array"); goto err3; } c0[i] = obj2.getNum(); obj2.free(); } } else { if (hasRange && n != 1) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = 1; c0[0] = 0; } obj1.free(); //----- C1 if (dict->lookup("C1", &obj1)->isArray()) { if (obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C1 array"); goto err3; } c1[i] = obj2.getNum(); obj2.free(); } } else { if (n != 1) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } c1[0] = 1; } obj1.free(); //----- N (exponent) if (!dict->lookup("N", &obj1)->isNum()) { error(errSyntaxError, -1, "Function has missing or invalid N"); goto err2; } e = obj1.getNum(); obj1.free(); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } ExponentialFunction::~ExponentialFunction() { } ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { memcpy((void *)this, (void *)func, sizeof(ExponentialFunction)); } void ExponentialFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < n; ++i) { out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); if (hasRange) { if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } } return; } //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, int recursion) { Object obj1, obj2; int i; ok = gFalse; funcs = NULL; bounds = NULL; encode = NULL; scale = NULL; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Stitching function with more than one input"); goto err1; } //----- Functions if (!dict->lookup("Functions", &obj1)->isArray()) { error(errSyntaxError, -1, "Missing 'Functions' entry in stitching function"); goto err1; } k = obj1.arrayGetLength(); funcs = (Function **)gmallocn(k, sizeof(Function *)); bounds = (double *)gmallocn(k + 1, sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); for (i = 0; i < k; ++i) { funcs[i] = NULL; } for (i = 0; i < k; ++i) { if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), recursion + 1))) { goto err2; } if (funcs[i]->getInputSize() != 1 || (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { error(errSyntaxError, -1, "Incompatible subfunctions in stitching function"); goto err2; } obj2.free(); } obj1.free(); //----- Bounds if (!dict->lookup("Bounds", &obj1)->isArray() || obj1.arrayGetLength() != k - 1) { error(errSyntaxError, -1, "Missing or invalid 'Bounds' entry in stitching function"); goto err1; } bounds[0] = domain[0][0]; for (i = 1; i < k; ++i) { if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Bounds' array in stitching function"); goto err2; } bounds[i] = obj2.getNum(); obj2.free(); } bounds[k] = domain[0][1]; obj1.free(); //----- Encode if (!dict->lookup("Encode", &obj1)->isArray() || obj1.arrayGetLength() != 2 * k) { error(errSyntaxError, -1, "Missing or invalid 'Encode' entry in stitching function"); goto err1; } for (i = 0; i < 2 * k; ++i) { if (!obj1.arrayGet(i, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Encode' array in stitching function"); goto err2; } encode[i] = obj2.getNum(); obj2.free(); } obj1.free(); //----- pre-compute the scale factors for (i = 0; i < k; ++i) { if (bounds[i] == bounds[i+1]) { // avoid a divide-by-zero -- in this situation, function i will // never be used anyway scale[i] = 0; } else { scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); } } ok = gTrue; return; err2: obj2.free(); err1: obj1.free(); } StitchingFunction::StitchingFunction(StitchingFunction *func) { int i; memcpy((void *)this, (void *)func, sizeof(StitchingFunction)); funcs = (Function **)gmallocn(k, sizeof(Function *)); for (i = 0; i < k; ++i) { funcs[i] = func->funcs[i]->copy(); } bounds = (double *)gmallocn(k + 1, sizeof(double)); memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); memcpy(encode, func->encode, 2 * k * sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); memcpy(scale, func->scale, k * sizeof(double)); ok = gTrue; } StitchingFunction::~StitchingFunction() { int i; if (funcs) { for (i = 0; i < k; ++i) { if (funcs[i]) { delete funcs[i]; } } } gfree(funcs); gfree(bounds); gfree(encode); gfree(scale); } void StitchingFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < k - 1; ++i) { if (x < bounds[i+1]) { break; } } x = encode[2*i] + (x - bounds[i]) * scale[i]; funcs[i]->transform(&x, out); } //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ // This is not an enum, because we can't foreward-declare the enum // type in Function.h // // NB: This must be kept in sync with psOpNames[] below. #define psOpAbs 0 #define psOpAdd 1 #define psOpAnd 2 #define psOpAtan 3 #define psOpBitshift 4 #define psOpCeiling 5 #define psOpCopy 6 #define psOpCos 7 #define psOpCvi 8 #define psOpCvr 9 #define psOpDiv 10 #define psOpDup 11 #define psOpEq 12 #define psOpExch 13 #define psOpExp 14 #define psOpFalse 15 #define psOpFloor 16 #define psOpGe 17 #define psOpGt 18 #define psOpIdiv 19 #define psOpIndex 20 #define psOpLe 21 #define psOpLn 22 #define psOpLog 23 #define psOpLt 24 #define psOpMod 25 #define psOpMul 26 #define psOpNe 27 #define psOpNeg 28 #define psOpNot 29 #define psOpOr 30 #define psOpPop 31 #define psOpRoll 32 #define psOpRound 33 #define psOpSin 34 #define psOpSqrt 35 #define psOpSub 36 #define psOpTrue 37 #define psOpTruncate 38 #define psOpXor 39 // the push/j/jz ops are used internally (and are not listed in psOpNames[]) #define psOpPush 40 #define psOpJ 41 #define psOpJz 42 #define nPSOps (sizeof(psOpNames) / sizeof(const char *)) // Note: 'if' and 'ifelse' are parsed separately. // The rest are listed here in alphabetical order. // // NB: This must be kept in sync with the psOpXXX defines above. static const char *psOpNames[] = { "abs", "add", "and", "atan", "bitshift", "ceiling", "copy", "cos", "cvi", "cvr", "div", "dup", "eq", "exch", "exp", "false", "floor", "ge", "gt", "idiv", "index", "le", "ln", "log", "lt", "mod", "mul", "ne", "neg", "not", "or", "pop", "roll", "round", "sin", "sqrt", "sub", "true", "truncate", "xor" }; struct PSCode { int op; union { double d; int i; } val; }; #define psStackSize 100 PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { Stream *str; GList *tokens; GString *tok; double in[funcMaxInputs]; int tokPtr, codePtr, i; codeString = NULL; code = NULL; codeSize = 0; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 4 function is missing range"); goto err1; } //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 4 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- tokenize the function codeString = new GString(); tokens = new GList(); str->reset(); while ((tok = getToken(str))) { tokens->append(tok); } str->close(); //----- parse the function if (tokens->getLength() < 1 || ((GString *)tokens->get(0))->cmp("{")) { error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); goto err2; } tokPtr = 1; codePtr = 0; if (!parseCode(tokens, &tokPtr, &codePtr)) { goto err2; } codeLen = codePtr; //----- set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; err2: deleteGList(tokens, GString); err1: return; } PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { memcpy((void *)this, (void *)func, sizeof(PostScriptFunction)); codeString = func->codeString->copy(); code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); memcpy(code, func->code, codeSize * sizeof(PSCode)); } PostScriptFunction::~PostScriptFunction() { gfree(code); if (codeString) { delete codeString; } } void PostScriptFunction::transform(double *in, double *out) { double stack[psStackSize]; double x; int sp, i; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } for (i = 0; i < m; ++i) { stack[psStackSize - 1 - i] = in[i]; } sp = exec(stack, psStackSize - m); // if (sp < psStackSize - n) { // error(errSyntaxWarning, -1, // "Extra values on stack at end of PostScript function"); // } if (sp > psStackSize - n) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); sp = psStackSize - n; } for (i = 0; i < n; ++i) { x = stack[sp + n - 1 - i]; if (x < range[i][0]) { out[i] = range[i][0]; } else if (x > range[i][1]) { out[i] = range[i][1]; } else { out[i] = x; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { GString *tok; char *p; int a, b, mid, cmp; int codePtr0, codePtr1; while (1) { if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); p = tok->getCString(); if (isdigit(*p) || *p == '.' || *p == '-') { addCodeD(codePtr, psOpPush, atof(tok->getCString())); } else if (!tok->cmp("{")) { codePtr0 = *codePtr; addCodeI(codePtr, psOpJz, 0); if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("if")) { code[codePtr0].val.i = *codePtr; } else if (!tok->cmp("{")) { codePtr1 = *codePtr; addCodeI(codePtr, psOpJ, 0); code[codePtr0].val.i = *codePtr; if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("ifelse")) { code[codePtr1].val.i = *codePtr; } else { error(errSyntaxError, -1, "Expected 'ifelse' in PostScript function stream"); return gFalse; } } else { error(errSyntaxError, -1, "Expected 'if' in PostScript function stream"); return gFalse; } } else if (!tok->cmp("}")) { break; } else if (!tok->cmp("if")) { error(errSyntaxError, -1, "Unexpected 'if' in PostScript function stream"); return gFalse; } else if (!tok->cmp("ifelse")) { error(errSyntaxError, -1, "Unexpected 'ifelse' in PostScript function stream"); return gFalse; } else { a = -1; b = nPSOps; cmp = 0; // make gcc happy // invariant: psOpNames[a] < tok < psOpNames[b] while (b - a > 1) { mid = (a + b) / 2; cmp = tok->cmp(psOpNames[mid]); if (cmp > 0) { a = mid; } else if (cmp < 0) { b = mid; } else { a = b = mid; } } if (cmp != 0) { error(errSyntaxError, -1, "Unknown operator '{0:t}' in PostScript function", tok); return gFalse; } addCode(codePtr, a); } } return gTrue; } void PostScriptFunction::addCode(int *codePtr, int op) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; ++(*codePtr); } void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.i = x; ++(*codePtr); } void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.d = x; ++(*codePtr); } GString *PostScriptFunction::getToken(Stream *str) { GString *s; int c; GBool comment; s = new GString(); comment = gFalse; while (1) { if ((c = str->getChar()) == EOF) { delete s; return NULL; } codeString->append((char)c); if (comment) { if (c == '\x0a' || c == '\x0d') { comment = gFalse; } } else if (c == '%') { comment = gTrue; } else if (!isspace(c)) { break; } } if (c == '{' || c == '}') { s->append((char)c); } else if (isdigit(c) || c == '.' || c == '-') { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { break; } str->getChar(); codeString->append((char)c); } } else { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !isalnum(c)) { break; } str->getChar(); codeString->append((char)c); } } return s; } int PostScriptFunction::exec(double *stack, int sp0) { PSCode *c; double tmp[psStackSize]; double t; int sp, ip, nn, k, i; sp = sp0; ip = 0; while (ip < codeLen) { c = &code[ip++]; switch(c->op) { case psOpAbs: if (sp >= psStackSize) { goto underflow; } stack[sp] = fabs(stack[sp]); break; case psOpAdd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] + stack[sp]; ++sp; break; case psOpAnd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; ++sp; break; case psOpAtan: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); ++sp; break; case psOpBitshift: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp + 1]; nn = (int)stack[sp]; if (nn > 0) { stack[sp + 1] = k << nn; } else if (nn < 0) { stack[sp + 1] = k >> -nn; } else { stack[sp + 1] = k; } ++sp; break; case psOpCeiling: if (sp >= psStackSize) { goto underflow; } stack[sp] = ceil(stack[sp]); break; case psOpCopy: if (sp >= psStackSize) { goto underflow; } nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (sp - nn < 0) { goto overflow; } for (i = 0; i < nn; ++i) { stack[sp - nn + i] = stack[sp + i]; } sp -= nn; break; case psOpCos: if (sp >= psStackSize) { goto underflow; } stack[sp] = cos(stack[sp]); break; case psOpCvi: if (sp >= psStackSize) { goto underflow; } stack[sp] = (int)stack[sp]; break; case psOpCvr: if (sp >= psStackSize) { goto underflow; } break; case psOpDiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] / stack[sp]; ++sp; break; case psOpDup: if (sp >= psStackSize) { goto underflow; } if (sp < 1) { goto overflow; } stack[sp - 1] = stack[sp]; --sp; break; case psOpEq: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; ++sp; break; case psOpExch: if (sp + 1 >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = stack[sp + 1]; stack[sp + 1] = t; break; case psOpExp: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = pow(stack[sp + 1], stack[sp]); ++sp; break; case psOpFalse: if (sp < 1) { goto overflow; } stack[sp - 1] = 0; --sp; break; case psOpFloor: if (sp >= psStackSize) { goto underflow; } stack[sp] = floor(stack[sp]); break; case psOpGe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; ++sp; break; case psOpGt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; ++sp; break; case psOpIdiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; ++sp; break; case psOpIndex: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp]; if (k < 0) { goto invalidArg; } if (sp + 1 + k >= psStackSize) { goto underflow; } stack[sp] = stack[sp + 1 + k]; break; case psOpLe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; ++sp; break; case psOpLn: if (sp >= psStackSize) { goto underflow; } stack[sp] = log(stack[sp]); break; case psOpLog: if (sp >= psStackSize) { goto underflow; } stack[sp] = log10(stack[sp]); break; case psOpLt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; ++sp; break; case psOpMod: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; ++sp; break; case psOpMul: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] * stack[sp]; ++sp; break; case psOpNe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; ++sp; break; case psOpNeg: if (sp >= psStackSize) { goto underflow; } stack[sp] = -stack[sp]; break; case psOpNot: if (sp >= psStackSize) { goto underflow; } stack[sp] = stack[sp] == 0 ? 1 : 0; break; case psOpOr: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; ++sp; break; case psOpPop: if (sp >= psStackSize) { goto underflow; } ++sp; break; case psOpRoll: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp++]; nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (k >= 0) { k %= nn; } else { k = -k % nn; if (k) { k = nn - k; } } for (i = 0; i < nn; ++i) { tmp[i] = stack[sp + i]; } for (i = 0; i < nn; ++i) { stack[sp + i] = tmp[(i + k) % nn]; } break; case psOpRound: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); break; case psOpSin: if (sp >= psStackSize) { goto underflow; } stack[sp] = sin(stack[sp]); break; case psOpSqrt: if (sp >= psStackSize) { goto underflow; } stack[sp] = sqrt(stack[sp]); break; case psOpSub: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] - stack[sp]; ++sp; break; case psOpTrue: if (sp < 1) { goto overflow; } stack[sp - 1] = 1; --sp; break; case psOpTruncate: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t) : ceil(t); break; case psOpXor: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; ++sp; break; case psOpPush: if (sp < 1) { goto overflow; } stack[--sp] = c->val.d; break; case psOpJ: ip = c->val.i; break; case psOpJz: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp++]; if (k == 0) { ip = c->val.i; } break; } } return sp; underflow: error(errSyntaxError, -1, "Stack underflow in PostScript function"); return sp; overflow: error(errSyntaxError, -1, "Stack overflow in PostScript function"); return sp; invalidArg: error(errSyntaxError, -1, "Invalid arg in PostScript function"); return sp; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10023/Function.h000066400000000000000000000161301417746362400225530ustar00rootroot00000000000000//======================================================================== // // Function.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef FUNCTION_H #define FUNCTION_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" class GList; class Dict; class Stream; struct PSCode; //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ #define funcMaxInputs 32 #define funcMaxOutputs 32 #define sampledFuncMaxInputs 16 class Function { public: Function(); virtual ~Function(); // Construct a function. Returns NULL if unsuccessful. static Function *parse(Object *funcObj, int recursion = 0); // Initialize the entries common to all function types. GBool init(Dict *dict); virtual Function *copy() = 0; // Return the function type: // -1 : identity // 0 : sampled // 2 : exponential // 3 : stitching // 4 : PostScript virtual int getType() = 0; // Return size of input and output tuples. int getInputSize() { return m; } int getOutputSize() { return n; } double getDomainMin(int i) { return domain[i][0]; } double getDomainMax(int i) { return domain[i][1]; } double getRangeMin(int i) { return range[i][0]; } double getRangeMax(int i) { return range[i][1]; } GBool getHasRange() { return hasRange; } // Transform an input tuple into an output tuple. virtual void transform(double *in, double *out) = 0; virtual GBool isOk() = 0; protected: int m, n; // size of input and output tuples double // min and max values for function domain domain[funcMaxInputs][2]; double // min and max values for function range range[funcMaxOutputs][2]; GBool hasRange; // set if range is defined }; //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ class IdentityFunction : public Function { public: IdentityFunction(); virtual ~IdentityFunction(); virtual Function *copy() { return new IdentityFunction(); } virtual int getType() { return -1; } virtual void transform(double *in, double *out); virtual GBool isOk() { return gTrue; } private: }; //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ class SampledFunction : public Function { public: SampledFunction(Object *funcObj, Dict *dict); virtual ~SampledFunction(); virtual Function *copy() { return new SampledFunction(this); } virtual int getType() { return 0; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getSampleSize(int i) { return sampleSize[i]; } double getEncodeMin(int i) { return encode[i][0]; } double getEncodeMax(int i) { return encode[i][1]; } double getDecodeMin(int i) { return decode[i][0]; } double getDecodeMax(int i) { return decode[i][1]; } double *getSamples() { return samples; } private: SampledFunction(SampledFunction *func); int // number of samples for each domain element sampleSize[funcMaxInputs]; double // min and max values for domain encoder encode[funcMaxInputs][2]; double // min and max values for range decoder decode[funcMaxOutputs][2]; double // input multipliers inputMul[funcMaxInputs]; int *idxOffset; double *samples; // the samples int nSamples; // size of the samples array double *sBuf; // buffer for the transform function double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ class ExponentialFunction : public Function { public: ExponentialFunction(Object *funcObj, Dict *dict); virtual ~ExponentialFunction(); virtual Function *copy() { return new ExponentialFunction(this); } virtual int getType() { return 2; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } double *getC0() { return c0; } double *getC1() { return c1; } double getE() { return e; } private: ExponentialFunction(ExponentialFunction *func); double c0[funcMaxOutputs]; double c1[funcMaxOutputs]; double e; GBool ok; }; //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ class StitchingFunction : public Function { public: StitchingFunction(Object *funcObj, Dict *dict, int recursion); virtual ~StitchingFunction(); virtual Function *copy() { return new StitchingFunction(this); } virtual int getType() { return 3; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getNumFuncs() { return k; } Function *getFunc(int i) { return funcs[i]; } double *getBounds() { return bounds; } double *getEncode() { return encode; } double *getScale() { return scale; } private: StitchingFunction(StitchingFunction *func); int k; Function **funcs; double *bounds; double *encode; double *scale; GBool ok; }; //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ class PostScriptFunction : public Function { public: PostScriptFunction(Object *funcObj, Dict *dict); virtual ~PostScriptFunction(); virtual Function *copy() { return new PostScriptFunction(this); } virtual int getType() { return 4; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } GString *getCodeString() { return codeString; } private: PostScriptFunction(PostScriptFunction *func); GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); void addCode(int *codePtr, int op); void addCodeI(int *codePtr, int op, int x); void addCodeD(int *codePtr, int op, double x); GString *getToken(Stream *str); int exec(double *stack, int sp0); GString *codeString; PSCode *code; int codeLen; int codeSize; double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10023/expected.txt000066400000000000000000000000451417746362400231550ustar00rootroot00000000000000Function.cc:1420:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10024/000077500000000000000000000000001417746362400206155ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10024/Splash.cc000066400000000000000000005731461417746362400223760ustar00rootroot00000000000000//======================================================================== // // Splash.cc // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "gmempp.h" #include "SplashErrorCodes.h" #include "SplashMath.h" #include "SplashBitmap.h" #include "SplashState.h" #include "SplashPath.h" #include "SplashXPath.h" #include "SplashXPathScanner.h" #include "SplashPattern.h" #include "SplashScreen.h" #include "SplashFont.h" #include "SplashGlyphBitmap.h" #include "Splash.h" // the MSVC math.h doesn't define this #ifndef M_PI #define M_PI 3.14159265358979323846 #endif //------------------------------------------------------------------------ // distance of Bezier control point from center for circle approximation // = (4 * (sqrt(2) - 1) / 3) * r #define bezierCircle ((SplashCoord)0.55228475) #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475)) // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline Guchar div255(int x) { return (Guchar)((x + (x >> 8) + 0x80) >> 8); } // Clip x to lie in [0, 255]. static inline Guchar clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : (Guchar)x; } // Used by drawImage and fillImageMask to divide the target // quadrilateral into sections. struct ImageSection { int y0, y1; // actual y range int ia0, ia1; // vertex indices for edge A int ib0, ib1; // vertex indices for edge B SplashCoord xa0, ya0, xa1, ya1; // edge A SplashCoord dxdya; // slope of edge A SplashCoord xb0, yb0, xb1, yb1; // edge B SplashCoord dxdyb; // slope of edge B }; //------------------------------------------------------------------------ // SplashPipe //------------------------------------------------------------------------ #define splashPipeMaxStages 9 struct SplashPipe { // source pattern SplashPattern *pattern; // source alpha and color Guchar aInput; SplashColor cSrcVal; // special cases and result color GBool noTransparency; GBool shapeOnly; SplashPipeResultColorCtrl resultColorCtrl; // non-isolated group correction // (this is only used when Splash::composite() is called to composite // a non-isolated group onto the backdrop) GBool nonIsolatedGroup; // the "run" function void (Splash::*run)(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); }; SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorNoAlphaBlendCMYK #endif }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaNoBlendCMYK #endif }; SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaBlendCMYK #endif }; //------------------------------------------------------------------------ // modified region //------------------------------------------------------------------------ void Splash::clearModRegion() { modXMin = bitmap->width; modYMin = bitmap->height; modXMax = -1; modYMax = -1; } inline void Splash::updateModX(int x) { if (x < modXMin) { modXMin = x; } if (x > modXMax) { modXMax = x; } } inline void Splash::updateModY(int y) { if (y < modYMin) { modYMin = y; } if (y > modYMax) { modYMax = y; } } //------------------------------------------------------------------------ // pipeline //------------------------------------------------------------------------ inline void Splash::pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup) { SplashColorMode mode; mode = bitmap->mode; pipe->pattern = NULL; // source color if (pattern && pattern->isStatic()) { pattern->getColor(0, 0, pipe->cSrcVal); pipe->pattern = NULL; } else { pipe->pattern = pattern; } // source alpha pipe->aInput = aInput; // special cases pipe->noTransparency = aInput == 255 && !state->softMask && !usesShape && !state->inNonIsolatedGroup && !state->inKnockoutGroup && !nonIsolatedGroup && state->overprintMask == 0xffffffff; pipe->shapeOnly = aInput == 255 && !state->softMask && usesShape && !state->inNonIsolatedGroup && !state->inKnockoutGroup && !nonIsolatedGroup && state->overprintMask == 0xffffffff; // result color if (pipe->noTransparency) { // the !state->blendFunc case is handled separately in pipeRun pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[mode]; } else if (!state->blendFunc) { pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[mode]; } else { pipe->resultColorCtrl = pipeResultColorAlphaBlend[mode]; } // non-isolated group correction pipe->nonIsolatedGroup = nonIsolatedGroup; // select the 'run' function pipe->run = &Splash::pipeRun; if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) { if (mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono1; } else if (mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleMono8; } else if (mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleRGB8; } else if (mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleBGR8; #if SPLASH_CMYK } else if (mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunSimpleCMYK8; #endif } } else if (!pipe->pattern && pipe->shapeOnly && !state->blendFunc) { if (mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunShapeMono1; } else if (mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeMono8; } else if (mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeRGB8; } else if (mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeBGR8; #if SPLASH_CMYK } else if (mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunShapeCMYK8; #endif } } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask && usesShape && !(state->inNonIsolatedGroup && groupBackBitmap->alpha) && !state->inKnockoutGroup && !state->blendFunc && !pipe->nonIsolatedGroup) { if (mode == splashModeMono1 && !bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono1; } else if (mode == splashModeMono8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAAMono8; } else if (mode == splashModeRGB8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAARGB8; } else if (mode == splashModeBGR8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAABGR8; #if SPLASH_CMYK } else if (mode == splashModeCMYK8 && bitmap->alpha) { pipe->run = &Splash::pipeRunAACMYK8; #endif } } } // general case void Splash::pipeRun(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar *shapePtr2; Guchar shape, aSrc, aDest, alphaI, alphaIm1, alpha0, aResult; SplashColor cSrc, cDest, cBlend; Guchar shapeVal, cResult0, cResult1, cResult2, cResult3; int cSrcStride, shapeStride, x, lastX, t; SplashColorPtr destColorPtr; Guchar destColorMask; Guchar *destAlphaPtr; SplashColorPtr color0Ptr; Guchar color0Mask; Guchar *alpha0Ptr; SplashColorPtr softMaskPtr; #if SPLASH_CMYK SplashColor cSrc2, cDest2; #endif if (cSrcPtr && !pipe->pattern) { cSrcStride = bitmapComps; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (shapePtr) { shapePtr2 = shapePtr; shapeStride = 1; for (; x0 <= x1; ++x0) { if (*shapePtr2) { break; } cSrcPtr += cSrcStride; ++shapePtr2; } } else { shapeVal = 0xff; shapePtr2 = &shapeVal; shapeStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; if (bitmap->mode == splashModeMono1) { destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); } else { destColorPtr = &bitmap->data[y * bitmap->rowSize + x0 * bitmapComps]; destColorMask = 0; // make gcc happy } if (bitmap->alpha) { destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; } else { destAlphaPtr = NULL; } if (state->softMask) { softMaskPtr = &state->softMask->data[y * state->softMask->rowSize + x0]; } else { softMaskPtr = NULL; } if (state->inKnockoutGroup) { if (bitmap->mode == splashModeMono1) { color0Ptr = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + ((groupBackX + x0) >> 3)]; color0Mask = (Guchar)(0x80 >> ((groupBackX + x0) & 7)); } else { color0Ptr = &groupBackBitmap->data[(groupBackY + y) * groupBackBitmap->rowSize + (groupBackX + x0) * bitmapComps]; color0Mask = 0; // make gcc happy } } else { color0Ptr = NULL; color0Mask = 0; // make gcc happy } if (state->inNonIsolatedGroup && groupBackBitmap->alpha) { alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + y) * groupBackBitmap->alphaRowSize + (groupBackX + x0)]; } else { alpha0Ptr = NULL; } for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr2; if (!shape) { if (bitmap->mode == splashModeMono1) { destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); } else { destColorPtr += bitmapComps; } if (destAlphaPtr) { ++destAlphaPtr; } if (softMaskPtr) { ++softMaskPtr; } if (color0Ptr) { if (bitmap->mode == splashModeMono1) { color0Ptr += color0Mask & 1; color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); } else { color0Ptr += bitmapComps; } } if (alpha0Ptr) { ++alpha0Ptr; } cSrcPtr += cSrcStride; shapePtr2 += shapeStride; continue; } lastX = x; //----- source color // static pattern: handled in pipeInit // fixed color: handled in pipeInit // dynamic pattern if (pipe->pattern) { pipe->pattern->getColor(x, y, pipe->cSrcVal); } cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy if (pipe->noTransparency && !state->blendFunc) { //----- result color switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: cResult0 = state->grayTransfer[cSrcPtr[0]]; break; case splashModeRGB8: case splashModeBGR8: cResult0 = state->rgbTransferR[cSrcPtr[0]]; cResult1 = state->rgbTransferG[cSrcPtr[1]]; cResult2 = state->rgbTransferB[cSrcPtr[2]]; break; #if SPLASH_CMYK case splashModeCMYK8: cResult0 = state->cmykTransferC[cSrcPtr[0]]; cResult1 = state->cmykTransferM[cSrcPtr[1]]; cResult2 = state->cmykTransferY[cSrcPtr[2]]; cResult3 = state->cmykTransferK[cSrcPtr[3]]; break; #endif } aResult = 255; } else { // if (noTransparency && !blendFunc) //----- read destination pixel // (or backdrop color, for knockout groups) if (color0Ptr) { switch (bitmap->mode) { case splashModeMono1: cDest[0] = (*color0Ptr & color0Mask) ? 0xff : 0x00; color0Ptr += color0Mask & 1; color0Mask = (Guchar)((color0Mask << 7) | (color0Mask >> 1)); break; case splashModeMono8: cDest[0] = *color0Ptr++; break; case splashModeRGB8: cDest[0] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[2] = color0Ptr[2]; color0Ptr += 3; break; case splashModeBGR8: cDest[2] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[0] = color0Ptr[2]; color0Ptr += 3; break; #if SPLASH_CMYK case splashModeCMYK8: cDest[0] = color0Ptr[0]; cDest[1] = color0Ptr[1]; cDest[2] = color0Ptr[2]; cDest[3] = color0Ptr[3]; color0Ptr += 4; break; #endif } } else { switch (bitmap->mode) { case splashModeMono1: cDest[0] = (*destColorPtr & destColorMask) ? 0xff : 0x00; break; case splashModeMono8: cDest[0] = *destColorPtr; break; case splashModeRGB8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; break; case splashModeBGR8: cDest[0] = destColorPtr[2]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[0]; break; #if SPLASH_CMYK case splashModeCMYK8: cDest[0] = destColorPtr[0]; cDest[1] = destColorPtr[1]; cDest[2] = destColorPtr[2]; cDest[3] = destColorPtr[3]; break; #endif } } if (destAlphaPtr) { aDest = *destAlphaPtr; } else { aDest = 0xff; } //----- read source color; handle overprint switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: cSrc[0] = state->grayTransfer[cSrcPtr[0]]; break; case splashModeRGB8: case splashModeBGR8: cSrc[0] = state->rgbTransferR[cSrcPtr[0]]; cSrc[1] = state->rgbTransferG[cSrcPtr[1]]; cSrc[2] = state->rgbTransferB[cSrcPtr[2]]; break; #if SPLASH_CMYK case splashModeCMYK8: if (state->overprintMask & 0x01) { cSrc[0] = state->cmykTransferC[cSrcPtr[0]]; } else { cSrc[0] = div255(aDest * cDest[0]); } if (state->overprintMask & 0x02) { cSrc[1] = state->cmykTransferM[cSrcPtr[1]]; } else { cSrc[1] = div255(aDest * cDest[1]); } if (state->overprintMask & 0x04) { cSrc[2] = state->cmykTransferY[cSrcPtr[2]]; } else { cSrc[2] = div255(aDest * cDest[2]); } if (state->overprintMask & 0x08) { cSrc[3] = state->cmykTransferK[cSrcPtr[3]]; } else { cSrc[3] = div255(aDest * cDest[3]); } break; #endif } //----- source alpha if (softMaskPtr) { if (shapePtr) { aSrc = div255(div255(pipe->aInput * *softMaskPtr++) * shape); } else { aSrc = div255(pipe->aInput * *softMaskPtr++); } } else if (shapePtr) { aSrc = div255(pipe->aInput * shape); } else { aSrc = pipe->aInput; } //----- non-isolated group correction if (pipe->nonIsolatedGroup) { // This path is only used when Splash::composite() is called to // composite a non-isolated group onto the backdrop. In this // case, shape is the source (group) alpha. t = (aDest * 255) / shape - aDest; switch (bitmap->mode) { #if SPLASH_CMYK case splashModeCMYK8: cSrc[3] = clip255(cSrc[3] + ((cSrc[3] - cDest[3]) * t) / 255); #endif case splashModeRGB8: case splashModeBGR8: cSrc[2] = clip255(cSrc[2] + ((cSrc[2] - cDest[2]) * t) / 255); cSrc[1] = clip255(cSrc[1] + ((cSrc[1] - cDest[1]) * t) / 255); case splashModeMono1: case splashModeMono8: cSrc[0] = clip255(cSrc[0] + ((cSrc[0] - cDest[0]) * t) / 255); break; } } //----- blend function if (state->blendFunc) { #if SPLASH_CMYK if (bitmap->mode == splashModeCMYK8) { // convert colors to additive cSrc2[0] = (Guchar)(0xff - cSrc[0]); cSrc2[1] = (Guchar)(0xff - cSrc[1]); cSrc2[2] = (Guchar)(0xff - cSrc[2]); cSrc2[3] = (Guchar)(0xff - cSrc[3]); cDest2[0] = (Guchar)(0xff - cDest[0]); cDest2[1] = (Guchar)(0xff - cDest[1]); cDest2[2] = (Guchar)(0xff - cDest[2]); cDest2[3] = (Guchar)(0xff - cDest[3]); (*state->blendFunc)(cSrc2, cDest2, cBlend, bitmap->mode); // convert result back to subtractive cBlend[0] = (Guchar)(0xff - cBlend[0]); cBlend[1] = (Guchar)(0xff - cBlend[1]); cBlend[2] = (Guchar)(0xff - cBlend[2]); cBlend[3] = (Guchar)(0xff - cBlend[3]); } else #endif (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode); } //----- result alpha and non-isolated group element correction // alphaI = alpha_i // alphaIm1 = alpha_(i-1) if (pipe->noTransparency) { alphaI = alphaIm1 = aResult = 255; } else if (alpha0Ptr) { if (color0Ptr) { // non-isolated, knockout aResult = aSrc; alpha0 = *alpha0Ptr++; alphaI = (Guchar)(aSrc + alpha0 - div255(aSrc * alpha0)); alphaIm1 = alpha0; } else { // non-isolated, non-knockout aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alpha0 = *alpha0Ptr++; alphaI = (Guchar)(aResult + alpha0 - div255(aResult * alpha0)); alphaIm1 = (Guchar)(alpha0 + aDest - div255(alpha0 * aDest)); } } else { if (color0Ptr) { // isolated, knockout aResult = aSrc; alphaI = aSrc; alphaIm1 = 0; } else { // isolated, non-knockout aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; alphaIm1 = aDest; } } //----- result color switch (pipe->resultColorCtrl) { case splashPipeResultColorNoAlphaBlendMono: cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); break; case splashPipeResultColorNoAlphaBlendRGB: cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); break; #if SPLASH_CMYK case splashPipeResultColorNoAlphaBlendCMYK: cResult0 = div255((255 - aDest) * cSrc[0] + aDest * cBlend[0]); cResult1 = div255((255 - aDest) * cSrc[1] + aDest * cBlend[1]); cResult2 = div255((255 - aDest) * cSrc[2] + aDest * cBlend[2]); cResult3 = div255((255 - aDest) * cSrc[3] + aDest * cBlend[3]); break; #endif case splashPipeResultColorAlphaNoBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI); } break; case splashPipeResultColorAlphaNoBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI); } break; #if SPLASH_CMYK case splashPipeResultColorAlphaNoBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * cSrc[0]) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * cSrc[1]) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * cSrc[2]) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * cSrc[3]) / alphaI); } break; #endif case splashPipeResultColorAlphaBlendMono: if (alphaI == 0) { cResult0 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI); } break; case splashPipeResultColorAlphaBlendRGB: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI); } break; #if SPLASH_CMYK case splashPipeResultColorAlphaBlendCMYK: if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest[0] + aSrc * ((255 - alphaIm1) * cSrc[0] + alphaIm1 * cBlend[0]) / 255) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest[1] + aSrc * ((255 - alphaIm1) * cSrc[1] + alphaIm1 * cBlend[1]) / 255) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest[2] + aSrc * ((255 - alphaIm1) * cSrc[2] + alphaIm1 * cBlend[2]) / 255) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest[3] + aSrc * ((255 - alphaIm1) * cSrc[3] + alphaIm1 * cBlend[3]) / 255) / alphaI); } break; #endif } } // if (noTransparency && !blendFunc) //----- write destination pixel switch (bitmap->mode) { case splashModeMono1: if (state->screen->test(x, y, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); break; case splashModeMono8: *destColorPtr++ = cResult0; break; case splashModeRGB8: destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; break; case splashModeBGR8: destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; break; #if SPLASH_CMYK case splashModeCMYK8: destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; break; #endif } if (destAlphaPtr) { *destAlphaPtr++ = aResult; } cSrcPtr += cSrcStride; shapePtr2 += shapeStride; } // for (x ...) updateModX(lastX); } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono1 && !bitmap->alpha) { void Splash::pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; SplashScreenCursor screenCursor; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); screenCursor = state->screen->getTestCursor(y); for (x = x0; x <= x1; ++x) { //----- write destination pixel cResult0 = state->grayTransfer[cSrcPtr[0]]; if (state->screen->testWithCursor(screenCursor, x, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeMono8 && bitmap->alpha) { void Splash::pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel *destColorPtr++ = state->grayTransfer[cSrcPtr[0]]; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeRGB8 && bitmap->alpha) { void Splash::pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr += 3; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeBGR8 && bitmap->alpha) { void Splash::pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->rgbTransferB[cSrcPtr[2]]; destColorPtr[1] = state->rgbTransferG[cSrcPtr[1]]; destColorPtr[2] = state->rgbTransferR[cSrcPtr[0]]; destColorPtr += 3; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } #if SPLASH_CMYK // special case: // !pipe->pattern && pipe->noTransparency && !state->blendFunc && // bitmap->mode == splashModeCMYK8 && bitmap->alpha) { void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } if (x0 > x1) { return; } updateModX(x0); updateModX(x1); updateModY(y); destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- write destination pixel destColorPtr[0] = state->cmykTransferC[cSrcPtr[0]]; destColorPtr[1] = state->cmykTransferM[cSrcPtr[1]]; destColorPtr[2] = state->cmykTransferY[cSrcPtr[2]]; destColorPtr[3] = state->cmykTransferK[cSrcPtr[3]]; destColorPtr += 4; *destAlphaPtr++ = 255; cSrcPtr += cSrcStride; } } #endif // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeMono1 && !bitmap->alpha void Splash::pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; SplashScreenCursor screenCursor; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); screenCursor = state->screen->getTestCursor(y); for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { cResult0 = cSrc0; } else { //----- read destination pixel cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; //----- result color // note: aDest = alphaI = aResult = 0xff cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); } //----- write destination pixel if (state->screen->testWithCursor(screenCursor, x, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeMono8 && bitmap->alpha void Splash::pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { ++destColorPtr; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; } else { //----- read destination alpha aDest = *destAlphaPtr; //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; } else { //----- read destination pixel cDest0 = *destColorPtr; //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); } } //----- write destination pixel *destColorPtr++ = cResult0; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeRGB8 && bitmap->alpha void Splash::pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination alpha aDest = *destAlphaPtr; //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeBGR8 && bitmap->alpha void Splash::pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination alpha aDest = *destAlphaPtr; //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; } else { //----- read destination pixel cDest0 = destColorPtr[2]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[0]; //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } } //----- write destination pixel destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #if SPLASH_CMYK // special case: // !pipe->pattern && pipe->shapeOnly && !state->blendFunc && // bitmap->mode == splashModeCMYK8 && bitmap->alpha void Splash::pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2, cSrc3; Guchar cDest0, cDest1, cDest2, cDest3; Guchar cResult0, cResult1, cResult2, cResult3; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 4; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; cDest3 = destColorPtr[3]; aDest = *destAlphaPtr; //----- overprint if (state->overprintMask & 1) { cSrc0 = state->cmykTransferC[cSrcPtr[0]]; } else { cSrc0 = div255(aDest * cDest0); } if (state->overprintMask & 2) { cSrc1 = state->cmykTransferM[cSrcPtr[1]]; } else { cSrc1 = div255(aDest * cDest1); } if (state->overprintMask & 4) { cSrc2 = state->cmykTransferY[cSrcPtr[2]]; } else { cSrc2 = div255(aDest * cDest2); } if (state->overprintMask & 8) { cSrc3 = state->cmykTransferK[cSrcPtr[3]]; } else { cSrc3 = div255(aDest * cDest3); } //----- source alpha aSrc = shape; //----- special case for aSrc = 255 if (aSrc == 255) { aResult = 255; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; cResult3 = cSrc3; } else { //----- special case for aDest = 0 if (aDest == 0) { aResult = aSrc; cResult0 = cSrc0; cResult1 = cSrc1; cResult2 = cSrc2; cResult3 = cSrc3; } else { //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); } } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #endif // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono1 && !bitmap->alpha void Splash::pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar destColorMask; SplashScreenCursor screenCursor; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + (x0 >> 3)]; destColorMask = (Guchar)(0x80 >> (x0 & 7)); screenCursor = state->screen->getTestCursor(y); for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = (*destColorPtr & destColorMask) ? 0xff : 0x00; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result color // note: aDest = alphaI = aResult = 0xff cResult0 = (Guchar)div255((0xff - aSrc) * cDest0 + aSrc * cSrc0); //----- write destination pixel if (state->screen->testWithCursor(screenCursor, x, cResult0)) { *destColorPtr |= destColorMask; } else { *destColorPtr &= (Guchar)~destColorMask; } destColorPtr += destColorMask & 1; destColorMask = (Guchar)((destColorMask << 7) | (destColorMask >> 1)); cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeMono8 && bitmap->alpha void Splash::pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult, cSrc0, cDest0, cResult0; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 1; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { ++destColorPtr; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = *destColorPtr; aDest = *destAlphaPtr; //----- source color cSrc0 = state->grayTransfer[cSrcPtr[0]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); } //----- write destination pixel *destColorPtr++ = cResult0; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeRGB8 && bitmap->alpha void Splash::pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; aDest = *destAlphaPtr; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeBGR8 && bitmap->alpha void Splash::pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2; Guchar cDest0, cDest1, cDest2; Guchar cResult0, cResult1, cResult2; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 3; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 3; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[2]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[0]; aDest = *destAlphaPtr; //----- source color cSrc0 = state->rgbTransferR[cSrcPtr[0]]; cSrc1 = state->rgbTransferG[cSrcPtr[1]]; cSrc2 = state->rgbTransferB[cSrcPtr[2]]; //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); } //----- write destination pixel destColorPtr[0] = cResult2; destColorPtr[1] = cResult1; destColorPtr[2] = cResult0; destColorPtr += 3; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #if SPLASH_CMYK // special case: // !pipe->pattern && !pipe->noTransparency && !state->softMask && // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc && // !pipe->nonIsolatedGroup && // bitmap->mode == splashModeCMYK8 && bitmap->alpha void Splash::pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr) { Guchar shape, aSrc, aDest, alphaI, aResult; Guchar cSrc0, cSrc1, cSrc2, cSrc3; Guchar cDest0, cDest1, cDest2, cDest3; Guchar cResult0, cResult1, cResult2, cResult3; SplashColorPtr destColorPtr; Guchar *destAlphaPtr; int cSrcStride, x, lastX; if (cSrcPtr) { cSrcStride = 4; } else { cSrcPtr = pipe->cSrcVal; cSrcStride = 0; } for (; x0 <= x1; ++x0) { if (*shapePtr) { break; } cSrcPtr += cSrcStride; ++shapePtr; } if (x0 > x1) { return; } updateModX(x0); updateModY(y); lastX = x0; destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x0]; destAlphaPtr = &bitmap->alpha[y * bitmap->alphaRowSize + x0]; for (x = x0; x <= x1; ++x) { //----- shape shape = *shapePtr; if (!shape) { destColorPtr += 4; ++destAlphaPtr; cSrcPtr += cSrcStride; ++shapePtr; continue; } lastX = x; //----- read destination pixel cDest0 = destColorPtr[0]; cDest1 = destColorPtr[1]; cDest2 = destColorPtr[2]; cDest3 = destColorPtr[3]; aDest = *destAlphaPtr; //----- overprint if (state->overprintMask & 1) { cSrc0 = state->cmykTransferC[cSrcPtr[0]]; } else { cSrc0 = div255(aDest * cDest0); } if (state->overprintMask & 2) { cSrc1 = state->cmykTransferM[cSrcPtr[1]]; } else { cSrc1 = div255(aDest * cDest1); } if (state->overprintMask & 4) { cSrc2 = state->cmykTransferY[cSrcPtr[2]]; } else { cSrc2 = div255(aDest * cDest2); } if (state->overprintMask & 8) { cSrc3 = state->cmykTransferK[cSrcPtr[3]]; } else { cSrc3 = div255(aDest * cDest3); } //----- source alpha aSrc = div255(pipe->aInput * shape); //----- result alpha and non-isolated group element correction aResult = (Guchar)(aSrc + aDest - div255(aSrc * aDest)); alphaI = aResult; //----- result color if (alphaI == 0) { cResult0 = 0; cResult1 = 0; cResult2 = 0; cResult3 = 0; } else { cResult0 = (Guchar)(((alphaI - aSrc) * cDest0 + aSrc * cSrc0) / alphaI); cResult1 = (Guchar)(((alphaI - aSrc) * cDest1 + aSrc * cSrc1) / alphaI); cResult2 = (Guchar)(((alphaI - aSrc) * cDest2 + aSrc * cSrc2) / alphaI); cResult3 = (Guchar)(((alphaI - aSrc) * cDest3 + aSrc * cSrc3) / alphaI); } //----- write destination pixel destColorPtr[0] = cResult0; destColorPtr[1] = cResult1; destColorPtr[2] = cResult2; destColorPtr[3] = cResult3; destColorPtr += 4; *destAlphaPtr++ = aResult; cSrcPtr += cSrcStride; ++shapePtr; } updateModX(lastX); } #endif //------------------------------------------------------------------------ // Transform a point from user space to device space. inline void Splash::transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo) { // [ m[0] m[1] 0 ] // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] // [ m[4] m[5] 1 ] *xo = xi * matrix[0] + yi * matrix[2] + matrix[4]; *yo = xi * matrix[1] + yi * matrix[3] + matrix[5]; } //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreenParams *screenParams) { bitmap = bitmapA; bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenParams); scanBuf = (Guchar *)gmalloc(bitmap->width); if (bitmap->mode == splashModeMono1) { scanBuf2 = (Guchar *)gmalloc(bitmap->width); } else { scanBuf2 = NULL; } groupBackBitmap = NULL; minLineWidth = 0; clearModRegion(); debugMode = gFalse; } Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreen *screenA) { bitmap = bitmapA; bitmapComps = splashColorModeNComps[bitmap->mode]; vectorAntialias = vectorAntialiasA; inShading = gFalse; state = new SplashState(bitmap->width, bitmap->height, vectorAntialias, screenA); scanBuf = (Guchar *)gmalloc(bitmap->width); if (bitmap->mode == splashModeMono1) { scanBuf2 = (Guchar *)gmalloc(bitmap->width); } else { scanBuf2 = NULL; } groupBackBitmap = NULL; minLineWidth = 0; clearModRegion(); debugMode = gFalse; } Splash::~Splash() { while (state->next) { restoreState(); } delete state; gfree(scanBuf); gfree(scanBuf2); } //------------------------------------------------------------------------ // state read //------------------------------------------------------------------------ SplashCoord *Splash::getMatrix() { return state->matrix; } SplashPattern *Splash::getStrokePattern() { return state->strokePattern; } SplashPattern *Splash::getFillPattern() { return state->fillPattern; } SplashScreen *Splash::getScreen() { return state->screen; } SplashBlendFunc Splash::getBlendFunc() { return state->blendFunc; } SplashCoord Splash::getStrokeAlpha() { return state->strokeAlpha; } SplashCoord Splash::getFillAlpha() { return state->fillAlpha; } SplashCoord Splash::getLineWidth() { return state->lineWidth; } int Splash::getLineCap() { return state->lineCap; } int Splash::getLineJoin() { return state->lineJoin; } SplashCoord Splash::getMiterLimit() { return state->miterLimit; } SplashCoord Splash::getFlatness() { return state->flatness; } SplashCoord *Splash::getLineDash() { return state->lineDash; } int Splash::getLineDashLength() { return state->lineDashLength; } SplashCoord Splash::getLineDashPhase() { return state->lineDashPhase; } SplashStrokeAdjustMode Splash::getStrokeAdjust() { return state->strokeAdjust; } SplashClip *Splash::getClip() { return state->clip; } SplashBitmap *Splash::getSoftMask() { return state->softMask; } GBool Splash::getInNonIsolatedGroup() { return state->inNonIsolatedGroup; } GBool Splash::getInKnockoutGroup() { return state->inKnockoutGroup; } //------------------------------------------------------------------------ // state write //------------------------------------------------------------------------ void Splash::setMatrix(SplashCoord *matrix) { memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord)); } void Splash::setStrokePattern(SplashPattern *strokePattern) { state->setStrokePattern(strokePattern); } void Splash::setFillPattern(SplashPattern *fillPattern) { state->setFillPattern(fillPattern); } void Splash::setScreen(SplashScreen *screen) { state->setScreen(screen); } void Splash::setBlendFunc(SplashBlendFunc func) { state->blendFunc = func; } void Splash::setStrokeAlpha(SplashCoord alpha) { state->strokeAlpha = alpha; } void Splash::setFillAlpha(SplashCoord alpha) { state->fillAlpha = alpha; } void Splash::setLineWidth(SplashCoord lineWidth) { state->lineWidth = lineWidth; } void Splash::setLineCap(int lineCap) { if (lineCap >= 0 && lineCap <= 2) { state->lineCap = lineCap; } else { state->lineCap = 0; } } void Splash::setLineJoin(int lineJoin) { if (lineJoin >= 0 && lineJoin <= 2) { state->lineJoin = lineJoin; } else { state->lineJoin = 0; } } void Splash::setMiterLimit(SplashCoord miterLimit) { state->miterLimit = miterLimit; } void Splash::setFlatness(SplashCoord flatness) { if (flatness < 1) { state->flatness = 1; } else { state->flatness = flatness; } } void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase) { state->setLineDash(lineDash, lineDashLength, lineDashPhase); } void Splash::setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust) { state->strokeAdjust = strokeAdjust; } void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { state->clipResetToRect(x0, y0, x1, y1); } SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) { return state->clipToRect(x0, y0, x1, y1); } SplashError Splash::clipToPath(SplashPath *path, GBool eo) { return state->clipToPath(path, eo); } void Splash::setSoftMask(SplashBitmap *softMask) { state->setSoftMask(softMask); } void Splash::setInTransparencyGroup(SplashBitmap *groupBackBitmapA, int groupBackXA, int groupBackYA, GBool nonIsolated, GBool knockout) { groupBackBitmap = groupBackBitmapA; groupBackX = groupBackXA; groupBackY = groupBackYA; state->inNonIsolatedGroup = nonIsolated; state->inKnockoutGroup = knockout; } void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray) { state->setTransfer(red, green, blue, gray); } void Splash::setOverprintMask(Guint overprintMask) { state->overprintMask = overprintMask; } void Splash::setEnablePathSimplification(GBool en) { state->enablePathSimplification = en; } //------------------------------------------------------------------------ // state save/restore //------------------------------------------------------------------------ void Splash::saveState() { SplashState *newState; newState = state->copy(); newState->next = state; state = newState; } SplashError Splash::restoreState() { SplashState *oldState; if (!state->next) { return splashErrNoSave; } oldState = state; state = state->next; delete oldState; return splashOk; } //------------------------------------------------------------------------ // drawing operations //------------------------------------------------------------------------ void Splash::clear(SplashColorPtr color, Guchar alpha) { SplashColorPtr row, p; Guchar mono; int x, y; switch (bitmap->mode) { case splashModeMono1: mono = (color[0] & 0x80) ? 0xff : 0x00; if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), mono, -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, mono, bitmap->rowSize * bitmap->height); } break; case splashModeMono8: if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } break; case splashModeRGB8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; } row += bitmap->rowSize; } } break; case splashModeBGR8: if (color[0] == color[1] && color[1] == color[2]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[2]; *p++ = color[1]; *p++ = color[0]; } row += bitmap->rowSize; } } break; #if SPLASH_CMYK case splashModeCMYK8: if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) { if (bitmap->rowSize < 0) { memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1), color[0], -bitmap->rowSize * bitmap->height); } else { memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height); } } else { row = bitmap->data; for (y = 0; y < bitmap->height; ++y) { p = row; for (x = 0; x < bitmap->width; ++x) { *p++ = color[0]; *p++ = color[1]; *p++ = color[2]; *p++ = color[3]; } row += bitmap->rowSize; } } break; #endif } if (bitmap->alpha) { memset(bitmap->alpha, alpha, bitmap->alphaRowSize * bitmap->height); } updateModX(0); updateModY(0); updateModX(bitmap->width - 1); updateModY(bitmap->height - 1); } SplashError Splash::stroke(SplashPath *path) { SplashPath *path2, *dPath; SplashCoord t0, t1, t2, t3, w, w2, lineDashMax, lineDashTotal; int lineCap, lineJoin, i; if (debugMode) { printf("stroke [dash:%d] [width:%.2f]:\n", state->lineDashLength, (double)state->lineWidth); dumpPath(path); } opClipRes = splashClipAllOutside; if (path->length == 0) { return splashErrEmptyPath; } path2 = flattenPath(path, state->matrix, state->flatness); // Compute an approximation of the transformed line width. // Given a CTM of [m0 m1], // [m2 m3] // if |m0|*|m3| >= |m1|*|m2| then use min{|m0|,|m3|}, else // use min{|m1|,|m2|}. // This handles the common cases -- [s 0 ] and [0 s] -- // [0 +/-s] [+/-s 0] // well, and still does something reasonable for the uncommon // case transforms. t0 = splashAbs(state->matrix[0]); t1 = splashAbs(state->matrix[1]); t2 = splashAbs(state->matrix[2]); t3 = splashAbs(state->matrix[3]); if (t0 * t3 >= t1 * t2) { w = (t0 < t3) ? t0 : t3; } else { w = (t1 < t2) ? t1 : t2; } w2 = w * state->lineWidth; // construct the dashed path if (state->lineDashLength > 0) { // check the maximum transformed dash element length (using the // same approximation as for line width) -- if it's less than 0.1 // pixel, don't apply the dash pattern; this avoids a huge // performance/memory hit with PDF files that use absurd dash // patterns like [0.0007 0.0003] lineDashTotal = 0; lineDashMax = 0; for (i = 0; i < state->lineDashLength; ++i) { lineDashTotal += state->lineDash[i]; if (state->lineDash[i] > lineDashMax) { lineDashMax = state->lineDash[i]; } } // Acrobat simply draws nothing if the dash array is [0] if (lineDashTotal == 0) { delete path2; return splashOk; } if (w * lineDashMax > 0.1) { dPath = makeDashedPath(path2); delete path2; path2 = dPath; if (path2->length == 0) { delete path2; return splashErrEmptyPath; } } } // round caps on narrow lines look bad, and can't be // stroke-adjusted, so use projecting caps instead (but we can't do // this if there are zero-length dashes or segments, because those // turn into round dots) lineCap = state->lineCap; lineJoin = state->lineJoin; if (state->strokeAdjust == splashStrokeAdjustCAD && w2 < 3.5) { if (lineCap == splashLineCapRound && !state->lineDashContainsZeroLengthDashes() && !path->containsZeroLengthSubpaths()) { lineCap = splashLineCapProjecting; } if (lineJoin == splashLineJoinRound) { lineJoin = splashLineJoinBevel; } } // if there is a min line width set, and the transformed line width // is smaller, use the min line width if (w > 0 && w2 < minLineWidth) { strokeWide(path2, minLineWidth / w, splashLineCapButt, splashLineJoinBevel); } else if (bitmap->mode == splashModeMono1 || !vectorAntialias) { // in monochrome mode or if antialiasing is disabled, use 0-width // lines for any transformed line width <= 1 -- lines less than 1 // pixel wide look too fat without antialiasing if (w2 < 1.001) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth, lineCap, lineJoin); } } else { // in gray and color modes, only use 0-width lines if the line // width is explicitly set to 0 if (state->lineWidth == 0) { strokeNarrow(path2); } else { strokeWide(path2, state->lineWidth, lineCap, lineJoin); } } delete path2; return splashOk; } void Splash::strokeNarrow(SplashPath *path) { SplashPipe pipe; SplashXPath *xPath; SplashXPathSeg *seg; int x0, x1, y0, y1, xa, xb, y; SplashCoord dxdy; SplashClipResult clipRes; int nClipRes[3]; int i; nClipRes[0] = nClipRes[1] = nClipRes[2] = 0; xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse, state->enablePathSimplification, state->strokeAdjust); pipeInit(&pipe, state->strokePattern, (Guchar)splashRound(state->strokeAlpha * 255), gTrue, gFalse); for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) { if (seg->y0 <= seg->y1) { y0 = splashFloor(seg->y0); y1 = splashFloor(seg->y1); x0 = splashFloor(seg->x0); x1 = splashFloor(seg->x1); } else { y0 = splashFloor(seg->y1); y1 = splashFloor(seg->y0); x0 = splashFloor(seg->x1); x1 = splashFloor(seg->x0); } if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0, x0 <= x1 ? x1 : x0, y1, state->strokeAdjust)) != splashClipAllOutside) { if (y0 == y1) { if (x0 <= x1) { drawStrokeSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside); } } else { dxdy = seg->dxdy; y = state->clip->getYMinI(state->strokeAdjust); if (y0 < y) { y0 = y; x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy); } y = state->clip->getYMaxI(state->strokeAdjust); if (y1 > y) { y1 = y; x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy); } if (x0 <= x1) { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 + 1; } if (xa == xb) { drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside); } xa = xb; } } else { xa = x0; for (y = y0; y <= y1; ++y) { if (y < y1) { xb = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy); } else { xb = x1 - 1; } if (xa == xb) { drawStrokeSpan(&pipe, xa, xa, y, clipRes == splashClipAllInside); } else { drawStrokeSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside); } xa = xb; } } } } ++nClipRes[clipRes]; } if (nClipRes[splashClipPartial] || (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) { opClipRes = splashClipPartial; } else if (nClipRes[splashClipAllInside]) { opClipRes = splashClipAllInside; } else { opClipRes = splashClipAllOutside; } delete xPath; } void Splash::drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip) { int x; x = state->clip->getXMinI(state->strokeAdjust); if (x > x0) { x0 = x; } x = state->clip->getXMaxI(state->strokeAdjust); if (x < x1) { x1 = x; } if (x0 > x1) { return; } for (x = x0; x <= x1; ++x) { scanBuf[x] = 0xff; } if (!noClip) { if (!state->clip->clipSpanBinary(scanBuf, y, x0, x1, state->strokeAdjust)) { return; } } (this->*pipe->run)(pipe, x0, x1, y, scanBuf + x0, NULL); } void Splash::strokeWide(SplashPath *path, SplashCoord w, int lineCap, int lineJoin) { SplashPath *path2; path2 = makeStrokePath(path, w, lineCap, lineJoin, gFalse); fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha); delete path2; } SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness) { SplashPath *fPath; SplashCoord flatness2; Guchar flag; int i; fPath = new SplashPath(); #if USE_FIXEDPOINT flatness2 = flatness; #else flatness2 = flatness * flatness; #endif i = 0; while (i < path->length) { flag = path->flags[i]; if (flag & splashPathFirst) { fPath->moveTo(path->pts[i].x, path->pts[i].y); ++i; } else { if (flag & splashPathCurve) { flattenCurve(path->pts[i-1].x, path->pts[i-1].y, path->pts[i ].x, path->pts[i ].y, path->pts[i+1].x, path->pts[i+1].y, path->pts[i+2].x, path->pts[i+2].y, matrix, flatness2, fPath); i += 3; } else { fPath->lineTo(path->pts[i].x, path->pts[i].y); ++i; } if (path->flags[i-1] & splashPathClosed) { fPath->close(); } } } return fPath; } void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath) { SplashCoord cx[splashMaxCurveSplits + 1][3]; SplashCoord cy[splashMaxCurveSplits + 1][3]; int cNext[splashMaxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, tx, ty, d1, d2; int p1, p2, p3; // initial segment p1 = 0; p2 = splashMaxCurveSplits; cx[p1][0] = x0; cy[p1][0] = y0; cx[p1][1] = x1; cy[p1][1] = y1; cx[p1][2] = x2; cy[p1][2] = y2; cx[p2][0] = x3; cy[p2][0] = y3; cNext[p1] = p2; while (p1 < splashMaxCurveSplits) { // get the next segment xl0 = cx[p1][0]; yl0 = cy[p1][0]; xx1 = cx[p1][1]; yy1 = cy[p1][1]; xx2 = cx[p1][2]; yy2 = cy[p1][2]; p2 = cNext[p1]; xr3 = cx[p2][0]; yr3 = cy[p2][0]; // compute the distances (in device space) from the control points // to the midpoint of the straight line (this is a bit of a hack, // but it's much faster than computing the actual distances to the // line) transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my); transform(matrix, xx1, yy1, &tx, &ty); #if USE_FIXEDPOINT d1 = splashDist(tx, ty, mx, my); #else dx = tx - mx; dy = ty - my; d1 = dx*dx + dy*dy; #endif transform(matrix, xx2, yy2, &tx, &ty); #if USE_FIXEDPOINT d2 = splashDist(tx, ty, mx, my); #else dx = tx - mx; dy = ty - my; d2 = dx*dx + dy*dy; #endif // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { fPath->lineTo(xr3, yr3); p1 = p2; // otherwise, subdivide the curve } else { xl1 = splashAvg(xl0, xx1); yl1 = splashAvg(yl0, yy1); xh = splashAvg(xx1, xx2); yh = splashAvg(yy1, yy2); xl2 = splashAvg(xl1, xh); yl2 = splashAvg(yl1, yh); xr2 = splashAvg(xx2, xr3); yr2 = splashAvg(yy2, yr3); xr1 = splashAvg(xh, xr2); yr1 = splashAvg(yh, yr2); xr0 = splashAvg(xl2, xr1); yr0 = splashAvg(yl2, yr1); // add the new subdivision points p3 = (p1 + p2) / 2; cx[p1][1] = xl1; cy[p1][1] = yl1; cx[p1][2] = xl2; cy[p1][2] = yl2; cNext[p1] = p3; cx[p3][0] = xr0; cy[p3][0] = yr0; cx[p3][1] = xr1; cy[p3][1] = yr1; cx[p3][2] = xr2; cy[p3][2] = yr2; cNext[p3] = p2; } } } SplashPath *Splash::makeDashedPath(SplashPath *path) { SplashPath *dPath; SplashCoord lineDashTotal; SplashCoord lineDashStartPhase, lineDashDist, segLen; SplashCoord x0, y0, x1, y1, xa, ya; GBool lineDashStartOn, lineDashEndOn, lineDashOn, newPath; int lineDashStartIdx, lineDashIdx, subpathStart, nDashes; int i, j, k; lineDashTotal = 0; for (i = 0; i < state->lineDashLength; ++i) { lineDashTotal += state->lineDash[i]; } // Acrobat simply draws nothing if the dash array is [0] if (lineDashTotal == 0) { return new SplashPath(); } lineDashStartPhase = state->lineDashPhase; if (lineDashStartPhase > lineDashTotal * 2) { i = splashFloor(lineDashStartPhase / (lineDashTotal * 2)); lineDashStartPhase -= lineDashTotal * i * 2; } else if (lineDashStartPhase < 0) { i = splashCeil(-lineDashStartPhase / (lineDashTotal * 2)); lineDashStartPhase += lineDashTotal * i * 2; } i = splashFloor(lineDashStartPhase / lineDashTotal); lineDashStartPhase -= (SplashCoord)i * lineDashTotal; lineDashStartOn = gTrue; lineDashStartIdx = 0; if (lineDashStartPhase > 0) { while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) { lineDashStartOn = !lineDashStartOn; lineDashStartPhase -= state->lineDash[lineDashStartIdx]; if (++lineDashStartIdx == state->lineDashLength) { lineDashStartIdx = 0; } } } dPath = new SplashPath(); // process each subpath i = 0; while (i < path->length) { // find the end of the subpath for (j = i; j < path->length - 1 && !(path->flags[j] & splashPathLast); ++j) ; // initialize the dash parameters lineDashOn = lineDashStartOn; lineDashEndOn = lineDashStartOn; lineDashIdx = lineDashStartIdx; lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase; subpathStart = dPath->length; nDashes = 0; // process each segment of the subpath newPath = gTrue; for (k = i; k < j; ++k) { // grab the segment x0 = path->pts[k].x; y0 = path->pts[k].y; x1 = path->pts[k+1].x; y1 = path->pts[k+1].y; segLen = splashDist(x0, y0, x1, y1); // process the segment while (segLen > 0) { // Special case for zero-length dash segments: draw a very // short -- but not zero-length -- segment. This ensures that // we get the correct behavior with butt and projecting line // caps. The PS/PDF specs imply that zero-length segments are // not drawn unless the line cap is round, but Acrobat and // Ghostscript both draw very short segments (for butt caps) // and squares (for projecting caps). if (lineDashDist == 0) { if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; ++nDashes; } xa = x0 + ((SplashCoord)0.001 / segLen) * (x1 - x0); ya = y0 + ((SplashCoord)0.001 / segLen) * (y1 - y0); dPath->lineTo(xa, ya); } } else if (lineDashDist >= segLen) { if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; ++nDashes; } dPath->lineTo(x1, y1); } lineDashDist -= segLen; segLen = 0; } else { xa = x0 + (lineDashDist / segLen) * (x1 - x0); ya = y0 + (lineDashDist / segLen) * (y1 - y0); if (lineDashOn) { if (newPath) { dPath->moveTo(x0, y0); newPath = gFalse; ++nDashes; } dPath->lineTo(xa, ya); } x0 = xa; y0 = ya; segLen -= lineDashDist; lineDashDist = 0; } lineDashEndOn = lineDashOn; // get the next entry in the dash array if (lineDashDist <= 0) { lineDashOn = !lineDashOn; if (++lineDashIdx == state->lineDashLength) { lineDashIdx = 0; } lineDashDist = state->lineDash[lineDashIdx]; newPath = gTrue; } } } // in a closed subpath, where the dash pattern is "on" at both the // start and end of the subpath, we need to merge the start and // end to get a proper line join if ((path->flags[j] & splashPathClosed) && lineDashStartOn && lineDashEndOn) { if (nDashes == 1) { dPath->close(); } else if (nDashes > 1) { k = subpathStart; do { ++k; dPath->lineTo(dPath->pts[k].x, dPath->pts[k].y); } while (!(dPath->flags[k] & splashPathLast)); ++k; memmove(&dPath->pts[subpathStart], &dPath->pts[k], (dPath->length - k) * sizeof(SplashPathPoint)); memmove(&dPath->flags[subpathStart], &dPath->flags[k], (dPath->length - k) * sizeof(Guchar)); dPath->length -= k - subpathStart; dPath->curSubpath -= k - subpathStart; } } i = j + 1; } return dPath; } SplashError Splash::fill(SplashPath *path, GBool eo) { if (debugMode) { printf("fill [eo:%d]:\n", eo); dumpPath(path); } return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha); } SplashError Splash::fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha) { SplashPipe pipe; SplashPath *path2; SplashXPath *xPath; SplashXPathScanner *scanner; int xMin, yMin, xMax, xMin2, xMax2, yMax, y, t; SplashClipResult clipRes; if (path->length == 0) { return splashErrEmptyPath; } if (pathAllOutside(path)) { opClipRes = splashClipAllOutside; return splashOk; } path2 = tweakFillPath(path); xPath = new SplashXPath(path2, state->matrix, state->flatness, gTrue, state->enablePathSimplification, state->strokeAdjust); if (path2 != path) { delete path2; } xMin = xPath->getXMin(); yMin = xPath->getYMin(); xMax = xPath->getXMax(); yMax = xPath->getYMax(); if (xMin > xMax || yMin > yMax) { delete xPath; return splashOk; } scanner = new SplashXPathScanner(xPath, eo, yMin, yMax); // check clipping if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, state->strokeAdjust)) != splashClipAllOutside) { if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { yMax = t; } if (xMin > xMax || yMin > yMax) { delete scanner; delete xPath; return splashOk; } pipeInit(&pipe, pattern, (Guchar)splashRound(alpha * 255), gTrue, gFalse); // draw the spans if (vectorAntialias && !inShading) { for (y = yMin; y <= yMax; ++y) { scanner->getSpan(scanBuf, y, xMin, xMax, &xMin2, &xMax2); if (xMin2 <= xMax2) { if (clipRes != splashClipAllInside) { state->clip->clipSpan(scanBuf, y, xMin2, xMax2, state->strokeAdjust); } (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); } } } else { for (y = yMin; y <= yMax; ++y) { scanner->getSpanBinary(scanBuf, y, xMin, xMax, &xMin2, &xMax2); if (xMin2 <= xMax2) { if (clipRes != splashClipAllInside) { state->clip->clipSpanBinary(scanBuf, y, xMin2, xMax2, state->strokeAdjust); } (this->*pipe.run)(&pipe, xMin2, xMax2, y, scanBuf + xMin2, NULL); } } } } opClipRes = clipRes; delete scanner; delete xPath; return splashOk; } // Applies various tweaks to a fill path: // (1) add stroke adjust hints to a filled rectangle // (2) applies a minimum width to a zero-width filled rectangle (so // stroke adjustment works correctly // (3) convert a degenerate fill ('moveto lineto fill' and 'moveto // lineto closepath fill') to a minimum-width filled rectangle // // These tweaks only apply to paths with a single subpath. // // Returns either the unchanged input path or a new path (in which // case the returned path must be deleted by the caller). SplashPath *Splash::tweakFillPath(SplashPath *path) { SplashPath *path2; SplashCoord xx0, yy0, xx1, yy1, dx, dy, d, wx, wy, w; int n; if (state->strokeAdjust == splashStrokeAdjustOff || path->hints) { return path; } n = path->getLength(); if (!((n == 2) || (n == 3 && path->flags[1] == 0) || (n == 4 && path->flags[1] == 0 && path->flags[2] == 0) || (n == 5 && path->flags[1] == 0 && path->flags[2] == 0 && path->flags[3] == 0))) { return path; } path2 = path; // degenerate fill (2 or 3 points) or rectangle of (nearly) zero // width --> replace with a min-width rectangle and hint if (n == 2 || (n == 3 && (path->flags[0] & splashPathClosed)) || (n == 3 && (splashAbs(path->pts[0].x - path->pts[2].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[2].y) < 0.001)) || ((n == 4 || (n == 5 && (path->flags[0] & splashPathClosed))) && ((splashAbs(path->pts[0].x - path->pts[1].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[1].y) < 0.001 && splashAbs(path->pts[2].x - path->pts[3].x) < 0.001 && splashAbs(path->pts[2].y - path->pts[3].y) < 0.001) || (splashAbs(path->pts[0].x - path->pts[3].x) < 0.001 && splashAbs(path->pts[0].y - path->pts[3].y) < 0.001 && splashAbs(path->pts[1].x - path->pts[2].x) < 0.001 && splashAbs(path->pts[1].y - path->pts[2].y) < 0.001)))) { wx = state->matrix[0] + state->matrix[2]; wy = state->matrix[1] + state->matrix[3]; w = splashSqrt(wx*wx + wy*wy); if (w < 0.001) { w = 0; } else { // min width is 0.1 -- this constant is minWidth * sqrt(2) w = (SplashCoord)0.1414 / w; } xx0 = path->pts[0].x; yy0 = path->pts[0].y; if (n <= 3) { xx1 = path->pts[1].x; yy1 = path->pts[1].y; } else { xx1 = path->pts[2].x; yy1 = path->pts[2].y; } dx = xx1 - xx0; dy = yy1 - yy0; d = splashSqrt(dx * dx + dy * dy); if (d < 0.001) { d = 0; } else { d = w / d; } dx *= d; dy *= d; path2 = new SplashPath(); path2->moveTo(xx0 + dy, yy0 - dx); path2->lineTo(xx1 + dy, yy1 - dx); path2->lineTo(xx1 - dy, yy1 + dx); path2->lineTo(xx0 - dy, yy0 + dx); path2->close(gTrue); path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); // unclosed rectangle --> close and hint } else if (n == 4 && !(path->flags[0] & splashPathClosed)) { path2->close(gTrue); path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); // closed rectangle --> hint } else if (n == 5 && (path->flags[0] & splashPathClosed)) { path2->addStrokeAdjustHint(0, 2, 0, 4); path2->addStrokeAdjustHint(1, 3, 0, 4); } return path2; } GBool Splash::pathAllOutside(SplashPath *path) { SplashCoord xMin1, yMin1, xMax1, yMax1; SplashCoord xMin2, yMin2, xMax2, yMax2; SplashCoord x, y; int xMinI, yMinI, xMaxI, yMaxI; int i; xMin1 = xMax1 = path->pts[0].x; yMin1 = yMax1 = path->pts[0].y; for (i = 1; i < path->length; ++i) { if (path->pts[i].x < xMin1) { xMin1 = path->pts[i].x; } else if (path->pts[i].x > xMax1) { xMax1 = path->pts[i].x; } if (path->pts[i].y < yMin1) { yMin1 = path->pts[i].y; } else if (path->pts[i].y > yMax1) { yMax1 = path->pts[i].y; } } transform(state->matrix, xMin1, yMin1, &x, &y); xMin2 = xMax2 = x; yMin2 = yMax2 = y; transform(state->matrix, xMin1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMin1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } transform(state->matrix, xMax1, yMax1, &x, &y); if (x < xMin2) { xMin2 = x; } else if (x > xMax2) { xMax2 = x; } if (y < yMin2) { yMin2 = y; } else if (y > yMax2) { yMax2 = y; } // sanity-check the coordinates - xMinI/yMinI/xMaxI/yMaxI are // 32-bit integers, so coords need to be < 2^31 SplashXPath::clampCoords(&xMin2, &yMin2); SplashXPath::clampCoords(&xMax2, &yMax2); xMinI = splashFloor(xMin2); yMinI = splashFloor(yMin2); xMaxI = splashFloor(xMax2); yMaxI = splashFloor(yMax2); return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI, state->strokeAdjust) == splashClipAllOutside; } SplashError Splash::fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font) { SplashGlyphBitmap glyph; SplashCoord xt, yt; int x0, y0, xFrac, yFrac; SplashError err; if (debugMode) { printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n", (double)x, (double)y, c, c, c); } transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); xFrac = splashFloor((xt - x0) * splashFontFraction); y0 = splashFloor(yt); yFrac = splashFloor((yt - y0) * splashFontFraction); if (!font->getGlyph(c, xFrac, yFrac, &glyph)) { return splashErrNoGlyph; } err = fillGlyph2(x0, y0, &glyph); if (glyph.freeData) { gfree(glyph.data); } return err; } SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph) { SplashCoord xt, yt; int x0, y0; transform(state->matrix, x, y, &xt, &yt); x0 = splashFloor(xt); y0 = splashFloor(yt); return fillGlyph2(x0, y0, glyph); } SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) { SplashPipe pipe; SplashClipResult clipRes; Guchar alpha; Guchar *p; int xMin, yMin, xMax, yMax; int x, y, xg, yg, xx, t; xg = x0 - glyph->x; yg = y0 - glyph->y; xMin = xg; xMax = xg + glyph->w - 1; yMin = yg; yMax = yg + glyph->h - 1; if ((clipRes = state->clip->testRect(xMin, yMin, xMax, yMax, state->strokeAdjust)) != splashClipAllOutside) { pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (clipRes == splashClipAllInside) { if (glyph->aa) { p = glyph->data; for (y = yMin; y <= yMax; ++y) { (this->*pipe.run)(&pipe, xMin, xMax, y, glyph->data + (y - yMin) * glyph->w, NULL); } } else { p = glyph->data; for (y = yMin; y <= yMax; ++y) { for (x = xMin; x <= xMax; x += 8) { alpha = *p++; for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { scanBuf[x + xx] = (alpha & 0x80) ? 0xff : 0x00; alpha = (Guchar)(alpha << 1); } } (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } else { if ((t = state->clip->getXMinI(state->strokeAdjust)) > xMin) { xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust)) < xMax) { xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > yMin) { yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust)) < yMax) { yMax = t; } if (xMin <= xMax && yMin <= yMax) { if (glyph->aa) { for (y = yMin; y <= yMax; ++y) { p = glyph->data + (y - yg) * glyph->w + (xMin - xg); memcpy(scanBuf + xMin, p, xMax - xMin + 1); state->clip->clipSpan(scanBuf, y, xMin, xMax, state->strokeAdjust); (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } else { for (y = yMin; y <= yMax; ++y) { p = glyph->data + (y - yg) * ((glyph->w + 7) >> 3) + ((xMin - xg) >> 3); alpha = *p++; xx = (xMin - xg) & 7; alpha = (Guchar)(alpha << xx); for (x = xMin; xx < 8 && x <= xMax; ++x, ++xx) { scanBuf[x] = (alpha & 0x80) ? 255 : 0; alpha = (Guchar)(alpha << 1); } for (; x <= xMax; x += 8) { alpha = *p++; for (xx = 0; xx < 8 && x + xx <= xMax; ++xx) { scanBuf[x + xx] = (alpha & 0x80) ? 255 : 0; alpha = (Guchar)(alpha << 1); } } state->clip->clipSpanBinary(scanBuf, y, xMin, xMax, state->strokeAdjust); (this->*pipe.run)(&pipe, xMin, xMax, y, scanBuf + xMin, NULL); } } } } } opClipRes = clipRes; return splashOk; } void Splash::getImageBounds(SplashCoord xyMin, SplashCoord xyMax, int *xyMinI, int *xyMaxI) { if (state->strokeAdjust == splashStrokeAdjustOff) { *xyMinI = splashFloor(xyMin); *xyMaxI = splashFloor(xyMax); if (*xyMaxI <= *xyMinI) { *xyMaxI = *xyMinI + 1; } } else { splashStrokeAdjust(xyMin, xyMax, xyMinI, xyMaxI, state->strokeAdjust); } } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashBitmap *scaledMask; SplashClipResult clipRes; GBool minorAxisZero; SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; if (debugMode) { printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; // rough estimate of size of scaled mask t0 = splashAbs(mat[0]); t1 = splashAbs(mat[1]); wSize = t0 > t1 ? t0 : t1; t0 = splashAbs(mat[2]); t1 = splashAbs(mat[3]); hSize = t0 > t1 ? t0 : t1; // stream-mode upscaling -- this is slower, so we only use it if the // upscaled mask is large (in which case clipping should remove many // pixels) #if USE_FIXEDPOINT if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { #else if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || (wSize > w && hSize > h && wSize * hSize > 10000000) || ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { upscaleMask(src, srcData, w, h, mat, glyphMode, interpolate); #endif // scaling only } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus horizontal flip } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // scaling plus horizontal and vertical flips } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1); horizFlipImage(scaledMask, scaledWidth, scaledHeight, 1); blitMask(scaledMask, x0, y0, clipRes); delete scaledMask; } // all other cases } else { arbitraryTransformMask(src, srcData, w, h, mat, glyphMode, interpolate); } return splashOk; } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. void Splash::upscaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashClipResult clipRes; SplashPipe pipe; Guchar *unscaledImage, *p; SplashCoord xMin, yMin, xMax, yMax, t; SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; SplashCoord ix, iy, sx, sy, pix0, pix1; int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt; // compute the bbox of the target quadrilateral xMin = xMax = mat[4]; t = mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } getImageBounds(xMin, xMax, &xMinI, &xMaxI); yMin = yMax = mat[5]; t = mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } getImageBounds(yMin, yMax, &yMinI, &yMaxI); // clipping clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } if (clipRes != splashClipAllInside) { if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { xMinI = tt; } if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { xMaxI = tt; } if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { yMinI = tt; } if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { yMaxI = tt; } } // invert the matrix det = mat[0] * mat[3] - mat[1] * mat[2]; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } det = (SplashCoord)1 / det; mi0 = det * mat[3] * srcWidth; mi1 = -det * mat[1] * srcHeight; mi2 = -det * mat[2] * srcWidth; mi3 = det * mat[0] * srcHeight; mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; // grab the image unscaledImage = (Guchar *)gmallocn(srcWidth, srcHeight); for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += srcWidth) { (*src)(srcData, p); for (x = 0; x < srcWidth; ++x) { p[x] = (Guchar)(p[x] * 255); } } // draw it pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (y = yMinI; y < yMaxI; ++y) { for (x = xMinI; x < xMaxI; ++x) { ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; if (interpolate) { if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { x0 = splashFloor(ix - 0.5); x1 = x0 + 1; sx = (ix - 0.5) - x0; y0 = splashFloor(iy - 0.5); y1 = y0 + 1; sy = (iy - 0.5) - y0; if (x0 < 0) { x0 = 0; } if (x1 >= srcWidth) { x1 = srcWidth - 1; } if (y0 < 0) { y0 = 0; } if (y1 >= srcHeight) { y1 = srcHeight - 1; } pix0 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledImage[y0 * srcWidth + x0] + sx * (SplashCoord)unscaledImage[y0 * srcWidth + x1]; pix1 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledImage[y1 * srcWidth + x0] + sx * (SplashCoord)unscaledImage[y1 * srcWidth + x1]; scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } else { scanBuf[x] = 0; } } else { x0 = splashFloor(ix); y0 = splashFloor(iy); if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { scanBuf[x] = unscaledImage[y0 * srcWidth + x0]; } else { scanBuf[x] = 0; } } } if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } } (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, NULL); } gfree(unscaledImage); } // The glyphMode flag is not currently used, but may be useful if the // stroke adjustment behavior is changed. void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate) { SplashBitmap *scaledMask; SplashClipResult clipRes; SplashPipe pipe; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int bw, y, xa, xb, x, i, xx, yy; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping xMin = splashRound(vx[0]); xMax = splashRound(vx[0]); yMin = splashRound(vy[0]); yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; } else if (t0 > xMax) { xMax = t0; } t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; } else if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } // compute the scale factors if (mat[0] >= 0) { t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image scaledMask = scaleMask(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); // construct the three sections i = 0; if (vy[1] < vy[i]) { i = 1; } if (vy[2] < vy[i]) { i = 2; } if (vy[3] < vy[i]) { i = 3; } // NB: if using fixed point, 0.000001 will be truncated to zero, // so these two comparisons must be <=, not < if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && vy[(i-1) & 3] < vy[(i+1) & 3]) { i = (i-1) & 3; } if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { section[0].y0 = splashRound(vy[i]); section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; section[0].ib0 = (i+1) & 3; section[0].ib1 = (i+2) & 3; } else { section[0].ia0 = (i+1) & 3; section[0].ia1 = (i+2) & 3; section[0].ib0 = i; section[0].ib1 = (i+3) & 3; } nSections = 1; } else { section[0].y0 = splashRound(vy[i]); section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i+2) & 3; if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[0].ia1 = section[2].ia0 = (i+1) & 3; section[0].ib1 = section[2].ib0 = (i+3) & 3; } else { section[0].ia1 = section[2].ia0 = (i+3) & 3; section[0].ib1 = section[2].ib0 = (i+1) & 3; } if (vy[(i+1) & 3] < vy[(i+3) & 3]) { section[1].y0 = splashRound(vy[(i+1) & 3]); section[2].y0 = splashRound(vy[(i+3) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = (i+1) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i+3) & 3; section[1].ib0 = (i+1) & 3; section[1].ib1 = (i+2) & 3; } } else { section[1].y0 = splashRound(vy[(i+3) & 3]); section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; section[1].ib0 = (i+3) & 3; section[1].ib1 = (i+2) & 3; } else { section[1].ia0 = (i+3) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } // scan all pixels inside the target region bw = bitmap->width; for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = splashRound(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya); xb = splashRound(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb); if (xa > xb) { continue; } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } // check the scanBuf bounds if (xa >= bw || xb < 0) { continue; } if (xa < 0) { xa = 0; } if (xb > bw) { xb = bw; } // get the scan line for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (xx < 0) { xx = 0; } else if (xx >= scaledWidth) { xx = scaledWidth - 1; } if (yy < 0) { yy = 0; } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } scanBuf[x] = scaledMask->data[yy * scaledWidth + xx]; } // clip the scan line if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, state->strokeAdjust); } } // draw the scan line (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, NULL); } } delete scaledMask; } // Scale an image mask into a SplashBitmap. SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, gFalse); if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { scaleMaskYdXd(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYdXu(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { scaleMaskYuXd(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { if (interpolate) { scaleMaskYuXuI(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleMaskYuXu(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } } return dest; } void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint *pixBuf; Guint pix; Guchar *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d0 = (255 << 23) / (yStep * xp); d1 = (255 << 23) / (yStep * (xp + 1)); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += pixBuf[xx++]; } // (255 * pix) / xStep * yStep pix = (pix * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix; } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint *pixBuf; Guint pix; Guchar *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * sizeof(int)); for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf); for (j = 0; j < srcWidth; ++j) { pixBuf[j] += lineBuf[j]; } } // init x scale Bresenham xt = 0; d = (255 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = pixBuf[x]; // (255 * pix) / yStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix; } } } gfree(pixBuf); gfree(lineBuf); } void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guint pix; Guchar *destPtr0, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1; int i; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; destPtr0 = dest->data; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; d0 = (255 << 23) / xp; d1 = (255 << 23) / (xp + 1); xx = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel pix = 0; for (i = 0; i < xStep; ++i) { pix += lineBuf[xx++]; } // (255 * pix) / xStep pix = (pix * d) >> 23; // store the pixel for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + i * scaledWidth + x; *destPtr = (Guchar)pix; } } destPtr0 += yStep * scaledWidth; } gfree(lineBuf); } void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf; Guchar pix; Guchar *srcPtr, *destPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; int i; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmalloc(srcWidth); // init y scale Bresenham yt = 0; destPtr = dest->data; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf); // init x scale Bresenham xt = 0; // generate one row srcPtr = lineBuf; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel pix = *srcPtr ? 255 : 0; ++srcPtr; // duplicate the pixel horizontally for (i = 0; i < xStep; ++i) { *destPtr++ = pix; } } // duplicate the row vertically for (i = 1 ; i < yStep; ++i) { memcpy(destPtr, destPtr - scaledWidth, scaledWidth); destPtr += scaledWidth; } } gfree(lineBuf); } void Splash::scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf0, *lineBuf1, *tBuf; Guchar pix; SplashCoord yr, xr, ys, xs, ySrc, xSrc; int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x; Guchar *destPtr; // ratios yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; // allocate buffers lineBuf0 = (Guchar *)gmalloc(scaledWidth); lineBuf1 = (Guchar *)gmalloc(scaledWidth); // read first two rows (*src)(srcData, lineBuf0); if (srcHeight > 1) { (*src)(srcData, lineBuf1); yBuf = 1; } else { memcpy(lineBuf1, lineBuf0, srcWidth); yBuf = 0; } // interpolate first two rows for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } lineBuf0[x] = (Guchar)(int) ((xs * (int)lineBuf0[xSrc0] + ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1]) * 255); lineBuf1[x] = (Guchar)(int) ((xs * (int)lineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); } destPtr = dest->data; for (y = 0; y < scaledHeight; ++y) { // compute vertical interpolation parameters ySrc = yr * y; ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); ySrc1 = ySrc0 + 1; ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); if (ySrc0 < 0) { ySrc0 = 0; ys = 1; } if (ySrc1 >= srcHeight) { ySrc1 = srcHeight - 1; ys = 0; } // read another row (if necessary) if (ySrc1 > yBuf) { tBuf = lineBuf0; lineBuf0 = lineBuf1; lineBuf1 = tBuf; (*src)(srcData, lineBuf1); // interpolate the row for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } lineBuf1[x] = (Guchar)(int) ((xs * (int)lineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1]) * 255); } ++yBuf; } // do the vertical interpolation for (x = 0; x < scaledWidth; ++x) { pix = (Guchar)(int)(ys * (int)lineBuf0[x] + ((SplashCoord)1 - ys) * (int)lineBuf1[x]); // store the pixel *destPtr++ = pix; } } gfree(lineBuf1); gfree(lineBuf0); } void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; int w, h, x0, x1, y0, y1, y, t; w = src->width; h = src->height; pipeInit(&pipe, state->fillPattern, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (clipRes == splashClipAllInside) { for (y = 0; y < h; ++y) { (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->data + y * (size_t)w, NULL); } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->data + (y - yDest) * (size_t)w + (x0 - xDest), x1 - x0); if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, x0, x1 - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, NULL); } } } } SplashError Splash::drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat, GBool interpolate) { GBool ok; SplashBitmap *scaledImg; SplashClipResult clipRes; GBool minorAxisZero; SplashCoord wSize, hSize, t0, t1; int x0, y0, x1, y1, scaledWidth, scaledHeight; int nComps; if (debugMode) { printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]); } // check color modes ok = gFalse; // make gcc happy nComps = 0; // make gcc happy switch (bitmap->mode) { case splashModeMono1: case splashModeMono8: ok = srcMode == splashModeMono8; nComps = 1; break; case splashModeRGB8: case splashModeBGR8: ok = srcMode == splashModeRGB8; nComps = 3; break; #if SPLASH_CMYK case splashModeCMYK8: ok = srcMode == splashModeCMYK8; nComps = 4; break; #endif default: ok = gFalse; break; } if (!ok) { return splashErrModeMismatch; } // check for singular matrix if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) { return splashErrSingularMatrix; } minorAxisZero = splashAbs(mat[1]) <= 0.0001 && splashAbs(mat[2]) <= 0.0001; // rough estimate of size of scaled image t0 = splashAbs(mat[0]); t1 = splashAbs(mat[1]); wSize = t0 > t1 ? t0 : t1; t0 = splashAbs(mat[2]); t1 = splashAbs(mat[3]); hSize = t0 > t1 ? t0 : t1; // stream-mode upscaling -- this is slower, so we only use it if the // upscaled image is large (in which case clipping should remove // many pixels) #if USE_FIXEDPOINT if ((wSize > 2 * w && hSize > 2 * h && (int)wSize > 1000000 / (int)hSize) || (wSize > w && hSize > h && (int)wSize > 10000000 / (int)hSize) || ((wSize > w || hSize > h) && (int)wSize > 25000000 / (int)hSize)) { #else if ((wSize > 2 * w && hSize > 2 * h && wSize * hSize > 1000000) || (wSize > w && hSize > h && wSize * hSize > 10000000) || ((wSize > w || hSize > h) && wSize * hSize > 25000000)) { #endif upscaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate); // scaling only } else if (mat[0] > 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus vertical flip } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[4], mat[0] + mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus horizontal flip } else if (mat[0] < 0 && minorAxisZero && mat[3] > 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[5], mat[3] + mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // scaling plus horizontal and vertical flips } else if (mat[0] < 0 && minorAxisZero && mat[3] < 0) { getImageBounds(mat[0] + mat[4], mat[4], &x0, &x1); getImageBounds(mat[3] + mat[5], mat[5], &y0, &y1); clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes != splashClipAllOutside) { scaledWidth = x1 - x0; scaledHeight = y1 - y0; scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h, scaledWidth, scaledHeight, interpolate); vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); horizFlipImage(scaledImg, scaledWidth, scaledHeight, nComps); blitImage(scaledImg, srcAlpha, x0, y0, clipRes); delete scaledImg; } // all other cases } else { arbitraryTransformImage(src, srcData, srcMode, nComps, srcAlpha, w, h, mat, interpolate); } return splashOk; } void Splash::upscaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate) { SplashClipResult clipRes; SplashPipe pipe; SplashColorPtr unscaledImage, pixelBuf, p, q, q00, q01, q10, q11; Guchar *unscaledAlpha, *alphaPtr; SplashCoord xMin, yMin, xMax, yMax, t; SplashCoord mi0, mi1, mi2, mi3, mi4, mi5, det; SplashCoord ix, iy, sx, sy, pix0, pix1; SplashBitmapRowSize rowSize; int xMinI, yMinI, xMaxI, yMaxI, x, y, x0, y0, x1, y1, tt, i; // compute the bbox of the target quadrilateral xMin = xMax = mat[4]; t = mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[2] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } t = mat[0] + mat[4]; if (t < xMin) { xMin = t; } else if (t > xMax) { xMax = t; } getImageBounds(xMin, xMax, &xMinI, &xMaxI); yMin = yMax = mat[5]; t = mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[3] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } t = mat[1] + mat[5]; if (t < yMin) { yMin = t; } else if (t > yMax) { yMax = t; } getImageBounds(yMin, yMax, &yMinI, &yMaxI); // clipping clipRes = state->clip->testRect(xMinI, yMinI, xMaxI - 1, yMaxI - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } if (clipRes != splashClipAllInside) { if ((tt = state->clip->getXMinI(state->strokeAdjust)) > xMinI) { xMinI = tt; } if ((tt = state->clip->getXMaxI(state->strokeAdjust) + 1) < xMaxI) { xMaxI = tt; } if ((tt = state->clip->getYMinI(state->strokeAdjust)) > yMinI) { yMinI = tt; } if ((tt = state->clip->getYMaxI(state->strokeAdjust) + 1) < yMaxI) { yMaxI = tt; } } // invert the matrix det = mat[0] * mat[3] - mat[1] * mat[2]; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in fillImageMask return; } det = (SplashCoord)1 / det; mi0 = det * mat[3] * srcWidth; mi1 = -det * mat[1] * srcHeight; mi2 = -det * mat[2] * srcWidth; mi3 = det * mat[0] * srcHeight; mi4 = det * (mat[2] * mat[5] - mat[3] * mat[4]) * srcWidth; mi5 = -det * (mat[0] * mat[5] - mat[1] * mat[4]) * srcHeight; // grab the image if (srcWidth > INT_MAX / nComps) { rowSize = -1; } else { rowSize = srcWidth * nComps; } unscaledImage = (SplashColorPtr)gmallocn64(srcHeight, rowSize); if (srcAlpha) { unscaledAlpha = (Guchar *)gmallocn(srcHeight, srcWidth); for (y = 0, p = unscaledImage, alphaPtr = unscaledAlpha; y < srcHeight; ++y, p += rowSize, alphaPtr += srcWidth) { (*src)(srcData, p, alphaPtr); } } else { unscaledAlpha = NULL; for (y = 0, p = unscaledImage; y < srcHeight; ++y, p += rowSize) { (*src)(srcData, p, NULL); } } // draw it pixelBuf = (SplashColorPtr)gmallocn(xMaxI - xMinI, nComps); pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); for (y = yMinI; y < yMaxI; ++y) { p = pixelBuf; for (x = xMinI; x < xMaxI; ++x) { ix = ((SplashCoord)x + 0.5) * mi0 + ((SplashCoord)y + 0.5) * mi2 + mi4; iy = ((SplashCoord)x + 0.5) * mi1 + ((SplashCoord)y + 0.5) * mi3 + mi5; if (interpolate) { if (ix >= 0 && ix < srcWidth && iy >= 0 && iy < srcHeight) { x0 = splashFloor(ix - 0.5); x1 = x0 + 1; sx = (ix - 0.5) - x0; y0 = splashFloor(iy - 0.5); y1 = y0 + 1; sy = (iy - 0.5) - y0; if (x0 < 0) { x0 = 0; } if (x1 >= srcWidth) { x1 = srcWidth - 1; } if (y0 < 0) { y0 = 0; } if (y1 >= srcHeight) { y1 = srcHeight - 1; } q00 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; q01 = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x1 * nComps]; q10 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x0 * nComps]; q11 = &unscaledImage[y1 * rowSize + (SplashBitmapRowSize)x1 * nComps]; for (i = 0; i < nComps; ++i) { pix0 = ((SplashCoord)1 - sx) * (int)*q00++ + sx * (int)*q01++; pix1 = ((SplashCoord)1 - sx) * (int)*q10++ + sx * (int)*q11++; *p++ = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } if (srcAlpha) { pix0 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledAlpha[y0 * srcWidth + x0] + sx * (SplashCoord)unscaledAlpha[y0 * srcWidth + x1]; pix1 = ((SplashCoord)1 - sx) * (SplashCoord)unscaledAlpha[y1 * srcWidth + x0] + sx * (SplashCoord)unscaledAlpha[y1 * srcWidth + x1]; scanBuf[x] = (Guchar)splashRound(((SplashCoord)1 - sy) * pix0 + sy * pix1); } else { scanBuf[x] = 0xff; } } else { for (i = 0; i < nComps; ++i) { *p++ = 0; } scanBuf[x] = 0; } } else { x0 = splashFloor(ix); y0 = splashFloor(iy); if (x0 >= 0 && x0 < srcWidth && y0 >= 0 && y0 < srcHeight) { q = &unscaledImage[y0 * rowSize + (SplashBitmapRowSize)x0 * nComps]; for (i = 0; i < nComps; ++i) { *p++ = *q++; } if (srcAlpha) { scanBuf[x] = unscaledAlpha[y0 * srcWidth + x0]; } else { scanBuf[x] = 0xff; } } else { for (i = 0; i < nComps; ++i) { *p++ = 0; } scanBuf[x] = 0; } } } if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xMinI, xMaxI - 1, state->strokeAdjust); } } (this->*pipe.run)(&pipe, xMinI, xMaxI - 1, y, scanBuf + xMinI, pixelBuf); } gfree(pixelBuf); gfree(unscaledImage); gfree(unscaledAlpha); } void Splash::arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate) { SplashBitmap *scaledImg; SplashClipResult clipRes; SplashPipe pipe; SplashColorPtr pixelBuf; int scaledWidth, scaledHeight, t0, t1; SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11; SplashCoord vx[4], vy[4]; int xMin, yMin, xMax, yMax; ImageSection section[3]; int nSections; int y, xa, xb, x, i, xx, yy; // compute the four vertices of the target quadrilateral vx[0] = mat[4]; vy[0] = mat[5]; vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5]; vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5]; vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5]; // clipping xMin = splashRound(vx[0]); xMax = splashRound(vx[0]); yMin = splashRound(vy[0]); yMax = splashRound(vy[0]); for (i = 1; i < 4; ++i) { t0 = splashRound(vx[i]); if (t0 < xMin) { xMin = t0; } else if (t0 > xMax) { xMax = t0; } t1 = splashRound(vy[i]); if (t1 < yMin) { yMin = t1; } else if (t1 > yMax) { yMax = t1; } } clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1, state->strokeAdjust); opClipRes = clipRes; if (clipRes == splashClipAllOutside) { return; } // compute the scale factors if (mat[0] >= 0) { t0 = splashRound(mat[0] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[0] + mat[4]); } if (mat[1] >= 0) { t1 = splashRound(mat[1] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[1] + mat[5]); } scaledWidth = t0 > t1 ? t0 : t1; if (mat[2] >= 0) { t0 = splashRound(mat[2] + mat[4]) - splashRound(mat[4]); } else { t0 = splashRound(mat[4]) - splashRound(mat[2] + mat[4]); } if (mat[3] >= 0) { t1 = splashRound(mat[3] + mat[5]) - splashRound(mat[5]); } else { t1 = splashRound(mat[5]) - splashRound(mat[3] + mat[5]); } scaledHeight = t0 > t1 ? t0 : t1; if (scaledWidth == 0) { scaledWidth = 1; } if (scaledHeight == 0) { scaledHeight = 1; } // compute the inverse transform (after scaling) matrix r00 = mat[0] / scaledWidth; r01 = mat[1] / scaledWidth; r10 = mat[2] / scaledHeight; r11 = mat[3] / scaledHeight; det = r00 * r11 - r01 * r10; if (splashAbs(det) < 1e-6) { // this should be caught by the singular matrix check in drawImage return; } ir00 = r11 / det; ir01 = -r01 / det; ir10 = -r10 / det; ir11 = r00 / det; // scale the input image scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate); // construct the three sections i = 0; if (vy[1] < vy[i]) { i = 1; } if (vy[2] < vy[i]) { i = 2; } if (vy[3] < vy[i]) { i = 3; } // NB: if using fixed point, 0.000001 will be truncated to zero, // so these two comparisons must be <=, not < if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 && vy[(i-1) & 3] < vy[(i+1) & 3]) { i = (i-1) & 3; } if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) { section[0].y0 = splashRound(vy[i]); section[0].y1 = splashRound(vy[(i+2) & 3]) - 1; if (vx[i] < vx[(i+1) & 3]) { section[0].ia0 = i; section[0].ia1 = (i+3) & 3; section[0].ib0 = (i+1) & 3; section[0].ib1 = (i+2) & 3; } else { section[0].ia0 = (i+1) & 3; section[0].ia1 = (i+2) & 3; section[0].ib0 = i; section[0].ib1 = (i+3) & 3; } nSections = 1; } else { section[0].y0 = splashRound(vy[i]); section[2].y1 = splashRound(vy[(i+2) & 3]) - 1; section[0].ia0 = section[0].ib0 = i; section[2].ia1 = section[2].ib1 = (i+2) & 3; if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[0].ia1 = section[2].ia0 = (i+1) & 3; section[0].ib1 = section[2].ib0 = (i+3) & 3; } else { section[0].ia1 = section[2].ia0 = (i+3) & 3; section[0].ib1 = section[2].ib0 = (i+1) & 3; } if (vy[(i+1) & 3] < vy[(i+3) & 3]) { section[1].y0 = splashRound(vy[(i+1) & 3]); section[2].y0 = splashRound(vy[(i+3) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = (i+1) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+3) & 3; } else { section[1].ia0 = i; section[1].ia1 = (i+3) & 3; section[1].ib0 = (i+1) & 3; section[1].ib1 = (i+2) & 3; } } else { section[1].y0 = splashRound(vy[(i+3) & 3]); section[2].y0 = splashRound(vy[(i+1) & 3]); if (vx[(i+1) & 3] < vx[(i+3) & 3]) { section[1].ia0 = i; section[1].ia1 = (i+1) & 3; section[1].ib0 = (i+3) & 3; section[1].ib1 = (i+2) & 3; } else { section[1].ia0 = (i+3) & 3; section[1].ia1 = (i+2) & 3; section[1].ib0 = i; section[1].ib1 = (i+1) & 3; } } section[0].y1 = section[1].y0 - 1; section[1].y1 = section[2].y0 - 1; nSections = 3; } for (i = 0; i < nSections; ++i) { section[i].xa0 = vx[section[i].ia0]; section[i].ya0 = vy[section[i].ia0]; section[i].xa1 = vx[section[i].ia1]; section[i].ya1 = vy[section[i].ia1]; section[i].xb0 = vx[section[i].ib0]; section[i].yb0 = vy[section[i].ib0]; section[i].xb1 = vx[section[i].ib1]; section[i].yb1 = vy[section[i].ib1]; section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0); section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0); } // initialize the pixel pipe pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); // make sure narrow images cover at least one pixel if (nSections == 1) { if (section[0].y0 == section[0].y1) { ++section[0].y1; clipRes = opClipRes = splashClipPartial; } } else { if (section[0].y0 == section[2].y1) { ++section[1].y1; clipRes = opClipRes = splashClipPartial; } } pixelBuf = (SplashColorPtr)gmallocn(xMax - xMin + 1, bitmapComps); // scan all pixels inside the target region for (i = 0; i < nSections; ++i) { for (y = section[i].y0; y <= section[i].y1; ++y) { xa = splashRound(section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya); xb = splashRound(section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb); if (xa > xb) { continue; } // make sure narrow images cover at least one pixel if (xa == xb) { ++xb; } // check the scanBuf bounds if (xa >= bitmap->width || xb < 0) { continue; } if (xa < 0) { xa = 0; } if (xb > bitmap->width) { xb = bitmap->width; } // clip the scan line memset(scanBuf + xa, 0xff, xb - xa); if (clipRes != splashClipAllInside) { if (vectorAntialias) { state->clip->clipSpan(scanBuf, y, xa, xb - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, y, xa, xb - 1, state->strokeAdjust); } } // draw the scan line for (x = xa; x < xb; ++x) { // map (x+0.5, y+0.5) back to the scaled image xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10); yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11); // xx should always be within bounds, but floating point // inaccuracy can cause problems if (xx < 0) { xx = 0; } else if (xx >= scaledWidth) { xx = scaledWidth - 1; } if (yy < 0) { yy = 0; } else if (yy >= scaledHeight) { yy = scaledHeight - 1; } // get the color scaledImg->getPixel(xx, yy, pixelBuf + (x - xa) * bitmapComps); // apply alpha if (srcAlpha) { scanBuf[x] = div255(scanBuf[x] * scaledImg->alpha[yy * scaledWidth + xx]); } } (this->*pipe.run)(&pipe, xa, xb - 1, y, scanBuf + xa, pixelBuf); } } gfree(pixelBuf); delete scaledImg; } // Scale an image into a SplashBitmap. SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate) { SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); if (scaledHeight < srcHeight) { if (scaledWidth < srcWidth) { scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } else { if (scaledWidth < srcWidth) { scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { if (interpolate) { scaleImageYuXuI(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } else { scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest); } } } return dest; } void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint *pixBuf, *alphaPixBuf; Guint pix0, pix1, pix2; #if SPLASH_CMYK Guint pix3; #endif Guint alpha; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = NULL; alphaPixBuf = NULL; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d0 = (1 << 23) / (yStep * xp); d1 = (1 << 23) / (yStep * (xp + 1)); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } switch (srcMode) { case splashModeMono8: // compute the final pixel pix0 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx++]; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; break; case splashModeRGB8: // compute the final pixel pix0 = pix1 = pix2 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx+1]; pix2 += pixBuf[xx+2]; xx += 3; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; *destPtr++ = (Guchar)pix1; *destPtr++ = (Guchar)pix2; break; #if SPLASH_CMYK case splashModeCMYK8: // compute the final pixel pix0 = pix1 = pix2 = pix3 = 0; for (i = 0; i < xStep; ++i) { pix0 += pixBuf[xx]; pix1 += pixBuf[xx+1]; pix2 += pixBuf[xx+2]; pix3 += pixBuf[xx+3]; xx += 4; } // pix / xStep * yStep pix0 = (pix0 * d) >> 23; pix1 = (pix1 * d) >> 23; pix2 = (pix2 * d) >> 23; pix3 = (pix3 * d) >> 23; // store the pixel *destPtr++ = (Guchar)pix0; *destPtr++ = (Guchar)pix1; *destPtr++ = (Guchar)pix2; *destPtr++ = (Guchar)pix3; break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // bgr8 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaPixBuf[xxa]; } // alpha / xStep * yStep alpha = (alpha * d) >> 23; *destAlphaPtr++ = (Guchar)alpha; } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); } void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint *pixBuf, *alphaPixBuf; Guint pix[splashMaxColorComps]; Guint alpha; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d; int i, j; // Bresenham parameters for y scale yp = srcHeight / scaledHeight; yq = srcHeight % scaledHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); pixBuf = (Guint *)gmallocn(srcWidth, (int)(nComps * sizeof(int))); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int)); } else { alphaLineBuf = NULL; alphaPixBuf = NULL; } // make gcc happy pix[0] = pix[1] = pix[2] = 0; #if SPLASH_CMYK pix[3] = 0; #endif // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // y scale Bresenham if ((yt += yq) >= scaledHeight) { yt -= scaledHeight; yStep = yp + 1; } else { yStep = yp; } // read rows from image memset(pixBuf, 0, srcWidth * nComps * sizeof(int)); if (srcAlpha) { memset(alphaPixBuf, 0, srcWidth * sizeof(int)); } for (i = 0; i < yStep; ++i) { (*src)(srcData, lineBuf, alphaLineBuf); for (j = 0; j < srcWidth * nComps; ++j) { pixBuf[j] += lineBuf[j]; } if (srcAlpha) { for (j = 0; j < srcWidth; ++j) { alphaPixBuf[j] += alphaLineBuf[j]; } } } // init x scale Bresenham xt = 0; d = (1 << 23) / yStep; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // compute the final pixel for (i = 0; i < nComps; ++i) { // pixBuf[] / yStep pix[i] = (pixBuf[x * nComps + i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; } break; case splashModeRGB8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; } break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < xStep; ++i) { *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[3]; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { // alphaPixBuf[] / yStep alpha = (alphaPixBuf[x] * d) >> 23; for (i = 0; i < xStep; ++i) { *destAlphaPtr++ = (Guchar)alpha; } } } } gfree(alphaPixBuf); gfree(alphaLineBuf); gfree(pixBuf); gfree(lineBuf); } void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guint pix[splashMaxColorComps]; Guint alpha; Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1; int i, j; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = srcWidth / scaledWidth; xq = srcWidth % scaledWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); } else { alphaLineBuf = NULL; } // make gcc happy pix[0] = pix[1] = pix[2] = 0; #if SPLASH_CMYK pix[3] = 0; #endif // init y scale Bresenham yt = 0; destPtr0 = dest->data; destAlphaPtr0 = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; d0 = (1 << 23) / xp; d1 = (1 << 23) / (xp + 1); xx = xxa = 0; for (x = 0; x < scaledWidth; ++x) { // x scale Bresenham if ((xt += xq) >= scaledWidth) { xt -= scaledWidth; xStep = xp + 1; d = d1; } else { xStep = xp; d = d0; } // compute the final pixel for (i = 0; i < nComps; ++i) { pix[i] = 0; } for (i = 0; i < xStep; ++i) { for (j = 0; j < nComps; ++j, ++xx) { pix[j] += lineBuf[xx]; } } for (i = 0; i < nComps; ++i) { // pix[] / xStep pix[i] = (pix[i] * d) >> 23; } // store the pixel switch (srcMode) { case splashModeMono8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; } break; case splashModeRGB8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; } break; #if SPLASH_CMYK case splashModeCMYK8: for (i = 0; i < yStep; ++i) { destPtr = destPtr0 + (i * scaledWidth + x) * nComps; *destPtr++ = (Guchar)pix[0]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[3]; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { alpha = 0; for (i = 0; i < xStep; ++i, ++xxa) { alpha += alphaLineBuf[xxa]; } // alpha / xStep alpha = (alpha * d) >> 23; for (i = 0; i < yStep; ++i) { destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; *destAlphaPtr = (Guchar)alpha; } } } destPtr0 += yStep * scaledWidth * nComps; if (srcAlpha) { destAlphaPtr0 += yStep * scaledWidth; } } gfree(alphaLineBuf); gfree(lineBuf); } void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf, *alphaLineBuf; Guchar pix0, pix1, pix2; #if SPLASH_CMYK Guchar pix3; #endif Guchar alpha; Guchar *srcPtr, *srcAlphaPtr; Guchar *destPtr, *destAlphaPtr; int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep; int i; // Bresenham parameters for y scale yp = scaledHeight / srcHeight; yq = scaledHeight % srcHeight; // Bresenham parameters for x scale xp = scaledWidth / srcWidth; xq = scaledWidth % srcWidth; // allocate buffers lineBuf = (Guchar *)gmallocn(srcWidth, nComps); if (srcAlpha) { alphaLineBuf = (Guchar *)gmalloc(srcWidth); } else { alphaLineBuf = NULL; } // init y scale Bresenham yt = 0; destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < srcHeight; ++y) { // y scale Bresenham if ((yt += yq) >= srcHeight) { yt -= srcHeight; yStep = yp + 1; } else { yStep = yp; } // read row from image (*src)(srcData, lineBuf, alphaLineBuf); // init x scale Bresenham xt = 0; // generate one row srcPtr = lineBuf; srcAlphaPtr = alphaLineBuf; for (x = 0; x < srcWidth; ++x) { // x scale Bresenham if ((xt += xq) >= srcWidth) { xt -= srcWidth; xStep = xp + 1; } else { xStep = xp; } // duplicate the pixel horizontally switch (srcMode) { case splashModeMono8: pix0 = *srcPtr++; for (i = 0; i < xStep; ++i) { *destPtr++ = pix0; } break; case splashModeRGB8: pix0 = *srcPtr++; pix1 = *srcPtr++; pix2 = *srcPtr++; for (i = 0; i < xStep; ++i) { *destPtr++ = pix0; *destPtr++ = pix1; *destPtr++ = pix2; } break; #if SPLASH_CMYK case splashModeCMYK8: pix0 = *srcPtr++; pix1 = *srcPtr++; pix2 = *srcPtr++; pix3 = *srcPtr++; for (i = 0; i < xStep; ++i) { *destPtr++ = pix0; *destPtr++ = pix1; *destPtr++ = pix2; *destPtr++ = pix3; } break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // duplicate the alpha value horizontally if (srcAlpha) { alpha = *srcAlphaPtr++; for (i = 0; i < xStep; ++i) { *destAlphaPtr++ = alpha; } } } // duplicate the row vertically for (i = 1; i < yStep; ++i) { memcpy(destPtr, destPtr - scaledWidth * nComps, scaledWidth * nComps); destPtr += scaledWidth * nComps; } if (srcAlpha) { for (i = 1; i < yStep; ++i) { memcpy(destAlphaPtr, destAlphaPtr - scaledWidth, scaledWidth); destAlphaPtr += scaledWidth; } } } gfree(alphaLineBuf); gfree(lineBuf); } void Splash::scaleImageYuXuI(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest) { Guchar *lineBuf0, *lineBuf1, *alphaLineBuf0, *alphaLineBuf1, *tBuf; Guchar pix[splashMaxColorComps]; SplashCoord yr, xr, ys, xs, ySrc, xSrc; int ySrc0, ySrc1, yBuf, xSrc0, xSrc1, y, x, i; Guchar *destPtr, *destAlphaPtr; // ratios yr = (SplashCoord)srcHeight / (SplashCoord)scaledHeight; xr = (SplashCoord)srcWidth / (SplashCoord)scaledWidth; // allocate buffers lineBuf0 = (Guchar *)gmallocn(scaledWidth, nComps); lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps); if (srcAlpha) { alphaLineBuf0 = (Guchar *)gmalloc(scaledWidth); alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth); } else { alphaLineBuf0 = NULL; alphaLineBuf1 = NULL; } // read first two rows (*src)(srcData, lineBuf0, alphaLineBuf0); if (srcHeight > 1) { (*src)(srcData, lineBuf1, alphaLineBuf1); yBuf = 1; } else { memcpy(lineBuf1, lineBuf0, srcWidth * nComps); if (srcAlpha) { memcpy(alphaLineBuf1, alphaLineBuf0, srcWidth); } yBuf = 0; } // interpolate first two rows for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } for (i = 0; i < nComps; ++i) { lineBuf0[x*nComps+i] = (Guchar)(int) (xs * (int)lineBuf0[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * (int)lineBuf0[xSrc1*nComps+i]); lineBuf1[x*nComps+i] = (Guchar)(int) (xs * (int)lineBuf1[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); } if (srcAlpha) { alphaLineBuf0[x] = (Guchar)(int) (xs * (int)alphaLineBuf0[xSrc0] + ((SplashCoord)1 - xs) * (int)alphaLineBuf0[xSrc1]); alphaLineBuf1[x] = (Guchar)(int) (xs * (int)alphaLineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); } } // make gcc happy pix[0] = pix[1] = pix[2] = 0; #if SPLASH_CMYK pix[3] = 0; #endif destPtr = dest->data; destAlphaPtr = dest->alpha; for (y = 0; y < scaledHeight; ++y) { // compute vertical interpolation parameters ySrc = yr * y; ySrc0 = splashFloor(ySrc + yr * 0.5 - 0.5); ySrc1 = ySrc0 + 1; ys = ((SplashCoord)ySrc1 + 0.5) - (ySrc + yr * 0.5); if (ySrc0 < 0) { ySrc0 = 0; ys = 1; } if (ySrc1 >= srcHeight) { ySrc1 = srcHeight - 1; ys = 0; } // read another row (if necessary) if (ySrc1 > yBuf) { tBuf = lineBuf0; lineBuf0 = lineBuf1; lineBuf1 = tBuf; tBuf = alphaLineBuf0; alphaLineBuf0 = alphaLineBuf1; alphaLineBuf1 = tBuf; (*src)(srcData, lineBuf1, alphaLineBuf1); // interpolate the row for (x = scaledWidth - 1; x >= 0; --x) { xSrc = xr * x; xSrc0 = splashFloor(xSrc + xr * 0.5 - 0.5); xSrc1 = xSrc0 + 1; xs = ((SplashCoord)xSrc1 + 0.5) - (xSrc + xr * 0.5); if (xSrc0 < 0) { xSrc0 = 0; } if (xSrc1 >= srcWidth) { xSrc1 = srcWidth - 1; } for (i = 0; i < nComps; ++i) { lineBuf1[x*nComps+i] = (Guchar)(int) (xs * (int)lineBuf1[xSrc0*nComps+i] + ((SplashCoord)1 - xs) * (int)lineBuf1[xSrc1*nComps+i]); } if (srcAlpha) { alphaLineBuf1[x] = (Guchar)(int) (xs * (int)alphaLineBuf1[xSrc0] + ((SplashCoord)1 - xs) * (int)alphaLineBuf1[xSrc1]); } } ++yBuf; } // do the vertical interpolation for (x = 0; x < scaledWidth; ++x) { for (i = 0; i < nComps; ++i) { pix[i] = (Guchar)(int) (ys * (int)lineBuf0[x*nComps+i] + ((SplashCoord)1 - ys) * (int)lineBuf1[x*nComps+i]); } // store the pixel switch (srcMode) { case splashModeMono8: *destPtr++ = pix[0]; break; case splashModeRGB8: *destPtr++ = pix[0]; *destPtr++ = pix[1]; *destPtr++ = pix[2]; break; #if SPLASH_CMYK case splashModeCMYK8: *destPtr++ = pix[0]; *destPtr++ = pix[1]; *destPtr++ = pix[2]; *destPtr++ = pix[3]; break; #endif case splashModeMono1: // mono1 is not allowed case splashModeBGR8: // BGR8 is not allowed default: break; } // process alpha if (srcAlpha) { *destAlphaPtr++ = (Guchar)(int) (ys * (int)alphaLineBuf0[x] + ((SplashCoord)1 - ys) * (int)alphaLineBuf1[x]); } } } gfree(alphaLineBuf1); gfree(alphaLineBuf0); gfree(lineBuf1); gfree(lineBuf0); } void Splash::vertFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; Guchar *p0, *p1; int w; w = width * nComps; lineBuf = (Guchar *)gmalloc(w); for (p0 = img->data, p1 = img->data + (height - 1) * (size_t)w; p0 < p1; p0 += w, p1 -= w) { memcpy(lineBuf, p0, w); memcpy(p0, p1, w); memcpy(p1, lineBuf, w); } if (img->alpha) { for (p0 = img->alpha, p1 = img->alpha + (height - 1) * (size_t)width; p0 < p1; p0 += width, p1 -= width) { memcpy(lineBuf, p0, width); memcpy(p0, p1, width); memcpy(p1, lineBuf, width); } } gfree(lineBuf); } void Splash::horizFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; SplashColorPtr p0, p1, p2; int w, x, y, i; w = width * nComps; lineBuf = (Guchar *)gmalloc(w); for (y = 0, p0 = img->data; y < height; ++y, p0 += img->rowSize) { memcpy(lineBuf, p0, w); p1 = p0; p2 = lineBuf + (w - nComps); for (x = 0; x < width; ++x) { for (i = 0; i < nComps; ++i) { p1[i] = p2[i]; } p1 += nComps; p2 -= nComps; } } if (img->alpha) { for (y = 0, p0 = img->alpha; y < height; ++y, p0 += width) { memcpy(lineBuf, p0, width); p1 = p0; p2 = lineBuf + (width - 1); for (x = 0; x < width; ++x) { *p1++ = *p2--; } } } gfree(lineBuf); } void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; int w, h, x0, y0, x1, y1, y; // split the image into clipped and unclipped regions w = src->width; h = src->height; if (clipRes == splashClipAllInside) { x0 = 0; y0 = 0; x1 = w; y1 = h; } else { if (state->clip->getNumPaths()) { x0 = x1 = w; y0 = y1 = h; } else { if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) { x0 = 0; } if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) { y0 = 0; } if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) { x1 = w; } if (x1 < x0) { x1 = x0; } if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) { y1 = h; } if (y1 < y0) { y1 = y0; } } } // draw the unclipped region if (x0 < w && y0 < h && x0 < x1 && y0 < y1) { pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse); if (srcAlpha) { for (y = y0; y < y1; ++y) { (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, src->alpha + y * src->alphaRowSize + x0, src->data + y * src->rowSize + x0 * bitmapComps); } } else { for (y = y0; y < y1; ++y) { (this->*pipe.run)(&pipe, xDest + x0, xDest + x1 - 1, yDest + y, NULL, src->data + y * src->getRowSize() + x0 * bitmapComps); } } } // draw the clipped regions if (y0 > 0) { blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0); } if (y1 < h) { blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1); } if (x0 > 0 && y0 < y1) { blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0); } if (x1 < w && y0 < y1) { blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0, w - x1, y1 - y0); } } void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashPipe pipe; int y; if (xDest < 0) { xSrc -= xDest; w += xDest; xDest = 0; } if (xDest + w > bitmap->width) { w = bitmap->width - xDest; } if (yDest < 0) { ySrc -= yDest; h += yDest; yDest = 0; } if (yDest + h > bitmap->height) { h = bitmap->height - yDest; } if (w <= 0 || h <= 0) { return; } pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse); if (srcAlpha) { for (y = 0; y < h; ++y) { memcpy(scanBuf + xDest, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, w); if (vectorAntialias) { state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, scanBuf + xDest, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } else { for (y = 0; y < h; ++y) { memset(scanBuf + xDest, 0xff, w); if (vectorAntialias) { state->clip->clipSpan(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } else { state->clip->clipSpanBinary(scanBuf, yDest + y, xDest, xDest + w - 1, state->strokeAdjust); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, scanBuf + xDest, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } } SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, GBool noClip, GBool nonIsolated) { SplashPipe pipe; Guchar *mono1Ptr, *lineBuf, *linePtr; Guchar mono1Mask, b; int x0, x1, x, y0, y1, y, t; if (!(src->mode == bitmap->mode || (src->mode == splashModeMono8 && bitmap->mode == splashModeMono1) || (src->mode == splashModeRGB8 && bitmap->mode == splashModeBGR8))) { return splashErrModeMismatch; } pipeInit(&pipe, NULL, (Guchar)splashRound(state->fillAlpha * 255), !noClip || src->alpha != NULL, nonIsolated); if (src->mode == splashModeMono1) { // in mono1 mode, pipeRun expects the source to be in mono8 // format, so we need to extract the source color values into // scanBuf, expanding them from mono1 to mono8 if (noClip) { if (src->alpha) { for (y = 0; y < h; ++y) { mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, scanBuf); } } else { for (y = 0; y < h; ++y) { mono1Ptr = src->data + (ySrc + y) * src->rowSize + (xSrc >> 3); mono1Mask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, NULL, scanBuf); } } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { if (src->alpha) { for (y = y0; y < y1; ++y) { mono1Ptr = src->data + (ySrc + y - yDest) * src->rowSize + ((xSrc + x0 - xDest) >> 3); mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); for (x = x0; x < x1; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } memcpy(scanBuf2 + x0, src->alpha + (ySrc + y - yDest) * src->alphaRowSize + (xSrc + x0 - xDest), x1 - x0); if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, state->strokeAdjust)) { continue; } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf2 + x0, scanBuf + x0); } } else { for (y = y0; y < y1; ++y) { mono1Ptr = src->data + (ySrc + y - yDest) * src->rowSize + ((xSrc + x0 - xDest) >> 3); mono1Mask = (Guchar)(0x80 >> ((xSrc + x0 - xDest) & 7)); for (x = x0; x < x1; ++x) { scanBuf[x] = (*mono1Ptr & mono1Mask) ? 0xff : 0x00; mono1Ptr += mono1Mask & 1; mono1Mask = (Guchar)((mono1Mask << 7) | (mono1Mask >> 1)); } memset(scanBuf2 + x0, 0xff, x1 - x0); if (!state->clip->clipSpanBinary(scanBuf2, y, x0, x1 - 1, state->strokeAdjust)) { continue; } (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf2 + x0, scanBuf + x0); } } } } } else if (src->mode == splashModeBGR8) { // in BGR8 mode, pipeRun expects the source to be in RGB8 format, // so we need to swap bytes lineBuf = (Guchar *)gmallocn(w, 3); if (noClip) { if (src->alpha) { for (y = 0; y < h; ++y) { memcpy(lineBuf, src->data + (ySrc + y) * src->rowSize + xSrc * 3, w * 3); for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, lineBuf); } } else { for (y = 0; y < h; ++y) { memcpy(lineBuf, src->data + (ySrc + y) * src->rowSize + xSrc * 3, w * 3); for (x = 0, linePtr = lineBuf; x < w; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, NULL, lineBuf); } } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { if (src->alpha) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->alpha + (ySrc + y - yDest) * src->alphaRowSize + (xSrc + x0 - xDest), x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); memcpy(lineBuf, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * 3, (x1 - x0) * 3); for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, lineBuf); } } else { for (y = y0; y < y1; ++y) { memset(scanBuf + x0, 0xff, x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); memcpy(lineBuf, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * 3, (x1 - x0) * 3); for (x = 0, linePtr = lineBuf; x < x1 - x0; ++x, linePtr += 3) { b = linePtr[0]; linePtr[0] = linePtr[2]; linePtr[2] = b; } (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, scanBuf + x0, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * bitmapComps); } } } } gfree(lineBuf); } else { // src->mode not mono1 or BGR8 if (noClip) { if (src->alpha) { for (y = 0; y < h; ++y) { // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, src->alpha + (ySrc + y) * src->alphaRowSize + xSrc, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } else { for (y = 0; y < h; ++y) { (this->*pipe.run)(&pipe, xDest, xDest + w - 1, yDest + y, NULL, src->data + (ySrc + y) * src->rowSize + xSrc * bitmapComps); } } } else { x0 = xDest; if ((t = state->clip->getXMinI(state->strokeAdjust)) > x0) { x0 = t; } x1 = xDest + w; if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < x1) { x1 = t; } y0 = yDest; if ((t = state->clip->getYMinI(state->strokeAdjust)) > y0) { y0 = t; } y1 = yDest + h; if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < y1) { y1 = t; } if (x0 < x1 && y0 < y1) { if (src->alpha) { for (y = y0; y < y1; ++y) { memcpy(scanBuf + x0, src->alpha + (ySrc + y - yDest) * src->alphaRowSize + (xSrc + x0 - xDest), x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); // this uses shape instead of alpha, which isn't technically // correct, but works out the same (this->*pipe.run)(&pipe, x0, x1 - 1, y, scanBuf + x0, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * bitmapComps); } } else { for (y = y0; y < y1; ++y) { memset(scanBuf + x0, 0xff, x1 - x0); state->clip->clipSpan(scanBuf, y, x0, x1 - 1, state->strokeAdjust); (this->*pipe.run)(&pipe, x0, x1 - 1, yDest + y, scanBuf + x0, src->data + (ySrc + y - yDest) * src->rowSize + (xSrc + x0 - xDest) * bitmapComps); } } } } } return splashOk; } void Splash::compositeBackground(SplashColorPtr color) { SplashColorPtr p; Guchar *q; Guchar alpha, alpha1, c, color0, color1, color2, mask; #if SPLASH_CMYK Guchar color3; #endif int x, y; switch (bitmap->mode) { case splashModeMono1: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; mask = 0x80; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { if (color0 & 0x80) { *p |= mask; } else { *p &= (Guchar)~mask; } } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); c = (*p & mask) ? 0xff : 0x00; c = div255(alpha1 * color0 + alpha * c); if (c & 0x80) { *p |= mask; } else { *p &= (Guchar)~mask; } } if (!(mask = (Guchar)(mask >> 1))) { mask = 0x80; ++p; } } } break; case splashModeMono8: color0 = color[0]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); p[0] = div255(alpha1 * color0 + alpha * p[0]); } ++p; } } break; case splashModeRGB8: case splashModeBGR8: color0 = color[0]; color1 = color[1]; color2 = color[2]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; p[1] = color1; p[2] = color2; } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); } p += 3; } } break; #if SPLASH_CMYK case splashModeCMYK8: color0 = color[0]; color1 = color[1]; color2 = color[2]; color3 = color[3]; for (y = 0; y < bitmap->height; ++y) { p = &bitmap->data[y * bitmap->rowSize]; q = &bitmap->alpha[y * bitmap->alphaRowSize]; for (x = 0; x < bitmap->width; ++x) { alpha = *q++; if (alpha == 0) { p[0] = color0; p[1] = color1; p[2] = color2; p[3] = color3; } else if (alpha != 255) { alpha1 = (Guchar)(255 - alpha); p[0] = div255(alpha1 * color0 + alpha * p[0]); p[1] = div255(alpha1 * color1 + alpha * p[1]); p[2] = div255(alpha1 * color2 + alpha * p[2]); p[3] = div255(alpha1 * color3 + alpha * p[3]); } p += 4; } } break; #endif } memset(bitmap->alpha, 255, bitmap->alphaRowSize * bitmap->height); } SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColorPtr p, q; Guchar mask, srcMask; int x, y; if (src->mode != bitmap->mode) { return splashErrModeMismatch; } switch (bitmap->mode) { case splashModeMono1: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)]; mask = (Guchar)(0x80 >> (xDest & 7)); q = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)]; srcMask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { if (*q & srcMask) { *p |= mask; } else { *p &= (Guchar)~mask; } if (!(mask = (Guchar)(mask >> 1))) { mask = 0x80; ++p; } if (!(srcMask = (Guchar)(srcMask >> 1))) { srcMask = 0x80; ++q; } } } break; case splashModeMono8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; q = &src->data[(ySrc + y) * src->rowSize + xSrc]; memcpy(p, q, w); } break; case splashModeRGB8: case splashModeBGR8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc]; memcpy(p, q, 3 * w); } break; #if SPLASH_CMYK case splashModeCMYK8: for (y = 0; y < h; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest]; q = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc]; memcpy(p, q, 4 * w); } break; #endif } if (bitmap->alpha) { for (y = 0; y < h; ++y) { q = &bitmap->alpha[(yDest + y) * bitmap->alphaRowSize + xDest]; memset(q, 0, w); } } return splashOk; } SplashError Splash::blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, int xDest, int yDest, int w, int h) { SplashColorPtr p, q; Guchar *alpha0Ptr; Guchar alpha0, aSrc, mask, srcMask; int x, y; if (bitmap->mode != dest->mode || !bitmap->alpha || !dest->alpha || !groupBackBitmap) { return splashErrModeMismatch; } switch (bitmap->mode) { case splashModeMono1: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + (xDest >> 3)]; mask = (Guchar)(0x80 >> (xDest & 7)); q = &bitmap->data[(ySrc + y) * bitmap->rowSize + (xSrc >> 3)]; srcMask = (Guchar)(0x80 >> (xSrc & 7)); for (x = 0; x < w; ++x) { if (*q & srcMask) { *p |= mask; } else { *p &= (Guchar)~mask; } if (!(mask = (Guchar)(mask >> 1))) { mask = 0x80; ++p; } if (!(srcMask = (Guchar)(srcMask >> 1))) { srcMask = 0x80; ++q; } } } break; case splashModeMono8: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + xDest]; q = &bitmap->data[(ySrc + y) * bitmap->rowSize + xSrc]; memcpy(p, q, w); } break; case splashModeRGB8: case splashModeBGR8: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + 3 * xDest]; q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 3 * xSrc]; memcpy(p, q, 3 * w); } break; #if SPLASH_CMYK case splashModeCMYK8: for (y = 0; y < h; ++y) { p = &dest->data[(yDest + y) * dest->rowSize + 4 * xDest]; q = &bitmap->data[(ySrc + y) * bitmap->rowSize + 4 * xSrc]; memcpy(p, q, 4 * w); } break; #endif } for (y = 0; y < h; ++y) { p = &dest->alpha[(yDest + y) * dest->alphaRowSize + xDest]; q = &bitmap->alpha[(ySrc + y) * bitmap->alphaRowSize + xSrc]; alpha0Ptr = &groupBackBitmap->alpha[(groupBackY + ySrc + y) * groupBackBitmap->alphaRowSize + (groupBackX + xSrc)]; for (x = 0; x < w; ++x) { alpha0 = *alpha0Ptr++; aSrc = *q++; *p++ = (Guchar)(alpha0 + aSrc - div255(alpha0 * aSrc)); } } return splashOk; } SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, int lineCap, int lineJoin, GBool flatten) { SplashPath *pathIn, *dashPath, *pathOut; SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext; SplashCoord crossprod, dotprod, miter, m; SplashCoord angle, angleNext, dAngle, xc, yc; SplashCoord dxJoin, dyJoin, dJoin, kappa; SplashCoord cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4; GBool first, last, closed; int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1; int left0, left1, left2, right0, right1, right2, join0, join1, join2; int leftFirst, rightFirst, firstPt; pathOut = new SplashPath(); if (path->length == 0) { return pathOut; } if (flatten) { pathIn = flattenPath(path, state->matrix, state->flatness); if (state->lineDashLength > 0) { dashPath = makeDashedPath(pathIn); delete pathIn; pathIn = dashPath; if (pathIn->length == 0) { delete pathIn; return pathOut; } } } else { pathIn = path; } subpathStart0 = subpathStart1 = 0; // make gcc happy seg = 0; // make gcc happy closed = gFalse; // make gcc happy left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy leftFirst = rightFirst = firstPt = 0; // make gcc happy i0 = 0; for (i1 = i0; !(pathIn->flags[i1] & splashPathLast) && i1 + 1 < pathIn->length && pathIn->pts[i1+1].x == pathIn->pts[i1].x && pathIn->pts[i1+1].y == pathIn->pts[i1].y; ++i1) ; while (i1 < pathIn->length) { if ((first = pathIn->flags[i0] & splashPathFirst)) { subpathStart0 = i0; subpathStart1 = i1; seg = 0; closed = pathIn->flags[i0] & splashPathClosed; } j0 = i1 + 1; if (j0 < pathIn->length) { for (j1 = j0; !(pathIn->flags[j1] & splashPathLast) && j1 + 1 < pathIn->length && pathIn->pts[j1+1].x == pathIn->pts[j1].x && pathIn->pts[j1+1].y == pathIn->pts[j1].y; ++j1) ; } else { j1 = j0; } if (pathIn->flags[i1] & splashPathLast) { if (first && lineCap == splashLineCapRound) { // special case: zero-length subpath with round line caps --> // draw a circle pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y + (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y + (SplashCoord)0.5 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y + bezierCircle2 * w, pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x - bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x, pathIn->pts[i0].y - (SplashCoord)0.5 * w); pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w, pathIn->pts[i0].y - (SplashCoord)0.5 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y - bezierCircle2 * w, pathIn->pts[i0].x + (SplashCoord)0.5 * w, pathIn->pts[i0].y); pathOut->close(); } i0 = j0; i1 = j1; continue; } last = pathIn->flags[j1] & splashPathLast; if (last) { k0 = subpathStart1 + 1; } else { k0 = j1 + 1; } for (k1 = k0; !(pathIn->flags[k1] & splashPathLast) && k1 + 1 < pathIn->length && pathIn->pts[k1+1].x == pathIn->pts[k1].x && pathIn->pts[k1+1].y == pathIn->pts[k1].y; ++k1) ; // compute the deltas for segment (i1, j0) #if USE_FIXEDPOINT // the 1/d value can be small, which introduces significant // inaccuracies in fixed point mode d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, pathIn->pts[j0].x, pathIn->pts[j0].y); dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d; dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d; #else d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y, pathIn->pts[j0].x, pathIn->pts[j0].y); dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x); dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y); #endif wdx = (SplashCoord)0.5 * w * dx; wdy = (SplashCoord)0.5 * w * dy; // draw the start cap if (i0 == subpathStart0) { firstPt = pathOut->length; } if (first && !closed) { switch (lineCap) { case splashLineCapButt: pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapRound: pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx, pathIn->pts[i0].y + wdx - bezierCircle * wdy, pathIn->pts[i0].x - wdx - bezierCircle * wdy, pathIn->pts[i0].y - wdy + bezierCircle * wdx, pathIn->pts[i0].x - wdx, pathIn->pts[i0].y - wdy); pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy, pathIn->pts[i0].y - wdy - bezierCircle * wdx, pathIn->pts[i0].x + wdy - bezierCircle * wdx, pathIn->pts[i0].y - wdx - bezierCircle * wdy, pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); break; case splashLineCapProjecting: pathOut->moveTo(pathIn->pts[i0].x - wdx - wdy, pathIn->pts[i0].y + wdx - wdy); pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy, pathIn->pts[i0].y - wdx - wdy); break; } } else { pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx); pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx); } // draw the left side of the segment rectangle and the end cap left2 = pathOut->length - 1; if (last && !closed) { switch (lineCap) { case splashLineCapButt: pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapRound: pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx, pathIn->pts[j0].y - wdx + bezierCircle * wdy, pathIn->pts[j0].x + wdx + bezierCircle * wdy, pathIn->pts[j0].y + wdy - bezierCircle * wdx, pathIn->pts[j0].x + wdx, pathIn->pts[j0].y + wdy); pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy, pathIn->pts[j0].y + wdy + bezierCircle * wdx, pathIn->pts[j0].x - wdy + bezierCircle * wdx, pathIn->pts[j0].y + wdx + bezierCircle * wdy, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); break; case splashLineCapProjecting: pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx, pathIn->pts[j0].y - wdx + wdy); pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx, pathIn->pts[j0].y + wdx + wdy); break; } } else { pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // draw the right side of the segment rectangle // (NB: if stroke adjustment is enabled, the closepath operation MUST // add a segment because this segment is used for a hint) right2 = pathOut->length - 1; pathOut->close(state->strokeAdjust != splashStrokeAdjustOff); // draw the join join2 = pathOut->length; if (!last || closed) { // compute the deltas for segment (j1, k0) #if USE_FIXEDPOINT // the 1/d value can be small, which introduces significant // inaccuracies in fixed point mode d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, pathIn->pts[k0].x, pathIn->pts[k0].y); dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d; dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d; #else d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y, pathIn->pts[k0].x, pathIn->pts[k0].y); dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x); dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y); #endif wdxNext = (SplashCoord)0.5 * w * dxNext; wdyNext = (SplashCoord)0.5 * w * dyNext; // compute the join parameters crossprod = dx * dyNext - dy * dxNext; dotprod = -(dx * dxNext + dy * dyNext); if (dotprod > 0.9999) { // avoid a divide-by-zero -- set miter to something arbitrary // such that sqrt(miter) will exceed miterLimit (and m is never // used in that situation) // (note: the comparison value (0.9999) has to be less than // 1-epsilon, where epsilon is the smallest value // representable in the fixed point format) miter = (state->miterLimit + 1) * (state->miterLimit + 1); m = 0; } else { miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod); if (miter < 1) { // this can happen because of floating point inaccuracies miter = 1; } m = splashSqrt(miter - 1); } // round join if (lineJoin == splashLineJoinRound) { // join angle < 180 if (crossprod < 0) { angle = atan2((double)dx, (double)-dy); angleNext = atan2((double)dxNext, (double)-dyNext); if (angle < angleNext) { angle += 2 * M_PI; } dAngle = (angle - angleNext) / M_PI; if (dAngle < 0.501) { // span angle is <= 90 degrees -> draw a single arc kappa = dAngle * bezierCircle * w; cx1 = pathIn->pts[j0].x - wdy + kappa * dx; cy1 = pathIn->pts[j0].y + wdx + kappa * dy; cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); pathOut->curveTo(cx2, cy2, cx1, cy1, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } else { // span angle is > 90 degrees -> split into two arcs dJoin = splashDist(-wdy, wdx, -wdyNext, wdxNext); if (dJoin > 0) { dxJoin = (-wdyNext + wdy) / dJoin; dyJoin = (wdxNext - wdx) / dJoin; xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos((double)((SplashCoord)0.5 * (angle + angleNext))); yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin((double)((SplashCoord)0.5 * (angle + angleNext))); kappa = dAngle * bezierCircle2 * w; cx1 = pathIn->pts[j0].x - wdy + kappa * dx; cy1 = pathIn->pts[j0].y + wdx + kappa * dy; cx2 = xc - kappa * dxJoin; cy2 = yc - kappa * dyJoin; cx3 = xc + kappa * dxJoin; cy3 = yc + kappa * dyJoin; cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext; cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); pathOut->curveTo(cx4, cy4, cx3, cy3, xc, yc); pathOut->curveTo(cx2, cy2, cx1, cy1, pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } } // join angle >= 180 } else { angle = atan2((double)-dx, (double)dy); angleNext = atan2((double)-dxNext, (double)dyNext); if (angleNext < angle) { angleNext += 2 * M_PI; } dAngle = (angleNext - angle) / M_PI; if (dAngle < 0.501) { // span angle is <= 90 degrees -> draw a single arc kappa = dAngle * bezierCircle * w; cx1 = pathIn->pts[j0].x + wdy + kappa * dx; cy1 = pathIn->pts[j0].y - wdx + kappa * dy; cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(cx1, cy1, cx2, cy2, pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } else { // span angle is > 90 degrees -> split into two arcs dJoin = splashDist(wdy, -wdx, wdyNext, -wdxNext); if (dJoin > 0) { dxJoin = (wdyNext - wdy) / dJoin; dyJoin = (-wdxNext + wdx) / dJoin; xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos((double)((SplashCoord)0.5 * (angle + angleNext))); yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin((double)((SplashCoord)0.5 * (angle + angleNext))); kappa = dAngle * bezierCircle2 * w; cx1 = pathIn->pts[j0].x + wdy + kappa * dx; cy1 = pathIn->pts[j0].y - wdx + kappa * dy; cx2 = xc - kappa * dxJoin; cy2 = yc - kappa * dyJoin; cx3 = xc + kappa * dxJoin; cy3 = yc + kappa * dyJoin; cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext; cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext; pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); pathOut->curveTo(cx1, cy1, cx2, cy2, xc, yc); pathOut->curveTo(cx3, cy3, cx4, cy4, pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } } } } else { pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y); // join angle < 180 if (crossprod < 0) { pathOut->lineTo(pathIn->pts[j0].x - wdyNext, pathIn->pts[j0].y + wdxNext); // miter join inside limit if (lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m, pathIn->pts[j0].y + wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx); } // join angle >= 180 } else { pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx); // miter join inside limit if (lineJoin == splashLineJoinMiter && splashSqrt(miter) <= state->miterLimit) { pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m, pathIn->pts[j0].y - wdx + wdy * m); pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); // bevel join or miter join outside limit } else { pathOut->lineTo(pathIn->pts[j0].x + wdyNext, pathIn->pts[j0].y - wdxNext); } } } pathOut->close(); } // add stroke adjustment hints if (state->strokeAdjust != splashStrokeAdjustOff) { // subpath with one segment if (seg == 0 && last) { switch (lineCap) { case splashLineCapButt: pathOut->addStrokeAdjustHint(firstPt, left2 + 1, firstPt, pathOut->length - 1); break; case splashLineCapProjecting: pathOut->addStrokeAdjustHint(firstPt, left2 + 1, firstPt, pathOut->length - 1, gTrue); break; case splashLineCapRound: break; } pathOut->addStrokeAdjustHint(left2, right2, firstPt, pathOut->length - 1); } else { // start of subpath if (seg == 1) { // start cap if (!closed) { switch (lineCap) { case splashLineCapButt: pathOut->addStrokeAdjustHint(firstPt, left1 + 1, firstPt, firstPt + 1); pathOut->addStrokeAdjustHint(firstPt, left1 + 1, right1 + 1, right1 + 1); break; case splashLineCapProjecting: pathOut->addStrokeAdjustHint(firstPt, left1 + 1, firstPt, firstPt + 1, gTrue); pathOut->addStrokeAdjustHint(firstPt, left1 + 1, right1 + 1, right1 + 1, gTrue); break; case splashLineCapRound: break; } } // first segment pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2); pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); } // middle of subpath if (seg > 1) { pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0); pathOut->addStrokeAdjustHint(left1, right1, join0, left2); pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1); } // end of subpath if (last) { if (closed) { // first segment pathOut->addStrokeAdjustHint(leftFirst, rightFirst, left2 + 1, right2); pathOut->addStrokeAdjustHint(leftFirst, rightFirst, join2, pathOut->length - 1); // last segment pathOut->addStrokeAdjustHint(left2, right2, left1 + 1, right1); pathOut->addStrokeAdjustHint(left2, right2, join1, pathOut->length - 1); pathOut->addStrokeAdjustHint(left2, right2, leftFirst - 1, leftFirst); pathOut->addStrokeAdjustHint(left2, right2, rightFirst + 1, rightFirst + 1); } else { // last segment pathOut->addStrokeAdjustHint(left2, right2, left1 + 1, right1); pathOut->addStrokeAdjustHint(left2, right2, join1, pathOut->length - 1); // end cap switch (lineCap) { case splashLineCapButt: pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, left2 + 1, left2 + 2); break; case splashLineCapProjecting: pathOut->addStrokeAdjustHint(left2 - 1, left2 + 1, left2 + 1, left2 + 2, gTrue); break; case splashLineCapRound: break; } } } } left0 = left1; left1 = left2; right0 = right1; right1 = right2; join0 = join1; join1 = join2; if (seg == 0) { leftFirst = left2; rightFirst = right2; } } i0 = j0; i1 = j1; ++seg; } if (pathIn != path) { delete pathIn; } return pathOut; } SplashClipResult Splash::limitRectToClipRect(int *xMin, int *yMin, int *xMax, int *yMax) { int t; if ((t = state->clip->getXMinI(state->strokeAdjust)) > *xMin) { *xMin = t; } if ((t = state->clip->getXMaxI(state->strokeAdjust) + 1) < *xMax) { *xMax = t; } if ((t = state->clip->getYMinI(state->strokeAdjust)) > *yMin) { *yMin = t; } if ((t = state->clip->getYMaxI(state->strokeAdjust) + 1) < *yMax) { *yMax = t; } if (*xMin >= *xMax || *yMin >= *yMax) { return splashClipAllOutside; } return state->clip->testRect(*xMin, *yMin, *xMax - 1, *yMax - 1, state->strokeAdjust); } void Splash::dumpPath(SplashPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n", i, (double)path->pts[i].x, (double)path->pts[i].y, (path->flags[i] & splashPathFirst) ? " first" : "", (path->flags[i] & splashPathLast) ? " last" : "", (path->flags[i] & splashPathClosed) ? " closed" : "", (path->flags[i] & splashPathCurve) ? " curve" : ""); } if (path->hintsLength == 0) { printf(" no hints\n"); } else { for (i = 0; i < path->hintsLength; ++i) { printf(" hint %3d: ctrl0=%d ctrl1=%d pts=%d..%d\n", i, path->hints[i].ctrl0, path->hints[i].ctrl1, path->hints[i].firstPt, path->hints[i].lastPt); } } } void Splash::dumpXPath(SplashXPath *path) { int i; for (i = 0; i < path->length; ++i) { printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f count=%d\n", i, (double)path->segs[i].x0, (double)path->segs[i].y0, (double)path->segs[i].x1, (double)path->segs[i].y1, path->segs[i].count); } } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10024/Splash.h000066400000000000000000000451351417746362400222300ustar00rootroot00000000000000//======================================================================== // // Splash.h // // Copyright 2003-2013 Glyph & Cog, LLC // //======================================================================== #ifndef SPLASH_H #define SPLASH_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "SplashTypes.h" #include "SplashClip.h" class Splash; class SplashBitmap; struct SplashGlyphBitmap; class SplashState; class SplashPattern; class SplashScreen; class SplashPath; class SplashXPath; class SplashFont; struct SplashPipe; //------------------------------------------------------------------------ // Retrieves the next line of pixels in an image mask. Normally, // fills in * and returns true. If the image stream is // exhausted, returns false. typedef GBool (*SplashImageMaskSource)(void *data, Guchar *pixel); // Retrieves the next line of pixels in an image. Normally, fills in // * and returns true. If the image stream is exhausted, // returns false. typedef GBool (*SplashImageSource)(void *data, SplashColorPtr colorLine, Guchar *alphaLine); //------------------------------------------------------------------------ enum SplashPipeResultColorCtrl { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, #if SPLASH_CMYK splashPipeResultColorNoAlphaBlendCMYK, #endif splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, #if SPLASH_CMYK splashPipeResultColorAlphaNoBlendCMYK, #endif splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB #if SPLASH_CMYK , splashPipeResultColorAlphaBlendCMYK #endif }; //------------------------------------------------------------------------ // Splash //------------------------------------------------------------------------ class Splash { public: // Create a new rasterizer object. Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreenParams *screenParams = NULL); Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA, SplashScreen *screenA); ~Splash(); //----- state read SplashCoord *getMatrix(); SplashPattern *getStrokePattern(); SplashPattern *getFillPattern(); SplashScreen *getScreen(); SplashBlendFunc getBlendFunc(); SplashCoord getStrokeAlpha(); SplashCoord getFillAlpha(); SplashCoord getLineWidth(); int getLineCap(); int getLineJoin(); SplashCoord getMiterLimit(); SplashCoord getFlatness(); SplashCoord *getLineDash(); int getLineDashLength(); SplashCoord getLineDashPhase(); SplashStrokeAdjustMode getStrokeAdjust(); SplashClip *getClip(); SplashBitmap *getSoftMask(); GBool getInNonIsolatedGroup(); GBool getInKnockoutGroup(); //----- state write void setMatrix(SplashCoord *matrix); void setStrokePattern(SplashPattern *strokeColor); void setFillPattern(SplashPattern *fillColor); void setScreen(SplashScreen *screen); void setBlendFunc(SplashBlendFunc func); void setStrokeAlpha(SplashCoord alpha); void setFillAlpha(SplashCoord alpha); void setLineWidth(SplashCoord lineWidth); void setLineCap(int lineCap); void setLineJoin(int lineJoin); void setMiterLimit(SplashCoord miterLimit); void setFlatness(SplashCoord flatness); // the array will be copied void setLineDash(SplashCoord *lineDash, int lineDashLength, SplashCoord lineDashPhase); void setStrokeAdjust(SplashStrokeAdjustMode strokeAdjust); // NB: uses transformed coordinates. void clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses transformed coordinates. SplashError clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1); // NB: uses untransformed coordinates. SplashError clipToPath(SplashPath *path, GBool eo); void setSoftMask(SplashBitmap *softMask); void setInTransparencyGroup(SplashBitmap *groupBackBitmapA, int groupBackXA, int groupBackYA, GBool nonIsolated, GBool knockout); void setTransfer(Guchar *red, Guchar *green, Guchar *blue, Guchar *gray); void setOverprintMask(Guint overprintMask); void setEnablePathSimplification(GBool en); //----- state save/restore void saveState(); SplashError restoreState(); //----- drawing operations // Fill the bitmap with . This is not subject to clipping. void clear(SplashColorPtr color, Guchar alpha = 0x00); // Stroke a path using the current stroke pattern. SplashError stroke(SplashPath *path); // Fill a path using the current fill pattern. SplashError fill(SplashPath *path, GBool eo); // Draw a character, using the current fill pattern. SplashError fillChar(SplashCoord x, SplashCoord y, int c, SplashFont *font); // Draw a glyph, using the current fill pattern. This function does // not free any data, i.e., it ignores glyph->freeData. SplashError fillGlyph(SplashCoord x, SplashCoord y, SplashGlyphBitmap *glyph); // Draws an image mask using the fill color. This will read // lines of pixels from , starting with the top line. "1" // pixels will be drawn with the current fill color; "0" pixels are // transparent. The matrix: // [ mat[0] mat[1] 0 ] // [ mat[2] mat[3] 0 ] // [ mat[4] mat[5] 1 ] // maps a unit square to the desired destination for the image, in // PostScript style: // [x' y' 1] = [x y 1] * mat // Note that the Splash y axis points downward, and the image source // is assumed to produce pixels in raster order, starting from the // top line. SplashError fillImageMask(SplashImageMaskSource src, void *srcData, int w, int h, SplashCoord *mat, GBool glyphMode, GBool interpolate); // Draw an image. This will read lines of pixels from // , starting with the top line. These pixels are assumed to // be in the source mode, . If is true, the // alpha values returned by are used; otherwise they are // ignored. The following combinations of source and target modes // are supported: // source target // ------ ------ // Mono8 Mono1 -- with dithering // Mono8 Mono8 // RGB8 RGB8 // BGR8 RGB8 // CMYK8 CMYK8 // The matrix behaves as for fillImageMask. SplashError drawImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, GBool srcAlpha, int w, int h, SplashCoord *mat, GBool interpolate); // Composite a rectangular region from onto this Splash // object. SplashError composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, GBool noClip, GBool nonIsolated); // Composite this Splash object onto a background color. The // background alpha is assumed to be 1. void compositeBackground(SplashColorPtr color); // Copy a rectangular region from onto the bitmap belonging to // this Splash object. The destination alpha values are all set to // zero. SplashError blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h); // Copy a rectangular region from the bitmap belonging to this // Splash object to . The alpha values are corrected for a // non-isolated group. SplashError blitCorrectedAlpha(SplashBitmap *dest, int xSrc, int ySrc, int xDest, int yDest, int w, int h); //----- misc // Construct a path for a stroke, given the path to be stroked and // the line width . All other stroke parameters are taken from // the current state. If is true, this function will // first flatten the path and handle the linedash. SplashPath *makeStrokePath(SplashPath *path, SplashCoord w, int lineCap, int lineJoin, GBool flatten = gTrue); // Reduce the size of a rectangle as much as possible by moving any // edges that are completely outside the clip region. Returns the // clipping status of the resulting rectangle. SplashClipResult limitRectToClipRect(int *xMin, int *yMin, int *xMax, int *yMax); // Return the associated bitmap. SplashBitmap *getBitmap() { return bitmap; } // Set the minimum line width. void setMinLineWidth(SplashCoord w) { minLineWidth = w; } // Get a bounding box which includes all modifications since the // last call to clearModRegion. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) { *xMin = modXMin; *yMin = modYMin; *xMax = modXMax; *yMax = modYMax; } // Clear the modified region bounding box. void clearModRegion(); // Get clipping status for the last drawing operation subject to // clipping. SplashClipResult getClipRes() { return opClipRes; } // Toggle debug mode on or off. void setDebugMode(GBool debugModeA) { debugMode = debugModeA; } #if 1 //~tmp: turn off anti-aliasing temporarily void setInShading(GBool sh) { inShading = sh; } #endif private: void pipeInit(SplashPipe *pipe, SplashPattern *pattern, Guchar aInput, GBool usesShape, GBool nonIsolatedGroup); void pipeRun(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunSimpleBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunSimpleCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void pipeRunShapeMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeRGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunShapeBGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunShapeCMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void pipeRunAAMono1(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAAMono8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAARGB8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); void pipeRunAABGR8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #if SPLASH_CMYK void pipeRunAACMYK8(SplashPipe *pipe, int x0, int x1, int y, Guchar *shapePtr, SplashColorPtr cSrcPtr); #endif void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void updateModX(int x); void updateModY(int y); void strokeNarrow(SplashPath *path); void drawStrokeSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip); void strokeWide(SplashPath *path, SplashCoord w, int lineCap, int lineJoin); SplashPath *flattenPath(SplashPath *path, SplashCoord *matrix, SplashCoord flatness); void flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath); SplashPath *makeDashedPath(SplashPath *xPath); SplashError fillWithPattern(SplashPath *path, GBool eo, SplashPattern *pattern, SplashCoord alpha); SplashPath *tweakFillPath(SplashPath *path); GBool pathAllOutside(SplashPath *path); SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph); void getImageBounds(SplashCoord xyMin, SplashCoord xyMax, int *xyMinI, int *xyMaxI); void upscaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate); void arbitraryTransformMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, SplashCoord *mat, GBool glyphMode, GBool interpolate); SplashBitmap *scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate); void scaleMaskYdXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYdXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXd(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXu(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleMaskYuXuI(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes); void upscaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate); void arbitraryTransformImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, GBool interpolate); SplashBitmap *scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, GBool interpolate); void scaleImageYdXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYdXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXd(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXu(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void scaleImageYuXuI(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); void vertFlipImage(SplashBitmap *img, int width, int height, int nComps); void horizFlipImage(SplashBitmap *img, int width, int height, int nComps); void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, SplashClipResult clipRes); void blitImageClipped(SplashBitmap *src, GBool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h); void dumpPath(SplashPath *path); void dumpXPath(SplashXPath *path); static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[]; static int pipeNonIsoGroupCorrection[]; SplashBitmap *bitmap; int bitmapComps; SplashState *state; Guchar *scanBuf; Guchar *scanBuf2; SplashBitmap // for transparency groups, this is the bitmap *groupBackBitmap; // containing the alpha0/color0 values int groupBackX, groupBackY; // offset within groupBackBitmap SplashCoord minLineWidth; int modXMin, modYMin, modXMax, modYMax; SplashClipResult opClipRes; GBool vectorAntialias; GBool inShading; GBool debugMode; }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10024/expected.txt000066400000000000000000000001071417746362400231550ustar00rootroot00000000000000Splash.cc:5555:bughuntingDivByZero Splash.cc:5556:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10025/000077500000000000000000000000001417746362400206165ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10025/Stream.cc000066400000000000000000003665671417746362400224070ustar00rootroot00000000000000//======================================================================== // // Stream.cc // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include #include #include "gmem.h" #include "gmempp.h" #include "gfile.h" #if MULTITHREADED #include "GMutex.h" #endif #include "config.h" #include "Error.h" #include "Object.h" #include "Lexer.h" #include "GfxState.h" #include "Stream.h" #include "JBIG2Stream.h" #include "JPXStream.h" #include "Stream-CCITT.h" #ifdef __DJGPP__ static GBool setDJSYSFLAGS = gFalse; #endif #ifdef VMS #ifdef __GNUC__ #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #endif //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ Stream::Stream() { } Stream::~Stream() { } void Stream::close() { } int Stream::getRawChar() { error(errInternal, -1, "Called getRawChar() on non-predictor stream"); return EOF; } int Stream::getBlock(char *buf, int size) { int n, c; n = 0; while (n < size) { if ((c = getChar()) == EOF) { break; } buf[n++] = (char)c; } return n; } char *Stream::getLine(char *buf, int size) { int i; int c; if (lookChar() == EOF || size < 0) return NULL; for (i = 0; i < size - 1; ++i) { c = getChar(); if (c == EOF || c == '\n') break; if (c == '\r') { if ((c = lookChar()) == '\n') getChar(); break; } buf[i] = (char)c; } buf[i] = '\0'; return buf; } Guint Stream::discardChars(Guint n) { char buf[4096]; Guint count, i, j; count = 0; while (count < n) { if ((i = n - count) > sizeof(buf)) { i = (Guint)sizeof(buf); } j = (Guint)getBlock(buf, (int)i); count += j; if (j != i) { break; } } return count; } GString *Stream::getPSFilter(int psLevel, const char *indent) { return new GString(); } Stream *Stream::addFilters(Object *dict, int recursion) { Object obj, obj2; Object params, params2; Stream *str; int i; str = this; dict->dictLookup("Filter", &obj); if (obj.isNull()) { obj.free(); dict->dictLookup("F", &obj); } dict->dictLookup("DecodeParms", ¶ms); if (params.isNull()) { params.free(); dict->dictLookup("DP", ¶ms); } if (obj.isName()) { str = makeFilter(obj.getName(), str, ¶ms, recursion); } else if (obj.isArray()) { for (i = 0; i < obj.arrayGetLength(); ++i) { obj.arrayGet(i, &obj2, recursion); if (params.isArray()) params.arrayGet(i, ¶ms2, recursion); else params2.initNull(); if (obj2.isName()) { str = makeFilter(obj2.getName(), str, ¶ms2, recursion); } else { error(errSyntaxError, getPos(), "Bad filter name"); str = new EOFStream(str); } obj2.free(); params2.free(); } } else if (!obj.isNull()) { error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream"); } obj.free(); params.free(); return str; } Stream *Stream::makeFilter(char *name, Stream *str, Object *params, int recursion) { int pred; // parameters int colors; int bits; int early; int encoding; GBool endOfLine, byteAlign, endOfBlock, black; int columns, rows; int colorXform; Object globals, obj; if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) { str = new ASCIIHexStream(str); } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) { str = new ASCII85Stream(str); } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) { pred = 1; columns = 1; colors = 1; bits = 8; early = 1; if (params->isDict()) { params->dictLookup("Predictor", &obj, recursion); if (obj.isInt()) pred = obj.getInt(); obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) columns = obj.getInt(); obj.free(); params->dictLookup("Colors", &obj, recursion); if (obj.isInt()) colors = obj.getInt(); obj.free(); params->dictLookup("BitsPerComponent", &obj, recursion); if (obj.isInt()) bits = obj.getInt(); obj.free(); params->dictLookup("EarlyChange", &obj, recursion); if (obj.isInt()) early = obj.getInt(); obj.free(); } str = new LZWStream(str, pred, columns, colors, bits, early); } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) { str = new RunLengthStream(str); } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) { encoding = 0; endOfLine = gFalse; byteAlign = gFalse; columns = 1728; rows = 0; endOfBlock = gTrue; black = gFalse; if (params->isDict()) { params->dictLookup("K", &obj, recursion); if (obj.isInt()) { encoding = obj.getInt(); } obj.free(); params->dictLookup("EndOfLine", &obj, recursion); if (obj.isBool()) { endOfLine = obj.getBool(); } obj.free(); params->dictLookup("EncodedByteAlign", &obj, recursion); if (obj.isBool()) { byteAlign = obj.getBool(); } obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) { columns = obj.getInt(); } obj.free(); params->dictLookup("Rows", &obj, recursion); if (obj.isInt()) { rows = obj.getInt(); } obj.free(); params->dictLookup("EndOfBlock", &obj, recursion); if (obj.isBool()) { endOfBlock = obj.getBool(); } obj.free(); params->dictLookup("BlackIs1", &obj, recursion); if (obj.isBool()) { black = obj.getBool(); } obj.free(); } str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign, columns, rows, endOfBlock, black); } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) { colorXform = -1; if (params->isDict()) { if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) { colorXform = obj.getInt(); } obj.free(); } str = new DCTStream(str, colorXform); } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) { pred = 1; columns = 1; colors = 1; bits = 8; if (params->isDict()) { params->dictLookup("Predictor", &obj, recursion); if (obj.isInt()) pred = obj.getInt(); obj.free(); params->dictLookup("Columns", &obj, recursion); if (obj.isInt()) columns = obj.getInt(); obj.free(); params->dictLookup("Colors", &obj, recursion); if (obj.isInt()) colors = obj.getInt(); obj.free(); params->dictLookup("BitsPerComponent", &obj, recursion); if (obj.isInt()) bits = obj.getInt(); obj.free(); } str = new FlateStream(str, pred, columns, colors, bits); } else if (!strcmp(name, "JBIG2Decode")) { if (params->isDict()) { params->dictLookup("JBIG2Globals", &globals, recursion); } str = new JBIG2Stream(str, &globals); globals.free(); } else if (!strcmp(name, "JPXDecode")) { str = new JPXStream(str); } else { error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name); str = new EOFStream(str); } return str; } //------------------------------------------------------------------------ // BaseStream //------------------------------------------------------------------------ BaseStream::BaseStream(Object *dictA) { dict = *dictA; } BaseStream::~BaseStream() { dict.free(); } //------------------------------------------------------------------------ // FilterStream //------------------------------------------------------------------------ FilterStream::FilterStream(Stream *strA) { str = strA; } FilterStream::~FilterStream() { } void FilterStream::close() { str->close(); } void FilterStream::setPos(GFileOffset pos, int dir) { error(errInternal, -1, "Called setPos() on FilterStream"); } //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) { int imgLineSize; str = strA; width = widthA; nComps = nCompsA; nBits = nBitsA; nVals = width * nComps; inputLineSize = (nVals * nBits + 7) >> 3; if (width > INT_MAX / nComps || nVals > (INT_MAX - 7) / nBits) { // force a call to gmallocn(-1,...), which will throw an exception inputLineSize = -1; } inputLine = (char *)gmallocn(inputLineSize, sizeof(char)); if (nBits == 8) { imgLine = (Guchar *)inputLine; } else { if (nBits == 1) { imgLineSize = (nVals + 7) & ~7; } else { imgLineSize = nVals; } imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar)); } imgIdx = nVals; } ImageStream::~ImageStream() { if (imgLine != (Guchar *)inputLine) { gfree(imgLine); } gfree(inputLine); } void ImageStream::reset() { str->reset(); } void ImageStream::close() { str->close(); } GBool ImageStream::getPixel(Guchar *pix) { int i; if (imgIdx >= nVals) { if (!getLine()) { return gFalse; } imgIdx = 0; } for (i = 0; i < nComps; ++i) { pix[i] = imgLine[imgIdx++]; } return gTrue; } Guchar *ImageStream::getLine() { Gulong buf, bitMask; int bits; int c; int i; char *p; if (str->getBlock(inputLine, inputLineSize) != inputLineSize) { return NULL; } if (nBits == 1) { p = inputLine; for (i = 0; i < nVals; i += 8) { c = *p++; imgLine[i+0] = (Guchar)((c >> 7) & 1); imgLine[i+1] = (Guchar)((c >> 6) & 1); imgLine[i+2] = (Guchar)((c >> 5) & 1); imgLine[i+3] = (Guchar)((c >> 4) & 1); imgLine[i+4] = (Guchar)((c >> 3) & 1); imgLine[i+5] = (Guchar)((c >> 2) & 1); imgLine[i+6] = (Guchar)((c >> 1) & 1); imgLine[i+7] = (Guchar)(c & 1); } } else if (nBits == 8) { // special case: imgLine == inputLine } else if (nBits == 16) { for (i = 0; i < nVals; ++i) { imgLine[i] = (Guchar)inputLine[2*i]; } } else { bitMask = (1 << nBits) - 1; buf = 0; bits = 0; p = inputLine; for (i = 0; i < nVals; ++i) { if (bits < nBits) { buf = (buf << 8) | (*p++ & 0xff); bits += 8; } imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask); bits -= nBits; } } return imgLine; } void ImageStream::skipLine() { str->getBlock(inputLine, inputLineSize); } //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ StreamPredictor::StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA) { str = strA; predictor = predictorA; width = widthA; nComps = nCompsA; nBits = nBitsA; predLine = NULL; ok = gFalse; nVals = width * nComps; pixBytes = (nComps * nBits + 7) >> 3; rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes; if (width <= 0 || nComps <= 0 || nBits <= 0 || nComps > gfxColorMaxComps || nBits > 16 || width >= INT_MAX / nComps || // check for overflow in nVals nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes return; } predLine = (Guchar *)gmalloc(rowBytes); reset(); ok = gTrue; } StreamPredictor::~StreamPredictor() { gfree(predLine); } void StreamPredictor::reset() { memset(predLine, 0, rowBytes); predIdx = rowBytes; } int StreamPredictor::lookChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx]; } int StreamPredictor::getChar() { if (predIdx >= rowBytes) { if (!getNextLine()) { return EOF; } } return predLine[predIdx++]; } int StreamPredictor::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (predIdx >= rowBytes) { if (!getNextLine()) { break; } } m = rowBytes - predIdx; if (m > size - n) { m = size - n; } memcpy(blk + n, predLine + predIdx, m); predIdx += m; n += m; } return n; } GBool StreamPredictor::getNextLine() { int curPred; Guchar upLeftBuf[gfxColorMaxComps * 2 + 1]; int left, up, upLeft, p, pa, pb, pc; int c; Gulong inBuf, outBuf, bitMask; int inBits, outBits; int i, j, k, kk; // get PNG optimum predictor number if (predictor >= 10) { if ((curPred = str->getRawChar()) == EOF) { return gFalse; } curPred += 10; } else { curPred = predictor; } // read the raw line, apply PNG (byte) predictor memset(upLeftBuf, 0, pixBytes + 1); for (i = pixBytes; i < rowBytes; ++i) { for (j = pixBytes; j > 0; --j) { upLeftBuf[j] = upLeftBuf[j-1]; } upLeftBuf[0] = predLine[i]; if ((c = str->getRawChar()) == EOF) { if (i > pixBytes) { // this ought to return false, but some (broken) PDF files // contain truncated image data, and Adobe apparently reads the // last partial line break; } return gFalse; } switch (curPred) { case 11: // PNG sub predLine[i] = (Guchar)(predLine[i - pixBytes] + c); break; case 12: // PNG up predLine[i] = (Guchar)(predLine[i] + c); break; case 13: // PNG average predLine[i] = (Guchar)(((predLine[i - pixBytes] + predLine[i]) >> 1) + c); break; case 14: // PNG Paeth left = predLine[i - pixBytes]; up = predLine[i]; upLeft = upLeftBuf[pixBytes]; p = left + up - upLeft; if ((pa = p - left) < 0) pa = -pa; if ((pb = p - up) < 0) pb = -pb; if ((pc = p - upLeft) < 0) pc = -pc; if (pa <= pb && pa <= pc) predLine[i] = (Guchar)(left + c); else if (pb <= pc) predLine[i] = (Guchar)(up + c); else predLine[i] = (Guchar)(upLeft + c); break; case 10: // PNG none default: // no predictor or TIFF predictor predLine[i] = (Guchar)c; break; } } // apply TIFF (component) predictor if (predictor == 2) { if (nBits == 8) { for (i = pixBytes; i < rowBytes; ++i) { predLine[i] = (Guchar)(predLine[i] + predLine[i - nComps]); } } else if (nBits == 16) { for (i = pixBytes; i < rowBytes; i += 2) { c = ((predLine[i] + predLine[i - 2*nComps]) << 8) + predLine[i + 1] + predLine[i + 1 - 2*nComps]; predLine[i] = (Guchar)(c >> 8); predLine[i+1] = (Guchar)(c & 0xff); } } else { memset(upLeftBuf, 0, nComps); bitMask = (1 << nBits) - 1; inBuf = outBuf = 0; inBits = outBits = 0; j = k = pixBytes; for (i = 0; i < width; ++i) { for (kk = 0; kk < nComps; ++kk) { if (inBits < nBits) { inBuf = (inBuf << 8) | (predLine[j++] & 0xff); inBits += 8; } upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] + (inBuf >> (inBits - nBits))) & bitMask); inBits -= nBits; outBuf = (outBuf << nBits) | upLeftBuf[kk]; outBits += nBits; if (outBits >= 8) { predLine[k++] = (Guchar)(outBuf >> (outBits - 8)); outBits -= 8; } } } if (outBits > 0) { predLine[k++] = (Guchar)((outBuf << (8 - outBits)) + (inBuf & ((1 << (8 - outBits)) - 1))); } } } // reset to start of line predIdx = pixBytes; return gTrue; } //------------------------------------------------------------------------ // SharedFile //------------------------------------------------------------------------ class SharedFile { public: SharedFile(FILE *fA); SharedFile *copy(); void free(); int readBlock(char *buf, GFileOffset pos, int size); GFileOffset getSize(); private: ~SharedFile(); FILE *f; int refCnt; #if MULTITHREADED GMutex mutex; #endif }; SharedFile::SharedFile(FILE *fA) { f = fA; refCnt = 1; #if MULTITHREADED gInitMutex(&mutex); #endif } SharedFile::~SharedFile() { #if MULTITHREADED gDestroyMutex(&mutex); #endif } SharedFile *SharedFile::copy() { #if MULTITHREADED gLockMutex(&mutex); #endif ++refCnt; #if MULTITHREADED gUnlockMutex(&mutex); #endif return this; } void SharedFile::free() { int newCount; #if MULTITHREADED gLockMutex(&mutex); #endif newCount = --refCnt; #if MULTITHREADED gUnlockMutex(&mutex); #endif if (newCount == 0) { delete this; } } int SharedFile::readBlock(char *buf, GFileOffset pos, int size) { int n; #if MULTITHREADED gLockMutex(&mutex); #endif gfseek(f, pos, SEEK_SET); n = (int)fread(buf, 1, size, f); #if MULTITHREADED gUnlockMutex(&mutex); #endif return n; } GFileOffset SharedFile::getSize() { GFileOffset size; #if MULTITHREADED gLockMutex(&mutex); #endif gfseek(f, 0, SEEK_END); size = gftell(f); #if MULTITHREADED gUnlockMutex(&mutex); #endif return size; } //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA): BaseStream(dictA) { f = new SharedFile(fA); start = startA; limited = limitedA; length = lengthA; bufPtr = bufEnd = buf; bufPos = start; } FileStream::FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA): BaseStream(dictA) { f = fA->copy(); start = startA; limited = limitedA; length = lengthA; bufPtr = bufEnd = buf; bufPos = start; } FileStream::~FileStream() { f->free(); } Stream *FileStream::copy() { Object dictA; dict.copy(&dictA); return new FileStream(f, start, limited, length, &dictA); } Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA) { return new FileStream(f, startA, limitedA, lengthA, dictA); } void FileStream::reset() { bufPtr = bufEnd = buf; bufPos = start; } int FileStream::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > size - n) { m = size - n; } memcpy(blk + n, bufPtr, m); bufPtr += m; n += m; } return n; } GBool FileStream::fillBuf() { int n; bufPos += (int)(bufEnd - buf); bufPtr = bufEnd = buf; if (limited && bufPos >= start + length) { return gFalse; } if (limited && bufPos + fileStreamBufSize > start + length) { n = (int)(start + length - bufPos); } else { n = fileStreamBufSize; } n = f->readBlock(buf, bufPos, n); bufEnd = buf + n; if (bufPtr >= bufEnd) { return gFalse; } return gTrue; } void FileStream::setPos(GFileOffset pos, int dir) { GFileOffset size; if (dir >= 0) { bufPos = pos; } else { size = f->getSize(); if (pos <= size) { bufPos = size - pos; } else { bufPos = 0; } } bufPtr = bufEnd = buf; } void FileStream::moveStart(int delta) { start += delta; bufPtr = bufEnd = buf; bufPos = start; } //------------------------------------------------------------------------ // MemStream //------------------------------------------------------------------------ MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA): BaseStream(dictA) { buf = bufA; start = startA; length = lengthA; bufEnd = buf + start + length; bufPtr = buf + start; needFree = gFalse; } MemStream::~MemStream() { if (needFree) { gfree(buf); } } Stream *MemStream::copy() { Object dictA; dict.copy(&dictA); return new MemStream(buf, start, length, &dictA); } Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited, GFileOffset lengthA, Object *dictA) { MemStream *subStr; Guint newStart, newLength; if (startA < start) { newStart = start; } else if (startA > start + length) { newStart = start + (int)length; } else { newStart = (int)startA; } if (!limited || newStart + lengthA > start + length) { newLength = start + length - newStart; } else { newLength = (Guint)lengthA; } subStr = new MemStream(buf, newStart, newLength, dictA); return subStr; } void MemStream::reset() { bufPtr = buf + start; } void MemStream::close() { } int MemStream::getBlock(char *blk, int size) { int n; if (size <= 0) { return 0; } if (bufEnd - bufPtr < size) { n = (int)(bufEnd - bufPtr); } else { n = size; } memcpy(blk, bufPtr, n); bufPtr += n; return n; } void MemStream::setPos(GFileOffset pos, int dir) { Guint i; if (dir >= 0) { i = (Guint)pos; } else { i = (Guint)(start + length - pos); } if (i < start) { i = start; } else if (i > start + length) { i = start + length; } bufPtr = buf + i; } void MemStream::moveStart(int delta) { start += delta; length -= delta; bufPtr = buf + start; } //------------------------------------------------------------------------ // EmbedStream //------------------------------------------------------------------------ EmbedStream::EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA): BaseStream(dictA) { str = strA; limited = limitedA; length = lengthA; } EmbedStream::~EmbedStream() { } Stream *EmbedStream::copy() { Object dictA; dict.copy(&dictA); return new EmbedStream(str, &dictA, limited, length); } Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA, GFileOffset lengthA, Object *dictA) { error(errInternal, -1, "Called makeSubStream() on EmbedStream"); return NULL; } int EmbedStream::getChar() { if (limited && !length) { return EOF; } --length; return str->getChar(); } int EmbedStream::lookChar() { if (limited && !length) { return EOF; } return str->lookChar(); } int EmbedStream::getBlock(char *blk, int size) { if (size <= 0) { return 0; } if (limited && length < (Guint)size) { size = (int)length; } length -= size; return str->getBlock(blk, size); } void EmbedStream::setPos(GFileOffset pos, int dir) { error(errInternal, -1, "Called setPos() on EmbedStream"); } GFileOffset EmbedStream::getStart() { error(errInternal, -1, "Called getStart() on EmbedStream"); return 0; } void EmbedStream::moveStart(int delta) { error(errInternal, -1, "Called moveStart() on EmbedStream"); } //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ ASCIIHexStream::ASCIIHexStream(Stream *strA): FilterStream(strA) { buf = EOF; eof = gFalse; } ASCIIHexStream::~ASCIIHexStream() { delete str; } Stream *ASCIIHexStream::copy() { return new ASCIIHexStream(str->copy()); } void ASCIIHexStream::reset() { str->reset(); buf = EOF; eof = gFalse; } int ASCIIHexStream::lookChar() { int c1, c2, x; if (buf != EOF) return buf; if (eof) { buf = EOF; return EOF; } do { c1 = str->getChar(); } while (isspace(c1)); if (c1 == '>') { eof = gTrue; buf = EOF; return buf; } do { c2 = str->getChar(); } while (isspace(c2)); if (c2 == '>') { eof = gTrue; c2 = '0'; } if (c1 >= '0' && c1 <= '9') { x = (c1 - '0') << 4; } else if (c1 >= 'A' && c1 <= 'F') { x = (c1 - 'A' + 10) << 4; } else if (c1 >= 'a' && c1 <= 'f') { x = (c1 - 'a' + 10) << 4; } else if (c1 == EOF) { eof = gTrue; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c1); x = 0; } if (c2 >= '0' && c2 <= '9') { x += c2 - '0'; } else if (c2 >= 'A' && c2 <= 'F') { x += c2 - 'A' + 10; } else if (c2 >= 'a' && c2 <= 'f') { x += c2 - 'a' + 10; } else if (c2 == EOF) { eof = gTrue; x = 0; } else { error(errSyntaxError, getPos(), "Illegal character <{0:02x}> in ASCIIHex stream", c2); } buf = x & 0xff; return buf; } GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/ASCIIHexDecode filter\n"); return s; } GBool ASCIIHexStream::isBinary(GBool last) { return str->isBinary(gFalse); } //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ ASCII85Stream::ASCII85Stream(Stream *strA): FilterStream(strA) { index = n = 0; eof = gFalse; } ASCII85Stream::~ASCII85Stream() { delete str; } Stream *ASCII85Stream::copy() { return new ASCII85Stream(str->copy()); } void ASCII85Stream::reset() { str->reset(); index = n = 0; eof = gFalse; } int ASCII85Stream::lookChar() { int k; Gulong t; if (index >= n) { if (eof) return EOF; index = 0; do { c[0] = str->getChar(); } while (Lexer::isSpace(c[0])); if (c[0] == '~' || c[0] == EOF) { eof = gTrue; n = 0; return EOF; } else if (c[0] == 'z') { b[0] = b[1] = b[2] = b[3] = 0; n = 4; } else { for (k = 1; k < 5; ++k) { do { c[k] = str->getChar(); } while (Lexer::isSpace(c[k])); if (c[k] == '~' || c[k] == EOF) break; } n = k - 1; if (k < 5 && (c[k] == '~' || c[k] == EOF)) { for (++k; k < 5; ++k) c[k] = 0x21 + 84; eof = gTrue; } t = 0; for (k = 0; k < 5; ++k) t = t * 85 + (c[k] - 0x21); for (k = 3; k >= 0; --k) { b[k] = (int)(t & 0xff); t >>= 8; } } } return b[index]; } GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/ASCII85Decode filter\n"); return s; } GBool ASCII85Stream::isBinary(GBool last) { return str->isBinary(gFalse); } //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA): FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = NULL; } } else { pred = NULL; } early = earlyA; eof = gFalse; inputBits = 0; clearTable(); } LZWStream::~LZWStream() { if (pred) { delete pred; } delete str; } Stream *LZWStream::copy() { if (pred) { return new LZWStream(str->copy(), pred->getPredictor(), pred->getWidth(), pred->getNComps(), pred->getNBits(), early); } else { return new LZWStream(str->copy(), 1, 0, 0, 0, early); } } int LZWStream::getChar() { if (pred) { return pred->getChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } int LZWStream::lookChar() { if (pred) { return pred->lookChar(); } if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex]; } int LZWStream::getRawChar() { if (eof) { return EOF; } if (seqIndex >= seqLength) { if (!processNextCode()) { return EOF; } } return seqBuf[seqIndex++]; } int LZWStream::getBlock(char *blk, int size) { int n, m; if (pred) { return pred->getBlock(blk, size); } if (eof) { return 0; } n = 0; while (n < size) { if (seqIndex >= seqLength) { if (!processNextCode()) { break; } } m = seqLength - seqIndex; if (m > size - n) { m = size - n; } memcpy(blk + n, seqBuf + seqIndex, m); seqIndex += m; n += m; } return n; } void LZWStream::reset() { str->reset(); if (pred) { pred->reset(); } eof = gFalse; inputBits = 0; clearTable(); } GBool LZWStream::processNextCode() { int code; int nextLength; int i, j; // check for EOF if (eof) { return gFalse; } // check for eod and clear-table codes start: code = getCode(); if (code == EOF || code == 257) { eof = gTrue; return gFalse; } if (code == 256) { clearTable(); goto start; } if (nextCode >= 4097) { error(errSyntaxError, getPos(), "Bad LZW stream - expected clear-table code"); clearTable(); } // process the next code nextLength = seqLength + 1; if (code < 256) { seqBuf[0] = (Guchar)code; seqLength = 1; } else if (code < nextCode) { seqLength = table[code].length; for (i = seqLength - 1, j = code; i > 0; --i) { seqBuf[i] = table[j].tail; j = table[j].head; } seqBuf[0] = (Guchar)j; } else if (code == nextCode) { seqBuf[seqLength] = (Guchar)newChar; ++seqLength; } else { error(errSyntaxError, getPos(), "Bad LZW stream - unexpected code"); eof = gTrue; return gFalse; } newChar = seqBuf[0]; if (first) { first = gFalse; } else { table[nextCode].length = nextLength; table[nextCode].head = prevCode; table[nextCode].tail = (Guchar)newChar; ++nextCode; if (nextCode + early == 512) nextBits = 10; else if (nextCode + early == 1024) nextBits = 11; else if (nextCode + early == 2048) nextBits = 12; } prevCode = code; // reset buffer seqIndex = 0; return gTrue; } void LZWStream::clearTable() { nextCode = 258; nextBits = 9; seqIndex = seqLength = 0; first = gTrue; } int LZWStream::getCode() { int c; int code; while (inputBits < nextBits) { if ((c = str->getChar()) == EOF) return EOF; inputBuf = (inputBuf << 8) | (c & 0xff); inputBits += 8; } code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1); inputBits -= nextBits; return code; } GString *LZWStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2 || pred) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< "); if (!early) { s->append("/EarlyChange 0 "); } s->append(">> /LZWDecode filter\n"); return s; } GBool LZWStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ RunLengthStream::RunLengthStream(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; eof = gFalse; } RunLengthStream::~RunLengthStream() { delete str; } Stream *RunLengthStream::copy() { return new RunLengthStream(str->copy()); } void RunLengthStream::reset() { str->reset(); bufPtr = bufEnd = buf; eof = gFalse; } int RunLengthStream::getBlock(char *blk, int size) { int n, m; n = 0; while (n < size) { if (bufPtr >= bufEnd) { if (!fillBuf()) { break; } } m = (int)(bufEnd - bufPtr); if (m > size - n) { m = size - n; } memcpy(blk + n, bufPtr, m); bufPtr += m; n += m; } return n; } GString *RunLengthStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("/RunLengthDecode filter\n"); return s; } GBool RunLengthStream::isBinary(GBool last) { return str->isBinary(gTrue); } GBool RunLengthStream::fillBuf() { int c; int n, i; if (eof) return gFalse; c = str->getChar(); if (c == 0x80 || c == EOF) { eof = gTrue; return gFalse; } if (c < 0x80) { n = c + 1; for (i = 0; i < n; ++i) buf[i] = (char)str->getChar(); } else { n = 0x101 - c; c = str->getChar(); for (i = 0; i < n; ++i) buf[i] = (char)c; } bufPtr = buf; bufEnd = buf + n; return gTrue; } //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, GBool byteAlignA, int columnsA, int rowsA, GBool endOfBlockA, GBool blackA): FilterStream(strA) { encoding = encodingA; endOfLine = endOfLineA; byteAlign = byteAlignA; columns = columnsA; if (columns < 1) { columns = 1; } else if (columns > INT_MAX - 3) { columns = INT_MAX - 3; } rows = rowsA; endOfBlock = endOfBlockA; black = blackA; blackXOR = black ? 0xff : 0x00; // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns // ---> max codingLine size = columns + 1 // refLine has two extra guard entries at the end // ---> max refLine size = columns + 3 codingLine = (int *)gmallocn(columns + 1, sizeof(int)); refLine = (int *)gmallocn(columns + 3, sizeof(int)); eof = gFalse; row = 0; nextLine2D = encoding < 0; inputBits = 0; codingLine[0] = columns; nextCol = columns; a0i = 0; err = gFalse; nErrors = 0; } CCITTFaxStream::~CCITTFaxStream() { delete str; gfree(refLine); gfree(codingLine); } Stream *CCITTFaxStream::copy() { return new CCITTFaxStream(str->copy(), encoding, endOfLine, byteAlign, columns, rows, endOfBlock, black); } void CCITTFaxStream::reset() { int code1; str->reset(); eof = gFalse; row = 0; nextLine2D = encoding < 0; inputBits = 0; codingLine[0] = columns; nextCol = columns; a0i = 0; // skip any initial zero bits and end-of-line marker, and get the 2D // encoding tag while ((code1 = lookBits(12)) == 0) { eatBits(1); } if (code1 == 0x001) { eatBits(12); endOfLine = gTrue; } if (encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } } int CCITTFaxStream::getChar() { int c, bitsNeeded, bitsAvail, bitsUsed; if (nextCol >= columns) { if (eof) { return EOF; } if (!readRow()) { return EOF; } } bitsAvail = codingLine[a0i] - nextCol; if (bitsAvail > 8) { c = (a0i & 1) ? 0x00 : 0xff; } else { c = 0; bitsNeeded = 8; do { bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; c <<= bitsUsed; if (!(a0i & 1)) { c |= 0xff >> (8 - bitsUsed); } bitsAvail -= bitsUsed; bitsNeeded -= bitsUsed; if (bitsAvail == 0) { if (codingLine[a0i] >= columns) { c <<= bitsNeeded; break; } ++a0i; bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; } } while (bitsNeeded > 0); } nextCol += 8; c ^= blackXOR; return c; } int CCITTFaxStream::lookChar() { int c, bitsNeeded, bitsAvail, bitsUsed, i; if (nextCol >= columns) { if (eof) { return EOF; } if (!readRow()) { return EOF; } } bitsAvail = codingLine[a0i] - nextCol; if (bitsAvail >= 8) { c = (a0i & 1) ? 0x00 : 0xff; } else { i = a0i; c = 0; bitsNeeded = 8; do { bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; c <<= bitsUsed; if (!(i & 1)) { c |= 0xff >> (8 - bitsUsed); } bitsAvail -= bitsUsed; bitsNeeded -= bitsUsed; if (bitsAvail == 0) { if (codingLine[i] >= columns) { c <<= bitsNeeded; break; } ++i; bitsAvail = codingLine[i] - codingLine[i - 1]; } } while (bitsNeeded > 0); } c ^= blackXOR; return c; } int CCITTFaxStream::getBlock(char *blk, int size) { int bytesRead, bitsAvail, bitsNeeded, bitsUsed, byte, c; bytesRead = 0; while (bytesRead < size) { if (nextCol >= columns) { if (eof) { break; } if (!readRow()) { break; } } bitsAvail = codingLine[a0i] - nextCol; byte = (a0i & 1) ? 0x00 : 0xff; if (bitsAvail > 8) { c = byte; bitsAvail -= 8; } else { c = 0; bitsNeeded = 8; do { bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded; c <<= bitsUsed; c |= byte >> (8 - bitsUsed); bitsAvail -= bitsUsed; bitsNeeded -= bitsUsed; if (bitsAvail == 0) { if (codingLine[a0i] >= columns) { c <<= bitsNeeded; break; } ++a0i; bitsAvail = codingLine[a0i] - codingLine[a0i - 1]; byte ^= 0xff; } } while (bitsNeeded > 0); } nextCol += 8; blk[bytesRead++] = (char)(c ^ blackXOR); } return bytesRead; } inline void CCITTFaxStream::addPixels(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = gTrue; ++nErrors; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } } inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) { if (a1 > codingLine[a0i]) { if (a1 > columns) { error(errSyntaxError, getPos(), "CCITTFax row is wrong length ({0:d})", a1); err = gTrue; ++nErrors; a1 = columns; } if ((a0i & 1) ^ blackPixels) { ++a0i; } codingLine[a0i] = a1; } else if (a1 < codingLine[a0i]) { if (a1 < 0) { error(errSyntaxError, getPos(), "Invalid CCITTFax code"); err = gTrue; ++nErrors; a1 = 0; } while (a0i > 0 && a1 <= codingLine[a0i - 1]) { --a0i; } codingLine[a0i] = a1; } } GBool CCITTFaxStream::readRow() { int code1, code2, code3; int b1i, blackPixels, i; GBool gotEOL; // if at eof just return EOF if (eof) { return gFalse; } err = gFalse; // 2-D encoding if (nextLine2D) { for (i = 0; codingLine[i] < columns; ++i) { refLine[i] = codingLine[i]; } refLine[i++] = columns; refLine[i++] = columns; refLine[i] = columns; codingLine[0] = 0; a0i = 0; b1i = 0; blackPixels = 0; // invariant: // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1] // <= columns // exception at left edge: // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible // exception at right edge: // refLine[b1i] = refLine[b1i+1] = columns is possible while (codingLine[a0i] < columns) { code1 = getTwoDimCode(); switch (code1) { case twoDimPass: addPixels(refLine[b1i + 1], blackPixels); if (refLine[b1i + 1] < columns) { b1i += 2; } break; case twoDimHoriz: code1 = code2 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); do { code2 += code3 = getWhiteCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); do { code2 += code3 = getBlackCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); if (codingLine[a0i] < columns) { addPixels(codingLine[a0i] + code2, blackPixels ^ 1); } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } break; case twoDimVertR3: addPixels(refLine[b1i] + 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertR2: addPixels(refLine[b1i] + 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertR1: addPixels(refLine[b1i] + 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVert0: addPixels(refLine[b1i], blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { ++b1i; while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL3: addPixelsNeg(refLine[b1i] - 3, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL2: addPixelsNeg(refLine[b1i] - 2, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case twoDimVertL1: addPixelsNeg(refLine[b1i] - 1, blackPixels); blackPixels ^= 1; if (codingLine[a0i] < columns) { if (b1i > 0) { --b1i; } else { ++b1i; } while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) { b1i += 2; } } break; case EOF: addPixels(columns, 0); err = gTrue; break; default: error(errSyntaxError, getPos(), "Bad 2D code {0:04x} in CCITTFax stream", code1); addPixels(columns, 0); err = gTrue; ++nErrors; break; } } // 1-D encoding } else { codingLine[0] = 0; a0i = 0; blackPixels = 0; while (codingLine[a0i] < columns) { code1 = 0; if (blackPixels) { do { code1 += code3 = getBlackCode(); } while (code3 >= 64); } else { do { code1 += code3 = getWhiteCode(); } while (code3 >= 64); } addPixels(codingLine[a0i] + code1, blackPixels); blackPixels ^= 1; } } // check for end-of-line marker, skipping over any extra zero bits // (if EncodedByteAlign is true and EndOfLine is false, there can // be "false" EOL markers -- i.e., if the last n unused bits in // row i are set to zero, and the first 11-n bits in row i+1 // happen to be zero -- so we don't look for EOL markers in this // case) gotEOL = gFalse; if (!endOfBlock && row == rows - 1) { eof = gTrue; } else if (endOfLine || !byteAlign) { code1 = lookBits(12); if (endOfLine) { while (code1 != EOF && code1 != 0x001) { eatBits(1); code1 = lookBits(12); } } else { while (code1 == 0) { eatBits(1); code1 = lookBits(12); } } if (code1 == 0x001) { eatBits(12); gotEOL = gTrue; } } // byte-align the row // (Adobe apparently doesn't do byte alignment after EOL markers // -- I've seen CCITT image data streams in two different formats, // both with the byteAlign flag set: // 1. xx:x0:01:yy:yy // 2. xx:00:1y:yy:yy // where xx is the previous line, yy is the next line, and colons // separate bytes.) if (byteAlign && !gotEOL) { inputBits &= ~7; } // check for end of stream if (lookBits(1) == EOF) { eof = gTrue; } // get 2D encoding tag if (!eof && encoding > 0) { nextLine2D = !lookBits(1); eatBits(1); } // check for end-of-block marker if (endOfBlock && !endOfLine && byteAlign) { // in this case, we didn't check for an EOL code above, so we // need to check here code1 = lookBits(24); if (code1 == 0x001001) { eatBits(12); gotEOL = gTrue; } } if (endOfBlock && gotEOL) { code1 = lookBits(12); if (code1 == 0x001) { eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } if (encoding >= 0) { for (i = 0; i < 4; ++i) { code1 = lookBits(12); if (code1 != 0x001) { error(errSyntaxError, getPos(), "Bad RTC code in CCITTFax stream"); ++nErrors; } eatBits(12); if (encoding > 0) { lookBits(1); eatBits(1); } } } eof = gTrue; } // look for an end-of-line marker after an error -- we only do // this if we know the stream contains end-of-line markers because // the "just plow on" technique tends to work better otherwise } else if (err && endOfLine) { while (1) { code1 = lookBits(13); if (code1 == EOF) { eof = gTrue; return gFalse; } if ((code1 >> 1) == 0x001) { break; } eatBits(1); } eatBits(12); if (encoding > 0) { eatBits(1); nextLine2D = !(code1 & 1); } } // corrupt CCITTFax streams can generate huge data expansion -- we // avoid that case by aborting decode after 1000 errors if (nErrors > 1000) { error(errSyntaxError, getPos(), "Too many errors in CCITTFaxStream - aborting decode"); eof = gTrue; return gFalse; } // set up for output nextCol = 0; a0i = (codingLine[0] > 0) ? 0 : 1; ++row; return gTrue; } short CCITTFaxStream::getTwoDimCode() { int code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { if ((code = lookBits(7)) != EOF) { p = &twoDimTab1[code]; if (p->bits > 0) { eatBits(p->bits); return p->n; } } } else { for (n = 1; n <= 7; ++n) { if ((code = lookBits(n)) == EOF) { break; } if (n < 7) { code <<= 7 - n; } p = &twoDimTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad two dim code ({0:04x}) in CCITTFax stream", code); ++nErrors; return EOF; } short CCITTFaxStream::getWhiteCode() { short code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(12); if (code == EOF) { return 1; } if ((code >> 5) == 0) { p = &whiteTab1[code]; } else { p = &whiteTab2[code >> 3]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 1; n <= 9; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 9) { code = (short)(code << (9 - n)); } p = &whiteTab2[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 11; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code = (short)(code << (12 - n)); } p = &whiteTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad white code ({0:04x}) in CCITTFax stream", code); ++nErrors; // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::getBlackCode() { short code; CCITTCode *p; int n; code = 0; // make gcc happy if (endOfBlock) { code = lookBits(13); if (code == EOF) { return 1; } if ((code >> 7) == 0) { p = &blackTab1[code]; } else if ((code >> 9) == 0 && (code >> 7) != 0) { p = &blackTab2[(code >> 1) - 64]; } else { p = &blackTab3[code >> 7]; } if (p->bits > 0) { eatBits(p->bits); return p->n; } } else { for (n = 2; n <= 6; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 6) { code = (short)(code << (6 - n)); } p = &blackTab3[code]; if (p->bits == n) { eatBits(n); return p->n; } } for (n = 7; n <= 12; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 12) { code = (short)(code << (12 - n)); } if (code >= 64) { p = &blackTab2[code - 64]; if (p->bits == n) { eatBits(n); return p->n; } } } for (n = 10; n <= 13; ++n) { code = lookBits(n); if (code == EOF) { return 1; } if (n < 13) { code = (short)(code << (13 - n)); } p = &blackTab1[code]; if (p->bits == n) { eatBits(n); return p->n; } } } error(errSyntaxError, getPos(), "Bad black code ({0:04x}) in CCITTFax stream", code); ++nErrors; // eat a bit and return a positive number so that the caller doesn't // go into an infinite loop eatBits(1); return 1; } short CCITTFaxStream::lookBits(int n) { int c; while (inputBits < n) { if ((c = str->getChar()) == EOF) { if (inputBits == 0) { return EOF; } // near the end of the stream, the caller may ask for more bits // than are available, but there may still be a valid code in // however many bits are available -- we need to return correct // data in this case return (short)((inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n))); } inputBuf = (inputBuf << 8) + c; inputBits += 8; } return (short)((inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n))); } GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { GString *s; char s1[50]; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< "); if (encoding != 0) { sprintf(s1, "/K %d ", encoding); s->append(s1); } if (endOfLine) { s->append("/EndOfLine true "); } if (byteAlign) { s->append("/EncodedByteAlign true "); } sprintf(s1, "/Columns %d ", columns); s->append(s1); if (rows != 0) { sprintf(s1, "/Rows %d ", rows); s->append(s1); } if (!endOfBlock) { s->append("/EndOfBlock false "); } if (black) { s->append("/BlackIs1 true "); } s->append(">> /CCITTFaxDecode filter\n"); return s; } GBool CCITTFaxStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ #if HAVE_JPEGLIB DCTStream::DCTStream(Stream *strA, GBool colorXformA): FilterStream(strA) { colorXform = colorXformA; lineBuf = NULL; inlineImage = str->isEmbedStream(); } DCTStream::~DCTStream() { delete str; } Stream *DCTStream::copy() { return new DCTStream(str->copy(), colorXform); } void DCTStream::reset() { int i; lineBuf = NULL; error = gFalse; str->reset(); // initialize the libjpeg decompression object decomp.err = jpeg_std_error(&errorMgr.err); errorMgr.err.error_exit = &errorExit; errorMgr.err.output_message = &errorMessage; if (setjmp(errorMgr.setjmpBuf)) { error = gTrue; return; } jpeg_create_decompress(&decomp); // set up the data source manager sourceMgr.src.next_input_byte = NULL; sourceMgr.src.bytes_in_buffer = 0; sourceMgr.src.init_source = &initSourceCbk; sourceMgr.src.fill_input_buffer = &fillInputBufferCbk; sourceMgr.src.skip_input_data = &skipInputDataCbk; sourceMgr.src.resync_to_restart = &jpeg_resync_to_restart; sourceMgr.src.term_source = &termSourceCbk; sourceMgr.str = this; decomp.src = &sourceMgr.src; // read the header jpeg_read_header(&decomp, TRUE); jpeg_calc_output_dimensions(&decomp); // set up the color transform if (!decomp.saw_Adobe_marker && colorXform >= 0) { if (decomp.num_components == 3) { decomp.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB; decomp.out_color_space = JCS_RGB; decomp.out_color_components = 3; } else if (decomp.num_components == 4) { decomp.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK; decomp.out_color_space = JCS_CMYK; decomp.out_color_components = 4; } } // allocate a line buffer if ((lineBufHeight = decomp.rec_outbuf_height) > 4) { lineBufHeight = 4; } lineBuf = (char *)gmallocn(lineBufHeight * decomp.out_color_components, decomp.output_width); for (i = 0; i < lineBufHeight; ++i) { lineBufRows[i] = lineBuf + i * decomp.out_color_components * decomp.output_width; } bufPtr = bufEnd = lineBuf; // start up the decompression process jpeg_start_decompress(&decomp); } void DCTStream::close() { // we don't call jpeg_finish_decompress() here because it will report // an error if the full image wasn't read if (setjmp(errorMgr.setjmpBuf)) { goto skip; } jpeg_destroy_decompress(&decomp); skip: gfree(lineBuf); FilterStream::close(); } int DCTStream::getChar() { if (error) { return EOF; } if (bufPtr == bufEnd) { if (!fillBuf()) { return EOF; } } return *bufPtr++ & 0xff; } int DCTStream::lookChar() { if (error) { return EOF; } if (bufPtr == bufEnd) { if (!fillBuf()) { return EOF; } } return *bufPtr & 0xff; } int DCTStream::getBlock(char *blk, int size) { int nRead, nAvail, n; if (error) { return 0; } nRead = 0; while (nRead < size) { if (bufPtr == bufEnd) { if (!fillBuf()) { break; } } nAvail = bufEnd - bufPtr; n = (nAvail < size - nRead) ? nAvail : size - nRead; memcpy(blk + nRead, bufPtr, n); bufPtr += n; nRead += n; } return nRead; } GBool DCTStream::fillBuf() { int nLines; if (setjmp(errorMgr.setjmpBuf)) { error = gTrue; return gFalse; } nLines = jpeg_read_scanlines(&decomp, (JSAMPARRAY)lineBufRows, lineBufHeight); bufPtr = lineBuf; bufEnd = lineBuf + nLines * decomp.out_color_components * decomp.output_width; return nLines > 0; } void DCTStream::errorExit(j_common_ptr d) { DCTErrorMgr *errMgr = (DCTErrorMgr *)d->err; longjmp(errMgr->setjmpBuf, 1); } void DCTStream::errorMessage(j_common_ptr d) { #if 0 // for debugging char buf[JMSG_LENGTH_MAX]; (*d->err->format_message)(d, buf); fprintf(stderr, "%s\n", buf); #endif } void DCTStream::initSourceCbk(j_decompress_ptr d) { DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; sourceMgr->src.next_input_byte = NULL; sourceMgr->src.bytes_in_buffer = 0; } boolean DCTStream::fillInputBufferCbk(j_decompress_ptr d) { DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; int c, n; // for inline images, we need to read one byte at a time so we don't // read past the end of the input data if (sourceMgr->str->inlineImage) { c = sourceMgr->str->str->getChar(); if (c == EOF) { sourceMgr->buf[0] = (char)0xff; sourceMgr->buf[1] = (char)JPEG_EOI; sourceMgr->src.bytes_in_buffer = 2; } else { sourceMgr->buf[0] = (char)c; sourceMgr->src.bytes_in_buffer = 1; } } else { n = sourceMgr->str->str->getBlock(sourceMgr->buf, dctStreamBufSize); if (n > 0) { sourceMgr->src.bytes_in_buffer = (size_t)n; } else { sourceMgr->buf[0] = (char)0xff; sourceMgr->buf[1] = (char)JPEG_EOI; sourceMgr->src.bytes_in_buffer = 2; } } sourceMgr->src.next_input_byte = (JOCTET *)sourceMgr->buf; return TRUE; } void DCTStream::skipInputDataCbk(j_decompress_ptr d, long numBytes) { DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src; if (numBytes > 0) { if ((long)sourceMgr->src.bytes_in_buffer < numBytes) { sourceMgr->str->str->discardChars( (Guint)(numBytes - sourceMgr->src.bytes_in_buffer)); sourceMgr->src.bytes_in_buffer = 0; } else { sourceMgr->src.bytes_in_buffer -= numBytes; sourceMgr->src.next_input_byte += numBytes; } } } void DCTStream::termSourceCbk(j_decompress_ptr d) { } #else // HAVE_JPEGLIB #define idctScaleA 1024 #define idctScaleB 1138 #define idctScaleC 1730 #define idctScaleD 1609 #define idctScaleE 1264 #define idctScaleF 1922 #define idctScaleG 1788 #define idctScaleH 2923 #define idctScaleI 2718 #define idctScaleJ 2528 static int idctScaleMat[64] = { idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE, idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB, idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG, idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF, idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE }; // color conversion parameters (16.16 fixed point format) #define dctCrToR 91881 // 1.4020 #define dctCbToG -22553 // -0.3441363 #define dctCrToG -46802 // -0.71413636 #define dctCbToB 116130 // 1.772 // The dctClip function clips signed integers to the [0,255] range. // To handle valid DCT inputs, this must support an input range of at // least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF // files) can result in arbitrary values, so we want to mask those // out. We round the input range size up to a power of 2 (so we can // use a bit mask), which gives us an input range of [-384,639]. The // end result is: // input output // ---------- ------ // <-384 X invalid inputs -> output is "don't care" // -384..-257 0 invalid inputs, clipped // -256..-1 0 valid inputs, need to be clipped // 0..255 0..255 // 256..511 255 valid inputs, need to be clipped // 512..639 255 invalid inputs, clipped // >=512 X invalid inputs -> output is "don't care" #define dctClipOffset 384 #define dctClipMask 1023 static Guchar dctClipData[1024]; static inline void dctClipInit() { static int initDone = 0; int i; if (!initDone) { for (i = -384; i < 0; ++i) { dctClipData[dctClipOffset + i] = 0; } for (i = 0; i < 256; ++i) { dctClipData[dctClipOffset + i] = (Guchar)i; } for (i = 256; i < 639; ++i) { dctClipData[dctClipOffset + i] = 255; } initDone = 1; } } static inline Guchar dctClip(int x) { return dctClipData[(dctClipOffset + x) & dctClipMask]; } // zig zag decode map static int dctZigZag[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; DCTStream::DCTStream(Stream *strA, GBool colorXformA): FilterStream(strA) { int i; colorXform = colorXformA; progressive = interleaved = gFalse; width = height = 0; mcuWidth = mcuHeight = 0; numComps = 0; comp = 0; x = y = 0; for (i = 0; i < 4; ++i) { frameBuf[i] = NULL; } rowBuf = NULL; memset(dcHuffTables, 0, sizeof(dcHuffTables)); memset(acHuffTables, 0, sizeof(acHuffTables)); dctClipInit(); } DCTStream::~DCTStream() { close(); delete str; } Stream *DCTStream::copy() { return new DCTStream(str->copy(), colorXform); } void DCTStream::reset() { int i; str->reset(); progressive = interleaved = gFalse; width = height = 0; numComps = 0; numQuantTables = 0; numDCHuffTables = 0; numACHuffTables = 0; gotJFIFMarker = gFalse; gotAdobeMarker = gFalse; restartInterval = 0; if (!readHeader(gTrue)) { // force an EOF condition progressive = gTrue; y = height; return; } // compute MCU size if (numComps == 1) { compInfo[0].hSample = compInfo[0].vSample = 1; } mcuWidth = compInfo[0].hSample; mcuHeight = compInfo[0].vSample; for (i = 1; i < numComps; ++i) { if (compInfo[i].hSample > mcuWidth) { mcuWidth = compInfo[i].hSample; } if (compInfo[i].vSample > mcuHeight) { mcuHeight = compInfo[i].vSample; } } mcuWidth *= 8; mcuHeight *= 8; // figure out color transform if (colorXform == -1) { if (numComps == 3) { if (gotJFIFMarker) { colorXform = 1; } else if (compInfo[0].id == 82 && compInfo[1].id == 71 && compInfo[2].id == 66) { // ASCII "RGB" colorXform = 0; } else { colorXform = 1; } } else { colorXform = 0; } } if (progressive || !interleaved) { // allocate a buffer for the whole image bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight; if (bufWidth <= 0 || bufHeight <= 0 || bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) { error(errSyntaxError, getPos(), "Invalid image size in DCT stream"); y = height; return; } for (i = 0; i < numComps; ++i) { frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int)); } // read the image data do { restartMarker = 0xd0; restart(); readScan(); } while (readHeader(gFalse)); // decode decodeImage(); // initialize counters comp = 0; x = 0; y = 0; } else { if (scanInfo.numComps != numComps) { error(errSyntaxError, getPos(), "Invalid scan in sequential DCT stream"); y = height; return; } // allocate a buffer for one row of MCUs bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth; rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth); rowBufPtr = rowBufEnd = rowBuf; // initialize counters y = -mcuHeight; restartMarker = 0xd0; restart(); } } void DCTStream::close() { int i; for (i = 0; i < 4; ++i) { gfree(frameBuf[i]); frameBuf[i] = NULL; } gfree(rowBuf); rowBuf = NULL; FilterStream::close(); } int DCTStream::getChar() { int c; if (progressive || !interleaved) { if (y >= height) { return EOF; } c = frameBuf[comp][y * bufWidth + x]; if (++comp == numComps) { comp = 0; if (++x == width) { x = 0; ++y; } } } else { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { return EOF; } y += mcuHeight; if (!readMCURow()) { y = height; return EOF; } } c = *rowBufPtr++; } return c; } int DCTStream::lookChar() { if (progressive || !interleaved) { if (y >= height) { return EOF; } return frameBuf[comp][y * bufWidth + x]; } else { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { return EOF; } if (!readMCURow()) { y = height; return EOF; } } return *rowBufPtr; } } int DCTStream::getBlock(char *blk, int size) { int nRead, nAvail, n; if (progressive || !interleaved) { if (y >= height) { return 0; } for (nRead = 0; nRead < size; ++nRead) { blk[nRead] = (char)frameBuf[comp][y * bufWidth + x]; if (++comp == numComps) { comp = 0; if (++x == width) { x = 0; ++y; if (y >= height) { ++nRead; break; } } } } } else { nRead = 0; while (nRead < size) { if (rowBufPtr == rowBufEnd) { if (y + mcuHeight >= height) { break; } y += mcuHeight; if (!readMCURow()) { y = height; break; } } nAvail = (int)(rowBufEnd - rowBufPtr); n = (nAvail < size - nRead) ? nAvail : size - nRead; memcpy(blk + nRead, rowBufPtr, n); rowBufPtr += n; nRead += n; } } return nRead; } void DCTStream::restart() { int i; inputBits = 0; restartCtr = restartInterval; for (i = 0; i < numComps; ++i) { compInfo[i].prevDC = 0; } eobRun = 0; } // Read one row of MCUs from a sequential JPEG stream. GBool DCTStream::readMCURow() { int data1[64]; Guchar data2[64]; Guchar *p1, *p2; int pY, pCb, pCr, pR, pG, pB; int h, v, horiz, vert, hSub, vSub; int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int c; for (cc = 0; cc < numComps; ++cc) { if (scanInfo.dcHuffTable[cc] >= numDCHuffTables || scanInfo.acHuffTable[cc] >= numACHuffTables) { error(errSyntaxError, getPos(), "Bad DCT data: invalid Huffman table index"); return gFalse; } if (compInfo[cc].quantTable > numQuantTables) { error(errSyntaxError, getPos(), "Bad DCT data: invalid quant table index"); return gFalse; } } for (x1 = 0; x1 < width; x1 += mcuWidth) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return gFalse; } if (++restartMarker == 0xd8) restartMarker = 0xd0; restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data1)) { return gFalse; } transformDataUnit(quantTables[compInfo[cc].quantTable], data1, data2); if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; p1[0] = data2[i]; p1[ numComps] = data2[i+1]; p1[2*numComps] = data2[i+2]; p1[3*numComps] = data2[i+3]; p1[4*numComps] = data2[i+4]; p1[5*numComps] = data2[i+5]; p1[6*numComps] = data2[i+6]; p1[7*numComps] = data2[i+7]; } } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) { for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc]; p2 = p1 + width * numComps; p1[0] = p1[numComps] = p2[0] = p2[numComps] = data2[i]; p1[2*numComps] = p1[3*numComps] = p2[2*numComps] = p2[3*numComps] = data2[i+1]; p1[4*numComps] = p1[5*numComps] = p2[4*numComps] = p2[5*numComps] = data2[i+2]; p1[6*numComps] = p1[7*numComps] = p2[6*numComps] = p2[7*numComps] = data2[i+3]; p1[8*numComps] = p1[9*numComps] = p2[8*numComps] = p2[9*numComps] = data2[i+4]; p1[10*numComps] = p1[11*numComps] = p2[10*numComps] = p2[11*numComps] = data2[i+5]; p1[12*numComps] = p1[13*numComps] = p2[12*numComps] = p2[13*numComps] = data2[i+6]; p1[14*numComps] = p1[15*numComps] = p2[14*numComps] = p2[15*numComps] = data2[i+7]; } } else { p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc]; i = 0; for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { for (y5 = 0; y5 < vSub; ++y5) { for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) { p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i]; } } ++i; } } } } } } --restartCtr; } // color space conversion if (colorXform) { // convert YCbCr to RGB if (numComps == 3) { for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) { pY = p1[0]; pCb = p1[1] - 128; pCr = p1[2] - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; p1[0] = dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; p1[1] = dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; p1[2] = dctClip(pB); } // convert YCbCrK to CMYK (K is passed through unchanged) } else if (numComps == 4) { for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) { pY = p1[0]; pCb = p1[1] - 128; pCr = p1[2] - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; p1[0] = (Guchar)(255 - dctClip(pR)); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; p1[1] = (Guchar)(255 - dctClip(pG)); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; p1[2] = (Guchar)(255 - dctClip(pB)); } } } rowBufPtr = rowBuf; if (y + mcuHeight <= height) { rowBufEnd = rowBuf + numComps * width * mcuHeight; } else { rowBufEnd = rowBuf + numComps * width * (height - y); } return gTrue; } // Read one scan from a progressive or non-interleaved JPEG stream. void DCTStream::readScan() { int data[64]; int x1, y1, dx1, dy1, x2, y2, y3, cc, i; int h, v, horiz, vert, vSub; int *p1; int c; for (cc = 0; cc < numComps; ++cc) { if (scanInfo.comp[cc] && (scanInfo.dcHuffTable[cc] >= numDCHuffTables || ((!progressive || scanInfo.lastCoeff > 0) && scanInfo.acHuffTable[cc] >= numACHuffTables))) { error(errSyntaxError, getPos(), "Bad DCT data: invalid Huffman table index"); return; } if (compInfo[cc].quantTable > numQuantTables) { error(errSyntaxError, getPos(), "Bad DCT data: invalid quant table index"); return; } } if (scanInfo.numComps == 1) { for (cc = 0; cc < numComps; ++cc) { if (scanInfo.comp[cc]) { break; } } dx1 = mcuWidth / compInfo[cc].hSample; dy1 = mcuHeight / compInfo[cc].vSample; } else { dx1 = mcuWidth; dy1 = mcuHeight; } for (y1 = 0; y1 < height; y1 += dy1) { for (x1 = 0; x1 < width; x1 += dx1) { // deal with restart marker if (restartInterval > 0 && restartCtr == 0) { c = readMarker(); if (c != restartMarker) { error(errSyntaxError, getPos(), "Bad DCT data: incorrect restart marker"); return; } if (++restartMarker == 0xd8) { restartMarker = 0xd0; } restart(); } // read one MCU for (cc = 0; cc < numComps; ++cc) { if (!scanInfo.comp[cc]) { continue; } h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; vSub = vert / 8; for (y2 = 0; y2 < dy1; y2 += vert) { for (x2 = 0; x2 < dx1; x2 += horiz) { // pull out the current values p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { data[i] = p1[0]; data[i+1] = p1[1]; data[i+2] = p1[2]; data[i+3] = p1[3]; data[i+4] = p1[4]; data[i+5] = p1[5]; data[i+6] = p1[6]; data[i+7] = p1[7]; p1 += bufWidth * vSub; } // read one data unit if (progressive) { if (!readProgressiveDataUnit( &dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } else { if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]], &acHuffTables[scanInfo.acHuffTable[cc]], &compInfo[cc].prevDC, data)) { return; } } // add the data unit into frameBuf p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = data[i]; p1[1] = data[i+1]; p1[2] = data[i+2]; p1[3] = data[i+3]; p1[4] = data[i+4]; p1[5] = data[i+5]; p1[6] = data[i+6]; p1[7] = data[i+7]; p1 += bufWidth * vSub; } } } } --restartCtr; } } } // Read one data unit from a sequential JPEG stream. GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp; int c; int i, j; if ((size = readHuffSym(dcHuffTable)) == 9999) { return gFalse; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return gFalse; } } else { amp = 0; } data[0] = *prevDC += amp; for (i = 1; i < 64; ++i) { data[i] = 0; } i = 1; while (i < 64) { run = 0; while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) { run += 0x10; } if (c == 9999) { return gFalse; } if (c == 0x00) { break; } else { run += (c >> 4) & 0x0f; size = c & 0x0f; amp = readAmp(size); if (amp == 9999) { return gFalse; } i += run; if (i < 64) { j = dctZigZag[i++]; data[j] = amp; } } } return gTrue; } // Read one data unit from a progressive JPEG stream. GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]) { int run, size, amp, bit, c; int i, j, k; // get the DC coefficient i = scanInfo.firstCoeff; if (i == 0) { if (scanInfo.ah == 0) { if ((size = readHuffSym(dcHuffTable)) == 9999) { return gFalse; } if (size > 0) { if ((amp = readAmp(size)) == 9999) { return gFalse; } } else { amp = 0; } data[0] += (*prevDC += amp) << scanInfo.al; } else { if ((bit = readBit()) == 9999) { return gFalse; } if (bit) { data[0] += 1 << scanInfo.al; } } ++i; } if (scanInfo.lastCoeff == 0) { return gTrue; } // check for an EOB run if (eobRun > 0) { while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } --eobRun; return gTrue; } // read the AC coefficients while (i <= scanInfo.lastCoeff) { if ((c = readHuffSym(acHuffTable)) == 9999) { return gFalse; } // ZRL if (c == 0xf0) { k = 0; while (k < 16 && i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] == 0) { ++k; } else { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } // EOB run } else if ((c & 0x0f) == 0x00) { j = c >> 4; eobRun = 0; for (k = 0; k < j; ++k) { if ((bit = readBit()) == EOF) { return gFalse; } eobRun = (eobRun << 1) | bit; } eobRun += 1 << j; while (i <= scanInfo.lastCoeff) { j = dctZigZag[i++]; if (data[j] != 0) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } } } --eobRun; break; // zero run and one AC coefficient } else { run = (c >> 4) & 0x0f; size = c & 0x0f; if ((amp = readAmp(size)) == 9999) { return gFalse; } j = 0; // make gcc happy for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) { j = dctZigZag[i++]; while (data[j] != 0 && i <= scanInfo.lastCoeff) { if ((bit = readBit()) == EOF) { return gFalse; } if (bit) { if (data[j] >= 0) { data[j] += 1 << scanInfo.al; } else { data[j] -= 1 << scanInfo.al; } } j = dctZigZag[i++]; } } data[j] = amp << scanInfo.al; } } return gTrue; } // Decode a progressive JPEG image. void DCTStream::decodeImage() { int dataIn[64]; Guchar dataOut[64]; Gushort *quantTable; int pY, pCb, pCr, pR, pG, pB; int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i; int h, v, horiz, vert, hSub, vSub; int *p0, *p1, *p2; for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) { for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) { for (cc = 0; cc < numComps; ++cc) { quantTable = quantTables[compInfo[cc].quantTable]; h = compInfo[cc].hSample; v = compInfo[cc].vSample; horiz = mcuWidth / h; vert = mcuHeight / v; hSub = horiz / 8; vSub = vert / 8; for (y2 = 0; y2 < mcuHeight; y2 += vert) { for (x2 = 0; x2 < mcuWidth; x2 += horiz) { // pull out the coded data unit p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { dataIn[i] = p1[0]; dataIn[i+1] = p1[1]; dataIn[i+2] = p1[2]; dataIn[i+3] = p1[3]; dataIn[i+4] = p1[4]; dataIn[i+5] = p1[5]; dataIn[i+6] = p1[6]; dataIn[i+7] = p1[7]; p1 += bufWidth * vSub; } // transform transformDataUnit(quantTable, dataIn, dataOut); // store back into frameBuf, doing replication for // subsampled components p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)]; if (hSub == 1 && vSub == 1) { for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) { p1[0] = dataOut[i] & 0xff; p1[1] = dataOut[i+1] & 0xff; p1[2] = dataOut[i+2] & 0xff; p1[3] = dataOut[i+3] & 0xff; p1[4] = dataOut[i+4] & 0xff; p1[5] = dataOut[i+5] & 0xff; p1[6] = dataOut[i+6] & 0xff; p1[7] = dataOut[i+7] & 0xff; p1 += bufWidth; } } else if (hSub == 2 && vSub == 2) { p2 = p1 + bufWidth; for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) { p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff; p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff; p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff; p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff; p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff; p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff; p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff; p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff; p1 += bufWidth * 2; p2 += bufWidth * 2; } } else { i = 0; for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) { for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) { p2 = p1 + x4; for (y5 = 0; y5 < vSub; ++y5) { for (x5 = 0; x5 < hSub; ++x5) { p2[x5] = dataOut[i] & 0xff; } p2 += bufWidth; } ++i; } p1 += bufWidth * vSub; } } } } } // color space conversion if (colorXform) { // convert YCbCr to RGB if (numComps == 3) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = dctClip(pB); } } // convert YCbCrK to CMYK (K is passed through unchanged) } else if (numComps == 4) { for (y2 = 0; y2 < mcuHeight; ++y2) { p0 = &frameBuf[0][(y1+y2) * bufWidth + x1]; p1 = &frameBuf[1][(y1+y2) * bufWidth + x1]; p2 = &frameBuf[2][(y1+y2) * bufWidth + x1]; for (x2 = 0; x2 < mcuWidth; ++x2) { pY = *p0; pCb = *p1 - 128; pCr = *p2 - 128; pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16; *p0++ = 255 - dctClip(pR); pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16; *p1++ = 255 - dctClip(pG); pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16; *p2++ = 255 - dctClip(pB); } } } } } } } // Transform one data unit -- this performs the dequantization and // IDCT steps. This IDCT algorithm is taken from: // Y. A. Reznik, A. T. Hinds, L. Yu, Z. Ni, and C-X. Zhang, // "Efficient fixed-point approximations of the 8x8 inverse discrete // cosine transform" (invited paper), Proc. SPIE Vol. 6696, Sep. 24, // 2007. // which is based on: // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, // "Practical Fast 1-D DCT Algorithms with 11 Multiplications", // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, // 988-991. // The stage numbers mentioned in the comments refer to Figure 1 in the // Loeffler paper. void DCTStream::transformDataUnit(Gushort *quantTable, int dataIn[64], Guchar dataOut[64]) { int v0, v1, v2, v3, v4, v5, v6, v7; int t0, t1, t2, t3, t4, t5, t6, t7; int *p, *scale; Gushort *q; int i; // dequant; inverse DCT on rows for (i = 0; i < 64; i += 8) { p = dataIn + i; q = quantTable + i; scale = idctScaleMat + i; // check for all-zero AC coefficients if (p[1] == 0 && p[2] == 0 && p[3] == 0 && p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) { t0 = p[0] * q[0] * scale[0]; if (i == 0) { t0 += 1 << 12; // rounding bias } p[0] = t0; p[1] = t0; p[2] = t0; p[3] = t0; p[4] = t0; p[5] = t0; p[6] = t0; p[7] = t0; continue; } // stage 4 v0 = p[0] * q[0] * scale[0]; if (i == 0) { v0 += 1 << 12; // rounding bias } v1 = p[4] * q[4] * scale[4]; v2 = p[2] * q[2] * scale[2]; v3 = p[6] * q[6] * scale[6]; t0 = p[1] * q[1] * scale[1]; t1 = p[7] * q[7] * scale[7]; v4 = t0 - t1; v7 = t0 + t1; v5 = p[3] * q[3] * scale[3]; v6 = p[5] * q[5] * scale[5]; // stage 3 t0 = v0 - v1; v0 = v0 + v1; v1 = t0; t0 = v2 + (v2 >> 5); t1 = t0 >> 2; t2 = t1 + (v2 >> 4); // 41/128 * v2 t3 = t0 - t1; // 99/128 * v2 t4 = v3 + (v3 >> 5); t5 = t4 >> 2; t6 = t5 + (v3 >> 4); // 41/128 * v3 t7 = t4 - t5; // 99/128 * v3 v2 = t2 - t7; v3 = t3 + t6; t0 = v4 - v6; v4 = v4 + v6; v6 = t0; t0 = v7 + v5; v5 = v7 - v5; v7 = t0; // stage 2 t0 = v0 - v3; v0 = v0 + v3; v3 = t0; t0 = v1 - v2; v1 = v1 + v2; v2 = t0; t0 = (v4 >> 9) - v4; t1 = v4 >> 1; // 1/2 * v4 t2 = (t0 >> 2) - t0; // 1533/2048 * v4 t3 = (v7 >> 9) - v7; t4 = v7 >> 1; // 1/2 * v7 t5 = (t3 >> 2) - t3; // 1533/2048 * v7 v4 = t2 - t4; v7 = t1 + t5; t0 = (v5 >> 3) - (v5 >> 7); t1 = t0 - (v5 >> 11); t2 = t0 + (t1 >> 1); // 719/4096 * v5 t3 = v5 - t0; // 113/256 * v5 t4 = (v6 >> 3) - (v6 >> 7); t5 = t4 - (v6 >> 11); t6 = t4 + (t5 >> 1); // 719/4096 * v6 t7 = v6 - t4; // 113/256 * v6 v5 = t3 - t6; v6 = t2 + t7; // stage 1 p[0] = v0 + v7; p[7] = v0 - v7; p[1] = v1 + v6; p[6] = v1 - v6; p[2] = v2 + v5; p[5] = v2 - v5; p[3] = v3 + v4; p[4] = v3 - v4; } // inverse DCT on columns for (i = 0; i < 8; ++i) { p = dataIn + i; // check for all-zero AC coefficients if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 && p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) { t0 = p[0*8]; p[1*8] = t0; p[2*8] = t0; p[3*8] = t0; p[4*8] = t0; p[5*8] = t0; p[6*8] = t0; p[7*8] = t0; continue; } // stage 4 v0 = p[0*8]; v1 = p[4*8]; v2 = p[2*8]; v3 = p[6*8]; t0 = p[1*8]; t1 = p[7*8]; v4 = t0 - t1; v7 = t0 + t1; v5 = p[3*8]; v6 = p[5*8]; // stage 3 t0 = v0 - v1; v0 = v0 + v1; v1 = t0; t0 = v2 + (v2 >> 5); t1 = t0 >> 2; t2 = t1 + (v2 >> 4); // 41/128 * v2 t3 = t0 - t1; // 99/128 * v2 t4 = v3 + (v3 >> 5); t5 = t4 >> 2; t6 = t5 + (v3 >> 4); // 41/128 * v3 t7 = t4 - t5; // 99/128 * v3 v2 = t2 - t7; v3 = t3 + t6; t0 = v4 - v6; v4 = v4 + v6; v6 = t0; t0 = v7 + v5; v5 = v7 - v5; v7 = t0; // stage 2 t0 = v0 - v3; v0 = v0 + v3; v3 = t0; t0 = v1 - v2; v1 = v1 + v2; v2 = t0; t0 = (v4 >> 9) - v4; t1 = v4 >> 1; // 1/2 * v4 t2 = (t0 >> 2) - t0; // 1533/2048 * v4 t3 = (v7 >> 9) - v7; t4 = v7 >> 1; // 1/2 * v7 t5 = (t3 >> 2) - t3; // 1533/2048 * v7 v4 = t2 - t4; v7 = t1 + t5; t0 = (v5 >> 3) - (v5 >> 7); t1 = t0 - (v5 >> 11); t2 = t0 + (t1 >> 1); // 719/4096 * v5 t3 = v5 - t0; // 113/256 * v5 t4 = (v6 >> 3) - (v6 >> 7); t5 = t4 - (v6 >> 11); t6 = t4 + (t5 >> 1); // 719/4096 * v6 t7 = v6 - t4; // 113/256 * v6 v5 = t3 - t6; v6 = t2 + t7; // stage 1 p[0*8] = v0 + v7; p[7*8] = v0 - v7; p[1*8] = v1 + v6; p[6*8] = v1 - v6; p[2*8] = v2 + v5; p[5*8] = v2 - v5; p[3*8] = v3 + v4; p[4*8] = v3 - v4; } // convert to 8-bit integers for (i = 0; i < 64; ++i) { dataOut[i] = dctClip(128 + (dataIn[i] >> 13)); } } int DCTStream::readHuffSym(DCTHuffTable *table) { Gushort code; int bit; int codeBits; code = 0; codeBits = 0; do { // add a bit to the code if ((bit = readBit()) == EOF) { return 9999; } code = (Gushort)((code << 1) + bit); ++codeBits; // look up code if (code < table->firstCode[codeBits]) { break; } if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) { code = (Gushort)(code - table->firstCode[codeBits]); return table->sym[table->firstSym[codeBits] + code]; } } while (codeBits < 16); error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream"); return 9999; } int DCTStream::readAmp(int size) { int amp, bit; int bits; amp = 0; for (bits = 0; bits < size; ++bits) { if ((bit = readBit()) == EOF) return 9999; amp = (amp << 1) + bit; } if (amp < (1 << (size - 1))) amp -= (1 << size) - 1; return amp; } int DCTStream::readBit() { int bit; int c, c2; if (inputBits == 0) { if ((c = str->getChar()) == EOF) return EOF; if (c == 0xff) { do { c2 = str->getChar(); } while (c2 == 0xff); if (c2 != 0x00) { error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff"); return EOF; } } inputBuf = c; inputBits = 8; } bit = (inputBuf >> (inputBits - 1)) & 1; --inputBits; return bit; } GBool DCTStream::readHeader(GBool frame) { GBool doScan; int n; int c = 0; // read headers doScan = gFalse; while (!doScan) { c = readMarker(); switch (c) { case 0xc0: // SOF0 (sequential) case 0xc1: // SOF1 (extended sequential) if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readBaselineSOF()) { return gFalse; } break; case 0xc2: // SOF2 (progressive) if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readProgressiveSOF()) { return gFalse; } break; case 0xc4: // DHT if (!readHuffmanTables()) { return gFalse; } break; case 0xd8: // SOI if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } break; case 0xd9: // EOI return gFalse; case 0xda: // SOS if (!readScanInfo()) { return gFalse; } doScan = gTrue; break; case 0xdb: // DQT if (!readQuantTables()) { return gFalse; } break; case 0xdd: // DRI if (!readRestartInterval()) { return gFalse; } break; case 0xe0: // APP0 if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readJFIFMarker()) { return gFalse; } break; case 0xee: // APP14 if (!frame) { error(errSyntaxError, getPos(), "Invalid DCT marker in scan <{0:02x}>", c); return gFalse; } if (!readAdobeMarker()) { return gFalse; } break; case EOF: error(errSyntaxError, getPos(), "Bad DCT header"); return gFalse; default: // skip APPn / COM / etc. if (c >= 0xe0) { n = read16() - 2; str->discardChars(n); } else { error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c); return gFalse; } break; } } return gTrue; } GBool DCTStream::readBaselineSOF() { int prec; int i; int c; read16(); // length prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return gFalse; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return gFalse; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return gFalse; } } progressive = gFalse; return gTrue; } GBool DCTStream::readProgressiveSOF() { int prec; int i; int c; read16(); // length prec = str->getChar(); height = read16(); width = read16(); numComps = str->getChar(); if (numComps <= 0 || numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); numComps = 0; return gFalse; } if (prec != 8) { error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec); return gFalse; } for (i = 0; i < numComps; ++i) { compInfo[i].id = str->getChar(); c = str->getChar(); compInfo[i].hSample = (c >> 4) & 0x0f; compInfo[i].vSample = c & 0x0f; compInfo[i].quantTable = str->getChar(); if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || compInfo[i].vSample < 1 || compInfo[i].vSample > 4) { error(errSyntaxError, getPos(), "Bad DCT sampling factor"); return gFalse; } if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) { error(errSyntaxError, getPos(), "Bad DCT quant table selector"); return gFalse; } } progressive = gTrue; return gTrue; } GBool DCTStream::readScanInfo() { int length; int id, c; int i, j; length = read16() - 2; scanInfo.numComps = str->getChar(); if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) { error(errSyntaxError, getPos(), "Bad number of components in DCT stream"); scanInfo.numComps = 0; return gFalse; } --length; if (length != 2 * scanInfo.numComps + 3) { error(errSyntaxError, getPos(), "Bad DCT scan info block"); return gFalse; } interleaved = scanInfo.numComps == numComps; for (j = 0; j < numComps; ++j) { scanInfo.comp[j] = gFalse; } for (i = 0; i < scanInfo.numComps; ++i) { id = str->getChar(); // some (broken) DCT streams reuse ID numbers, but at least they // keep the components in order, so we check compInfo[i] first to // work around the problem if (id == compInfo[i].id) { j = i; } else { for (j = 0; j < numComps; ++j) { if (id == compInfo[j].id) { break; } } if (j == numComps) { error(errSyntaxError, getPos(), "Bad DCT component ID in scan info block"); return gFalse; } } if (scanInfo.comp[j]) { error(errSyntaxError, getPos(), "Invalid DCT component ID in scan info block"); return gFalse; } scanInfo.comp[j] = gTrue; c = str->getChar(); scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f; scanInfo.acHuffTable[j] = c & 0x0f; } scanInfo.firstCoeff = str->getChar(); scanInfo.lastCoeff = str->getChar(); if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 || scanInfo.firstCoeff > scanInfo.lastCoeff) { error(errSyntaxError, getPos(), "Bad DCT coefficient numbers in scan info block"); return gFalse; } c = str->getChar(); scanInfo.ah = (c >> 4) & 0x0f; scanInfo.al = c & 0x0f; return gTrue; } GBool DCTStream::readQuantTables() { int length, prec, i, index; length = read16() - 2; while (length > 0) { index = str->getChar(); prec = (index >> 4) & 0x0f; index &= 0x0f; if (prec > 1 || index >= 4) { error(errSyntaxError, getPos(), "Bad DCT quantization table"); return gFalse; } if (index == numQuantTables) { numQuantTables = index + 1; } for (i = 0; i < 64; ++i) { if (prec) { quantTables[index][dctZigZag[i]] = (Gushort)read16(); } else { quantTables[index][dctZigZag[i]] = (Gushort)str->getChar(); } } if (prec) { length -= 129; } else { length -= 65; } } return gTrue; } GBool DCTStream::readHuffmanTables() { DCTHuffTable *tbl; int length; int index; Gushort code; Guchar sym; int i; int c; length = read16() - 2; while (length > 0) { index = str->getChar(); --length; if ((index & 0x0f) >= 4) { error(errSyntaxError, getPos(), "Bad DCT Huffman table"); return gFalse; } if (index & 0x10) { index &= 0x0f; if (index >= numACHuffTables) numACHuffTables = index+1; tbl = &acHuffTables[index]; } else { index &= 0x0f; if (index >= numDCHuffTables) numDCHuffTables = index+1; tbl = &dcHuffTables[index]; } sym = 0; code = 0; for (i = 1; i <= 16; ++i) { c = str->getChar(); tbl->firstSym[i] = sym; tbl->firstCode[i] = code; tbl->numCodes[i] = (Gushort)c; sym = (Guchar)(sym + c); code = (Gushort)((code + c) << 1); } length -= 16; for (i = 0; i < sym; ++i) tbl->sym[i] = (Guchar)str->getChar(); length -= sym; } return gTrue; } GBool DCTStream::readRestartInterval() { int length; length = read16(); if (length != 4) { error(errSyntaxError, getPos(), "Bad DCT restart interval"); return gFalse; } restartInterval = read16(); return gTrue; } GBool DCTStream::readJFIFMarker() { int length, i; char buf[5]; int c; length = read16(); length -= 2; if (length >= 5) { for (i = 0; i < 5; ++i) { if ((c = str->getChar()) == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return gFalse; } buf[i] = (char)c; } length -= 5; if (!memcmp(buf, "JFIF\0", 5)) { gotJFIFMarker = gTrue; } } while (length > 0) { if (str->getChar() == EOF) { error(errSyntaxError, getPos(), "Bad DCT APP0 marker"); return gFalse; } --length; } return gTrue; } GBool DCTStream::readAdobeMarker() { int length, i; char buf[12]; int c; length = read16(); if (length < 14) { goto err; } for (i = 0; i < 12; ++i) { if ((c = str->getChar()) == EOF) { goto err; } buf[i] = (char)c; } if (!strncmp(buf, "Adobe", 5)) { colorXform = buf[11]; gotAdobeMarker = gTrue; } for (i = 14; i < length; ++i) { if (str->getChar() == EOF) { goto err; } } return gTrue; err: error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker"); return gFalse; } GBool DCTStream::readTrailer() { int c; c = readMarker(); if (c != 0xd9) { // EOI error(errSyntaxError, getPos(), "Bad DCT trailer"); return gFalse; } return gTrue; } int DCTStream::readMarker() { int c; do { do { c = str->getChar(); } while (c != 0xff && c != EOF); do { c = str->getChar(); } while (c == 0xff); } while (c == 0x00); return c; } int DCTStream::read16() { int c1, c2; if ((c1 = str->getChar()) == EOF) return EOF; if ((c2 = str->getChar()) == EOF) return EOF; return (c1 << 8) + c2; } #endif // HAVE_JPEGLIB GString *DCTStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 2) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< >> /DCTDecode filter\n"); return s; } GBool DCTStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = { {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8}, {0, 9}, {0, 10}, {1, 11}, {1, 13}, {1, 15}, {1, 17}, {2, 19}, {2, 23}, {2, 27}, {2, 31}, {3, 35}, {3, 43}, {3, 51}, {3, 59}, {4, 67}, {4, 83}, {4, 99}, {4, 115}, {5, 131}, {5, 163}, {5, 195}, {5, 227}, {0, 258}, {0, 258}, {0, 258} }; FlateDecode FlateStream::distDecode[flateMaxDistCodes] = { { 0, 1}, { 0, 2}, { 0, 3}, { 0, 4}, { 1, 5}, { 1, 7}, { 2, 9}, { 2, 13}, { 3, 17}, { 3, 25}, { 4, 33}, { 4, 49}, { 5, 65}, { 5, 97}, { 6, 129}, { 6, 193}, { 7, 257}, { 7, 385}, { 8, 513}, { 8, 769}, { 9, 1025}, { 9, 1537}, {10, 2049}, {10, 3073}, {11, 4097}, {11, 6145}, {12, 8193}, {12, 12289}, {13, 16385}, {13, 24577} }; static FlateCode flateFixedLitCodeTabCodes[512] = { {7, 0x0100}, {8, 0x0050}, {8, 0x0010}, {8, 0x0118}, {7, 0x0110}, {8, 0x0070}, {8, 0x0030}, {9, 0x00c0}, {7, 0x0108}, {8, 0x0060}, {8, 0x0020}, {9, 0x00a0}, {8, 0x0000}, {8, 0x0080}, {8, 0x0040}, {9, 0x00e0}, {7, 0x0104}, {8, 0x0058}, {8, 0x0018}, {9, 0x0090}, {7, 0x0114}, {8, 0x0078}, {8, 0x0038}, {9, 0x00d0}, {7, 0x010c}, {8, 0x0068}, {8, 0x0028}, {9, 0x00b0}, {8, 0x0008}, {8, 0x0088}, {8, 0x0048}, {9, 0x00f0}, {7, 0x0102}, {8, 0x0054}, {8, 0x0014}, {8, 0x011c}, {7, 0x0112}, {8, 0x0074}, {8, 0x0034}, {9, 0x00c8}, {7, 0x010a}, {8, 0x0064}, {8, 0x0024}, {9, 0x00a8}, {8, 0x0004}, {8, 0x0084}, {8, 0x0044}, {9, 0x00e8}, {7, 0x0106}, {8, 0x005c}, {8, 0x001c}, {9, 0x0098}, {7, 0x0116}, {8, 0x007c}, {8, 0x003c}, {9, 0x00d8}, {7, 0x010e}, {8, 0x006c}, {8, 0x002c}, {9, 0x00b8}, {8, 0x000c}, {8, 0x008c}, {8, 0x004c}, {9, 0x00f8}, {7, 0x0101}, {8, 0x0052}, {8, 0x0012}, {8, 0x011a}, {7, 0x0111}, {8, 0x0072}, {8, 0x0032}, {9, 0x00c4}, {7, 0x0109}, {8, 0x0062}, {8, 0x0022}, {9, 0x00a4}, {8, 0x0002}, {8, 0x0082}, {8, 0x0042}, {9, 0x00e4}, {7, 0x0105}, {8, 0x005a}, {8, 0x001a}, {9, 0x0094}, {7, 0x0115}, {8, 0x007a}, {8, 0x003a}, {9, 0x00d4}, {7, 0x010d}, {8, 0x006a}, {8, 0x002a}, {9, 0x00b4}, {8, 0x000a}, {8, 0x008a}, {8, 0x004a}, {9, 0x00f4}, {7, 0x0103}, {8, 0x0056}, {8, 0x0016}, {8, 0x011e}, {7, 0x0113}, {8, 0x0076}, {8, 0x0036}, {9, 0x00cc}, {7, 0x010b}, {8, 0x0066}, {8, 0x0026}, {9, 0x00ac}, {8, 0x0006}, {8, 0x0086}, {8, 0x0046}, {9, 0x00ec}, {7, 0x0107}, {8, 0x005e}, {8, 0x001e}, {9, 0x009c}, {7, 0x0117}, {8, 0x007e}, {8, 0x003e}, {9, 0x00dc}, {7, 0x010f}, {8, 0x006e}, {8, 0x002e}, {9, 0x00bc}, {8, 0x000e}, {8, 0x008e}, {8, 0x004e}, {9, 0x00fc}, {7, 0x0100}, {8, 0x0051}, {8, 0x0011}, {8, 0x0119}, {7, 0x0110}, {8, 0x0071}, {8, 0x0031}, {9, 0x00c2}, {7, 0x0108}, {8, 0x0061}, {8, 0x0021}, {9, 0x00a2}, {8, 0x0001}, {8, 0x0081}, {8, 0x0041}, {9, 0x00e2}, {7, 0x0104}, {8, 0x0059}, {8, 0x0019}, {9, 0x0092}, {7, 0x0114}, {8, 0x0079}, {8, 0x0039}, {9, 0x00d2}, {7, 0x010c}, {8, 0x0069}, {8, 0x0029}, {9, 0x00b2}, {8, 0x0009}, {8, 0x0089}, {8, 0x0049}, {9, 0x00f2}, {7, 0x0102}, {8, 0x0055}, {8, 0x0015}, {8, 0x011d}, {7, 0x0112}, {8, 0x0075}, {8, 0x0035}, {9, 0x00ca}, {7, 0x010a}, {8, 0x0065}, {8, 0x0025}, {9, 0x00aa}, {8, 0x0005}, {8, 0x0085}, {8, 0x0045}, {9, 0x00ea}, {7, 0x0106}, {8, 0x005d}, {8, 0x001d}, {9, 0x009a}, {7, 0x0116}, {8, 0x007d}, {8, 0x003d}, {9, 0x00da}, {7, 0x010e}, {8, 0x006d}, {8, 0x002d}, {9, 0x00ba}, {8, 0x000d}, {8, 0x008d}, {8, 0x004d}, {9, 0x00fa}, {7, 0x0101}, {8, 0x0053}, {8, 0x0013}, {8, 0x011b}, {7, 0x0111}, {8, 0x0073}, {8, 0x0033}, {9, 0x00c6}, {7, 0x0109}, {8, 0x0063}, {8, 0x0023}, {9, 0x00a6}, {8, 0x0003}, {8, 0x0083}, {8, 0x0043}, {9, 0x00e6}, {7, 0x0105}, {8, 0x005b}, {8, 0x001b}, {9, 0x0096}, {7, 0x0115}, {8, 0x007b}, {8, 0x003b}, {9, 0x00d6}, {7, 0x010d}, {8, 0x006b}, {8, 0x002b}, {9, 0x00b6}, {8, 0x000b}, {8, 0x008b}, {8, 0x004b}, {9, 0x00f6}, {7, 0x0103}, {8, 0x0057}, {8, 0x0017}, {8, 0x011f}, {7, 0x0113}, {8, 0x0077}, {8, 0x0037}, {9, 0x00ce}, {7, 0x010b}, {8, 0x0067}, {8, 0x0027}, {9, 0x00ae}, {8, 0x0007}, {8, 0x0087}, {8, 0x0047}, {9, 0x00ee}, {7, 0x0107}, {8, 0x005f}, {8, 0x001f}, {9, 0x009e}, {7, 0x0117}, {8, 0x007f}, {8, 0x003f}, {9, 0x00de}, {7, 0x010f}, {8, 0x006f}, {8, 0x002f}, {9, 0x00be}, {8, 0x000f}, {8, 0x008f}, {8, 0x004f}, {9, 0x00fe}, {7, 0x0100}, {8, 0x0050}, {8, 0x0010}, {8, 0x0118}, {7, 0x0110}, {8, 0x0070}, {8, 0x0030}, {9, 0x00c1}, {7, 0x0108}, {8, 0x0060}, {8, 0x0020}, {9, 0x00a1}, {8, 0x0000}, {8, 0x0080}, {8, 0x0040}, {9, 0x00e1}, {7, 0x0104}, {8, 0x0058}, {8, 0x0018}, {9, 0x0091}, {7, 0x0114}, {8, 0x0078}, {8, 0x0038}, {9, 0x00d1}, {7, 0x010c}, {8, 0x0068}, {8, 0x0028}, {9, 0x00b1}, {8, 0x0008}, {8, 0x0088}, {8, 0x0048}, {9, 0x00f1}, {7, 0x0102}, {8, 0x0054}, {8, 0x0014}, {8, 0x011c}, {7, 0x0112}, {8, 0x0074}, {8, 0x0034}, {9, 0x00c9}, {7, 0x010a}, {8, 0x0064}, {8, 0x0024}, {9, 0x00a9}, {8, 0x0004}, {8, 0x0084}, {8, 0x0044}, {9, 0x00e9}, {7, 0x0106}, {8, 0x005c}, {8, 0x001c}, {9, 0x0099}, {7, 0x0116}, {8, 0x007c}, {8, 0x003c}, {9, 0x00d9}, {7, 0x010e}, {8, 0x006c}, {8, 0x002c}, {9, 0x00b9}, {8, 0x000c}, {8, 0x008c}, {8, 0x004c}, {9, 0x00f9}, {7, 0x0101}, {8, 0x0052}, {8, 0x0012}, {8, 0x011a}, {7, 0x0111}, {8, 0x0072}, {8, 0x0032}, {9, 0x00c5}, {7, 0x0109}, {8, 0x0062}, {8, 0x0022}, {9, 0x00a5}, {8, 0x0002}, {8, 0x0082}, {8, 0x0042}, {9, 0x00e5}, {7, 0x0105}, {8, 0x005a}, {8, 0x001a}, {9, 0x0095}, {7, 0x0115}, {8, 0x007a}, {8, 0x003a}, {9, 0x00d5}, {7, 0x010d}, {8, 0x006a}, {8, 0x002a}, {9, 0x00b5}, {8, 0x000a}, {8, 0x008a}, {8, 0x004a}, {9, 0x00f5}, {7, 0x0103}, {8, 0x0056}, {8, 0x0016}, {8, 0x011e}, {7, 0x0113}, {8, 0x0076}, {8, 0x0036}, {9, 0x00cd}, {7, 0x010b}, {8, 0x0066}, {8, 0x0026}, {9, 0x00ad}, {8, 0x0006}, {8, 0x0086}, {8, 0x0046}, {9, 0x00ed}, {7, 0x0107}, {8, 0x005e}, {8, 0x001e}, {9, 0x009d}, {7, 0x0117}, {8, 0x007e}, {8, 0x003e}, {9, 0x00dd}, {7, 0x010f}, {8, 0x006e}, {8, 0x002e}, {9, 0x00bd}, {8, 0x000e}, {8, 0x008e}, {8, 0x004e}, {9, 0x00fd}, {7, 0x0100}, {8, 0x0051}, {8, 0x0011}, {8, 0x0119}, {7, 0x0110}, {8, 0x0071}, {8, 0x0031}, {9, 0x00c3}, {7, 0x0108}, {8, 0x0061}, {8, 0x0021}, {9, 0x00a3}, {8, 0x0001}, {8, 0x0081}, {8, 0x0041}, {9, 0x00e3}, {7, 0x0104}, {8, 0x0059}, {8, 0x0019}, {9, 0x0093}, {7, 0x0114}, {8, 0x0079}, {8, 0x0039}, {9, 0x00d3}, {7, 0x010c}, {8, 0x0069}, {8, 0x0029}, {9, 0x00b3}, {8, 0x0009}, {8, 0x0089}, {8, 0x0049}, {9, 0x00f3}, {7, 0x0102}, {8, 0x0055}, {8, 0x0015}, {8, 0x011d}, {7, 0x0112}, {8, 0x0075}, {8, 0x0035}, {9, 0x00cb}, {7, 0x010a}, {8, 0x0065}, {8, 0x0025}, {9, 0x00ab}, {8, 0x0005}, {8, 0x0085}, {8, 0x0045}, {9, 0x00eb}, {7, 0x0106}, {8, 0x005d}, {8, 0x001d}, {9, 0x009b}, {7, 0x0116}, {8, 0x007d}, {8, 0x003d}, {9, 0x00db}, {7, 0x010e}, {8, 0x006d}, {8, 0x002d}, {9, 0x00bb}, {8, 0x000d}, {8, 0x008d}, {8, 0x004d}, {9, 0x00fb}, {7, 0x0101}, {8, 0x0053}, {8, 0x0013}, {8, 0x011b}, {7, 0x0111}, {8, 0x0073}, {8, 0x0033}, {9, 0x00c7}, {7, 0x0109}, {8, 0x0063}, {8, 0x0023}, {9, 0x00a7}, {8, 0x0003}, {8, 0x0083}, {8, 0x0043}, {9, 0x00e7}, {7, 0x0105}, {8, 0x005b}, {8, 0x001b}, {9, 0x0097}, {7, 0x0115}, {8, 0x007b}, {8, 0x003b}, {9, 0x00d7}, {7, 0x010d}, {8, 0x006b}, {8, 0x002b}, {9, 0x00b7}, {8, 0x000b}, {8, 0x008b}, {8, 0x004b}, {9, 0x00f7}, {7, 0x0103}, {8, 0x0057}, {8, 0x0017}, {8, 0x011f}, {7, 0x0113}, {8, 0x0077}, {8, 0x0037}, {9, 0x00cf}, {7, 0x010b}, {8, 0x0067}, {8, 0x0027}, {9, 0x00af}, {8, 0x0007}, {8, 0x0087}, {8, 0x0047}, {9, 0x00ef}, {7, 0x0107}, {8, 0x005f}, {8, 0x001f}, {9, 0x009f}, {7, 0x0117}, {8, 0x007f}, {8, 0x003f}, {9, 0x00df}, {7, 0x010f}, {8, 0x006f}, {8, 0x002f}, {9, 0x00bf}, {8, 0x000f}, {8, 0x008f}, {8, 0x004f}, {9, 0x00ff} }; FlateHuffmanTab FlateStream::fixedLitCodeTab = { flateFixedLitCodeTabCodes, 9 }; static FlateCode flateFixedDistCodeTabCodes[32] = { {5, 0x0000}, {5, 0x0010}, {5, 0x0008}, {5, 0x0018}, {5, 0x0004}, {5, 0x0014}, {5, 0x000c}, {5, 0x001c}, {5, 0x0002}, {5, 0x0012}, {5, 0x000a}, {5, 0x001a}, {5, 0x0006}, {5, 0x0016}, {5, 0x000e}, {0, 0x0000}, {5, 0x0001}, {5, 0x0011}, {5, 0x0009}, {5, 0x0019}, {5, 0x0005}, {5, 0x0015}, {5, 0x000d}, {5, 0x001d}, {5, 0x0003}, {5, 0x0013}, {5, 0x000b}, {5, 0x001b}, {5, 0x0007}, {5, 0x0017}, {5, 0x000f}, {0, 0x0000} }; FlateHuffmanTab FlateStream::fixedDistCodeTab = { flateFixedDistCodeTabCodes, 5 }; FlateStream::FlateStream(Stream *strA, int predictor, int columns, int colors, int bits): FilterStream(strA) { if (predictor != 1) { pred = new StreamPredictor(this, predictor, columns, colors, bits); if (!pred->isOk()) { delete pred; pred = NULL; } } else { pred = NULL; } litCodeTab.codes = NULL; distCodeTab.codes = NULL; memset(buf, 0, flateWindow); } FlateStream::~FlateStream() { if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(litCodeTab.codes); } if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(distCodeTab.codes); } if (pred) { delete pred; } delete str; } Stream *FlateStream::copy() { if (pred) { return new FlateStream(str->copy(), pred->getPredictor(), pred->getWidth(), pred->getNComps(), pred->getNBits()); } else { return new FlateStream(str->copy(), 1, 0, 0, 0); } } void FlateStream::reset() { int cmf, flg; index = 0; remain = 0; codeBuf = 0; codeSize = 0; compressedBlock = gFalse; endOfBlock = gTrue; eof = gTrue; str->reset(); if (pred) { pred->reset(); } // read header //~ need to look at window size? endOfBlock = eof = gTrue; cmf = str->getChar(); flg = str->getChar(); if (cmf == EOF || flg == EOF) return; if ((cmf & 0x0f) != 0x08) { error(errSyntaxError, getPos(), "Unknown compression method in flate stream"); return; } if ((((cmf << 8) + flg) % 31) != 0) { error(errSyntaxError, getPos(), "Bad FCHECK in flate stream"); return; } if (flg & 0x20) { error(errSyntaxError, getPos(), "FDICT bit set in flate stream"); return; } eof = gFalse; } int FlateStream::getChar() { int c; if (pred) { return pred->getChar(); } while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; index = (index + 1) & flateMask; --remain; return c; } int FlateStream::lookChar() { int c; if (pred) { return pred->lookChar(); } while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; return c; } int FlateStream::getRawChar() { int c; while (remain == 0) { if (endOfBlock && eof) return EOF; readSome(); } c = buf[index]; index = (index + 1) & flateMask; --remain; return c; } int FlateStream::getBlock(char *blk, int size) { int n; if (pred) { return pred->getBlock(blk, size); } n = 0; while (n < size) { if (remain == 0) { if (endOfBlock && eof) { break; } readSome(); } while (remain && n < size) { blk[n++] = buf[index]; index = (index + 1) & flateMask; --remain; } } return n; } GString *FlateStream::getPSFilter(int psLevel, const char *indent) { GString *s; if (psLevel < 3 || pred) { return NULL; } if (!(s = str->getPSFilter(psLevel, indent))) { return NULL; } s->append(indent)->append("<< >> /FlateDecode filter\n"); return s; } GBool FlateStream::isBinary(GBool last) { return str->isBinary(gTrue); } void FlateStream::readSome() { int code1, code2; int len, dist; int i, j, k; int c; if (endOfBlock) { if (!startBlock()) return; } if (compressedBlock) { if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF) goto err; if (code1 < 256) { buf[index] = (Guchar)code1; remain = 1; } else if (code1 == 256) { endOfBlock = gTrue; remain = 0; } else { code1 -= 257; code2 = lengthDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) goto err; len = lengthDecode[code1].first + code2; if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF) goto err; code2 = distDecode[code1].bits; if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF) goto err; dist = distDecode[code1].first + code2; i = index; j = (index - dist) & flateMask; for (k = 0; k < len; ++k) { buf[i] = buf[j]; i = (i + 1) & flateMask; j = (j + 1) & flateMask; } remain = len; } } else { len = (blockLen < flateWindow) ? blockLen : flateWindow; for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) { if ((c = str->getChar()) == EOF) { endOfBlock = eof = gTrue; break; } buf[j] = (Guchar)c; } remain = i; blockLen -= len; if (blockLen == 0) endOfBlock = gTrue; } return; err: error(errSyntaxError, getPos(), "Unexpected end of file in flate stream"); endOfBlock = eof = gTrue; remain = 0; } GBool FlateStream::startBlock() { int blockHdr; int c; int check; // free the code tables from the previous block if (litCodeTab.codes != fixedLitCodeTab.codes) { gfree(litCodeTab.codes); } litCodeTab.codes = NULL; if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(distCodeTab.codes); } distCodeTab.codes = NULL; // read block header blockHdr = getCodeWord(3); if (blockHdr & 1) eof = gTrue; blockHdr >>= 1; // uncompressed block if (blockHdr == 0) { compressedBlock = gFalse; if ((c = str->getChar()) == EOF) goto err; blockLen = c & 0xff; if ((c = str->getChar()) == EOF) goto err; blockLen |= (c & 0xff) << 8; if ((c = str->getChar()) == EOF) goto err; check = c & 0xff; if ((c = str->getChar()) == EOF) goto err; check |= (c & 0xff) << 8; if (check != (~blockLen & 0xffff)) error(errSyntaxError, getPos(), "Bad uncompressed block length in flate stream"); codeBuf = 0; codeSize = 0; // compressed block with fixed codes } else if (blockHdr == 1) { compressedBlock = gTrue; loadFixedCodes(); // compressed block with dynamic codes } else if (blockHdr == 2) { compressedBlock = gTrue; if (!readDynamicCodes()) { goto err; } // unknown block type } else { goto err; } endOfBlock = gFalse; return gTrue; err: error(errSyntaxError, getPos(), "Bad block header in flate stream"); endOfBlock = eof = gTrue; return gFalse; } void FlateStream::loadFixedCodes() { litCodeTab.codes = fixedLitCodeTab.codes; litCodeTab.maxLen = fixedLitCodeTab.maxLen; distCodeTab.codes = fixedDistCodeTab.codes; distCodeTab.maxLen = fixedDistCodeTab.maxLen; } GBool FlateStream::readDynamicCodes() { int numCodeLenCodes; int numLitCodes; int numDistCodes; int codeLenCodeLengths[flateMaxCodeLenCodes]; FlateHuffmanTab codeLenCodeTab; int len, repeat, code; int i; codeLenCodeTab.codes = NULL; // read lengths if ((numLitCodes = getCodeWord(5)) == EOF) { goto err; } numLitCodes += 257; if ((numDistCodes = getCodeWord(5)) == EOF) { goto err; } numDistCodes += 1; if ((numCodeLenCodes = getCodeWord(4)) == EOF) { goto err; } numCodeLenCodes += 4; if (numLitCodes > flateMaxLitCodes || numDistCodes > flateMaxDistCodes || numCodeLenCodes > flateMaxCodeLenCodes) { goto err; } // build the code length code table for (i = 0; i < flateMaxCodeLenCodes; ++i) { codeLenCodeLengths[i] = 0; } for (i = 0; i < numCodeLenCodes; ++i) { if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) { goto err; } } compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab); // build the literal and distance code tables len = 0; repeat = 0; i = 0; while (i < numLitCodes + numDistCodes) { if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) { goto err; } if (code == 16) { if ((repeat = getCodeWord(2)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } for (; repeat > 0; --repeat) { codeLengths[i++] = len; } } else if (code == 17) { if ((repeat = getCodeWord(3)) == EOF) { goto err; } repeat += 3; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else if (code == 18) { if ((repeat = getCodeWord(7)) == EOF) { goto err; } repeat += 11; if (i + repeat > numLitCodes + numDistCodes) { goto err; } len = 0; for (; repeat > 0; --repeat) { codeLengths[i++] = 0; } } else { codeLengths[i++] = len = code; } } compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab); compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab); gfree(codeLenCodeTab.codes); return gTrue; err: error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream"); gfree(codeLenCodeTab.codes); return gFalse; } // Convert an array of lengths, in value order, into a // Huffman code lookup table. void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) { int tabSize, len, code, code2, skip, val, i, t; // find max code length tab->maxLen = 0; for (val = 0; val < n; ++val) { if (lengths[val] > tab->maxLen) { tab->maxLen = lengths[val]; } } // allocate the table tabSize = 1 << tab->maxLen; tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode)); // clear the table for (i = 0; i < tabSize; ++i) { tab->codes[i].len = 0; tab->codes[i].val = 0; } // build the table for (len = 1, code = 0, skip = 2; len <= tab->maxLen; ++len, code <<= 1, skip <<= 1) { for (val = 0; val < n; ++val) { if (lengths[val] == len) { // bit-reverse the code code2 = 0; t = code; for (i = 0; i < len; ++i) { code2 = (code2 << 1) | (t & 1); t >>= 1; } // fill in the table entries for (i = code2; i < tabSize; i += skip) { tab->codes[i].len = (Gushort)len; tab->codes[i].val = (Gushort)val; } ++code; } } } } int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) { FlateCode *code; int c; while (codeSize < tab->maxLen) { if ((c = str->getChar()) == EOF) { break; } codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)]; if (codeSize == 0 || codeSize < code->len || code->len == 0) { return EOF; } codeBuf >>= code->len; codeSize -= code->len; return (int)code->val; } int FlateStream::getCodeWord(int bits) { int c; while (codeSize < bits) { if ((c = str->getChar()) == EOF) return EOF; codeBuf |= (c & 0xff) << codeSize; codeSize += 8; } c = codeBuf & ((1 << bits) - 1); codeBuf >>= bits; codeSize -= bits; return c; } //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ EOFStream::EOFStream(Stream *strA): FilterStream(strA) { } EOFStream::~EOFStream() { delete str; } Stream *EOFStream::copy() { return new EOFStream(str->copy()); } //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ BufStream::BufStream(Stream *strA, int bufSizeA): FilterStream(strA) { bufSize = bufSizeA; buf = (int *)gmallocn(bufSize, sizeof(int)); } BufStream::~BufStream() { gfree(buf); delete str; } Stream *BufStream::copy() { return new BufStream(str->copy(), bufSize); } void BufStream::reset() { int i; str->reset(); for (i = 0; i < bufSize; ++i) { buf[i] = str->getChar(); } } int BufStream::getChar() { int c, i; c = buf[0]; for (i = 1; i < bufSize; ++i) { buf[i-1] = buf[i]; } buf[bufSize - 1] = str->getChar(); return c; } int BufStream::lookChar() { return buf[0]; } int BufStream::lookChar(int idx) { return buf[idx]; } GBool BufStream::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA): FilterStream(strA) { length = lengthA; count = 0; } FixedLengthEncoder::~FixedLengthEncoder() { if (str->isEncoder()) delete str; } Stream *FixedLengthEncoder::copy() { error(errInternal, -1, "Called copy() on FixedLengthEncoder"); return NULL; } void FixedLengthEncoder::reset() { str->reset(); count = 0; } int FixedLengthEncoder::getChar() { if (length >= 0 && count >= length) return EOF; ++count; return str->getChar(); } int FixedLengthEncoder::lookChar() { if (length >= 0 && count >= length) return EOF; return str->getChar(); } GBool FixedLengthEncoder::isBinary(GBool last) { return str->isBinary(gTrue); } //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } ASCIIHexEncoder::~ASCIIHexEncoder() { if (str->isEncoder()) { delete str; } } Stream *ASCIIHexEncoder::copy() { error(errInternal, -1, "Called copy() on ASCIIHexEncoder"); return NULL; } void ASCIIHexEncoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } GBool ASCIIHexEncoder::fillBuf() { static const char *hex = "0123456789abcdef"; int c; if (eof) { return gFalse; } bufPtr = bufEnd = buf; if ((c = str->getChar()) == EOF) { *bufEnd++ = '>'; eof = gTrue; } else { if (lineLen >= 64) { *bufEnd++ = '\n'; lineLen = 0; } *bufEnd++ = hex[(c >> 4) & 0x0f]; *bufEnd++ = hex[c & 0x0f]; lineLen += 2; } return gTrue; } //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ ASCII85Encoder::ASCII85Encoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } ASCII85Encoder::~ASCII85Encoder() { if (str->isEncoder()) delete str; } Stream *ASCII85Encoder::copy() { error(errInternal, -1, "Called copy() on ASCII85Encoder"); return NULL; } void ASCII85Encoder::reset() { str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = gFalse; } GBool ASCII85Encoder::fillBuf() { Guint t; char buf1[5]; int c0, c1, c2, c3; int n, i; if (eof) { return gFalse; } c0 = str->getChar(); c1 = str->getChar(); c2 = str->getChar(); c3 = str->getChar(); bufPtr = bufEnd = buf; if (c3 == EOF) { if (c0 == EOF) { n = 0; t = 0; } else { if (c1 == EOF) { n = 1; t = c0 << 24; } else if (c2 == EOF) { n = 2; t = (c0 << 24) | (c1 << 16); } else { n = 3; t = (c0 << 24) | (c1 << 16) | (c2 << 8); } for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= n; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } *bufEnd++ = '~'; *bufEnd++ = '>'; eof = gTrue; } else { t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; if (t == 0) { *bufEnd++ = 'z'; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } else { for (i = 4; i >= 0; --i) { buf1[i] = (char)(t % 85 + 0x21); t /= 85; } for (i = 0; i <= 4; ++i) { *bufEnd++ = buf1[i]; if (++lineLen == 65) { *bufEnd++ = '\n'; lineLen = 0; } } } } return gTrue; } //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ RunLengthEncoder::RunLengthEncoder(Stream *strA): FilterStream(strA) { bufPtr = bufEnd = nextEnd = buf; eof = gFalse; } RunLengthEncoder::~RunLengthEncoder() { if (str->isEncoder()) delete str; } Stream *RunLengthEncoder::copy() { error(errInternal, -1, "Called copy() on RunLengthEncoder"); return NULL; } void RunLengthEncoder::reset() { str->reset(); bufPtr = bufEnd = nextEnd = buf; eof = gFalse; } // // When fillBuf finishes, buf[] looks like this: // +-----+--------------+-----------------+-- // + tag | ... data ... | next 0, 1, or 2 | // +-----+--------------+-----------------+-- // ^ ^ ^ // bufPtr bufEnd nextEnd // GBool RunLengthEncoder::fillBuf() { int c, c1, c2; int n; // already hit EOF? if (eof) return gFalse; // grab two bytes if (nextEnd < bufEnd + 1) { if ((c1 = str->getChar()) == EOF) { eof = gTrue; return gFalse; } } else { c1 = bufEnd[0] & 0xff; } if (nextEnd < bufEnd + 2) { if ((c2 = str->getChar()) == EOF) { eof = gTrue; buf[0] = 0; buf[1] = (char)c1; bufPtr = buf; bufEnd = &buf[2]; return gTrue; } } else { c2 = bufEnd[1] & 0xff; } // check for repeat c = 0; // make gcc happy if (c1 == c2) { n = 2; while (n < 128 && (c = str->getChar()) == c1) ++n; buf[0] = (char)(257 - n); buf[1] = (char)c1; bufEnd = &buf[2]; if (c == EOF) { eof = gTrue; } else if (n < 128) { buf[2] = (char)c; nextEnd = &buf[3]; } else { nextEnd = bufEnd; } // get up to 128 chars } else { buf[1] = (char)c1; buf[2] = (char)c2; n = 2; while (n < 128) { if ((c = str->getChar()) == EOF) { eof = gTrue; break; } ++n; buf[n] = (char)c; if (buf[n] == buf[n-1]) break; } if (buf[n] == buf[n-1]) { buf[0] = (char)(n-2-1); bufEnd = &buf[n-1]; nextEnd = &buf[n+1]; } else { buf[0] = (char)(n-1); bufEnd = nextEnd = &buf[n+1]; } } bufPtr = buf; return gTrue; } //------------------------------------------------------------------------ // LZWEncoder //------------------------------------------------------------------------ LZWEncoder::LZWEncoder(Stream *strA): FilterStream(strA) { inBufStart = 0; inBufLen = 0; outBufLen = 0; } LZWEncoder::~LZWEncoder() { if (str->isEncoder()) { delete str; } } Stream *LZWEncoder::copy() { error(errInternal, -1, "Called copy() on LZWEncoder"); return NULL; } void LZWEncoder::reset() { int i; str->reset(); // initialize code table for (i = 0; i < 256; ++i) { table[i].byte = i; table[i].next = NULL; table[i].children = NULL; } nextSeq = 258; codeLen = 9; // initialize input buffer inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf)); inBufStart = 0; // initialize output buffer with a clear-table code outBuf = 256; outBufLen = 9; needEOD = gFalse; } int LZWEncoder::getChar() { int ret; if (inBufLen == 0 && !needEOD && outBufLen == 0) { return EOF; } if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { fillBuf(); } if (outBufLen >= 8) { ret = (outBuf >> (outBufLen - 8)) & 0xff; outBufLen -= 8; } else { ret = (outBuf << (8 - outBufLen)) & 0xff; outBufLen = 0; } return ret; } int LZWEncoder::lookChar() { if (inBufLen == 0 && !needEOD && outBufLen == 0) { return EOF; } if (outBufLen < 8 && (inBufLen > 0 || needEOD)) { fillBuf(); } if (outBufLen >= 8) { return (outBuf >> (outBufLen - 8)) & 0xff; } else { return (outBuf << (8 - outBufLen)) & 0xff; } } // On input, outBufLen < 8. // This function generates, at most, 2 12-bit codes // --> outBufLen < 8 + 12 + 12 = 32 void LZWEncoder::fillBuf() { LZWEncoderNode *p0, *p1; int seqLen, code, i; if (needEOD) { outBuf = (outBuf << codeLen) | 257; outBufLen += codeLen; needEOD = gFalse; return; } // find longest matching sequence (if any) p0 = table + inBuf[inBufStart]; seqLen = 1; while (inBufLen > seqLen) { for (p1 = p0->children; p1; p1 = p1->next) { if (p1->byte == inBuf[inBufStart + seqLen]) { break; } } if (!p1) { break; } p0 = p1; ++seqLen; } code = (int)(p0 - table); // generate an output code outBuf = (outBuf << codeLen) | code; outBufLen += codeLen; // update the table table[nextSeq].byte = seqLen < inBufLen ? inBuf[inBufStart + seqLen] : 0; table[nextSeq].children = NULL; if (table[code].children) { table[nextSeq].next = table[code].children; } else { table[nextSeq].next = NULL; } table[code].children = table + nextSeq; ++nextSeq; // update the input buffer inBufStart += seqLen; inBufLen -= seqLen; if (inBufStart >= 4096 && inBufStart + inBufLen == sizeof(inBuf)) { memcpy(inBuf, inBuf + inBufStart, inBufLen); inBufStart = 0; inBufLen += str->getBlock((char *)inBuf + inBufLen, (int)sizeof(inBuf) - inBufLen); } // increment codeLen; generate clear-table code if (nextSeq == (1 << codeLen)) { ++codeLen; if (codeLen == 13) { outBuf = (outBuf << 12) | 256; outBufLen += 12; for (i = 0; i < 256; ++i) { table[i].next = NULL; table[i].children = NULL; } nextSeq = 258; codeLen = 9; } } // generate EOD next time if (inBufLen == 0) { needEOD = gTrue; } } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10025/Stream.h000066400000000000000000001006511417746362400222250ustar00rootroot00000000000000//======================================================================== // // Stream.h // // Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== #ifndef STREAM_H #define STREAM_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include #if HAVE_JPEGLIB #include #include #endif #include "gtypes.h" #include "gfile.h" #include "Object.h" class BaseStream; class SharedFile; //------------------------------------------------------------------------ enum StreamKind { strFile, strASCIIHex, strASCII85, strLZW, strRunLength, strCCITTFax, strDCT, strFlate, strJBIG2, strJPX, strWeird // internal-use stream types }; enum StreamColorSpaceMode { streamCSNone, streamCSDeviceGray, streamCSDeviceRGB, streamCSDeviceCMYK }; //------------------------------------------------------------------------ // This is in Stream.h instead of Decrypt.h to avoid really annoying // include file dependency loops. enum CryptAlgorithm { cryptRC4, cryptAES, cryptAES256 }; //------------------------------------------------------------------------ // Stream (base class) //------------------------------------------------------------------------ class Stream { public: // Constructor. Stream(); // Destructor. virtual ~Stream(); virtual Stream *copy() = 0; // Get kind of stream. virtual StreamKind getKind() = 0; virtual GBool isEmbedStream() { return gFalse; } // Reset stream to beginning. virtual void reset() = 0; // Close down the stream. virtual void close(); // Get next char from stream. virtual int getChar() = 0; // Peek at next char in stream. virtual int lookChar() = 0; // Get next char from stream without using the predictor. // This is only used by StreamPredictor. virtual int getRawChar(); // Get exactly bytes from stream. Returns the number of // bytes read -- the returned count will be less than at EOF. virtual int getBlock(char *blk, int size); // Get next line from stream. virtual char *getLine(char *buf, int size); // Discard the next bytes from stream. Returns the number of // bytes discarded, which will be less than only if EOF is // reached. virtual Guint discardChars(Guint n); // Get current position in file. virtual GFileOffset getPos() = 0; // Go to a position in the stream. If is negative, the // position is from the end of the file; otherwise the position is // from the start of the file. virtual void setPos(GFileOffset pos, int dir = 0) = 0; // Get PostScript command for the filter(s). virtual GString *getPSFilter(int psLevel, const char *indent); // Does this stream type potentially contain non-printable chars? virtual GBool isBinary(GBool last = gTrue) = 0; // Get the BaseStream of this stream. virtual BaseStream *getBaseStream() = 0; // Get the stream after the last decoder (this may be a BaseStream // or a DecryptStream). virtual Stream *getUndecodedStream() = 0; // Get the dictionary associated with this stream. virtual Dict *getDict() = 0; // Is this an encoding filter? virtual GBool isEncoder() { return gFalse; } // Get image parameters which are defined by the stream contents. virtual void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) {} // Return the next stream in the "stack". virtual Stream *getNextStream() { return NULL; } // Add filters to this stream according to the parameters in . // Returns the new stream. Stream *addFilters(Object *dict, int recursion = 0); private: Stream *makeFilter(char *name, Stream *str, Object *params, int recursion); }; //------------------------------------------------------------------------ // BaseStream // // This is the base class for all streams that read directly from a file. //------------------------------------------------------------------------ class BaseStream : public Stream { public: BaseStream(Object *dictA); virtual ~BaseStream(); virtual Stream *makeSubStream(GFileOffset start, GBool limited, GFileOffset length, Object *dict) = 0; virtual void setPos(GFileOffset pos, int dir = 0) = 0; virtual GBool isBinary(GBool last = gTrue) { return last; } virtual BaseStream *getBaseStream() { return this; } virtual Stream *getUndecodedStream() { return this; } virtual Dict *getDict() { return dict.getDict(); } virtual GString *getFileName() { return NULL; } // Get/set position of first byte of stream within the file. virtual GFileOffset getStart() = 0; virtual void moveStart(int delta) = 0; protected: Object dict; }; //------------------------------------------------------------------------ // FilterStream // // This is the base class for all streams that filter another stream. //------------------------------------------------------------------------ class FilterStream : public Stream { public: FilterStream(Stream *strA); virtual ~FilterStream(); virtual void close(); virtual GFileOffset getPos() { return str->getPos(); } virtual void setPos(GFileOffset pos, int dir = 0); virtual BaseStream *getBaseStream() { return str->getBaseStream(); } virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); } virtual Dict *getDict() { return str->getDict(); } virtual Stream *getNextStream() { return str; } protected: Stream *str; }; //------------------------------------------------------------------------ // ImageStream //------------------------------------------------------------------------ class ImageStream { public: // Create an image stream object for an image with the specified // parameters. Note that these are the actual image parameters, // which may be different from the predictor parameters. ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA); ~ImageStream(); // Reset the stream. void reset(); // Close down the stream. void close(); // Gets the next pixel from the stream. should be able to hold // at least nComps elements. Returns false at end of file. GBool getPixel(Guchar *pix); // Returns a pointer to the next line of pixels. Returns NULL at // end of file. Guchar *getLine(); // Skip an entire line from the image. void skipLine(); private: Stream *str; // base stream int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int inputLineSize; // input line buffer size char *inputLine; // input line buffer Guchar *imgLine; // line buffer int imgIdx; // current index in imgLine }; //------------------------------------------------------------------------ // StreamPredictor //------------------------------------------------------------------------ class StreamPredictor { public: // Create a predictor object. Note that the parameters are for the // predictor, and may not match the actual image parameters. StreamPredictor(Stream *strA, int predictorA, int widthA, int nCompsA, int nBitsA); ~StreamPredictor(); GBool isOk() { return ok; } void reset(); int lookChar(); int getChar(); int getBlock(char *blk, int size); int getPredictor() { return predictor; } int getWidth() { return width; } int getNComps() { return nComps; } int getNBits() { return nBits; } private: GBool getNextLine(); Stream *str; // base stream int predictor; // predictor int width; // pixels per line int nComps; // components per pixel int nBits; // bits per component int nVals; // components per line int pixBytes; // bytes per pixel int rowBytes; // bytes per line Guchar *predLine; // line buffer int predIdx; // current index in predLine GBool ok; }; //------------------------------------------------------------------------ // FileStream //------------------------------------------------------------------------ #define fileStreamBufSize 256 class FileStream : public BaseStream { public: FileStream(FILE *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual ~FileStream(); virtual Stream *copy(); virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return strFile; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getBlock(char *blk, int size); virtual GFileOffset getPos() { return bufPos + (int)(bufPtr - buf); } virtual void setPos(GFileOffset pos, int dir = 0); virtual GFileOffset getStart() { return start; } virtual void moveStart(int delta); private: FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, GFileOffset lengthA, Object *dictA); GBool fillBuf(); SharedFile *f; GFileOffset start; GBool limited; GFileOffset length; char buf[fileStreamBufSize]; char *bufPtr; char *bufEnd; GFileOffset bufPos; }; //------------------------------------------------------------------------ // MemStream //------------------------------------------------------------------------ class MemStream : public BaseStream { public: MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA); virtual ~MemStream(); virtual Stream *copy(); virtual Stream *makeSubStream(GFileOffset start, GBool limited, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual void close(); virtual int getChar() { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; } virtual int lookChar() { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; } virtual int getBlock(char *blk, int size); virtual GFileOffset getPos() { return (GFileOffset)(bufPtr - buf); } virtual void setPos(GFileOffset pos, int dir = 0); virtual GFileOffset getStart() { return start; } virtual void moveStart(int delta); private: char *buf; Guint start; Guint length; char *bufEnd; char *bufPtr; GBool needFree; }; //------------------------------------------------------------------------ // EmbedStream // // This is a special stream type used for embedded streams (inline // images). It reads directly from the base stream -- after the // EmbedStream is deleted, reads from the base stream will proceed where // the BaseStream left off. Note that this is very different behavior // that creating a new FileStream (using makeSubStream). //------------------------------------------------------------------------ class EmbedStream : public BaseStream { public: EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA); virtual ~EmbedStream(); virtual Stream *copy(); virtual Stream *makeSubStream(GFileOffset start, GBool limitedA, GFileOffset lengthA, Object *dictA); virtual StreamKind getKind() { return str->getKind(); } virtual GBool isEmbedStream() { return gTrue; } virtual void reset() {} virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); virtual GFileOffset getPos() { return str->getPos(); } virtual void setPos(GFileOffset pos, int dir = 0); virtual GFileOffset getStart(); virtual void moveStart(int delta); private: Stream *str; GBool limited; GFileOffset length; }; //------------------------------------------------------------------------ // ASCIIHexStream //------------------------------------------------------------------------ class ASCIIHexStream : public FilterStream { public: ASCIIHexStream(Stream *strA); virtual ~ASCIIHexStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strASCIIHex; } virtual void reset(); virtual int getChar() { int c = lookChar(); buf = EOF; return c; } virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int buf; GBool eof; }; //------------------------------------------------------------------------ // ASCII85Stream //------------------------------------------------------------------------ class ASCII85Stream : public FilterStream { public: ASCII85Stream(Stream *strA); virtual ~ASCII85Stream(); virtual Stream *copy(); virtual StreamKind getKind() { return strASCII85; } virtual void reset(); virtual int getChar() { int ch = lookChar(); ++index; return ch; } virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int c[5]; int b[4]; int index, n; GBool eof; }; //------------------------------------------------------------------------ // LZWStream //------------------------------------------------------------------------ class LZWStream : public FilterStream { public: LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA); virtual ~LZWStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strLZW; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: StreamPredictor *pred; // predictor int early; // early parameter GBool eof; // true if at eof int inputBuf; // input buffer int inputBits; // number of bits in input buffer struct { // decoding table int length; int head; Guchar tail; } table[4097]; int nextCode; // next code to be used int nextBits; // number of bits in next code word int prevCode; // previous code used in stream int newChar; // next char to be added to table Guchar seqBuf[4097]; // buffer for current sequence int seqLength; // length of current sequence int seqIndex; // index into current sequence GBool first; // first code after a table clear GBool processNextCode(); void clearTable(); int getCode(); }; //------------------------------------------------------------------------ // RunLengthStream //------------------------------------------------------------------------ class RunLengthStream : public FilterStream { public: RunLengthStream(Stream *strA); virtual ~RunLengthStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strRunLength; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: char buf[128]; // buffer char *bufPtr; // next char to read char *bufEnd; // end of buffer GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // CCITTFaxStream //------------------------------------------------------------------------ struct CCITTCodeTable; class CCITTFaxStream : public FilterStream { public: CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, GBool byteAlignA, int columnsA, int rowsA, GBool endOfBlockA, GBool blackA); virtual ~CCITTFaxStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strCCITTFax; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: int encoding; // 'K' parameter GBool endOfLine; // 'EndOfLine' parameter GBool byteAlign; // 'EncodedByteAlign' parameter int columns; // 'Columns' parameter int rows; // 'Rows' parameter GBool endOfBlock; // 'EndOfBlock' parameter GBool black; // 'BlackIs1' parameter int blackXOR; GBool eof; // true if at eof GBool nextLine2D; // true if next line uses 2D encoding int row; // current row Guint inputBuf; // input buffer int inputBits; // number of bits in input buffer int *codingLine; // coding line changing elements int *refLine; // reference line changing elements int nextCol; // next column to read int a0i; // index into codingLine GBool err; // error on current line int nErrors; // number of errors so far in this stream void addPixels(int a1, int blackPixels); void addPixelsNeg(int a1, int blackPixels); GBool readRow(); short getTwoDimCode(); short getWhiteCode(); short getBlackCode(); short lookBits(int n); void eatBits(int n) { if ((inputBits -= n) < 0) inputBits = 0; } }; //------------------------------------------------------------------------ // DCTStream //------------------------------------------------------------------------ #if HAVE_JPEGLIB class DCTStream; #define dctStreamBufSize 4096 struct DCTSourceMgr { jpeg_source_mgr src; DCTStream *str; char buf[dctStreamBufSize]; }; struct DCTErrorMgr { struct jpeg_error_mgr err; jmp_buf setjmpBuf; }; #else // HAVE_JPEGLIB // DCT component info struct DCTCompInfo { int id; // component ID int hSample, vSample; // horiz/vert sampling resolutions int quantTable; // quantization table number int prevDC; // DC coefficient accumulator }; struct DCTScanInfo { GBool comp[4]; // comp[i] is set if component i is // included in this scan int numComps; // number of components in the scan int dcHuffTable[4]; // DC Huffman table numbers int acHuffTable[4]; // AC Huffman table numbers int firstCoeff, lastCoeff; // first and last DCT coefficient int ah, al; // successive approximation parameters }; // DCT Huffman decoding table struct DCTHuffTable { Guchar firstSym[17]; // first symbol for this bit length Gushort firstCode[17]; // first code for this bit length Gushort numCodes[17]; // number of codes of this bit length Guchar sym[256]; // symbols }; #endif // HAVE_JPEGLIB class DCTStream : public FilterStream { public: DCTStream(Stream *strA, int colorXformA); virtual ~DCTStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strDCT; } virtual void reset(); virtual void close(); virtual int getChar(); virtual int lookChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); Stream *getRawStream() { return str; } private: #if HAVE_JPEGLIB int colorXform; // color transform: -1 = unspecified // 0 = none // 1 = YUV/YUVK -> RGB/CMYK jpeg_decompress_struct decomp; DCTErrorMgr errorMgr; DCTSourceMgr sourceMgr; GBool error; char *lineBuf; int lineBufHeight; char *lineBufRows[4]; char *bufPtr; char *bufEnd; GBool inlineImage; GBool fillBuf(); static void errorExit(j_common_ptr d); static void errorMessage(j_common_ptr d); static void initSourceCbk(j_decompress_ptr d); static boolean fillInputBufferCbk(j_decompress_ptr d); static void skipInputDataCbk(j_decompress_ptr d, long numBytes); static void termSourceCbk(j_decompress_ptr d); #else // HAVE_JPEGLIB GBool progressive; // set if in progressive mode GBool interleaved; // set if in interleaved mode int width, height; // image size int mcuWidth, mcuHeight; // size of min coding unit, in data units int bufWidth, bufHeight; // frameBuf size DCTCompInfo compInfo[4]; // info for each component DCTScanInfo scanInfo; // info for the current scan int numComps; // number of components in image int colorXform; // color transform: -1 = unspecified // 0 = none // 1 = YUV/YUVK -> RGB/CMYK GBool gotJFIFMarker; // set if APP0 JFIF marker was present GBool gotAdobeMarker; // set if APP14 Adobe marker was present int restartInterval; // restart interval, in MCUs Gushort quantTables[4][64]; // quantization tables int numQuantTables; // number of quantization tables DCTHuffTable dcHuffTables[4]; // DC Huffman tables DCTHuffTable acHuffTables[4]; // AC Huffman tables int numDCHuffTables; // number of DC Huffman tables int numACHuffTables; // number of AC Huffman tables Guchar *rowBuf; Guchar *rowBufPtr; // current position within rowBuf Guchar *rowBufEnd; // end of valid data in rowBuf int *frameBuf[4]; // buffer for frame (progressive mode) int comp, x, y; // current position within image/MCU int restartCtr; // MCUs left until restart int restartMarker; // next restart marker int eobRun; // number of EOBs left in the current run int inputBuf; // input buffer for variable length codes int inputBits; // number of valid bits in input buffer void restart(); GBool readMCURow(); void readScan(); GBool readDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable, DCTHuffTable *acHuffTable, int *prevDC, int data[64]); void decodeImage(); void transformDataUnit(Gushort *quantTable, int dataIn[64], Guchar dataOut[64]); int readHuffSym(DCTHuffTable *table); int readAmp(int size); int readBit(); GBool readHeader(GBool frame); GBool readBaselineSOF(); GBool readProgressiveSOF(); GBool readScanInfo(); GBool readQuantTables(); GBool readHuffmanTables(); GBool readRestartInterval(); GBool readJFIFMarker(); GBool readAdobeMarker(); GBool readTrailer(); int readMarker(); int read16(); #endif // HAVE_JPEGLIB }; //------------------------------------------------------------------------ // FlateStream //------------------------------------------------------------------------ #define flateWindow 32768 // buffer size #define flateMask (flateWindow-1) #define flateMaxHuffman 15 // max Huffman code length #define flateMaxCodeLenCodes 19 // max # code length codes #define flateMaxLitCodes 288 // max # literal codes #define flateMaxDistCodes 30 // max # distance codes // Huffman code table entry struct FlateCode { Gushort len; // code length, in bits Gushort val; // value represented by this code }; struct FlateHuffmanTab { FlateCode *codes; int maxLen; }; // Decoding info for length and distance code words struct FlateDecode { int bits; // # extra bits int first; // first length/distance }; class FlateStream : public FilterStream { public: FlateStream(Stream *strA, int predictor, int columns, int colors, int bits); virtual ~FlateStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strFlate; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual int getRawChar(); virtual int getBlock(char *blk, int size); virtual GString *getPSFilter(int psLevel, const char *indent); virtual GBool isBinary(GBool last = gTrue); private: StreamPredictor *pred; // predictor Guchar buf[flateWindow]; // output data buffer int index; // current index into output buffer int remain; // number valid bytes in output buffer int codeBuf; // input buffer int codeSize; // number of bits in input buffer int // literal and distance code lengths codeLengths[flateMaxLitCodes + flateMaxDistCodes]; FlateHuffmanTab litCodeTab; // literal code table FlateHuffmanTab distCodeTab; // distance code table GBool compressedBlock; // set if reading a compressed block int blockLen; // remaining length of uncompressed block GBool endOfBlock; // set when end of block is reached GBool eof; // set when end of stream is reached static int // code length code reordering codeLenCodeMap[flateMaxCodeLenCodes]; static FlateDecode // length decoding info lengthDecode[flateMaxLitCodes-257]; static FlateDecode // distance decoding info distDecode[flateMaxDistCodes]; static FlateHuffmanTab // fixed literal code table fixedLitCodeTab; static FlateHuffmanTab // fixed distance code table fixedDistCodeTab; void readSome(); GBool startBlock(); void loadFixedCodes(); GBool readDynamicCodes(); void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab); int getHuffmanCodeWord(FlateHuffmanTab *tab); int getCodeWord(int bits); }; //------------------------------------------------------------------------ // EOFStream //------------------------------------------------------------------------ class EOFStream : public FilterStream { public: EOFStream(Stream *strA); virtual ~EOFStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset() {} virtual int getChar() { return EOF; } virtual int lookChar() { return EOF; } virtual int getBlock(char *blk, int size) { return 0; } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } }; //------------------------------------------------------------------------ // BufStream //------------------------------------------------------------------------ class BufStream : public FilterStream { public: BufStream(Stream *strA, int bufSizeA); virtual ~BufStream(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue); int lookChar(int idx); private: int *buf; int bufSize; }; //------------------------------------------------------------------------ // FixedLengthEncoder //------------------------------------------------------------------------ class FixedLengthEncoder : public FilterStream { public: FixedLengthEncoder(Stream *strA, int lengthA); ~FixedLengthEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue); virtual GBool isEncoder() { return gTrue; } private: int length; int count; }; //------------------------------------------------------------------------ // ASCIIHexEncoder //------------------------------------------------------------------------ class ASCIIHexEncoder : public FilterStream { public: ASCIIHexEncoder(Stream *strA); virtual ~ASCIIHexEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } private: char buf[4]; char *bufPtr; char *bufEnd; int lineLen; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // ASCII85Encoder //------------------------------------------------------------------------ class ASCII85Encoder : public FilterStream { public: ASCII85Encoder(Stream *strA); virtual ~ASCII85Encoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gFalse; } virtual GBool isEncoder() { return gTrue; } private: char buf[8]; char *bufPtr; char *bufEnd; int lineLen; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // RunLengthEncoder //------------------------------------------------------------------------ class RunLengthEncoder : public FilterStream { public: RunLengthEncoder(Stream *strA); virtual ~RunLengthEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } virtual int lookChar() { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: char buf[131]; char *bufPtr; char *bufEnd; char *nextEnd; GBool eof; GBool fillBuf(); }; //------------------------------------------------------------------------ // LZWEncoder //------------------------------------------------------------------------ struct LZWEncoderNode { int byte; LZWEncoderNode *next; // next sibling LZWEncoderNode *children; // first child }; class LZWEncoder : public FilterStream { public: LZWEncoder(Stream *strA); virtual ~LZWEncoder(); virtual Stream *copy(); virtual StreamKind getKind() { return strWeird; } virtual void reset(); virtual int getChar(); virtual int lookChar(); virtual GString *getPSFilter(int psLevel, const char *indent) { return NULL; } virtual GBool isBinary(GBool last = gTrue) { return gTrue; } virtual GBool isEncoder() { return gTrue; } private: LZWEncoderNode table[4096]; int nextSeq; int codeLen; Guchar inBuf[8192]; int inBufStart; int inBufLen; int outBuf; int outBufLen; GBool needEOD; void fillBuf(); }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10025/expected.txt000066400000000000000000000000421417746362400231540ustar00rootroot00000000000000Stream.cc:360:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10026/000077500000000000000000000000001417746362400206175ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10026/Function.cc000066400000000000000000001055171417746362400227240ustar00rootroot00000000000000//======================================================================== // // Function.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include "gmem.h" #include "gmempp.h" #include "GList.h" #include "Object.h" #include "Dict.h" #include "Stream.h" #include "Error.h" #include "Function.h" //------------------------------------------------------------------------ // Max depth of nested functions. This is used to catch infinite // loops in the function object structure. #define recursionLimit 8 //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ Function::Function() { } Function::~Function() { } Function *Function::parse(Object *funcObj, int recursion) { Function *func; Dict *dict; int funcType; Object obj1; if (recursion > recursionLimit) { error(errSyntaxError, -1, "Loop detected in function objects"); return NULL; } if (funcObj->isStream()) { dict = funcObj->streamGetDict(); } else if (funcObj->isDict()) { dict = funcObj->getDict(); } else if (funcObj->isName("Identity")) { return new IdentityFunction(); } else { error(errSyntaxError, -1, "Expected function dictionary or stream"); return NULL; } if (!dict->lookup("FunctionType", &obj1)->isInt()) { error(errSyntaxError, -1, "Function type is missing or wrong type"); obj1.free(); return NULL; } funcType = obj1.getInt(); obj1.free(); if (funcType == 0) { func = new SampledFunction(funcObj, dict); } else if (funcType == 2) { func = new ExponentialFunction(funcObj, dict); } else if (funcType == 3) { func = new StitchingFunction(funcObj, dict, recursion); } else if (funcType == 4) { func = new PostScriptFunction(funcObj, dict); } else { error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); return NULL; } if (!func->isOk()) { delete func; return NULL; } return func; } GBool Function::init(Dict *dict) { Object obj1, obj2; int i; //----- Domain if (!dict->lookup("Domain", &obj1)->isArray()) { error(errSyntaxError, -1, "Function is missing domain"); goto err2; } m = obj1.arrayGetLength() / 2; if (m > funcMaxInputs) { error(errSyntaxError, -1, "Functions with more than {0:d} inputs are unsupported", funcMaxInputs); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function domain array"); goto err1; } domain[i][1] = obj2.getNum(); obj2.free(); } obj1.free(); //----- Range hasRange = gFalse; n = 0; if (dict->lookup("Range", &obj1)->isArray()) { hasRange = gTrue; n = obj1.arrayGetLength() / 2; if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function range array"); goto err1; } range[i][1] = obj2.getNum(); obj2.free(); } } obj1.free(); return gTrue; err1: obj2.free(); err2: obj1.free(); return gFalse; } //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ IdentityFunction::IdentityFunction() { int i; // fill these in with arbitrary values just in case they get used // somewhere m = funcMaxInputs; n = funcMaxOutputs; for (i = 0; i < funcMaxInputs; ++i) { domain[i][0] = 0; domain[i][1] = 1; } hasRange = gFalse; } IdentityFunction::~IdentityFunction() { } void IdentityFunction::transform(double *in, double *out) { int i; for (i = 0; i < funcMaxOutputs; ++i) { out[i] = in[i]; } } //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { Stream *str; int sampleBits; double sampleMul; Object obj1, obj2; Guint buf, bitMask; int bits; Guint s; double in[funcMaxInputs]; int i, j, t, bit, idx; idxOffset = NULL; samples = NULL; sBuf = NULL; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 0 function is missing range"); goto err1; } if (m > sampledFuncMaxInputs) { error(errSyntaxError, -1, "Sampled functions with more than {0:d} inputs are unsupported", sampledFuncMaxInputs); goto err1; } //----- buffer sBuf = (double *)gmallocn(1 << m, sizeof(double)); //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 0 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- Size if (!dict->lookup("Size", &obj1)->isArray() || obj1.arrayGetLength() != m) { error(errSyntaxError, -1, "Function has missing or invalid size array"); goto err2; } for (i = 0; i < m; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isInt()) { error(errSyntaxError, -1, "Illegal value in function size array"); goto err3; } sampleSize[i] = obj2.getInt(); if (sampleSize[i] <= 0) { error(errSyntaxError, -1, "Illegal non-positive value in function size array"); goto err3; } obj2.free(); } obj1.free(); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); for (i = 0; i < (1<= 1; --j, t <<= 1) { if (sampleSize[j] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idx = (idx + bit) * sampleSize[j-1]; } if (sampleSize[0] == 1) { bit = 0; } else { bit = (t >> (m - 1)) & 1; } idxOffset[i] = (idx + bit) * n; } //----- BitsPerSample if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); goto err2; } sampleBits = obj1.getInt(); sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); obj1.free(); //----- Encode if (dict->lookup("Encode", &obj1)->isArray() && obj1.arrayGetLength() == 2*m) { for (i = 0; i < m; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function encode array"); goto err3; } encode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < m; ++i) { encode[i][0] = 0; encode[i][1] = sampleSize[i] - 1; } } obj1.free(); for (i = 0; i < m; ++i) { inputMul[i] = (encode[i][1] - encode[i][0]) / (domain[i][1] - domain[i][0]); } //----- Decode if (dict->lookup("Decode", &obj1)->isArray() && obj1.arrayGetLength() == 2*n) { for (i = 0; i < n; ++i) { obj1.arrayGet(2*i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][0] = obj2.getNum(); obj2.free(); obj1.arrayGet(2*i+1, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function decode array"); goto err3; } decode[i][1] = obj2.getNum(); obj2.free(); } } else { for (i = 0; i < n; ++i) { decode[i][0] = range[i][0]; decode[i][1] = range[i][1]; } } obj1.free(); //----- samples nSamples = n; for (i = 0; i < m; ++i) nSamples *= sampleSize[i]; samples = (double *)gmallocn(nSamples, sizeof(double)); buf = 0; bits = 0; bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; str->reset(); for (i = 0; i < nSamples; ++i) { if (sampleBits == 8) { s = str->getChar(); } else if (sampleBits == 16) { s = str->getChar(); s = (s << 8) + str->getChar(); } else if (sampleBits == 32) { s = str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); s = (s << 8) + str->getChar(); } else { while (bits < sampleBits) { buf = (buf << 8) | (str->getChar() & 0xff); bits += 8; } s = (buf >> (bits - sampleBits)) & bitMask; bits -= sampleBits; } samples[i] = (double)s * sampleMul; } str->close(); // set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } SampledFunction::~SampledFunction() { if (idxOffset) { gfree(idxOffset); } if (samples) { gfree(samples); } if (sBuf) { gfree(sBuf); } } SampledFunction::SampledFunction(SampledFunction *func) { memcpy((void *)this, (void *)func, sizeof(SampledFunction)); idxOffset = (int *)gmallocn(1 << m, sizeof(int)); memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); samples = (double *)gmallocn(nSamples, sizeof(double)); memcpy(samples, func->samples, nSamples * sizeof(double)); sBuf = (double *)gmallocn(1 << m, sizeof(double)); } void SampledFunction::transform(double *in, double *out) { double x; int e[funcMaxInputs]; double efrac0[funcMaxInputs]; double efrac1[funcMaxInputs]; int i, j, k, idx0, t; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } // map input values into sample array for (i = 0; i < m; ++i) { x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) x = 0; } else if (x > sampleSize[i] - 1) { x = sampleSize[i] - 1; } e[i] = (int)x; if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { // this happens if in[i] = domain[i][1] e[i] = sampleSize[i] - 2; } efrac1[i] = x - e[i]; efrac0[i] = 1 - efrac1[i]; } // compute index for the first sample to be used idx0 = 0; for (k = m - 1; k >= 1; --k) { idx0 = (idx0 + e[k]) * sampleSize[k-1]; } idx0 = (idx0 + e[0]) * n; // for each output, do m-linear interpolation for (i = 0; i < n; ++i) { // pull 2^m values out of the sample array for (j = 0; j < (1<>= 1) { for (k = 0; k < t; k += 2) { sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; } } // map output value to range out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { Object obj1, obj2; int i; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Exponential function with more than one input"); goto err1; } //----- C0 if (dict->lookup("C0", &obj1)->isArray()) { if (hasRange && obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = obj1.arrayGetLength(); if (n > funcMaxOutputs) { error(errSyntaxError, -1, "Functions with more than {0:d} outputs are unsupported", funcMaxOutputs); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C0 array"); goto err3; } c0[i] = obj2.getNum(); obj2.free(); } } else { if (hasRange && n != 1) { error(errSyntaxError, -1, "Function's C0 array is wrong length"); goto err2; } n = 1; c0[0] = 0; } obj1.free(); //----- C1 if (dict->lookup("C1", &obj1)->isArray()) { if (obj1.arrayGetLength() != n) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } for (i = 0; i < n; ++i) { obj1.arrayGet(i, &obj2); if (!obj2.isNum()) { error(errSyntaxError, -1, "Illegal value in function C1 array"); goto err3; } c1[i] = obj2.getNum(); obj2.free(); } } else { if (n != 1) { error(errSyntaxError, -1, "Function's C1 array is wrong length"); goto err2; } c1[0] = 1; } obj1.free(); //----- N (exponent) if (!dict->lookup("N", &obj1)->isNum()) { error(errSyntaxError, -1, "Function has missing or invalid N"); goto err2; } e = obj1.getNum(); obj1.free(); ok = gTrue; return; err3: obj2.free(); err2: obj1.free(); err1: return; } ExponentialFunction::~ExponentialFunction() { } ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { memcpy((void *)this, (void *)func, sizeof(ExponentialFunction)); } void ExponentialFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < n; ++i) { out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); if (hasRange) { if (out[i] < range[i][0]) { out[i] = range[i][0]; } else if (out[i] > range[i][1]) { out[i] = range[i][1]; } } } return; } //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, int recursion) { Object obj1, obj2; int i; ok = gFalse; funcs = NULL; bounds = NULL; encode = NULL; scale = NULL; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (m != 1) { error(errSyntaxError, -1, "Stitching function with more than one input"); goto err1; } //----- Functions if (!dict->lookup("Functions", &obj1)->isArray()) { error(errSyntaxError, -1, "Missing 'Functions' entry in stitching function"); goto err1; } k = obj1.arrayGetLength(); funcs = (Function **)gmallocn(k, sizeof(Function *)); bounds = (double *)gmallocn(k + 1, sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); for (i = 0; i < k; ++i) { funcs[i] = NULL; } for (i = 0; i < k; ++i) { if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), recursion + 1))) { goto err2; } if (funcs[i]->getInputSize() != 1 || (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { error(errSyntaxError, -1, "Incompatible subfunctions in stitching function"); goto err2; } obj2.free(); } obj1.free(); //----- Bounds if (!dict->lookup("Bounds", &obj1)->isArray() || obj1.arrayGetLength() != k - 1) { error(errSyntaxError, -1, "Missing or invalid 'Bounds' entry in stitching function"); goto err1; } bounds[0] = domain[0][0]; for (i = 1; i < k; ++i) { if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Bounds' array in stitching function"); goto err2; } bounds[i] = obj2.getNum(); obj2.free(); } bounds[k] = domain[0][1]; obj1.free(); //----- Encode if (!dict->lookup("Encode", &obj1)->isArray() || obj1.arrayGetLength() != 2 * k) { error(errSyntaxError, -1, "Missing or invalid 'Encode' entry in stitching function"); goto err1; } for (i = 0; i < 2 * k; ++i) { if (!obj1.arrayGet(i, &obj2)->isNum()) { error(errSyntaxError, -1, "Invalid type in 'Encode' array in stitching function"); goto err2; } encode[i] = obj2.getNum(); obj2.free(); } obj1.free(); //----- pre-compute the scale factors for (i = 0; i < k; ++i) { if (bounds[i] == bounds[i+1]) { // avoid a divide-by-zero -- in this situation, function i will // never be used anyway scale[i] = 0; } else { scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); } } ok = gTrue; return; err2: obj2.free(); err1: obj1.free(); } StitchingFunction::StitchingFunction(StitchingFunction *func) { int i; memcpy((void *)this, (void *)func, sizeof(StitchingFunction)); funcs = (Function **)gmallocn(k, sizeof(Function *)); for (i = 0; i < k; ++i) { funcs[i] = func->funcs[i]->copy(); } bounds = (double *)gmallocn(k + 1, sizeof(double)); memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); encode = (double *)gmallocn(2 * k, sizeof(double)); memcpy(encode, func->encode, 2 * k * sizeof(double)); scale = (double *)gmallocn(k, sizeof(double)); memcpy(scale, func->scale, k * sizeof(double)); ok = gTrue; } StitchingFunction::~StitchingFunction() { int i; if (funcs) { for (i = 0; i < k; ++i) { if (funcs[i]) { delete funcs[i]; } } } gfree(funcs); gfree(bounds); gfree(encode); gfree(scale); } void StitchingFunction::transform(double *in, double *out) { double x; int i; if (in[0] < domain[0][0]) { x = domain[0][0]; } else if (in[0] > domain[0][1]) { x = domain[0][1]; } else { x = in[0]; } for (i = 0; i < k - 1; ++i) { if (x < bounds[i+1]) { break; } } x = encode[2*i] + (x - bounds[i]) * scale[i]; funcs[i]->transform(&x, out); } //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ // This is not an enum, because we can't foreward-declare the enum // type in Function.h // // NB: This must be kept in sync with psOpNames[] below. #define psOpAbs 0 #define psOpAdd 1 #define psOpAnd 2 #define psOpAtan 3 #define psOpBitshift 4 #define psOpCeiling 5 #define psOpCopy 6 #define psOpCos 7 #define psOpCvi 8 #define psOpCvr 9 #define psOpDiv 10 #define psOpDup 11 #define psOpEq 12 #define psOpExch 13 #define psOpExp 14 #define psOpFalse 15 #define psOpFloor 16 #define psOpGe 17 #define psOpGt 18 #define psOpIdiv 19 #define psOpIndex 20 #define psOpLe 21 #define psOpLn 22 #define psOpLog 23 #define psOpLt 24 #define psOpMod 25 #define psOpMul 26 #define psOpNe 27 #define psOpNeg 28 #define psOpNot 29 #define psOpOr 30 #define psOpPop 31 #define psOpRoll 32 #define psOpRound 33 #define psOpSin 34 #define psOpSqrt 35 #define psOpSub 36 #define psOpTrue 37 #define psOpTruncate 38 #define psOpXor 39 // the push/j/jz ops are used internally (and are not listed in psOpNames[]) #define psOpPush 40 #define psOpJ 41 #define psOpJz 42 #define nPSOps (sizeof(psOpNames) / sizeof(const char *)) // Note: 'if' and 'ifelse' are parsed separately. // The rest are listed here in alphabetical order. // // NB: This must be kept in sync with the psOpXXX defines above. static const char *psOpNames[] = { "abs", "add", "and", "atan", "bitshift", "ceiling", "copy", "cos", "cvi", "cvr", "div", "dup", "eq", "exch", "exp", "false", "floor", "ge", "gt", "idiv", "index", "le", "ln", "log", "lt", "mod", "mul", "ne", "neg", "not", "or", "pop", "roll", "round", "sin", "sqrt", "sub", "true", "truncate", "xor" }; struct PSCode { int op; union { double d; int i; } val; }; #define psStackSize 100 PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { Stream *str; GList *tokens; GString *tok; double in[funcMaxInputs]; int tokPtr, codePtr, i; codeString = NULL; code = NULL; codeSize = 0; ok = gFalse; //----- initialize the generic stuff if (!init(dict)) { goto err1; } if (!hasRange) { error(errSyntaxError, -1, "Type 4 function is missing range"); goto err1; } //----- get the stream if (!funcObj->isStream()) { error(errSyntaxError, -1, "Type 4 function isn't a stream"); goto err1; } str = funcObj->getStream(); //----- tokenize the function codeString = new GString(); tokens = new GList(); str->reset(); while ((tok = getToken(str))) { tokens->append(tok); } str->close(); //----- parse the function if (tokens->getLength() < 1 || ((GString *)tokens->get(0))->cmp("{")) { error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); goto err2; } tokPtr = 1; codePtr = 0; if (!parseCode(tokens, &tokPtr, &codePtr)) { goto err2; } codeLen = codePtr; //----- set up the cache for (i = 0; i < m; ++i) { in[i] = domain[i][0]; cacheIn[i] = in[i] - 1; } transform(in, cacheOut); ok = gTrue; err2: deleteGList(tokens, GString); err1: return; } PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { memcpy((void *)this, (void *)func, sizeof(PostScriptFunction)); codeString = func->codeString->copy(); code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); memcpy(code, func->code, codeSize * sizeof(PSCode)); } PostScriptFunction::~PostScriptFunction() { gfree(code); if (codeString) { delete codeString; } } void PostScriptFunction::transform(double *in, double *out) { double stack[psStackSize]; double x; int sp, i; // check the cache for (i = 0; i < m; ++i) { if (in[i] != cacheIn[i]) { break; } } if (i == m) { for (i = 0; i < n; ++i) { out[i] = cacheOut[i]; } return; } for (i = 0; i < m; ++i) { stack[psStackSize - 1 - i] = in[i]; } sp = exec(stack, psStackSize - m); // if (sp < psStackSize - n) { // error(errSyntaxWarning, -1, // "Extra values on stack at end of PostScript function"); // } if (sp > psStackSize - n) { error(errSyntaxError, -1, "Stack underflow in PostScript function"); sp = psStackSize - n; } for (i = 0; i < n; ++i) { x = stack[sp + n - 1 - i]; if (x < range[i][0]) { out[i] = range[i][0]; } else if (x > range[i][1]) { out[i] = range[i][1]; } else { out[i] = x; } } // save current result in the cache for (i = 0; i < m; ++i) { cacheIn[i] = in[i]; } for (i = 0; i < n; ++i) { cacheOut[i] = out[i]; } } GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { GString *tok; char *p; int a, b, mid, cmp; int codePtr0, codePtr1; while (1) { if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); p = tok->getCString(); if (isdigit(*p) || *p == '.' || *p == '-') { addCodeD(codePtr, psOpPush, atof(tok->getCString())); } else if (!tok->cmp("{")) { codePtr0 = *codePtr; addCodeI(codePtr, psOpJz, 0); if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("if")) { code[codePtr0].val.i = *codePtr; } else if (!tok->cmp("{")) { codePtr1 = *codePtr; addCodeI(codePtr, psOpJ, 0); code[codePtr0].val.i = *codePtr; if (!parseCode(tokens, tokPtr, codePtr)) { return gFalse; } if (*tokPtr >= tokens->getLength()) { error(errSyntaxError, -1, "Unexpected end of PostScript function stream"); return gFalse; } tok = (GString *)tokens->get((*tokPtr)++); if (!tok->cmp("ifelse")) { code[codePtr1].val.i = *codePtr; } else { error(errSyntaxError, -1, "Expected 'ifelse' in PostScript function stream"); return gFalse; } } else { error(errSyntaxError, -1, "Expected 'if' in PostScript function stream"); return gFalse; } } else if (!tok->cmp("}")) { break; } else if (!tok->cmp("if")) { error(errSyntaxError, -1, "Unexpected 'if' in PostScript function stream"); return gFalse; } else if (!tok->cmp("ifelse")) { error(errSyntaxError, -1, "Unexpected 'ifelse' in PostScript function stream"); return gFalse; } else { a = -1; b = nPSOps; cmp = 0; // make gcc happy // invariant: psOpNames[a] < tok < psOpNames[b] while (b - a > 1) { mid = (a + b) / 2; cmp = tok->cmp(psOpNames[mid]); if (cmp > 0) { a = mid; } else if (cmp < 0) { b = mid; } else { a = b = mid; } } if (cmp != 0) { error(errSyntaxError, -1, "Unknown operator '{0:t}' in PostScript function", tok); return gFalse; } addCode(codePtr, a); } } return gTrue; } void PostScriptFunction::addCode(int *codePtr, int op) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; ++(*codePtr); } void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.i = x; ++(*codePtr); } void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { if (*codePtr >= codeSize) { if (codeSize) { codeSize *= 2; } else { codeSize = 16; } code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); } code[*codePtr].op = op; code[*codePtr].val.d = x; ++(*codePtr); } GString *PostScriptFunction::getToken(Stream *str) { GString *s; int c; GBool comment; s = new GString(); comment = gFalse; while (1) { if ((c = str->getChar()) == EOF) { delete s; return NULL; } codeString->append((char)c); if (comment) { if (c == '\x0a' || c == '\x0d') { comment = gFalse; } } else if (c == '%') { comment = gTrue; } else if (!isspace(c)) { break; } } if (c == '{' || c == '}') { s->append((char)c); } else if (isdigit(c) || c == '.' || c == '-') { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { break; } str->getChar(); codeString->append((char)c); } } else { while (1) { s->append((char)c); c = str->lookChar(); if (c == EOF || !isalnum(c)) { break; } str->getChar(); codeString->append((char)c); } } return s; } int PostScriptFunction::exec(double *stack, int sp0) { PSCode *c; double tmp[psStackSize]; double t; int sp, ip, nn, k, i; sp = sp0; ip = 0; while (ip < codeLen) { c = &code[ip++]; switch(c->op) { case psOpAbs: if (sp >= psStackSize) { goto underflow; } stack[sp] = fabs(stack[sp]); break; case psOpAdd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] + stack[sp]; ++sp; break; case psOpAnd: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; ++sp; break; case psOpAtan: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); ++sp; break; case psOpBitshift: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp + 1]; nn = (int)stack[sp]; if (nn > 0) { stack[sp + 1] = k << nn; } else if (nn < 0) { stack[sp + 1] = k >> -nn; } else { stack[sp + 1] = k; } ++sp; break; case psOpCeiling: if (sp >= psStackSize) { goto underflow; } stack[sp] = ceil(stack[sp]); break; case psOpCopy: if (sp >= psStackSize) { goto underflow; } nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (sp - nn < 0) { goto overflow; } for (i = 0; i < nn; ++i) { stack[sp - nn + i] = stack[sp + i]; } sp -= nn; break; case psOpCos: if (sp >= psStackSize) { goto underflow; } stack[sp] = cos(stack[sp]); break; case psOpCvi: if (sp >= psStackSize) { goto underflow; } stack[sp] = (int)stack[sp]; break; case psOpCvr: if (sp >= psStackSize) { goto underflow; } break; case psOpDiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] / stack[sp]; ++sp; break; case psOpDup: if (sp >= psStackSize) { goto underflow; } if (sp < 1) { goto overflow; } stack[sp - 1] = stack[sp]; --sp; break; case psOpEq: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; ++sp; break; case psOpExch: if (sp + 1 >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = stack[sp + 1]; stack[sp + 1] = t; break; case psOpExp: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = pow(stack[sp + 1], stack[sp]); ++sp; break; case psOpFalse: if (sp < 1) { goto overflow; } stack[sp - 1] = 0; --sp; break; case psOpFloor: if (sp >= psStackSize) { goto underflow; } stack[sp] = floor(stack[sp]); break; case psOpGe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; ++sp; break; case psOpGt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; ++sp; break; case psOpIdiv: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; ++sp; break; case psOpIndex: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp]; if (k < 0) { goto invalidArg; } if (sp + 1 + k >= psStackSize) { goto underflow; } stack[sp] = stack[sp + 1 + k]; break; case psOpLe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; ++sp; break; case psOpLn: if (sp >= psStackSize) { goto underflow; } stack[sp] = log(stack[sp]); break; case psOpLog: if (sp >= psStackSize) { goto underflow; } stack[sp] = log10(stack[sp]); break; case psOpLt: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; ++sp; break; case psOpMod: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; ++sp; break; case psOpMul: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] * stack[sp]; ++sp; break; case psOpNe: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; ++sp; break; case psOpNeg: if (sp >= psStackSize) { goto underflow; } stack[sp] = -stack[sp]; break; case psOpNot: if (sp >= psStackSize) { goto underflow; } stack[sp] = stack[sp] == 0 ? 1 : 0; break; case psOpOr: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; ++sp; break; case psOpPop: if (sp >= psStackSize) { goto underflow; } ++sp; break; case psOpRoll: if (sp + 1 >= psStackSize) { goto underflow; } k = (int)stack[sp++]; nn = (int)stack[sp++]; if (nn < 0) { goto invalidArg; } if (sp + nn > psStackSize) { goto underflow; } if (k >= 0) { k %= nn; } else { k = -k % nn; if (k) { k = nn - k; } } for (i = 0; i < nn; ++i) { tmp[i] = stack[sp + i]; } for (i = 0; i < nn; ++i) { stack[sp + i] = tmp[(i + k) % nn]; } break; case psOpRound: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); break; case psOpSin: if (sp >= psStackSize) { goto underflow; } stack[sp] = sin(stack[sp]); break; case psOpSqrt: if (sp >= psStackSize) { goto underflow; } stack[sp] = sqrt(stack[sp]); break; case psOpSub: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = stack[sp + 1] - stack[sp]; ++sp; break; case psOpTrue: if (sp < 1) { goto overflow; } stack[sp - 1] = 1; --sp; break; case psOpTruncate: if (sp >= psStackSize) { goto underflow; } t = stack[sp]; stack[sp] = (t >= 0) ? floor(t) : ceil(t); break; case psOpXor: if (sp + 1 >= psStackSize) { goto underflow; } stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; ++sp; break; case psOpPush: if (sp < 1) { goto overflow; } stack[--sp] = c->val.d; break; case psOpJ: ip = c->val.i; break; case psOpJz: if (sp >= psStackSize) { goto underflow; } k = (int)stack[sp++]; if (k == 0) { ip = c->val.i; } break; } } return sp; underflow: error(errSyntaxError, -1, "Stack underflow in PostScript function"); return sp; overflow: error(errSyntaxError, -1, "Stack overflow in PostScript function"); return sp; invalidArg: error(errSyntaxError, -1, "Invalid arg in PostScript function"); return sp; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10026/Function.h000066400000000000000000000161301417746362400225560ustar00rootroot00000000000000//======================================================================== // // Function.h // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #ifndef FUNCTION_H #define FUNCTION_H #include #ifdef USE_GCC_PRAGMAS #pragma interface #endif #include "gtypes.h" #include "Object.h" class GList; class Dict; class Stream; struct PSCode; //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ #define funcMaxInputs 32 #define funcMaxOutputs 32 #define sampledFuncMaxInputs 16 class Function { public: Function(); virtual ~Function(); // Construct a function. Returns NULL if unsuccessful. static Function *parse(Object *funcObj, int recursion = 0); // Initialize the entries common to all function types. GBool init(Dict *dict); virtual Function *copy() = 0; // Return the function type: // -1 : identity // 0 : sampled // 2 : exponential // 3 : stitching // 4 : PostScript virtual int getType() = 0; // Return size of input and output tuples. int getInputSize() { return m; } int getOutputSize() { return n; } double getDomainMin(int i) { return domain[i][0]; } double getDomainMax(int i) { return domain[i][1]; } double getRangeMin(int i) { return range[i][0]; } double getRangeMax(int i) { return range[i][1]; } GBool getHasRange() { return hasRange; } // Transform an input tuple into an output tuple. virtual void transform(double *in, double *out) = 0; virtual GBool isOk() = 0; protected: int m, n; // size of input and output tuples double // min and max values for function domain domain[funcMaxInputs][2]; double // min and max values for function range range[funcMaxOutputs][2]; GBool hasRange; // set if range is defined }; //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ class IdentityFunction : public Function { public: IdentityFunction(); virtual ~IdentityFunction(); virtual Function *copy() { return new IdentityFunction(); } virtual int getType() { return -1; } virtual void transform(double *in, double *out); virtual GBool isOk() { return gTrue; } private: }; //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ class SampledFunction : public Function { public: SampledFunction(Object *funcObj, Dict *dict); virtual ~SampledFunction(); virtual Function *copy() { return new SampledFunction(this); } virtual int getType() { return 0; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getSampleSize(int i) { return sampleSize[i]; } double getEncodeMin(int i) { return encode[i][0]; } double getEncodeMax(int i) { return encode[i][1]; } double getDecodeMin(int i) { return decode[i][0]; } double getDecodeMax(int i) { return decode[i][1]; } double *getSamples() { return samples; } private: SampledFunction(SampledFunction *func); int // number of samples for each domain element sampleSize[funcMaxInputs]; double // min and max values for domain encoder encode[funcMaxInputs][2]; double // min and max values for range decoder decode[funcMaxOutputs][2]; double // input multipliers inputMul[funcMaxInputs]; int *idxOffset; double *samples; // the samples int nSamples; // size of the samples array double *sBuf; // buffer for the transform function double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ class ExponentialFunction : public Function { public: ExponentialFunction(Object *funcObj, Dict *dict); virtual ~ExponentialFunction(); virtual Function *copy() { return new ExponentialFunction(this); } virtual int getType() { return 2; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } double *getC0() { return c0; } double *getC1() { return c1; } double getE() { return e; } private: ExponentialFunction(ExponentialFunction *func); double c0[funcMaxOutputs]; double c1[funcMaxOutputs]; double e; GBool ok; }; //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ class StitchingFunction : public Function { public: StitchingFunction(Object *funcObj, Dict *dict, int recursion); virtual ~StitchingFunction(); virtual Function *copy() { return new StitchingFunction(this); } virtual int getType() { return 3; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } int getNumFuncs() { return k; } Function *getFunc(int i) { return funcs[i]; } double *getBounds() { return bounds; } double *getEncode() { return encode; } double *getScale() { return scale; } private: StitchingFunction(StitchingFunction *func); int k; Function **funcs; double *bounds; double *encode; double *scale; GBool ok; }; //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ class PostScriptFunction : public Function { public: PostScriptFunction(Object *funcObj, Dict *dict); virtual ~PostScriptFunction(); virtual Function *copy() { return new PostScriptFunction(this); } virtual int getType() { return 4; } virtual void transform(double *in, double *out); virtual GBool isOk() { return ok; } GString *getCodeString() { return codeString; } private: PostScriptFunction(PostScriptFunction *func); GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); void addCode(int *codePtr, int op); void addCodeI(int *codePtr, int op, int x); void addCodeD(int *codePtr, int op, double x); GString *getToken(Stream *str); int exec(double *stack, int sp0); GString *codeString; PSCode *code; int codeLen; int codeSize; double cacheIn[funcMaxInputs]; double cacheOut[funcMaxOutputs]; GBool ok; }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10026/README000066400000000000000000000000721417746362400214760ustar00rootroot00000000000000Details: https://nvd.nist.gov/vuln/detail/CVE-2019-10026 cppcheck-2.7/test/bug-hunting/cve/CVE-2019-10026/expected.txt000066400000000000000000000001601417746362400231560ustar00rootroot00000000000000Function.cc:1475:bughuntingDivByZero Function.cc:1477:bughuntingDivByZero Function.cc:1486:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-1010315/000077500000000000000000000000001417746362400207615ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-1010315/dsdiff.c000066400000000000000000000434741417746362400224000ustar00rootroot00000000000000//////////////////////////////////////////////////////////////////////////// // **** WAVPACK **** // // Hybrid Lossless Wavefile Compressor // // Copyright (c) 1998 - 2019 David Bryant. // // All Rights Reserved. // // Distributed under the BSD Software License (see license.txt) // //////////////////////////////////////////////////////////////////////////// // dsdiff.c // This module is a helper to the WavPack command-line programs to support DFF files. #include #include #include #include #include #include #include "wavpack.h" #include "utils.h" #include "md5.h" #ifdef _WIN32 #define strdup(x) _strdup(x) #endif #define WAVPACK_NO_ERROR 0 #define WAVPACK_SOFT_ERROR 1 #define WAVPACK_HARD_ERROR 2 extern int debug_logging_mode; #pragma pack(push,2) typedef struct { char ckID[4]; int64_t ckDataSize; } DFFChunkHeader; typedef struct { char ckID[4]; int64_t ckDataSize; char formType[4]; } DFFFileHeader; typedef struct { char ckID[4]; int64_t ckDataSize; uint32_t version; } DFFVersionChunk; typedef struct { char ckID[4]; int64_t ckDataSize; uint32_t sampleRate; } DFFSampleRateChunk; typedef struct { char ckID[4]; int64_t ckDataSize; uint16_t numChannels; } DFFChannelsHeader; typedef struct { char ckID[4]; int64_t ckDataSize; char compressionType[4]; } DFFCompressionHeader; #pragma pack(pop) #define DFFChunkHeaderFormat "4D" #define DFFFileHeaderFormat "4D4" #define DFFVersionChunkFormat "4DL" #define DFFSampleRateChunkFormat "4DL" #define DFFChannelsHeaderFormat "4DS" #define DFFCompressionHeaderFormat "4D4" int ParseDsdiffHeaderConfig (FILE *infile, char *infilename, char *fourcc, WavpackContext *wpc, WavpackConfig *config) { int64_t infilesize, total_samples; DFFFileHeader dff_file_header; DFFChunkHeader dff_chunk_header; uint32_t bcount; infilesize = DoGetFileSize (infile); memcpy (&dff_file_header, fourcc, 4); if ((!DoReadFile (infile, ((char *) &dff_file_header) + 4, sizeof (DFFFileHeader) - 4, &bcount) || bcount != sizeof (DFFFileHeader) - 4) || strncmp (dff_file_header.formType, "DSD ", 4)) { error_line ("%s is not a valid .DFF file!", infilename); return WAVPACK_SOFT_ERROR; } else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && !WavpackAddWrapper (wpc, &dff_file_header, sizeof (DFFFileHeader))) { error_line ("%s", WavpackGetErrorMessage (wpc)); return WAVPACK_SOFT_ERROR; } #if 1 // this might be a little too picky... WavpackBigEndianToNative (&dff_file_header, DFFFileHeaderFormat); if (infilesize && !(config->qmode & QMODE_IGNORE_LENGTH) && dff_file_header.ckDataSize && dff_file_header.ckDataSize + 1 && dff_file_header.ckDataSize + 12 != infilesize) { error_line ("%s is not a valid .DFF file (by total size)!", infilename); return WAVPACK_SOFT_ERROR; } if (debug_logging_mode) error_line ("file header indicated length = %lld", dff_file_header.ckDataSize); #endif // loop through all elements of the DSDIFF header // (until the data chuck) and copy them to the output file while (1) { if (!DoReadFile (infile, &dff_chunk_header, sizeof (DFFChunkHeader), &bcount) || bcount != sizeof (DFFChunkHeader)) { error_line ("%s is not a valid .DFF file!", infilename); return WAVPACK_SOFT_ERROR; } else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && !WavpackAddWrapper (wpc, &dff_chunk_header, sizeof (DFFChunkHeader))) { error_line ("%s", WavpackGetErrorMessage (wpc)); return WAVPACK_SOFT_ERROR; } WavpackBigEndianToNative (&dff_chunk_header, DFFChunkHeaderFormat); if (debug_logging_mode) error_line ("chunk header indicated length = %lld", dff_chunk_header.ckDataSize); if (!strncmp (dff_chunk_header.ckID, "FVER", 4)) { uint32_t version; if (dff_chunk_header.ckDataSize != sizeof (version) || !DoReadFile (infile, &version, sizeof (version), &bcount) || bcount != sizeof (version)) { error_line ("%s is not a valid .DFF file!", infilename); return WAVPACK_SOFT_ERROR; } else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && !WavpackAddWrapper (wpc, &version, sizeof (version))) { error_line ("%s", WavpackGetErrorMessage (wpc)); return WAVPACK_SOFT_ERROR; } WavpackBigEndianToNative (&version, "L"); if (debug_logging_mode) error_line ("dsdiff file version = 0x%08x", version); } else if (!strncmp (dff_chunk_header.ckID, "PROP", 4)) { char *prop_chunk; if (dff_chunk_header.ckDataSize < 4 || dff_chunk_header.ckDataSize > 1024) { error_line ("%s is not a valid .DFF file!", infilename); return WAVPACK_SOFT_ERROR; } if (debug_logging_mode) error_line ("got PROP chunk of %d bytes total", (int) dff_chunk_header.ckDataSize); prop_chunk = malloc ((size_t) dff_chunk_header.ckDataSize); if (!DoReadFile (infile, prop_chunk, (uint32_t) dff_chunk_header.ckDataSize, &bcount) || bcount != dff_chunk_header.ckDataSize) { error_line ("%s is not a valid .DFF file!", infilename); free (prop_chunk); return WAVPACK_SOFT_ERROR; } else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) && !WavpackAddWrapper (wpc, prop_chunk, (uint32_t) dff_chunk_header.ckDataSize)) { error_line ("%s", WavpackGetErrorMessage (wpc)); free (prop_chunk); return WAVPACK_SOFT_ERROR; } if (!strncmp (prop_chunk, "SND ", 4)) { char *cptr = prop_chunk + 4, *eptr = prop_chunk + dff_chunk_header.ckDataSize; uint16_t numChannels, chansSpecified, chanMask = 0; uint32_t sampleRate; while (eptr - cptr >= sizeof (dff_chunk_header)) { memcpy (&dff_chunk_header, cptr, sizeof (dff_chunk_header)); cptr += sizeof (dff_chunk_header); WavpackBigEndianToNative (&dff_chunk_header, DFFChunkHeaderFormat); if (dff_chunk_header.ckDataSize > 0 && dff_chunk_header.ckDataSize <= eptr - cptr) { if (!strncmp (dff_chunk_header.ckID, "FS ", 4) && dff_chunk_header.ckDataSize == 4) { memcpy (&sampleRate, cptr, sizeof (sampleRate)); WavpackBigEndianToNative (&sampleRate, "L"); cptr += dff_chunk_header.ckDataSize; if (debug_logging_mode) error_line ("got sample rate of %u Hz", sampleRate); } else if (!strncmp (dff_chunk_header.ckID, "CHNL", 4) && dff_chunk_header.ckDataSize >= 2) { memcpy (&numChannels, cptr, sizeof (numChannels)); WavpackBigEndianToNative (&numChannels, "S"); cptr += sizeof (numChannels); chansSpecified = (int)(dff_chunk_header.ckDataSize - sizeof (numChannels)) / 4; if (numChannels < chansSpecified || numChannels < 1) { error_line ("%s is not a valid .DFF file!", infilename); free (prop_chunk); return WAVPACK_SOFT_ERROR; } while (chansSpecified--) { if (!strncmp (cptr, "SLFT", 4) || !strncmp (cptr, "MLFT", 4)) chanMask |= 0x1; else if (!strncmp (cptr, "SRGT", 4) || !strncmp (cptr, "MRGT", 4)) chanMask |= 0x2; else if (!strncmp (cptr, "LS ", 4)) chanMask |= 0x10; else if (!strncmp (cptr, "RS ", 4)) chanMask |= 0x20; else if (!strncmp (cptr, "C ", 4)) chanMask |= 0x4; else if (!strncmp (cptr, "LFE ", 4)) chanMask |= 0x8; else if (debug_logging_mode) error_line ("undefined channel ID %c%c%c%c", cptr [0], cptr [1], cptr [2], cptr [3]); cptr += 4; } if (debug_logging_mode) error_line ("%d channels, mask = 0x%08x", numChannels, chanMask); } else if (!strncmp (dff_chunk_header.ckID, "CMPR", 4) && dff_chunk_header.ckDataSize >= 4) { if (strncmp (cptr, "DSD ", 4)) { error_line ("DSDIFF files must be uncompressed, not \"%c%c%c%c\"!", cptr [0], cptr [1], cptr [2], cptr [3]); free (prop_chunk); return WAVPACK_SOFT_ERROR; } cptr += dff_chunk_header.ckDataSize; } else { if (debug_logging_mode) error_line ("got PROP/SND chunk type \"%c%c%c%c\" of %d bytes", dff_chunk_header.ckID [0], dff_chunk_header.ckID [1], dff_chunk_header.ckID [2], dff_chunk_header.ckID [3], dff_chunk_header.ckDataSize); cptr += dff_chunk_header.ckDataSize; } } else { error_line ("%s is not a valid .DFF file!", infilename); free (prop_chunk); return WAVPACK_SOFT_ERROR; } } if (chanMask && (config->channel_mask || (config->qmode & QMODE_CHANS_UNASSIGNED))) { error_line ("this DSDIFF file already has channel order information!"); free (prop_chunk); return WAVPACK_SOFT_ERROR; } else if (chanMask) config->channel_mask = chanMask; config->bits_per_sample = 8; config->bytes_per_sample = 1; config->num_channels = numChannels; config->sample_rate = sampleRate / 8; config->qmode |= QMODE_DSD_MSB_FIRST; } else if (debug_logging_mode) error_line ("got unknown PROP chunk type \"%c%c%c%c\" of %d bytes", prop_chunk [0], prop_chunk [1], prop_chunk [2], prop_chunk [3], dff_chunk_header.ckDataSize); free (prop_chunk); } else if (!strncmp (dff_chunk_header.ckID, "DSD ", 4)) { total_samples = dff_chunk_header.ckDataSize / config->num_channels; break; } else { // just copy unknown chunks to output file int bytes_to_copy = (int)(((dff_chunk_header.ckDataSize) + 1) & ~(int64_t)1); char *buff; if (bytes_to_copy < 0 || bytes_to_copy > 4194304) { error_line ("%s is not a valid .DFF file!", infilename); return WAVPACK_SOFT_ERROR; } buff = malloc (bytes_to_copy); if (debug_logging_mode) error_line ("extra unknown chunk \"%c%c%c%c\" of %d bytes", dff_chunk_header.ckID [0], dff_chunk_header.ckID [1], dff_chunk_header.ckID [2], dff_chunk_header.ckID [3], dff_chunk_header.ckDataSize); if (!DoReadFile (infile, buff, bytes_to_copy, &bcount) || bcount != bytes_to_copy || (!(config->qmode & QMODE_NO_STORE_WRAPPER) && !WavpackAddWrapper (wpc, buff, bytes_to_copy))) { error_line ("%s", WavpackGetErrorMessage (wpc)); free (buff); return WAVPACK_SOFT_ERROR; } free (buff); } } if (debug_logging_mode) error_line ("setting configuration with %lld samples", total_samples); if (!WavpackSetConfiguration64 (wpc, config, total_samples, NULL)) { error_line ("%s: %s", infilename, WavpackGetErrorMessage (wpc)); return WAVPACK_SOFT_ERROR; } return WAVPACK_NO_ERROR; } int WriteDsdiffHeader (FILE *outfile, WavpackContext *wpc, int64_t total_samples, int qmode) { uint32_t chan_mask = WavpackGetChannelMask (wpc); int num_channels = WavpackGetNumChannels (wpc); DFFFileHeader file_header, prop_header; DFFChunkHeader data_header; DFFVersionChunk ver_chunk; DFFSampleRateChunk fs_chunk; DFFChannelsHeader chan_header; DFFCompressionHeader cmpr_header; char *cmpr_name = "\016not compressed", *chan_ids; int64_t file_size, prop_chunk_size, data_size; int cmpr_name_size, chan_ids_size; uint32_t bcount; if (debug_logging_mode) error_line ("WriteDsdiffHeader (), total samples = %lld, qmode = 0x%02x\n", (long long) total_samples, qmode); cmpr_name_size = (strlen (cmpr_name) + 1) & ~1; chan_ids_size = num_channels * 4; chan_ids = malloc (chan_ids_size); if (chan_ids) { uint32_t scan_mask = 0x1; char *cptr = chan_ids; int ci, uci = 0; for (ci = 0; ci < num_channels; ++ci) { while (scan_mask && !(scan_mask & chan_mask)) scan_mask <<= 1; if (scan_mask & 0x1) memcpy (cptr, num_channels <= 2 ? "SLFT" : "MLFT", 4); else if (scan_mask & 0x2) memcpy (cptr, num_channels <= 2 ? "SRGT" : "MRGT", 4); else if (scan_mask & 0x4) memcpy (cptr, "C ", 4); else if (scan_mask & 0x8) memcpy (cptr, "LFE ", 4); else if (scan_mask & 0x10) memcpy (cptr, "LS ", 4); else if (scan_mask & 0x20) memcpy (cptr, "RS ", 4); else { cptr [0] = 'C'; cptr [1] = (uci / 100) + '0'; cptr [2] = ((uci % 100) / 10) + '0'; cptr [3] = (uci % 10) + '0'; uci++; } scan_mask <<= 1; cptr += 4; } } else { error_line ("can't allocate memory!"); return FALSE; } data_size = total_samples * num_channels; prop_chunk_size = sizeof (prop_header) + sizeof (fs_chunk) + sizeof (chan_header) + chan_ids_size + sizeof (cmpr_header) + cmpr_name_size; file_size = sizeof (file_header) + sizeof (ver_chunk) + prop_chunk_size + sizeof (data_header) + ((data_size + 1) & ~(int64_t)1); memcpy (file_header.ckID, "FRM8", 4); file_header.ckDataSize = file_size - 12; memcpy (file_header.formType, "DSD ", 4); memcpy (prop_header.ckID, "PROP", 4); prop_header.ckDataSize = prop_chunk_size - 12; memcpy (prop_header.formType, "SND ", 4); memcpy (ver_chunk.ckID, "FVER", 4); ver_chunk.ckDataSize = sizeof (ver_chunk) - 12; ver_chunk.version = 0x01050000; memcpy (fs_chunk.ckID, "FS ", 4); fs_chunk.ckDataSize = sizeof (fs_chunk) - 12; fs_chunk.sampleRate = WavpackGetSampleRate (wpc) * 8; memcpy (chan_header.ckID, "CHNL", 4); chan_header.ckDataSize = sizeof (chan_header) + chan_ids_size - 12; chan_header.numChannels = num_channels; memcpy (cmpr_header.ckID, "CMPR", 4); cmpr_header.ckDataSize = sizeof (cmpr_header) + cmpr_name_size - 12; memcpy (cmpr_header.compressionType, "DSD ", 4); memcpy (data_header.ckID, "DSD ", 4); data_header.ckDataSize = data_size; WavpackNativeToBigEndian (&file_header, DFFFileHeaderFormat); WavpackNativeToBigEndian (&ver_chunk, DFFVersionChunkFormat); WavpackNativeToBigEndian (&prop_header, DFFFileHeaderFormat); WavpackNativeToBigEndian (&fs_chunk, DFFSampleRateChunkFormat); WavpackNativeToBigEndian (&chan_header, DFFChannelsHeaderFormat); WavpackNativeToBigEndian (&cmpr_header, DFFCompressionHeaderFormat); WavpackNativeToBigEndian (&data_header, DFFChunkHeaderFormat); if (!DoWriteFile (outfile, &file_header, sizeof (file_header), &bcount) || bcount != sizeof (file_header) || !DoWriteFile (outfile, &ver_chunk, sizeof (ver_chunk), &bcount) || bcount != sizeof (ver_chunk) || !DoWriteFile (outfile, &prop_header, sizeof (prop_header), &bcount) || bcount != sizeof (prop_header) || !DoWriteFile (outfile, &fs_chunk, sizeof (fs_chunk), &bcount) || bcount != sizeof (fs_chunk) || !DoWriteFile (outfile, &chan_header, sizeof (chan_header), &bcount) || bcount != sizeof (chan_header) || !DoWriteFile (outfile, chan_ids, chan_ids_size, &bcount) || bcount != chan_ids_size || !DoWriteFile (outfile, &cmpr_header, sizeof (cmpr_header), &bcount) || bcount != sizeof (cmpr_header) || !DoWriteFile (outfile, cmpr_name, cmpr_name_size, &bcount) || bcount != cmpr_name_size || !DoWriteFile (outfile, &data_header, sizeof (data_header), &bcount) || bcount != sizeof (data_header)) { error_line ("can't write .DSF data, disk probably full!"); free (chan_ids); return FALSE; } free (chan_ids); return TRUE; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-1010315/expected.txt000066400000000000000000000000411417746362400233160ustar00rootroot00000000000000dsdiff.c:282:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-12977/000077500000000000000000000000001417746362400206405ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-12977/cmd.txt000066400000000000000000000000421417746362400221400ustar00rootroot00000000000000-DMAGICKCORE_LIBOPENJP2_DELEGATE cppcheck-2.7/test/bug-hunting/cve/CVE-2019-12977/expected.txt000066400000000000000000000000331417746362400231760ustar00rootroot00000000000000jp2.c:865:bughuntingUninit cppcheck-2.7/test/bug-hunting/cve/CVE-2019-12977/jp2.c000066400000000000000000001175201417746362400215050ustar00rootroot00000000000000/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % JJJ PPPP 222 % % J P P 2 2 % % J PPPP 22 % % J J P 2 % % JJ P 22222 % % % % % % Read/Write JPEG-2000 Image Format % % % % Cristy % % Nathan Brown % % June 2001 % % % % % % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % % https://imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % */ /* Include declarations. */ #include "MagickCore/studio.h" #include "MagickCore/artifact.h" #include "MagickCore/attribute.h" #include "MagickCore/blob.h" #include "MagickCore/blob-private.h" #include "MagickCore/cache.h" #include "MagickCore/colorspace.h" #include "MagickCore/colorspace-private.h" #include "MagickCore/color.h" #include "MagickCore/color-private.h" #include "MagickCore/exception.h" #include "MagickCore/exception-private.h" #include "MagickCore/image.h" #include "MagickCore/image-private.h" #include "MagickCore/list.h" #include "MagickCore/magick.h" #include "MagickCore/memory_.h" #include "MagickCore/monitor.h" #include "MagickCore/monitor-private.h" #include "MagickCore/option.h" #include "MagickCore/pixel-accessor.h" #include "MagickCore/profile.h" #include "MagickCore/property.h" #include "MagickCore/quantum-private.h" #include "MagickCore/resource_.h" #include "MagickCore/semaphore.h" #include "MagickCore/static.h" #include "MagickCore/statistic.h" #include "MagickCore/string_.h" #include "MagickCore/string-private.h" #include "MagickCore/module.h" #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) #include #endif /* Forward declarations. */ #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) static MagickBooleanType WriteJP2Image(const ImageInfo *,Image *,ExceptionInfo *); #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s J 2 K % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsJ2K() returns MagickTrue if the image format type, identified by the % magick string, is J2K. % % The format of the IsJ2K method is: % % MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % % o magick: compare image format pattern against these bytes. % % o length: Specifies the length of the magick string. % */ static MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) { if (length < 4) return(MagickFalse); if (memcmp(magick,"\xff\x4f\xff\x51",4) == 0) return(MagickTrue); return(MagickFalse); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s J P 2 % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsJP2() returns MagickTrue if the image format type, identified by the % magick string, is JP2. % % The format of the IsJP2 method is: % % MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) % % A description of each parameter follows: % % o magick: compare image format pattern against these bytes. % % o length: Specifies the length of the magick string. % */ static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) { if (length < 4) return(MagickFalse); if (memcmp(magick,"\x0d\x0a\x87\x0a",4) == 0) return(MagickTrue); if (length < 12) return(MagickFalse); if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0) return(MagickTrue); return(MagickFalse); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e a d J P 2 I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000 % codestream (JPC) image file and returns it. It allocates the memory % necessary for the new Image structure and returns a pointer to the new % image or set of images. % % JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu. % % The format of the ReadJP2Image method is: % % Image *ReadJP2Image(const ImageInfo *image_info, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image_info: the image info. % % o exception: return any errors or warnings in this structure. % */ #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) static void JP2ErrorHandler(const char *message,void *client_data) { ExceptionInfo *exception; exception=(ExceptionInfo *) client_data; (void) ThrowMagickException(exception,GetMagickModule(),CoderError, message,"`%s'","OpenJP2"); } static OPJ_SIZE_T JP2ReadHandler(void *buffer,OPJ_SIZE_T length,void *context) { Image *image; ssize_t count; image=(Image *) context; count=ReadBlob(image,(ssize_t) length,(unsigned char *) buffer); if (count == 0) return((OPJ_SIZE_T) -1); return((OPJ_SIZE_T) count); } static OPJ_BOOL JP2SeekHandler(OPJ_OFF_T offset,void *context) { Image *image; image=(Image *) context; return(SeekBlob(image,offset,SEEK_SET) < 0 ? OPJ_FALSE : OPJ_TRUE); } static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context) { Image *image; image=(Image *) context; return(SeekBlob(image,offset,SEEK_CUR) < 0 ? -1 : offset); } static void JP2WarningHandler(const char *message,void *client_data) { ExceptionInfo *exception; exception=(ExceptionInfo *) client_data; (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning, message,"`%s'","OpenJP2"); } static OPJ_SIZE_T JP2WriteHandler(void *buffer,OPJ_SIZE_T length,void *context) { Image *image; ssize_t count; image=(Image *) context; count=WriteBlob(image,(ssize_t) length,(unsigned char *) buffer); return((OPJ_SIZE_T) count); } static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) { const char *option; Image *image; int jp2_status; MagickBooleanType status; opj_codec_t *jp2_codec; opj_dparameters_t parameters; opj_image_t *jp2_image; opj_stream_t *jp2_stream; register ssize_t i; ssize_t y; unsigned char sans[4]; /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickCoreSignature); if (image_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", image_info->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); image=AcquireImage(image_info,exception); status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == MagickFalse) { image=DestroyImageList(image); return((Image *) NULL); } /* Initialize JP2 codec. */ if (ReadBlob(image,4,sans) != 4) { image=DestroyImageList(image); return((Image *) NULL); } (void) SeekBlob(image,SEEK_SET,0); if (LocaleCompare(image_info->magick,"JPT") == 0) jp2_codec=opj_create_decompress(OPJ_CODEC_JPT); else if (IsJ2K(sans,4) != MagickFalse) jp2_codec=opj_create_decompress(OPJ_CODEC_J2K); else jp2_codec=opj_create_decompress(OPJ_CODEC_JP2); opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception); opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception); opj_set_default_decoder_parameters(¶meters); option=GetImageOption(image_info,"jp2:reduce-factor"); if (option != (const char *) NULL) parameters.cp_reduce=StringToInteger(option); option=GetImageOption(image_info,"jp2:quality-layers"); if (option != (const char *) NULL) parameters.cp_layer=StringToInteger(option); if (opj_setup_decoder(jp2_codec,¶meters) == 0) { opj_destroy_codec(jp2_codec); ThrowReaderException(DelegateError,"UnableToManageJP2Stream"); } jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,1); opj_stream_set_read_function(jp2_stream,JP2ReadHandler); opj_stream_set_write_function(jp2_stream,JP2WriteHandler); opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); opj_stream_set_user_data(jp2_stream,image,NULL); opj_stream_set_user_data_length(jp2_stream,GetBlobSize(image)); if (opj_read_header(jp2_stream,jp2_codec,&jp2_image) == 0) { opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); } jp2_status=OPJ_TRUE; if (image->ping == MagickFalse) { if ((image->columns != 0) && (image->rows != 0)) /* Extract an area from the image. */ jp2_status=opj_set_decode_area(jp2_codec,jp2_image, (OPJ_INT32) image->extract_info.x,(OPJ_INT32) image->extract_info.y, (OPJ_INT32) (image->extract_info.x+(ssize_t) image->columns), (OPJ_INT32) (image->extract_info.y+(ssize_t) image->rows)); else jp2_status=opj_set_decode_area(jp2_codec,jp2_image,0,0, jp2_image->comps[0].w,jp2_image->comps[0].h); if (jp2_status == OPJ_FALSE) { opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); } } if ((AcquireMagickResource(WidthResource,(size_t) jp2_image->comps[0].w) == MagickFalse) || (AcquireMagickResource(HeightResource,(size_t) jp2_image->comps[0].h) == MagickFalse)) { opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); } if ((image_info->number_scenes != 0) && (image_info->scene != 0)) jp2_status=opj_get_decoded_tile(jp2_codec,jp2_stream,jp2_image, (unsigned int) image_info->scene-1); else if (image->ping == MagickFalse) { jp2_status=opj_decode(jp2_codec,jp2_stream,jp2_image); if (jp2_status != OPJ_FALSE) jp2_status=opj_end_decompress(jp2_codec,jp2_stream); } if (jp2_status == OPJ_FALSE) { opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); } opj_stream_destroy(jp2_stream); for (i=0; i < (ssize_t) jp2_image->numcomps; i++) { if ((jp2_image->comps[0].dx == 0) || (jp2_image->comps[0].dy == 0) || (jp2_image->comps[0].prec != jp2_image->comps[i].prec) || (jp2_image->comps[0].sgnd != jp2_image->comps[i].sgnd) || ((image->ping == MagickFalse) && (jp2_image->comps[i].data == NULL))) { opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported") } } /* Convert JP2 image. */ image->columns=(size_t) jp2_image->comps[0].w; image->rows=(size_t) jp2_image->comps[0].h; image->depth=jp2_image->comps[0].prec; image->compression=JPEG2000Compression; if (jp2_image->numcomps == 1) SetImageColorspace(image,GRAYColorspace,exception); else if (jp2_image->color_space == 2) { SetImageColorspace(image,GRAYColorspace,exception); if (jp2_image->numcomps > 1) image->alpha_trait=BlendPixelTrait; } else if (jp2_image->color_space == 3) SetImageColorspace(image,Rec601YCbCrColorspace,exception); if (jp2_image->numcomps > 3) image->alpha_trait=BlendPixelTrait; if (jp2_image->icc_profile_buf != (unsigned char *) NULL) { StringInfo *profile; profile=BlobToStringInfo(jp2_image->icc_profile_buf, jp2_image->icc_profile_len); if (profile != (StringInfo *) NULL) { SetImageProfile(image,"icc",profile,exception); profile=DestroyStringInfo(profile); } } if (image->ping != MagickFalse) { opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); return(GetFirstImageInList(image)); } status=SetImageExtent(image,image->columns,image->rows,exception); if (status == MagickFalse) { opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); return(DestroyImageList(image)); } for (y=0; y < (ssize_t) image->rows; y++) { register Quantum *magick_restrict q; register ssize_t x; q=GetAuthenticPixels(image,0,y,image->columns,1,exception); if (q == (Quantum *) NULL) break; for (x=0; x < (ssize_t) image->columns; x++) { for (i=0; i < (ssize_t) jp2_image->numcomps; i++) { double pixel, scale; scale=QuantumRange/(double) ((1UL << jp2_image->comps[i].prec)-1); pixel=scale*(jp2_image->comps[i].data[y/jp2_image->comps[i].dy* image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx]+ (jp2_image->comps[i].sgnd ? 1UL << (jp2_image->comps[i].prec-1) : 0)); switch (i) { case 0: { if (jp2_image->numcomps == 1) { SetPixelGray(image,ClampToQuantum(pixel),q); SetPixelAlpha(image,OpaqueAlpha,q); break; } SetPixelRed(image,ClampToQuantum(pixel),q); SetPixelGreen(image,ClampToQuantum(pixel),q); SetPixelBlue(image,ClampToQuantum(pixel),q); SetPixelAlpha(image,OpaqueAlpha,q); break; } case 1: { if (jp2_image->numcomps == 2) { SetPixelAlpha(image,ClampToQuantum(pixel),q); break; } SetPixelGreen(image,ClampToQuantum(pixel),q); break; } case 2: { SetPixelBlue(image,ClampToQuantum(pixel),q); break; } case 3: { SetPixelAlpha(image,ClampToQuantum(pixel),q); break; } } } q+=GetPixelChannels(image); } if (SyncAuthenticPixels(image,exception) == MagickFalse) break; status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, image->rows); if (status == MagickFalse) break; } /* Free resources. */ opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); (void) CloseBlob(image); if ((image_info->number_scenes != 0) && (image_info->scene != 0)) AppendImageToList(&image,CloneImage(image,0,0,MagickTrue,exception)); return(GetFirstImageInList(image)); } #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e g i s t e r J P 2 I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RegisterJP2Image() adds attributes for the JP2 image format to the list of % supported formats. The attributes include the image format tag, a method % method to read and/or write the format, whether the format supports the % saving of more than one frame to the same file or blob, whether the format % supports native in-memory I/O, and a brief description of the format. % % The format of the RegisterJP2Image method is: % % size_t RegisterJP2Image(void) % */ ModuleExport size_t RegisterJP2Image(void) { char version[MagickPathExtent]; MagickInfo *entry; *version='\0'; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) (void) FormatLocaleString(version,MagickPathExtent,"%s",opj_version()); #endif entry=AcquireMagickInfo("JP2","JP2","JPEG-2000 File Format Syntax"); if (*version != '\0') entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); entry->magick=(IsImageFormatHandler *) IsJP2; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderDecoderSeekableStreamFlag; entry->flags|=CoderEncoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("JP2","J2C","JPEG-2000 Code Stream Syntax"); if (*version != '\0') entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); entry->magick=(IsImageFormatHandler *) IsJ2K; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderDecoderSeekableStreamFlag; entry->flags|=CoderEncoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("JP2","J2K","JPEG-2000 Code Stream Syntax"); if (*version != '\0') entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); entry->magick=(IsImageFormatHandler *) IsJ2K; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderDecoderSeekableStreamFlag; entry->flags|=CoderEncoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("JP2","JPM","JPEG-2000 File Format Syntax"); if (*version != '\0') entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); entry->magick=(IsImageFormatHandler *) IsJP2; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderDecoderSeekableStreamFlag; entry->flags|=CoderEncoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("JP2","JPT","JPEG-2000 File Format Syntax"); if (*version != '\0') entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); entry->magick=(IsImageFormatHandler *) IsJP2; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderDecoderSeekableStreamFlag; entry->flags|=CoderEncoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); entry=AcquireMagickInfo("JP2","JPC","JPEG-2000 Code Stream Syntax"); if (*version != '\0') entry->version=ConstantString(version); entry->mime_type=ConstantString("image/jp2"); entry->magick=(IsImageFormatHandler *) IsJP2; entry->flags^=CoderAdjoinFlag; entry->flags|=CoderDecoderSeekableStreamFlag; entry->flags|=CoderEncoderSeekableStreamFlag; #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) entry->decoder=(DecodeImageHandler *) ReadJP2Image; entry->encoder=(EncodeImageHandler *) WriteJP2Image; #endif (void) RegisterMagickInfo(entry); return(MagickImageCoderSignature); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % U n r e g i s t e r J P 2 I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % UnregisterJP2Image() removes format registrations made by the JP2 module % from the list of supported formats. % % The format of the UnregisterJP2Image method is: % % UnregisterJP2Image(void) % */ ModuleExport void UnregisterJP2Image(void) { (void) UnregisterMagickInfo("JPC"); (void) UnregisterMagickInfo("JPT"); (void) UnregisterMagickInfo("JPM"); (void) UnregisterMagickInfo("JP2"); (void) UnregisterMagickInfo("J2K"); } #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % W r i t e J P 2 I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % WriteJP2Image() writes an image in the JPEG 2000 image format. % % JP2 support originally written by Nathan Brown, nathanbrown@letu.edu % % The format of the WriteJP2Image method is: % % MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, % ExceptionInfo *exception) % % A description of each parameter follows. % % o image_info: the image info. % % o image: The image. % */ static void CinemaProfileCompliance(const opj_image_t *jp2_image, opj_cparameters_t *parameters) { /* Digital Cinema 4K profile compliant codestream. */ parameters->tile_size_on=OPJ_FALSE; parameters->cp_tdx=1; parameters->cp_tdy=1; parameters->tp_flag='C'; parameters->tp_on=1; parameters->cp_tx0=0; parameters->cp_ty0=0; parameters->image_offset_x0=0; parameters->image_offset_y0=0; parameters->cblockw_init=32; parameters->cblockh_init=32; parameters->csty|=0x01; parameters->prog_order=OPJ_CPRL; parameters->roi_compno=(-1); parameters->subsampling_dx=1; parameters->subsampling_dy=1; parameters->irreversible=1; if ((jp2_image->comps[0].w == 2048) || (jp2_image->comps[0].h == 1080)) { /* Digital Cinema 2K. */ parameters->cp_cinema=OPJ_CINEMA2K_24; parameters->cp_rsiz=OPJ_CINEMA2K; parameters->max_comp_size=1041666; if (parameters->numresolution > 6) parameters->numresolution=6; } if ((jp2_image->comps[0].w == 4096) || (jp2_image->comps[0].h == 2160)) { /* Digital Cinema 4K. */ parameters->cp_cinema=OPJ_CINEMA4K_24; parameters->cp_rsiz=OPJ_CINEMA4K; parameters->max_comp_size=1041666; if (parameters->numresolution < 1) parameters->numresolution=1; if (parameters->numresolution > 7) parameters->numresolution=7; parameters->numpocs=2; parameters->POC[0].tile=1; parameters->POC[0].resno0=0; parameters->POC[0].compno0=0; parameters->POC[0].layno1=1; parameters->POC[0].resno1=parameters->numresolution-1; parameters->POC[0].compno1=3; parameters->POC[0].prg1=OPJ_CPRL; parameters->POC[1].tile=1; parameters->POC[1].resno0=parameters->numresolution-1; parameters->POC[1].compno0=0; parameters->POC[1].layno1=1; parameters->POC[1].resno1=parameters->numresolution; parameters->POC[1].compno1=3; parameters->POC[1].prg1=OPJ_CPRL; } parameters->tcp_numlayers=1; parameters->tcp_rates[0]=((float) (jp2_image->numcomps*jp2_image->comps[0].w* jp2_image->comps[0].h*jp2_image->comps[0].prec))/(parameters->max_comp_size* 8*jp2_image->comps[0].dx*jp2_image->comps[0].dy); parameters->cp_disto_alloc=1; } static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, ExceptionInfo *exception) { const char *option, *property; int jp2_status; MagickBooleanType status; opj_codec_t *jp2_codec; OPJ_COLOR_SPACE jp2_colorspace; opj_cparameters_t parameters; opj_image_cmptparm_t jp2_info[5]; opj_image_t *jp2_image; opj_stream_t *jp2_stream; register ssize_t i; ssize_t y; unsigned int channels; /* Open image file. */ assert(image_info != (const ImageInfo *) NULL); assert(image_info->signature == MagickCoreSignature); assert(image != (Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); if (status == MagickFalse) return(status); /* Initialize JPEG 2000 API. */ opj_set_default_encoder_parameters(¶meters); for (i=1; i < 6; i++) if (((size_t) (1UL << (i+2)) > image->columns) && ((size_t) (1UL << (i+2)) > image->rows)) break; parameters.numresolution=i; option=GetImageOption(image_info,"jp2:number-resolutions"); if (option != (const char *) NULL) parameters.numresolution=StringToInteger(option); parameters.tcp_numlayers=1; parameters.tcp_rates[0]=0; /* lossless */ parameters.cp_disto_alloc=1; if ((image_info->quality != 0) && (image_info->quality != 100)) { parameters.tcp_distoratio[0]=(double) image_info->quality; parameters.cp_fixed_quality=OPJ_TRUE; } if (image_info->extract != (char *) NULL) { RectangleInfo geometry; int flags; /* Set tile size. */ flags=ParseAbsoluteGeometry(image_info->extract,&geometry); parameters.cp_tdx=(int) geometry.width; parameters.cp_tdy=(int) geometry.width; if ((flags & HeightValue) != 0) parameters.cp_tdy=(int) geometry.height; if ((flags & XValue) != 0) parameters.cp_tx0=geometry.x; if ((flags & YValue) != 0) parameters.cp_ty0=geometry.y; parameters.tile_size_on=OPJ_TRUE; } option=GetImageOption(image_info,"jp2:quality"); if (option != (const char *) NULL) { register const char *p; /* Set quality PSNR. */ p=option; for (i=0; sscanf(p,"%f",¶meters.tcp_distoratio[i]) == 1; i++) { if (i > 100) break; while ((*p != '\0') && (*p != ',')) p++; if (*p == '\0') break; p++; } parameters.tcp_numlayers=i+1; parameters.cp_fixed_quality=OPJ_TRUE; } option=GetImageOption(image_info,"jp2:progression-order"); if (option != (const char *) NULL) { if (LocaleCompare(option,"LRCP") == 0) parameters.prog_order=OPJ_LRCP; if (LocaleCompare(option,"RLCP") == 0) parameters.prog_order=OPJ_RLCP; if (LocaleCompare(option,"RPCL") == 0) parameters.prog_order=OPJ_RPCL; if (LocaleCompare(option,"PCRL") == 0) parameters.prog_order=OPJ_PCRL; if (LocaleCompare(option,"CPRL") == 0) parameters.prog_order=OPJ_CPRL; } option=GetImageOption(image_info,"jp2:rate"); if (option != (const char *) NULL) { register const char *p; /* Set compression rate. */ p=option; for (i=0; sscanf(p,"%f",¶meters.tcp_rates[i]) == 1; i++) { if (i >= 100) break; while ((*p != '\0') && (*p != ',')) p++; if (*p == '\0') break; p++; } parameters.tcp_numlayers=i+1; parameters.cp_disto_alloc=OPJ_TRUE; } if (image_info->sampling_factor != (const char *) NULL) (void) sscanf(image_info->sampling_factor,"%d,%d", ¶meters.subsampling_dx,¶meters.subsampling_dy); property=GetImageProperty(image,"comment",exception); if (property != (const char *) NULL) parameters.cp_comment=(char *) property; channels=3; jp2_colorspace=OPJ_CLRSPC_SRGB; if (image->colorspace == YUVColorspace) { jp2_colorspace=OPJ_CLRSPC_SYCC; parameters.subsampling_dx=2; } else { if (IsGrayColorspace(image->colorspace) != MagickFalse) { channels=1; jp2_colorspace=OPJ_CLRSPC_GRAY; } else (void) TransformImageColorspace(image,sRGBColorspace,exception); if (image->alpha_trait != UndefinedPixelTrait) channels++; } parameters.tcp_mct=channels == 3 ? 1 : 0; memset(jp2_info,0,sizeof(jp2_info)); for (i=0; i < (ssize_t) channels; i++) { jp2_info[i].prec=(OPJ_UINT32) image->depth; jp2_info[i].bpp=(OPJ_UINT32) image->depth; if ((image->depth == 1) && ((LocaleCompare(image_info->magick,"JPT") == 0) || (LocaleCompare(image_info->magick,"JP2") == 0))) { jp2_info[i].prec++; /* OpenJPEG returns exception for depth @ 1 */ jp2_info[i].bpp++; } jp2_info[i].sgnd=0; jp2_info[i].dx=parameters.subsampling_dx; jp2_info[i].dy=parameters.subsampling_dy; jp2_info[i].w=(OPJ_UINT32) image->columns; jp2_info[i].h=(OPJ_UINT32) image->rows; } jp2_image=opj_image_create((OPJ_UINT32) channels,jp2_info,jp2_colorspace); if (jp2_image == (opj_image_t *) NULL) ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); jp2_image->x0=parameters.image_offset_x0; jp2_image->y0=parameters.image_offset_y0; jp2_image->x1=(unsigned int) (2*parameters.image_offset_x0+(image->columns-1)* parameters.subsampling_dx+1); jp2_image->y1=(unsigned int) (2*parameters.image_offset_y0+(image->rows-1)* parameters.subsampling_dx+1); if ((image->depth == 12) && ((image->columns == 2048) || (image->rows == 1080) || (image->columns == 4096) || (image->rows == 2160))) CinemaProfileCompliance(jp2_image,¶meters); if (channels == 4) jp2_image->comps[3].alpha=1; else if ((channels == 2) && (jp2_colorspace == OPJ_CLRSPC_GRAY)) jp2_image->comps[1].alpha=1; /* Convert to JP2 pixels. */ for (y=0; y < (ssize_t) image->rows; y++) { register const Quantum *p; ssize_t x; p=GetVirtualPixels(image,0,y,image->columns,1,exception); if (p == (const Quantum *) NULL) break; for (x=0; x < (ssize_t) image->columns; x++) { for (i=0; i < (ssize_t) channels; i++) { double scale; register int *q; scale=(double) ((1UL << jp2_image->comps[i].prec)-1)/QuantumRange; q=jp2_image->comps[i].data+(y/jp2_image->comps[i].dy* image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx); switch (i) { case 0: { if (jp2_colorspace == OPJ_CLRSPC_GRAY) { *q=(int) (scale*GetPixelGray(image,p)); break; } *q=(int) (scale*GetPixelRed(image,p)); break; } case 1: { if (jp2_colorspace == OPJ_CLRSPC_GRAY) { *q=(int) (scale*GetPixelAlpha(image,p)); break; } *q=(int) (scale*GetPixelGreen(image,p)); break; } case 2: { *q=(int) (scale*GetPixelBlue(image,p)); break; } case 3: { *q=(int) (scale*GetPixelAlpha(image,p)); break; } } } p+=GetPixelChannels(image); } status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, image->rows); if (status == MagickFalse) break; } if (LocaleCompare(image_info->magick,"JPT") == 0) jp2_codec=opj_create_compress(OPJ_CODEC_JPT); else if (LocaleCompare(image_info->magick,"J2K") == 0) jp2_codec=opj_create_compress(OPJ_CODEC_J2K); else jp2_codec=opj_create_compress(OPJ_CODEC_JP2); opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception); opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception); opj_setup_encoder(jp2_codec,¶meters,jp2_image); jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,OPJ_FALSE); if (jp2_stream == (opj_stream_t *) NULL) { opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); } opj_stream_set_read_function(jp2_stream,JP2ReadHandler); opj_stream_set_write_function(jp2_stream,JP2WriteHandler); opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); opj_stream_set_user_data(jp2_stream,image,NULL); jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream); if ((jp2_status == 0) || (opj_encode(jp2_codec,jp2_stream) == 0) || (opj_end_compress(jp2_codec,jp2_stream) == 0)) { opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); } /* Free resources. */ opj_stream_destroy(jp2_stream); opj_destroy_codec(jp2_codec); opj_image_destroy(jp2_image); (void) CloseBlob(image); return(MagickTrue); } #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13390/000077500000000000000000000000001417746362400206265ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13390/README000066400000000000000000000000731417746362400215060ustar00rootroot00000000000000Details: https://nvd.nist.gov/vuln/detail/CVE-2019-13390 cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13390/cmd.txt000066400000000000000000000000251417746362400221270ustar00rootroot00000000000000-DCONFIG_ADX_MUXER=1 cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13390/expected.txt000066400000000000000000000000541417746362400231670ustar00rootroot00000000000000libavformat_rawenc.c:70:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13390/libavformat_rawenc.c000066400000000000000000000401361417746362400246430ustar00rootroot00000000000000/* * RAW muxers * Copyright (c) 2001 Fabrice Bellard * Copyright (c) 2005 Alex Beregszaszi * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "rawenc.h" #include "internal.h" int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt) { avio_write(s->pb, pkt->data, pkt->size); return 0; } static int force_one_stream(AVFormatContext *s) { if (s->nb_streams != 1) { av_log(s, AV_LOG_ERROR, "%s files have exactly one stream\n", s->oformat->name); return AVERROR(EINVAL); } return 0; } /* Note: Do not forget to add new entries to the Makefile as well. */ #if CONFIG_AC3_MUXER AVOutputFormat ff_ac3_muxer = { .name = "ac3", .long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), .mime_type = "audio/x-ac3", .extensions = "ac3", .audio_codec = AV_CODEC_ID_AC3, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_ADX_MUXER static int adx_write_trailer(AVFormatContext *s) { AVIOContext *pb = s->pb; AVCodecParameters *par = s->streams[0]->codecpar; if (pb->seekable & AVIO_SEEKABLE_NORMAL) { int64_t file_size = avio_tell(pb); uint64_t sample_count = (file_size - 36) / par->channels / 18 * 32; if (sample_count <= UINT32_MAX) { avio_seek(pb, 12, SEEK_SET); avio_wb32(pb, sample_count); avio_seek(pb, file_size, SEEK_SET); } } return 0; } AVOutputFormat ff_adx_muxer = { .name = "adx", .long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), .extensions = "adx", .audio_codec = AV_CODEC_ID_ADPCM_ADX, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .write_trailer = adx_write_trailer, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_APTX_MUXER AVOutputFormat ff_aptx_muxer = { .name = "aptx", .long_name = NULL_IF_CONFIG_SMALL("raw aptX (Audio Processing Technology for Bluetooth)"), .extensions = "aptx", .audio_codec = AV_CODEC_ID_APTX, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_APTX_HD_MUXER AVOutputFormat ff_aptx_hd_muxer = { .name = "aptx_hd", .long_name = NULL_IF_CONFIG_SMALL("raw aptX HD (Audio Processing Technology for Bluetooth)"), .extensions = "aptxhd", .audio_codec = AV_CODEC_ID_APTX_HD, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_AVS2_MUXER AVOutputFormat ff_avs2_muxer = { .name = "avs2", .long_name = NULL_IF_CONFIG_SMALL("raw AVS2-P2/IEEE1857.4 video"), .extensions = "avs,avs2", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_AVS2, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_CAVSVIDEO_MUXER AVOutputFormat ff_cavsvideo_muxer = { .name = "cavsvideo", .long_name = NULL_IF_CONFIG_SMALL("raw Chinese AVS (Audio Video Standard) video"), .extensions = "cavs", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_CAVS, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_CODEC2RAW_MUXER AVOutputFormat ff_codec2raw_muxer = { .name = "codec2raw", .long_name = NULL_IF_CONFIG_SMALL("raw codec2 muxer"), .audio_codec = AV_CODEC_ID_CODEC2, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DATA_MUXER AVOutputFormat ff_data_muxer = { .name = "data", .long_name = NULL_IF_CONFIG_SMALL("raw data"), .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DIRAC_MUXER AVOutputFormat ff_dirac_muxer = { .name = "dirac", .long_name = NULL_IF_CONFIG_SMALL("raw Dirac"), .extensions = "drc,vc2", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_DIRAC, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DNXHD_MUXER AVOutputFormat ff_dnxhd_muxer = { .name = "dnxhd", .long_name = NULL_IF_CONFIG_SMALL("raw DNxHD (SMPTE VC-3)"), .extensions = "dnxhd,dnxhr", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_DNXHD, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_DTS_MUXER AVOutputFormat ff_dts_muxer = { .name = "dts", .long_name = NULL_IF_CONFIG_SMALL("raw DTS"), .mime_type = "audio/x-dca", .extensions = "dts", .audio_codec = AV_CODEC_ID_DTS, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_EAC3_MUXER AVOutputFormat ff_eac3_muxer = { .name = "eac3", .long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), .mime_type = "audio/x-eac3", .extensions = "eac3", .audio_codec = AV_CODEC_ID_EAC3, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G722_MUXER AVOutputFormat ff_g722_muxer = { .name = "g722", .long_name = NULL_IF_CONFIG_SMALL("raw G.722"), .mime_type = "audio/G722", .extensions = "g722", .audio_codec = AV_CODEC_ID_ADPCM_G722, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G723_1_MUXER AVOutputFormat ff_g723_1_muxer = { .name = "g723_1", .long_name = NULL_IF_CONFIG_SMALL("raw G.723.1"), .mime_type = "audio/g723", .extensions = "tco,rco", .audio_codec = AV_CODEC_ID_G723_1, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G726_MUXER AVOutputFormat ff_g726_muxer = { .name = "g726", .long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left-justified\")"), .audio_codec = AV_CODEC_ID_ADPCM_G726, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_G726LE_MUXER AVOutputFormat ff_g726le_muxer = { .name = "g726le", .long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right-justified\")"), .audio_codec = AV_CODEC_ID_ADPCM_G726LE, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_GSM_MUXER AVOutputFormat ff_gsm_muxer = { .name = "gsm", .long_name = NULL_IF_CONFIG_SMALL("raw GSM"), .mime_type = "audio/x-gsm", .extensions = "gsm", .audio_codec = AV_CODEC_ID_GSM, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_H261_MUXER AVOutputFormat ff_h261_muxer = { .name = "h261", .long_name = NULL_IF_CONFIG_SMALL("raw H.261"), .mime_type = "video/x-h261", .extensions = "h261", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_H261, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_H263_MUXER AVOutputFormat ff_h263_muxer = { .name = "h263", .long_name = NULL_IF_CONFIG_SMALL("raw H.263"), .mime_type = "video/x-h263", .extensions = "h263", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_H263, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_H264_MUXER static int h264_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) { AVStream *st = s->streams[0]; if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && AV_RB24(pkt->data) != 0x000001) return ff_stream_add_bitstream_filter(st, "h264_mp4toannexb", NULL); return 1; } AVOutputFormat ff_h264_muxer = { .name = "h264", .long_name = NULL_IF_CONFIG_SMALL("raw H.264 video"), .extensions = "h264,264", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_H264, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .check_bitstream = h264_check_bitstream, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_HEVC_MUXER static int hevc_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) { AVStream *st = s->streams[0]; if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && AV_RB24(pkt->data) != 0x000001) return ff_stream_add_bitstream_filter(st, "hevc_mp4toannexb", NULL); return 1; } AVOutputFormat ff_hevc_muxer = { .name = "hevc", .long_name = NULL_IF_CONFIG_SMALL("raw HEVC video"), .extensions = "hevc,h265,265", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_HEVC, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .check_bitstream = hevc_check_bitstream, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_M4V_MUXER AVOutputFormat ff_m4v_muxer = { .name = "m4v", .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-4 video"), .extensions = "m4v", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MPEG4, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MJPEG_MUXER AVOutputFormat ff_mjpeg_muxer = { .name = "mjpeg", .long_name = NULL_IF_CONFIG_SMALL("raw MJPEG video"), .mime_type = "video/x-mjpeg", .extensions = "mjpg,mjpeg", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MJPEG, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_SINGLEJPEG_MUXER AVOutputFormat ff_singlejpeg_muxer = { .name = "singlejpeg", .long_name = NULL_IF_CONFIG_SMALL("JPEG single image"), .mime_type = "image/jpeg", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MJPEG, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, .write_header = force_one_stream, }; #endif #if CONFIG_MLP_MUXER AVOutputFormat ff_mlp_muxer = { .name = "mlp", .long_name = NULL_IF_CONFIG_SMALL("raw MLP"), .extensions = "mlp", .audio_codec = AV_CODEC_ID_MLP, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MP2_MUXER AVOutputFormat ff_mp2_muxer = { .name = "mp2", .long_name = NULL_IF_CONFIG_SMALL("MP2 (MPEG audio layer 2)"), .mime_type = "audio/mpeg", .extensions = "mp2,m2a,mpa", .audio_codec = AV_CODEC_ID_MP2, .video_codec = AV_CODEC_ID_NONE, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MPEG1VIDEO_MUXER AVOutputFormat ff_mpeg1video_muxer = { .name = "mpeg1video", .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-1 video"), .mime_type = "video/mpeg", .extensions = "mpg,mpeg,m1v", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MPEG1VIDEO, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_MPEG2VIDEO_MUXER AVOutputFormat ff_mpeg2video_muxer = { .name = "mpeg2video", .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-2 video"), .extensions = "m2v", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_MPEG2VIDEO, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_RAWVIDEO_MUXER AVOutputFormat ff_rawvideo_muxer = { .name = "rawvideo", .long_name = NULL_IF_CONFIG_SMALL("raw video"), .extensions = "yuv,rgb", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_RAWVIDEO, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_SBC_MUXER AVOutputFormat ff_sbc_muxer = { .name = "sbc", .long_name = NULL_IF_CONFIG_SMALL("raw SBC"), .mime_type = "audio/x-sbc", .extensions = "sbc,msbc", .audio_codec = AV_CODEC_ID_SBC, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_TRUEHD_MUXER AVOutputFormat ff_truehd_muxer = { .name = "truehd", .long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), .extensions = "thd", .audio_codec = AV_CODEC_ID_TRUEHD, .video_codec = AV_CODEC_ID_NONE, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif #if CONFIG_VC1_MUXER AVOutputFormat ff_vc1_muxer = { .name = "vc1", .long_name = NULL_IF_CONFIG_SMALL("raw VC-1 video"), .extensions = "vc1", .audio_codec = AV_CODEC_ID_NONE, .video_codec = AV_CODEC_ID_VC1, .write_header = force_one_stream, .write_packet = ff_raw_write_packet, .flags = AVFMT_NOTIMESTAMPS, }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13454/000077500000000000000000000000001417746362400206275ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13454/expected.txt000066400000000000000000000001031417746362400231630ustar00rootroot00000000000000layer.c:1616:bughuntingDivByZero layer.c:1617:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-13454/layer.c000066400000000000000000002432211417746362400221130ustar00rootroot00000000000000/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % L AAA Y Y EEEEE RRRR % % L A A Y Y E R R % % L AAAAA Y EEE RRRR % % L A A Y E R R % % LLLLL A A Y EEEEE R R % % % % MagickCore Image Layering Methods % % % % Software Design % % Cristy % % Anthony Thyssen % % January 2006 % % % % % % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % % https://imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % */ /* Include declarations. */ #include "MagickCore/studio.h" #include "MagickCore/artifact.h" #include "MagickCore/cache.h" #include "MagickCore/channel.h" #include "MagickCore/color.h" #include "MagickCore/color-private.h" #include "MagickCore/composite.h" #include "MagickCore/effect.h" #include "MagickCore/exception.h" #include "MagickCore/exception-private.h" #include "MagickCore/geometry.h" #include "MagickCore/image.h" #include "MagickCore/layer.h" #include "MagickCore/list.h" #include "MagickCore/memory_.h" #include "MagickCore/monitor.h" #include "MagickCore/monitor-private.h" #include "MagickCore/option.h" #include "MagickCore/pixel-accessor.h" #include "MagickCore/property.h" #include "MagickCore/profile.h" #include "MagickCore/resource_.h" #include "MagickCore/resize.h" #include "MagickCore/statistic.h" #include "MagickCore/string_.h" #include "MagickCore/transform.h" /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + C l e a r B o u n d s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ClearBounds() Clear the area specified by the bounds in an image to % transparency. This typically used to handle Background Disposal for the % previous frame in an animation sequence. % % Warning: no bounds checks are performed, except for the null or missed % image, for images that don't change. in all other cases bound must fall % within the image. % % The format is: % % void ClearBounds(Image *image,RectangleInfo *bounds, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image to had the area cleared in % % o bounds: the area to be clear within the imag image % % o exception: return any errors or warnings in this structure. % */ static void ClearBounds(Image *image,RectangleInfo *bounds, ExceptionInfo *exception) { ssize_t y; if (bounds->x < 0) return; if (image->alpha_trait == UndefinedPixelTrait) (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); for (y=0; y < (ssize_t) bounds->height; y++) { register ssize_t x; register Quantum *magick_restrict q; q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception); if (q == (Quantum *) NULL) break; for (x=0; x < (ssize_t) bounds->width; x++) { SetPixelAlpha(image,TransparentAlpha,q); q+=GetPixelChannels(image); } if (SyncAuthenticPixels(image,exception) == MagickFalse) break; } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + I s B o u n d s C l e a r e d % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared % when going from the first image to the second image. This typically used % to check if a proposed disposal method will work successfully to generate % the second frame image from the first disposed form of the previous frame. % % Warning: no bounds checks are performed, except for the null or missed % image, for images that don't change. in all other cases bound must fall % within the image. % % The format is: % % MagickBooleanType IsBoundsCleared(const Image *image1, % const Image *image2,RectangleInfo bounds,ExceptionInfo *exception) % % A description of each parameter follows: % % o image1, image 2: the images to check for cleared pixels % % o bounds: the area to be clear within the imag image % % o exception: return any errors or warnings in this structure. % */ static MagickBooleanType IsBoundsCleared(const Image *image1, const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception) { register const Quantum *p, *q; register ssize_t x; ssize_t y; if (bounds->x < 0) return(MagickFalse); for (y=0; y < (ssize_t) bounds->height; y++) { p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,exception); q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,exception); if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) break; for (x=0; x < (ssize_t) bounds->width; x++) { if ((GetPixelAlpha(image1,p) >= (Quantum) (QuantumRange/2)) && (GetPixelAlpha(image2,q) < (Quantum) (QuantumRange/2))) break; p+=GetPixelChannels(image1); q+=GetPixelChannels(image2); } if (x < (ssize_t) bounds->width) break; } return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o a l e s c e I m a g e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % CoalesceImages() composites a set of images while respecting any page % offsets and disposal methods. GIF, MIFF, and MNG animation sequences % typically start with an image background and each subsequent image % varies in size and offset. A new image sequence is returned with all % images the same size as the first images virtual canvas and composited % with the next image in the sequence. % % The format of the CoalesceImages method is: % % Image *CoalesceImages(Image *image,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image sequence. % % o exception: return any errors or warnings in this structure. % */ MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception) { Image *coalesce_image, *dispose_image, *previous; register Image *next; RectangleInfo bounds; /* Coalesce the image sequence. */ assert(image != (Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); next=GetFirstImageInList(image); bounds=next->page; if (bounds.width == 0) { bounds.width=next->columns; if (bounds.x > 0) bounds.width+=bounds.x; } if (bounds.height == 0) { bounds.height=next->rows; if (bounds.y > 0) bounds.height+=bounds.y; } bounds.x=0; bounds.y=0; coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue, exception); if (coalesce_image == (Image *) NULL) return((Image *) NULL); coalesce_image->background_color.alpha=(MagickRealType) TransparentAlpha; (void) SetImageBackgroundColor(coalesce_image,exception); coalesce_image->alpha_trait=next->alpha_trait; coalesce_image->page=bounds; coalesce_image->dispose=NoneDispose; /* Coalesce rest of the images. */ dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception); (void) CompositeImage(coalesce_image,next,CopyCompositeOp,MagickTrue, next->page.x,next->page.y,exception); next=GetNextImageInList(next); for ( ; next != (Image *) NULL; next=GetNextImageInList(next)) { /* Determine the bounds that was overlaid in the previous image. */ previous=GetPreviousImageInList(next); bounds=previous->page; bounds.width=previous->columns; bounds.height=previous->rows; if (bounds.x < 0) { bounds.width+=bounds.x; bounds.x=0; } if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns) bounds.width=coalesce_image->columns-bounds.x; if (bounds.y < 0) { bounds.height+=bounds.y; bounds.y=0; } if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows) bounds.height=coalesce_image->rows-bounds.y; /* Replace the dispose image with the new coalesced image. */ if (GetPreviousImageInList(next)->dispose != PreviousDispose) { dispose_image=DestroyImage(dispose_image); dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception); if (dispose_image == (Image *) NULL) { coalesce_image=DestroyImageList(coalesce_image); return((Image *) NULL); } } /* Clear the overlaid area of the coalesced bounds for background disposal */ if (next->previous->dispose == BackgroundDispose) ClearBounds(dispose_image,&bounds,exception); /* Next image is the dispose image, overlaid with next frame in sequence. */ coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception); coalesce_image->next->previous=coalesce_image; previous=coalesce_image; coalesce_image=GetNextImageInList(coalesce_image); (void) CompositeImage(coalesce_image,next, next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp, MagickTrue,next->page.x,next->page.y,exception); (void) CloneImageProfiles(coalesce_image,next); (void) CloneImageProperties(coalesce_image,next); (void) CloneImageArtifacts(coalesce_image,next); coalesce_image->page=previous->page; /* If a pixel goes opaque to transparent, use background dispose. */ if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse) coalesce_image->dispose=BackgroundDispose; else coalesce_image->dispose=NoneDispose; previous->dispose=coalesce_image->dispose; } dispose_image=DestroyImage(dispose_image); return(GetFirstImageInList(coalesce_image)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D i s p o s e I m a g e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % DisposeImages() returns the coalesced frames of a GIF animation as it would % appear after the GIF dispose method of that frame has been applied. That is % it returned the appearance of each frame before the next is overlaid. % % The format of the DisposeImages method is: % % Image *DisposeImages(Image *image,ExceptionInfo *exception) % % A description of each parameter follows: % % o images: the image sequence. % % o exception: return any errors or warnings in this structure. % */ MagickExport Image *DisposeImages(const Image *images,ExceptionInfo *exception) { Image *dispose_image, *dispose_images; RectangleInfo bounds; register Image *image, *next; /* Run the image through the animation sequence */ assert(images != (Image *) NULL); assert(images->signature == MagickCoreSignature); if (images->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); image=GetFirstImageInList(images); dispose_image=CloneImage(image,image->page.width,image->page.height, MagickTrue,exception); if (dispose_image == (Image *) NULL) return((Image *) NULL); dispose_image->page=image->page; dispose_image->page.x=0; dispose_image->page.y=0; dispose_image->dispose=NoneDispose; dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha; (void) SetImageBackgroundColor(dispose_image,exception); dispose_images=NewImageList(); for (next=image; image != (Image *) NULL; image=GetNextImageInList(image)) { Image *current_image; /* Overlay this frame's image over the previous disposal image. */ current_image=CloneImage(dispose_image,0,0,MagickTrue,exception); if (current_image == (Image *) NULL) { dispose_images=DestroyImageList(dispose_images); dispose_image=DestroyImage(dispose_image); return((Image *) NULL); } (void) CompositeImage(current_image,next, next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp, MagickTrue,next->page.x,next->page.y,exception); /* Handle Background dispose: image is displayed for the delay period. */ if (next->dispose == BackgroundDispose) { bounds=next->page; bounds.width=next->columns; bounds.height=next->rows; if (bounds.x < 0) { bounds.width+=bounds.x; bounds.x=0; } if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns) bounds.width=current_image->columns-bounds.x; if (bounds.y < 0) { bounds.height+=bounds.y; bounds.y=0; } if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows) bounds.height=current_image->rows-bounds.y; ClearBounds(current_image,&bounds,exception); } /* Select the appropriate previous/disposed image. */ if (next->dispose == PreviousDispose) current_image=DestroyImage(current_image); else { dispose_image=DestroyImage(dispose_image); dispose_image=current_image; current_image=(Image *) NULL; } /* Save the dispose image just calculated for return. */ { Image *dispose; dispose=CloneImage(dispose_image,0,0,MagickTrue,exception); if (dispose == (Image *) NULL) { dispose_images=DestroyImageList(dispose_images); dispose_image=DestroyImage(dispose_image); return((Image *) NULL); } (void) CloneImageProfiles(dispose,next); (void) CloneImageProperties(dispose,next); (void) CloneImageArtifacts(dispose,next); dispose->page.x=0; dispose->page.y=0; dispose->dispose=next->dispose; AppendImageToList(&dispose_images,dispose); } } dispose_image=DestroyImage(dispose_image); return(GetFirstImageInList(dispose_images)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + C o m p a r e P i x e l s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ComparePixels() Compare the two pixels and return true if the pixels % differ according to the given LayerType comparision method. % % This currently only used internally by CompareImagesBounds(). It is % doubtful that this sub-routine will be useful outside this module. % % The format of the ComparePixels method is: % % MagickBooleanType *ComparePixels(const LayerMethod method, % const PixelInfo *p,const PixelInfo *q) % % A description of each parameter follows: % % o method: What differences to look for. Must be one of % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer. % % o p, q: the pixels to test for appropriate differences. % */ static MagickBooleanType ComparePixels(const LayerMethod method, const PixelInfo *p,const PixelInfo *q) { double o1, o2; /* Any change in pixel values */ if (method == CompareAnyLayer) return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse)); o1 = (p->alpha_trait != UndefinedPixelTrait) ? p->alpha : OpaqueAlpha; o2 = (q->alpha_trait != UndefinedPixelTrait) ? q->alpha : OpaqueAlpha; /* Pixel goes from opaque to transprency. */ if (method == CompareClearLayer) return((MagickBooleanType) ((o1 >= ((double) QuantumRange/2.0)) && (o2 < ((double) QuantumRange/2.0)))); /* Overlay would change first pixel by second. */ if (method == CompareOverlayLayer) { if (o2 < ((double) QuantumRange/2.0)) return MagickFalse; return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse)); } return(MagickFalse); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + C o m p a r e I m a g e B o u n d s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % CompareImagesBounds() Given two images return the smallest rectangular area % by which the two images differ, accourding to the given 'Compare...' % layer method. % % This currently only used internally in this module, but may eventually % be used by other modules. % % The format of the CompareImagesBounds method is: % % RectangleInfo *CompareImagesBounds(const LayerMethod method, % const Image *image1,const Image *image2,ExceptionInfo *exception) % % A description of each parameter follows: % % o method: What differences to look for. Must be one of CompareAnyLayer, % CompareClearLayer, CompareOverlayLayer. % % o image1, image2: the two images to compare. % % o exception: return any errors or warnings in this structure. % */ static RectangleInfo CompareImagesBounds(const Image *image1, const Image *image2,const LayerMethod method,ExceptionInfo *exception) { RectangleInfo bounds; PixelInfo pixel1, pixel2; register const Quantum *p, *q; register ssize_t x; ssize_t y; /* Set bounding box of the differences between images. */ GetPixelInfo(image1,&pixel1); GetPixelInfo(image2,&pixel2); for (x=0; x < (ssize_t) image1->columns; x++) { p=GetVirtualPixels(image1,x,0,1,image1->rows,exception); q=GetVirtualPixels(image2,x,0,1,image2->rows,exception); if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) break; for (y=0; y < (ssize_t) image1->rows; y++) { GetPixelInfoPixel(image1,p,&pixel1); GetPixelInfoPixel(image2,q,&pixel2); if (ComparePixels(method,&pixel1,&pixel2)) break; p+=GetPixelChannels(image1); q+=GetPixelChannels(image2); } if (y < (ssize_t) image1->rows) break; } if (x >= (ssize_t) image1->columns) { /* Images are identical, return a null image. */ bounds.x=-1; bounds.y=-1; bounds.width=1; bounds.height=1; return(bounds); } bounds.x=x; for (x=(ssize_t) image1->columns-1; x >= 0; x--) { p=GetVirtualPixels(image1,x,0,1,image1->rows,exception); q=GetVirtualPixels(image2,x,0,1,image2->rows,exception); if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) break; for (y=0; y < (ssize_t) image1->rows; y++) { GetPixelInfoPixel(image1,p,&pixel1); GetPixelInfoPixel(image2,q,&pixel2); if (ComparePixels(method,&pixel1,&pixel2)) break; p+=GetPixelChannels(image1); q+=GetPixelChannels(image2); } if (y < (ssize_t) image1->rows) break; } bounds.width=(size_t) (x-bounds.x+1); for (y=0; y < (ssize_t) image1->rows; y++) { p=GetVirtualPixels(image1,0,y,image1->columns,1,exception); q=GetVirtualPixels(image2,0,y,image2->columns,1,exception); if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) break; for (x=0; x < (ssize_t) image1->columns; x++) { GetPixelInfoPixel(image1,p,&pixel1); GetPixelInfoPixel(image2,q,&pixel2); if (ComparePixels(method,&pixel1,&pixel2)) break; p+=GetPixelChannels(image1); q+=GetPixelChannels(image2); } if (x < (ssize_t) image1->columns) break; } bounds.y=y; for (y=(ssize_t) image1->rows-1; y >= 0; y--) { p=GetVirtualPixels(image1,0,y,image1->columns,1,exception); q=GetVirtualPixels(image2,0,y,image2->columns,1,exception); if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) break; for (x=0; x < (ssize_t) image1->columns; x++) { GetPixelInfoPixel(image1,p,&pixel1); GetPixelInfoPixel(image2,q,&pixel2); if (ComparePixels(method,&pixel1,&pixel2)) break; p+=GetPixelChannels(image1); q+=GetPixelChannels(image2); } if (x < (ssize_t) image1->columns) break; } bounds.height=(size_t) (y-bounds.y+1); return(bounds); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o m p a r e I m a g e L a y e r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % CompareImagesLayers() compares each image with the next in a sequence and % returns the minimum bounding region of all the pixel differences (of the % LayerMethod specified) it discovers. % % Images do NOT have to be the same size, though it is best that all the % images are 'coalesced' (images are all the same size, on a flattened % canvas, so as to represent exactly how an specific frame should look). % % No GIF dispose methods are applied, so GIF animations must be coalesced % before applying this image operator to find differences to them. % % The format of the CompareImagesLayers method is: % % Image *CompareImagesLayers(const Image *images, % const LayerMethod method,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o method: the layers type to compare images with. Must be one of... % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer. % % o exception: return any errors or warnings in this structure. % */ MagickExport Image *CompareImagesLayers(const Image *image, const LayerMethod method,ExceptionInfo *exception) { Image *image_a, *image_b, *layers; RectangleInfo *bounds; register const Image *next; register ssize_t i; assert(image != (const Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); assert((method == CompareAnyLayer) || (method == CompareClearLayer) || (method == CompareOverlayLayer)); /* Allocate bounds memory. */ next=GetFirstImageInList(image); bounds=(RectangleInfo *) AcquireQuantumMemory((size_t) GetImageListLength(next),sizeof(*bounds)); if (bounds == (RectangleInfo *) NULL) ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); /* Set up first comparision images. */ image_a=CloneImage(next,next->page.width,next->page.height, MagickTrue,exception); if (image_a == (Image *) NULL) { bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); return((Image *) NULL); } image_a->background_color.alpha=(MagickRealType) TransparentAlpha; (void) SetImageBackgroundColor(image_a,exception); image_a->page=next->page; image_a->page.x=0; image_a->page.y=0; (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x, next->page.y,exception); /* Compute the bounding box of changes for the later images */ i=0; next=GetNextImageInList(next); for ( ; next != (const Image *) NULL; next=GetNextImageInList(next)) { image_b=CloneImage(image_a,0,0,MagickTrue,exception); if (image_b == (Image *) NULL) { image_a=DestroyImage(image_a); bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); return((Image *) NULL); } (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x, next->page.y,exception); bounds[i]=CompareImagesBounds(image_b,image_a,method,exception); image_b=DestroyImage(image_b); i++; } image_a=DestroyImage(image_a); /* Clone first image in sequence. */ next=GetFirstImageInList(image); layers=CloneImage(next,0,0,MagickTrue,exception); if (layers == (Image *) NULL) { bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); return((Image *) NULL); } /* Deconstruct the image sequence. */ i=0; next=GetNextImageInList(next); for ( ; next != (const Image *) NULL; next=GetNextImageInList(next)) { if ((bounds[i].x == -1) && (bounds[i].y == -1) && (bounds[i].width == 1) && (bounds[i].height == 1)) { /* An empty frame is returned from CompareImageBounds(), which means the current frame is identical to the previous frame. */ i++; continue; } image_a=CloneImage(next,0,0,MagickTrue,exception); if (image_a == (Image *) NULL) break; image_b=CropImage(image_a,&bounds[i],exception); image_a=DestroyImage(image_a); if (image_b == (Image *) NULL) break; AppendImageToList(&layers,image_b); i++; } bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); if (next != (Image *) NULL) { layers=DestroyImageList(layers); return((Image *) NULL); } return(GetFirstImageInList(layers)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + O p t i m i z e L a y e r F r a m e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % OptimizeLayerFrames() takes a coalesced GIF animation, and compares each % frame against the three different 'disposal' forms of the previous frame. % From this it then attempts to select the smallest cropped image and % disposal method needed to reproduce the resulting image. % % Note that this not easy, and may require the expansion of the bounds % of previous frame, simply clear pixels for the next animation frame to % transparency according to the selected dispose method. % % The format of the OptimizeLayerFrames method is: % % Image *OptimizeLayerFrames(const Image *image, % const LayerMethod method,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o method: the layers technique to optimize with. Must be one of... % OptimizeImageLayer, or OptimizePlusLayer. The Plus form allows % the addition of extra 'zero delay' frames to clear pixels from % the previous frame, and the removal of frames that done change, % merging the delay times together. % % o exception: return any errors or warnings in this structure. % */ /* Define a 'fake' dispose method where the frame is duplicated, (for OptimizePlusLayer) with a extra zero time delay frame which does a BackgroundDisposal to clear the pixels that need to be cleared. */ #define DupDispose ((DisposeType)9) /* Another 'fake' dispose method used to removed frames that don't change. */ #define DelDispose ((DisposeType)8) #define DEBUG_OPT_FRAME 0 static Image *OptimizeLayerFrames(const Image *image,const LayerMethod method, ExceptionInfo *exception) { ExceptionInfo *sans_exception; Image *prev_image, *dup_image, *bgnd_image, *optimized_image; RectangleInfo try_bounds, bgnd_bounds, dup_bounds, *bounds; MagickBooleanType add_frames, try_cleared, cleared; DisposeType *disposals; register const Image *curr; register ssize_t i; assert(image != (const Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); assert(method == OptimizeLayer || method == OptimizeImageLayer || method == OptimizePlusLayer); /* Are we allowed to add/remove frames from animation? */ add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse; /* Ensure all the images are the same size. */ curr=GetFirstImageInList(image); for (; curr != (Image *) NULL; curr=GetNextImageInList(curr)) { if ((curr->columns != image->columns) || (curr->rows != image->rows)) ThrowImageException(OptionError,"ImagesAreNotTheSameSize"); if ((curr->page.x != 0) || (curr->page.y != 0) || (curr->page.width != image->page.width) || (curr->page.height != image->page.height)) ThrowImageException(OptionError,"ImagePagesAreNotCoalesced"); } /* Allocate memory (times 2 if we allow the use of frame duplications) */ curr=GetFirstImageInList(image); bounds=(RectangleInfo *) AcquireQuantumMemory((size_t) GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)* sizeof(*bounds)); if (bounds == (RectangleInfo *) NULL) ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); disposals=(DisposeType *) AcquireQuantumMemory((size_t) GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)* sizeof(*disposals)); if (disposals == (DisposeType *) NULL) { bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } /* Initialise Previous Image as fully transparent */ prev_image=CloneImage(curr,curr->columns,curr->rows,MagickTrue,exception); if (prev_image == (Image *) NULL) { bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); disposals=(DisposeType *) RelinquishMagickMemory(disposals); return((Image *) NULL); } prev_image->page=curr->page; /* ERROR: <-- should not be need, but is! */ prev_image->page.x=0; prev_image->page.y=0; prev_image->dispose=NoneDispose; prev_image->background_color.alpha_trait=BlendPixelTrait; prev_image->background_color.alpha=(MagickRealType) TransparentAlpha; (void) SetImageBackgroundColor(prev_image,exception); /* Figure out the area of overlay of the first frame No pixel could be cleared as all pixels are already cleared. */ #if DEBUG_OPT_FRAME i=0; (void) FormatLocaleFile(stderr,"frame %.20g :-\n",(double) i); #endif disposals[0]=NoneDispose; bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception); #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n", (double) bounds[i].width,(double) bounds[i].height, (double) bounds[i].x,(double) bounds[i].y ); #endif /* Compute the bounding box of changes for each pair of images. */ i=1; bgnd_image=(Image *) NULL; dup_image=(Image *) NULL; dup_bounds.width=0; dup_bounds.height=0; dup_bounds.x=0; dup_bounds.y=0; curr=GetNextImageInList(curr); for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr)) { #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr,"frame %.20g :-\n",(double) i); #endif /* Assume none disposal is the best */ bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception); cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception); disposals[i-1]=NoneDispose; #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n", (double) bounds[i].width,(double) bounds[i].height, (double) bounds[i].x,(double) bounds[i].y, bounds[i].x < 0?" (unchanged)":"", cleared?" (pixels cleared)":""); #endif if (bounds[i].x < 0) { /* Image frame is exactly the same as the previous frame! If not adding frames leave it to be cropped down to a null image. Otherwise mark previous image for deleted, transfering its crop bounds to the current image. */ if (add_frames && i>=2) { disposals[i-1]=DelDispose; disposals[i]=NoneDispose; bounds[i]=bounds[i-1]; i++; continue; } } else { /* Compare a none disposal against a previous disposal */ try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception); try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception); #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n", (double) try_bounds.width,(double) try_bounds.height, (double) try_bounds.x,(double) try_bounds.y, try_cleared?" (pixels were cleared)":""); #endif if ((!try_cleared && cleared) || try_bounds.width * try_bounds.height < bounds[i].width * bounds[i].height) { cleared=try_cleared; bounds[i]=try_bounds; disposals[i-1]=PreviousDispose; #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr,"previous: accepted\n"); } else { (void) FormatLocaleFile(stderr,"previous: rejected\n"); #endif } /* If we are allowed lets try a complex frame duplication. It is useless if the previous image already clears pixels correctly. This method will always clear all the pixels that need to be cleared. */ dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */ if (add_frames) { dup_image=CloneImage(curr->previous,0,0,MagickTrue,exception); if (dup_image == (Image *) NULL) { bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); disposals=(DisposeType *) RelinquishMagickMemory(disposals); prev_image=DestroyImage(prev_image); return((Image *) NULL); } dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception); ClearBounds(dup_image,&dup_bounds,exception); try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception); if (cleared || dup_bounds.width*dup_bounds.height +try_bounds.width*try_bounds.height < bounds[i].width * bounds[i].height) { cleared=MagickFalse; bounds[i]=try_bounds; disposals[i-1]=DupDispose; /* to be finalised later, if found to be optimial */ } else dup_bounds.width=dup_bounds.height=0; } /* Now compare against a simple background disposal */ bgnd_image=CloneImage(curr->previous,0,0,MagickTrue,exception); if (bgnd_image == (Image *) NULL) { bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); disposals=(DisposeType *) RelinquishMagickMemory(disposals); prev_image=DestroyImage(prev_image); if (dup_image != (Image *) NULL) dup_image=DestroyImage(dup_image); return((Image *) NULL); } bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */ ClearBounds(bgnd_image,&bgnd_bounds,exception); try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception); try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception); #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "background: %s\n", try_cleared?"(pixels cleared)":""); #endif if (try_cleared) { /* Straight background disposal failed to clear pixels needed! Lets try expanding the disposal area of the previous frame, to include the pixels that are cleared. This guaranteed to work, though may not be the most optimized solution. */ try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception); #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n", (double) try_bounds.width,(double) try_bounds.height, (double) try_bounds.x,(double) try_bounds.y, try_bounds.x<0?" (no expand nessary)":""); #endif if (bgnd_bounds.x < 0) bgnd_bounds = try_bounds; else { #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n", (double) bgnd_bounds.width,(double) bgnd_bounds.height, (double) bgnd_bounds.x,(double) bgnd_bounds.y ); #endif if (try_bounds.x < bgnd_bounds.x) { bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x; if (bgnd_bounds.width < try_bounds.width) bgnd_bounds.width = try_bounds.width; bgnd_bounds.x = try_bounds.x; } else { try_bounds.width += try_bounds.x - bgnd_bounds.x; if (bgnd_bounds.width < try_bounds.width) bgnd_bounds.width = try_bounds.width; } if (try_bounds.y < bgnd_bounds.y) { bgnd_bounds.height += bgnd_bounds.y - try_bounds.y; if (bgnd_bounds.height < try_bounds.height) bgnd_bounds.height = try_bounds.height; bgnd_bounds.y = try_bounds.y; } else { try_bounds.height += try_bounds.y - bgnd_bounds.y; if (bgnd_bounds.height < try_bounds.height) bgnd_bounds.height = try_bounds.height; } #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, " to : %.20gx%.20g%+.20g%+.20g\n", (double) bgnd_bounds.width,(double) bgnd_bounds.height, (double) bgnd_bounds.x,(double) bgnd_bounds.y ); #endif } ClearBounds(bgnd_image,&bgnd_bounds,exception); #if DEBUG_OPT_FRAME /* Something strange is happening with a specific animation * CompareAnyLayers (normal method) and CompareClearLayers returns the whole * image, which is not posibly correct! As verified by previous tests. * Something changed beyond the bgnd_bounds clearing. But without being able * to see, or writet he image at this point it is hard to tell what is wrong! * Only CompareOverlay seemed to return something sensible. */ try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception); (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n", (double) try_bounds.width,(double) try_bounds.height, (double) try_bounds.x,(double) try_bounds.y ); try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception); try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception); (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n", (double) try_bounds.width,(double) try_bounds.height, (double) try_bounds.x,(double) try_bounds.y, try_cleared?" (pixels cleared)":""); #endif try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception); #if DEBUG_OPT_FRAME try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception); (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n", (double) try_bounds.width,(double) try_bounds.height, (double) try_bounds.x,(double) try_bounds.y, try_cleared?" (pixels cleared)":""); #endif } /* Test if this background dispose is smaller than any of the other methods we tryed before this (including duplicated frame) */ if (cleared || bgnd_bounds.width*bgnd_bounds.height +try_bounds.width*try_bounds.height < bounds[i-1].width*bounds[i-1].height +dup_bounds.width*dup_bounds.height +bounds[i].width*bounds[i].height) { cleared=MagickFalse; bounds[i-1]=bgnd_bounds; bounds[i]=try_bounds; if (disposals[i-1] == DupDispose) dup_image=DestroyImage(dup_image); disposals[i-1]=BackgroundDispose; #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr,"expand_bgnd: accepted\n"); } else { (void) FormatLocaleFile(stderr,"expand_bgnd: reject\n"); #endif } } /* Finalise choice of dispose, set new prev_image, and junk any extra images as appropriate, */ if (disposals[i-1] == DupDispose) { if (bgnd_image != (Image *) NULL) bgnd_image=DestroyImage(bgnd_image); prev_image=DestroyImage(prev_image); prev_image=dup_image, dup_image=(Image *) NULL; bounds[i+1]=bounds[i]; bounds[i]=dup_bounds; disposals[i-1]=DupDispose; disposals[i]=BackgroundDispose; i++; } else { if (dup_image != (Image *) NULL) dup_image=DestroyImage(dup_image); if (disposals[i-1] != PreviousDispose) prev_image=DestroyImage(prev_image); if (disposals[i-1] == BackgroundDispose) prev_image=bgnd_image, bgnd_image=(Image *) NULL; if (bgnd_image != (Image *) NULL) bgnd_image=DestroyImage(bgnd_image); if (disposals[i-1] == NoneDispose) { prev_image=ReferenceImage(curr->previous); if (prev_image == (Image *) NULL) { bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); disposals=(DisposeType *) RelinquishMagickMemory(disposals); return((Image *) NULL); } } } assert(prev_image != (Image *) NULL); disposals[i]=disposals[i-1]; #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "final %.20g : %s %.20gx%.20g%+.20g%+.20g\n", (double) i-1, CommandOptionToMnemonic(MagickDisposeOptions,disposals[i-1]), (double) bounds[i-1].width,(double) bounds[i-1].height, (double) bounds[i-1].x,(double) bounds[i-1].y ); #endif #if DEBUG_OPT_FRAME (void) FormatLocaleFile(stderr, "interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n", (double) i, CommandOptionToMnemonic(MagickDisposeOptions,disposals[i]), (double) bounds[i].width,(double) bounds[i].height, (double) bounds[i].x,(double) bounds[i].y ); (void) FormatLocaleFile(stderr,"\n"); #endif i++; } prev_image=DestroyImage(prev_image); /* Optimize all images in sequence. */ sans_exception=AcquireExceptionInfo(); i=0; curr=GetFirstImageInList(image); optimized_image=NewImageList(); while (curr != (const Image *) NULL) { prev_image=CloneImage(curr,0,0,MagickTrue,exception); if (prev_image == (Image *) NULL) break; if (prev_image->alpha_trait == UndefinedPixelTrait) (void) SetImageAlphaChannel(prev_image,OpaqueAlphaChannel,exception); if (disposals[i] == DelDispose) { size_t time = 0; while (disposals[i] == DelDispose) { time += curr->delay*1000/curr->ticks_per_second; curr=GetNextImageInList(curr); i++; } time += curr->delay*1000/curr->ticks_per_second; prev_image->ticks_per_second = 100L; prev_image->delay = time*prev_image->ticks_per_second/1000; } bgnd_image=CropImage(prev_image,&bounds[i],sans_exception); prev_image=DestroyImage(prev_image); if (bgnd_image == (Image *) NULL) break; bgnd_image->dispose=disposals[i]; if (disposals[i] == DupDispose) { bgnd_image->delay=0; bgnd_image->dispose=NoneDispose; } else curr=GetNextImageInList(curr); AppendImageToList(&optimized_image,bgnd_image); i++; } sans_exception=DestroyExceptionInfo(sans_exception); bounds=(RectangleInfo *) RelinquishMagickMemory(bounds); disposals=(DisposeType *) RelinquishMagickMemory(disposals); if (curr != (Image *) NULL) { optimized_image=DestroyImageList(optimized_image); return((Image *) NULL); } return(GetFirstImageInList(optimized_image)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % O p t i m i z e I m a g e L a y e r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % OptimizeImageLayers() compares each image the GIF disposed forms of the % previous image in the sequence. From this it attempts to select the % smallest cropped image to replace each frame, while preserving the results % of the GIF animation. % % The format of the OptimizeImageLayers method is: % % Image *OptimizeImageLayers(const Image *image, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o exception: return any errors or warnings in this structure. % */ MagickExport Image *OptimizeImageLayers(const Image *image, ExceptionInfo *exception) { return(OptimizeLayerFrames(image,OptimizeImageLayer,exception)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % O p t i m i z e P l u s I m a g e L a y e r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may % also add or even remove extra frames in the animation, if it improves % the total number of pixels in the resulting GIF animation. % % The format of the OptimizePlusImageLayers method is: % % Image *OptimizePlusImageLayers(const Image *image, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o exception: return any errors or warnings in this structure. % */ MagickExport Image *OptimizePlusImageLayers(const Image *image, ExceptionInfo *exception) { return OptimizeLayerFrames(image,OptimizePlusLayer,exception); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % O p t i m i z e I m a g e T r a n s p a r e n c y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % OptimizeImageTransparency() takes a frame optimized GIF animation, and % compares the overlayed pixels against the disposal image resulting from all % the previous frames in the animation. Any pixel that does not change the % disposal image (and thus does not effect the outcome of an overlay) is made % transparent. % % WARNING: This modifies the current images directly, rather than generate % a new image sequence. % % The format of the OptimizeImageTransperency method is: % % void OptimizeImageTransperency(Image *image,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image sequence % % o exception: return any errors or warnings in this structure. % */ MagickExport void OptimizeImageTransparency(const Image *image, ExceptionInfo *exception) { Image *dispose_image; register Image *next; /* Run the image through the animation sequence */ assert(image != (Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); next=GetFirstImageInList(image); dispose_image=CloneImage(next,next->page.width,next->page.height, MagickTrue,exception); if (dispose_image == (Image *) NULL) return; dispose_image->page=next->page; dispose_image->page.x=0; dispose_image->page.y=0; dispose_image->dispose=NoneDispose; dispose_image->background_color.alpha_trait=BlendPixelTrait; dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha; (void) SetImageBackgroundColor(dispose_image,exception); while (next != (Image *) NULL) { Image *current_image; /* Overlay this frame's image over the previous disposal image */ current_image=CloneImage(dispose_image,0,0,MagickTrue,exception); if (current_image == (Image *) NULL) { dispose_image=DestroyImage(dispose_image); return; } (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y, exception); /* At this point the image would be displayed, for the delay period ** Work out the disposal of the previous image */ if (next->dispose == BackgroundDispose) { RectangleInfo bounds=next->page; bounds.width=next->columns; bounds.height=next->rows; if (bounds.x < 0) { bounds.width+=bounds.x; bounds.x=0; } if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns) bounds.width=current_image->columns-bounds.x; if (bounds.y < 0) { bounds.height+=bounds.y; bounds.y=0; } if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows) bounds.height=current_image->rows-bounds.y; ClearBounds(current_image,&bounds,exception); } if (next->dispose != PreviousDispose) { dispose_image=DestroyImage(dispose_image); dispose_image=current_image; } else current_image=DestroyImage(current_image); /* Optimize Transparency of the next frame (if present) */ next=GetNextImageInList(next); if (next != (Image *) NULL) { (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp, MagickTrue,-(next->page.x),-(next->page.y),exception); } } dispose_image=DestroyImage(dispose_image); return; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e m o v e D u p l i c a t e L a y e r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RemoveDuplicateLayers() removes any image that is exactly the same as the % next image in the given image list. Image size and virtual canvas offset % must also match, though not the virtual canvas size itself. % % No check is made with regards to image disposal setting, though it is the % dispose setting of later image that is kept. Also any time delays are also % added together. As such coalesced image animations should still produce the % same result, though with duplicte frames merged into a single frame. % % The format of the RemoveDuplicateLayers method is: % % void RemoveDuplicateLayers(Image **image,ExceptionInfo *exception) % % A description of each parameter follows: % % o images: the image list % % o exception: return any errors or warnings in this structure. % */ MagickExport void RemoveDuplicateLayers(Image **images, ExceptionInfo *exception) { register Image *curr, *next; RectangleInfo bounds; assert((*images) != (const Image *) NULL); assert((*images)->signature == MagickCoreSignature); if ((*images)->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); curr=GetFirstImageInList(*images); for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next) { if (curr->columns != next->columns || curr->rows != next->rows || curr->page.x != next->page.x || curr->page.y != next->page.y) continue; bounds=CompareImagesBounds(curr,next,CompareAnyLayer,exception); if (bounds.x < 0) { /* the two images are the same, merge time delays and delete one. */ size_t time; time = curr->delay*1000/curr->ticks_per_second; time += next->delay*1000/next->ticks_per_second; next->ticks_per_second = 100L; next->delay = time*curr->ticks_per_second/1000; next->iterations = curr->iterations; *images = curr; (void) DeleteImageFromList(images); } } *images = GetFirstImageInList(*images); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e m o v e Z e r o D e l a y L a y e r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RemoveZeroDelayLayers() removes any image that as a zero delay time. Such % images generally represent intermediate or partial updates in GIF % animations used for file optimization. They are not ment to be displayed % to users of the animation. Viewable images in an animation should have a % time delay of 3 or more centi-seconds (hundredths of a second). % % However if all the frames have a zero time delay, then either the animation % is as yet incomplete, or it is not a GIF animation. This a non-sensible % situation, so no image will be removed and a 'Zero Time Animation' warning % (exception) given. % % No warning will be given if no image was removed because all images had an % appropriate non-zero time delay set. % % Due to the special requirements of GIF disposal handling, GIF animations % should be coalesced first, before calling this function, though that is not % a requirement. % % The format of the RemoveZeroDelayLayers method is: % % void RemoveZeroDelayLayers(Image **image,ExceptionInfo *exception) % % A description of each parameter follows: % % o images: the image list % % o exception: return any errors or warnings in this structure. % */ MagickExport void RemoveZeroDelayLayers(Image **images, ExceptionInfo *exception) { Image *i; assert((*images) != (const Image *) NULL); assert((*images)->signature == MagickCoreSignature); if ((*images)->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); i=GetFirstImageInList(*images); for ( ; i != (Image *) NULL; i=GetNextImageInList(i)) if (i->delay != 0L) break; if (i == (Image *) NULL) { (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename); return; } i=GetFirstImageInList(*images); while (i != (Image *) NULL) { if (i->delay == 0L) { (void) DeleteImageFromList(&i); *images=i; } else i=GetNextImageInList(i); } *images=GetFirstImageInList(*images); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o m p o s i t e L a y e r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % CompositeLayers() compose the source image sequence over the destination % image sequence, starting with the current image in both lists. % % Each layer from the two image lists are composted together until the end of % one of the image lists is reached. The offset of each composition is also % adjusted to match the virtual canvas offsets of each layer. As such the % given offset is relative to the virtual canvas, and not the actual image. % % Composition uses given x and y offsets, as the 'origin' location of the % source images virtual canvas (not the real image) allowing you to compose a % list of 'layer images' into the destiantioni images. This makes it well % sutiable for directly composing 'Clears Frame Animations' or 'Coaleased % Animations' onto a static or other 'Coaleased Animation' destination image % list. GIF disposal handling is not looked at. % % Special case:- If one of the image sequences is the last image (just a % single image remaining), that image is repeatally composed with all the % images in the other image list. Either the source or destination lists may % be the single image, for this situation. % % In the case of a single destination image (or last image given), that image % will ve cloned to match the number of images remaining in the source image % list. % % This is equivelent to the "-layer Composite" Shell API operator. % % % The format of the CompositeLayers method is: % % void CompositeLayers(Image *destination, const CompositeOperator % compose, Image *source, const ssize_t x_offset, const ssize_t y_offset, % ExceptionInfo *exception); % % A description of each parameter follows: % % o destination: the destination images and results % % o source: source image(s) for the layer composition % % o compose, x_offset, y_offset: arguments passed on to CompositeImages() % % o exception: return any errors or warnings in this structure. % */ static inline void CompositeCanvas(Image *destination, const CompositeOperator compose,Image *source,ssize_t x_offset, ssize_t y_offset,ExceptionInfo *exception) { const char *value; x_offset+=source->page.x-destination->page.x; y_offset+=source->page.y-destination->page.y; value=GetImageArtifact(source,"compose:outside-overlay"); (void) CompositeImage(destination,source,compose, (value != (const char *) NULL) && (IsStringTrue(value) != MagickFalse) ? MagickFalse : MagickTrue,x_offset,y_offset,exception); } MagickExport void CompositeLayers(Image *destination, const CompositeOperator compose, Image *source,const ssize_t x_offset, const ssize_t y_offset,ExceptionInfo *exception) { assert(destination != (Image *) NULL); assert(destination->signature == MagickCoreSignature); assert(source != (Image *) NULL); assert(source->signature == MagickCoreSignature); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); if (source->debug != MagickFalse || destination->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s", source->filename,destination->filename); /* Overlay single source image over destation image/list */ if (source->next == (Image *) NULL) while (destination != (Image *) NULL) { CompositeCanvas(destination, compose, source, x_offset, y_offset, exception); destination=GetNextImageInList(destination); } /* Overlay source image list over single destination. Multiple clones of destination image are created to match source list. Original Destination image becomes first image of generated list. As such the image list pointer does not require any change in caller. Some animation attributes however also needs coping in this case. */ else if (destination->next == (Image *) NULL) { Image *dest = CloneImage(destination,0,0,MagickTrue,exception); CompositeCanvas(destination, compose, source, x_offset, y_offset, exception); /* copy source image attributes ? */ if (source->next != (Image *) NULL) { destination->delay = source->delay; destination->iterations = source->iterations; } source=GetNextImageInList(source); while (source != (Image *) NULL) { AppendImageToList(&destination, CloneImage(dest,0,0,MagickTrue,exception)); destination=GetLastImageInList(destination); CompositeCanvas(destination, compose, source, x_offset, y_offset, exception); destination->delay = source->delay; destination->iterations = source->iterations; source=GetNextImageInList(source); } dest=DestroyImage(dest); } /* Overlay a source image list over a destination image list until either list runs out of images. (Does not repeat) */ else while (source != (Image *) NULL && destination != (Image *) NULL) { CompositeCanvas(destination, compose, source, x_offset, y_offset, exception); source=GetNextImageInList(source); destination=GetNextImageInList(destination); } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M e r g e I m a g e L a y e r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % MergeImageLayers() composes all the image layers from the current given % image onward to produce a single image of the merged layers. % % The inital canvas's size depends on the given LayerMethod, and is % initialized using the first images background color. The images % are then compositied onto that image in sequence using the given % composition that has been assigned to each individual image. % % The format of the MergeImageLayers is: % % Image *MergeImageLayers(Image *image,const LayerMethod method, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image list to be composited together % % o method: the method of selecting the size of the initial canvas. % % MergeLayer: Merge all layers onto a canvas just large enough % to hold all the actual images. The virtual canvas of the % first image is preserved but otherwise ignored. % % FlattenLayer: Use the virtual canvas size of first image. % Images which fall outside this canvas is clipped. % This can be used to 'fill out' a given virtual canvas. % % MosaicLayer: Start with the virtual canvas of the first image, % enlarging left and right edges to contain all images. % Images with negative offsets will be clipped. % % TrimBoundsLayer: Determine the overall bounds of all the image % layers just as in "MergeLayer", then adjust the the canvas % and offsets to be relative to those bounds, without overlaying % the images. % % WARNING: a new image is not returned, the original image % sequence page data is modified instead. % % o exception: return any errors or warnings in this structure. % */ MagickExport Image *MergeImageLayers(Image *image,const LayerMethod method, ExceptionInfo *exception) { #define MergeLayersTag "Merge/Layers" Image *canvas; MagickBooleanType proceed; RectangleInfo page; register const Image *next; size_t number_images, height, width; ssize_t scene; assert(image != (Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); /* Determine canvas image size, and its virtual canvas size and offset */ page=image->page; width=image->columns; height=image->rows; switch (method) { case TrimBoundsLayer: case MergeLayer: default: { next=GetNextImageInList(image); for ( ; next != (Image *) NULL; next=GetNextImageInList(next)) { if (page.x > next->page.x) { width+=page.x-next->page.x; page.x=next->page.x; } if (page.y > next->page.y) { height+=page.y-next->page.y; page.y=next->page.y; } if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x)) width=(size_t) next->page.x+(ssize_t) next->columns-page.x; if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y)) height=(size_t) next->page.y+(ssize_t) next->rows-page.y; } break; } case FlattenLayer: { if (page.width > 0) width=page.width; if (page.height > 0) height=page.height; page.x=0; page.y=0; break; } case MosaicLayer: { if (page.width > 0) width=page.width; if (page.height > 0) height=page.height; for (next=image; next != (Image *) NULL; next=GetNextImageInList(next)) { if (method == MosaicLayer) { page.x=next->page.x; page.y=next->page.y; if ((ssize_t) width < (next->page.x+(ssize_t) next->columns)) width=(size_t) next->page.x+next->columns; if ((ssize_t) height < (next->page.y+(ssize_t) next->rows)) height=(size_t) next->page.y+next->rows; } } page.width=width; page.height=height; page.x=0; page.y=0; } break; } /* Set virtual canvas size if not defined. */ if (page.width == 0) page.width=page.x < 0 ? width : width+page.x; if (page.height == 0) page.height=page.y < 0 ? height : height+page.y; /* Handle "TrimBoundsLayer" method separately to normal 'layer merge'. */ if (method == TrimBoundsLayer) { number_images=GetImageListLength(image); for (scene=0; scene < (ssize_t) number_images; scene++) { image->page.x-=page.x; image->page.y-=page.y; image->page.width=width; image->page.height=height; proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene, number_images); if (proceed == MagickFalse) break; image=GetNextImageInList(image); if (image == (Image *) NULL) break; } return((Image *) NULL); } /* Create canvas size of width and height, and background color. */ canvas=CloneImage(image,width,height,MagickTrue,exception); if (canvas == (Image *) NULL) return((Image *) NULL); (void) SetImageBackgroundColor(canvas,exception); canvas->page=page; canvas->dispose=UndefinedDispose; /* Compose images onto canvas, with progress monitor */ number_images=GetImageListLength(image); for (scene=0; scene < (ssize_t) number_images; scene++) { (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x- canvas->page.x,image->page.y-canvas->page.y,exception); proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene, number_images); if (proceed == MagickFalse) break; image=GetNextImageInList(image); if (image == (Image *) NULL) break; } return(canvas); } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14249/000077500000000000000000000000001417746362400206325ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14249/README000066400000000000000000000003751417746362400215170ustar00rootroot00000000000000 Division by zero Details: https://nvd.nist.gov/vuln/detail/CVE-2019-14249 Fix: https://sourceforge.net/p/libdwarf/code/ci/cb7198abde46c2ae29957ad460da6886eaa606ba/tree/libdwarf/dwarf_elf_load_headers.c?diff=99e77c3894877a1dd80b82808d8309eded4e5599 cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14249/dwarf_elf_load_headers.c000066400000000000000000001734271417746362400254370ustar00rootroot00000000000000/* Copyright 2018 David Anderson. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This reads elf headers and creates generic-elf structures containing the Elf headers. */ #include "config.h" #include #include /* For memcpy etc */ #include #include #include /* for open() */ #include /* for open() */ #include /* for open() */ #ifdef HAVE_UNISTD_H #include /* lseek read close */ #elif defined(_WIN32) && defined(_MSC_VER) #include #endif /* HAVE_UNISTD_H */ /* Windows specific header files */ #if defined(_WIN32) && defined(HAVE_STDAFX_H) #include "stdafx.h" #endif /* HAVE_STDAFX_H */ #include "libdwarfdefs.h" #include "dwarf.h" #include "libdwarf.h" #include "dwarf_base_types.h" #include "dwarf_opaque.h" #include "memcpy_swap.h" #include "dwarf_elfstructs.h" #include "dwarf_reading.h" #include "dwarf_elf_defines.h" #include "dwarf_elfread.h" #include "dwarf_object_detector.h" #include "dwarf_object_read_common.h" #include "dwarf_util.h" #ifndef O_BINARY #define O_BINARY 0 #endif /* O_BINARY */ #ifdef HAVE_UNUSED_ATTRIBUTE #define UNUSEDARG __attribute__ ((unused)) #else #define UNUSEDARG #endif #define TRUE 1 #define FALSE 0 #ifdef WORDS_BIGENDIAN #define ASNAR(func,t,s) \ do { \ unsigned tbyte = sizeof(t) - sizeof(s); \ t = 0; \ func(((char *)&t)+tbyte,&s[0],sizeof(s)); \ } while (0) #else /* LITTLE ENDIAN */ #define ASNAR(func,t,s) \ do { \ t = 0; \ func(&t,&s[0],sizeof(s)); \ } while (0) #endif /* end LITTLE- BIG-ENDIAN */ static int _dwarf_load_elf_section_is_dwarf(const char *sname) { if (!strncmp(sname,".rel",4)) { return FALSE; } if (!strncmp(sname,".debug_",7)) { return TRUE; } if (!strncmp(sname,".zdebug_",8)) { return TRUE; } if (!strcmp(sname,".eh_frame")) { return TRUE; } if (!strncmp(sname,".gdb_index",10)) { return TRUE; } return FALSE; } static int is_empty_section(Dwarf_Unsigned type) { if (type == SHT_NOBITS) { return TRUE; } if (type == SHT_NULL) { return TRUE; } return FALSE; } #if 0 int dwarf_construct_elf_access_path(const char *path, dwarf_elf_object_access_internals_t **mp,int *errcode) { int fd = -1; int res = 0; dwarf_elf_object_access_internals_t *mymp = 0; fd = open(path, O_RDONLY|O_BINARY); if (fd < 0) { *errcode = DW_DLE_PATH_SIZE_TOO_SMALL; return DW_DLV_ERROR; } res = dwarf_construct_elf_access(fd, path,&mymp,errcode); if (res != DW_DLV_OK) { close(fd); return res; } mymp->f_destruct_close_fd = TRUE; *mp = mymp; return res; } #endif /* 0 */ /* Here path is not essential. Pass in with "" if unknown. */ int dwarf_construct_elf_access(int fd, const char *path, dwarf_elf_object_access_internals_t **mp,int *errcode) { unsigned ftype = 0; unsigned endian = 0; unsigned offsetsize = 0; Dwarf_Unsigned filesize = 0; dwarf_elf_object_access_internals_t *mfp = 0; int res = 0; res = dwarf_object_detector_fd(fd, &ftype,&endian,&offsetsize, &filesize, errcode); if (res != DW_DLV_OK) { return res; } mfp = calloc(1,sizeof(dwarf_elf_object_access_internals_t)); if (!mfp) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } /* For non-libelf Elf, call it 'F'. Libelf Elf uses 'E' */ mfp->f_ident[0] = 'F'; mfp->f_ident[1] = 1; mfp->f_fd = fd; mfp->f_destruct_close_fd = FALSE; mfp->f_is_64bit = ((offsetsize==64)?TRUE:FALSE); mfp->f_filesize = filesize; mfp->f_offsetsize = offsetsize; mfp->f_pointersize = offsetsize; mfp->f_endian = endian; mfp->f_ftype = ftype; mfp->f_path = strdup(path); *mp = mfp; return DW_DLV_OK; } /* Caller must zero the passed in pointer after this returns to remind the caller to avoid use of the pointer. */ int dwarf_destruct_elf_access(dwarf_elf_object_access_internals_t* ep, UNUSEDARG int *errcode) { struct generic_shdr *shp = 0; Dwarf_Unsigned shcount = 0; Dwarf_Unsigned i = 0; free(ep->f_ehdr); shp = ep->f_shdr; shcount = ep->f_loc_shdr.g_count; for (i = 0; i < shcount; ++i,++shp) { free(shp->gh_rels); shp->gh_rels = 0; free(shp->gh_content); shp->gh_content = 0; free(shp->gh_sht_group_array); shp->gh_sht_group_array = 0; shp->gh_sht_group_array_count = 0; } free(ep->f_shdr); free(ep->f_phdr); free(ep->f_elf_shstrings_data); free(ep->f_dynamic); free(ep->f_symtab_sect_strings); free(ep->f_dynsym_sect_strings); free(ep->f_symtab); free(ep->f_dynsym); /* if TRUE close f_fd on destruct.*/ if (ep->f_destruct_close_fd) { close(ep->f_fd); } ep->f_ident[0] = 'X'; free(ep->f_path); free(ep); return DW_DLV_OK; } static int generic_ehdr_from_32(dwarf_elf_object_access_internals_t *ep, struct generic_ehdr *ehdr, dw_elf32_ehdr *e, UNUSEDARG int *errcode) { int i = 0; for (i = 0; i < EI_NIDENT; ++i) { ehdr->ge_ident[i] = e->e_ident[i]; } ASNAR(ep->f_copy_word,ehdr->ge_type,e->e_type); ASNAR(ep->f_copy_word,ehdr->ge_machine,e->e_machine); ASNAR(ep->f_copy_word,ehdr->ge_version,e->e_version); ASNAR(ep->f_copy_word,ehdr->ge_entry,e->e_entry); ASNAR(ep->f_copy_word,ehdr->ge_phoff,e->e_phoff); ASNAR(ep->f_copy_word,ehdr->ge_shoff,e->e_shoff); ASNAR(ep->f_copy_word,ehdr->ge_flags,e->e_flags); ASNAR(ep->f_copy_word,ehdr->ge_ehsize,e->e_ehsize); ASNAR(ep->f_copy_word,ehdr->ge_phentsize,e->e_phentsize); ASNAR(ep->f_copy_word,ehdr->ge_phnum,e->e_phnum); ASNAR(ep->f_copy_word,ehdr->ge_shentsize,e->e_shentsize); ASNAR(ep->f_copy_word,ehdr->ge_shnum,e->e_shnum); ASNAR(ep->f_copy_word,ehdr->ge_shstrndx,e->e_shstrndx); ep->f_machine = ehdr->ge_machine; ep->f_ehdr = ehdr; ep->f_loc_ehdr.g_name = "Elf File Header"; ep->f_loc_ehdr.g_offset = 0; ep->f_loc_ehdr.g_count = 1; ep->f_loc_ehdr.g_entrysize = sizeof(dw_elf32_ehdr); ep->f_loc_ehdr.g_totalsize = sizeof(dw_elf32_ehdr); return DW_DLV_OK; } static int generic_ehdr_from_64(dwarf_elf_object_access_internals_t* ep, struct generic_ehdr *ehdr, dw_elf64_ehdr *e, UNUSEDARG int *errcode) { int i = 0; for (i = 0; i < EI_NIDENT; ++i) { ehdr->ge_ident[i] = e->e_ident[i]; } ASNAR(ep->f_copy_word,ehdr->ge_type,e->e_type); ASNAR(ep->f_copy_word,ehdr->ge_machine,e->e_machine); ASNAR(ep->f_copy_word,ehdr->ge_version,e->e_version); ASNAR(ep->f_copy_word,ehdr->ge_entry,e->e_entry); ASNAR(ep->f_copy_word,ehdr->ge_phoff,e->e_phoff); ASNAR(ep->f_copy_word,ehdr->ge_shoff,e->e_shoff); ASNAR(ep->f_copy_word,ehdr->ge_flags,e->e_flags); ASNAR(ep->f_copy_word,ehdr->ge_ehsize,e->e_ehsize); ASNAR(ep->f_copy_word,ehdr->ge_phentsize,e->e_phentsize); ASNAR(ep->f_copy_word,ehdr->ge_phnum,e->e_phnum); ASNAR(ep->f_copy_word,ehdr->ge_shentsize,e->e_shentsize); ASNAR(ep->f_copy_word,ehdr->ge_shnum,e->e_shnum); ASNAR(ep->f_copy_word,ehdr->ge_shstrndx,e->e_shstrndx); ep->f_machine = ehdr->ge_machine; ep->f_ehdr = ehdr; ep->f_loc_ehdr.g_name = "Elf File Header"; ep->f_loc_ehdr.g_offset = 0; ep->f_loc_ehdr.g_count = 1; ep->f_loc_ehdr.g_entrysize = sizeof(dw_elf64_ehdr); ep->f_loc_ehdr.g_totalsize = sizeof(dw_elf64_ehdr); return DW_DLV_OK; } #if 0 /* not used */ static int generic_phdr_from_phdr32(dwarf_elf_object_access_internals_t* ep, struct generic_phdr **phdr_out, Dwarf_Unsigned * count_out, Dwarf_Unsigned offset, Dwarf_Unsigned entsize, Dwarf_Unsigned count, int *errcode) { dw_elf32_phdr *pph =0; dw_elf32_phdr *orig_pph =0; struct generic_phdr *gphdr =0; struct generic_phdr *orig_gphdr =0; Dwarf_Unsigned i = 0; int res = 0; *count_out = 0; pph = (dw_elf32_phdr *)calloc(count, entsize); if (pph == 0) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } gphdr = (struct generic_phdr *)calloc(count,sizeof(*gphdr)); if (gphdr == 0) { free(pph); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } orig_pph = pph; orig_gphdr = gphdr; res = RRMOA(ep->f_fd,pph,offset,count*entsize, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(pph); free(gphdr); return res; } for (i = 0; i < count; ++i, pph++,gphdr++) { ASNAR(ep->f_copy_word,gphdr->gp_type,pph->p_type); ASNAR(ep->f_copy_word,gphdr->gp_offset,pph->p_offset); ASNAR(ep->f_copy_word,gphdr->gp_vaddr,pph->p_vaddr); ASNAR(ep->f_copy_word,gphdr->gp_paddr,pph->p_paddr); ASNAR(ep->f_copy_word,gphdr->gp_filesz,pph->p_filesz); ASNAR(ep->f_copy_word,gphdr->gp_memsz,pph->p_memsz); ASNAR(ep->f_copy_word,gphdr->gp_flags,pph->p_flags); ASNAR(ep->f_copy_word,gphdr->gp_align,pph->p_align); } free(orig_pph); *phdr_out = orig_gphdr; *count_out = count; ep->f_phdr = orig_gphdr; ep->f_loc_phdr.g_name = "Program Header"; ep->f_loc_phdr.g_offset = offset; ep->f_loc_phdr.g_count = count; ep->f_loc_phdr.g_entrysize = sizeof(dw_elf32_phdr); ep->f_loc_phdr.g_totalsize = sizeof(dw_elf32_phdr)*count; return DW_DLV_OK; } static int generic_phdr_from_phdr64(dwarf_elf_object_access_internals_t* ep, struct generic_phdr **phdr_out, Dwarf_Unsigned * count_out, Dwarf_Unsigned offset, Dwarf_Unsigned entsize, Dwarf_Unsigned count, int *errcode) { dw_elf64_phdr *pph =0; dw_elf64_phdr *orig_pph =0; struct generic_phdr *gphdr =0; struct generic_phdr *orig_gphdr =0; int res = 0; Dwarf_Unsigned i = 0; *count_out = 0; pph = (dw_elf64_phdr *)calloc(count, entsize); if (pph == 0) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } gphdr = (struct generic_phdr *)calloc(count,sizeof(*gphdr)); if (gphdr == 0) { free(pph); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } orig_pph = pph; orig_gphdr = gphdr; res = RRMOA(ep->f_fd,pph,offset,count*entsize, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(pph); free(gphdr); return res; } for (i = 0; i < count; ++i, pph++,gphdr++) { ASNAR(ep->f_copy_word,gphdr->gp_type,pph->p_type); ASNAR(ep->f_copy_word,gphdr->gp_offset,pph->p_offset); ASNAR(ep->f_copy_word,gphdr->gp_vaddr,pph->p_vaddr); ASNAR(ep->f_copy_word,gphdr->gp_paddr,pph->p_paddr); ASNAR(ep->f_copy_word,gphdr->gp_filesz,pph->p_filesz); ASNAR(ep->f_copy_word,gphdr->gp_memsz,pph->p_memsz); ASNAR(ep->f_copy_word,gphdr->gp_flags,pph->p_flags); ASNAR(ep->f_copy_word,gphdr->gp_align,pph->p_align); } free(orig_pph); *phdr_out = orig_gphdr; *count_out = count; ep->f_phdr = orig_gphdr; ep->f_loc_phdr.g_name = "Program Header"; ep->f_loc_phdr.g_offset = offset; ep->f_loc_phdr.g_count = count; ep->f_loc_phdr.g_entrysize = sizeof(dw_elf64_phdr); ep->f_loc_phdr.g_totalsize = sizeof(dw_elf64_phdr)*count; return DW_DLV_OK; } #endif /* not used */ static int generic_shdr_from_shdr32(dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned * count_out, Dwarf_Unsigned offset, Dwarf_Unsigned entsize, Dwarf_Unsigned count, int *errcode) { dw_elf32_shdr *psh =0; dw_elf32_shdr *orig_psh =0; struct generic_shdr *gshdr =0; struct generic_shdr *orig_gshdr =0; Dwarf_Unsigned i = 0; int res = 0; *count_out = 0; psh = (dw_elf32_shdr *)calloc(count, entsize); if (!psh) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } gshdr = (struct generic_shdr *)calloc(count,sizeof(*gshdr)); if (!gshdr) { free(psh); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } orig_psh = psh; orig_gshdr = gshdr; res = RRMOA(ep->f_fd,psh,offset,count*entsize, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(psh); free(gshdr); return res; } for (i = 0; i < count; ++i, psh++,gshdr++) { gshdr->gh_secnum = i; ASNAR(ep->f_copy_word,gshdr->gh_name,psh->sh_name); ASNAR(ep->f_copy_word,gshdr->gh_type,psh->sh_type); ASNAR(ep->f_copy_word,gshdr->gh_flags,psh->sh_flags); ASNAR(ep->f_copy_word,gshdr->gh_addr,psh->sh_addr); ASNAR(ep->f_copy_word,gshdr->gh_offset,psh->sh_offset); ASNAR(ep->f_copy_word,gshdr->gh_size,psh->sh_size); ASNAR(ep->f_copy_word,gshdr->gh_link,psh->sh_link); ASNAR(ep->f_copy_word,gshdr->gh_info,psh->sh_info); ASNAR(ep->f_copy_word,gshdr->gh_addralign,psh->sh_addralign); ASNAR(ep->f_copy_word,gshdr->gh_entsize,psh->sh_entsize); if (gshdr->gh_type == SHT_REL || gshdr->gh_type == SHT_RELA) { gshdr->gh_reloc_target_secnum = gshdr->gh_info; } } free(orig_psh); *count_out = count; ep->f_shdr = orig_gshdr; ep->f_loc_shdr.g_name = "Section Header"; ep->f_loc_shdr.g_count = count; ep->f_loc_shdr.g_offset = offset; ep->f_loc_shdr.g_entrysize = sizeof(dw_elf32_shdr); ep->f_loc_shdr.g_totalsize = sizeof(dw_elf32_shdr)*count; return DW_DLV_OK; } static int generic_shdr_from_shdr64(dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned * count_out, Dwarf_Unsigned offset, Dwarf_Unsigned entsize, Dwarf_Unsigned count, int *errcode) { dw_elf64_shdr *psh =0; dw_elf64_shdr *orig_psh =0; struct generic_shdr *gshdr =0; struct generic_shdr *orig_gshdr =0; Dwarf_Unsigned i = 0; int res = 0; *count_out = 0; psh = (dw_elf64_shdr *)calloc(count, entsize); if (!psh) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } gshdr = (struct generic_shdr *)calloc(count,sizeof(*gshdr)); if (gshdr == 0) { free(psh); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } orig_psh = psh; orig_gshdr = gshdr; res = RRMOA(ep->f_fd,psh,offset,count*entsize, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(psh); free(gshdr); return res; } for (i = 0; i < count; ++i, psh++,gshdr++) { gshdr->gh_secnum = i; ASNAR(ep->f_copy_word,gshdr->gh_name,psh->sh_name); ASNAR(ep->f_copy_word,gshdr->gh_type,psh->sh_type); ASNAR(ep->f_copy_word,gshdr->gh_flags,psh->sh_flags); ASNAR(ep->f_copy_word,gshdr->gh_addr,psh->sh_addr); ASNAR(ep->f_copy_word,gshdr->gh_offset,psh->sh_offset); ASNAR(ep->f_copy_word,gshdr->gh_size,psh->sh_size); ASNAR(ep->f_copy_word,gshdr->gh_link,psh->sh_link); ASNAR(ep->f_copy_word,gshdr->gh_info,psh->sh_info); ASNAR(ep->f_copy_word,gshdr->gh_addralign,psh->sh_addralign); ASNAR(ep->f_copy_word,gshdr->gh_entsize,psh->sh_entsize); if (gshdr->gh_type == SHT_REL || gshdr->gh_type == SHT_RELA) { gshdr->gh_reloc_target_secnum = gshdr->gh_info; } } free(orig_psh); *count_out = count; ep->f_shdr = orig_gshdr; ep->f_loc_shdr.g_name = "Section Header"; ep->f_loc_shdr.g_count = count; ep->f_loc_shdr.g_offset = offset; ep->f_loc_shdr.g_entrysize = sizeof(dw_elf64_shdr); ep->f_loc_shdr.g_totalsize = sizeof(dw_elf64_shdr)*count; return DW_DLV_OK; } static int dwarf_generic_elf_load_symbols32( dwarf_elf_object_access_internals_t *ep, struct generic_symentry **gsym_out, Dwarf_Unsigned offset,Dwarf_Unsigned size, Dwarf_Unsigned *count_out,int *errcode) { Dwarf_Unsigned ecount = 0; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned i = 0; dw_elf32_sym *psym = 0; dw_elf32_sym *orig_psym = 0; struct generic_symentry * gsym = 0; struct generic_symentry * orig_gsym = 0; int res = 0; ecount = (long)(size/sizeof(dw_elf32_sym)); size2 = ecount * sizeof(dw_elf32_sym); if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } psym = calloc(ecount,sizeof(dw_elf32_sym)); if (!psym) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } gsym = calloc(ecount,sizeof(struct generic_symentry)); if (!gsym) { free(psym); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,psym,offset,size, ep->f_filesize,errcode); if (res!= DW_DLV_OK) { free(psym); free(gsym); return res; } orig_psym = psym; orig_gsym = gsym; for (i = 0; i < ecount; ++i,++psym,++gsym) { Dwarf_Unsigned bind = 0; Dwarf_Unsigned type = 0; ASNAR(ep->f_copy_word,gsym->gs_name,psym->st_name); ASNAR(ep->f_copy_word,gsym->gs_value,psym->st_value); ASNAR(ep->f_copy_word,gsym->gs_size,psym->st_size); ASNAR(ep->f_copy_word,gsym->gs_info,psym->st_info); ASNAR(ep->f_copy_word,gsym->gs_other,psym->st_other); ASNAR(ep->f_copy_word,gsym->gs_shndx,psym->st_shndx); bind = gsym->gs_info >> 4; type = gsym->gs_info & 0xf; gsym->gs_bind = bind; gsym->gs_type = type; } *count_out = ecount; *gsym_out = orig_gsym; free(orig_psym); return DW_DLV_OK; } static int dwarf_generic_elf_load_symbols64( dwarf_elf_object_access_internals_t *ep, struct generic_symentry **gsym_out, Dwarf_Unsigned offset,Dwarf_Unsigned size, Dwarf_Unsigned *count_out,int *errcode) { Dwarf_Unsigned ecount = 0; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned i = 0; dw_elf64_sym *psym = 0; dw_elf64_sym *orig_psym = 0; struct generic_symentry * gsym = 0; struct generic_symentry * orig_gsym = 0; int res = 0; ecount = (long)(size/sizeof(dw_elf64_sym)); size2 = ecount * sizeof(dw_elf64_sym); if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } psym = calloc(ecount,sizeof(dw_elf64_sym)); if (!psym) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } gsym = calloc(ecount,sizeof(struct generic_symentry)); if (!gsym) { free(psym); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,psym,offset,size, ep->f_filesize,errcode); if (res!= DW_DLV_OK) { free(psym); free(gsym); *errcode = DW_DLE_ALLOC_FAIL; return res; } orig_psym = psym; orig_gsym = gsym; for (i = 0; i < ecount; ++i,++psym,++gsym) { Dwarf_Unsigned bind = 0; Dwarf_Unsigned type = 0; ASNAR(ep->f_copy_word,gsym->gs_name,psym->st_name); ASNAR(ep->f_copy_word,gsym->gs_value,psym->st_value); ASNAR(ep->f_copy_word,gsym->gs_size,psym->st_size); ASNAR(ep->f_copy_word,gsym->gs_info,psym->st_info); ASNAR(ep->f_copy_word,gsym->gs_other,psym->st_other); ASNAR(ep->f_copy_word,gsym->gs_shndx,psym->st_shndx); bind = gsym->gs_info >> 4; type = gsym->gs_info & 0xf; gsym->gs_bind = bind; gsym->gs_type = type; } *count_out = ecount; *gsym_out = orig_gsym; free(orig_psym); return DW_DLV_OK; } static int dwarf_generic_elf_load_symbols( dwarf_elf_object_access_internals_t *ep, int secnum, struct generic_shdr *psh, struct generic_symentry **gsym_out, Dwarf_Unsigned *count_out,int *errcode) { int res = 0; struct generic_symentry *gsym = 0; Dwarf_Unsigned count = 0; if (!secnum) { return DW_DLV_NO_ENTRY; } if (ep->f_offsetsize == 32) { res = dwarf_generic_elf_load_symbols32(ep, &gsym, psh->gh_offset,psh->gh_size, &count,errcode); } else if (ep->f_offsetsize == 64) { res = dwarf_generic_elf_load_symbols64(ep, &gsym, psh->gh_offset,psh->gh_size, &count,errcode); } else { *errcode = DW_DLE_OFFSET_SIZE; return DW_DLV_ERROR; } if (res == DW_DLV_OK) { *gsym_out = gsym; *count_out = count; } return res; } #if 0 int dwarf_load_elf_dynsym_symbols( dwarf_elf_object_access_internals_t *ep, int*errcode) { int res = 0; struct generic_symentry *gsym = 0; Dwarf_Unsigned count = 0; Dwarf_Unsigned secnum = ep->f_dynsym_sect_index; struct generic_shdr * psh = 0; if (!secnum) { return DW_DLV_NO_ENTRY; } psh = ep->f_shdr + secnum; res = dwarf_generic_elf_load_symbols(ep, secnum, psh, &gsym, &count,errcode); if (res == DW_DLV_OK) { ep->f_dynsym = gsym; ep->f_loc_dynsym.g_count = count; } return res; } #endif /* 0 */ int _dwarf_load_elf_symtab_symbols( dwarf_elf_object_access_internals_t *ep, int*errcode) { int res = 0; struct generic_symentry *gsym = 0; Dwarf_Unsigned count = 0; Dwarf_Unsigned secnum = ep->f_symtab_sect_index; struct generic_shdr * psh = 0; if (!secnum) { return DW_DLV_NO_ENTRY; } psh = ep->f_shdr + secnum; res = dwarf_generic_elf_load_symbols(ep, secnum, psh, &gsym, &count,errcode); if (res == DW_DLV_OK) { ep->f_symtab = gsym; ep->f_loc_symtab.g_count = count; } return res; } static int generic_rel_from_rela32( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh, dw_elf32_rela *relp, struct generic_rela *grel, int *errcode) { Dwarf_Unsigned ecount = 0; Dwarf_Unsigned size = gsh->gh_size; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned i = 0; ecount = size/sizeof(dw_elf32_rela); size2 = ecount * sizeof(dw_elf32_rela); if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } for (i = 0; i < ecount; ++i,++relp,++grel) { ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); /* addend signed */ ASNAR(ep->f_copy_word,grel->gr_addend,relp->r_addend); SIGN_EXTEND(grel->gr_addend,sizeof(relp->r_addend)); grel->gr_isrela = TRUE; grel->gr_sym = grel->gr_info>>8; /* ELF32_R_SYM */ grel->gr_type = grel->gr_info & 0xff; } return DW_DLV_OK; } static int generic_rel_from_rela64( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh, dw_elf64_rela *relp, struct generic_rela *grel, int *errcode) { Dwarf_Unsigned ecount = 0; Dwarf_Unsigned size = gsh->gh_size; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned i = 0; int objlittleendian = (ep->f_endian == DW_OBJECT_LSB); int ismips64 = (ep->f_machine == EM_MIPS); int issparcv9 = (ep->f_machine == EM_SPARCV9); ecount = size/sizeof(dw_elf64_rela); size2 = ecount * sizeof(dw_elf64_rela); if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } for (i = 0; i < ecount; ++i,++relp,++grel) { ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); ASNAR(ep->f_copy_word,grel->gr_addend,relp->r_addend); SIGN_EXTEND(grel->gr_addend,sizeof(relp->r_addend)); if (ismips64 && objlittleendian) { char realsym[4]; memcpy(realsym,&relp->r_info,sizeof(realsym)); ASNAR(ep->f_copy_word,grel->gr_sym,realsym); grel->gr_type = relp->r_info[7]; grel->gr_type2 = relp->r_info[6]; grel->gr_type3 = relp->r_info[5]; } else if (issparcv9) { /* Always Big Endian? */ char realsym[4]; memcpy(realsym,&relp->r_info,sizeof(realsym)); ASNAR(ep->f_copy_word,grel->gr_sym,realsym); grel->gr_type = relp->r_info[7]; } else { grel->gr_sym = grel->gr_info >> 32; grel->gr_type = grel->gr_info & 0xffffffff; } grel->gr_isrela = TRUE; } return DW_DLV_OK; } #if 0 static int generic_rel_from_rel32( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh, dw_elf32_rel *relp, struct generic_rela *grel,int *errcode) { Dwarf_Unsigned ecount = 0; Dwarf_Unsigned size = gsh->gh_size; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned i = 0; ecount = size/sizeof(dw_elf32_rel); size2 = ecount * sizeof(dw_elf32_rel); if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } for (i = 0; i < ecount; ++i,++relp,++grel) { grel->gr_isrela = 0; ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); grel->gr_addend = 0; /* Unused for plain .rel */ grel->gr_sym = grel->gr_info >>8; /* ELF32_R_SYM */ grel->gr_isrela = FALSE; grel->gr_type = grel->gr_info & 0xff; } return DW_DLV_OK; } #endif /* 0 */ #if 0 static int generic_rel_from_rel64( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh, dw_elf64_rel *relp, struct generic_rela *grel,int *errcode) { Dwarf_Unsigned ecount = 0; Dwarf_Unsigned size = gsh->gh_size; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned i = 0; int objlittleendian = (ep->f_endian == DW_OBJECT_LSB); int ismips64 = (ep->f_machine == EM_MIPS); int issparcv9 = (ep->f_machine == EM_SPARCV9); ecount = size/sizeof(dw_elf64_rel); size2 = ecount * sizeof(dw_elf64_rel); if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } for (i = 0; i < ecount; ++i,++relp,++grel) { grel->gr_isrela = 0; ASNAR(ep->f_copy_word,grel->gr_offset,relp->r_offset); ASNAR(ep->f_copy_word,grel->gr_info,relp->r_info); grel->gr_addend = 0; /* Unused for plain .rel */ if (ismips64 && objlittleendian) { char realsym[4]; memcpy(realsym,&relp->r_info,sizeof(realsym)); ASNAR(ep->f_copy_word,grel->gr_sym,realsym); grel->gr_type = relp->r_info[7]; grel->gr_type2 = relp->r_info[6]; grel->gr_type3 = relp->r_info[5]; } else if (issparcv9) { /* Always Big Endian? */ char realsym[4]; memcpy(realsym,&relp->r_info,sizeof(realsym)); ASNAR(ep->f_copy_word,grel->gr_sym,realsym); grel->gr_type = relp->r_info[7]; } else { grel->gr_sym = grel->gr_info >>32; grel->gr_type = grel->gr_info & 0xffffffff; } grel->gr_isrela = FALSE; } return DW_DLV_OK; } #endif /* 0 */ #if 0 int dwarf_load_elf_dynstr( dwarf_elf_object_access_internals_t *ep, int *errcode) { struct generic_shdr *strpsh = 0; int res = 0; Dwarf_Unsigned strsectindex =0; Dwarf_Unsigned strsectlength = 0; if (!ep->f_dynsym_sect_strings_sect_index) { return DW_DLV_NO_ENTRY; } strsectindex = ep->f_dynsym_sect_strings_sect_index; strsectlength = ep->f_dynsym_sect_strings_max; strpsh = ep->f_shdr + strsectindex; /* Alloc an extra byte as a guaranteed NUL byte at the end of the strings in case the section is corrupted and lacks a NUL at end. */ ep->f_dynsym_sect_strings = calloc(1,strsectlength+1); if (!ep->f_dynsym_sect_strings) { ep->f_dynsym_sect_strings = 0; ep->f_dynsym_sect_strings_max = 0; ep->f_dynsym_sect_strings_sect_index = 0; *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,ep->f_dynsym_sect_strings, strpsh->gh_offset, strsectlength, ep->f_filesize,errcode); if (res != DW_DLV_OK) { ep->f_dynsym_sect_strings = 0; ep->f_dynsym_sect_strings_max = 0; ep->f_dynsym_sect_strings_sect_index = 0; return res; } return DW_DLV_OK; } #endif /* 0 */ int _dwarf_load_elf_symstr( dwarf_elf_object_access_internals_t *ep, int *errcode) { struct generic_shdr *strpsh = 0; int res = 0; Dwarf_Unsigned strsectindex =0; Dwarf_Unsigned strsectlength = 0; if (!ep->f_symtab_sect_strings_sect_index) { return DW_DLV_NO_ENTRY; } strsectindex = ep->f_symtab_sect_strings_sect_index; strsectlength = ep->f_symtab_sect_strings_max; strpsh = ep->f_shdr + strsectindex; /* Alloc an extra byte as a guaranteed NUL byte at the end of the strings in case the section is corrupted and lacks a NUL at end. */ ep->f_symtab_sect_strings = calloc(1,strsectlength+1); if (!ep->f_symtab_sect_strings) { ep->f_symtab_sect_strings = 0; ep->f_symtab_sect_strings_max = 0; ep->f_symtab_sect_strings_sect_index = 0; *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,ep->f_symtab_sect_strings, strpsh->gh_offset, strsectlength, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(ep->f_symtab_sect_strings); ep->f_symtab_sect_strings = 0; ep->f_symtab_sect_strings_max = 0; ep->f_symtab_sect_strings_sect_index = 0; return res; } return DW_DLV_OK; } static int _dwarf_elf_load_sectstrings( dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned stringsection, int *errcode) { int res = 0; struct generic_shdr *psh = 0; Dwarf_Unsigned secoffset = 0; ep->f_elf_shstrings_length = 0; if (stringsection >= ep->f_ehdr->ge_shnum) { *errcode = DW_DLE_SECTION_INDEX_BAD; return DW_DLV_ERROR; } psh = ep->f_shdr + stringsection; secoffset = psh->gh_offset; if (is_empty_section(psh->gh_type)) { *errcode = DW_DLE_ELF_STRING_SECTION_MISSING; return DW_DLV_ERROR; } if (psh->gh_size > ep->f_elf_shstrings_max) { free(ep->f_elf_shstrings_data); ep->f_elf_shstrings_data = (char *)malloc(psh->gh_size); ep->f_elf_shstrings_max = psh->gh_size; if (!ep->f_elf_shstrings_data) { ep->f_elf_shstrings_max = 0; *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } } ep->f_elf_shstrings_length = psh->gh_size; res = RRMOA(ep->f_fd,ep->f_elf_shstrings_data,secoffset, psh->gh_size, ep->f_filesize,errcode); return res; } static int elf_load_sectheaders32( dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned offset,Dwarf_Unsigned entsize, Dwarf_Unsigned count,int *errcode) { Dwarf_Unsigned generic_count = 0; int res = 0; if (count == 0) { return DW_DLV_NO_ENTRY; } if (entsize < sizeof(dw_elf32_shdr)) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } if ((offset > ep->f_filesize) || (entsize > 200) || (count > ep->f_filesize) || ((count *entsize +offset) > ep->f_filesize)) { *errcode = DW_DLE_FILE_OFFSET_BAD; return DW_DLV_ERROR; } res = generic_shdr_from_shdr32(ep,&generic_count, offset,entsize,count,errcode); if (res != DW_DLV_OK) { return res; } if (generic_count != count) { *errcode = DW_DLE_ELF_SECTION_COUNT_MISMATCH; return DW_DLV_ERROR; } return DW_DLV_OK; } static int elf_load_sectheaders64( dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned offset,Dwarf_Unsigned entsize, Dwarf_Unsigned count,int*errcode) { Dwarf_Unsigned generic_count = 0; int res = 0; if (count == 0) { return DW_DLV_NO_ENTRY; } if (entsize < sizeof(dw_elf64_shdr)) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } if ((offset > ep->f_filesize) || (entsize > 200) || (count > ep->f_filesize) || ((count *entsize +offset) > ep->f_filesize)) { *errcode = DW_DLE_FILE_OFFSET_BAD; return DW_DLV_ERROR; } res = generic_shdr_from_shdr64(ep,&generic_count, offset,entsize,count,errcode); if (res != DW_DLV_OK) { return res; } if (generic_count != count) { *errcode = DW_DLE_ELF_SECTION_COUNT_MISMATCH; return DW_DLV_ERROR; } return DW_DLV_OK; } static int _dwarf_elf_load_rela_32( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh, struct generic_rela ** grel_out, Dwarf_Unsigned *count_out, int *errcode) { Dwarf_Unsigned count = 0; Dwarf_Unsigned size = 0; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned sizeg = 0; Dwarf_Unsigned offset = 0; int res = 0; dw_elf32_rela *relp = 0; Dwarf_Unsigned object_reclen = sizeof(dw_elf32_rela); struct generic_rela *grel = 0; offset = gsh->gh_offset; size = gsh->gh_size; if (size == 0) { return DW_DLV_NO_ENTRY; } if ((offset > ep->f_filesize) || (size > ep->f_filesize) || ((size +offset) > ep->f_filesize)) { *errcode = DW_DLE_FILE_OFFSET_BAD; return DW_DLV_ERROR; } count = (long)(size/object_reclen); size2 = count * object_reclen; if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } relp = (dw_elf32_rela *)malloc(size); if (!relp) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,relp,offset,size, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(relp); return res; } sizeg = count*sizeof(struct generic_rela); grel = (struct generic_rela *)malloc(sizeg); if (!grel) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = generic_rel_from_rela32(ep,gsh,relp,grel,errcode); free(relp); if (res == DW_DLV_OK) { gsh->gh_relcount = count; gsh->gh_rels = grel; *count_out = count; *grel_out = grel; return res; } /* Some sort of issue */ count_out = 0; free(grel); return res; } #if 0 static int _dwarf_elf_load_rel_32( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh,struct generic_rela ** grel_out, Dwarf_Unsigned *count_out,int *errcode) { Dwarf_Unsigned count = 0; Dwarf_Unsigned size = 0; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned sizeg = 0; Dwarf_Unsigned offset = 0; int res = 0; dw_elf32_rel* relp = 0; Dwarf_Unsigned object_reclen = sizeof(dw_elf32_rel); struct generic_rela *grel = 0; offset = gsh->gh_offset; size = gsh->gh_size; if (size == 0) { return DW_DLV_NO_ENTRY; } if ((offset > ep->f_filesize) || (size > ep->f_filesize) || ((size +offset) > ep->f_filesize)) { *errcode = DW_DLE_FILE_OFFSET_BAD; return DW_DLV_ERROR; } count = size/object_reclen; size2 = count * object_reclen; if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } relp = (dw_elf32_rel *)malloc(size); if (!relp) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,relp,offset,size, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(relp); return res; } sizeg = count *sizeof(struct generic_rela); grel = (struct generic_rela *)malloc(sizeg); if (!grel) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = generic_rel_from_rel32(ep,gsh,relp,grel,errcode); free(relp); if (res == DW_DLV_OK) { *count_out = count; *grel_out = grel; return res; } /* Some sort of error */ count_out = 0; free (grel); return res; } #endif /* 0 */ #if 0 static int _dwarf_elf_load_rel_64( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh,struct generic_rela ** grel_out, Dwarf_Unsigned *count_out,int *errcode) { Dwarf_Unsigned count = 0; Dwarf_Unsigned size = 0; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned sizeg = 0; Dwarf_Unsigned offset = 0; int res = 0; dw_elf64_rel* relp = 0; Dwarf_Unsigned object_reclen = sizeof(dw_elf64_rel); struct generic_rela *grel = 0; offset = gsh->gh_offset; size = gsh->gh_size; if (size == 0) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } if ((offset > ep->f_filesize) || (size > ep->f_filesize) || ((size +offset) > ep->f_filesize)) { *errcode = DW_DLE_FILE_OFFSET_BAD; return DW_DLV_ERROR; } count = size/object_reclen; size2 = count * object_reclen; if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } relp = (dw_elf64_rel *)malloc(size); if (!relp) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,relp,offset,size, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(relp); return res; } sizeg = count*sizeof(struct generic_rela); grel = (struct generic_rela *)malloc(sizeg); if (!grel) { free(relp); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = generic_rel_from_rel64(ep,gsh,relp,grel,errcode); free(relp); if (res == DW_DLV_OK) { *count_out = count; *grel_out = grel; return res; } /* Some sort of error */ count_out = 0; free (grel); return res; } #endif /* 0 */ static int _dwarf_elf_load_rela_64( dwarf_elf_object_access_internals_t *ep, struct generic_shdr * gsh, struct generic_rela ** grel_out, Dwarf_Unsigned *count_out,int *errcode) { Dwarf_Unsigned count = 0; Dwarf_Unsigned size = 0; Dwarf_Unsigned size2 = 0; Dwarf_Unsigned sizeg = 0; Dwarf_Unsigned offset = 0; int res = 0; dw_elf64_rela *relp = 0; Dwarf_Unsigned object_reclen = sizeof(dw_elf64_rela); struct generic_rela *grel = 0; offset = gsh->gh_offset; size = gsh->gh_size; if (size == 0) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } if ((offset > ep->f_filesize) || (size > ep->f_filesize) || ((size +offset) > ep->f_filesize)) { *errcode = DW_DLE_FILE_OFFSET_BAD; return DW_DLV_ERROR; } count = (long)(size/object_reclen); size2 = count * object_reclen; if (size != size2) { *errcode = DW_DLE_SECTION_SIZE_ERROR; return DW_DLV_ERROR; } /* Here want native rela size from the file */ relp = (dw_elf64_rela *)malloc(size); if (!relp) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,relp,offset,size, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(relp); return res; } sizeg = count*sizeof(struct generic_rela); /* Here want generic-record size from the file */ grel = (struct generic_rela *)malloc(sizeg); if (!grel) { free(relp); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = generic_rel_from_rela64(ep,gsh,relp,grel,errcode); free(relp); if (res == DW_DLV_OK) { *count_out = count; *grel_out = grel; return res; } /* Some sort of error */ count_out = 0; free (grel); return res; } /* Is this rela section related to dwarf at all? set oksecnum zero if not. Else set targ secnum. Never returns DW_DLV_NO_ENTRY. */ static int this_is_a_section_dwarf_related( dwarf_elf_object_access_internals_t *ep, struct generic_shdr *gshdr, unsigned *oksecnum_out, int *errcode) { unsigned oksecnum = 0; struct generic_shdr *gstarg = 0; if (gshdr->gh_type != SHT_RELA) { *oksecnum_out = 0; return DW_DLV_OK; } oksecnum = gshdr->gh_reloc_target_secnum; if (oksecnum >= ep->f_loc_shdr.g_count) { *oksecnum_out = 0; *errcode = DW_DLE_ELF_SECTION_ERROR; return DW_DLV_ERROR; } gstarg = ep->f_shdr+oksecnum; if (!gstarg->gh_is_dwarf) { *oksecnum_out = 0; /* no reloc needed. */ return DW_DLV_OK; } *oksecnum_out = oksecnum; return DW_DLV_OK; } /* Secnum here is the secnum of rela. Not the target of the relocations. */ int _dwarf_load_elf_rela( dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned secnum, int *errcode) { struct generic_shdr *gshdr = 0; Dwarf_Unsigned seccount = 0; unsigned offsetsize = 0; struct generic_rela *grp = 0; Dwarf_Unsigned count_read = 0; int res = 0; unsigned oksec = 0; if (!ep) { *errcode = DW_DLE_INTERNAL_NULL_POINTER; return DW_DLV_ERROR; } offsetsize = ep->f_offsetsize; seccount = ep->f_loc_shdr.g_count; if (secnum >= seccount) { *errcode = DW_DLE_ELF_SECTION_ERROR; return DW_DLV_ERROR; } gshdr = ep->f_shdr +secnum; if (is_empty_section(gshdr->gh_type)) { return DW_DLV_NO_ENTRY; } res = this_is_a_section_dwarf_related(ep,gshdr,&oksec,errcode); if (res == DW_DLV_ERROR) { return res; } if (!oksec) { return DW_DLV_OK; } /* We will actually read these relocations. Others get ignored. */ if (offsetsize == 32) { res = _dwarf_elf_load_rela_32(ep, gshdr,&grp,&count_read,errcode); } else if (offsetsize == 64) { res = _dwarf_elf_load_rela_64(ep, gshdr,&grp,&count_read,errcode); } else { *errcode = DW_DLE_OFFSET_SIZE; return DW_DLV_ERROR; } if (res == DW_DLV_ERROR) { return res; } if (res == DW_DLV_NO_ENTRY) { return res; } gshdr->gh_rels = grp; gshdr->gh_relcount = count_read; return DW_DLV_OK; } #if 0 int _dwarf_load_elf_rel( dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned secnum, int *errcode) { struct generic_shdr *gshdr = 0; Dwarf_Unsigned generic_count = 0; unsigned offsetsize = 0; struct generic_rela *grp = 0; Dwarf_Unsigned count_read = 0; int res = 0; if (!ep) { *errcode = DW_DLE_INTERNAL_NULL_POINTER; return DW_DLV_ERROR; } offsetsize = ep->f_offsetsize; generic_count = ep->f_loc_shdr.g_count; if (secnum >= generic_count) { *errcode = DW_DLE_ELF_SECTION_ERROR; return DW_DLV_ERROR; } gshdr = ep->f_shdr +secnum; if (is_empty_section(gshdr->gh_type)) { return DW_DLV_NO_ENTRY; } if (offsetsize == 32) { res = _dwarf_elf_load_rel_32(ep, gshdr,&grp,&count_read,errcode); } else if (offsetsize == 64) { res = _dwarf_elf_load_rel_64(ep, gshdr,&grp,&count_read,errcode); } else { *errcode = DW_DLE_OFFSET_SIZE; return DW_DLV_ERROR; } if (res == DW_DLV_ERROR) { return res; } if (res == DW_DLV_NO_ENTRY) { return res; } gshdr->gh_rels = grp; gshdr->gh_relcount = count_read; return DW_DLV_OK; } #endif /* 0 */ static int validate_section_name_string(Dwarf_Unsigned section_length, Dwarf_Unsigned string_loc_index, const char * strings_start, int * errcode) { const char *endpoint = strings_start + section_length; const char *cur = 0; if (section_length <= string_loc_index) { *errcode = DW_DLE_SECTION_STRING_OFFSET_BAD; return DW_DLV_ERROR; } cur = string_loc_index+strings_start; for ( ; cur < endpoint; ++cur) { if (!*cur) { return DW_DLV_OK; } } *errcode = DW_DLE_SECTION_STRING_OFFSET_BAD; return DW_DLV_ERROR; } static int _dwarf_elf_load_sect_namestring( dwarf_elf_object_access_internals_t *ep, int *errcode) { struct generic_shdr *gshdr = 0; Dwarf_Unsigned generic_count = 0; Dwarf_Unsigned i = 1; const char *stringsecbase = 0; stringsecbase = ep->f_elf_shstrings_data; gshdr = ep->f_shdr; generic_count = ep->f_loc_shdr.g_count; for (i = 0; i < generic_count; i++, ++gshdr) { const char *namestr = ""; int res = 0; res = validate_section_name_string(ep->f_elf_shstrings_length, gshdr->gh_name, stringsecbase, errcode); if (res != DW_DLV_OK) { gshdr->gh_namestring = namestr; return res; } gshdr->gh_namestring = stringsecbase + gshdr->gh_name; } return DW_DLV_OK; } static int elf_load_elf_header32( dwarf_elf_object_access_internals_t *ep,int *errcode) { int res = 0; dw_elf32_ehdr ehdr32; struct generic_ehdr *ehdr = 0; res = RRMOA(ep->f_fd,&ehdr32,0,sizeof(ehdr32), ep->f_filesize,errcode); if (res != DW_DLV_OK) { return res; } ehdr = (struct generic_ehdr *)calloc(1, sizeof(struct generic_ehdr)); if (!ehdr) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = generic_ehdr_from_32(ep,ehdr,&ehdr32,errcode); return res; } static int elf_load_elf_header64( dwarf_elf_object_access_internals_t *ep,int *errcode) { int res = 0; dw_elf64_ehdr ehdr64; struct generic_ehdr *ehdr = 0; res = RRMOA(ep->f_fd,&ehdr64,0,sizeof(ehdr64), ep->f_filesize,errcode); if (res != DW_DLV_OK) { return res; } ehdr = (struct generic_ehdr *)calloc(1, sizeof(struct generic_ehdr)); if (!ehdr) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } res = generic_ehdr_from_64(ep,ehdr,&ehdr64,errcode); return res; } static int validate_struct_sizes( #ifdef HAVE_ELF_H int*errcode #else UNUSEDARG int*errcode #endif ) { #ifdef HAVE_ELF_H /* This is a sanity check when we have an elf.h to check against. */ if (sizeof(Elf32_Ehdr) != sizeof(dw_elf32_ehdr)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf64_Ehdr) != sizeof(dw_elf64_ehdr)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf32_Shdr) != sizeof(dw_elf32_shdr)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf64_Shdr) != sizeof(dw_elf64_shdr)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf32_Phdr) != sizeof(dw_elf32_phdr)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf64_Phdr) != sizeof(dw_elf64_phdr)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf32_Rel) != sizeof(dw_elf32_rel)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf64_Rel) != sizeof(dw_elf64_rel)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf32_Rela) != sizeof(dw_elf32_rela)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf64_Rela) != sizeof(dw_elf64_rela)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf32_Sym) != sizeof(dw_elf32_sym)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } if (sizeof(Elf64_Sym) != sizeof(dw_elf64_sym)) { *errcode = DW_DLE_BAD_TYPE_SIZE; return DW_DLV_ERROR; } #endif /* HAVE_ELF_H */ return DW_DLV_OK; } int _dwarf_load_elf_header( dwarf_elf_object_access_internals_t *ep,int*errcode) { unsigned offsetsize = ep->f_offsetsize; int res = 0; res = validate_struct_sizes(errcode); if (res != DW_DLV_OK) { return res; } if (offsetsize == 32) { res = elf_load_elf_header32(ep,errcode); } else if (offsetsize == 64) { if (sizeof(Dwarf_Unsigned) < 8) { *errcode = DW_DLE_INTEGER_TOO_SMALL; return DW_DLV_ERROR; } res = elf_load_elf_header64(ep,errcode); } else { *errcode = DW_DLE_OFFSET_SIZE; return DW_DLV_ERROR; } return res; } static int validate_links( dwarf_elf_object_access_internals_t *ep, Dwarf_Unsigned knownsect, Dwarf_Unsigned string_sect, int *errcode) { struct generic_shdr* pshk = 0; if (!knownsect) { return DW_DLV_OK; } if (!string_sect) { *errcode = DW_DLE_ELF_STRING_SECTION_ERROR; return DW_DLV_ERROR; } pshk = ep->f_shdr + knownsect; if (string_sect != pshk->gh_link) { *errcode = DW_DLE_ELF_SECTION_LINK_ERROR; return DW_DLV_ERROR; } return DW_DLV_OK; } static int string_endswith(const char *n,const char *q) { unsigned long len = strlen(n); unsigned long qlen = strlen(q); const char *startpt = 0; if (len < qlen) { return FALSE; } startpt = n + (len-qlen); if (strcmp(startpt,q)) { return FALSE; } return TRUE; } /* We are allowing either SHT_GROUP or .group to indicate a group section, but really one should have both or neither! */ static int elf_sht_groupsec(Dwarf_Unsigned type, const char *sname) { /* ARM compilers name SHT group "__ARM_grp" not .group */ if ((type == SHT_GROUP) || (!strcmp(sname,".group"))) { return TRUE; } return FALSE; } static int elf_flagmatches(Dwarf_Unsigned flagsword,Dwarf_Unsigned flag) { if ((flagsword&flag) == flag) { return TRUE; } return FALSE; } /* For SHT_GROUP sections. */ static int read_gs_section_group( dwarf_elf_object_access_internals_t *ep, struct generic_shdr* psh, int *errcode) { Dwarf_Unsigned i = 0; int res = 0; if (!psh->gh_sht_group_array) { Dwarf_Unsigned seclen = psh->gh_size; char *data = 0; char *dp = 0; Dwarf_Unsigned* grouparray = 0; char dblock[4]; Dwarf_Unsigned va = 0; Dwarf_Unsigned count = 0; int foundone = 0; if (seclen < DWARF_32BIT_SIZE) { *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; return DW_DLV_ERROR; } data = malloc(seclen); if (!data) { *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } dp = data; count = seclen/psh->gh_entsize; if (count > ep->f_loc_shdr.g_count) { /* Impossible */ free(data); *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; return DW_DLV_ERROR; } if (psh->gh_entsize != DWARF_32BIT_SIZE) { *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; free(data); return DW_DLV_ERROR; } res = RRMOA(ep->f_fd,data,psh->gh_offset,seclen, ep->f_filesize,errcode); if (res != DW_DLV_OK) { free(data); return res; } grouparray = malloc(count * sizeof(Dwarf_Unsigned)); if (!grouparray) { free(data); *errcode = DW_DLE_ALLOC_FAIL; return DW_DLV_ERROR; } memcpy(dblock,dp,DWARF_32BIT_SIZE); ASNAR(memcpy,va,dblock); /* There is ambiguity on the endianness of this stuff. */ if (va != 1 && va != 0x1000000) { /* Could be corrupted elf object. */ *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; free(data); free(grouparray); return DW_DLV_ERROR; } grouparray[0] = 1; dp = dp + DWARF_32BIT_SIZE; for (i = 1; i < count; ++i,dp += DWARF_32BIT_SIZE) { Dwarf_Unsigned gseca = 0; Dwarf_Unsigned gsecb = 0; struct generic_shdr* targpsh = 0; memcpy(dblock,dp,DWARF_32BIT_SIZE); ASNAR(memcpy,gseca,dblock); ASNAR(_dwarf_memcpy_swap_bytes,gsecb,dblock); if (!gseca) { free(data); free(grouparray); *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; return DW_DLV_ERROR; } grouparray[i] = gseca; if (gseca > ep->f_loc_shdr.g_count) { /* Might be confused endianness by the compiler generating the SHT_GROUP. This is pretty horrible. */ if (gsecb > ep->f_loc_shdr.g_count) { *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; free(data); free(grouparray); return DW_DLV_ERROR; } /* Ok. Yes, ugly. */ gseca = gsecb; grouparray[i] = gseca; } targpsh = ep->f_shdr + gseca; if (targpsh->gh_section_group_number) { /* multi-assignment to groups. Oops. */ free(data); free(grouparray); *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; return DW_DLV_ERROR; } targpsh->gh_section_group_number = ep->f_sg_next_group_number; foundone = 1; } if (foundone) { ++ep->f_sg_next_group_number; ++ep->f_sht_group_type_section_count; } free(data); psh->gh_sht_group_array = grouparray; psh->gh_sht_group_array_count = count; } return DW_DLV_OK; } /* Does related things. A) Counts the number of SHT_GROUP and for each builds an array of the sections in the group (which we expect are all DWARF-related) and sets the group number in each mentioned section. B) Counts the number of SHF_GROUP flags. C) If gnu groups: ensure all the DWARF sections marked with right group based on A(we will mark unmarked as group 1, DW_GROUPNUMBER_BASE). D) If arm groups (SHT_GROUP zero, SHF_GROUP non-zero): Check the relocations of all SHF_GROUP section FIXME: algorithm needed. If SHT_GROUP and SHF_GROUP this is GNU groups. If no SHT_GROUP and have SHF_GROUP this is arm cc groups and we must use relocation information to identify the group members. It seems(?) impossible for an object to have both dwo sections and (SHF_GROUP or SHT_GROUP), but we do not rule that out here. */ static int _dwarf_elf_setup_all_section_groups( dwarf_elf_object_access_internals_t *ep, int *errcode) { struct generic_shdr* psh = 0; Dwarf_Unsigned i = 0; Dwarf_Unsigned count = 0; int res = 0; count = ep->f_loc_shdr.g_count; psh = ep->f_shdr; /* Does step A and step B */ for (i = 0; i < count; ++psh,++i) { const char *name = psh->gh_namestring; if (is_empty_section(psh->gh_type)) { /* No data here. */ continue; } if (!elf_sht_groupsec(psh->gh_type,name)) { /* Step B */ if (elf_flagmatches(psh->gh_flags,SHF_GROUP)) { ep->f_shf_group_flag_section_count++; } continue; } /* Looks like a section group. Do Step A. */ res =read_gs_section_group(ep,psh,errcode); if (res != DW_DLV_OK) { return res; } } /* Any sections not marked above or here are in grep DW_GROUPNUMBER_BASE (1). Section C. */ psh = ep->f_shdr; for (i = 0; i < count; ++psh,++i) { const char *name = psh->gh_namestring; if (is_empty_section(psh->gh_type)) { /* No data here. */ continue; } if (elf_sht_groupsec(psh->gh_type,name)) { continue; } /* Not a section group */ if (string_endswith(name,".dwo")) { if (psh->gh_section_group_number) { /* multi-assignment to groups. Oops. */ *errcode = DW_DLE_ELF_SECTION_GROUP_ERROR; return DW_DLV_ERROR; } psh->gh_is_dwarf = TRUE; psh->gh_section_group_number = DW_GROUPNUMBER_DWO; ep->f_dwo_group_section_count++; } else if (_dwarf_load_elf_section_is_dwarf(name)) { if (!psh->gh_section_group_number) { psh->gh_section_group_number = DW_GROUPNUMBER_BASE; } psh->gh_is_dwarf = TRUE; } else { /* Do nothing. */ } } if (ep->f_sht_group_type_section_count) { /* Not ARM. Done. */ } if (!ep->f_shf_group_flag_section_count) { /* Nothing more to do. */ return DW_DLV_OK; } return DW_DLV_OK; } static int _dwarf_elf_find_sym_sections( dwarf_elf_object_access_internals_t *ep, int *errcode) { struct generic_shdr* psh = 0; Dwarf_Unsigned i = 0; Dwarf_Unsigned count = 0; int res = 0; count = ep->f_loc_shdr.g_count; psh = ep->f_shdr; for (i = 0; i < count; ++psh,++i) { const char *name = psh->gh_namestring; if (is_empty_section(psh->gh_type)) { /* No data here. */ continue; } if (!strcmp(name,".dynsym")) { ep->f_dynsym_sect_index = i; ep->f_loc_dynsym.g_offset = psh->gh_offset; } else if (!strcmp(name,".dynstr")) { ep->f_dynsym_sect_strings_sect_index = i; ep->f_dynsym_sect_strings_max = psh->gh_size; } else if (!strcmp(name,".symtab")) { ep->f_symtab_sect_index = i; ep->f_loc_symtab.g_offset = psh->gh_offset; } else if (!strcmp(name,".strtab")) { ep->f_symtab_sect_strings_sect_index = i; ep->f_symtab_sect_strings_max = psh->gh_size; } else if (!strcmp(name,".dynamic")) { ep->f_dynamic_sect_index = i; ep->f_loc_dynamic.g_offset = psh->gh_offset; } } #if 0 res = validate_links(ep,ep->f_dynsym_sect_index, ep->f_dynsym_sect_strings_sect_index,errcode); if (res!= DW_DLV_OK) { return res; } #endif /* 0 */ res = validate_links(ep,ep->f_symtab_sect_index, ep->f_symtab_sect_strings_sect_index,errcode); if (res!= DW_DLV_OK) { return res; } return DW_DLV_OK; } int _dwarf_load_elf_sectheaders( dwarf_elf_object_access_internals_t *ep,int*errcode) { int res = 0; if (ep->f_offsetsize == 32) { res = elf_load_sectheaders32(ep,ep->f_ehdr->ge_shoff, ep->f_ehdr->ge_shentsize, ep->f_ehdr->ge_shnum,errcode); } else if (ep->f_offsetsize == 64) { res = elf_load_sectheaders64(ep,ep->f_ehdr->ge_shoff, ep->f_ehdr->ge_shentsize, ep->f_ehdr->ge_shnum,errcode); } else { *errcode = DW_DLE_OFFSET_SIZE; return DW_DLV_ERROR; } if (res != DW_DLV_OK) { return res; } res = _dwarf_elf_load_sectstrings(ep, ep->f_ehdr->ge_shstrndx,errcode); if (res != DW_DLV_OK) { return res; } res = _dwarf_elf_load_sect_namestring(ep,errcode); if (res != DW_DLV_OK) { return res; } res = _dwarf_elf_find_sym_sections(ep,errcode); if (res != DW_DLV_OK) { return res; } res = _dwarf_elf_setup_all_section_groups(ep,errcode); return res; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14249/expected.txt000066400000000000000000000000621417746362400231720ustar00rootroot00000000000000dwarf_elf_load_headers.c:1838:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14284/000077500000000000000000000000001417746362400206315ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14284/README000066400000000000000000000000741417746362400215120ustar00rootroot00000000000000 Details: https://nvd.nist.gov/vuln/detail/CVE-2019-14284 cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14284/expected.txt000066400000000000000000000000421417746362400231670ustar00rootroot00000000000000floppy.c:2131:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14284/floppy.c000066400000000000000000004367561417746362400223330ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/block/floppy.c * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1993, 1994 Alain Knaff * Copyright (C) 1998 Alan Cox */ /* * 02.12.91 - Changed to static variables to indicate need for reset * and recalibrate. This makes some things easier (output_byte reset * checking etc), and means less interrupt jumping in case of errors, * so the code is hopefully easier to understand. */ /* * This file is certainly a mess. I've tried my best to get it working, * but I don't like programming floppies, and I have only one anyway. * Urgel. I should check for more errors, and do more graceful error * recovery. Seems there are problems with several drives. I've tried to * correct them. No promises. */ /* * As with hd.c, all routines within this file can (and will) be called * by interrupts, so extreme caution is needed. A hardware interrupt * handler may not sleep, or a kernel panic will happen. Thus I cannot * call "floppy-on" directly, but have to set a special timer interrupt * etc. */ /* * 28.02.92 - made track-buffering routines, based on the routines written * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus. */ /* * Automatic floppy-detection and formatting written by Werner Almesberger * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with * the floppy-change signal detection. */ /* * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed * FDC data overrun bug, added some preliminary stuff for vertical * recording support. * * 1992/9/17: Added DMA allocation & DMA functions. -- hhb. * * TODO: Errors are still not counted properly. */ /* 1992/9/20 * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl) * modeled after the freeware MS-DOS program fdformat/88 V1.8 by * Christoph H. Hochst\"atter. * I have fixed the shift values to the ones I always use. Maybe a new * ioctl() should be created to be able to modify them. * There is a bug in the driver that makes it impossible to format a * floppy as the first thing after bootup. */ /* * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and * this helped the floppy driver as well. Much cleaner, and still seems to * work. */ /* 1994/6/24 --bbroad-- added the floppy table entries and made * minor modifications to allow 2.88 floppies to be run. */ /* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more * disk types. */ /* * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger * format bug fixes, but unfortunately some new bugs too... */ /* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write * errors to allow safe writing by specialized programs. */ /* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks * by defining bit 1 of the "stretch" parameter to mean put sectors on the * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's * drives are "upside-down"). */ /* * 1995/8/26 -- Andreas Busse -- added Mips support. */ /* * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent * features to asm/floppy.h. */ /* * 1998/1/21 -- Richard Gooch -- devfs support */ /* * 1998/05/07 -- Russell King -- More portability cleanups; moved definition of * interrupt and dma channel to asm/floppy.h. Cleaned up some formatting & * use of '0' for NULL. */ /* * 1998/06/07 -- Alan Cox -- Merged the 2.0.34 fixes for resource allocation * failures. */ /* * 1998/09/20 -- David Weinehall -- Added slow-down code for buggy PS/2-drives. */ /* * 1999/08/13 -- Paul Slootman -- floppy stopped working on Alpha after 24 * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were * being used to store jiffies, which are unsigned longs). */ /* * 2000/08/28 -- Arnaldo Carvalho de Melo * - get rid of check_region * - s/suser/capable/ */ /* * 2001/08/26 -- Paul Gortmaker - fix insmod oops on machines with no * floppy controller (lingering task on list after module is gone... boom.) */ /* * 2002/02/07 -- Anton Altaparmakov - Fix io ports reservation to correct range * (0x3f2-0x3f5, 0x3f7). This fix is a bit of a hack but the proper fix * requires many non-obvious changes in arch dependent code. */ /* 2003/07/28 -- Daniele Bellucci . * Better audit of register_blkdev. */ #undef FLOPPY_SILENT_DCL_CLEAR #define REALLY_SLOW_IO #define DEBUGT 2 #define DPRINT(format, args ...) \ pr_info("floppy%d: " format, current_drive, ## args) #define DCL_DEBUG /* debug disk change line */ #ifdef DCL_DEBUG #define debug_dcl(test, fmt, args ...) \ do { if ((test) & FD_DEBUG) DPRINT(fmt, ## args); } while (0) #else #define debug_dcl(test, fmt, args ...) \ do { if (0) DPRINT(fmt, ## args); } while (0) #endif /* do print messages for unexpected interrupts */ static int print_unex = 1; #include #include #include #include #include #include #define FDPATCHES #include #include #include #include #include #include #include #include #include #include #include #include /* CMOS defines */ #include #include #include #include #include #include #include #include #include #include /* * PS/2 floppies have much slower step rates than regular floppies. * It's been recommended that take about 1/4 of the default speed * in some more extreme cases. */ static DEFINE_MUTEX(floppy_mutex); static int slow_floppy; #include #include static int FLOPPY_IRQ = 6; static int FLOPPY_DMA = 2; static int can_use_virtual_dma = 2; /* ======= * can use virtual DMA: * 0 = use of virtual DMA disallowed by config * 1 = use of virtual DMA prescribed by config * 2 = no virtual DMA preference configured. By default try hard DMA, * but fall back on virtual DMA when not enough memory available */ static int use_virtual_dma; /* ======= * use virtual DMA * 0 using hard DMA * 1 using virtual DMA * This variable is set to virtual when a DMA mem problem arises, and * reset back in floppy_grab_irq_and_dma. * It is not safe to reset it in other circumstances, because the floppy * driver may have several buffers in use at once, and we do currently not * record each buffers capabilities */ static DEFINE_SPINLOCK(floppy_lock); static unsigned short virtual_dma_port = 0x3f0; irqreturn_t floppy_interrupt(int irq, void *dev_id); static int set_dor(int fdc, char mask, char data); #define K_64 0x10000 /* 64KB */ /* the following is the mask of allowed drives. By default units 2 and * 3 of both floppy controllers are disabled, because switching on the * motor of these drives causes system hangs on some PCI computers. drive * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if * a drive is allowed. * * NOTE: This must come before we include the arch floppy header because * some ports reference this variable from there. -DaveM */ static int allowed_drive_mask = 0x33; #include static int irqdma_allocated; #include #include #include /* for the compatibility eject ioctl */ #include static LIST_HEAD(floppy_reqs); static struct request *current_req; static int set_next_request(void); #ifndef fd_get_dma_residue #define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA) #endif /* Dma Memory related stuff */ #ifndef fd_dma_mem_free #define fd_dma_mem_free(addr, size) free_pages(addr, get_order(size)) #endif #ifndef fd_dma_mem_alloc #define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL, get_order(size)) #endif #ifndef fd_cacheflush #define fd_cacheflush(addr, size) /* nothing... */ #endif static inline void fallback_on_nodma_alloc(char **addr, size_t l) { #ifdef FLOPPY_CAN_FALLBACK_ON_NODMA if (*addr) return; /* we have the memory */ if (can_use_virtual_dma != 2) return; /* no fallback allowed */ pr_info("DMA memory shortage. Temporarily falling back on virtual DMA\n"); *addr = (char *)nodma_mem_alloc(l); #else return; #endif } /* End dma memory related stuff */ static unsigned long fake_change; static bool initialized; #define ITYPE(x) (((x) >> 2) & 0x1f) #define TOMINOR(x) ((x & 3) | ((x & 4) << 5)) #define UNIT(x) ((x) & 0x03) /* drive on fdc */ #define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */ /* reverse mapping from unit and fdc to drive */ #define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2)) #define DP (&drive_params[current_drive]) #define DRS (&drive_state[current_drive]) #define DRWE (&write_errors[current_drive]) #define FDCS (&fdc_state[fdc]) #define UDP (&drive_params[drive]) #define UDRS (&drive_state[drive]) #define UDRWE (&write_errors[drive]) #define UFDCS (&fdc_state[FDC(drive)]) #define PH_HEAD(floppy, head) (((((floppy)->stretch & 2) >> 1) ^ head) << 2) #define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH) /* read/write */ #define COMMAND (raw_cmd->cmd[0]) #define DR_SELECT (raw_cmd->cmd[1]) #define TRACK (raw_cmd->cmd[2]) #define HEAD (raw_cmd->cmd[3]) #define SECTOR (raw_cmd->cmd[4]) #define SIZECODE (raw_cmd->cmd[5]) #define SECT_PER_TRACK (raw_cmd->cmd[6]) #define GAP (raw_cmd->cmd[7]) #define SIZECODE2 (raw_cmd->cmd[8]) #define NR_RW 9 /* format */ #define F_SIZECODE (raw_cmd->cmd[2]) #define F_SECT_PER_TRACK (raw_cmd->cmd[3]) #define F_GAP (raw_cmd->cmd[4]) #define F_FILL (raw_cmd->cmd[5]) #define NR_F 6 /* * Maximum disk size (in kilobytes). * This default is used whenever the current disk size is unknown. * [Now it is rather a minimum] */ #define MAX_DISK_SIZE 4 /* 3984 */ /* * globals used by 'result()' */ #define MAX_REPLIES 16 static unsigned char reply_buffer[MAX_REPLIES]; static int inr; /* size of reply buffer, when called from interrupt */ #define ST0 (reply_buffer[0]) #define ST1 (reply_buffer[1]) #define ST2 (reply_buffer[2]) #define ST3 (reply_buffer[0]) /* result of GETSTATUS */ #define R_TRACK (reply_buffer[3]) #define R_HEAD (reply_buffer[4]) #define R_SECTOR (reply_buffer[5]) #define R_SIZECODE (reply_buffer[6]) #define SEL_DLY (2 * HZ / 100) /* * this struct defines the different floppy drive types. */ static struct { struct floppy_drive_params params; const char *name; /* name printed while booting */ } default_drive_params[] = { /* NOTE: the time values in jiffies should be in msec! CMOS drive type | Maximum data rate supported by drive type | | Head load time, msec | | | Head unload time, msec (not used) | | | | Step rate interval, usec | | | | | Time needed for spinup time (jiffies) | | | | | | Timeout for spinning down (jiffies) | | | | | | | Spindown offset (where disk stops) | | | | | | | | Select delay | | | | | | | | | RPS | | | | | | | | | | Max number of tracks | | | | | | | | | | | Interrupt timeout | | | | | | | | | | | | Max nonintlv. sectors | | | | | | | | | | | | | -Max Errors- flags */ {{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0, 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" }, {{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0, 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/ {{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0, 0, { 2, 5, 6,23,10,20,12, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/ {{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/ {{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/ {{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0, 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/ {{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0, 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/ /* | --autodetected formats--- | | | * read_track | | Name printed when booting * | Native format * Frequency of disk change checks */ }; static struct floppy_drive_params drive_params[N_DRIVE]; static struct floppy_drive_struct drive_state[N_DRIVE]; static struct floppy_write_errors write_errors[N_DRIVE]; static struct timer_list motor_off_timer[N_DRIVE]; static struct gendisk *disks[N_DRIVE]; static struct blk_mq_tag_set tag_sets[N_DRIVE]; static struct block_device *opened_bdev[N_DRIVE]; static DEFINE_MUTEX(open_lock); static struct floppy_raw_cmd *raw_cmd, default_raw_cmd; /* * This struct defines the different floppy types. * * Bit 0 of 'stretch' tells if the tracks need to be doubled for some * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch' * tells if the disk is in Commodore 1581 format, which means side 0 sectors * are located on side 1 of the disk but with a side 0 ID, and vice-versa. * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical * side 0 is on physical side 0 (but with the misnamed sector IDs). * 'stretch' should probably be renamed to something more general, like * 'options'. * * Bits 2 through 9 of 'stretch' tell the number of the first sector. * The LSB (bit 2) is flipped. For most disks, the first sector * is 1 (represented by 0x00<<2). For some CP/M and music sampler * disks (such as Ensoniq EPS 16plus) it is 0 (represented as 0x01<<2). * For Amstrad CPC disks it is 0xC1 (represented as 0xC0<<2). * * Other parameters should be self-explanatory (see also setfdprm(8)). */ /* Size | Sectors per track | | Head | | | Tracks | | | | Stretch | | | | | Gap 1 size | | | | | | Data rate, | 0x40 for perp | | | | | | | Spec1 (stepping rate, head unload | | | | | | | | /fmt gap (gap2) */ static struct floppy_struct floppy_type[32] = { { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */ { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */ { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */ { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */ { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */ { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */ { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */ { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */ { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */ { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */ { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */ { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */ { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */ { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */ { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */ { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */ { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */ { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */ { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */ { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */ { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */ { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */ { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */ { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */ { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */ { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */ { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */ { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */ { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */ }; #define SECTSIZE (_FD_SECTSIZE(*floppy)) /* Auto-detection: Disk type used until the next media change occurs. */ static struct floppy_struct *current_type[N_DRIVE]; /* * User-provided type information. current_type points to * the respective entry of this array. */ static struct floppy_struct user_params[N_DRIVE]; static sector_t floppy_sizes[256]; static char floppy_device_name[] = "floppy"; /* * The driver is trying to determine the correct media format * while probing is set. rw_interrupt() clears it after a * successful access. */ static int probing; /* Synchronization of FDC access. */ #define FD_COMMAND_NONE -1 #define FD_COMMAND_ERROR 2 #define FD_COMMAND_OKAY 3 static volatile int command_status = FD_COMMAND_NONE; static unsigned long fdc_busy; static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); static DECLARE_WAIT_QUEUE_HEAD(command_done); /* Errors during formatting are counted here. */ static int format_errors; /* Format request descriptor. */ static struct format_descr format_req; /* * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), * H is head unload time (1=16ms, 2=32ms, etc) */ /* * Track buffer * Because these are written to by the DMA controller, they must * not contain a 64k byte boundary crossing, or data will be * corrupted/lost. */ static char *floppy_track_buffer; static int max_buffer_sectors; static int *errors; typedef void (*done_f)(int); static const struct cont_t { void (*interrupt)(void); /* this is called after the interrupt of the * main command */ void (*redo)(void); /* this is called to retry the operation */ void (*error)(void); /* this is called to tally an error */ done_f done; /* this is called to say if the operation has * succeeded/failed */ } *cont; static void floppy_ready(void); static void floppy_start(void); static void process_fd_request(void); static void recalibrate_floppy(void); static void floppy_shutdown(struct work_struct *); static int floppy_request_regions(int); static void floppy_release_regions(int); static int floppy_grab_irq_and_dma(void); static void floppy_release_irq_and_dma(void); /* * The "reset" variable should be tested whenever an interrupt is scheduled, * after the commands have been sent. This is to ensure that the driver doesn't * get wedged when the interrupt doesn't come because of a failed command. * reset doesn't need to be tested before sending commands, because * output_byte is automatically disabled when reset is set. */ static void reset_fdc(void); /* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */ #define NO_TRACK -1 #define NEED_1_RECAL -2 #define NEED_2_RECAL -3 static atomic_t usage_count = ATOMIC_INIT(0); /* buffer related variables */ static int buffer_track = -1; static int buffer_drive = -1; static int buffer_min = -1; static int buffer_max = -1; /* fdc related variables, should end up in a struct */ static struct floppy_fdc_state fdc_state[N_FDC]; static int fdc; /* current fdc */ static struct workqueue_struct *floppy_wq; static struct floppy_struct *_floppy = floppy_type; static unsigned char current_drive; static long current_count_sectors; static unsigned char fsector_t; /* sector in track */ static unsigned char in_sector_offset; /* offset within physical sector, * expressed in units of 512 bytes */ static inline bool drive_no_geom(int drive) { return !current_type[drive] && !ITYPE(UDRS->fd_device); } #ifndef fd_eject static inline int fd_eject(int drive) { return -EINVAL; } #endif /* * Debugging * ========= */ #ifdef DEBUGT static long unsigned debugtimer; static inline void set_debugt(void) { debugtimer = jiffies; } static inline void debugt(const char *func, const char *msg) { if (DP->flags & DEBUGT) pr_info("%s:%s dtime=%lu\n", func, msg, jiffies - debugtimer); } #else static inline void set_debugt(void) {} static inline void debugt(const char *func, const char *msg) {} #endif /* DEBUGT */ static DECLARE_DELAYED_WORK(fd_timeout, floppy_shutdown); static const char *timeout_message; static void is_alive(const char *func, const char *message) { /* this routine checks whether the floppy driver is "alive" */ if (test_bit(0, &fdc_busy) && command_status < 2 && !delayed_work_pending(&fd_timeout)) { DPRINT("%s: timeout handler died. %s\n", func, message); } } static void (*do_floppy)(void) = NULL; #define OLOGSIZE 20 static void (*lasthandler)(void); static unsigned long interruptjiffies; static unsigned long resultjiffies; static int resultsize; static unsigned long lastredo; static struct output_log { unsigned char data; unsigned char status; unsigned long jiffies; } output_log[OLOGSIZE]; static int output_log_pos; #define current_reqD -1 #define MAXTIMEOUT -2 static void __reschedule_timeout(int drive, const char *message) { unsigned long delay; if (drive == current_reqD) drive = current_drive; if (drive < 0 || drive >= N_DRIVE) { delay = 20UL * HZ; drive = 0; } else delay = UDP->timeout; mod_delayed_work(floppy_wq, &fd_timeout, delay); if (UDP->flags & FD_DEBUG) DPRINT("reschedule timeout %s\n", message); timeout_message = message; } static void reschedule_timeout(int drive, const char *message) { unsigned long flags; spin_lock_irqsave(&floppy_lock, flags); __reschedule_timeout(drive, message); spin_unlock_irqrestore(&floppy_lock, flags); } #define INFBOUND(a, b) (a) = max_t(int, a, b) #define SUPBOUND(a, b) (a) = min_t(int, a, b) /* * Bottom half floppy driver. * ========================== * * This part of the file contains the code talking directly to the hardware, * and also the main service loop (seek-configure-spinup-command) */ /* * disk change. * This routine is responsible for maintaining the FD_DISK_CHANGE flag, * and the last_checked date. * * last_checked is the date of the last check which showed 'no disk change' * FD_DISK_CHANGE is set under two conditions: * 1. The floppy has been changed after some i/o to that floppy already * took place. * 2. No floppy disk is in the drive. This is done in order to ensure that * requests are quickly flushed in case there is no disk in the drive. It * follows that FD_DISK_CHANGE can only be cleared if there is a disk in * the drive. * * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet. * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on * each seek. If a disk is present, the disk change line should also be * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk * change line is set, this means either that no disk is in the drive, or * that it has been removed since the last seek. * * This means that we really have a third possibility too: * The floppy has been changed after the last seek. */ static int disk_change(int drive) { int fdc = FDC(drive); if (time_before(jiffies, UDRS->select_date + UDP->select_delay)) DPRINT("WARNING disk change called early\n"); if (!(FDCS->dor & (0x10 << UNIT(drive))) || (FDCS->dor & 3) != UNIT(drive) || fdc != FDC(drive)) { DPRINT("probing disk change on unselected drive\n"); DPRINT("drive=%d fdc=%d dor=%x\n", drive, FDC(drive), (unsigned int)FDCS->dor); } debug_dcl(UDP->flags, "checking disk change line for drive %d\n", drive); debug_dcl(UDP->flags, "jiffies=%lu\n", jiffies); debug_dcl(UDP->flags, "disk change line=%x\n", fd_inb(FD_DIR) & 0x80); debug_dcl(UDP->flags, "flags=%lx\n", UDRS->flags); if (UDP->flags & FD_BROKEN_DCL) return test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80) { set_bit(FD_VERIFY_BIT, &UDRS->flags); /* verify write protection */ if (UDRS->maxblock) /* mark it changed */ set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); /* invalidate its geometry */ if (UDRS->keep_data >= 0) { if ((UDP->flags & FTD_MSG) && current_type[drive] != NULL) DPRINT("Disk type is undefined after disk change\n"); current_type[drive] = NULL; floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE << 1; } return 1; } else { UDRS->last_checked = jiffies; clear_bit(FD_DISK_NEWCHANGE_BIT, &UDRS->flags); } return 0; } static inline int is_selected(int dor, int unit) { return ((dor & (0x10 << unit)) && (dor & 3) == unit); } static bool is_ready_state(int status) { int state = status & (STATUS_READY | STATUS_DIR | STATUS_DMA); return state == STATUS_READY; } static int set_dor(int fdc, char mask, char data) { unsigned char unit; unsigned char drive; unsigned char newdor; unsigned char olddor; if (FDCS->address == -1) return -1; olddor = FDCS->dor; newdor = (olddor & mask) | data; if (newdor != olddor) { unit = olddor & 0x3; if (is_selected(olddor, unit) && !is_selected(newdor, unit)) { drive = REVDRIVE(fdc, unit); debug_dcl(UDP->flags, "calling disk change from set_dor\n"); disk_change(drive); } FDCS->dor = newdor; fd_outb(newdor, FD_DOR); unit = newdor & 0x3; if (!is_selected(olddor, unit) && is_selected(newdor, unit)) { drive = REVDRIVE(fdc, unit); UDRS->select_date = jiffies; } } return olddor; } static void twaddle(void) { if (DP->select_delay) return; fd_outb(FDCS->dor & ~(0x10 << UNIT(current_drive)), FD_DOR); fd_outb(FDCS->dor, FD_DOR); DRS->select_date = jiffies; } /* * Reset all driver information about the current fdc. * This is needed after a reset, and after a raw command. */ static void reset_fdc_info(int mode) { int drive; FDCS->spec1 = FDCS->spec2 = -1; FDCS->need_configure = 1; FDCS->perp_mode = 1; FDCS->rawcmd = 0; for (drive = 0; drive < N_DRIVE; drive++) if (FDC(drive) == fdc && (mode || UDRS->track != NEED_1_RECAL)) UDRS->track = NEED_2_RECAL; } /* selects the fdc and drive, and enables the fdc's input/dma. */ static void set_fdc(int drive) { if (drive >= 0 && drive < N_DRIVE) { fdc = FDC(drive); current_drive = drive; } if (fdc != 1 && fdc != 0) { pr_info("bad fdc value\n"); return; } set_dor(fdc, ~0, 8); #if N_FDC > 1 set_dor(1 - fdc, ~8, 0); #endif if (FDCS->rawcmd == 2) reset_fdc_info(1); if (fd_inb(FD_STATUS) != STATUS_READY) FDCS->reset = 1; } /* locks the driver */ static int lock_fdc(int drive) { if (WARN(atomic_read(&usage_count) == 0, "Trying to lock fdc while usage count=0\n")) return -1; if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy))) return -EINTR; command_status = FD_COMMAND_NONE; reschedule_timeout(drive, "lock fdc"); set_fdc(drive); return 0; } /* unlocks the driver */ static void unlock_fdc(void) { if (!test_bit(0, &fdc_busy)) DPRINT("FDC access conflict!\n"); raw_cmd = NULL; command_status = FD_COMMAND_NONE; cancel_delayed_work(&fd_timeout); do_floppy = NULL; cont = NULL; clear_bit(0, &fdc_busy); wake_up(&fdc_wait); } /* switches the motor off after a given timeout */ static void motor_off_callback(struct timer_list *t) { unsigned long nr = t - motor_off_timer; unsigned char mask = ~(0x10 << UNIT(nr)); if (WARN_ON_ONCE(nr >= N_DRIVE)) return; set_dor(FDC(nr), mask, 0); } /* schedules motor off */ static void floppy_off(unsigned int drive) { unsigned long volatile delta; int fdc = FDC(drive); if (!(FDCS->dor & (0x10 << UNIT(drive)))) return; del_timer(motor_off_timer + drive); /* make spindle stop in a position which minimizes spinup time * next time */ if (UDP->rps) { delta = jiffies - UDRS->first_read_date + HZ - UDP->spindown_offset; delta = ((delta * UDP->rps) % HZ) / UDP->rps; motor_off_timer[drive].expires = jiffies + UDP->spindown - delta; } add_timer(motor_off_timer + drive); } /* * cycle through all N_DRIVE floppy drives, for disk change testing. * stopping at current drive. This is done before any long operation, to * be sure to have up to date disk change information. */ static void scandrives(void) { int i; int drive; int saved_drive; if (DP->select_delay) return; saved_drive = current_drive; for (i = 0; i < N_DRIVE; i++) { drive = (saved_drive + i + 1) % N_DRIVE; if (UDRS->fd_ref == 0 || UDP->select_delay != 0) continue; /* skip closed drives */ set_fdc(drive); if (!(set_dor(fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) & (0x10 << UNIT(drive)))) /* switch the motor off again, if it was off to * begin with */ set_dor(fdc, ~(0x10 << UNIT(drive)), 0); } set_fdc(saved_drive); } static void empty(void) {} static void (*floppy_work_fn)(void); static void floppy_work_workfn(struct work_struct *work) { floppy_work_fn(); } static DECLARE_WORK(floppy_work, floppy_work_workfn); static void schedule_bh(void (*handler)(void)) { WARN_ON(work_pending(&floppy_work)); floppy_work_fn = handler; queue_work(floppy_wq, &floppy_work); } static void (*fd_timer_fn)(void) = NULL; static void fd_timer_workfn(struct work_struct *work) { fd_timer_fn(); } static DECLARE_DELAYED_WORK(fd_timer, fd_timer_workfn); static void cancel_activity(void) { do_floppy = NULL; cancel_delayed_work_sync(&fd_timer); cancel_work_sync(&floppy_work); } /* this function makes sure that the disk stays in the drive during the * transfer */ static void fd_watchdog(void) { debug_dcl(DP->flags, "calling disk change from watchdog\n"); if (disk_change(current_drive)) { DPRINT("disk removed during i/o\n"); cancel_activity(); cont->done(0); reset_fdc(); } else { cancel_delayed_work(&fd_timer); fd_timer_fn = fd_watchdog; queue_delayed_work(floppy_wq, &fd_timer, HZ / 10); } } static void main_command_interrupt(void) { cancel_delayed_work(&fd_timer); cont->interrupt(); } /* waits for a delay (spinup or select) to pass */ static int fd_wait_for_completion(unsigned long expires, void (*function)(void)) { if (FDCS->reset) { reset_fdc(); /* do the reset during sleep to win time * if we don't need to sleep, it's a good * occasion anyways */ return 1; } if (time_before(jiffies, expires)) { cancel_delayed_work(&fd_timer); fd_timer_fn = function; queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies); return 1; } return 0; } static void setup_DMA(void) { unsigned long f; if (raw_cmd->length == 0) { int i; pr_info("zero dma transfer size:"); for (i = 0; i < raw_cmd->cmd_count; i++) pr_cont("%x,", raw_cmd->cmd[i]); pr_cont("\n"); cont->done(0); FDCS->reset = 1; return; } if (((unsigned long)raw_cmd->kernel_data) % 512) { pr_info("non aligned address: %p\n", raw_cmd->kernel_data); cont->done(0); FDCS->reset = 1; return; } f = claim_dma_lock(); fd_disable_dma(); #ifdef fd_dma_setup if (fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length, (raw_cmd->flags & FD_RAW_READ) ? DMA_MODE_READ : DMA_MODE_WRITE, FDCS->address) < 0) { release_dma_lock(f); cont->done(0); FDCS->reset = 1; return; } release_dma_lock(f); #else fd_clear_dma_ff(); fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length); fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ) ? DMA_MODE_READ : DMA_MODE_WRITE); fd_set_dma_addr(raw_cmd->kernel_data); fd_set_dma_count(raw_cmd->length); virtual_dma_port = FDCS->address; fd_enable_dma(); release_dma_lock(f); #endif } static void show_floppy(void); /* waits until the fdc becomes ready */ static int wait_til_ready(void) { int status; int counter; if (FDCS->reset) return -1; for (counter = 0; counter < 10000; counter++) { status = fd_inb(FD_STATUS); if (status & STATUS_READY) return status; } if (initialized) { DPRINT("Getstatus times out (%x) on fdc %d\n", status, fdc); show_floppy(); } FDCS->reset = 1; return -1; } /* sends a command byte to the fdc */ static int output_byte(char byte) { int status = wait_til_ready(); if (status < 0) return -1; if (is_ready_state(status)) { fd_outb(byte, FD_DATA); output_log[output_log_pos].data = byte; output_log[output_log_pos].status = status; output_log[output_log_pos].jiffies = jiffies; output_log_pos = (output_log_pos + 1) % OLOGSIZE; return 0; } FDCS->reset = 1; if (initialized) { DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n", byte, fdc, status); show_floppy(); } return -1; } /* gets the response from the fdc */ static int result(void) { int i; int status = 0; for (i = 0; i < MAX_REPLIES; i++) { status = wait_til_ready(); if (status < 0) break; status &= STATUS_DIR | STATUS_READY | STATUS_BUSY | STATUS_DMA; if ((status & ~STATUS_BUSY) == STATUS_READY) { resultjiffies = jiffies; resultsize = i; return i; } if (status == (STATUS_DIR | STATUS_READY | STATUS_BUSY)) reply_buffer[i] = fd_inb(FD_DATA); else break; } if (initialized) { DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n", fdc, status, i); show_floppy(); } FDCS->reset = 1; return -1; } #define MORE_OUTPUT -2 /* does the fdc need more output? */ static int need_more_output(void) { int status = wait_til_ready(); if (status < 0) return -1; if (is_ready_state(status)) return MORE_OUTPUT; return result(); } /* Set perpendicular mode as required, based on data rate, if supported. * 82077 Now tested. 1Mbps data rate only possible with 82077-1. */ static void perpendicular_mode(void) { unsigned char perp_mode; if (raw_cmd->rate & 0x40) { switch (raw_cmd->rate & 3) { case 0: perp_mode = 2; break; case 3: perp_mode = 3; break; default: DPRINT("Invalid data rate for perpendicular mode!\n"); cont->done(0); FDCS->reset = 1; /* * convenient way to return to * redo without too much hassle * (deep stack et al.) */ return; } } else perp_mode = 0; if (FDCS->perp_mode == perp_mode) return; if (FDCS->version >= FDC_82077_ORIG) { output_byte(FD_PERPENDICULAR); output_byte(perp_mode); FDCS->perp_mode = perp_mode; } else if (perp_mode) { DPRINT("perpendicular mode not supported by this FDC.\n"); } } /* perpendicular_mode */ static int fifo_depth = 0xa; static int no_fifo; static int fdc_configure(void) { /* Turn on FIFO */ output_byte(FD_CONFIGURE); if (need_more_output() != MORE_OUTPUT) return 0; output_byte(0); output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf)); output_byte(0); /* pre-compensation from track 0 upwards */ return 1; } #define NOMINAL_DTR 500 /* Issue a "SPECIFY" command to set the step rate time, head unload time, * head load time, and DMA disable flag to values needed by floppy. * * The value "dtr" is the data transfer rate in Kbps. It is needed * to account for the data rate-based scaling done by the 82072 and 82077 * FDC types. This parameter is ignored for other types of FDCs (i.e. * 8272a). * * Note that changing the data transfer rate has a (probably deleterious) * effect on the parameters subject to scaling for 82072/82077 FDCs, so * fdc_specify is called again after each data transfer rate * change. * * srt: 1000 to 16000 in microseconds * hut: 16 to 240 milliseconds * hlt: 2 to 254 milliseconds * * These values are rounded up to the next highest available delay time. */ static void fdc_specify(void) { unsigned char spec1; unsigned char spec2; unsigned long srt; unsigned long hlt; unsigned long hut; unsigned long dtr = NOMINAL_DTR; unsigned long scale_dtr = NOMINAL_DTR; int hlt_max_code = 0x7f; int hut_max_code = 0xf; if (FDCS->need_configure && FDCS->version >= FDC_82072A) { fdc_configure(); FDCS->need_configure = 0; } switch (raw_cmd->rate & 0x03) { case 3: dtr = 1000; break; case 1: dtr = 300; if (FDCS->version >= FDC_82078) { /* chose the default rate table, not the one * where 1 = 2 Mbps */ output_byte(FD_DRIVESPEC); if (need_more_output() == MORE_OUTPUT) { output_byte(UNIT(current_drive)); output_byte(0xc0); } } break; case 2: dtr = 250; break; } if (FDCS->version >= FDC_82072) { scale_dtr = dtr; hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */ hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */ } /* Convert step rate from microseconds to milliseconds and 4 bits */ srt = 16 - DIV_ROUND_UP(DP->srt * scale_dtr / 1000, NOMINAL_DTR); if (slow_floppy) srt = srt / 4; SUPBOUND(srt, 0xf); INFBOUND(srt, 0); hlt = DIV_ROUND_UP(DP->hlt * scale_dtr / 2, NOMINAL_DTR); if (hlt < 0x01) hlt = 0x01; else if (hlt > 0x7f) hlt = hlt_max_code; hut = DIV_ROUND_UP(DP->hut * scale_dtr / 16, NOMINAL_DTR); if (hut < 0x1) hut = 0x1; else if (hut > 0xf) hut = hut_max_code; spec1 = (srt << 4) | hut; spec2 = (hlt << 1) | (use_virtual_dma & 1); /* If these parameters did not change, just return with success */ if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) { /* Go ahead and set spec1 and spec2 */ output_byte(FD_SPECIFY); output_byte(FDCS->spec1 = spec1); output_byte(FDCS->spec2 = spec2); } } /* fdc_specify */ /* Set the FDC's data transfer rate on behalf of the specified drive. * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue * of the specify command (i.e. using the fdc_specify function). */ static int fdc_dtr(void) { /* If data rate not already set to desired value, set it. */ if ((raw_cmd->rate & 3) == FDCS->dtr) return 0; /* Set dtr */ fd_outb(raw_cmd->rate & 3, FD_DCR); /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) * need a stabilization period of several milliseconds to be * enforced after data rate changes before R/W operations. * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) */ FDCS->dtr = raw_cmd->rate & 3; return fd_wait_for_completion(jiffies + 2UL * HZ / 100, floppy_ready); } /* fdc_dtr */ static void tell_sector(void) { pr_cont(": track %d, head %d, sector %d, size %d", R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE); } /* tell_sector */ static void print_errors(void) { DPRINT(""); if (ST0 & ST0_ECE) { pr_cont("Recalibrate failed!"); } else if (ST2 & ST2_CRC) { pr_cont("data CRC error"); tell_sector(); } else if (ST1 & ST1_CRC) { pr_cont("CRC error"); tell_sector(); } else if ((ST1 & (ST1_MAM | ST1_ND)) || (ST2 & ST2_MAM)) { if (!probing) { pr_cont("sector not found"); tell_sector(); } else pr_cont("probe failed..."); } else if (ST2 & ST2_WC) { /* seek error */ pr_cont("wrong cylinder"); } else if (ST2 & ST2_BC) { /* cylinder marked as bad */ pr_cont("bad cylinder"); } else { pr_cont("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2); tell_sector(); } pr_cont("\n"); } /* * OK, this error interpreting routine is called after a * DMA read/write has succeeded * or failed, so we check the results, and copy any buffers. * hhb: Added better error reporting. * ak: Made this into a separate routine. */ static int interpret_errors(void) { char bad; if (inr != 7) { DPRINT("-- FDC reply error\n"); FDCS->reset = 1; return 1; } /* check IC to find cause of interrupt */ switch (ST0 & ST0_INTR) { case 0x40: /* error occurred during command execution */ if (ST1 & ST1_EOC) return 0; /* occurs with pseudo-DMA */ bad = 1; if (ST1 & ST1_WP) { DPRINT("Drive is write protected\n"); clear_bit(FD_DISK_WRITABLE_BIT, &DRS->flags); cont->done(0); bad = 2; } else if (ST1 & ST1_ND) { set_bit(FD_NEED_TWADDLE_BIT, &DRS->flags); } else if (ST1 & ST1_OR) { if (DP->flags & FTD_MSG) DPRINT("Over/Underrun - retrying\n"); bad = 0; } else if (*errors >= DP->max_errors.reporting) { print_errors(); } if (ST2 & ST2_WC || ST2 & ST2_BC) /* wrong cylinder => recal */ DRS->track = NEED_2_RECAL; return bad; case 0x80: /* invalid command given */ DPRINT("Invalid FDC command given!\n"); cont->done(0); return 2; case 0xc0: DPRINT("Abnormal termination caused by polling\n"); cont->error(); return 2; default: /* (0) Normal command termination */ return 0; } } /* * This routine is called when everything should be correctly set up * for the transfer (i.e. floppy motor is on, the correct floppy is * selected, and the head is sitting on the right track). */ static void setup_rw_floppy(void) { int i; int r; int flags; unsigned long ready_date; void (*function)(void); flags = raw_cmd->flags; if (flags & (FD_RAW_READ | FD_RAW_WRITE)) flags |= FD_RAW_INTR; if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) { ready_date = DRS->spinup_date + DP->spinup; /* If spinup will take a long time, rerun scandrives * again just before spinup completion. Beware that * after scandrives, we must again wait for selection. */ if (time_after(ready_date, jiffies + DP->select_delay)) { ready_date -= DP->select_delay; function = floppy_start; } else function = setup_rw_floppy; /* wait until the floppy is spinning fast enough */ if (fd_wait_for_completion(ready_date, function)) return; } if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE)) setup_DMA(); if (flags & FD_RAW_INTR) do_floppy = main_command_interrupt; r = 0; for (i = 0; i < raw_cmd->cmd_count; i++) r |= output_byte(raw_cmd->cmd[i]); debugt(__func__, "rw_command"); if (r) { cont->error(); reset_fdc(); return; } if (!(flags & FD_RAW_INTR)) { inr = result(); cont->interrupt(); } else if (flags & FD_RAW_NEED_DISK) fd_watchdog(); } static int blind_seek; /* * This is the routine called after every seek (or recalibrate) interrupt * from the floppy controller. */ static void seek_interrupt(void) { debugt(__func__, ""); if (inr != 2 || (ST0 & 0xF8) != 0x20) { DPRINT("seek failed\n"); DRS->track = NEED_2_RECAL; cont->error(); cont->redo(); return; } if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek) { debug_dcl(DP->flags, "clearing NEWCHANGE flag because of effective seek\n"); debug_dcl(DP->flags, "jiffies=%lu\n", jiffies); clear_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); /* effective seek */ DRS->select_date = jiffies; } DRS->track = ST1; floppy_ready(); } static void check_wp(void) { if (test_bit(FD_VERIFY_BIT, &DRS->flags)) { /* check write protection */ output_byte(FD_GETSTATUS); output_byte(UNIT(current_drive)); if (result() != 1) { FDCS->reset = 1; return; } clear_bit(FD_VERIFY_BIT, &DRS->flags); clear_bit(FD_NEED_TWADDLE_BIT, &DRS->flags); debug_dcl(DP->flags, "checking whether disk is write protected\n"); debug_dcl(DP->flags, "wp=%x\n", ST3 & 0x40); if (!(ST3 & 0x40)) set_bit(FD_DISK_WRITABLE_BIT, &DRS->flags); else clear_bit(FD_DISK_WRITABLE_BIT, &DRS->flags); } } static void seek_floppy(void) { int track; blind_seek = 0; debug_dcl(DP->flags, "calling disk change from %s\n", __func__); if (!test_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags) && disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) { /* the media changed flag should be cleared after the seek. * If it isn't, this means that there is really no disk in * the drive. */ set_bit(FD_DISK_CHANGED_BIT, &DRS->flags); cont->done(0); cont->redo(); return; } if (DRS->track <= NEED_1_RECAL) { recalibrate_floppy(); return; } else if (test_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags) && (raw_cmd->flags & FD_RAW_NEED_DISK) && (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) { /* we seek to clear the media-changed condition. Does anybody * know a more elegant way, which works on all drives? */ if (raw_cmd->track) track = raw_cmd->track - 1; else { if (DP->flags & FD_SILENT_DCL_CLEAR) { set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0); blind_seek = 1; raw_cmd->flags |= FD_RAW_NEED_SEEK; } track = 1; } } else { check_wp(); if (raw_cmd->track != DRS->track && (raw_cmd->flags & FD_RAW_NEED_SEEK)) track = raw_cmd->track; else { setup_rw_floppy(); return; } } do_floppy = seek_interrupt; output_byte(FD_SEEK); output_byte(UNIT(current_drive)); if (output_byte(track) < 0) { reset_fdc(); return; } debugt(__func__, ""); } static void recal_interrupt(void) { debugt(__func__, ""); if (inr != 2) FDCS->reset = 1; else if (ST0 & ST0_ECE) { switch (DRS->track) { case NEED_1_RECAL: debugt(__func__, "need 1 recal"); /* after a second recalibrate, we still haven't * reached track 0. Probably no drive. Raise an * error, as failing immediately might upset * computers possessed by the Devil :-) */ cont->error(); cont->redo(); return; case NEED_2_RECAL: debugt(__func__, "need 2 recal"); /* If we already did a recalibrate, * and we are not at track 0, this * means we have moved. (The only way * not to move at recalibration is to * be already at track 0.) Clear the * new change flag */ debug_dcl(DP->flags, "clearing NEWCHANGE flag because of second recalibrate\n"); clear_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); DRS->select_date = jiffies; /* fall through */ default: debugt(__func__, "default"); /* Recalibrate moves the head by at * most 80 steps. If after one * recalibrate we don't have reached * track 0, this might mean that we * started beyond track 80. Try * again. */ DRS->track = NEED_1_RECAL; break; } } else DRS->track = ST1; floppy_ready(); } static void print_result(char *message, int inr) { int i; DPRINT("%s ", message); if (inr >= 0) for (i = 0; i < inr; i++) pr_cont("repl[%d]=%x ", i, reply_buffer[i]); pr_cont("\n"); } /* interrupt handler. Note that this can be called externally on the Sparc */ irqreturn_t floppy_interrupt(int irq, void *dev_id) { int do_print; unsigned long f; void (*handler)(void) = do_floppy; lasthandler = handler; interruptjiffies = jiffies; f = claim_dma_lock(); fd_disable_dma(); release_dma_lock(f); do_floppy = NULL; if (fdc >= N_FDC || FDCS->address == -1) { /* we don't even know which FDC is the culprit */ pr_info("DOR0=%x\n", fdc_state[0].dor); pr_info("floppy interrupt on bizarre fdc %d\n", fdc); pr_info("handler=%ps\n", handler); is_alive(__func__, "bizarre fdc"); return IRQ_NONE; } FDCS->reset = 0; /* We have to clear the reset flag here, because apparently on boxes * with level triggered interrupts (PS/2, Sparc, ...), it is needed to * emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the * emission of the SENSEI's. * It is OK to emit floppy commands because we are in an interrupt * handler here, and thus we have to fear no interference of other * activity. */ do_print = !handler && print_unex && initialized; inr = result(); if (do_print) print_result("unexpected interrupt", inr); if (inr == 0) { int max_sensei = 4; do { output_byte(FD_SENSEI); inr = result(); if (do_print) print_result("sensei", inr); max_sensei--; } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2 && max_sensei); } if (!handler) { FDCS->reset = 1; return IRQ_NONE; } schedule_bh(handler); is_alive(__func__, "normal interrupt end"); /* FIXME! Was it really for us? */ return IRQ_HANDLED; } static void recalibrate_floppy(void) { debugt(__func__, ""); do_floppy = recal_interrupt; output_byte(FD_RECALIBRATE); if (output_byte(UNIT(current_drive)) < 0) reset_fdc(); } /* * Must do 4 FD_SENSEIs after reset because of ``drive polling''. */ static void reset_interrupt(void) { debugt(__func__, ""); result(); /* get the status ready for set_fdc */ if (FDCS->reset) { pr_info("reset set in interrupt, calling %ps\n", cont->error); cont->error(); /* a reset just after a reset. BAD! */ } cont->redo(); } /* * reset is done by pulling bit 2 of DOR low for a while (old FDCs), * or by setting the self clearing bit 7 of STATUS (newer FDCs) */ static void reset_fdc(void) { unsigned long flags; do_floppy = reset_interrupt; FDCS->reset = 0; reset_fdc_info(0); /* Pseudo-DMA may intercept 'reset finished' interrupt. */ /* Irrelevant for systems with true DMA (i386). */ flags = claim_dma_lock(); fd_disable_dma(); release_dma_lock(flags); if (FDCS->version >= FDC_82072A) fd_outb(0x80 | (FDCS->dtr & 3), FD_STATUS); else { fd_outb(FDCS->dor & ~0x04, FD_DOR); udelay(FD_RESET_DELAY); fd_outb(FDCS->dor, FD_DOR); } } static void show_floppy(void) { int i; pr_info("\n"); pr_info("floppy driver state\n"); pr_info("-------------------\n"); pr_info("now=%lu last interrupt=%lu diff=%lu last called handler=%ps\n", jiffies, interruptjiffies, jiffies - interruptjiffies, lasthandler); pr_info("timeout_message=%s\n", timeout_message); pr_info("last output bytes:\n"); for (i = 0; i < OLOGSIZE; i++) pr_info("%2x %2x %lu\n", output_log[(i + output_log_pos) % OLOGSIZE].data, output_log[(i + output_log_pos) % OLOGSIZE].status, output_log[(i + output_log_pos) % OLOGSIZE].jiffies); pr_info("last result at %lu\n", resultjiffies); pr_info("last redo_fd_request at %lu\n", lastredo); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, reply_buffer, resultsize, true); pr_info("status=%x\n", fd_inb(FD_STATUS)); pr_info("fdc_busy=%lu\n", fdc_busy); if (do_floppy) pr_info("do_floppy=%ps\n", do_floppy); if (work_pending(&floppy_work)) pr_info("floppy_work.func=%ps\n", floppy_work.func); if (delayed_work_pending(&fd_timer)) pr_info("delayed work.function=%p expires=%ld\n", fd_timer.work.func, fd_timer.timer.expires - jiffies); if (delayed_work_pending(&fd_timeout)) pr_info("timer_function=%p expires=%ld\n", fd_timeout.work.func, fd_timeout.timer.expires - jiffies); pr_info("cont=%p\n", cont); pr_info("current_req=%p\n", current_req); pr_info("command_status=%d\n", command_status); pr_info("\n"); } static void floppy_shutdown(struct work_struct *arg) { unsigned long flags; if (initialized) show_floppy(); cancel_activity(); flags = claim_dma_lock(); fd_disable_dma(); release_dma_lock(flags); /* avoid dma going to a random drive after shutdown */ if (initialized) DPRINT("floppy timeout called\n"); FDCS->reset = 1; if (cont) { cont->done(0); cont->redo(); /* this will recall reset when needed */ } else { pr_info("no cont in shutdown!\n"); process_fd_request(); } is_alive(__func__, ""); } /* start motor, check media-changed condition and write protection */ static int start_motor(void (*function)(void)) { int mask; int data; mask = 0xfc; data = UNIT(current_drive); if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) { if (!(FDCS->dor & (0x10 << UNIT(current_drive)))) { set_debugt(); /* no read since this drive is running */ DRS->first_read_date = 0; /* note motor start time if motor is not yet running */ DRS->spinup_date = jiffies; data |= (0x10 << UNIT(current_drive)); } } else if (FDCS->dor & (0x10 << UNIT(current_drive))) mask &= ~(0x10 << UNIT(current_drive)); /* starts motor and selects floppy */ del_timer(motor_off_timer + current_drive); set_dor(fdc, mask, data); /* wait_for_completion also schedules reset if needed. */ return fd_wait_for_completion(DRS->select_date + DP->select_delay, function); } static void floppy_ready(void) { if (FDCS->reset) { reset_fdc(); return; } if (start_motor(floppy_ready)) return; if (fdc_dtr()) return; debug_dcl(DP->flags, "calling disk change from floppy_ready\n"); if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) && disk_change(current_drive) && !DP->select_delay) twaddle(); /* this clears the dcl on certain * drive/controller combinations */ #ifdef fd_chose_dma_mode if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) { unsigned long flags = claim_dma_lock(); fd_chose_dma_mode(raw_cmd->kernel_data, raw_cmd->length); release_dma_lock(flags); } #endif if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)) { perpendicular_mode(); fdc_specify(); /* must be done here because of hut, hlt ... */ seek_floppy(); } else { if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) fdc_specify(); setup_rw_floppy(); } } static void floppy_start(void) { reschedule_timeout(current_reqD, "floppy start"); scandrives(); debug_dcl(DP->flags, "setting NEWCHANGE in floppy_start\n"); set_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); floppy_ready(); } /* * ======================================================================== * here ends the bottom half. Exported routines are: * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc, * start_motor, reset_fdc, reset_fdc_info, interpret_errors. * Initialization also uses output_byte, result, set_dor, floppy_interrupt * and set_dor. * ======================================================================== */ /* * General purpose continuations. * ============================== */ static void do_wakeup(void) { reschedule_timeout(MAXTIMEOUT, "do wakeup"); cont = NULL; command_status += 2; wake_up(&command_done); } static const struct cont_t wakeup_cont = { .interrupt = empty, .redo = do_wakeup, .error = empty, .done = (done_f)empty }; static const struct cont_t intr_cont = { .interrupt = empty, .redo = process_fd_request, .error = empty, .done = (done_f)empty }; static int wait_til_done(void (*handler)(void), bool interruptible) { int ret; schedule_bh(handler); if (interruptible) wait_event_interruptible(command_done, command_status >= 2); else wait_event(command_done, command_status >= 2); if (command_status < 2) { cancel_activity(); cont = &intr_cont; reset_fdc(); return -EINTR; } if (FDCS->reset) command_status = FD_COMMAND_ERROR; if (command_status == FD_COMMAND_OKAY) ret = 0; else ret = -EIO; command_status = FD_COMMAND_NONE; return ret; } static void generic_done(int result) { command_status = result; cont = &wakeup_cont; } static void generic_success(void) { cont->done(1); } static void generic_failure(void) { cont->done(0); } static void success_and_wakeup(void) { generic_success(); cont->redo(); } /* * formatting and rw support. * ========================== */ static int next_valid_format(void) { int probed_format; probed_format = DRS->probed_format; while (1) { if (probed_format >= 8 || !DP->autodetect[probed_format]) { DRS->probed_format = 0; return 1; } if (floppy_type[DP->autodetect[probed_format]].sect) { DRS->probed_format = probed_format; return 0; } probed_format++; } } static void bad_flp_intr(void) { int err_count; if (probing) { DRS->probed_format++; if (!next_valid_format()) return; } err_count = ++(*errors); INFBOUND(DRWE->badness, err_count); if (err_count > DP->max_errors.abort) cont->done(0); if (err_count > DP->max_errors.reset) FDCS->reset = 1; else if (err_count > DP->max_errors.recal) DRS->track = NEED_2_RECAL; } static void set_floppy(int drive) { int type = ITYPE(UDRS->fd_device); if (type) _floppy = floppy_type + type; else _floppy = current_type[drive]; } /* * formatting support. * =================== */ static void format_interrupt(void) { switch (interpret_errors()) { case 1: cont->error(); case 2: break; case 0: cont->done(1); } cont->redo(); } #define FM_MODE(x, y) ((y) & ~(((x)->rate & 0x80) >> 1)) #define CT(x) ((x) | 0xc0) static void setup_format_params(int track) { int n; int il; int count; int head_shift; int track_shift; struct fparm { unsigned char track, head, sect, size; } *here = (struct fparm *)floppy_track_buffer; raw_cmd = &default_raw_cmd; raw_cmd->track = track; raw_cmd->flags = (FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK); raw_cmd->rate = _floppy->rate & 0x43; raw_cmd->cmd_count = NR_F; COMMAND = FM_MODE(_floppy, FD_FORMAT); DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy, format_req.head); F_SIZECODE = FD_SIZECODE(_floppy); F_SECT_PER_TRACK = _floppy->sect << 2 >> F_SIZECODE; F_GAP = _floppy->fmt_gap; F_FILL = FD_FILL_BYTE; raw_cmd->kernel_data = floppy_track_buffer; raw_cmd->length = 4 * F_SECT_PER_TRACK; /* allow for about 30ms for data transport per track */ head_shift = (F_SECT_PER_TRACK + 5) / 6; /* a ``cylinder'' is two tracks plus a little stepping time */ track_shift = 2 * head_shift + 3; /* position of logical sector 1 on this track */ n = (track_shift * format_req.track + head_shift * format_req.head) % F_SECT_PER_TRACK; /* determine interleave */ il = 1; if (_floppy->fmt_gap < 0x22) il++; /* initialize field */ for (count = 0; count < F_SECT_PER_TRACK; ++count) { here[count].track = format_req.track; here[count].head = format_req.head; here[count].sect = 0; here[count].size = F_SIZECODE; } /* place logical sectors */ for (count = 1; count <= F_SECT_PER_TRACK; ++count) { here[n].sect = count; n = (n + il) % F_SECT_PER_TRACK; if (here[n].sect) { /* sector busy, find next free sector */ ++n; if (n >= F_SECT_PER_TRACK) { n -= F_SECT_PER_TRACK; while (here[n].sect) ++n; } } } if (_floppy->stretch & FD_SECTBASEMASK) { for (count = 0; count < F_SECT_PER_TRACK; count++) here[count].sect += FD_SECTBASE(_floppy) - 1; } } static void redo_format(void) { buffer_track = -1; setup_format_params(format_req.track << STRETCH(_floppy)); floppy_start(); debugt(__func__, "queue format request"); } static const struct cont_t format_cont = { .interrupt = format_interrupt, .redo = redo_format, .error = bad_flp_intr, .done = generic_done }; static int do_format(int drive, struct format_descr *tmp_format_req) { int ret; if (lock_fdc(drive)) return -EINTR; set_floppy(drive); if (!_floppy || _floppy->track > DP->tracks || tmp_format_req->track >= _floppy->track || tmp_format_req->head >= _floppy->head || (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) || !_floppy->fmt_gap) { process_fd_request(); return -EINVAL; } format_req = *tmp_format_req; format_errors = 0; cont = &format_cont; errors = &format_errors; ret = wait_til_done(redo_format, true); if (ret == -EINTR) return -EINTR; process_fd_request(); return ret; } /* * Buffer read/write and support * ============================= */ static void floppy_end_request(struct request *req, blk_status_t error) { unsigned int nr_sectors = current_count_sectors; unsigned int drive = (unsigned long)req->rq_disk->private_data; /* current_count_sectors can be zero if transfer failed */ if (error) nr_sectors = blk_rq_cur_sectors(req); if (blk_update_request(req, error, nr_sectors << 9)) return; __blk_mq_end_request(req, error); /* We're done with the request */ floppy_off(drive); current_req = NULL; } /* new request_done. Can handle physical sectors which are smaller than a * logical buffer */ static void request_done(int uptodate) { struct request *req = current_req; int block; char msg[sizeof("request done ") + sizeof(int) * 3]; probing = 0; snprintf(msg, sizeof(msg), "request done %d", uptodate); reschedule_timeout(MAXTIMEOUT, msg); if (!req) { pr_info("floppy.c: no request in request_done\n"); return; } if (uptodate) { /* maintain values for invalidation on geometry * change */ block = current_count_sectors + blk_rq_pos(req); INFBOUND(DRS->maxblock, block); if (block > _floppy->sect) DRS->maxtrack = 1; floppy_end_request(req, 0); } else { if (rq_data_dir(req) == WRITE) { /* record write error information */ DRWE->write_errors++; if (DRWE->write_errors == 1) { DRWE->first_error_sector = blk_rq_pos(req); DRWE->first_error_generation = DRS->generation; } DRWE->last_error_sector = blk_rq_pos(req); DRWE->last_error_generation = DRS->generation; } floppy_end_request(req, BLK_STS_IOERR); } } /* Interrupt handler evaluating the result of the r/w operation */ static void rw_interrupt(void) { int eoc; int ssize; int heads; int nr_sectors; if (R_HEAD >= 2) { /* some Toshiba floppy controllers occasionnally seem to * return bogus interrupts after read/write operations, which * can be recognized by a bad head number (>= 2) */ return; } if (!DRS->first_read_date) DRS->first_read_date = jiffies; nr_sectors = 0; ssize = DIV_ROUND_UP(1 << SIZECODE, 4); if (ST1 & ST1_EOC) eoc = 1; else eoc = 0; if (COMMAND & 0x80) heads = 2; else heads = 1; nr_sectors = (((R_TRACK - TRACK) * heads + R_HEAD - HEAD) * SECT_PER_TRACK + R_SECTOR - SECTOR + eoc) << SIZECODE >> 2; if (nr_sectors / ssize > DIV_ROUND_UP(in_sector_offset + current_count_sectors, ssize)) { DPRINT("long rw: %x instead of %lx\n", nr_sectors, current_count_sectors); pr_info("rs=%d s=%d\n", R_SECTOR, SECTOR); pr_info("rh=%d h=%d\n", R_HEAD, HEAD); pr_info("rt=%d t=%d\n", R_TRACK, TRACK); pr_info("heads=%d eoc=%d\n", heads, eoc); pr_info("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, fsector_t, ssize); pr_info("in_sector_offset=%d\n", in_sector_offset); } nr_sectors -= in_sector_offset; INFBOUND(nr_sectors, 0); SUPBOUND(current_count_sectors, nr_sectors); switch (interpret_errors()) { case 2: cont->redo(); return; case 1: if (!current_count_sectors) { cont->error(); cont->redo(); return; } break; case 0: if (!current_count_sectors) { cont->redo(); return; } current_type[current_drive] = _floppy; floppy_sizes[TOMINOR(current_drive)] = _floppy->size; break; } if (probing) { if (DP->flags & FTD_MSG) DPRINT("Auto-detected floppy type %s in fd%d\n", _floppy->name, current_drive); current_type[current_drive] = _floppy; floppy_sizes[TOMINOR(current_drive)] = _floppy->size; probing = 0; } if (CT(COMMAND) != FD_READ || raw_cmd->kernel_data == bio_data(current_req->bio)) { /* transfer directly from buffer */ cont->done(1); } else if (CT(COMMAND) == FD_READ) { buffer_track = raw_cmd->track; buffer_drive = current_drive; INFBOUND(buffer_max, nr_sectors + fsector_t); } cont->redo(); } /* Compute maximal contiguous buffer size. */ static int buffer_chain_size(void) { struct bio_vec bv; int size; struct req_iterator iter; char *base; base = bio_data(current_req->bio); size = 0; rq_for_each_segment(bv, current_req, iter) { if (page_address(bv.bv_page) + bv.bv_offset != base + size) break; size += bv.bv_len; } return size >> 9; } /* Compute the maximal transfer size */ static int transfer_size(int ssize, int max_sector, int max_size) { SUPBOUND(max_sector, fsector_t + max_size); /* alignment */ max_sector -= (max_sector % _floppy->sect) % ssize; /* transfer size, beginning not aligned */ current_count_sectors = max_sector - fsector_t; return max_sector; } /* * Move data from/to the track buffer to/from the buffer cache. */ static void copy_buffer(int ssize, int max_sector, int max_sector_2) { int remaining; /* number of transferred 512-byte sectors */ struct bio_vec bv; char *buffer; char *dma_buffer; int size; struct req_iterator iter; max_sector = transfer_size(ssize, min(max_sector, max_sector_2), blk_rq_sectors(current_req)); if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE && buffer_max > fsector_t + blk_rq_sectors(current_req)) current_count_sectors = min_t(int, buffer_max - fsector_t, blk_rq_sectors(current_req)); remaining = current_count_sectors << 9; if (remaining > blk_rq_bytes(current_req) && CT(COMMAND) == FD_WRITE) { DPRINT("in copy buffer\n"); pr_info("current_count_sectors=%ld\n", current_count_sectors); pr_info("remaining=%d\n", remaining >> 9); pr_info("current_req->nr_sectors=%u\n", blk_rq_sectors(current_req)); pr_info("current_req->current_nr_sectors=%u\n", blk_rq_cur_sectors(current_req)); pr_info("max_sector=%d\n", max_sector); pr_info("ssize=%d\n", ssize); } buffer_max = max(max_sector, buffer_max); dma_buffer = floppy_track_buffer + ((fsector_t - buffer_min) << 9); size = blk_rq_cur_bytes(current_req); rq_for_each_segment(bv, current_req, iter) { if (!remaining) break; size = bv.bv_len; SUPBOUND(size, remaining); buffer = page_address(bv.bv_page) + bv.bv_offset; if (dma_buffer + size > floppy_track_buffer + (max_buffer_sectors << 10) || dma_buffer < floppy_track_buffer) { DPRINT("buffer overrun in copy buffer %d\n", (int)((floppy_track_buffer - dma_buffer) >> 9)); pr_info("fsector_t=%d buffer_min=%d\n", fsector_t, buffer_min); pr_info("current_count_sectors=%ld\n", current_count_sectors); if (CT(COMMAND) == FD_READ) pr_info("read\n"); if (CT(COMMAND) == FD_WRITE) pr_info("write\n"); break; } if (((unsigned long)buffer) % 512) DPRINT("%p buffer not aligned\n", buffer); if (CT(COMMAND) == FD_READ) memcpy(buffer, dma_buffer, size); else memcpy(dma_buffer, buffer, size); remaining -= size; dma_buffer += size; } if (remaining) { if (remaining > 0) max_sector -= remaining >> 9; DPRINT("weirdness: remaining %d\n", remaining >> 9); } } /* work around a bug in pseudo DMA * (on some FDCs) pseudo DMA does not stop when the CPU stops * sending data. Hence we need a different way to signal the * transfer length: We use SECT_PER_TRACK. Unfortunately, this * does not work with MT, hence we can only transfer one head at * a time */ static void virtualdmabug_workaround(void) { int hard_sectors; int end_sector; if (CT(COMMAND) == FD_WRITE) { COMMAND &= ~0x80; /* switch off multiple track mode */ hard_sectors = raw_cmd->length >> (7 + SIZECODE); end_sector = SECTOR + hard_sectors - 1; if (end_sector > SECT_PER_TRACK) { pr_info("too many sectors %d > %d\n", end_sector, SECT_PER_TRACK); return; } SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK * points to end of transfer */ } } /* * Formulate a read/write request. * this routine decides where to load the data (directly to buffer, or to * tmp floppy area), how much data to load (the size of the buffer, the whole * track, or a single sector) * All floppy_track_buffer handling goes in here. If we ever add track buffer * allocation on the fly, it should be done here. No other part should need * modification. */ static int make_raw_rw_request(void) { int aligned_sector_t; int max_sector; int max_size; int tracksize; int ssize; if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n")) return 0; set_fdc((long)current_req->rq_disk->private_data); raw_cmd = &default_raw_cmd; raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK; raw_cmd->cmd_count = NR_RW; if (rq_data_dir(current_req) == READ) { raw_cmd->flags |= FD_RAW_READ; COMMAND = FM_MODE(_floppy, FD_READ); } else if (rq_data_dir(current_req) == WRITE) { raw_cmd->flags |= FD_RAW_WRITE; COMMAND = FM_MODE(_floppy, FD_WRITE); } else { DPRINT("%s: unknown command\n", __func__); return 0; } max_sector = _floppy->sect * _floppy->head; TRACK = (int)blk_rq_pos(current_req) / max_sector; fsector_t = (int)blk_rq_pos(current_req) % max_sector; if (_floppy->track && TRACK >= _floppy->track) { if (blk_rq_cur_sectors(current_req) & 1) { current_count_sectors = 1; return 1; } else return 0; } HEAD = fsector_t / _floppy->sect; if (((_floppy->stretch & (FD_SWAPSIDES | FD_SECTBASEMASK)) || test_bit(FD_NEED_TWADDLE_BIT, &DRS->flags)) && fsector_t < _floppy->sect) max_sector = _floppy->sect; /* 2M disks have phantom sectors on the first track */ if ((_floppy->rate & FD_2M) && (!TRACK) && (!HEAD)) { max_sector = 2 * _floppy->sect / 3; if (fsector_t >= max_sector) { current_count_sectors = min_t(int, _floppy->sect - fsector_t, blk_rq_sectors(current_req)); return 1; } SIZECODE = 2; } else SIZECODE = FD_SIZECODE(_floppy); raw_cmd->rate = _floppy->rate & 0x43; if ((_floppy->rate & FD_2M) && (TRACK || HEAD) && raw_cmd->rate == 2) raw_cmd->rate = 1; if (SIZECODE) SIZECODE2 = 0xff; else SIZECODE2 = 0x80; raw_cmd->track = TRACK << STRETCH(_floppy); DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy, HEAD); GAP = _floppy->gap; ssize = DIV_ROUND_UP(1 << SIZECODE, 4); SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE; SECTOR = ((fsector_t % _floppy->sect) << 2 >> SIZECODE) + FD_SECTBASE(_floppy); /* tracksize describes the size which can be filled up with sectors * of size ssize. */ tracksize = _floppy->sect - _floppy->sect % ssize; if (tracksize < _floppy->sect) { SECT_PER_TRACK++; if (tracksize <= fsector_t % _floppy->sect) SECTOR--; /* if we are beyond tracksize, fill up using smaller sectors */ while (tracksize <= fsector_t % _floppy->sect) { while (tracksize + ssize > _floppy->sect) { SIZECODE--; ssize >>= 1; } SECTOR++; SECT_PER_TRACK++; tracksize += ssize; } max_sector = HEAD * _floppy->sect + tracksize; } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) { max_sector = _floppy->sect; } else if (!HEAD && CT(COMMAND) == FD_WRITE) { /* for virtual DMA bug workaround */ max_sector = _floppy->sect; } in_sector_offset = (fsector_t % _floppy->sect) % ssize; aligned_sector_t = fsector_t - in_sector_offset; max_size = blk_rq_sectors(current_req); if ((raw_cmd->track == buffer_track) && (current_drive == buffer_drive) && (fsector_t >= buffer_min) && (fsector_t < buffer_max)) { /* data already in track buffer */ if (CT(COMMAND) == FD_READ) { copy_buffer(1, max_sector, buffer_max); return 1; } } else if (in_sector_offset || blk_rq_sectors(current_req) < ssize) { if (CT(COMMAND) == FD_WRITE) { unsigned int sectors; sectors = fsector_t + blk_rq_sectors(current_req); if (sectors > ssize && sectors < ssize + ssize) max_size = ssize + ssize; else max_size = ssize; } raw_cmd->flags &= ~FD_RAW_WRITE; raw_cmd->flags |= FD_RAW_READ; COMMAND = FM_MODE(_floppy, FD_READ); } else if ((unsigned long)bio_data(current_req->bio) < MAX_DMA_ADDRESS) { unsigned long dma_limit; int direct, indirect; indirect = transfer_size(ssize, max_sector, max_buffer_sectors * 2) - fsector_t; /* * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide * on a 64 bit machine! */ max_size = buffer_chain_size(); dma_limit = (MAX_DMA_ADDRESS - ((unsigned long)bio_data(current_req->bio))) >> 9; if ((unsigned long)max_size > dma_limit) max_size = dma_limit; /* 64 kb boundaries */ if (CROSS_64KB(bio_data(current_req->bio), max_size << 9)) max_size = (K_64 - ((unsigned long)bio_data(current_req->bio)) % K_64) >> 9; direct = transfer_size(ssize, max_sector, max_size) - fsector_t; /* * We try to read tracks, but if we get too many errors, we * go back to reading just one sector at a time. * * This means we should be able to read a sector even if there * are other bad sectors on this track. */ if (!direct || (indirect * 2 > direct * 3 && *errors < DP->max_errors.read_track && ((!probing || (DP->read_track & (1 << DRS->probed_format)))))) { max_size = blk_rq_sectors(current_req); } else { raw_cmd->kernel_data = bio_data(current_req->bio); raw_cmd->length = current_count_sectors << 9; if (raw_cmd->length == 0) { DPRINT("%s: zero dma transfer attempted\n", __func__); DPRINT("indirect=%d direct=%d fsector_t=%d\n", indirect, direct, fsector_t); return 0; } virtualdmabug_workaround(); return 2; } } if (CT(COMMAND) == FD_READ) max_size = max_sector; /* unbounded */ /* claim buffer track if needed */ if (buffer_track != raw_cmd->track || /* bad track */ buffer_drive != current_drive || /* bad drive */ fsector_t > buffer_max || fsector_t < buffer_min || ((CT(COMMAND) == FD_READ || (!in_sector_offset && blk_rq_sectors(current_req) >= ssize)) && max_sector > 2 * max_buffer_sectors + buffer_min && max_size + fsector_t > 2 * max_buffer_sectors + buffer_min)) { /* not enough space */ buffer_track = -1; buffer_drive = current_drive; buffer_max = buffer_min = aligned_sector_t; } raw_cmd->kernel_data = floppy_track_buffer + ((aligned_sector_t - buffer_min) << 9); if (CT(COMMAND) == FD_WRITE) { /* copy write buffer to track buffer. * if we get here, we know that the write * is either aligned or the data already in the buffer * (buffer will be overwritten) */ if (in_sector_offset && buffer_track == -1) DPRINT("internal error offset !=0 on write\n"); buffer_track = raw_cmd->track; buffer_drive = current_drive; copy_buffer(ssize, max_sector, 2 * max_buffer_sectors + buffer_min); } else transfer_size(ssize, max_sector, 2 * max_buffer_sectors + buffer_min - aligned_sector_t); /* round up current_count_sectors to get dma xfer size */ raw_cmd->length = in_sector_offset + current_count_sectors; raw_cmd->length = ((raw_cmd->length - 1) | (ssize - 1)) + 1; raw_cmd->length <<= 9; if ((raw_cmd->length < current_count_sectors << 9) || (raw_cmd->kernel_data != bio_data(current_req->bio) && CT(COMMAND) == FD_WRITE && (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max || aligned_sector_t < buffer_min)) || raw_cmd->length % (128 << SIZECODE) || raw_cmd->length <= 0 || current_count_sectors <= 0) { DPRINT("fractionary current count b=%lx s=%lx\n", raw_cmd->length, current_count_sectors); if (raw_cmd->kernel_data != bio_data(current_req->bio)) pr_info("addr=%d, length=%ld\n", (int)((raw_cmd->kernel_data - floppy_track_buffer) >> 9), current_count_sectors); pr_info("st=%d ast=%d mse=%d msi=%d\n", fsector_t, aligned_sector_t, max_sector, max_size); pr_info("ssize=%x SIZECODE=%d\n", ssize, SIZECODE); pr_info("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n", COMMAND, SECTOR, HEAD, TRACK); pr_info("buffer drive=%d\n", buffer_drive); pr_info("buffer track=%d\n", buffer_track); pr_info("buffer_min=%d\n", buffer_min); pr_info("buffer_max=%d\n", buffer_max); return 0; } if (raw_cmd->kernel_data != bio_data(current_req->bio)) { if (raw_cmd->kernel_data < floppy_track_buffer || current_count_sectors < 0 || raw_cmd->length < 0 || raw_cmd->kernel_data + raw_cmd->length > floppy_track_buffer + (max_buffer_sectors << 10)) { DPRINT("buffer overrun in schedule dma\n"); pr_info("fsector_t=%d buffer_min=%d current_count=%ld\n", fsector_t, buffer_min, raw_cmd->length >> 9); pr_info("current_count_sectors=%ld\n", current_count_sectors); if (CT(COMMAND) == FD_READ) pr_info("read\n"); if (CT(COMMAND) == FD_WRITE) pr_info("write\n"); return 0; } } else if (raw_cmd->length > blk_rq_bytes(current_req) || current_count_sectors > blk_rq_sectors(current_req)) { DPRINT("buffer overrun in direct transfer\n"); return 0; } else if (raw_cmd->length < current_count_sectors << 9) { DPRINT("more sectors than bytes\n"); pr_info("bytes=%ld\n", raw_cmd->length >> 9); pr_info("sectors=%ld\n", current_count_sectors); } if (raw_cmd->length == 0) { DPRINT("zero dma transfer attempted from make_raw_request\n"); return 0; } virtualdmabug_workaround(); return 2; } static int set_next_request(void) { current_req = list_first_entry_or_null(&floppy_reqs, struct request, queuelist); if (current_req) { current_req->error_count = 0; list_del_init(¤t_req->queuelist); } return current_req != NULL; } static void redo_fd_request(void) { int drive; int tmp; lastredo = jiffies; if (current_drive < N_DRIVE) floppy_off(current_drive); do_request: if (!current_req) { int pending; spin_lock_irq(&floppy_lock); pending = set_next_request(); spin_unlock_irq(&floppy_lock); if (!pending) { do_floppy = NULL; unlock_fdc(); return; } } drive = (long)current_req->rq_disk->private_data; set_fdc(drive); reschedule_timeout(current_reqD, "redo fd request"); set_floppy(drive); raw_cmd = &default_raw_cmd; raw_cmd->flags = 0; if (start_motor(redo_fd_request)) return; disk_change(current_drive); if (test_bit(current_drive, &fake_change) || test_bit(FD_DISK_CHANGED_BIT, &DRS->flags)) { DPRINT("disk absent or changed during operation\n"); request_done(0); goto do_request; } if (!_floppy) { /* Autodetection */ if (!probing) { DRS->probed_format = 0; if (next_valid_format()) { DPRINT("no autodetectable formats\n"); _floppy = NULL; request_done(0); goto do_request; } } probing = 1; _floppy = floppy_type + DP->autodetect[DRS->probed_format]; } else probing = 0; errors = &(current_req->error_count); tmp = make_raw_rw_request(); if (tmp < 2) { request_done(tmp); goto do_request; } if (test_bit(FD_NEED_TWADDLE_BIT, &DRS->flags)) twaddle(); schedule_bh(floppy_start); debugt(__func__, "queue fd request"); return; } static const struct cont_t rw_cont = { .interrupt = rw_interrupt, .redo = redo_fd_request, .error = bad_flp_intr, .done = request_done }; static void process_fd_request(void) { cont = &rw_cont; schedule_bh(redo_fd_request); } static blk_status_t floppy_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { blk_mq_start_request(bd->rq); if (WARN(max_buffer_sectors == 0, "VFS: %s called on non-open device\n", __func__)) return BLK_STS_IOERR; if (WARN(atomic_read(&usage_count) == 0, "warning: usage count=0, current_req=%p sect=%ld flags=%llx\n", current_req, (long)blk_rq_pos(current_req), (unsigned long long) current_req->cmd_flags)) return BLK_STS_IOERR; spin_lock_irq(&floppy_lock); list_add_tail(&bd->rq->queuelist, &floppy_reqs); spin_unlock_irq(&floppy_lock); if (test_and_set_bit(0, &fdc_busy)) { /* fdc busy, this new request will be treated when the current one is done */ is_alive(__func__, "old request running"); return BLK_STS_OK; } command_status = FD_COMMAND_NONE; __reschedule_timeout(MAXTIMEOUT, "fd_request"); set_fdc(0); process_fd_request(); is_alive(__func__, ""); return BLK_STS_OK; } static const struct cont_t poll_cont = { .interrupt = success_and_wakeup, .redo = floppy_ready, .error = generic_failure, .done = generic_done }; static int poll_drive(bool interruptible, int flag) { /* no auto-sense, just clear dcl */ raw_cmd = &default_raw_cmd; raw_cmd->flags = flag; raw_cmd->track = 0; raw_cmd->cmd_count = 0; cont = &poll_cont; debug_dcl(DP->flags, "setting NEWCHANGE in poll_drive\n"); set_bit(FD_DISK_NEWCHANGE_BIT, &DRS->flags); return wait_til_done(floppy_ready, interruptible); } /* * User triggered reset * ==================== */ static void reset_intr(void) { pr_info("weird, reset interrupt called\n"); } static const struct cont_t reset_cont = { .interrupt = reset_intr, .redo = success_and_wakeup, .error = generic_failure, .done = generic_done }; static int user_reset_fdc(int drive, int arg, bool interruptible) { int ret; if (lock_fdc(drive)) return -EINTR; if (arg == FD_RESET_ALWAYS) FDCS->reset = 1; if (FDCS->reset) { cont = &reset_cont; ret = wait_til_done(reset_fdc, interruptible); if (ret == -EINTR) return -EINTR; } process_fd_request(); return 0; } /* * Misc Ioctl's and support * ======================== */ static inline int fd_copyout(void __user *param, const void *address, unsigned long size) { return copy_to_user(param, address, size) ? -EFAULT : 0; } static inline int fd_copyin(void __user *param, void *address, unsigned long size) { return copy_from_user(address, param, size) ? -EFAULT : 0; } static const char *drive_name(int type, int drive) { struct floppy_struct *floppy; if (type) floppy = floppy_type + type; else { if (UDP->native_format) floppy = floppy_type + UDP->native_format; else return "(null)"; } if (floppy->name) return floppy->name; else return "(null)"; } /* raw commands */ static void raw_cmd_done(int flag) { int i; if (!flag) { raw_cmd->flags |= FD_RAW_FAILURE; raw_cmd->flags |= FD_RAW_HARDFAILURE; } else { raw_cmd->reply_count = inr; if (raw_cmd->reply_count > MAX_REPLIES) raw_cmd->reply_count = 0; for (i = 0; i < raw_cmd->reply_count; i++) raw_cmd->reply[i] = reply_buffer[i]; if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE)) { unsigned long flags; flags = claim_dma_lock(); raw_cmd->length = fd_get_dma_residue(); release_dma_lock(flags); } if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) && (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0))) raw_cmd->flags |= FD_RAW_FAILURE; if (disk_change(current_drive)) raw_cmd->flags |= FD_RAW_DISK_CHANGE; else raw_cmd->flags &= ~FD_RAW_DISK_CHANGE; if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER) motor_off_callback(&motor_off_timer[current_drive]); if (raw_cmd->next && (!(raw_cmd->flags & FD_RAW_FAILURE) || !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) && ((raw_cmd->flags & FD_RAW_FAILURE) || !(raw_cmd->flags & FD_RAW_STOP_IF_SUCCESS))) { raw_cmd = raw_cmd->next; return; } } generic_done(flag); } static const struct cont_t raw_cmd_cont = { .interrupt = success_and_wakeup, .redo = floppy_start, .error = generic_failure, .done = raw_cmd_done }; static int raw_cmd_copyout(int cmd, void __user *param, struct floppy_raw_cmd *ptr) { int ret; while (ptr) { struct floppy_raw_cmd cmd = *ptr; cmd.next = NULL; cmd.kernel_data = NULL; ret = copy_to_user(param, &cmd, sizeof(cmd)); if (ret) return -EFAULT; param += sizeof(struct floppy_raw_cmd); if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length) { if (ptr->length >= 0 && ptr->length <= ptr->buffer_length) { long length = ptr->buffer_length - ptr->length; ret = fd_copyout(ptr->data, ptr->kernel_data, length); if (ret) return ret; } } ptr = ptr->next; } return 0; } static void raw_cmd_free(struct floppy_raw_cmd **ptr) { struct floppy_raw_cmd *next; struct floppy_raw_cmd *this; this = *ptr; *ptr = NULL; while (this) { if (this->buffer_length) { fd_dma_mem_free((unsigned long)this->kernel_data, this->buffer_length); this->buffer_length = 0; } next = this->next; kfree(this); this = next; } } static int raw_cmd_copyin(int cmd, void __user *param, struct floppy_raw_cmd **rcmd) { struct floppy_raw_cmd *ptr; int ret; int i; *rcmd = NULL; loop: ptr = kmalloc(sizeof(struct floppy_raw_cmd), GFP_KERNEL); if (!ptr) return -ENOMEM; *rcmd = ptr; ret = copy_from_user(ptr, param, sizeof(*ptr)); ptr->next = NULL; ptr->buffer_length = 0; ptr->kernel_data = NULL; if (ret) return -EFAULT; param += sizeof(struct floppy_raw_cmd); if (ptr->cmd_count > 33) /* the command may now also take up the space * initially intended for the reply & the * reply count. Needed for long 82078 commands * such as RESTORE, which takes ... 17 command * bytes. Murphy's law #137: When you reserve * 16 bytes for a structure, you'll one day * discover that you really need 17... */ return -EINVAL; for (i = 0; i < 16; i++) ptr->reply[i] = 0; ptr->resultcode = 0; if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) { if (ptr->length <= 0) return -EINVAL; ptr->kernel_data = (char *)fd_dma_mem_alloc(ptr->length); fallback_on_nodma_alloc(&ptr->kernel_data, ptr->length); if (!ptr->kernel_data) return -ENOMEM; ptr->buffer_length = ptr->length; } if (ptr->flags & FD_RAW_WRITE) { ret = fd_copyin(ptr->data, ptr->kernel_data, ptr->length); if (ret) return ret; } if (ptr->flags & FD_RAW_MORE) { rcmd = &(ptr->next); ptr->rate &= 0x43; goto loop; } return 0; } static int raw_cmd_ioctl(int cmd, void __user *param) { struct floppy_raw_cmd *my_raw_cmd; int drive; int ret2; int ret; if (FDCS->rawcmd <= 1) FDCS->rawcmd = 1; for (drive = 0; drive < N_DRIVE; drive++) { if (FDC(drive) != fdc) continue; if (drive == current_drive) { if (UDRS->fd_ref > 1) { FDCS->rawcmd = 2; break; } } else if (UDRS->fd_ref) { FDCS->rawcmd = 2; break; } } if (FDCS->reset) return -EIO; ret = raw_cmd_copyin(cmd, param, &my_raw_cmd); if (ret) { raw_cmd_free(&my_raw_cmd); return ret; } raw_cmd = my_raw_cmd; cont = &raw_cmd_cont; ret = wait_til_done(floppy_start, true); debug_dcl(DP->flags, "calling disk change from raw_cmd ioctl\n"); if (ret != -EINTR && FDCS->reset) ret = -EIO; DRS->track = NO_TRACK; ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd); if (!ret) ret = ret2; raw_cmd_free(&my_raw_cmd); return ret; } static int invalidate_drive(struct block_device *bdev) { /* invalidate the buffer track to force a reread */ set_bit((long)bdev->bd_disk->private_data, &fake_change); process_fd_request(); check_disk_change(bdev); return 0; } static int set_geometry(unsigned int cmd, struct floppy_struct *g, int drive, int type, struct block_device *bdev) { int cnt; /* sanity checking for parameters. */ if (g->sect <= 0 || g->head <= 0 || g->track <= 0 || g->track > UDP->tracks >> STRETCH(g) || /* check if reserved bits are set */ (g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_SECTBASEMASK)) != 0) return -EINVAL; if (type) { if (!capable(CAP_SYS_ADMIN)) return -EPERM; mutex_lock(&open_lock); if (lock_fdc(drive)) { mutex_unlock(&open_lock); return -EINTR; } floppy_type[type] = *g; floppy_type[type].name = "user format"; for (cnt = type << 2; cnt < (type << 2) + 4; cnt++) floppy_sizes[cnt] = floppy_sizes[cnt + 0x80] = floppy_type[type].size + 1; process_fd_request(); for (cnt = 0; cnt < N_DRIVE; cnt++) { struct block_device *bdev = opened_bdev[cnt]; if (!bdev || ITYPE(drive_state[cnt].fd_device) != type) continue; __invalidate_device(bdev, true); } mutex_unlock(&open_lock); } else { int oldStretch; if (lock_fdc(drive)) return -EINTR; if (cmd != FDDEFPRM) { /* notice a disk change immediately, else * we lose our settings immediately*/ if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) return -EINTR; } oldStretch = g->stretch; user_params[drive] = *g; if (buffer_drive == drive) SUPBOUND(buffer_max, user_params[drive].sect); current_type[drive] = &user_params[drive]; floppy_sizes[drive] = user_params[drive].size; if (cmd == FDDEFPRM) DRS->keep_data = -1; else DRS->keep_data = 1; /* invalidation. Invalidate only when needed, i.e. * when there are already sectors in the buffer cache * whose number will change. This is useful, because * mtools often changes the geometry of the disk after * looking at the boot block */ if (DRS->maxblock > user_params[drive].sect || DRS->maxtrack || ((user_params[drive].sect ^ oldStretch) & (FD_SWAPSIDES | FD_SECTBASEMASK))) invalidate_drive(bdev); else process_fd_request(); } return 0; } /* handle obsolete ioctl's */ static unsigned int ioctl_table[] = { FDCLRPRM, FDSETPRM, FDDEFPRM, FDGETPRM, FDMSGON, FDMSGOFF, FDFMTBEG, FDFMTTRK, FDFMTEND, FDSETEMSGTRESH, FDFLUSH, FDSETMAXERRS, FDGETMAXERRS, FDGETDRVTYP, FDSETDRVPRM, FDGETDRVPRM, FDGETDRVSTAT, FDPOLLDRVSTAT, FDRESET, FDGETFDCSTAT, FDWERRORCLR, FDWERRORGET, FDRAWCMD, FDEJECT, FDTWADDLE }; static int normalize_ioctl(unsigned int *cmd, int *size) { int i; for (i = 0; i < ARRAY_SIZE(ioctl_table); i++) { if ((*cmd & 0xffff) == (ioctl_table[i] & 0xffff)) { *size = _IOC_SIZE(*cmd); *cmd = ioctl_table[i]; if (*size > _IOC_SIZE(*cmd)) { pr_info("ioctl not yet supported\n"); return -EFAULT; } return 0; } } return -EINVAL; } static int get_floppy_geometry(int drive, int type, struct floppy_struct **g) { if (type) *g = &floppy_type[type]; else { if (lock_fdc(drive)) return -EINTR; if (poll_drive(false, 0) == -EINTR) return -EINTR; process_fd_request(); *g = current_type[drive]; } if (!*g) return -ENODEV; return 0; } static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo) { int drive = (long)bdev->bd_disk->private_data; int type = ITYPE(drive_state[drive].fd_device); struct floppy_struct *g; int ret; ret = get_floppy_geometry(drive, type, &g); if (ret) return ret; geo->heads = g->head; geo->sectors = g->sect; geo->cylinders = g->track; return 0; } static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long param) { int drive = (long)bdev->bd_disk->private_data; int type = ITYPE(UDRS->fd_device); int i; int ret; int size; union inparam { struct floppy_struct g; /* geometry */ struct format_descr f; struct floppy_max_errors max_errors; struct floppy_drive_params dp; } inparam; /* parameters coming from user space */ const void *outparam; /* parameters passed back to user space */ /* convert compatibility eject ioctls into floppy eject ioctl. * We do this in order to provide a means to eject floppy disks before * installing the new fdutils package */ if (cmd == CDROMEJECT || /* CD-ROM eject */ cmd == 0x6470) { /* SunOS floppy eject */ DPRINT("obsolete eject ioctl\n"); DPRINT("please use floppycontrol --eject\n"); cmd = FDEJECT; } if (!((cmd & 0xff00) == 0x0200)) return -EINVAL; /* convert the old style command into a new style command */ ret = normalize_ioctl(&cmd, &size); if (ret) return ret; /* permission checks */ if (((cmd & 0x40) && !(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) || ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))) return -EPERM; if (WARN_ON(size < 0 || size > sizeof(inparam))) return -EINVAL; /* copyin */ memset(&inparam, 0, sizeof(inparam)); if (_IOC_DIR(cmd) & _IOC_WRITE) { ret = fd_copyin((void __user *)param, &inparam, size); if (ret) return ret; } switch (cmd) { case FDEJECT: if (UDRS->fd_ref != 1) /* somebody else has this drive open */ return -EBUSY; if (lock_fdc(drive)) return -EINTR; /* do the actual eject. Fails on * non-Sparc architectures */ ret = fd_eject(UNIT(drive)); set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); set_bit(FD_VERIFY_BIT, &UDRS->flags); process_fd_request(); return ret; case FDCLRPRM: if (lock_fdc(drive)) return -EINTR; current_type[drive] = NULL; floppy_sizes[drive] = MAX_DISK_SIZE << 1; UDRS->keep_data = 0; return invalidate_drive(bdev); case FDSETPRM: case FDDEFPRM: return set_geometry(cmd, &inparam.g, drive, type, bdev); case FDGETPRM: ret = get_floppy_geometry(drive, type, (struct floppy_struct **)&outparam); if (ret) return ret; memcpy(&inparam.g, outparam, offsetof(struct floppy_struct, name)); outparam = &inparam.g; break; case FDMSGON: UDP->flags |= FTD_MSG; return 0; case FDMSGOFF: UDP->flags &= ~FTD_MSG; return 0; case FDFMTBEG: if (lock_fdc(drive)) return -EINTR; if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) return -EINTR; ret = UDRS->flags; process_fd_request(); if (ret & FD_VERIFY) return -ENODEV; if (!(ret & FD_DISK_WRITABLE)) return -EROFS; return 0; case FDFMTTRK: if (UDRS->fd_ref != 1) return -EBUSY; return do_format(drive, &inparam.f); case FDFMTEND: case FDFLUSH: if (lock_fdc(drive)) return -EINTR; return invalidate_drive(bdev); case FDSETEMSGTRESH: UDP->max_errors.reporting = (unsigned short)(param & 0x0f); return 0; case FDGETMAXERRS: outparam = &UDP->max_errors; break; case FDSETMAXERRS: UDP->max_errors = inparam.max_errors; break; case FDGETDRVTYP: outparam = drive_name(type, drive); SUPBOUND(size, strlen((const char *)outparam) + 1); break; case FDSETDRVPRM: *UDP = inparam.dp; break; case FDGETDRVPRM: outparam = UDP; break; case FDPOLLDRVSTAT: if (lock_fdc(drive)) return -EINTR; if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) return -EINTR; process_fd_request(); /* fall through */ case FDGETDRVSTAT: outparam = UDRS; break; case FDRESET: return user_reset_fdc(drive, (int)param, true); case FDGETFDCSTAT: outparam = UFDCS; break; case FDWERRORCLR: memset(UDRWE, 0, sizeof(*UDRWE)); return 0; case FDWERRORGET: outparam = UDRWE; break; case FDRAWCMD: if (type) return -EINVAL; if (lock_fdc(drive)) return -EINTR; set_floppy(drive); i = raw_cmd_ioctl(cmd, (void __user *)param); if (i == -EINTR) return -EINTR; process_fd_request(); return i; case FDTWADDLE: if (lock_fdc(drive)) return -EINTR; twaddle(); process_fd_request(); return 0; default: return -EINVAL; } if (_IOC_DIR(cmd) & _IOC_READ) return fd_copyout((void __user *)param, outparam, size); return 0; } static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long param) { int ret; mutex_lock(&floppy_mutex); ret = fd_locked_ioctl(bdev, mode, cmd, param); mutex_unlock(&floppy_mutex); return ret; } #ifdef CONFIG_COMPAT struct compat_floppy_drive_params { char cmos; compat_ulong_t max_dtr; compat_ulong_t hlt; compat_ulong_t hut; compat_ulong_t srt; compat_ulong_t spinup; compat_ulong_t spindown; unsigned char spindown_offset; unsigned char select_delay; unsigned char rps; unsigned char tracks; compat_ulong_t timeout; unsigned char interleave_sect; struct floppy_max_errors max_errors; char flags; char read_track; short autodetect[8]; compat_int_t checkfreq; compat_int_t native_format; }; struct compat_floppy_drive_struct { signed char flags; compat_ulong_t spinup_date; compat_ulong_t select_date; compat_ulong_t first_read_date; short probed_format; short track; short maxblock; short maxtrack; compat_int_t generation; compat_int_t keep_data; compat_int_t fd_ref; compat_int_t fd_device; compat_int_t last_checked; compat_caddr_t dmabuf; compat_int_t bufblocks; }; struct compat_floppy_fdc_state { compat_int_t spec1; compat_int_t spec2; compat_int_t dtr; unsigned char version; unsigned char dor; compat_ulong_t address; unsigned int rawcmd : 2; unsigned int reset : 1; unsigned int need_configure : 1; unsigned int perp_mode : 2; unsigned int has_fifo : 1; unsigned int driver_version; unsigned char track[4]; }; struct compat_floppy_write_errors { unsigned int write_errors; compat_ulong_t first_error_sector; compat_int_t first_error_generation; compat_ulong_t last_error_sector; compat_int_t last_error_generation; compat_uint_t badness; }; #define FDSETPRM32 _IOW(2, 0x42, struct compat_floppy_struct) #define FDDEFPRM32 _IOW(2, 0x43, struct compat_floppy_struct) #define FDSETDRVPRM32 _IOW(2, 0x90, struct compat_floppy_drive_params) #define FDGETDRVPRM32 _IOR(2, 0x11, struct compat_floppy_drive_params) #define FDGETDRVSTAT32 _IOR(2, 0x12, struct compat_floppy_drive_struct) #define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct compat_floppy_drive_struct) #define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state) #define FDWERRORGET32 _IOR(2, 0x17, struct compat_floppy_write_errors) static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned int cmd, struct compat_floppy_struct __user *arg) { struct floppy_struct v; int drive, type; int err; BUILD_BUG_ON(offsetof(struct floppy_struct, name) != offsetof(struct compat_floppy_struct, name)); if (!(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) return -EPERM; memset(&v, 0, sizeof(struct floppy_struct)); if (copy_from_user(&v, arg, offsetof(struct floppy_struct, name))) return -EFAULT; mutex_lock(&floppy_mutex); drive = (long)bdev->bd_disk->private_data; type = ITYPE(UDRS->fd_device); err = set_geometry(cmd == FDSETPRM32 ? FDSETPRM : FDDEFPRM, &v, drive, type, bdev); mutex_unlock(&floppy_mutex); return err; } static int compat_get_prm(int drive, struct compat_floppy_struct __user *arg) { struct compat_floppy_struct v; struct floppy_struct *p; int err; memset(&v, 0, sizeof(v)); mutex_lock(&floppy_mutex); err = get_floppy_geometry(drive, ITYPE(UDRS->fd_device), &p); if (err) { mutex_unlock(&floppy_mutex); return err; } memcpy(&v, p, offsetof(struct floppy_struct, name)); mutex_unlock(&floppy_mutex); if (copy_to_user(arg, &v, sizeof(struct compat_floppy_struct))) return -EFAULT; return 0; } static int compat_setdrvprm(int drive, struct compat_floppy_drive_params __user *arg) { struct compat_floppy_drive_params v; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (copy_from_user(&v, arg, sizeof(struct compat_floppy_drive_params))) return -EFAULT; mutex_lock(&floppy_mutex); UDP->cmos = v.cmos; UDP->max_dtr = v.max_dtr; UDP->hlt = v.hlt; UDP->hut = v.hut; UDP->srt = v.srt; UDP->spinup = v.spinup; UDP->spindown = v.spindown; UDP->spindown_offset = v.spindown_offset; UDP->select_delay = v.select_delay; UDP->rps = v.rps; UDP->tracks = v.tracks; UDP->timeout = v.timeout; UDP->interleave_sect = v.interleave_sect; UDP->max_errors = v.max_errors; UDP->flags = v.flags; UDP->read_track = v.read_track; memcpy(UDP->autodetect, v.autodetect, sizeof(v.autodetect)); UDP->checkfreq = v.checkfreq; UDP->native_format = v.native_format; mutex_unlock(&floppy_mutex); return 0; } static int compat_getdrvprm(int drive, struct compat_floppy_drive_params __user *arg) { struct compat_floppy_drive_params v; memset(&v, 0, sizeof(struct compat_floppy_drive_params)); mutex_lock(&floppy_mutex); v.cmos = UDP->cmos; v.max_dtr = UDP->max_dtr; v.hlt = UDP->hlt; v.hut = UDP->hut; v.srt = UDP->srt; v.spinup = UDP->spinup; v.spindown = UDP->spindown; v.spindown_offset = UDP->spindown_offset; v.select_delay = UDP->select_delay; v.rps = UDP->rps; v.tracks = UDP->tracks; v.timeout = UDP->timeout; v.interleave_sect = UDP->interleave_sect; v.max_errors = UDP->max_errors; v.flags = UDP->flags; v.read_track = UDP->read_track; memcpy(v.autodetect, UDP->autodetect, sizeof(v.autodetect)); v.checkfreq = UDP->checkfreq; v.native_format = UDP->native_format; mutex_unlock(&floppy_mutex); if (copy_from_user(arg, &v, sizeof(struct compat_floppy_drive_params))) return -EFAULT; return 0; } static int compat_getdrvstat(int drive, bool poll, struct compat_floppy_drive_struct __user *arg) { struct compat_floppy_drive_struct v; memset(&v, 0, sizeof(struct compat_floppy_drive_struct)); mutex_lock(&floppy_mutex); if (poll) { if (lock_fdc(drive)) goto Eintr; if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) goto Eintr; process_fd_request(); } v.spinup_date = UDRS->spinup_date; v.select_date = UDRS->select_date; v.first_read_date = UDRS->first_read_date; v.probed_format = UDRS->probed_format; v.track = UDRS->track; v.maxblock = UDRS->maxblock; v.maxtrack = UDRS->maxtrack; v.generation = UDRS->generation; v.keep_data = UDRS->keep_data; v.fd_ref = UDRS->fd_ref; v.fd_device = UDRS->fd_device; v.last_checked = UDRS->last_checked; v.dmabuf = (uintptr_t)UDRS->dmabuf; v.bufblocks = UDRS->bufblocks; mutex_unlock(&floppy_mutex); if (copy_from_user(arg, &v, sizeof(struct compat_floppy_drive_struct))) return -EFAULT; return 0; Eintr: mutex_unlock(&floppy_mutex); return -EINTR; } static int compat_getfdcstat(int drive, struct compat_floppy_fdc_state __user *arg) { struct compat_floppy_fdc_state v32; struct floppy_fdc_state v; mutex_lock(&floppy_mutex); v = *UFDCS; mutex_unlock(&floppy_mutex); memset(&v32, 0, sizeof(struct compat_floppy_fdc_state)); v32.spec1 = v.spec1; v32.spec2 = v.spec2; v32.dtr = v.dtr; v32.version = v.version; v32.dor = v.dor; v32.address = v.address; v32.rawcmd = v.rawcmd; v32.reset = v.reset; v32.need_configure = v.need_configure; v32.perp_mode = v.perp_mode; v32.has_fifo = v.has_fifo; v32.driver_version = v.driver_version; memcpy(v32.track, v.track, 4); if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_fdc_state))) return -EFAULT; return 0; } static int compat_werrorget(int drive, struct compat_floppy_write_errors __user *arg) { struct compat_floppy_write_errors v32; struct floppy_write_errors v; memset(&v32, 0, sizeof(struct compat_floppy_write_errors)); mutex_lock(&floppy_mutex); v = *UDRWE; mutex_unlock(&floppy_mutex); v32.write_errors = v.write_errors; v32.first_error_sector = v.first_error_sector; v32.first_error_generation = v.first_error_generation; v32.last_error_sector = v.last_error_sector; v32.last_error_generation = v.last_error_generation; v32.badness = v.badness; if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_write_errors))) return -EFAULT; return 0; } static int fd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long param) { int drive = (long)bdev->bd_disk->private_data; switch (cmd) { case FDMSGON: case FDMSGOFF: case FDSETEMSGTRESH: case FDFLUSH: case FDWERRORCLR: case FDEJECT: case FDCLRPRM: case FDFMTBEG: case FDRESET: case FDTWADDLE: return fd_ioctl(bdev, mode, cmd, param); case FDSETMAXERRS: case FDGETMAXERRS: case FDGETDRVTYP: case FDFMTEND: case FDFMTTRK: case FDRAWCMD: return fd_ioctl(bdev, mode, cmd, (unsigned long)compat_ptr(param)); case FDSETPRM32: case FDDEFPRM32: return compat_set_geometry(bdev, mode, cmd, compat_ptr(param)); case FDGETPRM32: return compat_get_prm(drive, compat_ptr(param)); case FDSETDRVPRM32: return compat_setdrvprm(drive, compat_ptr(param)); case FDGETDRVPRM32: return compat_getdrvprm(drive, compat_ptr(param)); case FDPOLLDRVSTAT32: return compat_getdrvstat(drive, true, compat_ptr(param)); case FDGETDRVSTAT32: return compat_getdrvstat(drive, false, compat_ptr(param)); case FDGETFDCSTAT32: return compat_getfdcstat(drive, compat_ptr(param)); case FDWERRORGET32: return compat_werrorget(drive, compat_ptr(param)); } return -EINVAL; } #endif static void __init config_types(void) { bool has_drive = false; int drive; /* read drive info out of physical CMOS */ drive = 0; if (!UDP->cmos) UDP->cmos = FLOPPY0_TYPE; drive = 1; if (!UDP->cmos && FLOPPY1_TYPE) UDP->cmos = FLOPPY1_TYPE; /* FIXME: additional physical CMOS drive detection should go here */ for (drive = 0; drive < N_DRIVE; drive++) { unsigned int type = UDP->cmos; struct floppy_drive_params *params; const char *name = NULL; char temparea[32]; if (type < ARRAY_SIZE(default_drive_params)) { params = &default_drive_params[type].params; if (type) { name = default_drive_params[type].name; allowed_drive_mask |= 1 << drive; } else allowed_drive_mask &= ~(1 << drive); } else { params = &default_drive_params[0].params; snprintf(temparea, sizeof(temparea), "unknown type %d (usb?)", type); name = temparea; } if (name) { const char *prepend; if (!has_drive) { prepend = ""; has_drive = true; pr_info("Floppy drive(s):"); } else { prepend = ","; } pr_cont("%s fd%d is %s", prepend, drive, name); } *UDP = *params; } if (has_drive) pr_cont("\n"); } static void floppy_release(struct gendisk *disk, fmode_t mode) { int drive = (long)disk->private_data; mutex_lock(&floppy_mutex); mutex_lock(&open_lock); if (!UDRS->fd_ref--) { DPRINT("floppy_release with fd_ref == 0"); UDRS->fd_ref = 0; } if (!UDRS->fd_ref) opened_bdev[drive] = NULL; mutex_unlock(&open_lock); mutex_unlock(&floppy_mutex); } /* * floppy_open check for aliasing (/dev/fd0 can be the same as * /dev/PS0 etc), and disallows simultaneous access to the same * drive with different device numbers. */ static int floppy_open(struct block_device *bdev, fmode_t mode) { int drive = (long)bdev->bd_disk->private_data; int old_dev, new_dev; int try; int res = -EBUSY; char *tmp; mutex_lock(&floppy_mutex); mutex_lock(&open_lock); old_dev = UDRS->fd_device; if (opened_bdev[drive] && opened_bdev[drive] != bdev) goto out2; if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)) { set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); set_bit(FD_VERIFY_BIT, &UDRS->flags); } UDRS->fd_ref++; opened_bdev[drive] = bdev; res = -ENXIO; if (!floppy_track_buffer) { /* if opening an ED drive, reserve a big buffer, * else reserve a small one */ if ((UDP->cmos == 6) || (UDP->cmos == 5)) try = 64; /* Only 48 actually useful */ else try = 32; /* Only 24 actually useful */ tmp = (char *)fd_dma_mem_alloc(1024 * try); if (!tmp && !floppy_track_buffer) { try >>= 1; /* buffer only one side */ INFBOUND(try, 16); tmp = (char *)fd_dma_mem_alloc(1024 * try); } if (!tmp && !floppy_track_buffer) fallback_on_nodma_alloc(&tmp, 2048 * try); if (!tmp && !floppy_track_buffer) { DPRINT("Unable to allocate DMA memory\n"); goto out; } if (floppy_track_buffer) { if (tmp) fd_dma_mem_free((unsigned long)tmp, try * 1024); } else { buffer_min = buffer_max = -1; floppy_track_buffer = tmp; max_buffer_sectors = try; } } new_dev = MINOR(bdev->bd_dev); UDRS->fd_device = new_dev; set_capacity(disks[drive], floppy_sizes[new_dev]); if (old_dev != -1 && old_dev != new_dev) { if (buffer_drive == drive) buffer_track = -1; } if (UFDCS->rawcmd == 1) UFDCS->rawcmd = 2; if (!(mode & FMODE_NDELAY)) { if (mode & (FMODE_READ|FMODE_WRITE)) { UDRS->last_checked = 0; clear_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags); check_disk_change(bdev); if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags)) goto out; if (test_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags)) goto out; } res = -EROFS; if ((mode & FMODE_WRITE) && !test_bit(FD_DISK_WRITABLE_BIT, &UDRS->flags)) goto out; } mutex_unlock(&open_lock); mutex_unlock(&floppy_mutex); return 0; out: UDRS->fd_ref--; if (!UDRS->fd_ref) opened_bdev[drive] = NULL; out2: mutex_unlock(&open_lock); mutex_unlock(&floppy_mutex); return res; } /* * Check if the disk has been changed or if a change has been faked. */ static unsigned int floppy_check_events(struct gendisk *disk, unsigned int clearing) { int drive = (long)disk->private_data; if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || test_bit(FD_VERIFY_BIT, &UDRS->flags)) return DISK_EVENT_MEDIA_CHANGE; if (time_after(jiffies, UDRS->last_checked + UDP->checkfreq)) { if (lock_fdc(drive)) return 0; poll_drive(false, 0); process_fd_request(); } if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || test_bit(FD_VERIFY_BIT, &UDRS->flags) || test_bit(drive, &fake_change) || drive_no_geom(drive)) return DISK_EVENT_MEDIA_CHANGE; return 0; } /* * This implements "read block 0" for floppy_revalidate(). * Needed for format autodetection, checking whether there is * a disk in the drive, and whether that disk is writable. */ struct rb0_cbdata { int drive; struct completion complete; }; static void floppy_rb0_cb(struct bio *bio) { struct rb0_cbdata *cbdata = (struct rb0_cbdata *)bio->bi_private; int drive = cbdata->drive; if (bio->bi_status) { pr_info("floppy: error %d while reading block 0\n", bio->bi_status); set_bit(FD_OPEN_SHOULD_FAIL_BIT, &UDRS->flags); } complete(&cbdata->complete); } static int __floppy_read_block_0(struct block_device *bdev, int drive) { struct bio bio; struct bio_vec bio_vec; struct page *page; struct rb0_cbdata cbdata; size_t size; page = alloc_page(GFP_NOIO); if (!page) { process_fd_request(); return -ENOMEM; } size = bdev->bd_block_size; if (!size) size = 1024; cbdata.drive = drive; bio_init(&bio, &bio_vec, 1); bio_set_dev(&bio, bdev); bio_add_page(&bio, page, size, 0); bio.bi_iter.bi_sector = 0; bio.bi_flags |= (1 << BIO_QUIET); bio.bi_private = &cbdata; bio.bi_end_io = floppy_rb0_cb; bio_set_op_attrs(&bio, REQ_OP_READ, 0); init_completion(&cbdata.complete); submit_bio(&bio); process_fd_request(); wait_for_completion(&cbdata.complete); __free_page(page); return 0; } /* revalidate the floppy disk, i.e. trigger format autodetection by reading * the bootblock (block 0). "Autodetection" is also needed to check whether * there is a disk in the drive at all... Thus we also do it for fixed * geometry formats */ static int floppy_revalidate(struct gendisk *disk) { int drive = (long)disk->private_data; int cf; int res = 0; if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || test_bit(FD_VERIFY_BIT, &UDRS->flags) || test_bit(drive, &fake_change) || drive_no_geom(drive)) { if (WARN(atomic_read(&usage_count) == 0, "VFS: revalidate called on non-open device.\n")) return -EFAULT; res = lock_fdc(drive); if (res) return res; cf = (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) || test_bit(FD_VERIFY_BIT, &UDRS->flags)); if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) { process_fd_request(); /*already done by another thread */ return 0; } UDRS->maxblock = 0; UDRS->maxtrack = 0; if (buffer_drive == drive) buffer_track = -1; clear_bit(drive, &fake_change); clear_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); if (cf) UDRS->generation++; if (drive_no_geom(drive)) { /* auto-sensing */ res = __floppy_read_block_0(opened_bdev[drive], drive); } else { if (cf) poll_drive(false, FD_RAW_NEED_DISK); process_fd_request(); } } set_capacity(disk, floppy_sizes[UDRS->fd_device]); return res; } static const struct block_device_operations floppy_fops = { .owner = THIS_MODULE, .open = floppy_open, .release = floppy_release, .ioctl = fd_ioctl, .getgeo = fd_getgeo, .check_events = floppy_check_events, .revalidate_disk = floppy_revalidate, #ifdef CONFIG_COMPAT .compat_ioctl = fd_compat_ioctl, #endif }; /* * Floppy Driver initialization * ============================= */ /* Determine the floppy disk controller type */ /* This routine was written by David C. Niemi */ static char __init get_fdc_version(void) { int r; output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */ if (FDCS->reset) return FDC_NONE; r = result(); if (r <= 0x00) return FDC_NONE; /* No FDC present ??? */ if ((r == 1) && (reply_buffer[0] == 0x80)) { pr_info("FDC %d is an 8272A\n", fdc); return FDC_8272A; /* 8272a/765 don't know DUMPREGS */ } if (r != 10) { pr_info("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n", fdc, r); return FDC_UNKNOWN; } if (!fdc_configure()) { pr_info("FDC %d is an 82072\n", fdc); return FDC_82072; /* 82072 doesn't know CONFIGURE */ } output_byte(FD_PERPENDICULAR); if (need_more_output() == MORE_OUTPUT) { output_byte(0); } else { pr_info("FDC %d is an 82072A\n", fdc); return FDC_82072A; /* 82072A as found on Sparcs. */ } output_byte(FD_UNLOCK); r = result(); if ((r == 1) && (reply_buffer[0] == 0x80)) { pr_info("FDC %d is a pre-1991 82077\n", fdc); return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know * LOCK/UNLOCK */ } if ((r != 1) || (reply_buffer[0] != 0x00)) { pr_info("FDC %d init: UNLOCK: unexpected return of %d bytes.\n", fdc, r); return FDC_UNKNOWN; } output_byte(FD_PARTID); r = result(); if (r != 1) { pr_info("FDC %d init: PARTID: unexpected return of %d bytes.\n", fdc, r); return FDC_UNKNOWN; } if (reply_buffer[0] == 0x80) { pr_info("FDC %d is a post-1991 82077\n", fdc); return FDC_82077; /* Revised 82077AA passes all the tests */ } switch (reply_buffer[0] >> 5) { case 0x0: /* Either a 82078-1 or a 82078SL running at 5Volt */ pr_info("FDC %d is an 82078.\n", fdc); return FDC_82078; case 0x1: pr_info("FDC %d is a 44pin 82078\n", fdc); return FDC_82078; case 0x2: pr_info("FDC %d is a S82078B\n", fdc); return FDC_S82078B; case 0x3: pr_info("FDC %d is a National Semiconductor PC87306\n", fdc); return FDC_87306; default: pr_info("FDC %d init: 82078 variant with unknown PARTID=%d.\n", fdc, reply_buffer[0] >> 5); return FDC_82078_UNKN; } } /* get_fdc_version */ /* lilo configuration */ static void __init floppy_set_flags(int *ints, int param, int param2) { int i; for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) { if (param) default_drive_params[i].params.flags |= param2; else default_drive_params[i].params.flags &= ~param2; } DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param); } static void __init daring(int *ints, int param, int param2) { int i; for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) { if (param) { default_drive_params[i].params.select_delay = 0; default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR; } else { default_drive_params[i].params.select_delay = 2 * HZ / 100; default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR; } } DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken"); } static void __init set_cmos(int *ints, int dummy, int dummy2) { int current_drive = 0; if (ints[0] != 2) { DPRINT("wrong number of parameters for CMOS\n"); return; } current_drive = ints[1]; if (current_drive < 0 || current_drive >= 8) { DPRINT("bad drive for set_cmos\n"); return; } #if N_FDC > 1 if (current_drive >= 4 && !FDC2) FDC2 = 0x370; #endif DP->cmos = ints[2]; DPRINT("setting CMOS code to %d\n", ints[2]); } static struct param_table { const char *name; void (*fn) (int *ints, int param, int param2); int *var; int def_param; int param2; } config_params[] __initdata = { {"allowed_drive_mask", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */ {"all_drives", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */ {"asus_pci", NULL, &allowed_drive_mask, 0x33, 0}, {"irq", NULL, &FLOPPY_IRQ, 6, 0}, {"dma", NULL, &FLOPPY_DMA, 2, 0}, {"daring", daring, NULL, 1, 0}, #if N_FDC > 1 {"two_fdc", NULL, &FDC2, 0x370, 0}, {"one_fdc", NULL, &FDC2, 0, 0}, #endif {"thinkpad", floppy_set_flags, NULL, 1, FD_INVERTED_DCL}, {"broken_dcl", floppy_set_flags, NULL, 1, FD_BROKEN_DCL}, {"messages", floppy_set_flags, NULL, 1, FTD_MSG}, {"silent_dcl_clear", floppy_set_flags, NULL, 1, FD_SILENT_DCL_CLEAR}, {"debug", floppy_set_flags, NULL, 1, FD_DEBUG}, {"nodma", NULL, &can_use_virtual_dma, 1, 0}, {"omnibook", NULL, &can_use_virtual_dma, 1, 0}, {"yesdma", NULL, &can_use_virtual_dma, 0, 0}, {"fifo_depth", NULL, &fifo_depth, 0xa, 0}, {"nofifo", NULL, &no_fifo, 0x20, 0}, {"usefifo", NULL, &no_fifo, 0, 0}, {"cmos", set_cmos, NULL, 0, 0}, {"slow", NULL, &slow_floppy, 1, 0}, {"unexpected_interrupts", NULL, &print_unex, 1, 0}, {"no_unexpected_interrupts", NULL, &print_unex, 0, 0}, {"L40SX", NULL, &print_unex, 0, 0} EXTRA_FLOPPY_PARAMS }; static int __init floppy_setup(char *str) { int i; int param; int ints[11]; str = get_options(str, ARRAY_SIZE(ints), ints); if (str) { for (i = 0; i < ARRAY_SIZE(config_params); i++) { if (strcmp(str, config_params[i].name) == 0) { if (ints[0]) param = ints[1]; else param = config_params[i].def_param; if (config_params[i].fn) config_params[i].fn(ints, param, config_params[i]. param2); if (config_params[i].var) { DPRINT("%s=%d\n", str, param); *config_params[i].var = param; } return 1; } } } if (str) { DPRINT("unknown floppy option [%s]\n", str); DPRINT("allowed options are:"); for (i = 0; i < ARRAY_SIZE(config_params); i++) pr_cont(" %s", config_params[i].name); pr_cont("\n"); } else DPRINT("botched floppy option\n"); DPRINT("Read Documentation/blockdev/floppy.txt\n"); return 0; } static int have_no_fdc = -ENODEV; static ssize_t floppy_cmos_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *p = to_platform_device(dev); int drive; drive = p->id; return sprintf(buf, "%X\n", UDP->cmos); } static DEVICE_ATTR(cmos, 0444, floppy_cmos_show, NULL); static struct attribute *floppy_dev_attrs[] = { &dev_attr_cmos.attr, NULL }; ATTRIBUTE_GROUPS(floppy_dev); static void floppy_device_release(struct device *dev) {} static int floppy_resume(struct device *dev) { int fdc; for (fdc = 0; fdc < N_FDC; fdc++) if (FDCS->address != -1) user_reset_fdc(-1, FD_RESET_ALWAYS, false); return 0; } static const struct dev_pm_ops floppy_pm_ops = { .resume = floppy_resume, .restore = floppy_resume, }; static struct platform_driver floppy_driver = { .driver = { .name = "floppy", .pm = &floppy_pm_ops, }, }; static const struct blk_mq_ops floppy_mq_ops = { .queue_rq = floppy_queue_rq, }; static struct platform_device floppy_device[N_DRIVE]; static bool floppy_available(int drive) { if (!(allowed_drive_mask & (1 << drive))) return false; if (fdc_state[FDC(drive)].version == FDC_NONE) return false; return true; } static struct kobject *floppy_find(dev_t dev, int *part, void *data) { int drive = (*part & 3) | ((*part & 0x80) >> 5); if (drive >= N_DRIVE || !floppy_available(drive)) return NULL; if (((*part >> 2) & 0x1f) >= ARRAY_SIZE(floppy_type)) return NULL; *part = 0; return get_disk_and_module(disks[drive]); } static int __init do_floppy_init(void) { int i, unit, drive, err; set_debugt(); interruptjiffies = resultjiffies = jiffies; #if defined(CONFIG_PPC) if (check_legacy_ioport(FDC1)) return -ENODEV; #endif raw_cmd = NULL; floppy_wq = alloc_ordered_workqueue("floppy", 0); if (!floppy_wq) return -ENOMEM; for (drive = 0; drive < N_DRIVE; drive++) { disks[drive] = alloc_disk(1); if (!disks[drive]) { err = -ENOMEM; goto out_put_disk; } disks[drive]->queue = blk_mq_init_sq_queue(&tag_sets[drive], &floppy_mq_ops, 2, BLK_MQ_F_SHOULD_MERGE); if (IS_ERR(disks[drive]->queue)) { err = PTR_ERR(disks[drive]->queue); disks[drive]->queue = NULL; goto out_put_disk; } blk_queue_bounce_limit(disks[drive]->queue, BLK_BOUNCE_HIGH); blk_queue_max_hw_sectors(disks[drive]->queue, 64); disks[drive]->major = FLOPPY_MAJOR; disks[drive]->first_minor = TOMINOR(drive); disks[drive]->fops = &floppy_fops; disks[drive]->events = DISK_EVENT_MEDIA_CHANGE; sprintf(disks[drive]->disk_name, "fd%d", drive); timer_setup(&motor_off_timer[drive], motor_off_callback, 0); } err = register_blkdev(FLOPPY_MAJOR, "fd"); if (err) goto out_put_disk; err = platform_driver_register(&floppy_driver); if (err) goto out_unreg_blkdev; blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE, floppy_find, NULL, NULL); for (i = 0; i < 256; i++) if (ITYPE(i)) floppy_sizes[i] = floppy_type[ITYPE(i)].size; else floppy_sizes[i] = MAX_DISK_SIZE << 1; reschedule_timeout(MAXTIMEOUT, "floppy init"); config_types(); for (i = 0; i < N_FDC; i++) { fdc = i; memset(FDCS, 0, sizeof(*FDCS)); FDCS->dtr = -1; FDCS->dor = 0x4; #if defined(__sparc__) || defined(__mc68000__) /*sparcs/sun3x don't have a DOR reset which we can fall back on to */ #ifdef __mc68000__ if (MACH_IS_SUN3X) #endif FDCS->version = FDC_82072A; #endif } use_virtual_dma = can_use_virtual_dma & 1; fdc_state[0].address = FDC1; if (fdc_state[0].address == -1) { cancel_delayed_work(&fd_timeout); err = -ENODEV; goto out_unreg_region; } #if N_FDC > 1 fdc_state[1].address = FDC2; #endif fdc = 0; /* reset fdc in case of unexpected interrupt */ err = floppy_grab_irq_and_dma(); if (err) { cancel_delayed_work(&fd_timeout); err = -EBUSY; goto out_unreg_region; } /* initialise drive state */ for (drive = 0; drive < N_DRIVE; drive++) { memset(UDRS, 0, sizeof(*UDRS)); memset(UDRWE, 0, sizeof(*UDRWE)); set_bit(FD_DISK_NEWCHANGE_BIT, &UDRS->flags); set_bit(FD_DISK_CHANGED_BIT, &UDRS->flags); set_bit(FD_VERIFY_BIT, &UDRS->flags); UDRS->fd_device = -1; floppy_track_buffer = NULL; max_buffer_sectors = 0; } /* * Small 10 msec delay to let through any interrupt that * initialization might have triggered, to not * confuse detection: */ msleep(10); for (i = 0; i < N_FDC; i++) { fdc = i; FDCS->driver_version = FD_DRIVER_VERSION; for (unit = 0; unit < 4; unit++) FDCS->track[unit] = 0; if (FDCS->address == -1) continue; FDCS->rawcmd = 2; if (user_reset_fdc(-1, FD_RESET_ALWAYS, false)) { /* free ioports reserved by floppy_grab_irq_and_dma() */ floppy_release_regions(fdc); FDCS->address = -1; FDCS->version = FDC_NONE; continue; } /* Try to determine the floppy controller type */ FDCS->version = get_fdc_version(); if (FDCS->version == FDC_NONE) { /* free ioports reserved by floppy_grab_irq_and_dma() */ floppy_release_regions(fdc); FDCS->address = -1; continue; } if (can_use_virtual_dma == 2 && FDCS->version < FDC_82072A) can_use_virtual_dma = 0; have_no_fdc = 0; /* Not all FDCs seem to be able to handle the version command * properly, so force a reset for the standard FDC clones, * to avoid interrupt garbage. */ user_reset_fdc(-1, FD_RESET_ALWAYS, false); } fdc = 0; cancel_delayed_work(&fd_timeout); current_drive = 0; initialized = true; if (have_no_fdc) { DPRINT("no floppy controllers found\n"); err = have_no_fdc; goto out_release_dma; } for (drive = 0; drive < N_DRIVE; drive++) { if (!floppy_available(drive)) continue; floppy_device[drive].name = floppy_device_name; floppy_device[drive].id = drive; floppy_device[drive].dev.release = floppy_device_release; floppy_device[drive].dev.groups = floppy_dev_groups; err = platform_device_register(&floppy_device[drive]); if (err) goto out_remove_drives; /* to be cleaned up... */ disks[drive]->private_data = (void *)(long)drive; disks[drive]->flags |= GENHD_FL_REMOVABLE; device_add_disk(&floppy_device[drive].dev, disks[drive], NULL); } return 0; out_remove_drives: while (drive--) { if (floppy_available(drive)) { del_gendisk(disks[drive]); platform_device_unregister(&floppy_device[drive]); } } out_release_dma: if (atomic_read(&usage_count)) floppy_release_irq_and_dma(); out_unreg_region: blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); platform_driver_unregister(&floppy_driver); out_unreg_blkdev: unregister_blkdev(FLOPPY_MAJOR, "fd"); out_put_disk: destroy_workqueue(floppy_wq); for (drive = 0; drive < N_DRIVE; drive++) { if (!disks[drive]) break; if (disks[drive]->queue) { del_timer_sync(&motor_off_timer[drive]); blk_cleanup_queue(disks[drive]->queue); disks[drive]->queue = NULL; blk_mq_free_tag_set(&tag_sets[drive]); } put_disk(disks[drive]); } return err; } #ifndef MODULE static __init void floppy_async_init(void *data, async_cookie_t cookie) { do_floppy_init(); } #endif static int __init floppy_init(void) { #ifdef MODULE return do_floppy_init(); #else /* Don't hold up the bootup by the floppy initialization */ async_schedule(floppy_async_init, NULL); return 0; #endif } static const struct io_region { int offset; int size; } io_regions[] = { { 2, 1 }, /* address + 3 is sometimes reserved by pnp bios for motherboard */ { 4, 2 }, /* address + 6 is reserved, and may be taken by IDE. * Unfortunately, Adaptec doesn't know this :-(, */ { 7, 1 }, }; static void floppy_release_allocated_regions(int fdc, const struct io_region *p) { while (p != io_regions) { p--; release_region(FDCS->address + p->offset, p->size); } } #define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)])) static int floppy_request_regions(int fdc) { const struct io_region *p; for (p = io_regions; p < ARRAY_END(io_regions); p++) { if (!request_region(FDCS->address + p->offset, p->size, "floppy")) { DPRINT("Floppy io-port 0x%04lx in use\n", FDCS->address + p->offset); floppy_release_allocated_regions(fdc, p); return -EBUSY; } } return 0; } static void floppy_release_regions(int fdc) { floppy_release_allocated_regions(fdc, ARRAY_END(io_regions)); } static int floppy_grab_irq_and_dma(void) { if (atomic_inc_return(&usage_count) > 1) return 0; /* * We might have scheduled a free_irq(), wait it to * drain first: */ flush_workqueue(floppy_wq); if (fd_request_irq()) { DPRINT("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); atomic_dec(&usage_count); return -1; } if (fd_request_dma()) { DPRINT("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA); if (can_use_virtual_dma & 2) use_virtual_dma = can_use_virtual_dma = 1; if (!(can_use_virtual_dma & 1)) { fd_free_irq(); atomic_dec(&usage_count); return -1; } } for (fdc = 0; fdc < N_FDC; fdc++) { if (FDCS->address != -1) { if (floppy_request_regions(fdc)) goto cleanup; } } for (fdc = 0; fdc < N_FDC; fdc++) { if (FDCS->address != -1) { reset_fdc_info(1); fd_outb(FDCS->dor, FD_DOR); } } fdc = 0; set_dor(0, ~0, 8); /* avoid immediate interrupt */ for (fdc = 0; fdc < N_FDC; fdc++) if (FDCS->address != -1) fd_outb(FDCS->dor, FD_DOR); /* * The driver will try and free resources and relies on us * to know if they were allocated or not. */ fdc = 0; irqdma_allocated = 1; return 0; cleanup: fd_free_irq(); fd_free_dma(); while (--fdc >= 0) floppy_release_regions(fdc); atomic_dec(&usage_count); return -1; } static void floppy_release_irq_and_dma(void) { int old_fdc; #ifndef __sparc__ int drive; #endif long tmpsize; unsigned long tmpaddr; if (!atomic_dec_and_test(&usage_count)) return; if (irqdma_allocated) { fd_disable_dma(); fd_free_dma(); fd_free_irq(); irqdma_allocated = 0; } set_dor(0, ~0, 8); #if N_FDC > 1 set_dor(1, ~8, 0); #endif if (floppy_track_buffer && max_buffer_sectors) { tmpsize = max_buffer_sectors * 1024; tmpaddr = (unsigned long)floppy_track_buffer; floppy_track_buffer = NULL; max_buffer_sectors = 0; buffer_min = buffer_max = -1; fd_dma_mem_free(tmpaddr, tmpsize); } #ifndef __sparc__ for (drive = 0; drive < N_FDC * 4; drive++) if (timer_pending(motor_off_timer + drive)) pr_info("motor off timer %d still active\n", drive); #endif if (delayed_work_pending(&fd_timeout)) pr_info("floppy timer still active:%s\n", timeout_message); if (delayed_work_pending(&fd_timer)) pr_info("auxiliary floppy timer still active\n"); if (work_pending(&floppy_work)) pr_info("work still pending\n"); old_fdc = fdc; for (fdc = 0; fdc < N_FDC; fdc++) if (FDCS->address != -1) floppy_release_regions(fdc); fdc = old_fdc; } #ifdef MODULE static char *floppy; static void __init parse_floppy_cfg_string(char *cfg) { char *ptr; while (*cfg) { ptr = cfg; while (*cfg && *cfg != ' ' && *cfg != '\t') cfg++; if (*cfg) { *cfg = '\0'; cfg++; } if (*ptr) floppy_setup(ptr); } } static int __init floppy_module_init(void) { if (floppy) parse_floppy_cfg_string(floppy); return floppy_init(); } module_init(floppy_module_init); static void __exit floppy_module_exit(void) { int drive; blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); unregister_blkdev(FLOPPY_MAJOR, "fd"); platform_driver_unregister(&floppy_driver); destroy_workqueue(floppy_wq); for (drive = 0; drive < N_DRIVE; drive++) { del_timer_sync(&motor_off_timer[drive]); if (floppy_available(drive)) { del_gendisk(disks[drive]); platform_device_unregister(&floppy_device[drive]); } blk_cleanup_queue(disks[drive]->queue); blk_mq_free_tag_set(&tag_sets[drive]); /* * These disks have not called add_disk(). Don't put down * queue reference in put_disk(). */ if (!(allowed_drive_mask & (1 << drive)) || fdc_state[FDC(drive)].version == FDC_NONE) disks[drive]->queue = NULL; put_disk(disks[drive]); } cancel_delayed_work_sync(&fd_timeout); cancel_delayed_work_sync(&fd_timer); if (atomic_read(&usage_count)) floppy_release_irq_and_dma(); /* eject disk, if any */ fd_eject(0); } module_exit(floppy_module_exit); module_param(floppy, charp, 0); module_param(FLOPPY_IRQ, int, 0); module_param(FLOPPY_DMA, int, 0); MODULE_AUTHOR("Alain L. Knaff"); MODULE_SUPPORTED_DEVICE("fd"); MODULE_LICENSE("GPL"); /* This doesn't actually get used other than for module information */ static const struct pnp_device_id floppy_pnpids[] = { {"PNP0700", 0}, {} }; MODULE_DEVICE_TABLE(pnp, floppy_pnpids); #else __setup("floppy=", floppy_setup); module_init(floppy_init) #endif MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR); cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14494/000077500000000000000000000000001417746362400206345ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14494/README000066400000000000000000000001151417746362400215110ustar00rootroot00000000000000Project: Poppler Details: https://nvd.nist.gov/vuln/detail/CVE-2019-14494 cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.cc000066400000000000000000004223201417746362400242600ustar00rootroot00000000000000//======================================================================== // // SplashOutputDev.cc // // Copyright 2003 Glyph & Cog, LLC // //======================================================================== //======================================================================== // // Modified under the Poppler project - http://poppler.freedesktop.org // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2005 Takashi Iwai // Copyright (C) 2006 Stefan Schweizer // Copyright (C) 2006-2019 Albert Astals Cid // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2006 Scott Turner // Copyright (C) 2007 Koji Otani // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2009-2016 Thomas Freitag // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009, 2014-2016, 2019 William Bader // Copyright (C) 2010 Patrick Spendrin // Copyright (C) 2010 Brian Cameron // Copyright (C) 2010 Paweł Wiejacha // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2011, 2012, 2017 Adrian Johnson // Copyright (C) 2013 Lu Wang // Copyright (C) 2013 Li Junling // Copyright (C) 2014 Ed Porras // Copyright (C) 2014 Richard PALO // Copyright (C) 2015 Tamas Szekeres // Copyright (C) 2015 Kenji Uno // Copyright (C) 2016 Takahiro Hashimoto // Copyright (C) 2017 Even Rouault // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Stefan Brüns // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Christian Persch // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== #include #include #include #include "goo/gfile.h" #include "GlobalParams.h" #include "Error.h" #include "Object.h" #include "Gfx.h" #include "GfxFont.h" #include "Page.h" #include "PDFDoc.h" #include "Link.h" #include "FontEncodingTables.h" #include "fofi/FoFiTrueType.h" #include "splash/SplashBitmap.h" #include "splash/SplashGlyphBitmap.h" #include "splash/SplashPattern.h" #include "splash/SplashScreen.h" #include "splash/SplashPath.h" #include "splash/SplashState.h" #include "splash/SplashErrorCodes.h" #include "splash/SplashFontEngine.h" #include "splash/SplashFont.h" #include "splash/SplashFontFile.h" #include "splash/SplashFontFileID.h" #include "splash/Splash.h" #include "SplashOutputDev.h" #include static const double s_minLineWidth = 0.0; static inline void convertGfxColor(SplashColorPtr dest, SplashColorMode colorMode, GfxColorSpace *colorSpace, GfxColor *src) { SplashColor color; GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif // make gcc happy color[0] = color[1] = color[2] = 0; #ifdef SPLASH_CMYK color[3] = 0; #endif switch (colorMode) { case splashModeMono1: case splashModeMono8: colorSpace->getGray(src, &gray); color[0] = colToByte(gray); break; case splashModeXBGR8: color[3] = 255; // fallthrough case splashModeBGR8: case splashModeRGB8: colorSpace->getRGB(src, &rgb); color[0] = colToByte(rgb.r); color[1] = colToByte(rgb.g); color[2] = colToByte(rgb.b); break; #ifdef SPLASH_CMYK case splashModeCMYK8: colorSpace->getCMYK(src, &cmyk); color[0] = colToByte(cmyk.c); color[1] = colToByte(cmyk.m); color[2] = colToByte(cmyk.y); color[3] = colToByte(cmyk.k); break; case splashModeDeviceN8: colorSpace->getDeviceN(src, &deviceN); for (int i = 0; i < SPOT_NCOMPS + 4; i++) color[i] = colToByte(deviceN.c[i]); break; #endif } splashColorCopy(dest, color); } // Copy a color according to the color mode. // Use convertGfxShortColor() below when the destination is a bitmap // to avoid overwriting cells. // Calling this in SplashGouraudPattern::getParameterizedColor() fixes bug 90570. // Use convertGfxColor() above when the destination is an array of SPOT_NCOMPS+4 bytes, // to ensure that everything is initialized. static inline void convertGfxShortColor(SplashColorPtr dest, SplashColorMode colorMode, GfxColorSpace *colorSpace, GfxColor *src) { switch (colorMode) { case splashModeMono1: case splashModeMono8: { GfxGray gray; colorSpace->getGray(src, &gray); dest[0] = colToByte(gray); } break; case splashModeXBGR8: dest[3] = 255; // fallthrough case splashModeBGR8: case splashModeRGB8: { GfxRGB rgb; colorSpace->getRGB(src, &rgb); dest[0] = colToByte(rgb.r); dest[1] = colToByte(rgb.g); dest[2] = colToByte(rgb.b); } break; #ifdef SPLASH_CMYK case splashModeCMYK8: { GfxCMYK cmyk; colorSpace->getCMYK(src, &cmyk); dest[0] = colToByte(cmyk.c); dest[1] = colToByte(cmyk.m); dest[2] = colToByte(cmyk.y); dest[3] = colToByte(cmyk.k); } break; case splashModeDeviceN8: { GfxColor deviceN; colorSpace->getDeviceN(src, &deviceN); for (int i = 0; i < SPOT_NCOMPS + 4; i++) dest[i] = colToByte(deviceN.c[i]); } break; #endif } } //------------------------------------------------------------------------ // SplashGouraudPattern //------------------------------------------------------------------------ SplashGouraudPattern::SplashGouraudPattern(bool bDirectColorTranslationA, GfxState *stateA, GfxGouraudTriangleShading *shadingA) { state = stateA; shading = shadingA; bDirectColorTranslation = bDirectColorTranslationA; gfxMode = shadingA->getColorSpace()->getMode(); } SplashGouraudPattern::~SplashGouraudPattern() { } void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) { GfxColor src; GfxColorSpace* srcColorSpace = shading->getColorSpace(); int colorComps = 3; #ifdef SPLASH_CMYK if (mode == splashModeCMYK8) colorComps=4; else if (mode == splashModeDeviceN8) colorComps=4 + SPOT_NCOMPS; #endif shading->getParameterizedColor(colorinterp, &src); if (bDirectColorTranslation) { for (int m = 0; m < colorComps; ++m) dest[m] = colToByte(src.c[m]); } else { convertGfxShortColor(dest, mode, srcColorSpace, &src); } } //------------------------------------------------------------------------ // SplashFunctionPattern //------------------------------------------------------------------------ SplashFunctionPattern::SplashFunctionPattern(SplashColorMode colorModeA, GfxState *stateA, GfxFunctionShading *shadingA) { Matrix ctm; SplashColor defaultColor; GfxColor srcColor; const double *matrix = shadingA->getMatrix(); shading = shadingA; state = stateA; colorMode = colorModeA; state->getCTM(&ctm); double a1 = ctm.m[0]; double b1 = ctm.m[1]; double c1 = ctm.m[2]; double d1 = ctm.m[3]; ctm.m[0] = matrix[0] * a1 + matrix[1] * c1; ctm.m[1] = matrix[0] * b1 + matrix[1] * d1; ctm.m[2] = matrix[2] * a1 + matrix[3] * c1; ctm.m[3] = matrix[2] * b1 + matrix[3] * d1; ctm.m[4] = matrix[4] * a1 + matrix[5] * c1 + ctm.m[4]; ctm.m[5] = matrix[4] * b1 + matrix[5] * d1 + ctm.m[5]; ctm.invertTo(&ictm); gfxMode = shadingA->getColorSpace()->getMode(); shadingA->getColorSpace()->getDefaultColor(&srcColor); shadingA->getDomain(&xMin, &yMin, &xMax, &yMax); convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } SplashFunctionPattern::~SplashFunctionPattern() { } bool SplashFunctionPattern::getColor(int x, int y, SplashColorPtr c) { GfxColor gfxColor; double xc, yc; ictm.transform(x, y, &xc, &yc); if (xc < xMin || xc > xMax || yc < yMin || yc > yMax) return false; shading->getColor(xc, yc, &gfxColor); convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); return true; } //------------------------------------------------------------------------ // SplashUnivariatePattern //------------------------------------------------------------------------ SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) { Matrix ctm; double xMin, yMin, xMax, yMax; shading = shadingA; state = stateA; colorMode = colorModeA; state->getCTM(&ctm); ctm.invertTo(&ictm); // get the function domain t0 = shading->getDomain0(); t1 = shading->getDomain1(); dt = t1 - t0; stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax); shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax); gfxMode = shadingA->getColorSpace()->getMode(); } SplashUnivariatePattern::~SplashUnivariatePattern() { } bool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) { GfxColor gfxColor; double xc, yc, t; ictm.transform(x, y, &xc, &yc); if (! getParameter (xc, yc, &t)) return false; const int filled = shading->getColor(t, &gfxColor); if (unlikely(filled < shading->getColorSpace()->getNComps())) { for (int i = filled; i < shading->getColorSpace()->getNComps(); ++i) gfxColor.c[i] = 0; } convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor); return true; } bool SplashUnivariatePattern::testPosition(int x, int y) { double xc, yc, t; ictm.transform(x, y, &xc, &yc); if (! getParameter (xc, yc, &t)) return false; return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0); } //------------------------------------------------------------------------ // SplashRadialPattern //------------------------------------------------------------------------ #define RADIAL_EPSILON (1. / 1024 / 1024) SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA): SplashUnivariatePattern(colorModeA, stateA, shadingA) { SplashColor defaultColor; GfxColor srcColor; shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr); dx -= x0; dy -= y0; dr -= r0; a = dx*dx + dy*dy - dr*dr; if (fabs(a) > RADIAL_EPSILON) inva = 1.0 / a; shadingA->getColorSpace()->getDefaultColor(&srcColor); convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } SplashRadialPattern::~SplashRadialPattern() { } bool SplashRadialPattern::getParameter(double xs, double ys, double *t) { double b, c, s0, s1; // We want to solve this system of equations: // // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2 // 2. xc(s) = x0 + s * (x1 - xo) // 3. yc(s) = y0 + s * (y1 - yo) // 4. rc(s) = r0 + s * (r1 - ro) // // To simplify the system a little, we translate // our coordinates to have the origin in (x0,y0) xs -= x0; ys -= y0; // Then we have to solve the equation: // A*s^2 - 2*B*s + C = 0 // where // A = dx^2 + dy^2 - dr^2 // B = xs*dx + ys*dy + r0*dr // C = xs^2 + ys^2 - r0^2 b = xs*dx + ys*dy + r0*dr; c = xs*xs + ys*ys - r0*r0; if (fabs(a) <= RADIAL_EPSILON) { // A is 0, thus the equation simplifies to: // -2*B*s + C = 0 // If B is 0, we can either have no solution or an indeterminate // equation, thus we behave as if we had an invalid solution if (fabs(b) <= RADIAL_EPSILON) return false; s0 = s1 = 0.5 * c / b; } else { double d; d = b*b - a*c; if (d < 0) return false; d = sqrt (d); s0 = b + d; s1 = b - d; // If A < 0, one of the two solutions will have negative radius, // thus it will be ignored. Otherwise we know that s1 <= s0 // (because d >=0 implies b - d <= b + d), so if both are valid it // will be the true solution. s0 *= inva; s1 *= inva; } if (r0 + s0 * dr >= 0) { if (0 <= s0 && s0 <= 1) { *t = t0 + dt * s0; return true; } else if (s0 < 0 && shading->getExtend0()) { *t = t0; return true; } else if (s0 > 1 && shading->getExtend1()) { *t = t1; return true; } } if (r0 + s1 * dr >= 0) { if (0 <= s1 && s1 <= 1) { *t = t0 + dt * s1; return true; } else if (s1 < 0 && shading->getExtend0()) { *t = t0; return true; } else if (s1 > 1 && shading->getExtend1()) { *t = t1; return true; } } return false; } #undef RADIAL_EPSILON //------------------------------------------------------------------------ // SplashAxialPattern //------------------------------------------------------------------------ SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA): SplashUnivariatePattern(colorModeA, stateA, shadingA) { SplashColor defaultColor; GfxColor srcColor; shadingA->getCoords(&x0, &y0, &x1, &y1); dx = x1 - x0; dy = y1 - y0; const double mul_denominator = (dx * dx + dy * dy); if (unlikely(mul_denominator == 0)) { mul = 0; } else { mul = 1 / mul_denominator; } shadingA->getColorSpace()->getDefaultColor(&srcColor); convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } SplashAxialPattern::~SplashAxialPattern() { } bool SplashAxialPattern::getParameter(double xc, double yc, double *t) { double s; xc -= x0; yc -= y0; s = (xc * dx + yc * dy) * mul; if (0 <= s && s <= 1) { *t = t0 + dt * s; } else if (s < 0 && shading->getExtend0()) { *t = t0; } else if (s > 1 && shading->getExtend1()) { *t = t1; } else { return false; } return true; } //------------------------------------------------------------------------ // Type 3 font cache size parameters #define type3FontCacheAssoc 8 #define type3FontCacheMaxSets 8 #define type3FontCacheSize (128*1024) //------------------------------------------------------------------------ // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result. static inline unsigned char div255(int x) { return (unsigned char)((x + (x >> 8) + 0x80) >> 8); } //------------------------------------------------------------------------ // Blend functions //------------------------------------------------------------------------ static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = (dest[i] * src[i]) / 255; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < 0x80 ? (src[i] * 2 * dest[i]) / 255 : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < src[i] ? dest[i] : src[i]; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] > src[i] ? dest[i] : src[i]; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (src[i] == 255) { blend[i] = 255; } else { x = (dest[i] * 255) / (255 - src[i]); blend[i] = x <= 255 ? x : 255; } } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (src[i] == 0) { blend[i] = 0; } else { x = ((255 - dest[i]) * 255) / src[i]; blend[i] = x <= 255 ? 255 - x : 0; } } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = src[i] < 0x80 ? (dest[i] * 2 * src[i]) / 255 : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i, x; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { if (src[i] < 0x80) { blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255); } else { if (dest[i] < 0x40) { x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255; } else { x = (int)sqrt(255.0 * dest[i]); } blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255; } } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } #endif } static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i]; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } if (cm == splashModeDeviceN8) { for (i = 4; i < splashColorModeNComps[cm]; ++i) { if (dest[i] == 0 && src[i] == 0) blend[i] = 0; } } #endif } static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { int i; #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; } } #endif { for (i = 0; i < splashColorModeNComps[cm]; ++i) { blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255; } } #ifdef SPLASH_CMYK if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) { for (i = 0; i < splashColorModeNComps[cm]; ++i) { dest[i] = 255 - dest[i]; src[i] = 255 - src[i]; blend[i] = 255 - blend[i]; } } if (cm == splashModeDeviceN8) { for (i = 4; i < splashColorModeNComps[cm]; ++i) { if (dest[i] == 0 && src[i] == 0) blend[i] = 0; } } #endif } static int getLum(int r, int g, int b) { return (int)(0.3 * r + 0.59 * g + 0.11 * b); } static int getSat(int r, int g, int b) { int rgbMin, rgbMax; rgbMin = rgbMax = r; if (g < rgbMin) { rgbMin = g; } else if (g > rgbMax) { rgbMax = g; } if (b < rgbMin) { rgbMin = b; } else if (b > rgbMax) { rgbMax = b; } return rgbMax - rgbMin; } static void clipColor(int rIn, int gIn, int bIn, unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { int lum, rgbMin, rgbMax; lum = getLum(rIn, gIn, bIn); rgbMin = rgbMax = rIn; if (gIn < rgbMin) { rgbMin = gIn; } else if (gIn > rgbMax) { rgbMax = gIn; } if (bIn < rgbMin) { rgbMin = bIn; } else if (bIn > rgbMax) { rgbMax = bIn; } if (rgbMin < 0) { *rOut = (unsigned char)(lum + ((rIn - lum) * lum) / (lum - rgbMin)); *gOut = (unsigned char)(lum + ((gIn - lum) * lum) / (lum - rgbMin)); *bOut = (unsigned char)(lum + ((bIn - lum) * lum) / (lum - rgbMin)); } else if (rgbMax > 255) { *rOut = (unsigned char)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum)); *gOut = (unsigned char)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum)); *bOut = (unsigned char)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum)); } else { *rOut = rIn; *gOut = gIn; *bOut = bIn; } } static void setLum(unsigned char rIn, unsigned char gIn, unsigned char bIn, int lum, unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { int d; d = lum - getLum(rIn, gIn, bIn); clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut); } static void setSat(unsigned char rIn, unsigned char gIn, unsigned char bIn, int sat, unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { int rgbMin, rgbMid, rgbMax; unsigned char *minOut, *midOut, *maxOut; if (rIn < gIn) { rgbMin = rIn; minOut = rOut; rgbMid = gIn; midOut = gOut; } else { rgbMin = gIn; minOut = gOut; rgbMid = rIn; midOut = rOut; } if (bIn > rgbMid) { rgbMax = bIn; maxOut = bOut; } else if (bIn > rgbMin) { rgbMax = rgbMid; maxOut = midOut; rgbMid = bIn; midOut = bOut; } else { rgbMax = rgbMid; maxOut = midOut; rgbMid = rgbMin; midOut = minOut; rgbMin = bIn; minOut = bOut; } if (rgbMax > rgbMin) { *midOut = (unsigned char)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin); *maxOut = (unsigned char)sat; } else { *midOut = *maxOut = 0; } *minOut = 0; } static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { unsigned char r0, g0, b0; #ifdef SPLASH_CMYK unsigned char r1, g1, b1; int i; SplashColor src2, dest2; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; #ifdef SPLASH_CMYK case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } // NB: inputs have already been converted to additive mode setSat(src2[0], src2[1], src2[2], getSat(dest2[0], dest2[1], dest2[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), &r1, &g1, &b1); blend[0] = r1; blend[1] = g1; blend[2] = b1; blend[3] = dest2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; #endif } } static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { unsigned char r0, g0, b0; #ifdef SPLASH_CMYK unsigned char r1, g1, b1; int i; SplashColor src2, dest2; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; #ifdef SPLASH_CMYK case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } setSat(dest2[0], dest2[1], dest2[2], getSat(src2[0], src2[1], src2[2]), &r0, &g0, &b0); setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]), &r1, &g1, &b1); blend[0] = r1; blend[1] = g1; blend[2] = b1; blend[3] = dest2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; #endif } } static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { #ifdef SPLASH_CMYK unsigned char r, g, b; int i; SplashColor src2, dest2; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]), &blend[0], &blend[1], &blend[2]); break; #ifdef SPLASH_CMYK case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } setLum(src2[0], src2[1], src2[2], getLum(dest2[0], dest2[1], dest2[2]), &r, &g, &b); blend[0] = r; blend[1] = g; blend[2] = b; blend[3] = dest2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; #endif } } static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) { #ifdef SPLASH_CMYK unsigned char r, g, b; int i; SplashColor src2, dest2; #endif switch (cm) { case splashModeMono1: case splashModeMono8: blend[0] = dest[0]; break; case splashModeXBGR8: src[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]), &blend[0], &blend[1], &blend[2]); break; #ifdef SPLASH_CMYK case splashModeCMYK8: case splashModeDeviceN8: for (i = 0; i < 4; i++) { // convert to additive src2[i] = 0xff - src[i]; dest2[i] = 0xff - dest[i]; } setLum(dest2[0], dest2[1], dest2[2], getLum(src2[0], src2[1], src2[2]), &r, &g, &b); blend[0] = r; blend[1] = g; blend[2] = b; blend[3] = src2[3]; for (i = 0; i < 4; i++) { // convert back to subtractive blend[i] = 0xff - blend[i]; } break; #endif } } // NB: This must match the GfxBlendMode enum defined in GfxState.h. static const SplashBlendFunc splashOutBlendFuncs[] = { nullptr, &splashOutBlendMultiply, &splashOutBlendScreen, &splashOutBlendOverlay, &splashOutBlendDarken, &splashOutBlendLighten, &splashOutBlendColorDodge, &splashOutBlendColorBurn, &splashOutBlendHardLight, &splashOutBlendSoftLight, &splashOutBlendDifference, &splashOutBlendExclusion, &splashOutBlendHue, &splashOutBlendSaturation, &splashOutBlendColor, &splashOutBlendLuminosity }; //------------------------------------------------------------------------ // SplashOutFontFileID //------------------------------------------------------------------------ class SplashOutFontFileID: public SplashFontFileID { public: SplashOutFontFileID(const Ref *rA) { r = *rA; } ~SplashOutFontFileID() {} bool matches(SplashFontFileID *id) override { return ((SplashOutFontFileID *)id)->r == r; } private: Ref r; }; //------------------------------------------------------------------------ // T3FontCache //------------------------------------------------------------------------ struct T3FontCacheTag { unsigned short code; unsigned short mru; // valid bit (0x8000) and MRU index }; class T3FontCache { public: T3FontCache(const Ref *fontID, double m11A, double m12A, double m21A, double m22A, int glyphXA, int glyphYA, int glyphWA, int glyphHA, bool aa, bool validBBoxA); ~T3FontCache(); T3FontCache(const T3FontCache &) = delete; T3FontCache& operator=(const T3FontCache &) = delete; bool matches(const Ref *idA, double m11A, double m12A, double m21A, double m22A) { return fontID == *idA && m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; } Ref fontID; // PDF font ID double m11, m12, m21, m22; // transform matrix int glyphX, glyphY; // pixel offset of glyph bitmaps int glyphW, glyphH; // size of glyph bitmaps, in pixels bool validBBox; // false if the bbox was [0 0 0 0] int glyphSize; // size of glyph bitmaps, in bytes int cacheSets; // number of sets in cache int cacheAssoc; // cache associativity (glyphs per set) unsigned char *cacheData; // glyph pixmap cache T3FontCacheTag *cacheTags; // cache tags, i.e., char codes }; T3FontCache::T3FontCache(const Ref *fontIDA, double m11A, double m12A, double m21A, double m22A, int glyphXA, int glyphYA, int glyphWA, int glyphHA, bool validBBoxA, bool aa) { fontID = *fontIDA; m11 = m11A; m12 = m12A; m21 = m21A; m22 = m22A; glyphX = glyphXA; glyphY = glyphYA; glyphW = glyphWA; glyphH = glyphHA; validBBox = validBBoxA; // sanity check for excessively large glyphs (which most likely // indicate an incorrect BBox) if (glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0 || glyphW * glyphH > 100000) { glyphW = glyphH = 100; validBBox = false; } if (aa) { glyphSize = glyphW * glyphH; } else { glyphSize = ((glyphW + 7) >> 3) * glyphH; } cacheAssoc = type3FontCacheAssoc; for (cacheSets = type3FontCacheMaxSets; cacheSets > 1 && cacheSets * cacheAssoc * glyphSize > type3FontCacheSize; cacheSets >>= 1) ; if (glyphSize < 10485760 / cacheAssoc / cacheSets) { cacheData = (unsigned char *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize); } else { error(errSyntaxWarning, -1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n" " This could teoretically result in wrong rendering,\n" " but most probably the document is bogus.\n" " Please report a bug if you think the rendering may be wrong because of this."); cacheData = nullptr; } if (cacheData != nullptr) { cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc, sizeof(T3FontCacheTag)); for (int i = 0; i < cacheSets * cacheAssoc; ++i) { cacheTags[i].mru = i & (cacheAssoc - 1); } } else { cacheTags = nullptr; } } T3FontCache::~T3FontCache() { gfree(cacheData); gfree(cacheTags); } struct T3GlyphStack { unsigned short code; // character code bool haveDx; // set after seeing a d0/d1 operator bool doNotCache; // set if we see a gsave/grestore before // the d0/d1 //----- cache info T3FontCache *cache; // font cache for the current font T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph unsigned char *cacheData; // pointer to cache data for the glyph //----- saved state SplashBitmap *origBitmap; Splash *origSplash; double origCTM4, origCTM5; T3GlyphStack *next; // next object on stack }; //------------------------------------------------------------------------ // SplashTransparencyGroup //------------------------------------------------------------------------ struct SplashTransparencyGroup { int tx, ty; // translation coordinates SplashBitmap *tBitmap; // bitmap for transparency group SplashBitmap *softmask; // bitmap for softmasks GfxColorSpace *blendingColorSpace; bool isolated; //----- for knockout SplashBitmap *shape; bool knockout; SplashCoord knockoutOpacity; bool fontAA; //----- saved state SplashBitmap *origBitmap; Splash *origSplash; SplashTransparencyGroup *next; }; //------------------------------------------------------------------------ // SplashOutputDev //------------------------------------------------------------------------ SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, SplashColorPtr paperColorA, bool bitmapTopDownA, SplashThinLineMode thinLineMode, bool overprintPreviewA) { colorMode = colorModeA; bitmapRowPad = bitmapRowPadA; bitmapTopDown = bitmapTopDownA; bitmapUpsideDown = false; fontAntialias = true; vectorAntialias = true; overprintPreview = overprintPreviewA; enableFreeTypeHinting = false; enableSlightHinting = false; setupScreenParams(72.0, 72.0); reverseVideo = reverseVideoA; if (paperColorA != nullptr) { splashColorCopy(paperColor, paperColorA); } else { splashClearColor(paperColor); } skipHorizText = false; skipRotatedText = false; keepAlphaChannel = paperColorA == nullptr; doc = nullptr; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setMinLineWidth(s_minLineWidth); splash->setThinLineMode(thinLineMode); splash->clear(paperColor, 0); fontEngine = nullptr; nT3Fonts = 0; t3GlyphStack = nullptr; font = nullptr; needFontUpdate = false; textClipPath = nullptr; transpGroupStack = nullptr; nestCount = 0; xref = nullptr; } void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) { screenParams.size = -1; screenParams.dotRadius = -1; screenParams.gamma = (SplashCoord)1.0; screenParams.blackThreshold = (SplashCoord)0.0; screenParams.whiteThreshold = (SplashCoord)1.0; // use clustered dithering for resolution >= 300 dpi // (compare to 299.9 to avoid floating point issues) if (hDPI > 299.9 && vDPI > 299.9) { screenParams.type = splashScreenStochasticClustered; if (screenParams.size < 0) { screenParams.size = 64; } if (screenParams.dotRadius < 0) { screenParams.dotRadius = 2; } } else { screenParams.type = splashScreenDispersed; if (screenParams.size < 0) { screenParams.size = 4; } } } SplashOutputDev::~SplashOutputDev() { int i; for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } if (fontEngine) { delete fontEngine; } if (splash) { delete splash; } if (bitmap) { delete bitmap; } delete textClipPath; } void SplashOutputDev::startDoc(PDFDoc *docA) { int i; doc = docA; if (fontEngine) { delete fontEngine; } fontEngine = new SplashFontEngine( globalParams->getEnableFreeType(), enableFreeTypeHinting, enableSlightHinting, getFontAntialias() && colorMode != splashModeMono1); for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } nT3Fonts = 0; } void SplashOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { int w, h; SplashCoord mat[6]; SplashColor color; xref = xrefA; if (state) { setupScreenParams(state->getHDPI(), state->getVDPI()); w = (int)(state->getPageWidth() + 0.5); if (w <= 0) { w = 1; } h = (int)(state->getPageHeight() + 0.5); if (h <= 0) { h = 1; } } else { w = h = 1; } SplashThinLineMode thinLineMode = splashThinLineDefault; if (splash) { thinLineMode = splash->getThinLineMode(); delete splash; splash = nullptr; } if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) { if (bitmap) { delete bitmap; bitmap = nullptr; } bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); if (!bitmap->getDataPtr()) { delete bitmap; w = h = 1; bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); } } splash = new Splash(bitmap, vectorAntialias, &screenParams); splash->setThinLineMode(thinLineMode); splash->setMinLineWidth(s_minLineWidth); if (state) { const double *ctm = state->getCTM(); mat[0] = (SplashCoord)ctm[0]; mat[1] = (SplashCoord)ctm[1]; mat[2] = (SplashCoord)ctm[2]; mat[3] = (SplashCoord)ctm[3]; mat[4] = (SplashCoord)ctm[4]; mat[5] = (SplashCoord)ctm[5]; splash->setMatrix(mat); } switch (colorMode) { case splashModeMono1: case splashModeMono8: color[0] = 0; break; case splashModeXBGR8: color[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: color[0] = color[1] = color[2] = 0; break; #ifdef SPLASH_CMYK case splashModeCMYK8: color[0] = color[1] = color[2] = color[3] = 0; break; case splashModeDeviceN8: for (int i = 0; i < 4 + SPOT_NCOMPS; i++) color[i] = 0; break; #endif } splash->setStrokePattern(new SplashSolidColor(color)); splash->setFillPattern(new SplashSolidColor(color)); splash->setLineCap(splashLineCapButt); splash->setLineJoin(splashLineJoinMiter); splash->setLineDash(nullptr, 0, 0); splash->setMiterLimit(10); splash->setFlatness(1); // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true splash->setStrokeAdjust(true); splash->clear(paperColor, 0); } void SplashOutputDev::endPage() { if (colorMode != splashModeMono1 && !keepAlphaChannel) { splash->compositeBackground(paperColor); } } void SplashOutputDev::saveState(GfxState *state) { splash->saveState(); if (t3GlyphStack && !t3GlyphStack->haveDx) { t3GlyphStack->doNotCache = true; error(errSyntaxWarning, -1, "Save (q) operator before d0/d1 in Type 3 glyph"); } } void SplashOutputDev::restoreState(GfxState *state) { splash->restoreState(); needFontUpdate = true; if (t3GlyphStack && !t3GlyphStack->haveDx) { t3GlyphStack->doNotCache = true; error(errSyntaxWarning, -1, "Restore (Q) operator before d0/d1 in Type 3 glyph"); } } void SplashOutputDev::updateAll(GfxState *state) { updateLineDash(state); updateLineJoin(state); updateLineCap(state); updateLineWidth(state); updateFlatness(state); updateMiterLimit(state); updateStrokeAdjust(state); updateFillColorSpace(state); updateFillColor(state); updateStrokeColorSpace(state); updateStrokeColor(state); needFontUpdate = true; } void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) { SplashCoord mat[6]; const double *ctm = state->getCTM(); mat[0] = (SplashCoord)ctm[0]; mat[1] = (SplashCoord)ctm[1]; mat[2] = (SplashCoord)ctm[2]; mat[3] = (SplashCoord)ctm[3]; mat[4] = (SplashCoord)ctm[4]; mat[5] = (SplashCoord)ctm[5]; splash->setMatrix(mat); } void SplashOutputDev::updateLineDash(GfxState *state) { double *dashPattern; int dashLength; double dashStart; SplashCoord dash[20]; int i; state->getLineDash(&dashPattern, &dashLength, &dashStart); if (dashLength > 20) { dashLength = 20; } for (i = 0; i < dashLength; ++i) { dash[i] = (SplashCoord)dashPattern[i]; if (dash[i] < 0) { dash[i] = 0; } } splash->setLineDash(dash, dashLength, (SplashCoord)dashStart); } void SplashOutputDev::updateFlatness(GfxState *state) { #if 0 // Acrobat ignores the flatness setting, and always renders curves // with a fairly small flatness value splash->setFlatness(state->getFlatness()); #endif } void SplashOutputDev::updateLineJoin(GfxState *state) { splash->setLineJoin(state->getLineJoin()); } void SplashOutputDev::updateLineCap(GfxState *state) { splash->setLineCap(state->getLineCap()); } void SplashOutputDev::updateMiterLimit(GfxState *state) { splash->setMiterLimit(state->getMiterLimit()); } void SplashOutputDev::updateLineWidth(GfxState *state) { splash->setLineWidth(state->getLineWidth()); } void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) { #if 0 // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true splash->setStrokeAdjust(state->getStrokeAdjust()); #endif } void SplashOutputDev::updateFillColorSpace(GfxState *state) { #ifdef SPLASH_CMYK if (colorMode == splashModeDeviceN8) state->getFillColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); #endif } void SplashOutputDev::updateStrokeColorSpace(GfxState *state) { #ifdef SPLASH_CMYK if (colorMode == splashModeDeviceN8) state->getStrokeColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); #endif } void SplashOutputDev::updateFillColor(GfxState *state) { GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif switch (colorMode) { case splashModeMono1: case splashModeMono8: state->getFillGray(&gray); splash->setFillPattern(getColor(gray)); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: state->getFillRGB(&rgb); splash->setFillPattern(getColor(&rgb)); break; #ifdef SPLASH_CMYK case splashModeCMYK8: state->getFillCMYK(&cmyk); splash->setFillPattern(getColor(&cmyk)); break; case splashModeDeviceN8: state->getFillDeviceN(&deviceN); splash->setFillPattern(getColor(&deviceN)); break; #endif } } void SplashOutputDev::updateStrokeColor(GfxState *state) { GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif switch (colorMode) { case splashModeMono1: case splashModeMono8: state->getStrokeGray(&gray); splash->setStrokePattern(getColor(gray)); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: state->getStrokeRGB(&rgb); splash->setStrokePattern(getColor(&rgb)); break; #ifdef SPLASH_CMYK case splashModeCMYK8: state->getStrokeCMYK(&cmyk); splash->setStrokePattern(getColor(&cmyk)); break; case splashModeDeviceN8: state->getStrokeDeviceN(&deviceN); splash->setStrokePattern(getColor(&deviceN)); break; #endif } } SplashPattern *SplashOutputDev::getColor(GfxGray gray) { SplashColor color; if (reverseVideo) { gray = gfxColorComp1 - gray; } color[0] = colToByte(gray); return new SplashSolidColor(color); } SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) { GfxColorComp r, g, b; SplashColor color; if (reverseVideo) { r = gfxColorComp1 - rgb->r; g = gfxColorComp1 - rgb->g; b = gfxColorComp1 - rgb->b; } else { r = rgb->r; g = rgb->g; b = rgb->b; } color[0] = colToByte(r); color[1] = colToByte(g); color[2] = colToByte(b); if (colorMode == splashModeXBGR8) color[3] = 255; return new SplashSolidColor(color); } #ifdef SPLASH_CMYK SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) { SplashColor color; color[0] = colToByte(cmyk->c); color[1] = colToByte(cmyk->m); color[2] = colToByte(cmyk->y); color[3] = colToByte(cmyk->k); return new SplashSolidColor(color); } SplashPattern *SplashOutputDev::getColor(GfxColor *deviceN) { SplashColor color; for (int i = 0; i < 4 + SPOT_NCOMPS; i++) color[i] = colToByte(deviceN->c[i]); return new SplashSolidColor(color); } #endif void SplashOutputDev::getMatteColor(SplashColorMode colorMode, GfxImageColorMap *colorMap, const GfxColor *matteColorIn, SplashColor matteColor) { GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif switch (colorMode) { case splashModeMono1: case splashModeMono8: colorMap->getColorSpace()->getGray(matteColorIn, &gray); matteColor[0] = colToByte(gray); break; case splashModeRGB8: case splashModeBGR8: colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); matteColor[0] = colToByte(rgb.r); matteColor[1] = colToByte(rgb.g); matteColor[2] = colToByte(rgb.b); break; case splashModeXBGR8: colorMap->getColorSpace()->getRGB(matteColorIn, &rgb); matteColor[0] = colToByte(rgb.r); matteColor[1] = colToByte(rgb.g); matteColor[2] = colToByte(rgb.b); matteColor[3] = 255; break; #ifdef SPLASH_CMYK case splashModeCMYK8: colorMap->getColorSpace()->getCMYK(matteColorIn, &cmyk); matteColor[0] = colToByte(cmyk.c); matteColor[1] = colToByte(cmyk.m); matteColor[2] = colToByte(cmyk.y); matteColor[3] = colToByte(cmyk.k); break; case splashModeDeviceN8: colorMap->getColorSpace()->getDeviceN(matteColorIn, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) matteColor[cp] = colToByte(deviceN.c[cp]); break; #endif } } void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace, bool overprintFlag, int overprintMode, const GfxColor *singleColor, bool grayIndexed) { #ifdef SPLASH_CMYK unsigned int mask; GfxCMYK cmyk; bool additive = false; int i; if (colorSpace->getMode() == csIndexed) { setOverprintMask(((GfxIndexedColorSpace *)colorSpace)->getBase(), overprintFlag, overprintMode, singleColor, grayIndexed); return; } if (overprintFlag && overprintPreview) { mask = colorSpace->getOverprintMask(); if (singleColor && overprintMode && colorSpace->getMode() == csDeviceCMYK) { colorSpace->getCMYK(singleColor, &cmyk); if (cmyk.c == 0) { mask &= ~1; } if (cmyk.m == 0) { mask &= ~2; } if (cmyk.y == 0) { mask &= ~4; } if (cmyk.k == 0) { mask &= ~8; } } if (grayIndexed) { mask &= ~7; } else if (colorSpace->getMode() == csSeparation) { GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace; additive = deviceSep->getName()->cmp("All") != 0 && mask == 0x0f && !deviceSep->isNonMarking(); } else if (colorSpace->getMode() == csDeviceN) { GfxDeviceNColorSpace *deviceNCS = (GfxDeviceNColorSpace *)colorSpace; additive = mask == 0x0f && !deviceNCS->isNonMarking(); for (i = 0; i < deviceNCS->getNComps() && additive; i++) { if (deviceNCS->getColorantName(i)->cmp("Cyan") == 0) { additive = false; } else if (deviceNCS->getColorantName(i)->cmp("Magenta") == 0) { additive = false; } else if (deviceNCS->getColorantName(i)->cmp("Yellow") == 0) { additive = false; } else if (deviceNCS->getColorantName(i)->cmp("Black") == 0) { additive = false; } } } } else { mask = 0xffffffff; } splash->setOverprintMask(mask, additive); #endif } void SplashOutputDev::updateBlendMode(GfxState *state) { splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]); } void SplashOutputDev::updateFillOpacity(GfxState *state) { splash->setFillAlpha((SplashCoord)state->getFillOpacity()); if (transpGroupStack != nullptr && (SplashCoord)state->getFillOpacity() < transpGroupStack->knockoutOpacity) { transpGroupStack->knockoutOpacity = (SplashCoord)state->getFillOpacity(); } } void SplashOutputDev::updateStrokeOpacity(GfxState *state) { splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity()); if (transpGroupStack != nullptr && (SplashCoord)state->getStrokeOpacity() < transpGroupStack->knockoutOpacity) { transpGroupStack->knockoutOpacity = (SplashCoord)state->getStrokeOpacity(); } } void SplashOutputDev::updatePatternOpacity(GfxState *state) { splash->setPatternAlpha((SplashCoord)state->getStrokeOpacity(), (SplashCoord)state->getFillOpacity()); } void SplashOutputDev::clearPatternOpacity(GfxState *state) { splash->clearPatternAlpha(); } void SplashOutputDev::updateFillOverprint(GfxState *state) { splash->setFillOverprint(state->getFillOverprint()); } void SplashOutputDev::updateStrokeOverprint(GfxState *state) { splash->setStrokeOverprint(state->getStrokeOverprint()); } void SplashOutputDev::updateOverprintMode(GfxState *state) { splash->setOverprintMode(state->getOverprintMode()); } void SplashOutputDev::updateTransfer(GfxState *state) { Function **transfer; unsigned char red[256], green[256], blue[256], gray[256]; double x, y; int i; transfer = state->getTransfer(); if (transfer[0] && transfer[0]->getInputSize() == 1 && transfer[0]->getOutputSize() == 1) { if (transfer[1] && transfer[1]->getInputSize() == 1 && transfer[1]->getOutputSize() == 1 && transfer[2] && transfer[2]->getInputSize() == 1 && transfer[2]->getOutputSize() == 1 && transfer[3] && transfer[3]->getInputSize() == 1 && transfer[3]->getOutputSize() == 1) { for (i = 0; i < 256; ++i) { x = i / 255.0; transfer[0]->transform(&x, &y); red[i] = (unsigned char)(y * 255.0 + 0.5); transfer[1]->transform(&x, &y); green[i] = (unsigned char)(y * 255.0 + 0.5); transfer[2]->transform(&x, &y); blue[i] = (unsigned char)(y * 255.0 + 0.5); transfer[3]->transform(&x, &y); gray[i] = (unsigned char)(y * 255.0 + 0.5); } } else { for (i = 0; i < 256; ++i) { x = i / 255.0; transfer[0]->transform(&x, &y); red[i] = green[i] = blue[i] = gray[i] = (unsigned char)(y * 255.0 + 0.5); } } } else { for (i = 0; i < 256; ++i) { red[i] = green[i] = blue[i] = gray[i] = (unsigned char)i; } } splash->setTransfer(red, green, blue, gray); } void SplashOutputDev::updateFont(GfxState * /*state*/) { needFontUpdate = true; } void SplashOutputDev::doUpdateFont(GfxState *state) { GfxFont *gfxFont; GfxFontLoc *fontLoc; GfxFontType fontType; SplashOutFontFileID *id = nullptr; SplashFontFile *fontFile; SplashFontSrc *fontsrc = nullptr; FoFiTrueType *ff; GooString *fileName; char *tmpBuf; int tmpBufLen; int *codeToGID; const double *textMat; double m11, m12, m21, m22, fontSize; int faceIndex = 0; SplashCoord mat[4]; int n, i; bool recreateFont = false; bool doAdjustFontMatrix = false; needFontUpdate = false; font = nullptr; fileName = nullptr; tmpBuf = nullptr; fontLoc = nullptr; if (!(gfxFont = state->getFont())) { goto err1; } fontType = gfxFont->getType(); if (fontType == fontType3) { goto err1; } // sanity-check the font size - skip anything larger than 10 inches // (this avoids problems allocating memory for the font cache) if (state->getTransformedFontSize() > 10 * (state->getHDPI() + state->getVDPI())) { goto err1; } // check the font file cache reload: delete id; delete fontLoc; fontLoc = nullptr; if (fontsrc && !fontsrc->isFile) { fontsrc->unref(); fontsrc = nullptr; } id = new SplashOutFontFileID(gfxFont->getID()); if ((fontFile = fontEngine->getFontFile(id))) { delete id; } else { if (!(fontLoc = gfxFont->locateFont((xref) ? xref : doc->getXRef(), nullptr))) { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); goto err2; } // embedded font if (fontLoc->locType == gfxFontLocEmbedded) { // if there is an embedded font, read it to memory tmpBuf = gfxFont->readEmbFontFile((xref) ? xref : doc->getXRef(), &tmpBufLen); if (! tmpBuf) goto err2; // external font } else { // gfxFontLocExternal fileName = fontLoc->path; fontType = fontLoc->fontType; doAdjustFontMatrix = true; } fontsrc = new SplashFontSrc; if (fileName) fontsrc->setFile(fileName, false); else fontsrc->setBuf(tmpBuf, tmpBufLen, true); // load the font file switch (fontType) { case fontType1: if (!(fontFile = fontEngine->loadType1Font( id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) goto reload; goto err2; } break; case fontType1C: if (!(fontFile = fontEngine->loadType1CFont( id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) goto reload; goto err2; } break; case fontType1COT: if (!(fontFile = fontEngine->loadOpenTypeT1CFont( id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) goto reload; goto err2; } break; case fontTrueType: case fontTrueTypeOT: if (fileName) ff = FoFiTrueType::load(fileName->c_str()); else ff = FoFiTrueType::make(tmpBuf, tmpBufLen); if (ff) { codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff); n = 256; delete ff; // if we're substituting for a non-TrueType font, we need to mark // all notdef codes as "do not draw" (rather than drawing TrueType // notdef glyphs) if (gfxFont->getType() != fontTrueType && gfxFont->getType() != fontTrueTypeOT) { for (i = 0; i < 256; ++i) { if (codeToGID[i] == 0) { codeToGID[i] = -1; } } } } else { codeToGID = nullptr; n = 0; } if (!(fontFile = fontEngine->loadTrueTypeFont( id, fontsrc, codeToGID, n))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) goto reload; goto err2; } break; case fontCIDType0: case fontCIDType0C: if (!(fontFile = fontEngine->loadCIDFont( id, fontsrc))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) goto reload; goto err2; } break; case fontCIDType0COT: if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); codeToGID = (int *)gmallocn(n, sizeof(int)); memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); } else { codeToGID = nullptr; n = 0; } if (!(fontFile = fontEngine->loadOpenTypeCFFFont( id, fontsrc, codeToGID, n))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) goto reload; goto err2; } break; case fontCIDType2: case fontCIDType2OT: codeToGID = nullptr; n = 0; if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); if (n) { codeToGID = (int *)gmallocn(n, sizeof(int)); memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); } } else { if (fileName) ff = FoFiTrueType::load(fileName->c_str()); else ff = FoFiTrueType::make(tmpBuf, tmpBufLen); if (! ff) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); goto err2; } codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n); delete ff; } if (!(fontFile = fontEngine->loadTrueTypeFont( id, fontsrc, codeToGID, n, faceIndex))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) goto reload; goto err2; } break; default: // this shouldn't happen goto err2; } fontFile->doAdjustMatrix = doAdjustFontMatrix; } // get the font matrix textMat = state->getTextMat(); fontSize = state->getFontSize(); m11 = textMat[0] * fontSize * state->getHorizScaling(); m12 = textMat[1] * fontSize * state->getHorizScaling(); m21 = textMat[2] * fontSize; m22 = textMat[3] * fontSize; // create the scaled font mat[0] = m11; mat[1] = m12; mat[2] = m21; mat[3] = m22; font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); // for substituted fonts: adjust the font matrix -- compare the // width of 'm' in the original font and the substituted font if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) { double w1, w2, w3; CharCode code; const char *name; for (code = 0; code < 256; ++code) { if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') { break; } } if (code < 256) { w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code); w2 = font->getGlyphAdvance(code); w3 = ((Gfx8BitFont *)gfxFont)->getWidth(0); if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) { // if real font is substantially narrower than substituted // font, reduce the font size accordingly if (w1 > 0.01 && w1 < 0.9 * w2) { w1 /= w2; m11 *= w1; m21 *= w1; recreateFont = true; } } } } if (recreateFont) { mat[0] = m11; mat[1] = m12; mat[2] = m21; mat[3] = m22; font = fontEngine->getFont(fontFile, mat, splash->getMatrix()); } delete fontLoc; if (fontsrc && !fontsrc->isFile) fontsrc->unref(); return; err2: delete id; delete fontLoc; err1: if (fontsrc && !fontsrc->isFile) fontsrc->unref(); return; } void SplashOutputDev::stroke(GfxState *state) { if (state->getStrokeColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); SplashPath path = convertPath(state, state->getPath(), false); splash->stroke(&path); } void SplashOutputDev::fill(GfxState *state) { if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); SplashPath path = convertPath(state, state->getPath(), true); splash->fill(&path, false); } void SplashOutputDev::eoFill(GfxState *state) { if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); SplashPath path = convertPath(state, state->getPath(), true); splash->fill(&path, true); } void SplashOutputDev::clip(GfxState *state) { SplashPath path = convertPath(state, state->getPath(), true); splash->clipToPath(&path, false); } void SplashOutputDev::eoClip(GfxState *state) { SplashPath path = convertPath(state, state->getPath(), true); splash->clipToPath(&path, true); } void SplashOutputDev::clipToStrokePath(GfxState *state) { SplashPath *path2; SplashPath path = convertPath(state, state->getPath(), false); path2 = splash->makeStrokePath(&path, state->getLineWidth()); splash->clipToPath(path2, false); delete path2; } SplashPath SplashOutputDev::convertPath(GfxState *state, GfxPath *path, bool dropEmptySubpaths) { SplashPath sPath; GfxSubpath *subpath; int n, i, j; n = dropEmptySubpaths ? 1 : 0; for (i = 0; i < path->getNumSubpaths(); ++i) { subpath = path->getSubpath(i); if (subpath->getNumPoints() > n) { sPath.reserve(subpath->getNumPoints() + 1); sPath.moveTo((SplashCoord)subpath->getX(0), (SplashCoord)subpath->getY(0)); j = 1; while (j < subpath->getNumPoints()) { if (subpath->getCurve(j)) { sPath.curveTo((SplashCoord)subpath->getX(j), (SplashCoord)subpath->getY(j), (SplashCoord)subpath->getX(j+1), (SplashCoord)subpath->getY(j+1), (SplashCoord)subpath->getX(j+2), (SplashCoord)subpath->getY(j+2)); j += 3; } else { sPath.lineTo((SplashCoord)subpath->getX(j), (SplashCoord)subpath->getY(j)); ++j; } } if (subpath->isClosed()) { sPath.close(); } } } return sPath; } void SplashOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode *u, int uLen) { SplashPath *path; int render; bool doFill, doStroke, doClip, strokeAdjust; double m[4]; bool horiz; if (skipHorizText || skipRotatedText) { state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); horiz = m[0] > 0 && fabs(m[1]) < 0.001 && fabs(m[2]) < 0.001 && m[3] < 0; if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { return; } } // check for invisible text -- this is used by Acrobat Capture render = state->getRender(); if (render == 3) { return; } if (needFontUpdate) { doUpdateFont(state); } if (!font) { return; } x -= originX; y -= originY; doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking(); doStroke = ((render & 3) == 1 || (render & 3) == 2) && !state->getStrokeColorSpace()->isNonMarking(); doClip = render & 4; path = nullptr; SplashCoord lineWidth = splash->getLineWidth(); if (doStroke && lineWidth == 0.0) splash->setLineWidth(1 / state->getVDPI()); if (doStroke || doClip) { if ((path = font->getGlyphPath(code))) { path->offset((SplashCoord)x, (SplashCoord)y); } } // don't use stroke adjustment when stroking text -- the results // tend to be ugly (because characters with horizontal upper or // lower edges get misaligned relative to the other characters) strokeAdjust = false; // make gcc happy if (doStroke) { strokeAdjust = splash->getStrokeAdjust(); splash->setStrokeAdjust(false); } // fill and stroke if (doFill && doStroke) { if (path) { setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); splash->fill(path, false); setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); splash->stroke(path); } // fill } else if (doFill) { setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font); // stroke } else if (doStroke) { if (path) { setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(), state->getOverprintMode(), state->getStrokeColor()); splash->stroke(path); } } splash->setLineWidth(lineWidth); // clip if (doClip) { if (path) { if (textClipPath) { textClipPath->append(path); } else { textClipPath = path; path = nullptr; } } } if (doStroke) { splash->setStrokeAdjust(strokeAdjust); } if (path) { delete path; } } bool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen) { GfxFont *gfxFont; const Ref *fontID; const double *ctm, *bbox; T3FontCache *t3Font; T3GlyphStack *t3gs; bool validBBox; double m[4]; bool horiz; double x1, y1, xMin, yMin, xMax, yMax, xt, yt; int i, j; // check for invisible text -- this is used by Acrobat Capture if (state->getRender() == 3) { // this is a bit of cheating, we say yes, font is already on cache // so we actually skip the rendering of it return true; } if (skipHorizText || skipRotatedText) { state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); horiz = m[0] > 0 && fabs(m[1]) < 0.001 && fabs(m[2]) < 0.001 && m[3] < 0; if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) { return true; } } if (!(gfxFont = state->getFont())) { return false; } fontID = gfxFont->getID(); ctm = state->getCTM(); state->transform(0, 0, &xt, &yt); // is it the first (MRU) font in the cache? if (!(nT3Fonts > 0 && t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) { // is the font elsewhere in the cache? for (i = 1; i < nT3Fonts; ++i) { if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) { t3Font = t3FontCache[i]; for (j = i; j > 0; --j) { t3FontCache[j] = t3FontCache[j - 1]; } t3FontCache[0] = t3Font; break; } } if (i >= nT3Fonts) { // create new entry in the font cache if (nT3Fonts == splashOutT3FontCacheSize) { t3gs = t3GlyphStack; while (t3gs != nullptr) { if (t3gs->cache == t3FontCache[nT3Fonts - 1]) { error(errSyntaxWarning, -1, "t3FontCache reaches limit but font still on stack in SplashOutputDev::beginType3Char"); return true; } t3gs = t3gs->next; } delete t3FontCache[nT3Fonts - 1]; --nT3Fonts; } for (j = nT3Fonts; j > 0; --j) { t3FontCache[j] = t3FontCache[j - 1]; } ++nT3Fonts; bbox = gfxFont->getFontBBox(); if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) { // unspecified bounding box -- just take a guess xMin = xt - 5; xMax = xMin + 30; yMax = yt + 15; yMin = yMax - 45; validBBox = false; } else { state->transform(bbox[0], bbox[1], &x1, &y1); xMin = xMax = x1; yMin = yMax = y1; state->transform(bbox[0], bbox[3], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(bbox[2], bbox[1], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(bbox[2], bbox[3], &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } validBBox = true; } t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3], (int)floor(xMin - xt) - 2, (int)floor(yMin - yt) - 2, (int)ceil(xMax) - (int)floor(xMin) + 4, (int)ceil(yMax) - (int)floor(yMin) + 4, validBBox, colorMode != splashModeMono1); } } t3Font = t3FontCache[0]; // is the glyph in the cache? i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; for (j = 0; j < t3Font->cacheAssoc; ++j) { if (t3Font->cacheTags != nullptr) { if ((t3Font->cacheTags[i+j].mru & 0x8000) && t3Font->cacheTags[i+j].code == code) { drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j], t3Font->cacheData + (i+j) * t3Font->glyphSize); return true; } } } // push a new Type 3 glyph record t3gs = new T3GlyphStack(); t3gs->next = t3GlyphStack; t3GlyphStack = t3gs; t3GlyphStack->code = code; t3GlyphStack->cache = t3Font; t3GlyphStack->cacheTag = nullptr; t3GlyphStack->cacheData = nullptr; t3GlyphStack->haveDx = false; t3GlyphStack->doNotCache = false; return false; } void SplashOutputDev::endType3Char(GfxState *state) { T3GlyphStack *t3gs; if (t3GlyphStack->cacheTag) { --nestCount; memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(), t3GlyphStack->cache->glyphSize); delete bitmap; delete splash; bitmap = t3GlyphStack->origBitmap; splash = t3GlyphStack->origSplash; const double *ctm = state->getCTM(); state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], t3GlyphStack->origCTM4, t3GlyphStack->origCTM5); updateCTM(state, 0, 0, 0, 0, 0, 0); drawType3Glyph(state, t3GlyphStack->cache, t3GlyphStack->cacheTag, t3GlyphStack->cacheData); } t3gs = t3GlyphStack; t3GlyphStack = t3gs->next; delete t3gs; } void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) { if (likely(t3GlyphStack != nullptr)) { t3GlyphStack->haveDx = true; } else { error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D0"); } } void SplashOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) { T3FontCache *t3Font; SplashColor color; double xt, yt, xMin, xMax, yMin, yMax, x1, y1; int i, j; // ignore multiple d0/d1 operators if (!t3GlyphStack || t3GlyphStack->haveDx) { return; } t3GlyphStack->haveDx = true; // don't cache if we got a gsave/grestore before the d1 if (t3GlyphStack->doNotCache) { return; } if (unlikely(t3GlyphStack == nullptr)) { error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1"); return; } if (unlikely(t3GlyphStack->origBitmap != nullptr)) { error(errSyntaxWarning, -1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1"); return; } if (unlikely(t3GlyphStack->origSplash != nullptr)) { error(errSyntaxWarning, -1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1"); return; } t3Font = t3GlyphStack->cache; // check for a valid bbox state->transform(0, 0, &xt, &yt); state->transform(llx, lly, &x1, &y1); xMin = xMax = x1; yMin = yMax = y1; state->transform(llx, ury, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(urx, lly, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } state->transform(urx, ury, &x1, &y1); if (x1 < xMin) { xMin = x1; } else if (x1 > xMax) { xMax = x1; } if (y1 < yMin) { yMin = y1; } else if (y1 > yMax) { yMax = y1; } if (xMin - xt < t3Font->glyphX || yMin - yt < t3Font->glyphY || xMax - xt > t3Font->glyphX + t3Font->glyphW || yMax - yt > t3Font->glyphY + t3Font->glyphH) { if (t3Font->validBBox) { error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph"); } return; } if (t3Font->cacheTags == nullptr) return; // allocate a cache entry i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc; for (j = 0; j < t3Font->cacheAssoc; ++j) { if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) { t3Font->cacheTags[i+j].mru = 0x8000; t3Font->cacheTags[i+j].code = t3GlyphStack->code; t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j]; t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize; } else { ++t3Font->cacheTags[i+j].mru; } } // save state t3GlyphStack->origBitmap = bitmap; t3GlyphStack->origSplash = splash; const double *ctm = state->getCTM(); t3GlyphStack->origCTM4 = ctm[4]; t3GlyphStack->origCTM5 = ctm[5]; // create the temporary bitmap if (colorMode == splashModeMono1) { bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono1, false); splash = new Splash(bitmap, false, t3GlyphStack->origSplash->getScreen()); color[0] = 0; splash->clear(color); color[0] = 0xff; } else { bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1, splashModeMono8, false); splash = new Splash(bitmap, vectorAntialias, t3GlyphStack->origSplash->getScreen()); color[0] = 0x00; splash->clear(color); color[0] = 0xff; } splash->setMinLineWidth(s_minLineWidth); splash->setThinLineMode(splashThinLineDefault); splash->setFillPattern(new SplashSolidColor(color)); splash->setStrokePattern(new SplashSolidColor(color)); //~ this should copy other state from t3GlyphStack->origSplash? state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3], -t3Font->glyphX, -t3Font->glyphY); updateCTM(state, 0, 0, 0, 0, 0, 0); ++nestCount; } void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font, T3FontCacheTag * /*tag*/, unsigned char *data) { SplashGlyphBitmap glyph; setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); glyph.x = -t3Font->glyphX; glyph.y = -t3Font->glyphY; glyph.w = t3Font->glyphW; glyph.h = t3Font->glyphH; glyph.aa = colorMode != splashModeMono1; glyph.data = data; glyph.freeData = false; splash->fillGlyph(0, 0, &glyph); } void SplashOutputDev::beginTextObject(GfxState *state) { } void SplashOutputDev::endTextObject(GfxState *state) { if (textClipPath) { splash->clipToPath(textClipPath, false); delete textClipPath; textClipPath = nullptr; } } struct SplashOutImageMaskData { ImageStream *imgStr; bool invert; int width, height, y; }; bool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) { SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data; unsigned char *p; SplashColorPtr q; int x; if (imgMaskData->y == imgMaskData->height) { return false; } if (!(p = imgMaskData->imgStr->getLine())) { return false; } for (x = 0, q = line; x < imgMaskData->width; ++x) { *q++ = *p++ ^ imgMaskData->invert; } ++imgMaskData->y; return true; } void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) { SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; if (state->getFillColorSpace()->isNonMarking()) { return; } setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(), state->getOverprintMode(), state->getFillColor()); const double *ctm = state->getCTM(); for (int i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) return; } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; imgMaskData.imgStr = new ImageStream(str, width, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = invert ? 0 : 1; imgMaskData.width = width; imgMaskData.height = height; imgMaskData.y = 0; splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); if (inlineImg) { while (imgMaskData.y < height) { imgMaskData.imgStr->getLine(); ++imgMaskData.y; } } delete imgMaskData.imgStr; str->close(); } void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) { const double *ctm; SplashCoord mat[6]; SplashOutImageMaskData imgMaskData; Splash *maskSplash; SplashColor maskColor; double bbox[4] = {0, 0, 1, 1}; // default; if (state->getFillColorSpace()->isNonMarking()) { return; } ctm = state->getCTM(); for (int i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) return; } beginTransparencyGroup(state, bbox, nullptr, false, false, false); baseMatrix[4] -= transpGroupStack->tx; baseMatrix[5] -= transpGroupStack->ty; ctm = state->getCTM(); mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; imgMaskData.imgStr = new ImageStream(str, width, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = invert ? 0 : 1; imgMaskData.width = width; imgMaskData.height = height; imgMaskData.y = 0; transpGroupStack->softmask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); maskSplash = new Splash(transpGroupStack->softmask, vectorAntialias); maskColor[0] = 0; maskSplash->clear(maskColor); maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); delete maskSplash; delete imgMaskData.imgStr; str->close(); } void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { double bbox[4] = {0,0,1,1}; // dummy /* transfer mask to alpha channel! */ // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight()); // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight()); if (transpGroupStack->softmask != nullptr) { unsigned char *dest = bitmap->getAlphaPtr(); unsigned char *src = transpGroupStack->softmask->getDataPtr(); for (int c= 0; c < transpGroupStack->softmask->getRowSize() * transpGroupStack->softmask->getHeight(); c++) { dest[c] = src[c]; } delete transpGroupStack->softmask; transpGroupStack->softmask = nullptr; } endTransparencyGroup(state); baseMatrix[4] += transpGroupStack->tx; baseMatrix[5] += transpGroupStack->ty; paintTransparencyGroup(state, bbox); } struct SplashOutImageData { ImageStream *imgStr; GfxImageColorMap *colorMap; SplashColorPtr lookup; int *maskColors; SplashColorMode colorMode; int width, height, y; ImageStream *maskStr; GfxImageColorMap *maskColorMap; SplashColor matteColor; }; #ifdef USE_CMS bool SplashOutputDev::useIccImageSrc(void *data) { SplashOutImageData *imgData = (SplashOutImageData *)data; if (!imgData->lookup && imgData->colorMap->getColorSpace()->getMode() == csICCBased) { GfxICCBasedColorSpace *colorSpace = (GfxICCBasedColorSpace *) imgData->colorMap->getColorSpace(); switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceGray) return true; break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceRGB) return true; break; #ifdef SPLASH_CMYK case splashModeCMYK8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceCMYK) return true; break; case splashModeDeviceN8: if (colorSpace->getAlt() != nullptr && colorSpace->getAlt()->getMode() == csDeviceN) return true; break; #endif } } return false; } #endif // Clip x to lie in [0, 255]. static inline unsigned char clip255(int x) { return x < 0 ? 0 : x > 255 ? 255 : x; } bool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, unsigned char * /*alphaLine*/) { SplashOutImageData *imgData = (SplashOutImageData *)data; unsigned char *p; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif int nComps, x; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { int destComps = 1; if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) destComps = 3; else if (imgData->colorMode == splashModeXBGR8) destComps = 4; #ifdef SPLASH_CMYK else if (imgData->colorMode == splashModeCMYK8) destComps = 4; else if (imgData->colorMode == splashModeDeviceN8) destComps = SPOT_NCOMPS + 4; #endif memset(colorLine, 0, imgData->width * destComps); return false; } nComps = imgData->colorMap->getNumPixelComps(); if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { *q++ = imgData->lookup[*p]; } break; case splashModeRGB8: case splashModeBGR8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; } break; case splashModeXBGR8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; } break; #ifdef SPLASH_CMYK case splashModeCMYK8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; } break; case splashModeDeviceN8: for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) { col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) *q++ = col[cp]; } break; #endif } } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: if (imgData->colorMap->useRGBLine()) { imgData->colorMap->getRGBLine(p, (unsigned char *) colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); } } break; case splashModeXBGR8: if (imgData->colorMap->useRGBLine()) { imgData->colorMap->getRGBXLine(p, (unsigned char *) colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); *q++ = 255; } } break; #ifdef SPLASH_CMYK case splashModeCMYK8: if (imgData->colorMap->useCMYKLine()) { imgData->colorMap->getCMYKLine(p, (unsigned char *) colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); } } break; case splashModeDeviceN8: if (imgData->colorMap->useDeviceNLine()) { imgData->colorMap->getDeviceNLine(p, (unsigned char *) colorLine, imgData->width); } else { for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) { imgData->colorMap->getDeviceN(p, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) *q++ = colToByte(deviceN.c[cp]); } } break; #endif } } if (imgData->maskStr != nullptr && (p = imgData->maskStr->getLine()) != nullptr) { int destComps = splashColorModeNComps[imgData->colorMode]; int convComps = (imgData->colorMode == splashModeXBGR8) ? 3 : destComps; imgData->maskColorMap->getGrayLine(p, p, imgData->width); for (x = 0, q = colorLine; x < imgData->width; ++x, p++, q += destComps) { for (int cp = 0; cp < convComps; cp++) { q[cp] = (*p) ? clip255(imgData->matteColor[cp] + (int) (q[cp] - imgData->matteColor[cp]) * 255 / *p) : imgData->matteColor[cp]; } } } ++imgData->y; return true; } #ifdef USE_CMS bool SplashOutputDev::iccImageSrc(void *data, SplashColorPtr colorLine, unsigned char * /*alphaLine*/) { SplashOutImageData *imgData = (SplashOutImageData *)data; unsigned char *p; int nComps; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { int destComps = 1; if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8) destComps = 3; else if (imgData->colorMode == splashModeXBGR8) destComps = 4; #ifdef SPLASH_CMYK else if (imgData->colorMode == splashModeCMYK8) destComps = 4; else if (imgData->colorMode == splashModeDeviceN8) destComps = SPOT_NCOMPS + 4; #endif memset(colorLine, 0, imgData->width * destComps); return false; } if (imgData->colorMode == splashModeXBGR8) { SplashColorPtr q; int x; for (x = 0, q = colorLine; x < imgData->width; ++x) { *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = 255; } } else { nComps = imgData->colorMap->getNumPixelComps(); memcpy(colorLine, p, imgData->width * nComps); } ++imgData->y; return true; } void SplashOutputDev::iccTransform(void *data, SplashBitmap *bitmap) { SplashOutImageData *imgData = (SplashOutImageData *)data; int nComps = imgData->colorMap->getNumPixelComps(); unsigned char *colorLine = (unsigned char *) gmalloc(nComps * bitmap->getWidth()); unsigned char *rgbxLine = (imgData->colorMode == splashModeXBGR8) ? (unsigned char *) gmalloc(3 * bitmap->getWidth()) : nullptr; for (int i = 0; i < bitmap->getHeight(); i++) { unsigned char *p = bitmap->getDataPtr() + i * bitmap->getRowSize(); switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGrayLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGBLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; #ifdef SPLASH_CMYK case splashModeCMYK8: imgData->colorMap->getCMYKLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; case splashModeDeviceN8: imgData->colorMap->getDeviceNLine(p, colorLine, bitmap->getWidth()); memcpy(p, colorLine, nComps * bitmap->getWidth()); break; #endif case splashModeXBGR8: unsigned char *q; unsigned char *b = p; int x; for (x = 0, q = rgbxLine; x < bitmap->getWidth(); ++x, b+=4) { *q++ = b[2]; *q++ = b[1]; *q++ = b[0]; } imgData->colorMap->getRGBLine(rgbxLine, colorLine, bitmap->getWidth()); b = p; for (x = 0, q = colorLine; x < bitmap->getWidth(); ++x, b+=4) { b[2] = *q++; b[1] = *q++; b[0] = *q++; } break; } } gfree(colorLine); if (rgbxLine != nullptr) gfree(rgbxLine); } #endif bool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine) { SplashOutImageData *imgData = (SplashOutImageData *)data; unsigned char *p, *aq; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif unsigned char alpha; int nComps, x, i; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { return false; } nComps = imgData->colorMap->getNumPixelComps(); for (x = 0, q = colorLine, aq = alphaLine; x < imgData->width; ++x, p += nComps) { alpha = 0; for (i = 0; i < nComps; ++i) { if (p[i] < imgData->maskColors[2*i] || p[i] > imgData->maskColors[2*i+1]) { alpha = 0xff; break; } } if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: *q++ = imgData->lookup[*p]; break; case splashModeRGB8: case splashModeBGR8: col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; break; case splashModeXBGR8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = 255; break; #ifdef SPLASH_CMYK case splashModeCMYK8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; break; case splashModeDeviceN8: col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) *q++ = col[cp]; break; #endif } *aq++ = alpha; } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); if (imgData->colorMode == splashModeXBGR8) *q++ = 255; break; #ifdef SPLASH_CMYK case splashModeCMYK8: imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); break; case splashModeDeviceN8: imgData->colorMap->getDeviceN(p, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) *q++ = colToByte(deviceN.c[cp]); break; #endif } *aq++ = alpha; } } ++imgData->y; return true; } struct TilingSplashOutBitmap { SplashBitmap *bitmap; SplashPattern *pattern; SplashColorMode colorMode; int paintType; int repeatX; int repeatY; int y; }; bool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine) { TilingSplashOutBitmap *imgData = (TilingSplashOutBitmap *)data; if (imgData->y == imgData->bitmap->getHeight()) { imgData->repeatY--; if (imgData->repeatY == 0) return false; imgData->y = 0; } if (imgData->paintType == 1) { const SplashColorMode cMode = imgData->bitmap->getMode(); SplashColorPtr q = colorLine; // For splashModeBGR8 and splashModeXBGR8 we need to use getPixel // for the others we can use raw access if (cMode == splashModeBGR8 || cMode == splashModeXBGR8) { for (int m = 0; m < imgData->repeatX; m++) { for (int x = 0; x < imgData->bitmap->getWidth(); x++) { imgData->bitmap->getPixel(x, imgData->y, q); q += splashColorModeNComps[cMode]; } } } else { const int n = imgData->bitmap->getRowSize(); SplashColorPtr p; for (int m = 0; m < imgData->repeatX; m++) { p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize(); for (int x = 0; x < n; ++x) { *q++ = *p++; } } } if (alphaLine != nullptr) { SplashColorPtr aq = alphaLine; SplashColorPtr p; const int n = imgData->bitmap->getWidth() - 1; for (int m = 0; m < imgData->repeatX; m++) { p = imgData->bitmap->getAlphaPtr() + imgData->y * imgData->bitmap->getWidth(); for (int x = 0; x < n; ++x) { *aq++ = *p++; } // This is a hack, because of how Splash antialias works if we overwrite the // last alpha pixel of the tile most/all of the files look much better *aq++ = (n == 0) ? *p : *(p - 1); } } } else { SplashColor col, pat; SplashColorPtr dest = colorLine; for (int m = 0; m < imgData->repeatX; m++) { for (int x = 0; x < imgData->bitmap->getWidth(); x++) { imgData->bitmap->getPixel(x, imgData->y, col); imgData->pattern->getColor(x, imgData->y, pat); for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) { #ifdef SPLASH_CMYK if (imgData->colorMode == splashModeCMYK8 || imgData->colorMode == splashModeDeviceN8) dest[i] = div255(pat[i] * (255 - col[0])); else #endif dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0])); } dest += splashColorModeNComps[imgData->colorMode]; } } if (alphaLine != nullptr) { const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y; SplashColorPtr aq = alphaLine; SplashColorPtr p; const int n = imgData->bitmap->getWidth(); for (int m = 0; m < imgData->repeatX; m++) { p = imgData->bitmap->getAlphaPtr() + y * imgData->bitmap->getWidth(); for (int x = 0; x < n; ++x) { *aq++ = *p++; } } } } ++imgData->y; return true; } void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, int *maskColors, bool inlineImg) { SplashCoord mat[6]; SplashOutImageData imgData; SplashColorMode srcMode; SplashImageSource src; SplashICCTransform tf; GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; bool grayIndexed = false; GfxColor deviceN; #endif unsigned char pix; int n, i; const double *ctm = state->getCTM(); for (i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) return; } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgData.imgStr->reset(); imgData.colorMap = colorMap; imgData.maskColors = maskColors; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.maskStr = nullptr; imgData.maskColorMap = nullptr; imgData.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = nullptr; if (colorMap->getNumPixelComps() == 1) { n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 3); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3*i] = colToByte(rgb.r); imgData.lookup[3*i+1] = colToByte(rgb.g); imgData.lookup[3*i+2] = colToByte(rgb.b); } break; case splashModeXBGR8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); if (likely(imgData.lookup != nullptr)) { for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[4*i] = colToByte(rgb.r); imgData.lookup[4*i+1] = colToByte(rgb.g); imgData.lookup[4*i+2] = colToByte(rgb.b); imgData.lookup[4*i+3] = 255; } } break; #ifdef SPLASH_CMYK case splashModeCMYK8: grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { grayIndexed = false; } imgData.lookup[4*i] = colToByte(cmyk.c); imgData.lookup[4*i+1] = colToByte(cmyk.m); imgData.lookup[4*i+2] = colToByte(cmyk.y); imgData.lookup[4*i+3] = colToByte(cmyk.k); } break; case splashModeDeviceN8: colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray; imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) { grayIndexed = false; } colorMap->getDeviceN(&pix, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) imgData.lookup[(SPOT_NCOMPS+4)*i +cp] = colToByte(deviceN.c[cp]); } break; #endif } } #ifdef SPLASH_CMYK setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr, grayIndexed); #else setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); #endif if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else { srcMode = colorMode; } #ifdef USE_CMS src = maskColors ? &alphaImageSrc : useIccImageSrc(&imgData) ? &iccImageSrc : &imageSrc; tf = maskColors == nullptr && useIccImageSrc(&imgData) ? &iccTransform : nullptr; #else src = maskColors ? &alphaImageSrc : &imageSrc; tf = nullptr; #endif splash->drawImage(src, tf, &imgData, srcMode, maskColors ? true : false, width, height, mat, interpolate); if (inlineImg) { while (imgData.y < height) { imgData.imgStr->getLine(); ++imgData.y; } } gfree(imgData.lookup); delete imgData.imgStr; str->close(); } struct SplashOutMaskedImageData { ImageStream *imgStr; GfxImageColorMap *colorMap; SplashBitmap *mask; SplashColorPtr lookup; SplashColorMode colorMode; int width, height, y; }; bool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine) { SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data; unsigned char *p, *aq; SplashColorPtr q, col; GfxRGB rgb; GfxGray gray; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif unsigned char alpha; unsigned char *maskPtr; int maskBit; int nComps, x; if (imgData->y == imgData->height) { return false; } if (!(p = imgData->imgStr->getLine())) { return false; } nComps = imgData->colorMap->getNumPixelComps(); maskPtr = imgData->mask->getDataPtr() + imgData->y * imgData->mask->getRowSize(); maskBit = 0x80; for (x = 0, q = colorLine, aq = alphaLine; x < imgData->width; ++x, p += nComps) { alpha = (*maskPtr & maskBit) ? 0xff : 0x00; if (!(maskBit >>= 1)) { ++maskPtr; maskBit = 0x80; } if (imgData->lookup) { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: *q++ = imgData->lookup[*p]; break; case splashModeRGB8: case splashModeBGR8: col = &imgData->lookup[3 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; break; case splashModeXBGR8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = 255; break; #ifdef SPLASH_CMYK case splashModeCMYK8: col = &imgData->lookup[4 * *p]; *q++ = col[0]; *q++ = col[1]; *q++ = col[2]; *q++ = col[3]; break; case splashModeDeviceN8: col = &imgData->lookup[(SPOT_NCOMPS+4) * *p]; for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) *q++ = col[cp]; break; #endif } *aq++ = alpha; } else { switch (imgData->colorMode) { case splashModeMono1: case splashModeMono8: imgData->colorMap->getGray(p, &gray); *q++ = colToByte(gray); break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: imgData->colorMap->getRGB(p, &rgb); *q++ = colToByte(rgb.r); *q++ = colToByte(rgb.g); *q++ = colToByte(rgb.b); if (imgData->colorMode == splashModeXBGR8) *q++ = 255; break; #ifdef SPLASH_CMYK case splashModeCMYK8: imgData->colorMap->getCMYK(p, &cmyk); *q++ = colToByte(cmyk.c); *q++ = colToByte(cmyk.m); *q++ = colToByte(cmyk.y); *q++ = colToByte(cmyk.k); break; case splashModeDeviceN8: imgData->colorMap->getDeviceN(p, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) *q++ = colToByte(deviceN.c[cp]); break; #endif } *aq++ = alpha; } } ++imgData->y; return true; } void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) { GfxImageColorMap *maskColorMap; SplashCoord mat[6]; SplashOutMaskedImageData imgData; SplashOutImageMaskData imgMaskData; SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; SplashColor maskColor; GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif unsigned char pix; int n, i; #ifdef SPLASH_CMYK colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); #endif setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); // If the mask is higher resolution than the image, use // drawSoftMaskedImage() instead. if (maskWidth > width || maskHeight > height) { Object maskDecode(new Array((xref) ? xref : doc->getXRef())); maskDecode.arrayAdd(Object(maskInvert ? 0 : 1)); maskDecode.arrayAdd(Object(maskInvert ? 1 : 0)); maskColorMap = new GfxImageColorMap(1, &maskDecode, new GfxDeviceGrayColorSpace()); drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); delete maskColorMap; } else { //----- scale the mask image to the same size as the source image mat[0] = (SplashCoord)width; mat[1] = 0; mat[2] = 0; mat[3] = (SplashCoord)height; mat[4] = 0; mat[5] = 0; imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1); imgMaskData.imgStr->reset(); imgMaskData.invert = maskInvert ? 0 : 1; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); if (!maskBitmap->getDataPtr()) { delete maskBitmap; width = height = 1; maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); } maskSplash = new Splash(maskBitmap, false); maskColor[0] = 0; maskSplash->clear(maskColor); maskColor[0] = 0xff; maskSplash->setFillPattern(new SplashSolidColor(maskColor)); maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, maskWidth, maskHeight, mat, false); delete imgMaskData.imgStr; maskStr->close(); delete maskSplash; //----- draw the source image const double *ctm = state->getCTM(); for (i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { delete maskBitmap; return; } } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgData.imgStr->reset(); imgData.colorMap = colorMap; imgData.mask = maskBitmap; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = nullptr; if (colorMap->getNumPixelComps() == 1) { n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 3); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3*i] = colToByte(rgb.r); imgData.lookup[3*i+1] = colToByte(rgb.g); imgData.lookup[3*i+2] = colToByte(rgb.b); } break; case splashModeXBGR8: imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[4*i] = colToByte(rgb.r); imgData.lookup[4*i+1] = colToByte(rgb.g); imgData.lookup[4*i+2] = colToByte(rgb.b); imgData.lookup[4*i+3] = 255; } break; #ifdef SPLASH_CMYK case splashModeCMYK8: imgData.lookup = (SplashColorPtr)gmallocn(n, 4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); imgData.lookup[4*i] = colToByte(cmyk.c); imgData.lookup[4*i+1] = colToByte(cmyk.m); imgData.lookup[4*i+2] = colToByte(cmyk.y); imgData.lookup[4*i+3] = colToByte(cmyk.k); } break; case splashModeDeviceN8: imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4); for (i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getDeviceN(&pix, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]); } break; #endif } } if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else { srcMode = colorMode; } splash->drawImage(&maskedImageSrc, nullptr, &imgData, srcMode, true, width, height, mat, interpolate); delete maskBitmap; gfree(imgData.lookup); delete imgData.imgStr; str->close(); } } void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object * /* ref */, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, bool maskInterpolate) { SplashCoord mat[6]; SplashOutImageData imgData; SplashOutImageData imgMaskData; SplashColorMode srcMode; SplashBitmap *maskBitmap; Splash *maskSplash; SplashColor maskColor; GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif unsigned char pix; #ifdef SPLASH_CMYK colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); #endif setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); const double *ctm = state->getCTM(); for (int i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) return; } mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; //----- set up the soft mask if (maskColorMap->getMatteColor() != nullptr) { int maskChars; if (checkedMultiply(maskWidth, maskHeight, &maskChars)) { return; } unsigned char *data = (unsigned char *) gmalloc(maskChars); maskStr->reset(); const int readChars = maskStr->doGetChars(maskChars, data); if (unlikely(readChars < maskChars)) { memset(&data[readChars], 0, maskChars - readChars); } maskStr->close(); maskStr = new AutoFreeMemStream((char *)data, 0, maskChars, maskStr->getDictObject()->copy()); } imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); imgMaskData.imgStr->reset(); imgMaskData.colorMap = maskColorMap; imgMaskData.maskColors = nullptr; imgMaskData.colorMode = splashModeMono8; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; imgMaskData.maskStr = nullptr; imgMaskData.maskColorMap = nullptr; const unsigned imgMaskDataLookupSize = 1 << maskColorMap->getBits(); imgMaskData.lookup = (SplashColorPtr)gmalloc(imgMaskDataLookupSize); for (unsigned i = 0; i < imgMaskDataLookupSize; ++i) { pix = (unsigned char)i; maskColorMap->getGray(&pix, &gray); imgMaskData.lookup[i] = colToByte(gray); } maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); maskSplash = new Splash(maskBitmap, vectorAntialias); maskColor[0] = 0; maskSplash->clear(maskColor); maskSplash->drawImage(&imageSrc, nullptr, &imgMaskData, splashModeMono8, false, maskWidth, maskHeight, mat, maskInterpolate); delete imgMaskData.imgStr; if (maskColorMap->getMatteColor() == nullptr) { maskStr->close(); } gfree(imgMaskData.lookup); delete maskSplash; splash->setSoftMask(maskBitmap); //----- draw the source image imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgData.imgStr->reset(); imgData.colorMap = colorMap; imgData.maskColors = nullptr; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; imgData.maskStr = nullptr; imgData.maskColorMap = nullptr; if (maskColorMap->getMatteColor() != nullptr) { getMatteColor(colorMode, colorMap, maskColorMap->getMatteColor(), imgData.matteColor); imgData.maskColorMap = maskColorMap; imgData.maskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); imgData.maskStr->reset(); } imgData.y = 0; // special case for one-channel (monochrome/gray/separation) images: // build a lookup table here imgData.lookup = nullptr; if (colorMap->getNumPixelComps() == 1) { const unsigned n = 1 << colorMap->getBits(); switch (colorMode) { case splashModeMono1: case splashModeMono8: imgData.lookup = (SplashColorPtr)gmalloc(n); for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getGray(&pix, &gray); imgData.lookup[i] = colToByte(gray); } break; case splashModeRGB8: case splashModeBGR8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 3); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[3*i] = colToByte(rgb.r); imgData.lookup[3*i+1] = colToByte(rgb.g); imgData.lookup[3*i+2] = colToByte(rgb.b); } } break; case splashModeXBGR8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getRGB(&pix, &rgb); imgData.lookup[4*i] = colToByte(rgb.r); imgData.lookup[4*i+1] = colToByte(rgb.g); imgData.lookup[4*i+2] = colToByte(rgb.b); imgData.lookup[4*i+3] = 255; } } break; #ifdef SPLASH_CMYK case splashModeCMYK8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, 4); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getCMYK(&pix, &cmyk); imgData.lookup[4*i] = colToByte(cmyk.c); imgData.lookup[4*i+1] = colToByte(cmyk.m); imgData.lookup[4*i+2] = colToByte(cmyk.y); imgData.lookup[4*i+3] = colToByte(cmyk.k); } } break; case splashModeDeviceN8: imgData.lookup = (SplashColorPtr)gmallocn_checkoverflow(n, SPOT_NCOMPS+4); if (likely(imgData.lookup != nullptr)) { for (unsigned i = 0; i < n; ++i) { pix = (unsigned char)i; colorMap->getDeviceN(&pix, &deviceN); for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]); } } break; #endif } } if (colorMode == splashModeMono1) { srcMode = splashModeMono8; } else { srcMode = colorMode; } splash->drawImage(&imageSrc, nullptr, &imgData, srcMode, false, width, height, mat, interpolate); splash->setSoftMask(nullptr); gfree(imgData.lookup); delete imgData.maskStr; delete imgData.imgStr; if (maskColorMap->getMatteColor() != nullptr) { maskStr->close(); delete maskStr; } str->close(); } bool SplashOutputDev::checkTransparencyGroup(GfxState *state, bool knockout) { if (state->getFillOpacity() != 1 || state->getStrokeOpacity() != 1 || state->getAlphaIsShape() || state->getBlendMode() != gfxBlendNormal || splash->getSoftMask() != nullptr || knockout) return true; return transpGroupStack != nullptr && transpGroupStack->shape != nullptr; } void SplashOutputDev::beginTransparencyGroup(GfxState *state, const double *bbox, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, bool forSoftMask) { SplashTransparencyGroup *transpGroup; SplashColor color; double xMin, yMin, xMax, yMax, x, y; int tx, ty, w, h, i; // transform the bbox state->transform(bbox[0], bbox[1], &x, &y); xMin = xMax = x; yMin = yMax = y; state->transform(bbox[0], bbox[3], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } state->transform(bbox[2], bbox[1], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } state->transform(bbox[2], bbox[3], &x, &y); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } tx = (int)floor(xMin); if (tx < 0) { tx = 0; } else if (tx >= bitmap->getWidth()) { tx = bitmap->getWidth() - 1; } ty = (int)floor(yMin); if (ty < 0) { ty = 0; } else if (ty >= bitmap->getHeight()) { ty = bitmap->getHeight() - 1; } w = (int)ceil(xMax) - tx + 1; if (tx + w > bitmap->getWidth()) { w = bitmap->getWidth() - tx; } if (w < 1) { w = 1; } h = (int)ceil(yMax) - ty + 1; if (ty + h > bitmap->getHeight()) { h = bitmap->getHeight() - ty; } if (h < 1) { h = 1; } // push a new stack entry transpGroup = new SplashTransparencyGroup(); transpGroup->softmask = nullptr; transpGroup->tx = tx; transpGroup->ty = ty; transpGroup->blendingColorSpace = blendingColorSpace; transpGroup->isolated = isolated; transpGroup->shape = (knockout && !isolated) ? SplashBitmap::copy(bitmap) : nullptr; transpGroup->knockout = (knockout && isolated); transpGroup->knockoutOpacity = 1.0; transpGroup->next = transpGroupStack; transpGroupStack = transpGroup; // save state transpGroup->origBitmap = bitmap; transpGroup->origSplash = splash; transpGroup->fontAA = fontEngine->getAA(); //~ this handles the blendingColorSpace arg for soft masks, but //~ not yet for transparency groups // switch to the blending color space if (forSoftMask && isolated && blendingColorSpace) { if (blendingColorSpace->getMode() == csDeviceGray || blendingColorSpace->getMode() == csCalGray || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 1)) { colorMode = splashModeMono8; } else if (blendingColorSpace->getMode() == csDeviceRGB || blendingColorSpace->getMode() == csCalRGB || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 3)) { //~ does this need to use BGR8? colorMode = splashModeRGB8; #ifdef SPLASH_CMYK } else if (blendingColorSpace->getMode() == csDeviceCMYK || (blendingColorSpace->getMode() == csICCBased && blendingColorSpace->getNComps() == 4)) { colorMode = splashModeCMYK8; #endif } } // create the temporary bitmap bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, true, bitmapTopDown, bitmap->getSeparationList()); if (!bitmap->getDataPtr()) { delete bitmap; w = h = 1; bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, true, bitmapTopDown); } splash = new Splash(bitmap, vectorAntialias, transpGroup->origSplash->getScreen()); if (transpGroup->next != nullptr && transpGroup->next->knockout) { fontEngine->setAA(false); } splash->setThinLineMode(transpGroup->origSplash->getThinLineMode()); splash->setMinLineWidth(s_minLineWidth); //~ Acrobat apparently copies at least the fill and stroke colors, and //~ maybe other state(?) -- but not the clipping path (and not sure //~ what else) //~ [this is likely the same situation as in type3D1()] splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy()); splash->setStrokePattern( transpGroup->origSplash->getStrokePattern()->copy()); if (isolated) { for (i = 0; i < splashMaxColorComps; ++i) { color[i] = 0; } if (colorMode == splashModeXBGR8) color[3] = 255; splash->clear(color, 0); } else { SplashBitmap *shape = (knockout) ? transpGroup->shape : (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->shape : transpGroup->origBitmap; int shapeTx = (knockout) ? tx : (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->tx + tx : tx; int shapeTy = (knockout) ? ty : (transpGroup->next != nullptr && transpGroup->next->shape != nullptr) ? transpGroup->next->ty + ty : ty; splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h); splash->setInNonIsolatedGroup(shape, shapeTx, shapeTy); } transpGroup->tBitmap = bitmap; state->shiftCTMAndClip(-tx, -ty); updateCTM(state, 0, 0, 0, 0, 0, 0); ++nestCount; } void SplashOutputDev::endTransparencyGroup(GfxState *state) { // restore state --nestCount; delete splash; bitmap = transpGroupStack->origBitmap; colorMode = bitmap->getMode(); splash = transpGroupStack->origSplash; state->shiftCTMAndClip(transpGroupStack->tx, transpGroupStack->ty); updateCTM(state, 0, 0, 0, 0, 0, 0); } void SplashOutputDev::paintTransparencyGroup(GfxState *state, const double *bbox) { SplashBitmap *tBitmap; SplashTransparencyGroup *transpGroup; bool isolated; int tx, ty; tx = transpGroupStack->tx; ty = transpGroupStack->ty; tBitmap = transpGroupStack->tBitmap; isolated = transpGroupStack->isolated; // paint the transparency group onto the parent bitmap // - the clip path was set in the parent's state) if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) { SplashCoord knockoutOpacity = (transpGroupStack->next != nullptr) ? transpGroupStack->next->knockoutOpacity : transpGroupStack->knockoutOpacity; splash->setOverprintMask(0xffffffff, false); splash->composite(tBitmap, 0, 0, tx, ty, tBitmap->getWidth(), tBitmap->getHeight(), false, !isolated, transpGroupStack->next != nullptr && transpGroupStack->next->knockout, knockoutOpacity); fontEngine->setAA(transpGroupStack->fontAA); if (transpGroupStack->next != nullptr && transpGroupStack->next->shape != nullptr) { transpGroupStack->next->knockout = true; } } // pop the stack transpGroup = transpGroupStack; transpGroupStack = transpGroup->next; if (transpGroupStack != nullptr && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) { transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity; } delete transpGroup->shape; delete transpGroup; delete tBitmap; } void SplashOutputDev::setSoftMask(GfxState *state, const double *bbox, bool alpha, Function *transferFunc, GfxColor *backdropColor) { SplashBitmap *softMask, *tBitmap; Splash *tSplash; SplashTransparencyGroup *transpGroup; SplashColor color; SplashColorPtr p; GfxGray gray; GfxRGB rgb; #ifdef SPLASH_CMYK GfxCMYK cmyk; GfxColor deviceN; #endif double lum, lum2; int tx, ty, x, y; tx = transpGroupStack->tx; ty = transpGroupStack->ty; tBitmap = transpGroupStack->tBitmap; // composite with backdrop color if (!alpha && tBitmap->getMode() != splashModeMono1) { //~ need to correctly handle the case where no blending color //~ space is given if (transpGroupStack->blendingColorSpace) { tSplash = new Splash(tBitmap, vectorAntialias, transpGroupStack->origSplash->getScreen()); switch (tBitmap->getMode()) { case splashModeMono1: // transparency is not supported in mono1 mode break; case splashModeMono8: transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); color[0] = colToByte(gray); tSplash->compositeBackground(color); break; case splashModeXBGR8: color[3] = 255; // fallthrough case splashModeRGB8: case splashModeBGR8: transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb); color[0] = colToByte(rgb.r); color[1] = colToByte(rgb.g); color[2] = colToByte(rgb.b); tSplash->compositeBackground(color); break; #ifdef SPLASH_CMYK case splashModeCMYK8: transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk); color[0] = colToByte(cmyk.c); color[1] = colToByte(cmyk.m); color[2] = colToByte(cmyk.y); color[3] = colToByte(cmyk.k); tSplash->compositeBackground(color); break; case splashModeDeviceN8: transpGroupStack->blendingColorSpace->getDeviceN(backdropColor, &deviceN); for (int cp=0; cp < SPOT_NCOMPS+4; cp++) color[cp] = colToByte(deviceN.c[cp]); tSplash->compositeBackground(color); break; #endif } delete tSplash; } } softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); unsigned char fill = 0; if (transpGroupStack->blendingColorSpace) { transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); fill = colToByte(gray); } memset(softMask->getDataPtr(), fill, softMask->getRowSize() * softMask->getHeight()); p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; int xMax = tBitmap->getWidth(); int yMax = tBitmap->getHeight(); if (xMax > bitmap->getWidth() - tx) xMax = bitmap->getWidth() - tx; if (yMax > bitmap->getHeight() - ty) yMax = bitmap->getHeight() - ty; for (y = 0; y < yMax; ++y) { for (x = 0; x < xMax; ++x) { if (alpha) { if (transferFunc) { lum = tBitmap->getAlpha(x, y) / 255.0; transferFunc->transform(&lum, &lum2); p[x] = (int)(lum2 * 255.0 + 0.5); } else p[x] = tBitmap->getAlpha(x, y); } else { tBitmap->getPixel(x, y, color); // convert to luminosity switch (tBitmap->getMode()) { case splashModeMono1: case splashModeMono8: lum = color[0] / 255.0; break; case splashModeXBGR8: case splashModeRGB8: case splashModeBGR8: lum = (0.3 / 255.0) * color[0] + (0.59 / 255.0) * color[1] + (0.11 / 255.0) * color[2]; break; #ifdef SPLASH_CMYK case splashModeCMYK8: case splashModeDeviceN8: lum = (1 - color[3] / 255.0) - (0.3 / 255.0) * color[0] - (0.59 / 255.0) * color[1] - (0.11 / 255.0) * color[2]; if (lum < 0) { lum = 0; } break; #endif } if (transferFunc) { transferFunc->transform(&lum, &lum2); } else { lum2 = lum; } p[x] = (int)(lum2 * 255.0 + 0.5); } } p += softMask->getRowSize(); } splash->setSoftMask(softMask); // pop the stack transpGroup = transpGroupStack; transpGroupStack = transpGroup->next; delete transpGroup; delete tBitmap; } void SplashOutputDev::clearSoftMask(GfxState *state) { splash->setSoftMask(nullptr); } void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) { splashColorCopy(paperColor, paperColorA); } int SplashOutputDev::getBitmapWidth() { return bitmap->getWidth(); } int SplashOutputDev::getBitmapHeight() { return bitmap->getHeight(); } SplashBitmap *SplashOutputDev::takeBitmap() { SplashBitmap *ret; ret = bitmap; bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, colorMode != splashModeMono1, bitmapTopDown); return ret; } void SplashOutputDev::getModRegion(int *xMin, int *yMin, int *xMax, int *yMax) { splash->getModRegion(xMin, yMin, xMax, yMax); } void SplashOutputDev::clearModRegion() { splash->clearModRegion(); } #if 1 //~tmp: turn off anti-aliasing temporarily bool SplashOutputDev::getVectorAntialias() { return splash->getVectorAntialias(); } void SplashOutputDev::setVectorAntialias(bool vaa) { vaa = vaa && colorMode != splashModeMono1; vectorAntialias = vaa; splash->setVectorAntialias(vaa); } #endif void SplashOutputDev::setFreeTypeHinting(bool enable, bool enableSlightHintingA) { enableFreeTypeHinting = enable; enableSlightHinting = enableSlightHintingA; } bool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, Object *str, const double *ptm, int paintType, int /*tilingType*/, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) { PDFRectangle box; Gfx *gfx; Splash *formerSplash = splash; SplashBitmap *formerBitmap = bitmap; double width, height; int surface_width, surface_height, result_width, result_height, i; int repeatX, repeatY; SplashCoord matc[6]; Matrix m1; const double *ctm; double savedCTM[6]; double kx, ky, sx, sy; bool retValue = false; width = bbox[2] - bbox[0]; height = bbox[3] - bbox[1]; if (xStep != width || yStep != height) return false; // calculate offsets ctm = state->getCTM(); for (i = 0; i < 6; ++i) { savedCTM[i] = ctm[i]; } state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]); ctm = state->getCTM(); for (i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } } matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4]; matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5]; if (splashAbs(ctm[1]) > splashAbs(ctm[0])) { kx = -ctm[1]; ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1]; } else { kx = ctm[0]; ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0]; } result_width = (int) ceil(fabs(kx * width * (x1 - x0))); result_height = (int) ceil(fabs(ky * height * (y1 - y0))); kx = state->getHDPI() / 72.0; ky = state->getVDPI() / 72.0; m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx; m1.m[1] = 0; m1.m[2] = 0; m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky; m1.m[4] = 0; m1.m[5] = 0; m1.transform(width, height, &kx, &ky); surface_width = (int) ceil (fabs(kx)); surface_height = (int) ceil (fabs(ky)); sx = (double) result_width / (surface_width * (x1 - x0)); sy = (double) result_height / (surface_height * (y1 - y0)); m1.m[0] *= sx; m1.m[3] *= sy; m1.transform(width, height, &kx, &ky); if(fabs(kx) < 1 && fabs(ky) < 1) { kx = std::min(kx, ky); ky = 2 / kx; m1.m[0] *= ky; m1.m[3] *= ky; m1.transform(width, height, &kx, &ky); surface_width = (int) ceil (fabs(kx)); surface_height = (int) ceil (fabs(ky)); repeatX = x1 - x0; repeatY = y1 - y0; } else { if ((unsigned long) surface_width * surface_height > 0x800000L) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } while(fabs(kx) > 16384 || fabs(ky) > 16384) { // limit pattern bitmap size m1.m[0] /= 2; m1.m[3] /= 2; m1.transform(width, height, &kx, &ky); } surface_width = (int) ceil (fabs(kx)); surface_height = (int) ceil (fabs(ky)); // adjust repeat values to completely fill region repeatX = result_width / surface_width; repeatY = result_height / surface_height; if (surface_width * repeatX < result_width) repeatX++; if (surface_height * repeatY < result_height) repeatY++; if (x1 - x0 > repeatX) repeatX = x1 - x0; if (y1 - y0 > repeatY) repeatY = y1 - y0; } // restore CTM and calculate rotate and scale with rounded matrix state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]); state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]); ctm = state->getCTM(); matc[0] = ctm[0]; matc[1] = ctm[1]; matc[2] = ctm[2]; matc[3] = ctm[3]; if (surface_width == 0 || surface_height == 0 || repeatX * repeatY <= 4) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } m1.transform(bbox[0], bbox[1], &kx, &ky); m1.m[4] = -kx; m1.m[5] = -ky; bitmap = new SplashBitmap(surface_width, surface_height, 1, (paintType == 1) ? colorMode : splashModeMono8, true); if (bitmap->getDataPtr() == nullptr) { SplashBitmap *tBitmap = bitmap; bitmap = formerBitmap; delete tBitmap; state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); return false; } splash = new Splash(bitmap, true); if (paintType == 2) { SplashColor clearColor; #ifdef SPLASH_CMYK clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF; #else clearColor[0] = 0xFF; #endif splash->clear(clearColor, 0); } else { splash->clear(paperColor, 0); } splash->setThinLineMode(formerSplash->getThinLineMode()); splash->setMinLineWidth(s_minLineWidth); box.x1 = bbox[0]; box.y1 = bbox[1]; box.x2 = bbox[2]; box.y2 = bbox[3]; gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA); // set pattern transformation matrix gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]); gfx->display(str); delete splash; splash = formerSplash; TilingSplashOutBitmap imgData; imgData.bitmap = bitmap; imgData.paintType = paintType; imgData.pattern = splash->getFillPattern(); imgData.colorMode = colorMode; imgData.y = 0; imgData.repeatX = repeatX; imgData.repeatY = repeatY; SplashBitmap *tBitmap = bitmap; bitmap = formerBitmap; result_width = tBitmap->getWidth() * imgData.repeatX; result_height = tBitmap->getHeight() * imgData.repeatY; if (splashAbs(matc[1]) > splashAbs(matc[0])) { kx = -matc[1]; ky = matc[2] - (matc[0] * matc[3]) / matc[1]; } else { kx = matc[0]; ky = matc[3] - (matc[1] * matc[2]) / matc[0]; } kx = result_width / (fabs(kx) + 1); ky = result_height / (fabs(ky) + 1); state->concatCTM(kx, 0, 0, ky, 0, 0); ctm = state->getCTM(); matc[0] = ctm[0]; matc[1] = ctm[1]; matc[2] = ctm[2]; matc[3] = ctm[3]; bool minorAxisZero = matc[1] == 0 && matc[2] == 0; if (matc[0] > 0 && minorAxisZero && matc[3] > 0) { // draw the tiles for (int y = 0; y < imgData.repeatY; ++y) { for (int x = 0; x < imgData.repeatX; ++x) { x0 = splashFloor(matc[4]) + x * tBitmap->getWidth(); y0 = splashFloor(matc[5]) + y * tBitmap->getHeight(); splash->blitImage(tBitmap, true, x0, y0); } } retValue = true; } else { retValue = splash->drawImage(&tilingBitmapSrc, nullptr, &imgData, colorMode, true, result_width, result_height, matc, false, true) == splashOk; } delete tBitmap; delete gfx; return retValue; } bool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) { GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode(); bool bDirectColorTranslation = false; // triggers an optimization. switch (colorMode) { case splashModeRGB8: bDirectColorTranslation = (shadingMode == csDeviceRGB); break; #ifdef SPLASH_CMYK case splashModeCMYK8: case splashModeDeviceN8: bDirectColorTranslation = (shadingMode == csDeviceCMYK); break; #endif default: break; } // restore vector antialias because we support it here if (shading->isParameterized()) { SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading); bool vaa = getVectorAntialias(); bool retVal = false; setVectorAntialias(true); retVal = splash->gouraudTriangleShadedFill(splashShading); setVectorAntialias(vaa); delete splashShading; return retVal; } return false; } bool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) { double xMin, yMin, xMax, yMax; bool vaa = getVectorAntialias(); // restore vector antialias because we support it here setVectorAntialias(true); bool retVal = false; // get the clip region bbox if (pattern->getShading()->getHasBBox()) { pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); } else { state->getClipBBox(&xMin, &yMin, &xMax, &yMax); xMin = floor (xMin); yMin = floor (yMin); xMax = ceil (xMax); yMax = ceil (yMax); { Matrix ctm, ictm; double x[4], y[4]; int i; state->getCTM(&ctm); ctm.invertTo(&ictm); ictm.transform(xMin, yMin, &x[0], &y[0]); ictm.transform(xMax, yMin, &x[1], &y[1]); ictm.transform(xMin, yMax, &x[2], &y[2]); ictm.transform(xMax, yMax, &x[3], &y[3]); xMin = xMax = x[0]; yMin = yMax = y[0]; for (i = 1; i < 4; i++) { xMin = std::min(xMin, x[i]); yMin = std::min(yMin, y[i]); xMax = std::max(xMax, x[i]); yMax = std::max(yMax, y[i]); } } } // fill the region state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); SplashPath path = convertPath(state, state->getPath(), true); #ifdef SPLASH_CMYK pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); #endif setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); retVal = (splash->shadedFill(&path, pattern->getShading()->getHasBBox(), pattern) == splashOk); state->clearPath(); setVectorAntialias(vaa); return retVal; } bool SplashOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { SplashFunctionPattern *pattern = new SplashFunctionPattern(colorMode, state, shading); double xMin, yMin, xMax, yMax; bool vaa = getVectorAntialias(); // restore vector antialias because we support it here setVectorAntialias(true); bool retVal = false; // get the clip region bbox if (pattern->getShading()->getHasBBox()) { pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax); } else { state->getClipBBox(&xMin, &yMin, &xMax, &yMax); xMin = floor (xMin); yMin = floor (yMin); xMax = ceil (xMax); yMax = ceil (yMax); { Matrix ctm, ictm; double x[4], y[4]; int i; state->getCTM(&ctm); ctm.invertTo(&ictm); ictm.transform(xMin, yMin, &x[0], &y[0]); ictm.transform(xMax, yMin, &x[1], &y[1]); ictm.transform(xMin, yMax, &x[2], &y[2]); ictm.transform(xMax, yMax, &x[3], &y[3]); xMin = xMax = x[0]; yMin = yMax = y[0]; for (i = 1; i < 4; i++) { xMin = std::min(xMin, x[i]); yMin = std::min(yMin, y[i]); xMax = std::max(xMax, x[i]); yMax = std::max(yMax, y[i]); } } } // fill the region state->moveTo(xMin, yMin); state->lineTo(xMax, yMin); state->lineTo(xMax, yMax); state->lineTo(xMin, yMax); state->closePath(); SplashPath path = convertPath(state, state->getPath(), true); #ifdef SPLASH_CMYK pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); #endif setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); retVal = (splash->shadedFill(&path, pattern->getShading()->getHasBBox(), pattern) == splashOk); state->clearPath(); setVectorAntialias(vaa); delete pattern; return retVal; } bool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading); bool retVal = univariateShadedFill(state, pattern, tMin, tMax); delete pattern; return retVal; } bool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) { SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading); bool retVal = univariateShadedFill(state, pattern, tMin, tMax); delete pattern; return retVal; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14494/SplashOutputDev.h000066400000000000000000000441201417746362400241200ustar00rootroot00000000000000//======================================================================== // // SplashOutputDev.h // // Copyright 2003 Glyph & Cog, LLC // //======================================================================== //======================================================================== // // Modified under the Poppler project - http://poppler.freedesktop.org // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2005 Takashi Iwai // Copyright (C) 2009-2016 Thomas Freitag // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2011, 2017 Adrian Johnson // Copyright (C) 2012, 2015, 2018 Albert Astals Cid // Copyright (C) 2015, 2016 William Bader // Copyright (C) 2018 Stefan Brüns // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== #ifndef SPLASHOUTPUTDEV_H #define SPLASHOUTPUTDEV_H #include "splash/SplashTypes.h" #include "splash/SplashPattern.h" #include "poppler-config.h" #include "OutputDev.h" #include "GfxState.h" #include "GlobalParams.h" class PDFDoc; class Gfx8BitFont; class SplashBitmap; class Splash; class SplashPath; class SplashFontEngine; class SplashFont; class T3FontCache; struct T3FontCacheTag; struct T3GlyphStack; struct SplashTransparencyGroup; //------------------------------------------------------------------------ // Splash dynamic pattern //------------------------------------------------------------------------ class SplashFunctionPattern : public SplashPattern { public: SplashFunctionPattern(SplashColorMode colorMode, GfxState *state, GfxFunctionShading *shading); SplashPattern *copy() override { return new SplashFunctionPattern(colorMode, state, (GfxFunctionShading *) shading); } ~SplashFunctionPattern(); bool testPosition(int x, int y) override { return true; } bool isStatic() override { return false; } bool getColor(int x, int y, SplashColorPtr c) override; virtual GfxFunctionShading *getShading() { return shading; } bool isCMYK() override { return gfxMode == csDeviceCMYK; } protected: Matrix ictm; double xMin, yMin, xMax, yMax; GfxFunctionShading *shading; GfxState *state; SplashColorMode colorMode; GfxColorSpaceMode gfxMode; }; class SplashUnivariatePattern : public SplashPattern { public: SplashUnivariatePattern(SplashColorMode colorMode, GfxState *state, GfxUnivariateShading *shading); ~SplashUnivariatePattern(); bool getColor(int x, int y, SplashColorPtr c) override; bool testPosition(int x, int y) override; bool isStatic() override { return false; } virtual bool getParameter(double xs, double ys, double *t) = 0; virtual GfxUnivariateShading *getShading() { return shading; } bool isCMYK() override { return gfxMode == csDeviceCMYK; } protected: Matrix ictm; double t0, t1, dt; GfxUnivariateShading *shading; GfxState *state; SplashColorMode colorMode; GfxColorSpaceMode gfxMode; }; class SplashAxialPattern : public SplashUnivariatePattern { public: SplashAxialPattern(SplashColorMode colorMode, GfxState *state, GfxAxialShading *shading); SplashPattern *copy() override { return new SplashAxialPattern(colorMode, state, (GfxAxialShading *) shading); } ~SplashAxialPattern(); bool getParameter(double xs, double ys, double *t) override; private: double x0, y0, x1, y1; double dx, dy, mul; }; // see GfxState.h, GfxGouraudTriangleShading class SplashGouraudPattern : public SplashGouraudColor { public: SplashGouraudPattern(bool bDirectColorTranslation, GfxState *state, GfxGouraudTriangleShading *shading); SplashPattern *copy() override { return new SplashGouraudPattern(bDirectColorTranslation, state, shading); } ~SplashGouraudPattern(); bool getColor(int x, int y, SplashColorPtr c) override { return false; } bool testPosition(int x, int y) override { return false; } bool isStatic() override { return false; } bool isCMYK() override { return gfxMode == csDeviceCMYK; } bool isParameterized() override { return shading->isParameterized(); } int getNTriangles() override { return shading->getNTriangles(); } void getTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2) override { shading->getTriangle(i, x0, y0, color0, x1, y1, color1, x2, y2, color2); } void getParameterizedColor(double t, SplashColorMode mode, SplashColorPtr c) override; private: GfxGouraudTriangleShading *shading; GfxState *state; bool bDirectColorTranslation; GfxColorSpaceMode gfxMode; }; // see GfxState.h, GfxRadialShading class SplashRadialPattern : public SplashUnivariatePattern { public: SplashRadialPattern(SplashColorMode colorMode, GfxState *state, GfxRadialShading *shading); SplashPattern *copy() override { return new SplashRadialPattern(colorMode, state, (GfxRadialShading *) shading); } ~SplashRadialPattern(); bool getParameter(double xs, double ys, double *t) override; private: double x0, y0, r0, dx, dy, dr; double a, inva; }; //------------------------------------------------------------------------ // number of Type 3 fonts to cache #define splashOutT3FontCacheSize 8 //------------------------------------------------------------------------ // SplashOutputDev //------------------------------------------------------------------------ class SplashOutputDev : public OutputDev { public: // Constructor. SplashOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, bool reverseVideoA, SplashColorPtr paperColorA, bool bitmapTopDownA = true, SplashThinLineMode thinLineMode = splashThinLineDefault, bool overprintPreviewA = globalParams->getOverprintPreview()); // Destructor. ~SplashOutputDev(); //----- get info about output device // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing // operations. bool useTilingPatternFill() override { return true; } // Does this device use functionShadedFill(), axialShadedFill(), and // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. bool useShadedFills(int type) override { return (type >= 1 && type <= 5) ? true : false; } // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) bool upsideDown() override { return bitmapTopDown ^ bitmapUpsideDown; } // Does this device use drawChar() or drawString()? bool useDrawChar() override { return true; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. bool interpretType3Chars() override { return true; } //----- initialization and control // Start a page. void startPage(int pageNum, GfxState *state, XRef *xref) override; // End a page. void endPage() override; //----- save/restore graphics state void saveState(GfxState *state) override; void restoreState(GfxState *state) override; //----- update graphics state void updateAll(GfxState *state) override; void updateCTM(GfxState *state, double m11, double m12, double m21, double m22, double m31, double m32) override; void updateLineDash(GfxState *state) override; void updateFlatness(GfxState *state) override; void updateLineJoin(GfxState *state) override; void updateLineCap(GfxState *state) override; void updateMiterLimit(GfxState *state) override; void updateLineWidth(GfxState *state) override; void updateStrokeAdjust(GfxState *state) override; void updateFillColorSpace(GfxState *state) override; void updateStrokeColorSpace(GfxState *state) override; void updateFillColor(GfxState *state) override; void updateStrokeColor(GfxState *state) override; void updateBlendMode(GfxState *state) override; void updateFillOpacity(GfxState *state) override; void updateStrokeOpacity(GfxState *state) override; void updatePatternOpacity(GfxState *state) override; void clearPatternOpacity(GfxState *state) override; void updateFillOverprint(GfxState *state) override; void updateStrokeOverprint(GfxState *state) override; void updateOverprintMode(GfxState *state) override; void updateTransfer(GfxState *state) override; //----- update text state void updateFont(GfxState *state) override; //----- path painting void stroke(GfxState *state) override; void fill(GfxState *state) override; void eoFill(GfxState *state) override; bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *catalog, Object *str, const double *pmat, int paintType, int tilingType, Dict *resDict, const double *mat, const double *bbox, int x0, int y0, int x1, int y1, double xStep, double yStep) override; bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override; bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override; bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) override; bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) override; //----- path clipping void clip(GfxState *state) override; void eoClip(GfxState *state) override; void clipToStrokePath(GfxState *state) override; //----- text drawing void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode *u, int uLen) override; bool beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, Unicode *u, int uLen) override; void endType3Char(GfxState *state) override; void beginTextObject(GfxState *state) override; void endTextObject(GfxState *state) override; //----- image drawing void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override; void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) override; void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) override; void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, int *maskColors, bool inlineImg) override; void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) override; void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, bool maskInterpolate) override; //----- Type 3 font operators void type3D0(GfxState *state, double wx, double wy) override; void type3D1(GfxState *state, double wx, double wy, double llx, double lly, double urx, double ury) override; //----- transparency groups and soft masks bool checkTransparencyGroup(GfxState *state, bool knockout) override; void beginTransparencyGroup(GfxState *state, const double *bbox, GfxColorSpace *blendingColorSpace, bool isolated, bool knockout, bool forSoftMask) override; void endTransparencyGroup(GfxState *state) override; void paintTransparencyGroup(GfxState *state, const double *bbox) override; void setSoftMask(GfxState *state, const double *bbox, bool alpha, Function *transferFunc, GfxColor *backdropColor) override; void clearSoftMask(GfxState *state) override; //----- special access // Called to indicate that a new PDF document has been loaded. void startDoc(PDFDoc *docA); void setPaperColor(SplashColorPtr paperColorA); bool isReverseVideo() { return reverseVideo; } void setReverseVideo(bool reverseVideoA) { reverseVideo = reverseVideoA; } // Get the bitmap and its size. SplashBitmap *getBitmap() { return bitmap; } int getBitmapWidth(); int getBitmapHeight(); // Returns the last rasterized bitmap, transferring ownership to the // caller. SplashBitmap *takeBitmap(); // Set this flag to true to generate an upside-down bitmap (useful // for Windows BMP files). void setBitmapUpsideDown(bool f) { bitmapUpsideDown = f; } // Get the Splash object. Splash *getSplash() { return splash; } // Get the modified region. void getModRegion(int *xMin, int *yMin, int *xMax, int *yMax); // Clear the modified region. void clearModRegion(); SplashFont *getCurrentFont() { return font; } // If is true, don't draw horizontal text. // If is true, don't draw rotated (non-horizontal) text. void setSkipText(bool skipHorizTextA, bool skipRotatedTextA) { skipHorizText = skipHorizTextA; skipRotatedText = skipRotatedTextA; } int getNestCount() { return nestCount; } #if 1 //~tmp: turn off anti-aliasing temporarily bool getVectorAntialias() override; void setVectorAntialias(bool vaa) override; #endif bool getFontAntialias() { return fontAntialias; } void setFontAntialias(bool anti) { fontAntialias = anti; } void setFreeTypeHinting(bool enable, bool enableSlightHinting); protected: void doUpdateFont(GfxState *state); private: bool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax); void setupScreenParams(double hDPI, double vDPI); SplashPattern *getColor(GfxGray gray); SplashPattern *getColor(GfxRGB *rgb); #ifdef SPLASH_CMYK SplashPattern *getColor(GfxCMYK *cmyk); SplashPattern *getColor(GfxColor *deviceN); #endif static void getMatteColor( SplashColorMode colorMode, GfxImageColorMap *colorMap, const GfxColor * matteColor, SplashColor splashMatteColor); void setOverprintMask(GfxColorSpace *colorSpace, bool overprintFlag, int overprintMode, const GfxColor *singleColor, bool grayIndexed = false); SplashPath convertPath(GfxState *state, GfxPath *path, bool dropEmptySubpaths); void drawType3Glyph(GfxState *state, T3FontCache *t3Font, T3FontCacheTag *tag, unsigned char *data); #ifdef USE_CMS bool useIccImageSrc(void *data); static void iccTransform(void *data, SplashBitmap *bitmap); static bool iccImageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine); #endif static bool imageMaskSrc(void *data, SplashColorPtr line); static bool imageSrc(void *data, SplashColorPtr colorLine, unsigned char *alphaLine); static bool alphaImageSrc(void *data, SplashColorPtr line, unsigned char *alphaLine); static bool maskedImageSrc(void *data, SplashColorPtr line, unsigned char *alphaLine); static bool tilingBitmapSrc(void *data, SplashColorPtr line, unsigned char *alphaLine); bool keepAlphaChannel; // don't fill with paper color, keep alpha channel SplashColorMode colorMode; int bitmapRowPad; bool bitmapTopDown; bool bitmapUpsideDown; bool fontAntialias; bool vectorAntialias; bool overprintPreview; bool enableFreeTypeHinting; bool enableSlightHinting; bool reverseVideo; // reverse video mode SplashColor paperColor; // paper color SplashScreenParams screenParams; bool skipHorizText; bool skipRotatedText; PDFDoc *doc; // the current document XRef *xref; // the xref of the current document SplashBitmap *bitmap; Splash *splash; SplashFontEngine *fontEngine; T3FontCache * // Type 3 font cache t3FontCache[splashOutT3FontCacheSize]; int nT3Fonts; // number of valid entries in t3FontCache T3GlyphStack *t3GlyphStack; // Type 3 glyph context stack SplashFont *font; // current font bool needFontUpdate; // set when the font needs to be updated SplashPath *textClipPath; // clipping path built with text object SplashTransparencyGroup * // transparency group stack transpGroupStack; int nestCount; }; #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14494/expected.txt000066400000000000000000000001311417746362400231710ustar00rootroot00000000000000SplashOutputDev.cc:4584:bughuntingDivByZero SplashOutputDev.cc:4585:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14981/000077500000000000000000000000001417746362400206355ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14981/expected.txt000066400000000000000000000000431417746362400231740ustar00rootroot00000000000000feature.c:2291:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-14981/feature.c000066400000000000000000003032111417746362400224340ustar00rootroot00000000000000/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % FFFFF EEEEE AAA TTTTT U U RRRR EEEEE % % F E A A T U U R R E % % FFF EEE AAAAA T U U RRRR EEE % % F E A A T U U R R E % % F EEEEE A A T UUU R R EEEEE % % % % % % MagickCore Image Feature Methods % % % % Software Design % % Cristy % % July 1992 % % % % % % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % % https://imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % */ /* Include declarations. */ #include "MagickCore/studio.h" #include "MagickCore/animate.h" #include "MagickCore/artifact.h" #include "MagickCore/blob.h" #include "MagickCore/blob-private.h" #include "MagickCore/cache.h" #include "MagickCore/cache-private.h" #include "MagickCore/cache-view.h" #include "MagickCore/channel.h" #include "MagickCore/client.h" #include "MagickCore/color.h" #include "MagickCore/color-private.h" #include "MagickCore/colorspace.h" #include "MagickCore/colorspace-private.h" #include "MagickCore/composite.h" #include "MagickCore/composite-private.h" #include "MagickCore/compress.h" #include "MagickCore/constitute.h" #include "MagickCore/display.h" #include "MagickCore/draw.h" #include "MagickCore/enhance.h" #include "MagickCore/exception.h" #include "MagickCore/exception-private.h" #include "MagickCore/feature.h" #include "MagickCore/gem.h" #include "MagickCore/geometry.h" #include "MagickCore/list.h" #include "MagickCore/image-private.h" #include "MagickCore/magic.h" #include "MagickCore/magick.h" #include "MagickCore/matrix.h" #include "MagickCore/memory_.h" #include "MagickCore/module.h" #include "MagickCore/monitor.h" #include "MagickCore/monitor-private.h" #include "MagickCore/morphology-private.h" #include "MagickCore/option.h" #include "MagickCore/paint.h" #include "MagickCore/pixel-accessor.h" #include "MagickCore/profile.h" #include "MagickCore/property.h" #include "MagickCore/quantize.h" #include "MagickCore/quantum-private.h" #include "MagickCore/random_.h" #include "MagickCore/resource_.h" #include "MagickCore/segment.h" #include "MagickCore/semaphore.h" #include "MagickCore/signature-private.h" #include "MagickCore/string_.h" #include "MagickCore/thread-private.h" #include "MagickCore/timer.h" #include "MagickCore/utility.h" #include "MagickCore/version.h" /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C a n n y E d g e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % CannyEdgeImage() uses a multi-stage algorithm to detect a wide range of % edges in images. % % The format of the CannyEdgeImage method is: % % Image *CannyEdgeImage(const Image *image,const double radius, % const double sigma,const double lower_percent, % const double upper_percent,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o radius: the radius of the gaussian smoothing filter. % % o sigma: the sigma of the gaussian smoothing filter. % % o lower_percent: percentage of edge pixels in the lower threshold. % % o upper_percent: percentage of edge pixels in the upper threshold. % % o exception: return any errors or warnings in this structure. % */ typedef struct _CannyInfo { double magnitude, intensity; int orientation; ssize_t x, y; } CannyInfo; static inline MagickBooleanType IsAuthenticPixel(const Image *image, const ssize_t x,const ssize_t y) { if ((x < 0) || (x >= (ssize_t) image->columns)) return(MagickFalse); if ((y < 0) || (y >= (ssize_t) image->rows)) return(MagickFalse); return(MagickTrue); } static MagickBooleanType TraceEdges(Image *edge_image,CacheView *edge_view, MatrixInfo *canny_cache,const ssize_t x,const ssize_t y, const double lower_threshold,ExceptionInfo *exception) { CannyInfo edge, pixel; MagickBooleanType status; register Quantum *q; register ssize_t i; q=GetCacheViewAuthenticPixels(edge_view,x,y,1,1,exception); if (q == (Quantum *) NULL) return(MagickFalse); *q=QuantumRange; status=SyncCacheViewAuthenticPixels(edge_view,exception); if (status == MagickFalse) return(MagickFalse); if (GetMatrixElement(canny_cache,0,0,&edge) == MagickFalse) return(MagickFalse); edge.x=x; edge.y=y; if (SetMatrixElement(canny_cache,0,0,&edge) == MagickFalse) return(MagickFalse); for (i=1; i != 0;) { ssize_t v; i--; status=GetMatrixElement(canny_cache,i,0,&edge); if (status == MagickFalse) return(MagickFalse); for (v=(-1); v <= 1; v++) { ssize_t u; for (u=(-1); u <= 1; u++) { if ((u == 0) && (v == 0)) continue; if (IsAuthenticPixel(edge_image,edge.x+u,edge.y+v) == MagickFalse) continue; /* Not an edge if gradient value is below the lower threshold. */ q=GetCacheViewAuthenticPixels(edge_view,edge.x+u,edge.y+v,1,1, exception); if (q == (Quantum *) NULL) return(MagickFalse); status=GetMatrixElement(canny_cache,edge.x+u,edge.y+v,&pixel); if (status == MagickFalse) return(MagickFalse); if ((GetPixelIntensity(edge_image,q) == 0.0) && (pixel.intensity >= lower_threshold)) { *q=QuantumRange; status=SyncCacheViewAuthenticPixels(edge_view,exception); if (status == MagickFalse) return(MagickFalse); edge.x+=u; edge.y+=v; status=SetMatrixElement(canny_cache,i,0,&edge); if (status == MagickFalse) return(MagickFalse); i++; } } } } return(MagickTrue); } MagickExport Image *CannyEdgeImage(const Image *image,const double radius, const double sigma,const double lower_percent,const double upper_percent, ExceptionInfo *exception) { #define CannyEdgeImageTag "CannyEdge/Image" CacheView *edge_view; CannyInfo element; char geometry[MagickPathExtent]; double lower_threshold, max, min, upper_threshold; Image *edge_image; KernelInfo *kernel_info; MagickBooleanType status; MagickOffsetType progress; MatrixInfo *canny_cache; ssize_t y; assert(image != (const Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); /* Filter out noise. */ (void) FormatLocaleString(geometry,MagickPathExtent, "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma); kernel_info=AcquireKernelInfo(geometry,exception); if (kernel_info == (KernelInfo *) NULL) ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); edge_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,exception); kernel_info=DestroyKernelInfo(kernel_info); if (edge_image == (Image *) NULL) return((Image *) NULL); if (TransformImageColorspace(edge_image,GRAYColorspace,exception) == MagickFalse) { edge_image=DestroyImage(edge_image); return((Image *) NULL); } (void) SetImageAlphaChannel(edge_image,OffAlphaChannel,exception); /* Find the intensity gradient of the image. */ canny_cache=AcquireMatrixInfo(edge_image->columns,edge_image->rows, sizeof(CannyInfo),exception); if (canny_cache == (MatrixInfo *) NULL) { edge_image=DestroyImage(edge_image); return((Image *) NULL); } status=MagickTrue; edge_view=AcquireVirtualCacheView(edge_image,exception); #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(edge_image,edge_image,edge_image->rows,1) #endif for (y=0; y < (ssize_t) edge_image->rows; y++) { register const Quantum *magick_restrict p; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns+1,2, exception); if (p == (const Quantum *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) edge_image->columns; x++) { CannyInfo pixel; double dx, dy; register const Quantum *magick_restrict kernel_pixels; ssize_t v; static double Gx[2][2] = { { -1.0, +1.0 }, { -1.0, +1.0 } }, Gy[2][2] = { { +1.0, +1.0 }, { -1.0, -1.0 } }; (void) memset(&pixel,0,sizeof(pixel)); dx=0.0; dy=0.0; kernel_pixels=p; for (v=0; v < 2; v++) { ssize_t u; for (u=0; u < 2; u++) { double intensity; intensity=GetPixelIntensity(edge_image,kernel_pixels+u); dx+=0.5*Gx[v][u]*intensity; dy+=0.5*Gy[v][u]*intensity; } kernel_pixels+=edge_image->columns+1; } pixel.magnitude=hypot(dx,dy); pixel.orientation=0; if (fabs(dx) > MagickEpsilon) { double slope; slope=dy/dx; if (slope < 0.0) { if (slope < -2.41421356237) pixel.orientation=0; else if (slope < -0.414213562373) pixel.orientation=1; else pixel.orientation=2; } else { if (slope > 2.41421356237) pixel.orientation=0; else if (slope > 0.414213562373) pixel.orientation=3; else pixel.orientation=2; } } if (SetMatrixElement(canny_cache,x,y,&pixel) == MagickFalse) continue; p+=GetPixelChannels(edge_image); } } edge_view=DestroyCacheView(edge_view); /* Non-maxima suppression, remove pixels that are not considered to be part of an edge. */ progress=0; (void) GetMatrixElement(canny_cache,0,0,&element); max=element.intensity; min=element.intensity; edge_view=AcquireAuthenticCacheView(edge_image,exception); #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(edge_image,edge_image,edge_image->rows,1) #endif for (y=0; y < (ssize_t) edge_image->rows; y++) { register Quantum *magick_restrict q; register ssize_t x; if (status == MagickFalse) continue; q=GetCacheViewAuthenticPixels(edge_view,0,y,edge_image->columns,1, exception); if (q == (Quantum *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) edge_image->columns; x++) { CannyInfo alpha_pixel, beta_pixel, pixel; (void) GetMatrixElement(canny_cache,x,y,&pixel); switch (pixel.orientation) { case 0: default: { /* 0 degrees, north and south. */ (void) GetMatrixElement(canny_cache,x,y-1,&alpha_pixel); (void) GetMatrixElement(canny_cache,x,y+1,&beta_pixel); break; } case 1: { /* 45 degrees, northwest and southeast. */ (void) GetMatrixElement(canny_cache,x-1,y-1,&alpha_pixel); (void) GetMatrixElement(canny_cache,x+1,y+1,&beta_pixel); break; } case 2: { /* 90 degrees, east and west. */ (void) GetMatrixElement(canny_cache,x-1,y,&alpha_pixel); (void) GetMatrixElement(canny_cache,x+1,y,&beta_pixel); break; } case 3: { /* 135 degrees, northeast and southwest. */ (void) GetMatrixElement(canny_cache,x+1,y-1,&beta_pixel); (void) GetMatrixElement(canny_cache,x-1,y+1,&alpha_pixel); break; } } pixel.intensity=pixel.magnitude; if ((pixel.magnitude < alpha_pixel.magnitude) || (pixel.magnitude < beta_pixel.magnitude)) pixel.intensity=0; (void) SetMatrixElement(canny_cache,x,y,&pixel); #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp critical (MagickCore_CannyEdgeImage) #endif { if (pixel.intensity < min) min=pixel.intensity; if (pixel.intensity > max) max=pixel.intensity; } *q=0; q+=GetPixelChannels(edge_image); } if (SyncCacheViewAuthenticPixels(edge_view,exception) == MagickFalse) status=MagickFalse; } edge_view=DestroyCacheView(edge_view); /* Estimate hysteresis threshold. */ lower_threshold=lower_percent*(max-min)+min; upper_threshold=upper_percent*(max-min)+min; /* Hysteresis threshold. */ edge_view=AcquireAuthenticCacheView(edge_image,exception); for (y=0; y < (ssize_t) edge_image->rows; y++) { register ssize_t x; if (status == MagickFalse) continue; for (x=0; x < (ssize_t) edge_image->columns; x++) { CannyInfo pixel; register const Quantum *magick_restrict p; /* Edge if pixel gradient higher than upper threshold. */ p=GetCacheViewVirtualPixels(edge_view,x,y,1,1,exception); if (p == (const Quantum *) NULL) continue; status=GetMatrixElement(canny_cache,x,y,&pixel); if (status == MagickFalse) continue; if ((GetPixelIntensity(edge_image,p) == 0.0) && (pixel.intensity >= upper_threshold)) status=TraceEdges(edge_image,edge_view,canny_cache,x,y,lower_threshold, exception); } if (image->progress_monitor != (MagickProgressMonitor) NULL) { MagickBooleanType proceed; #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp atomic #endif progress++; proceed=SetImageProgress(image,CannyEdgeImageTag,progress,image->rows); if (proceed == MagickFalse) status=MagickFalse; } } edge_view=DestroyCacheView(edge_view); /* Free resources. */ canny_cache=DestroyMatrixInfo(canny_cache); return(edge_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t I m a g e F e a t u r e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetImageFeatures() returns features for each channel in the image in % each of four directions (horizontal, vertical, left and right diagonals) % for the specified distance. The features include the angular second % moment, contrast, correlation, sum of squares: variance, inverse difference % moment, sum average, sum varience, sum entropy, entropy, difference variance,% difference entropy, information measures of correlation 1, information % measures of correlation 2, and maximum correlation coefficient. You can % access the red channel contrast, for example, like this: % % channel_features=GetImageFeatures(image,1,exception); % contrast=channel_features[RedPixelChannel].contrast[0]; % % Use MagickRelinquishMemory() to free the features buffer. % % The format of the GetImageFeatures method is: % % ChannelFeatures *GetImageFeatures(const Image *image, % const size_t distance,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o distance: the distance. % % o exception: return any errors or warnings in this structure. % */ static inline double MagickLog10(const double x) { #define Log10Epsilon (1.0e-11) if (fabs(x) < Log10Epsilon) return(log10(Log10Epsilon)); return(log10(fabs(x))); } MagickExport ChannelFeatures *GetImageFeatures(const Image *image, const size_t distance,ExceptionInfo *exception) { typedef struct _ChannelStatistics { PixelInfo direction[4]; /* horizontal, vertical, left and right diagonals */ } ChannelStatistics; CacheView *image_view; ChannelFeatures *channel_features; ChannelStatistics **cooccurrence, correlation, *density_x, *density_xy, *density_y, entropy_x, entropy_xy, entropy_xy1, entropy_xy2, entropy_y, mean, **Q, *sum, sum_squares, variance; PixelPacket gray, *grays; MagickBooleanType status; register ssize_t i, r; size_t length; unsigned int number_grays; assert(image != (Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); if ((image->columns < (distance+1)) || (image->rows < (distance+1))) return((ChannelFeatures *) NULL); length=MaxPixelChannels+1UL; channel_features=(ChannelFeatures *) AcquireQuantumMemory(length, sizeof(*channel_features)); if (channel_features == (ChannelFeatures *) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); (void) memset(channel_features,0,length* sizeof(*channel_features)); /* Form grays. */ grays=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*grays)); if (grays == (PixelPacket *) NULL) { channel_features=(ChannelFeatures *) RelinquishMagickMemory( channel_features); (void) ThrowMagickException(exception,GetMagickModule(), ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); return(channel_features); } for (i=0; i <= (ssize_t) MaxMap; i++) { grays[i].red=(~0U); grays[i].green=(~0U); grays[i].blue=(~0U); grays[i].alpha=(~0U); grays[i].black=(~0U); } status=MagickTrue; image_view=AcquireVirtualCacheView(image,exception); #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(image,image,image->rows,1) #endif for (r=0; r < (ssize_t) image->rows; r++) { register const Quantum *magick_restrict p; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,0,r,image->columns,1,exception); if (p == (const Quantum *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) image->columns; x++) { grays[ScaleQuantumToMap(GetPixelRed(image,p))].red= ScaleQuantumToMap(GetPixelRed(image,p)); grays[ScaleQuantumToMap(GetPixelGreen(image,p))].green= ScaleQuantumToMap(GetPixelGreen(image,p)); grays[ScaleQuantumToMap(GetPixelBlue(image,p))].blue= ScaleQuantumToMap(GetPixelBlue(image,p)); if (image->colorspace == CMYKColorspace) grays[ScaleQuantumToMap(GetPixelBlack(image,p))].black= ScaleQuantumToMap(GetPixelBlack(image,p)); if (image->alpha_trait != UndefinedPixelTrait) grays[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha= ScaleQuantumToMap(GetPixelAlpha(image,p)); p+=GetPixelChannels(image); } } image_view=DestroyCacheView(image_view); if (status == MagickFalse) { grays=(PixelPacket *) RelinquishMagickMemory(grays); channel_features=(ChannelFeatures *) RelinquishMagickMemory( channel_features); return(channel_features); } (void) memset(&gray,0,sizeof(gray)); for (i=0; i <= (ssize_t) MaxMap; i++) { if (grays[i].red != ~0U) grays[gray.red++].red=grays[i].red; if (grays[i].green != ~0U) grays[gray.green++].green=grays[i].green; if (grays[i].blue != ~0U) grays[gray.blue++].blue=grays[i].blue; if (image->colorspace == CMYKColorspace) if (grays[i].black != ~0U) grays[gray.black++].black=grays[i].black; if (image->alpha_trait != UndefinedPixelTrait) if (grays[i].alpha != ~0U) grays[gray.alpha++].alpha=grays[i].alpha; } /* Allocate spatial dependence matrix. */ number_grays=gray.red; if (gray.green > number_grays) number_grays=gray.green; if (gray.blue > number_grays) number_grays=gray.blue; if (image->colorspace == CMYKColorspace) if (gray.black > number_grays) number_grays=gray.black; if (image->alpha_trait != UndefinedPixelTrait) if (gray.alpha > number_grays) number_grays=gray.alpha; cooccurrence=(ChannelStatistics **) AcquireQuantumMemory(number_grays, sizeof(*cooccurrence)); density_x=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), sizeof(*density_x)); density_xy=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), sizeof(*density_xy)); density_y=(ChannelStatistics *) AcquireQuantumMemory(2*(number_grays+1), sizeof(*density_y)); Q=(ChannelStatistics **) AcquireQuantumMemory(number_grays,sizeof(*Q)); sum=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(*sum)); if ((cooccurrence == (ChannelStatistics **) NULL) || (density_x == (ChannelStatistics *) NULL) || (density_xy == (ChannelStatistics *) NULL) || (density_y == (ChannelStatistics *) NULL) || (Q == (ChannelStatistics **) NULL) || (sum == (ChannelStatistics *) NULL)) { if (Q != (ChannelStatistics **) NULL) { for (i=0; i < (ssize_t) number_grays; i++) Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); Q=(ChannelStatistics **) RelinquishMagickMemory(Q); } if (sum != (ChannelStatistics *) NULL) sum=(ChannelStatistics *) RelinquishMagickMemory(sum); if (density_y != (ChannelStatistics *) NULL) density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); if (density_xy != (ChannelStatistics *) NULL) density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); if (density_x != (ChannelStatistics *) NULL) density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); if (cooccurrence != (ChannelStatistics **) NULL) { for (i=0; i < (ssize_t) number_grays; i++) cooccurrence[i]=(ChannelStatistics *) RelinquishMagickMemory(cooccurrence[i]); cooccurrence=(ChannelStatistics **) RelinquishMagickMemory( cooccurrence); } grays=(PixelPacket *) RelinquishMagickMemory(grays); channel_features=(ChannelFeatures *) RelinquishMagickMemory( channel_features); (void) ThrowMagickException(exception,GetMagickModule(), ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); return(channel_features); } (void) memset(&correlation,0,sizeof(correlation)); (void) memset(density_x,0,2*(number_grays+1)*sizeof(*density_x)); (void) memset(density_xy,0,2*(number_grays+1)*sizeof(*density_xy)); (void) memset(density_y,0,2*(number_grays+1)*sizeof(*density_y)); (void) memset(&mean,0,sizeof(mean)); (void) memset(sum,0,number_grays*sizeof(*sum)); (void) memset(&sum_squares,0,sizeof(sum_squares)); (void) memset(density_xy,0,2*number_grays*sizeof(*density_xy)); (void) memset(&entropy_x,0,sizeof(entropy_x)); (void) memset(&entropy_xy,0,sizeof(entropy_xy)); (void) memset(&entropy_xy1,0,sizeof(entropy_xy1)); (void) memset(&entropy_xy2,0,sizeof(entropy_xy2)); (void) memset(&entropy_y,0,sizeof(entropy_y)); (void) memset(&variance,0,sizeof(variance)); for (i=0; i < (ssize_t) number_grays; i++) { cooccurrence[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays, sizeof(**cooccurrence)); Q[i]=(ChannelStatistics *) AcquireQuantumMemory(number_grays,sizeof(**Q)); if ((cooccurrence[i] == (ChannelStatistics *) NULL) || (Q[i] == (ChannelStatistics *) NULL)) break; (void) memset(cooccurrence[i],0,number_grays* sizeof(**cooccurrence)); (void) memset(Q[i],0,number_grays*sizeof(**Q)); } if (i < (ssize_t) number_grays) { for (i--; i >= 0; i--) { if (Q[i] != (ChannelStatistics *) NULL) Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); if (cooccurrence[i] != (ChannelStatistics *) NULL) cooccurrence[i]=(ChannelStatistics *) RelinquishMagickMemory(cooccurrence[i]); } Q=(ChannelStatistics **) RelinquishMagickMemory(Q); cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); sum=(ChannelStatistics *) RelinquishMagickMemory(sum); density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); grays=(PixelPacket *) RelinquishMagickMemory(grays); channel_features=(ChannelFeatures *) RelinquishMagickMemory( channel_features); (void) ThrowMagickException(exception,GetMagickModule(), ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); return(channel_features); } /* Initialize spatial dependence matrix. */ status=MagickTrue; image_view=AcquireVirtualCacheView(image,exception); for (r=0; r < (ssize_t) image->rows; r++) { register const Quantum *magick_restrict p; register ssize_t x; ssize_t offset, u, v; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,-(ssize_t) distance,r,image->columns+ 2*distance,distance+2,exception); if (p == (const Quantum *) NULL) { status=MagickFalse; continue; } p+=distance*GetPixelChannels(image);; for (x=0; x < (ssize_t) image->columns; x++) { for (i=0; i < 4; i++) { switch (i) { case 0: default: { /* Horizontal adjacency. */ offset=(ssize_t) distance; break; } case 1: { /* Vertical adjacency. */ offset=(ssize_t) (image->columns+2*distance); break; } case 2: { /* Right diagonal adjacency. */ offset=(ssize_t) ((image->columns+2*distance)-distance); break; } case 3: { /* Left diagonal adjacency. */ offset=(ssize_t) ((image->columns+2*distance)+distance); break; } } u=0; v=0; while (grays[u].red != ScaleQuantumToMap(GetPixelRed(image,p))) u++; while (grays[v].red != ScaleQuantumToMap(GetPixelRed(image,p+offset*GetPixelChannels(image)))) v++; cooccurrence[u][v].direction[i].red++; cooccurrence[v][u].direction[i].red++; u=0; v=0; while (grays[u].green != ScaleQuantumToMap(GetPixelGreen(image,p))) u++; while (grays[v].green != ScaleQuantumToMap(GetPixelGreen(image,p+offset*GetPixelChannels(image)))) v++; cooccurrence[u][v].direction[i].green++; cooccurrence[v][u].direction[i].green++; u=0; v=0; while (grays[u].blue != ScaleQuantumToMap(GetPixelBlue(image,p))) u++; while (grays[v].blue != ScaleQuantumToMap(GetPixelBlue(image,p+offset*GetPixelChannels(image)))) v++; cooccurrence[u][v].direction[i].blue++; cooccurrence[v][u].direction[i].blue++; if (image->colorspace == CMYKColorspace) { u=0; v=0; while (grays[u].black != ScaleQuantumToMap(GetPixelBlack(image,p))) u++; while (grays[v].black != ScaleQuantumToMap(GetPixelBlack(image,p+offset*GetPixelChannels(image)))) v++; cooccurrence[u][v].direction[i].black++; cooccurrence[v][u].direction[i].black++; } if (image->alpha_trait != UndefinedPixelTrait) { u=0; v=0; while (grays[u].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p))) u++; while (grays[v].alpha != ScaleQuantumToMap(GetPixelAlpha(image,p+offset*GetPixelChannels(image)))) v++; cooccurrence[u][v].direction[i].alpha++; cooccurrence[v][u].direction[i].alpha++; } } p+=GetPixelChannels(image); } } grays=(PixelPacket *) RelinquishMagickMemory(grays); image_view=DestroyCacheView(image_view); if (status == MagickFalse) { for (i=0; i < (ssize_t) number_grays; i++) cooccurrence[i]=(ChannelStatistics *) RelinquishMagickMemory(cooccurrence[i]); cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); channel_features=(ChannelFeatures *) RelinquishMagickMemory( channel_features); (void) ThrowMagickException(exception,GetMagickModule(), ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); return(channel_features); } /* Normalize spatial dependence matrix. */ for (i=0; i < 4; i++) { double normalize; register ssize_t y; switch (i) { case 0: default: { /* Horizontal adjacency. */ normalize=2.0*image->rows*(image->columns-distance); break; } case 1: { /* Vertical adjacency. */ normalize=2.0*(image->rows-distance)*image->columns; break; } case 2: { /* Right diagonal adjacency. */ normalize=2.0*(image->rows-distance)*(image->columns-distance); break; } case 3: { /* Left diagonal adjacency. */ normalize=2.0*(image->rows-distance)*(image->columns-distance); break; } } normalize=PerceptibleReciprocal(normalize); for (y=0; y < (ssize_t) number_grays; y++) { register ssize_t x; for (x=0; x < (ssize_t) number_grays; x++) { cooccurrence[x][y].direction[i].red*=normalize; cooccurrence[x][y].direction[i].green*=normalize; cooccurrence[x][y].direction[i].blue*=normalize; if (image->colorspace == CMYKColorspace) cooccurrence[x][y].direction[i].black*=normalize; if (image->alpha_trait != UndefinedPixelTrait) cooccurrence[x][y].direction[i].alpha*=normalize; } } } /* Compute texture features. */ #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(image,image,number_grays,1) #endif for (i=0; i < 4; i++) { register ssize_t y; for (y=0; y < (ssize_t) number_grays; y++) { register ssize_t x; for (x=0; x < (ssize_t) number_grays; x++) { /* Angular second moment: measure of homogeneity of the image. */ channel_features[RedPixelChannel].angular_second_moment[i]+= cooccurrence[x][y].direction[i].red* cooccurrence[x][y].direction[i].red; channel_features[GreenPixelChannel].angular_second_moment[i]+= cooccurrence[x][y].direction[i].green* cooccurrence[x][y].direction[i].green; channel_features[BluePixelChannel].angular_second_moment[i]+= cooccurrence[x][y].direction[i].blue* cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].angular_second_moment[i]+= cooccurrence[x][y].direction[i].black* cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].angular_second_moment[i]+= cooccurrence[x][y].direction[i].alpha* cooccurrence[x][y].direction[i].alpha; /* Correlation: measure of linear-dependencies in the image. */ sum[y].direction[i].red+=cooccurrence[x][y].direction[i].red; sum[y].direction[i].green+=cooccurrence[x][y].direction[i].green; sum[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) sum[y].direction[i].black+=cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) sum[y].direction[i].alpha+=cooccurrence[x][y].direction[i].alpha; correlation.direction[i].red+=x*y*cooccurrence[x][y].direction[i].red; correlation.direction[i].green+=x*y* cooccurrence[x][y].direction[i].green; correlation.direction[i].blue+=x*y* cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) correlation.direction[i].black+=x*y* cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) correlation.direction[i].alpha+=x*y* cooccurrence[x][y].direction[i].alpha; /* Inverse Difference Moment. */ channel_features[RedPixelChannel].inverse_difference_moment[i]+= cooccurrence[x][y].direction[i].red/((y-x)*(y-x)+1); channel_features[GreenPixelChannel].inverse_difference_moment[i]+= cooccurrence[x][y].direction[i].green/((y-x)*(y-x)+1); channel_features[BluePixelChannel].inverse_difference_moment[i]+= cooccurrence[x][y].direction[i].blue/((y-x)*(y-x)+1); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].inverse_difference_moment[i]+= cooccurrence[x][y].direction[i].black/((y-x)*(y-x)+1); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].inverse_difference_moment[i]+= cooccurrence[x][y].direction[i].alpha/((y-x)*(y-x)+1); /* Sum average. */ density_xy[y+x+2].direction[i].red+= cooccurrence[x][y].direction[i].red; density_xy[y+x+2].direction[i].green+= cooccurrence[x][y].direction[i].green; density_xy[y+x+2].direction[i].blue+= cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) density_xy[y+x+2].direction[i].black+= cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) density_xy[y+x+2].direction[i].alpha+= cooccurrence[x][y].direction[i].alpha; /* Entropy. */ channel_features[RedPixelChannel].entropy[i]-= cooccurrence[x][y].direction[i].red* MagickLog10(cooccurrence[x][y].direction[i].red); channel_features[GreenPixelChannel].entropy[i]-= cooccurrence[x][y].direction[i].green* MagickLog10(cooccurrence[x][y].direction[i].green); channel_features[BluePixelChannel].entropy[i]-= cooccurrence[x][y].direction[i].blue* MagickLog10(cooccurrence[x][y].direction[i].blue); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].entropy[i]-= cooccurrence[x][y].direction[i].black* MagickLog10(cooccurrence[x][y].direction[i].black); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].entropy[i]-= cooccurrence[x][y].direction[i].alpha* MagickLog10(cooccurrence[x][y].direction[i].alpha); /* Information Measures of Correlation. */ density_x[x].direction[i].red+=cooccurrence[x][y].direction[i].red; density_x[x].direction[i].green+=cooccurrence[x][y].direction[i].green; density_x[x].direction[i].blue+=cooccurrence[x][y].direction[i].blue; if (image->alpha_trait != UndefinedPixelTrait) density_x[x].direction[i].alpha+= cooccurrence[x][y].direction[i].alpha; if (image->colorspace == CMYKColorspace) density_x[x].direction[i].black+= cooccurrence[x][y].direction[i].black; density_y[y].direction[i].red+=cooccurrence[x][y].direction[i].red; density_y[y].direction[i].green+=cooccurrence[x][y].direction[i].green; density_y[y].direction[i].blue+=cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) density_y[y].direction[i].black+= cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) density_y[y].direction[i].alpha+= cooccurrence[x][y].direction[i].alpha; } mean.direction[i].red+=y*sum[y].direction[i].red; sum_squares.direction[i].red+=y*y*sum[y].direction[i].red; mean.direction[i].green+=y*sum[y].direction[i].green; sum_squares.direction[i].green+=y*y*sum[y].direction[i].green; mean.direction[i].blue+=y*sum[y].direction[i].blue; sum_squares.direction[i].blue+=y*y*sum[y].direction[i].blue; if (image->colorspace == CMYKColorspace) { mean.direction[i].black+=y*sum[y].direction[i].black; sum_squares.direction[i].black+=y*y*sum[y].direction[i].black; } if (image->alpha_trait != UndefinedPixelTrait) { mean.direction[i].alpha+=y*sum[y].direction[i].alpha; sum_squares.direction[i].alpha+=y*y*sum[y].direction[i].alpha; } } /* Correlation: measure of linear-dependencies in the image. */ channel_features[RedPixelChannel].correlation[i]= (correlation.direction[i].red-mean.direction[i].red* mean.direction[i].red)/(sqrt(sum_squares.direction[i].red- (mean.direction[i].red*mean.direction[i].red))*sqrt( sum_squares.direction[i].red-(mean.direction[i].red* mean.direction[i].red))); channel_features[GreenPixelChannel].correlation[i]= (correlation.direction[i].green-mean.direction[i].green* mean.direction[i].green)/(sqrt(sum_squares.direction[i].green- (mean.direction[i].green*mean.direction[i].green))*sqrt( sum_squares.direction[i].green-(mean.direction[i].green* mean.direction[i].green))); channel_features[BluePixelChannel].correlation[i]= (correlation.direction[i].blue-mean.direction[i].blue* mean.direction[i].blue)/(sqrt(sum_squares.direction[i].blue- (mean.direction[i].blue*mean.direction[i].blue))*sqrt( sum_squares.direction[i].blue-(mean.direction[i].blue* mean.direction[i].blue))); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].correlation[i]= (correlation.direction[i].black-mean.direction[i].black* mean.direction[i].black)/(sqrt(sum_squares.direction[i].black- (mean.direction[i].black*mean.direction[i].black))*sqrt( sum_squares.direction[i].black-(mean.direction[i].black* mean.direction[i].black))); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].correlation[i]= (correlation.direction[i].alpha-mean.direction[i].alpha* mean.direction[i].alpha)/(sqrt(sum_squares.direction[i].alpha- (mean.direction[i].alpha*mean.direction[i].alpha))*sqrt( sum_squares.direction[i].alpha-(mean.direction[i].alpha* mean.direction[i].alpha))); } /* Compute more texture features. */ #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(image,image,number_grays,1) #endif for (i=0; i < 4; i++) { register ssize_t x; for (x=2; x < (ssize_t) (2*number_grays); x++) { /* Sum average. */ channel_features[RedPixelChannel].sum_average[i]+= x*density_xy[x].direction[i].red; channel_features[GreenPixelChannel].sum_average[i]+= x*density_xy[x].direction[i].green; channel_features[BluePixelChannel].sum_average[i]+= x*density_xy[x].direction[i].blue; if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].sum_average[i]+= x*density_xy[x].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].sum_average[i]+= x*density_xy[x].direction[i].alpha; /* Sum entropy. */ channel_features[RedPixelChannel].sum_entropy[i]-= density_xy[x].direction[i].red* MagickLog10(density_xy[x].direction[i].red); channel_features[GreenPixelChannel].sum_entropy[i]-= density_xy[x].direction[i].green* MagickLog10(density_xy[x].direction[i].green); channel_features[BluePixelChannel].sum_entropy[i]-= density_xy[x].direction[i].blue* MagickLog10(density_xy[x].direction[i].blue); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].sum_entropy[i]-= density_xy[x].direction[i].black* MagickLog10(density_xy[x].direction[i].black); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].sum_entropy[i]-= density_xy[x].direction[i].alpha* MagickLog10(density_xy[x].direction[i].alpha); /* Sum variance. */ channel_features[RedPixelChannel].sum_variance[i]+= (x-channel_features[RedPixelChannel].sum_entropy[i])* (x-channel_features[RedPixelChannel].sum_entropy[i])* density_xy[x].direction[i].red; channel_features[GreenPixelChannel].sum_variance[i]+= (x-channel_features[GreenPixelChannel].sum_entropy[i])* (x-channel_features[GreenPixelChannel].sum_entropy[i])* density_xy[x].direction[i].green; channel_features[BluePixelChannel].sum_variance[i]+= (x-channel_features[BluePixelChannel].sum_entropy[i])* (x-channel_features[BluePixelChannel].sum_entropy[i])* density_xy[x].direction[i].blue; if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].sum_variance[i]+= (x-channel_features[BlackPixelChannel].sum_entropy[i])* (x-channel_features[BlackPixelChannel].sum_entropy[i])* density_xy[x].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].sum_variance[i]+= (x-channel_features[AlphaPixelChannel].sum_entropy[i])* (x-channel_features[AlphaPixelChannel].sum_entropy[i])* density_xy[x].direction[i].alpha; } } /* Compute more texture features. */ #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(image,image,number_grays,1) #endif for (i=0; i < 4; i++) { register ssize_t y; for (y=0; y < (ssize_t) number_grays; y++) { register ssize_t x; for (x=0; x < (ssize_t) number_grays; x++) { /* Sum of Squares: Variance */ variance.direction[i].red+=(y-mean.direction[i].red+1)* (y-mean.direction[i].red+1)*cooccurrence[x][y].direction[i].red; variance.direction[i].green+=(y-mean.direction[i].green+1)* (y-mean.direction[i].green+1)*cooccurrence[x][y].direction[i].green; variance.direction[i].blue+=(y-mean.direction[i].blue+1)* (y-mean.direction[i].blue+1)*cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) variance.direction[i].black+=(y-mean.direction[i].black+1)* (y-mean.direction[i].black+1)*cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) variance.direction[i].alpha+=(y-mean.direction[i].alpha+1)* (y-mean.direction[i].alpha+1)* cooccurrence[x][y].direction[i].alpha; /* Sum average / Difference Variance. */ density_xy[MagickAbsoluteValue(y-x)].direction[i].red+= cooccurrence[x][y].direction[i].red; density_xy[MagickAbsoluteValue(y-x)].direction[i].green+= cooccurrence[x][y].direction[i].green; density_xy[MagickAbsoluteValue(y-x)].direction[i].blue+= cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) density_xy[MagickAbsoluteValue(y-x)].direction[i].black+= cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) density_xy[MagickAbsoluteValue(y-x)].direction[i].alpha+= cooccurrence[x][y].direction[i].alpha; /* Information Measures of Correlation. */ entropy_xy.direction[i].red-=cooccurrence[x][y].direction[i].red* MagickLog10(cooccurrence[x][y].direction[i].red); entropy_xy.direction[i].green-=cooccurrence[x][y].direction[i].green* MagickLog10(cooccurrence[x][y].direction[i].green); entropy_xy.direction[i].blue-=cooccurrence[x][y].direction[i].blue* MagickLog10(cooccurrence[x][y].direction[i].blue); if (image->colorspace == CMYKColorspace) entropy_xy.direction[i].black-=cooccurrence[x][y].direction[i].black* MagickLog10(cooccurrence[x][y].direction[i].black); if (image->alpha_trait != UndefinedPixelTrait) entropy_xy.direction[i].alpha-= cooccurrence[x][y].direction[i].alpha*MagickLog10( cooccurrence[x][y].direction[i].alpha); entropy_xy1.direction[i].red-=(cooccurrence[x][y].direction[i].red* MagickLog10(density_x[x].direction[i].red*density_y[y].direction[i].red)); entropy_xy1.direction[i].green-=(cooccurrence[x][y].direction[i].green* MagickLog10(density_x[x].direction[i].green* density_y[y].direction[i].green)); entropy_xy1.direction[i].blue-=(cooccurrence[x][y].direction[i].blue* MagickLog10(density_x[x].direction[i].blue*density_y[y].direction[i].blue)); if (image->colorspace == CMYKColorspace) entropy_xy1.direction[i].black-=( cooccurrence[x][y].direction[i].black*MagickLog10( density_x[x].direction[i].black*density_y[y].direction[i].black)); if (image->alpha_trait != UndefinedPixelTrait) entropy_xy1.direction[i].alpha-=( cooccurrence[x][y].direction[i].alpha*MagickLog10( density_x[x].direction[i].alpha*density_y[y].direction[i].alpha)); entropy_xy2.direction[i].red-=(density_x[x].direction[i].red* density_y[y].direction[i].red*MagickLog10(density_x[x].direction[i].red* density_y[y].direction[i].red)); entropy_xy2.direction[i].green-=(density_x[x].direction[i].green* density_y[y].direction[i].green*MagickLog10(density_x[x].direction[i].green* density_y[y].direction[i].green)); entropy_xy2.direction[i].blue-=(density_x[x].direction[i].blue* density_y[y].direction[i].blue*MagickLog10(density_x[x].direction[i].blue* density_y[y].direction[i].blue)); if (image->colorspace == CMYKColorspace) entropy_xy2.direction[i].black-=(density_x[x].direction[i].black* density_y[y].direction[i].black*MagickLog10( density_x[x].direction[i].black*density_y[y].direction[i].black)); if (image->alpha_trait != UndefinedPixelTrait) entropy_xy2.direction[i].alpha-=(density_x[x].direction[i].alpha* density_y[y].direction[i].alpha*MagickLog10( density_x[x].direction[i].alpha*density_y[y].direction[i].alpha)); } } channel_features[RedPixelChannel].variance_sum_of_squares[i]= variance.direction[i].red; channel_features[GreenPixelChannel].variance_sum_of_squares[i]= variance.direction[i].green; channel_features[BluePixelChannel].variance_sum_of_squares[i]= variance.direction[i].blue; if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].variance_sum_of_squares[i]= variance.direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].variance_sum_of_squares[i]= variance.direction[i].alpha; } /* Compute more texture features. */ (void) memset(&variance,0,sizeof(variance)); (void) memset(&sum_squares,0,sizeof(sum_squares)); #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(image,image,number_grays,1) #endif for (i=0; i < 4; i++) { register ssize_t x; for (x=0; x < (ssize_t) number_grays; x++) { /* Difference variance. */ variance.direction[i].red+=density_xy[x].direction[i].red; variance.direction[i].green+=density_xy[x].direction[i].green; variance.direction[i].blue+=density_xy[x].direction[i].blue; if (image->colorspace == CMYKColorspace) variance.direction[i].black+=density_xy[x].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) variance.direction[i].alpha+=density_xy[x].direction[i].alpha; sum_squares.direction[i].red+=density_xy[x].direction[i].red* density_xy[x].direction[i].red; sum_squares.direction[i].green+=density_xy[x].direction[i].green* density_xy[x].direction[i].green; sum_squares.direction[i].blue+=density_xy[x].direction[i].blue* density_xy[x].direction[i].blue; if (image->colorspace == CMYKColorspace) sum_squares.direction[i].black+=density_xy[x].direction[i].black* density_xy[x].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) sum_squares.direction[i].alpha+=density_xy[x].direction[i].alpha* density_xy[x].direction[i].alpha; /* Difference entropy. */ channel_features[RedPixelChannel].difference_entropy[i]-= density_xy[x].direction[i].red* MagickLog10(density_xy[x].direction[i].red); channel_features[GreenPixelChannel].difference_entropy[i]-= density_xy[x].direction[i].green* MagickLog10(density_xy[x].direction[i].green); channel_features[BluePixelChannel].difference_entropy[i]-= density_xy[x].direction[i].blue* MagickLog10(density_xy[x].direction[i].blue); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].difference_entropy[i]-= density_xy[x].direction[i].black* MagickLog10(density_xy[x].direction[i].black); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].difference_entropy[i]-= density_xy[x].direction[i].alpha* MagickLog10(density_xy[x].direction[i].alpha); /* Information Measures of Correlation. */ entropy_x.direction[i].red-=(density_x[x].direction[i].red* MagickLog10(density_x[x].direction[i].red)); entropy_x.direction[i].green-=(density_x[x].direction[i].green* MagickLog10(density_x[x].direction[i].green)); entropy_x.direction[i].blue-=(density_x[x].direction[i].blue* MagickLog10(density_x[x].direction[i].blue)); if (image->colorspace == CMYKColorspace) entropy_x.direction[i].black-=(density_x[x].direction[i].black* MagickLog10(density_x[x].direction[i].black)); if (image->alpha_trait != UndefinedPixelTrait) entropy_x.direction[i].alpha-=(density_x[x].direction[i].alpha* MagickLog10(density_x[x].direction[i].alpha)); entropy_y.direction[i].red-=(density_y[x].direction[i].red* MagickLog10(density_y[x].direction[i].red)); entropy_y.direction[i].green-=(density_y[x].direction[i].green* MagickLog10(density_y[x].direction[i].green)); entropy_y.direction[i].blue-=(density_y[x].direction[i].blue* MagickLog10(density_y[x].direction[i].blue)); if (image->colorspace == CMYKColorspace) entropy_y.direction[i].black-=(density_y[x].direction[i].black* MagickLog10(density_y[x].direction[i].black)); if (image->alpha_trait != UndefinedPixelTrait) entropy_y.direction[i].alpha-=(density_y[x].direction[i].alpha* MagickLog10(density_y[x].direction[i].alpha)); } /* Difference variance. */ channel_features[RedPixelChannel].difference_variance[i]= (((double) number_grays*number_grays*sum_squares.direction[i].red)- (variance.direction[i].red*variance.direction[i].red))/ ((double) number_grays*number_grays*number_grays*number_grays); channel_features[GreenPixelChannel].difference_variance[i]= (((double) number_grays*number_grays*sum_squares.direction[i].green)- (variance.direction[i].green*variance.direction[i].green))/ ((double) number_grays*number_grays*number_grays*number_grays); channel_features[BluePixelChannel].difference_variance[i]= (((double) number_grays*number_grays*sum_squares.direction[i].blue)- (variance.direction[i].blue*variance.direction[i].blue))/ ((double) number_grays*number_grays*number_grays*number_grays); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].difference_variance[i]= (((double) number_grays*number_grays*sum_squares.direction[i].black)- (variance.direction[i].black*variance.direction[i].black))/ ((double) number_grays*number_grays*number_grays*number_grays); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].difference_variance[i]= (((double) number_grays*number_grays*sum_squares.direction[i].alpha)- (variance.direction[i].alpha*variance.direction[i].alpha))/ ((double) number_grays*number_grays*number_grays*number_grays); /* Information Measures of Correlation. */ channel_features[RedPixelChannel].measure_of_correlation_1[i]= (entropy_xy.direction[i].red-entropy_xy1.direction[i].red)/ (entropy_x.direction[i].red > entropy_y.direction[i].red ? entropy_x.direction[i].red : entropy_y.direction[i].red); channel_features[GreenPixelChannel].measure_of_correlation_1[i]= (entropy_xy.direction[i].green-entropy_xy1.direction[i].green)/ (entropy_x.direction[i].green > entropy_y.direction[i].green ? entropy_x.direction[i].green : entropy_y.direction[i].green); channel_features[BluePixelChannel].measure_of_correlation_1[i]= (entropy_xy.direction[i].blue-entropy_xy1.direction[i].blue)/ (entropy_x.direction[i].blue > entropy_y.direction[i].blue ? entropy_x.direction[i].blue : entropy_y.direction[i].blue); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].measure_of_correlation_1[i]= (entropy_xy.direction[i].black-entropy_xy1.direction[i].black)/ (entropy_x.direction[i].black > entropy_y.direction[i].black ? entropy_x.direction[i].black : entropy_y.direction[i].black); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].measure_of_correlation_1[i]= (entropy_xy.direction[i].alpha-entropy_xy1.direction[i].alpha)/ (entropy_x.direction[i].alpha > entropy_y.direction[i].alpha ? entropy_x.direction[i].alpha : entropy_y.direction[i].alpha); channel_features[RedPixelChannel].measure_of_correlation_2[i]= (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].red- entropy_xy.direction[i].red))))); channel_features[GreenPixelChannel].measure_of_correlation_2[i]= (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].green- entropy_xy.direction[i].green))))); channel_features[BluePixelChannel].measure_of_correlation_2[i]= (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].blue- entropy_xy.direction[i].blue))))); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].measure_of_correlation_2[i]= (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].black- entropy_xy.direction[i].black))))); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].measure_of_correlation_2[i]= (sqrt(fabs(1.0-exp(-2.0*(double) (entropy_xy2.direction[i].alpha- entropy_xy.direction[i].alpha))))); } /* Compute more texture features. */ #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status) \ magick_number_threads(image,image,number_grays,1) #endif for (i=0; i < 4; i++) { ssize_t z; for (z=0; z < (ssize_t) number_grays; z++) { register ssize_t y; ChannelStatistics pixel; (void) memset(&pixel,0,sizeof(pixel)); for (y=0; y < (ssize_t) number_grays; y++) { register ssize_t x; for (x=0; x < (ssize_t) number_grays; x++) { /* Contrast: amount of local variations present in an image. */ if (((y-x) == z) || ((x-y) == z)) { pixel.direction[i].red+=cooccurrence[x][y].direction[i].red; pixel.direction[i].green+=cooccurrence[x][y].direction[i].green; pixel.direction[i].blue+=cooccurrence[x][y].direction[i].blue; if (image->colorspace == CMYKColorspace) pixel.direction[i].black+=cooccurrence[x][y].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) pixel.direction[i].alpha+= cooccurrence[x][y].direction[i].alpha; } /* Maximum Correlation Coefficient. */ Q[z][y].direction[i].red+=cooccurrence[z][x].direction[i].red* cooccurrence[y][x].direction[i].red/density_x[z].direction[i].red/ density_y[x].direction[i].red; Q[z][y].direction[i].green+=cooccurrence[z][x].direction[i].green* cooccurrence[y][x].direction[i].green/ density_x[z].direction[i].green/density_y[x].direction[i].red; Q[z][y].direction[i].blue+=cooccurrence[z][x].direction[i].blue* cooccurrence[y][x].direction[i].blue/density_x[z].direction[i].blue/ density_y[x].direction[i].blue; if (image->colorspace == CMYKColorspace) Q[z][y].direction[i].black+=cooccurrence[z][x].direction[i].black* cooccurrence[y][x].direction[i].black/ density_x[z].direction[i].black/density_y[x].direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) Q[z][y].direction[i].alpha+= cooccurrence[z][x].direction[i].alpha* cooccurrence[y][x].direction[i].alpha/ density_x[z].direction[i].alpha/ density_y[x].direction[i].alpha; } } channel_features[RedPixelChannel].contrast[i]+=z*z* pixel.direction[i].red; channel_features[GreenPixelChannel].contrast[i]+=z*z* pixel.direction[i].green; channel_features[BluePixelChannel].contrast[i]+=z*z* pixel.direction[i].blue; if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].contrast[i]+=z*z* pixel.direction[i].black; if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].contrast[i]+=z*z* pixel.direction[i].alpha; } /* Maximum Correlation Coefficient. Future: return second largest eigenvalue of Q. */ channel_features[RedPixelChannel].maximum_correlation_coefficient[i]= sqrt((double) -1.0); channel_features[GreenPixelChannel].maximum_correlation_coefficient[i]= sqrt((double) -1.0); channel_features[BluePixelChannel].maximum_correlation_coefficient[i]= sqrt((double) -1.0); if (image->colorspace == CMYKColorspace) channel_features[BlackPixelChannel].maximum_correlation_coefficient[i]= sqrt((double) -1.0); if (image->alpha_trait != UndefinedPixelTrait) channel_features[AlphaPixelChannel].maximum_correlation_coefficient[i]= sqrt((double) -1.0); } /* Relinquish resources. */ sum=(ChannelStatistics *) RelinquishMagickMemory(sum); for (i=0; i < (ssize_t) number_grays; i++) Q[i]=(ChannelStatistics *) RelinquishMagickMemory(Q[i]); Q=(ChannelStatistics **) RelinquishMagickMemory(Q); density_y=(ChannelStatistics *) RelinquishMagickMemory(density_y); density_xy=(ChannelStatistics *) RelinquishMagickMemory(density_xy); density_x=(ChannelStatistics *) RelinquishMagickMemory(density_x); for (i=0; i < (ssize_t) number_grays; i++) cooccurrence[i]=(ChannelStatistics *) RelinquishMagickMemory(cooccurrence[i]); cooccurrence=(ChannelStatistics **) RelinquishMagickMemory(cooccurrence); return(channel_features); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % H o u g h L i n e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Use HoughLineImage() in conjunction with any binary edge extracted image (we % recommand Canny) to identify lines in the image. The algorithm accumulates % counts for every white pixel for every possible orientation (for angles from % 0 to 179 in 1 degree increments) and distance from the center of the image to % the corner (in 1 px increments) and stores the counts in an accumulator % matrix of angle vs distance. The size of the accumulator is 180x(diagonal/2). % Next it searches this space for peaks in counts and converts the locations % of the peaks to slope and intercept in the normal x,y input image space. Use % the slope/intercepts to find the endpoints clipped to the bounds of the % image. The lines are then drawn. The counts are a measure of the length of % the lines. % % The format of the HoughLineImage method is: % % Image *HoughLineImage(const Image *image,const size_t width, % const size_t height,const size_t threshold,ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o width, height: find line pairs as local maxima in this neighborhood. % % o threshold: the line count threshold. % % o exception: return any errors or warnings in this structure. % */ static inline double MagickRound(double x) { /* Round the fraction to nearest integer. */ if ((x-floor(x)) < (ceil(x)-x)) return(floor(x)); return(ceil(x)); } static Image *RenderHoughLines(const ImageInfo *image_info,const size_t columns, const size_t rows,ExceptionInfo *exception) { #define BoundingBox "viewbox" DrawInfo *draw_info; Image *image; MagickBooleanType status; /* Open image. */ image=AcquireImage(image_info,exception); status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); if (status == MagickFalse) { image=DestroyImageList(image); return((Image *) NULL); } image->columns=columns; image->rows=rows; draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL); draw_info->affine.sx=image->resolution.x == 0.0 ? 1.0 : image->resolution.x/ DefaultResolution; draw_info->affine.sy=image->resolution.y == 0.0 ? 1.0 : image->resolution.y/ DefaultResolution; image->columns=(size_t) (draw_info->affine.sx*image->columns); image->rows=(size_t) (draw_info->affine.sy*image->rows); status=SetImageExtent(image,image->columns,image->rows,exception); if (status == MagickFalse) return(DestroyImageList(image)); if (SetImageBackgroundColor(image,exception) == MagickFalse) { image=DestroyImageList(image); return((Image *) NULL); } /* Render drawing. */ if (GetBlobStreamData(image) == (unsigned char *) NULL) draw_info->primitive=FileToString(image->filename,~0UL,exception); else { draw_info->primitive=(char *) AcquireMagickMemory((size_t) GetBlobSize(image)+1); if (draw_info->primitive != (char *) NULL) { (void) memcpy(draw_info->primitive,GetBlobStreamData(image), (size_t) GetBlobSize(image)); draw_info->primitive[GetBlobSize(image)]='\0'; } } (void) DrawImage(image,draw_info,exception); draw_info=DestroyDrawInfo(draw_info); (void) CloseBlob(image); return(GetFirstImageInList(image)); } MagickExport Image *HoughLineImage(const Image *image,const size_t width, const size_t height,const size_t threshold,ExceptionInfo *exception) { #define HoughLineImageTag "HoughLine/Image" CacheView *image_view; char message[MagickPathExtent], path[MagickPathExtent]; const char *artifact; double hough_height; Image *lines_image = NULL; ImageInfo *image_info; int file; MagickBooleanType status; MagickOffsetType progress; MatrixInfo *accumulator; PointInfo center; register ssize_t y; size_t accumulator_height, accumulator_width, line_count; /* Create the accumulator. */ assert(image != (const Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); accumulator_width=180; hough_height=((sqrt(2.0)*(double) (image->rows > image->columns ? image->rows : image->columns))/2.0); accumulator_height=(size_t) (2.0*hough_height); accumulator=AcquireMatrixInfo(accumulator_width,accumulator_height, sizeof(double),exception); if (accumulator == (MatrixInfo *) NULL) ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); if (NullMatrix(accumulator) == MagickFalse) { accumulator=DestroyMatrixInfo(accumulator); ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); } /* Populate the accumulator. */ status=MagickTrue; progress=0; center.x=(double) image->columns/2.0; center.y=(double) image->rows/2.0; image_view=AcquireVirtualCacheView(image,exception); for (y=0; y < (ssize_t) image->rows; y++) { register const Quantum *magick_restrict p; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); if (p == (Quantum *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) image->columns; x++) { if (GetPixelIntensity(image,p) > (QuantumRange/2.0)) { register ssize_t i; for (i=0; i < 180; i++) { double count, radius; radius=(((double) x-center.x)*cos(DegreesToRadians((double) i)))+ (((double) y-center.y)*sin(DegreesToRadians((double) i))); (void) GetMatrixElement(accumulator,i,(ssize_t) MagickRound(radius+hough_height),&count); count++; (void) SetMatrixElement(accumulator,i,(ssize_t) MagickRound(radius+hough_height),&count); } } p+=GetPixelChannels(image); } if (image->progress_monitor != (MagickProgressMonitor) NULL) { MagickBooleanType proceed; #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp atomic #endif progress++; proceed=SetImageProgress(image,CannyEdgeImageTag,progress,image->rows); if (proceed == MagickFalse) status=MagickFalse; } } image_view=DestroyCacheView(image_view); if (status == MagickFalse) { accumulator=DestroyMatrixInfo(accumulator); return((Image *) NULL); } /* Generate line segments from accumulator. */ file=AcquireUniqueFileResource(path); if (file == -1) { accumulator=DestroyMatrixInfo(accumulator); return((Image *) NULL); } (void) FormatLocaleString(message,MagickPathExtent, "# Hough line transform: %.20gx%.20g%+.20g\n",(double) width, (double) height,(double) threshold); if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) status=MagickFalse; (void) FormatLocaleString(message,MagickPathExtent, "viewbox 0 0 %.20g %.20g\n",(double) image->columns,(double) image->rows); if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) status=MagickFalse; (void) FormatLocaleString(message,MagickPathExtent, "# x1,y1 x2,y2 # count angle distance\n"); if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) status=MagickFalse; line_count=image->columns > image->rows ? image->columns/4 : image->rows/4; if (threshold != 0) line_count=threshold; for (y=0; y < (ssize_t) accumulator_height; y++) { register ssize_t x; for (x=0; x < (ssize_t) accumulator_width; x++) { double count; (void) GetMatrixElement(accumulator,x,y,&count); if (count >= (double) line_count) { double maxima; SegmentInfo line; ssize_t v; /* Is point a local maxima? */ maxima=count; for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++) { ssize_t u; for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++) { if ((u != 0) || (v !=0)) { (void) GetMatrixElement(accumulator,x+u,y+v,&count); if (count > maxima) { maxima=count; break; } } } if (u < (ssize_t) (width/2)) break; } (void) GetMatrixElement(accumulator,x,y,&count); if (maxima > count) continue; if ((x >= 45) && (x <= 135)) { /* y = (r-x cos(t))/sin(t) */ line.x1=0.0; line.y1=((double) (y-(accumulator_height/2.0))-((line.x1- (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ sin(DegreesToRadians((double) x))+(image->rows/2.0); line.x2=(double) image->columns; line.y2=((double) (y-(accumulator_height/2.0))-((line.x2- (image->columns/2.0))*cos(DegreesToRadians((double) x))))/ sin(DegreesToRadians((double) x))+(image->rows/2.0); } else { /* x = (r-y cos(t))/sin(t) */ line.y1=0.0; line.x1=((double) (y-(accumulator_height/2.0))-((line.y1- (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ cos(DegreesToRadians((double) x))+(image->columns/2.0); line.y2=(double) image->rows; line.x2=((double) (y-(accumulator_height/2.0))-((line.y2- (image->rows/2.0))*sin(DegreesToRadians((double) x))))/ cos(DegreesToRadians((double) x))+(image->columns/2.0); } (void) FormatLocaleString(message,MagickPathExtent, "line %g,%g %g,%g # %g %g %g\n",line.x1,line.y1,line.x2,line.y2, maxima,(double) x,(double) y); if (write(file,message,strlen(message)) != (ssize_t) strlen(message)) status=MagickFalse; } } } (void) close(file); /* Render lines to image canvas. */ image_info=AcquireImageInfo(); image_info->background_color=image->background_color; (void) FormatLocaleString(image_info->filename,MagickPathExtent,"%s",path); artifact=GetImageArtifact(image,"background"); if (artifact != (const char *) NULL) (void) SetImageOption(image_info,"background",artifact); artifact=GetImageArtifact(image,"fill"); if (artifact != (const char *) NULL) (void) SetImageOption(image_info,"fill",artifact); artifact=GetImageArtifact(image,"stroke"); if (artifact != (const char *) NULL) (void) SetImageOption(image_info,"stroke",artifact); artifact=GetImageArtifact(image,"strokewidth"); if (artifact != (const char *) NULL) (void) SetImageOption(image_info,"strokewidth",artifact); lines_image=RenderHoughLines(image_info,image->columns,image->rows,exception); artifact=GetImageArtifact(image,"hough-lines:accumulator"); if ((lines_image != (Image *) NULL) && (IsStringTrue(artifact) != MagickFalse)) { Image *accumulator_image; accumulator_image=MatrixToImage(accumulator,exception); if (accumulator_image != (Image *) NULL) AppendImageToList(&lines_image,accumulator_image); } /* Free resources. */ accumulator=DestroyMatrixInfo(accumulator); image_info=DestroyImageInfo(image_info); (void) RelinquishUniqueFileResource(path); return(GetFirstImageInList(lines_image)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M e a n S h i f t I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % MeanShiftImage() delineate arbitrarily shaped clusters in the image. For % each pixel, it visits all the pixels in the neighborhood specified by % the window centered at the pixel and excludes those that are outside the % radius=(window-1)/2 surrounding the pixel. From those pixels, it finds those % that are within the specified color distance from the current mean, and % computes a new x,y centroid from those coordinates and a new mean. This new % x,y centroid is used as the center for a new window. This process iterates % until it converges and the final mean is replaces the (original window % center) pixel value. It repeats this process for the next pixel, etc., % until it processes all pixels in the image. Results are typically better with % colorspaces other than sRGB. We recommend YIQ, YUV or YCbCr. % % The format of the MeanShiftImage method is: % % Image *MeanShiftImage(const Image *image,const size_t width, % const size_t height,const double color_distance, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: the image. % % o width, height: find pixels in this neighborhood. % % o color_distance: the color distance. % % o exception: return any errors or warnings in this structure. % */ MagickExport Image *MeanShiftImage(const Image *image,const size_t width, const size_t height,const double color_distance,ExceptionInfo *exception) { #define MaxMeanShiftIterations 100 #define MeanShiftImageTag "MeanShift/Image" CacheView *image_view, *mean_view, *pixel_view; Image *mean_image; MagickBooleanType status; MagickOffsetType progress; ssize_t y; assert(image != (const Image *) NULL); assert(image->signature == MagickCoreSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(exception != (ExceptionInfo *) NULL); assert(exception->signature == MagickCoreSignature); mean_image=CloneImage(image,0,0,MagickTrue,exception); if (mean_image == (Image *) NULL) return((Image *) NULL); if (SetImageStorageClass(mean_image,DirectClass,exception) == MagickFalse) { mean_image=DestroyImage(mean_image); return((Image *) NULL); } status=MagickTrue; progress=0; image_view=AcquireVirtualCacheView(image,exception); pixel_view=AcquireVirtualCacheView(image,exception); mean_view=AcquireAuthenticCacheView(mean_image,exception); #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static) shared(status,progress) \ magick_number_threads(mean_image,mean_image,mean_image->rows,1) #endif for (y=0; y < (ssize_t) mean_image->rows; y++) { register const Quantum *magick_restrict p; register Quantum *magick_restrict q; register ssize_t x; if (status == MagickFalse) continue; p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); q=GetCacheViewAuthenticPixels(mean_view,0,y,mean_image->columns,1, exception); if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) mean_image->columns; x++) { PixelInfo mean_pixel, previous_pixel; PointInfo mean_location, previous_location; register ssize_t i; GetPixelInfo(image,&mean_pixel); GetPixelInfoPixel(image,p,&mean_pixel); mean_location.x=(double) x; mean_location.y=(double) y; for (i=0; i < MaxMeanShiftIterations; i++) { double distance, gamma; PixelInfo sum_pixel; PointInfo sum_location; ssize_t count, v; sum_location.x=0.0; sum_location.y=0.0; GetPixelInfo(image,&sum_pixel); previous_location=mean_location; previous_pixel=mean_pixel; count=0; for (v=(-((ssize_t) height/2)); v <= (((ssize_t) height/2)); v++) { ssize_t u; for (u=(-((ssize_t) width/2)); u <= (((ssize_t) width/2)); u++) { if ((v*v+u*u) <= (ssize_t) ((width/2)*(height/2))) { PixelInfo pixel; status=GetOneCacheViewVirtualPixelInfo(pixel_view,(ssize_t) MagickRound(mean_location.x+u),(ssize_t) MagickRound( mean_location.y+v),&pixel,exception); distance=(mean_pixel.red-pixel.red)*(mean_pixel.red-pixel.red)+ (mean_pixel.green-pixel.green)*(mean_pixel.green-pixel.green)+ (mean_pixel.blue-pixel.blue)*(mean_pixel.blue-pixel.blue); if (distance <= (color_distance*color_distance)) { sum_location.x+=mean_location.x+u; sum_location.y+=mean_location.y+v; sum_pixel.red+=pixel.red; sum_pixel.green+=pixel.green; sum_pixel.blue+=pixel.blue; sum_pixel.alpha+=pixel.alpha; count++; } } } } gamma=1.0/count; mean_location.x=gamma*sum_location.x; mean_location.y=gamma*sum_location.y; mean_pixel.red=gamma*sum_pixel.red; mean_pixel.green=gamma*sum_pixel.green; mean_pixel.blue=gamma*sum_pixel.blue; mean_pixel.alpha=gamma*sum_pixel.alpha; distance=(mean_location.x-previous_location.x)* (mean_location.x-previous_location.x)+ (mean_location.y-previous_location.y)* (mean_location.y-previous_location.y)+ 255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)* 255.0*QuantumScale*(mean_pixel.red-previous_pixel.red)+ 255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)* 255.0*QuantumScale*(mean_pixel.green-previous_pixel.green)+ 255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue)* 255.0*QuantumScale*(mean_pixel.blue-previous_pixel.blue); if (distance <= 3.0) break; } SetPixelRed(mean_image,ClampToQuantum(mean_pixel.red),q); SetPixelGreen(mean_image,ClampToQuantum(mean_pixel.green),q); SetPixelBlue(mean_image,ClampToQuantum(mean_pixel.blue),q); SetPixelAlpha(mean_image,ClampToQuantum(mean_pixel.alpha),q); p+=GetPixelChannels(image); q+=GetPixelChannels(mean_image); } if (SyncCacheViewAuthenticPixels(mean_view,exception) == MagickFalse) status=MagickFalse; if (image->progress_monitor != (MagickProgressMonitor) NULL) { MagickBooleanType proceed; #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp atomic #endif progress++; proceed=SetImageProgress(image,MeanShiftImageTag,progress,image->rows); if (proceed == MagickFalse) status=MagickFalse; } } mean_view=DestroyCacheView(mean_view); pixel_view=DestroyCacheView(pixel_view); image_view=DestroyCacheView(image_view); return(mean_image); } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-15939/000077500000000000000000000000001417746362400206415ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-15939/cmd.txt000066400000000000000000000000361417746362400221440ustar00rootroot00000000000000-DCV_EXPORTS_W= -DCV_EXPORTS= cppcheck-2.7/test/bug-hunting/cve/CVE-2019-15939/expected.txt000066400000000000000000000001751417746362400232060ustar00rootroot00000000000000hog.cpp:92:bughuntingDivByZero hog.cpp:93:bughuntingDivByZero hog.cpp:94:bughuntingDivByZero hog.cpp:95:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-15939/hog.cpp000066400000000000000000005454601417746362400221400ustar00rootroot00000000000000/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "precomp.hpp" #include "cascadedetect.hpp" #include "opencv2/core/core_c.h" #include "opencv2/core/hal/intrin.hpp" #include "opencl_kernels_objdetect.hpp" #include #include #include /****************************************************************************************\ The code below is implementation of HOG (Histogram-of-Oriented Gradients) descriptor and object detection, introduced by Navneet Dalal and Bill Triggs. The computed feature vectors are compatible with the INRIA Object Detection and Localization Toolkit (http://pascal.inrialpes.fr/soft/olt/) \****************************************************************************************/ namespace cv { #define NTHREADS 256 enum {DESCR_FORMAT_COL_BY_COL, DESCR_FORMAT_ROW_BY_ROW}; static int numPartsWithin(int size, int part_size, int stride) { CV_Assert(stride != 0); return (size - part_size + stride) / stride; } static Size numPartsWithin(cv::Size size, cv::Size part_size, cv::Size stride) { return Size(numPartsWithin(size.width, part_size.width, stride.width), numPartsWithin(size.height, part_size.height, stride.height)); } static size_t getBlockHistogramSize(Size block_size, Size cell_size, int nbins) { CV_Assert(!cell_size.empty()); Size cells_per_block = Size(block_size.width / cell_size.width, block_size.height / cell_size.height); return (size_t)(nbins * cells_per_block.area()); } size_t HOGDescriptor::getDescriptorSize() const { CV_Assert(blockSize.width % cellSize.width == 0 && blockSize.height % cellSize.height == 0); CV_Assert((winSize.width - blockSize.width) % blockStride.width == 0 && (winSize.height - blockSize.height) % blockStride.height == 0 ); return (size_t)nbins* (blockSize.width/cellSize.width)* (blockSize.height/cellSize.height)* ((winSize.width - blockSize.width)/blockStride.width + 1)* ((winSize.height - blockSize.height)/blockStride.height + 1); } double HOGDescriptor::getWinSigma() const { return winSigma > 0 ? winSigma : (blockSize.width + blockSize.height)/8.; } bool HOGDescriptor::checkDetectorSize() const { size_t detectorSize = svmDetector.size(), descriptorSize = getDescriptorSize(); return detectorSize == 0 || detectorSize == descriptorSize || detectorSize == descriptorSize + 1; } void HOGDescriptor::setSVMDetector(InputArray _svmDetector) { _svmDetector.getMat().convertTo(svmDetector, CV_32F); CV_Assert(checkDetectorSize()); Mat detector_reordered(1, (int)svmDetector.size(), CV_32FC1); size_t block_hist_size = getBlockHistogramSize(blockSize, cellSize, nbins); cv::Size blocks_per_img = numPartsWithin(winSize, blockSize, blockStride); for (int i = 0; i < blocks_per_img.height; ++i) for (int j = 0; j < blocks_per_img.width; ++j) { const float *src = &svmDetector[0] + (j * blocks_per_img.height + i) * block_hist_size; float *dst = detector_reordered.ptr() + (i * blocks_per_img.width + j) * block_hist_size; for (size_t k = 0; k < block_hist_size; ++k) dst[k] = src[k]; } size_t descriptor_size = getDescriptorSize(); free_coef = svmDetector.size() > descriptor_size ? svmDetector[descriptor_size] : 0; detector_reordered.copyTo(oclSvmDetector); } #define CV_TYPE_NAME_HOG_DESCRIPTOR "opencv-object-detector-hog" bool HOGDescriptor::read(FileNode& obj) { CV_Assert(!obj["winSize"].empty()); if (!obj.isMap()) return false; FileNodeIterator it = obj["winSize"].begin(); it >> winSize.width >> winSize.height; CV_Assert(!winSize.empty()); it = obj["blockSize"].begin(); it >> blockSize.width >> blockSize.height; CV_Assert(!blockSize.empty()); it = obj["blockStride"].begin(); it >> blockStride.width >> blockStride.height; CV_Assert(!blockStride.empty()); it = obj["cellSize"].begin(); it >> cellSize.width >> cellSize.height; CV_Assert(!cellSize.empty()); obj["nbins"] >> nbins; CV_Assert(nbins > 0); obj["derivAperture"] >> derivAperture; obj["winSigma"] >> winSigma; obj["histogramNormType"] >> histogramNormType; obj["L2HysThreshold"] >> L2HysThreshold; obj["gammaCorrection"] >> gammaCorrection; obj["nlevels"] >> nlevels; CV_Assert(nlevels > 0); if (obj["signedGradient"].empty()) signedGradient = false; else obj["signedGradient"] >> signedGradient; FileNode vecNode = obj["SVMDetector"]; if (vecNode.isSeq()) { std::vector _svmDetector; vecNode >> _svmDetector; setSVMDetector(_svmDetector); } return true; } void HOGDescriptor::write(FileStorage& fs, const String& objName) const { if (!objName.empty()) fs << objName; fs << "{" CV_TYPE_NAME_HOG_DESCRIPTOR << "winSize" << winSize << "blockSize" << blockSize << "blockStride" << blockStride << "cellSize" << cellSize << "nbins" << nbins << "derivAperture" << derivAperture << "winSigma" << getWinSigma() << "histogramNormType" << histogramNormType << "L2HysThreshold" << L2HysThreshold << "gammaCorrection" << gammaCorrection << "nlevels" << nlevels << "signedGradient" << signedGradient; if (!svmDetector.empty()) fs << "SVMDetector" << svmDetector; fs << "}"; } bool HOGDescriptor::load(const String& filename, const String& objname) { FileStorage fs(filename, FileStorage::READ); FileNode obj = !objname.empty() ? fs[objname] : fs.getFirstTopLevelNode(); return read(obj); } void HOGDescriptor::save(const String& filename, const String& objName) const { FileStorage fs(filename, FileStorage::WRITE); write(fs, !objName.empty() ? objName : FileStorage::getDefaultObjectName(filename)); } void HOGDescriptor::copyTo(HOGDescriptor& c) const { c.winSize = winSize; c.blockSize = blockSize; c.blockStride = blockStride; c.cellSize = cellSize; c.nbins = nbins; c.derivAperture = derivAperture; c.winSigma = winSigma; c.histogramNormType = histogramNormType; c.L2HysThreshold = L2HysThreshold; c.gammaCorrection = gammaCorrection; c.setSVMDetector(svmDetector); c.nlevels = nlevels; c.signedGradient = signedGradient; } void HOGDescriptor::computeGradient(const Mat& img, Mat& grad, Mat& qangle, Size paddingTL, Size paddingBR) const { CV_INSTRUMENT_REGION(); CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 ); Size gradsize(img.cols + paddingTL.width + paddingBR.width, img.rows + paddingTL.height + paddingBR.height); grad.create(gradsize, CV_32FC2); // qangle.create(gradsize, CV_8UC2); // [0..nbins-1] - quantized gradient orientation Size wholeSize; Point roiofs; img.locateROI(wholeSize, roiofs); int i, x, y; int cn = img.channels(); Mat_ _lut(1, 256); const float* const lut = &_lut(0,0); #if CV_SIMD128 v_float32x4 idx(0.0f, 1.0f, 2.0f, 3.0f); v_float32x4 ifour = v_setall_f32(4.0); float* const _data = &_lut(0, 0); if (gammaCorrection) for (i = 0; i < 256; i += 4) { v_store(_data + i, v_sqrt(idx)); idx += ifour; } else for (i = 0; i < 256; i += 4) { v_store(_data + i, idx); idx += ifour; } #else if (gammaCorrection) for (i = 0; i < 256; i++) _lut(0,i) = std::sqrt((float)i); else for (i = 0; i < 256; i++) _lut(0,i) = (float)i; #endif AutoBuffer mapbuf(gradsize.width + gradsize.height + 4); int* xmap = mapbuf.data() + 1; int* ymap = xmap + gradsize.width + 2; const int borderType = (int)BORDER_REFLECT_101; for (x = -1; x < gradsize.width + 1; x++) xmap[x] = borderInterpolate(x - paddingTL.width + roiofs.x, wholeSize.width, borderType) - roiofs.x; for (y = -1; y < gradsize.height + 1; y++) ymap[y] = borderInterpolate(y - paddingTL.height + roiofs.y, wholeSize.height, borderType) - roiofs.y; // x- & y- derivatives for the whole row int width = gradsize.width; AutoBuffer _dbuf(width*4); float* const dbuf = _dbuf.data(); Mat Dx(1, width, CV_32F, dbuf); Mat Dy(1, width, CV_32F, dbuf + width); Mat Mag(1, width, CV_32F, dbuf + width*2); Mat Angle(1, width, CV_32F, dbuf + width*3); if (cn == 3) { int end = gradsize.width + 2; xmap -= 1, x = 0; #if CV_SIMD128 for ( ; x <= end - 4; x += 4) { v_int32x4 mul_res = v_load(xmap + x); mul_res += mul_res + mul_res; v_store(xmap + x, mul_res); } #endif for ( ; x < end; ++x) xmap[x] *= 3; xmap += 1; } float angleScale = signedGradient ? (float)(nbins/(2.0*CV_PI)) : (float)(nbins/CV_PI); for (y = 0; y < gradsize.height; y++) { const uchar* imgPtr = img.ptr(ymap[y]); //In case subimage is used ptr() generates an assert for next and prev rows //(see http://code.opencv.org/issues/4149) const uchar* prevPtr = img.data + img.step*ymap[y-1]; const uchar* nextPtr = img.data + img.step*ymap[y+1]; float* gradPtr = grad.ptr(y); uchar* qanglePtr = qangle.ptr(y); if (cn == 1) { for (x = 0; x < width; x++) { int x1 = xmap[x]; dbuf[x] = (float)(lut[imgPtr[xmap[x+1]]] - lut[imgPtr[xmap[x-1]]]); dbuf[width + x] = (float)(lut[nextPtr[x1]] - lut[prevPtr[x1]]); } } else { x = 0; #if CV_SIMD128 for ( ; x <= width - 4; x += 4) { int x0 = xmap[x], x1 = xmap[x+1], x2 = xmap[x+2], x3 = xmap[x+3]; typedef const uchar* const T; T p02 = imgPtr + xmap[x+1], p00 = imgPtr + xmap[x-1]; T p12 = imgPtr + xmap[x+2], p10 = imgPtr + xmap[x]; T p22 = imgPtr + xmap[x+3], p20 = p02; T p32 = imgPtr + xmap[x+4], p30 = p12; v_float32x4 _dx0 = v_float32x4(lut[p02[0]], lut[p12[0]], lut[p22[0]], lut[p32[0]]) - v_float32x4(lut[p00[0]], lut[p10[0]], lut[p20[0]], lut[p30[0]]); v_float32x4 _dx1 = v_float32x4(lut[p02[1]], lut[p12[1]], lut[p22[1]], lut[p32[1]]) - v_float32x4(lut[p00[1]], lut[p10[1]], lut[p20[1]], lut[p30[1]]); v_float32x4 _dx2 = v_float32x4(lut[p02[2]], lut[p12[2]], lut[p22[2]], lut[p32[2]]) - v_float32x4(lut[p00[2]], lut[p10[2]], lut[p20[2]], lut[p30[2]]); v_float32x4 _dy0 = v_float32x4(lut[nextPtr[x0]], lut[nextPtr[x1]], lut[nextPtr[x2]], lut[nextPtr[x3]]) - v_float32x4(lut[prevPtr[x0]], lut[prevPtr[x1]], lut[prevPtr[x2]], lut[prevPtr[x3]]); v_float32x4 _dy1 = v_float32x4(lut[nextPtr[x0+1]], lut[nextPtr[x1+1]], lut[nextPtr[x2+1]], lut[nextPtr[x3+1]]) - v_float32x4(lut[prevPtr[x0+1]], lut[prevPtr[x1+1]], lut[prevPtr[x2+1]], lut[prevPtr[x3+1]]); v_float32x4 _dy2 = v_float32x4(lut[nextPtr[x0+2]], lut[nextPtr[x1+2]], lut[nextPtr[x2+2]], lut[nextPtr[x3+2]]) - v_float32x4(lut[prevPtr[x0+2]], lut[prevPtr[x1+2]], lut[prevPtr[x2+2]], lut[prevPtr[x3+2]]); v_float32x4 _mag0 = (_dx0 * _dx0) + (_dy0 * _dy0); v_float32x4 _mag1 = (_dx1 * _dx1) + (_dy1 * _dy1); v_float32x4 _mag2 = (_dx2 * _dx2) + (_dy2 * _dy2); v_float32x4 mask = v_reinterpret_as_f32(_mag2 > _mag1); _dx2 = v_select(mask, _dx2, _dx1); _dy2 = v_select(mask, _dy2, _dy1); mask = v_reinterpret_as_f32(v_max(_mag2, _mag1) > _mag0); _dx2 = v_select(mask, _dx2, _dx0); _dy2 = v_select(mask, _dy2, _dy0); v_store(dbuf + x, _dx2); v_store(dbuf + x + width, _dy2); } #endif for ( ; x < width; x++) { int x1 = xmap[x]; float dx0, dy0, dx, dy, mag0, mag; const uchar* p2 = imgPtr + xmap[x+1]; const uchar* p0 = imgPtr + xmap[x-1]; dx0 = lut[p2[2]] - lut[p0[2]]; dy0 = lut[nextPtr[x1+2]] - lut[prevPtr[x1+2]]; mag0 = dx0*dx0 + dy0*dy0; dx = lut[p2[1]] - lut[p0[1]]; dy = lut[nextPtr[x1+1]] - lut[prevPtr[x1+1]]; mag = dx*dx + dy*dy; if (mag0 < mag) { dx0 = dx; dy0 = dy; mag0 = mag; } dx = lut[p2[0]] - lut[p0[0]]; dy = lut[nextPtr[x1]] - lut[prevPtr[x1]]; mag = dx*dx + dy*dy; if (mag0 < mag) { dx0 = dx; dy0 = dy; mag0 = mag; } dbuf[x] = dx0; dbuf[x+width] = dy0; } } // computing angles and magnidutes cartToPolar( Dx, Dy, Mag, Angle, false ); // filling the result matrix x = 0; #if CV_SIMD128 v_float32x4 fhalf = v_setall_f32(0.5f); v_float32x4 _angleScale = v_setall_f32(angleScale), fone = v_setall_f32(1.0f); v_int32x4 ione = v_setall_s32(1), _nbins = v_setall_s32(nbins), izero = v_setzero_s32(); for ( ; x <= width - 4; x += 4) { int x2 = x << 1; v_float32x4 _mag = v_load(dbuf + x + (width << 1)); v_float32x4 _angle = v_load(dbuf + x + width * 3); _angle = (_angleScale * _angle) - fhalf; v_int32x4 _hidx = v_floor(_angle); _angle -= v_cvt_f32(_hidx); v_float32x4 ft0 = _mag * (fone - _angle); v_float32x4 ft1 = _mag * _angle; v_store_interleave(gradPtr + x2, ft0, ft1); v_int32x4 mask0 = _hidx >> 31; v_int32x4 it0 = mask0 & _nbins; mask0 = (_hidx >= _nbins); v_int32x4 it1 = mask0 & _nbins; _hidx += (it0 - it1); it0 = v_reinterpret_as_s32(v_pack(v_pack(_hidx, izero), v_reinterpret_as_s16(izero))); _hidx += ione; _hidx &= (_hidx < _nbins); it1 = v_reinterpret_as_s32(v_pack(v_pack(_hidx, izero), v_reinterpret_as_s16(izero))); v_uint8x16 it2, it3; v_zip(v_reinterpret_as_u8(it0), v_reinterpret_as_u8(it1), it2, it3); v_store_low(qanglePtr + x2, it2); } #endif for ( ; x < width; x++) { float mag = dbuf[x+width*2], angle = dbuf[x+width*3]*angleScale - 0.5f; int hidx = cvFloor(angle); angle -= hidx; gradPtr[x*2] = mag*(1.f - angle); gradPtr[x*2+1] = mag*angle; if (hidx < 0) hidx += nbins; else if (hidx >= nbins) hidx -= nbins; CV_Assert((unsigned)hidx < (unsigned)nbins ); qanglePtr[x*2] = (uchar)hidx; hidx++; hidx &= hidx < nbins ? -1 : 0; qanglePtr[x*2+1] = (uchar)hidx; } } } struct HOGCache { struct BlockData { BlockData() : histOfs(0), imgOffset() {} int histOfs; Point imgOffset; }; struct PixData { size_t gradOfs, qangleOfs; int histOfs[4]; float histWeights[4]; float gradWeight; }; HOGCache(); HOGCache(const HOGDescriptor* descriptor, const Mat& img, const Size& paddingTL, const Size& paddingBR, bool useCache, const Size& cacheStride); virtual ~HOGCache() {} virtual void init(const HOGDescriptor* descriptor, const Mat& img, const Size& paddingTL, const Size& paddingBR, bool useCache, const Size& cacheStride); Size windowsInImage(const Size& imageSize, const Size& winStride) const; Rect getWindow(const Size& imageSize, const Size& winStride, int idx) const; const float* getBlock(Point pt, float* buf); virtual void normalizeBlockHistogram(float* histogram) const; std::vector pixData; std::vector blockData; bool useCache; std::vector ymaxCached; Size winSize; Size cacheStride; Size nblocks, ncells; int blockHistogramSize; int count1, count2, count4; Point imgoffset; Mat_ blockCache; Mat_ blockCacheFlags; Mat grad, qangle; const HOGDescriptor* descriptor; }; HOGCache::HOGCache() : blockHistogramSize(), count1(), count2(), count4() { useCache = false; descriptor = 0; } HOGCache::HOGCache(const HOGDescriptor* _descriptor, const Mat& _img, const Size& _paddingTL, const Size& _paddingBR, bool _useCache, const Size& _cacheStride) { init(_descriptor, _img, _paddingTL, _paddingBR, _useCache, _cacheStride); } void HOGCache::init(const HOGDescriptor* _descriptor, const Mat& _img, const Size& _paddingTL, const Size& _paddingBR, bool _useCache, const Size& _cacheStride) { descriptor = _descriptor; cacheStride = _cacheStride; useCache = _useCache; descriptor->computeGradient(_img, grad, qangle, _paddingTL, _paddingBR); imgoffset = _paddingTL; winSize = descriptor->winSize; Size blockSize = descriptor->blockSize; Size blockStride = descriptor->blockStride; Size cellSize = descriptor->cellSize; int i, j, nbins = descriptor->nbins; int rawBlockSize = blockSize.width*blockSize.height; nblocks = Size((winSize.width - blockSize.width)/blockStride.width + 1, (winSize.height - blockSize.height)/blockStride.height + 1); ncells = Size(blockSize.width/cellSize.width, blockSize.height/cellSize.height); blockHistogramSize = ncells.width*ncells.height*nbins; if (useCache) { Size cacheSize((grad.cols - blockSize.width)/cacheStride.width+1, (winSize.height/cacheStride.height)+1); blockCache.create(cacheSize.height, cacheSize.width*blockHistogramSize); blockCacheFlags.create(cacheSize); size_t cacheRows = blockCache.rows; ymaxCached.resize(cacheRows); for (size_t ii = 0; ii < cacheRows; ii++) ymaxCached[ii] = -1; } Mat_ weights(blockSize); float sigma = (float)descriptor->getWinSigma(); float scale = 1.f/(sigma*sigma*2); { AutoBuffer di(blockSize.height), dj(blockSize.width); float* _di = di.data(), *_dj = dj.data(); float bh = blockSize.height * 0.5f, bw = blockSize.width * 0.5f; i = 0; #if CV_SIMD128 v_float32x4 idx(0.0f, 1.0f, 2.0f, 3.0f); v_float32x4 _bw = v_setall_f32(bw), _bh = v_setall_f32(bh); v_float32x4 ifour = v_setall_f32(4.0); for (; i <= blockSize.height - 4; i += 4) { v_float32x4 t = idx - _bh; t *= t; idx += ifour; v_store(_di + i, t); } #endif for ( ; i < blockSize.height; ++i) { _di[i] = i - bh; _di[i] *= _di[i]; } j = 0; #if CV_SIMD128 idx = v_float32x4(0.0f, 1.0f, 2.0f, 3.0f); for (; j <= blockSize.height - 4; j += 4) { v_float32x4 t = idx - _bw; t *= t; idx += ifour; v_store(_dj + j, t); } #endif for ( ; j < blockSize.width; ++j) { _dj[j] = j - bw; _dj[j] *= _dj[j]; } for (i = 0; i < blockSize.height; i++) for (j = 0; j < blockSize.width; j++) weights(i,j) = std::exp(-(_di[i] + _dj[j])*scale); } blockData.resize(nblocks.width*nblocks.height); pixData.resize(rawBlockSize*3); // Initialize 2 lookup tables, pixData & blockData. // Here is why: // // The detection algorithm runs in 4 nested loops (at each pyramid layer): // loop over the windows within the input image // loop over the blocks within each window // loop over the cells within each block // loop over the pixels in each cell // // As each of the loops runs over a 2-dimensional array, // we could get 8(!) nested loops in total, which is very-very slow. // // To speed the things up, we do the following: // 1. loop over windows is unrolled in the HOGDescriptor::{compute|detect} methods; // inside we compute the current search window using getWindow() method. // Yes, it involves some overhead (function call + couple of divisions), // but it's tiny in fact. // 2. loop over the blocks is also unrolled. Inside we use pre-computed blockData[j] // to set up gradient and histogram pointers. // 3. loops over cells and pixels in each cell are merged // (since there is no overlap between cells, each pixel in the block is processed once) // and also unrolled. Inside we use PixData[k] to access the gradient values and // update the histogram // count1 = count2 = count4 = 0; for (j = 0; j < blockSize.width; j++) for (i = 0; i < blockSize.height; i++) { PixData* data = 0; float cellX = (j+0.5f)/cellSize.width - 0.5f; float cellY = (i+0.5f)/cellSize.height - 0.5f; int icellX0 = cvFloor(cellX); int icellY0 = cvFloor(cellY); int icellX1 = icellX0 + 1, icellY1 = icellY0 + 1; cellX -= icellX0; cellY -= icellY0; if ((unsigned)icellX0 < (unsigned)ncells.width && (unsigned)icellX1 < (unsigned)ncells.width) { if ((unsigned)icellY0 < (unsigned)ncells.height && (unsigned)icellY1 < (unsigned)ncells.height) { data = &pixData[rawBlockSize*2 + (count4++)]; data->histOfs[0] = (icellX0*ncells.height + icellY0)*nbins; data->histWeights[0] = (1.f - cellX)*(1.f - cellY); data->histOfs[1] = (icellX1*ncells.height + icellY0)*nbins; data->histWeights[1] = cellX*(1.f - cellY); data->histOfs[2] = (icellX0*ncells.height + icellY1)*nbins; data->histWeights[2] = (1.f - cellX)*cellY; data->histOfs[3] = (icellX1*ncells.height + icellY1)*nbins; data->histWeights[3] = cellX*cellY; } else { data = &pixData[rawBlockSize + (count2++)]; if ((unsigned)icellY0 < (unsigned)ncells.height) { icellY1 = icellY0; cellY = 1.f - cellY; } data->histOfs[0] = (icellX0*ncells.height + icellY1)*nbins; data->histWeights[0] = (1.f - cellX)*cellY; data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; data->histWeights[1] = cellX*cellY; data->histOfs[2] = data->histOfs[3] = 0; data->histWeights[2] = data->histWeights[3] = 0; } } else { if ((unsigned)icellX0 < (unsigned)ncells.width) { icellX1 = icellX0; cellX = 1.f - cellX; } if ((unsigned)icellY0 < (unsigned)ncells.height && (unsigned)icellY1 < (unsigned)ncells.height) { data = &pixData[rawBlockSize + (count2++)]; data->histOfs[0] = (icellX1*ncells.height + icellY0)*nbins; data->histWeights[0] = cellX*(1.f - cellY); data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; data->histWeights[1] = cellX*cellY; data->histOfs[2] = data->histOfs[3] = 0; data->histWeights[2] = data->histWeights[3] = 0; } else { data = &pixData[count1++]; if ((unsigned)icellY0 < (unsigned)ncells.height) { icellY1 = icellY0; cellY = 1.f - cellY; } data->histOfs[0] = (icellX1*ncells.height + icellY1)*nbins; data->histWeights[0] = cellX*cellY; data->histOfs[1] = data->histOfs[2] = data->histOfs[3] = 0; data->histWeights[1] = data->histWeights[2] = data->histWeights[3] = 0; } } data->gradOfs = (grad.cols*i + j)*2; data->qangleOfs = (qangle.cols*i + j)*2; data->gradWeight = weights(i,j); } assert( count1 + count2 + count4 == rawBlockSize ); // defragment pixData for (j = 0; j < count2; j++) pixData[j + count1] = pixData[j + rawBlockSize]; for (j = 0; j < count4; j++) pixData[j + count1 + count2] = pixData[j + rawBlockSize*2]; count2 += count1; count4 += count2; // initialize blockData for (j = 0; j < nblocks.width; j++) for (i = 0; i < nblocks.height; i++) { BlockData& data = blockData[j*nblocks.height + i]; data.histOfs = (j*nblocks.height + i)*blockHistogramSize; data.imgOffset = Point(j*blockStride.width,i*blockStride.height); } } const float* HOGCache::getBlock(Point pt, float* buf) { float* blockHist = buf; assert(descriptor != 0); // Size blockSize = descriptor->blockSize; pt += imgoffset; // CV_Assert( (unsigned)pt.x <= (unsigned)(grad.cols - blockSize.width) && // (unsigned)pt.y <= (unsigned)(grad.rows - blockSize.height) ); if (useCache) { CV_Assert( pt.x % cacheStride.width == 0 && pt.y % cacheStride.height == 0 ); Point cacheIdx(pt.x/cacheStride.width, (pt.y/cacheStride.height) % blockCache.rows); if (pt.y != ymaxCached[cacheIdx.y]) { Mat_ cacheRow = blockCacheFlags.row(cacheIdx.y); cacheRow = (uchar)0; ymaxCached[cacheIdx.y] = pt.y; } blockHist = &blockCache[cacheIdx.y][cacheIdx.x*blockHistogramSize]; uchar& computedFlag = blockCacheFlags(cacheIdx.y, cacheIdx.x); if (computedFlag != 0) return blockHist; computedFlag = (uchar)1; // set it at once, before actual computing } int k, C1 = count1, C2 = count2, C4 = count4; const float* gradPtr = grad.ptr(pt.y) + pt.x*2; const uchar* qanglePtr = qangle.ptr(pt.y) + pt.x*2; // CV_Assert( blockHist != 0 ); memset(blockHist, 0, sizeof(float) * blockHistogramSize); const PixData* _pixData = &pixData[0]; for (k = 0; k < C1; k++) { const PixData& pk = _pixData[k]; const float* const a = gradPtr + pk.gradOfs; float w = pk.gradWeight*pk.histWeights[0]; const uchar* h = qanglePtr + pk.qangleOfs; int h0 = h[0], h1 = h[1]; float* hist = blockHist + pk.histOfs[0]; float t0 = hist[h0] + a[0]*w; float t1 = hist[h1] + a[1]*w; hist[h0] = t0; hist[h1] = t1; } #if CV_SIMD128 float hist0[4], hist1[4]; for ( ; k < C2; k++) { const PixData& pk = _pixData[k]; const float* const a = gradPtr + pk.gradOfs; const uchar* const h = qanglePtr + pk.qangleOfs; int h0 = h[0], h1 = h[1]; v_float32x4 _a0 = v_setall_f32(a[0]), _a1 = v_setall_f32(a[1]); v_float32x4 w = v_setall_f32(pk.gradWeight) * v_load(pk.histWeights); v_float32x4 _t0 = _a0 * w, _t1 = _a1 * w; v_store(hist0, _t0); v_store(hist1, _t1); float* hist = blockHist + pk.histOfs[0]; float t0 = hist[h0] + hist0[0]; float t1 = hist[h1] + hist1[0]; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[1]; t0 = hist[h0] + hist0[1]; t1 = hist[h1] + hist1[1]; hist[h0] = t0; hist[h1] = t1; } #else for ( ; k < C2; k++) { const PixData& pk = _pixData[k]; const float* const a = gradPtr + pk.gradOfs; float w, t0, t1, a0 = a[0], a1 = a[1]; const uchar* const h = qanglePtr + pk.qangleOfs; int h0 = h[0], h1 = h[1]; float* hist = blockHist + pk.histOfs[0]; w = pk.gradWeight*pk.histWeights[0]; t0 = hist[h0] + a0*w; t1 = hist[h1] + a1*w; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[1]; w = pk.gradWeight*pk.histWeights[1]; t0 = hist[h0] + a0*w; t1 = hist[h1] + a1*w; hist[h0] = t0; hist[h1] = t1; } #endif #if CV_SIMD128 for ( ; k < C4; k++) { const PixData& pk = _pixData[k]; const float* const a = gradPtr + pk.gradOfs; const uchar* const h = qanglePtr + pk.qangleOfs; int h0 = h[0], h1 = h[1]; v_float32x4 _a0 = v_setall_f32(a[0]), _a1 = v_setall_f32(a[1]); v_float32x4 w = v_setall_f32(pk.gradWeight) * v_load(pk.histWeights); v_float32x4 _t0 = _a0 * w, _t1 = _a1 * w; v_store(hist0, _t0); v_store(hist1, _t1); float* hist = blockHist + pk.histOfs[0]; float t0 = hist[h0] + hist0[0]; float t1 = hist[h1] + hist1[0]; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[1]; t0 = hist[h0] + hist0[1]; t1 = hist[h1] + hist1[1]; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[2]; t0 = hist[h0] + hist0[2]; t1 = hist[h1] + hist1[2]; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[3]; t0 = hist[h0] + hist0[3]; t1 = hist[h1] + hist1[3]; hist[h0] = t0; hist[h1] = t1; } #else for ( ; k < C4; k++) { const PixData& pk = _pixData[k]; const float* a = gradPtr + pk.gradOfs; float w, t0, t1, a0 = a[0], a1 = a[1]; const uchar* h = qanglePtr + pk.qangleOfs; int h0 = h[0], h1 = h[1]; float* hist = blockHist + pk.histOfs[0]; w = pk.gradWeight*pk.histWeights[0]; t0 = hist[h0] + a0*w; t1 = hist[h1] + a1*w; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[1]; w = pk.gradWeight*pk.histWeights[1]; t0 = hist[h0] + a0*w; t1 = hist[h1] + a1*w; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[2]; w = pk.gradWeight*pk.histWeights[2]; t0 = hist[h0] + a0*w; t1 = hist[h1] + a1*w; hist[h0] = t0; hist[h1] = t1; hist = blockHist + pk.histOfs[3]; w = pk.gradWeight*pk.histWeights[3]; t0 = hist[h0] + a0*w; t1 = hist[h1] + a1*w; hist[h0] = t0; hist[h1] = t1; } #endif normalizeBlockHistogram(blockHist); return blockHist; } void HOGCache::normalizeBlockHistogram(float* _hist) const { float* hist = &_hist[0], sum = 0.0f, partSum[4]; size_t i = 0, sz = blockHistogramSize; #if CV_SIMD128 v_float32x4 p0 = v_load(hist); v_float32x4 s = p0 * p0; for (i = 4; i <= sz - 4; i += 4) { p0 = v_load(hist + i); s += p0 * p0; } v_store(partSum, s); #else partSum[0] = 0.0f; partSum[1] = 0.0f; partSum[2] = 0.0f; partSum[3] = 0.0f; for ( ; i <= sz - 4; i += 4) { partSum[0] += hist[i] * hist[i]; partSum[1] += hist[i+1] * hist[i+1]; partSum[2] += hist[i+2] * hist[i+2]; partSum[3] += hist[i+3] * hist[i+3]; } #endif float t0 = partSum[0] + partSum[1]; float t1 = partSum[2] + partSum[3]; sum = t0 + t1; for ( ; i < sz; ++i) sum += hist[i]*hist[i]; float scale = 1.f/(std::sqrt(sum)+sz*0.1f), thresh = (float)descriptor->L2HysThreshold; i = 0, sum = 0.0f; #if CV_SIMD128 v_float32x4 _scale = v_setall_f32(scale); static v_float32x4 _threshold = v_setall_f32(thresh); v_float32x4 p = _scale * v_load(hist); p = v_min(p, _threshold); s = p * p; v_store(hist, p); for (i = 4; i <= sz - 4; i += 4) { p = v_load(hist + i); p *= _scale; p = v_min(p, _threshold); s += p * p; v_store(hist + i, p); } v_store(partSum, s); #else partSum[0] = 0.0f; partSum[1] = 0.0f; partSum[2] = 0.0f; partSum[3] = 0.0f; for ( ; i <= sz - 4; i += 4) { hist[i] = std::min(hist[i]*scale, thresh); hist[i+1] = std::min(hist[i+1]*scale, thresh); hist[i+2] = std::min(hist[i+2]*scale, thresh); hist[i+3] = std::min(hist[i+3]*scale, thresh); partSum[0] += hist[i]*hist[i]; partSum[1] += hist[i+1]*hist[i+1]; partSum[2] += hist[i+2]*hist[i+2]; partSum[3] += hist[i+3]*hist[i+3]; } #endif t0 = partSum[0] + partSum[1]; t1 = partSum[2] + partSum[3]; sum = t0 + t1; for ( ; i < sz; ++i) { hist[i] = std::min(hist[i]*scale, thresh); sum += hist[i]*hist[i]; } scale = 1.f/(std::sqrt(sum)+1e-3f), i = 0; #if CV_SIMD128 v_float32x4 _scale2 = v_setall_f32(scale); for ( ; i <= sz - 4; i += 4) { v_float32x4 t = _scale2 * v_load(hist + i); v_store(hist + i, t); } #endif for ( ; i < sz; ++i) hist[i] *= scale; } Size HOGCache::windowsInImage(const Size& imageSize, const Size& winStride) const { return Size((imageSize.width - winSize.width)/winStride.width + 1, (imageSize.height - winSize.height)/winStride.height + 1); } Rect HOGCache::getWindow(const Size& imageSize, const Size& winStride, int idx) const { int nwindowsX = (imageSize.width - winSize.width)/winStride.width + 1; int y = idx / nwindowsX; int x = idx - nwindowsX*y; return Rect( x*winStride.width, y*winStride.height, winSize.width, winSize.height ); } static inline int gcd(int a, int b) { if (a < b) std::swap(a, b); while (b > 0) { int r = a % b; a = b; b = r; } return a; } #ifdef HAVE_OPENCL static bool ocl_compute_gradients_8UC1(int height, int width, InputArray _img, float angle_scale, UMat grad, UMat qangle, bool correct_gamma, int nbins) { ocl::Kernel k("compute_gradients_8UC1_kernel", ocl::objdetect::objdetect_hog_oclsrc); if (k.empty()) return false; UMat img = _img.getUMat(); size_t localThreads[3] = { NTHREADS, 1, 1 }; size_t globalThreads[3] = { (size_t)width, (size_t)height, 1 }; char correctGamma = (correct_gamma) ? 1 : 0; int grad_quadstep = (int)grad.step >> 3; int qangle_elem_size = CV_ELEM_SIZE1(qangle.type()); int qangle_step = (int)qangle.step / (2 * qangle_elem_size); int idx = 0; idx = k.set(idx, height); idx = k.set(idx, width); idx = k.set(idx, (int)img.step1()); idx = k.set(idx, grad_quadstep); idx = k.set(idx, qangle_step); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(img)); idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(grad)); idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(qangle)); idx = k.set(idx, angle_scale); idx = k.set(idx, correctGamma); idx = k.set(idx, nbins); return k.run(2, globalThreads, localThreads, false); } static bool ocl_computeGradient(InputArray img, UMat grad, UMat qangle, int nbins, Size effect_size, bool gamma_correction, bool signedGradient) { float angleScale = signedGradient ? (float)(nbins/(2.0*CV_PI)) : (float)(nbins/CV_PI); return ocl_compute_gradients_8UC1(effect_size.height, effect_size.width, img, angleScale, grad, qangle, gamma_correction, nbins); } #define CELL_WIDTH 8 #define CELL_HEIGHT 8 #define CELLS_PER_BLOCK_X 2 #define CELLS_PER_BLOCK_Y 2 static bool ocl_compute_hists(int nbins, int block_stride_x, int block_stride_y, int height, int width, UMat grad, UMat qangle, UMat gauss_w_lut, UMat block_hists, size_t block_hist_size) { ocl::Kernel k("compute_hists_lut_kernel", ocl::objdetect::objdetect_hog_oclsrc); if (k.empty()) return false; bool is_cpu = cv::ocl::Device::getDefault().type() == cv::ocl::Device::TYPE_CPU; cv::String opts; if (is_cpu) opts = "-D CPU "; else opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); k.create("compute_hists_lut_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); if (k.empty()) return false; int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x)/block_stride_x; int img_block_height = (height - CELLS_PER_BLOCK_Y * CELL_HEIGHT + block_stride_y)/block_stride_y; int blocks_total = img_block_width * img_block_height; int qangle_elem_size = CV_ELEM_SIZE1(qangle.type()); int grad_quadstep = (int)grad.step >> 2; int qangle_step = (int)qangle.step / qangle_elem_size; int blocks_in_group = 4; size_t localThreads[3] = { (size_t)blocks_in_group * 24, 2, 1 }; size_t globalThreads[3] = {((img_block_width * img_block_height + blocks_in_group - 1)/blocks_in_group) * localThreads[0], 2, 1 }; int hists_size = (nbins * CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y * 12) * sizeof(float); int final_hists_size = (nbins * CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y) * sizeof(float); int smem = (hists_size + final_hists_size) * blocks_in_group; int idx = 0; idx = k.set(idx, block_stride_x); idx = k.set(idx, block_stride_y); idx = k.set(idx, nbins); idx = k.set(idx, (int)block_hist_size); idx = k.set(idx, img_block_width); idx = k.set(idx, blocks_in_group); idx = k.set(idx, blocks_total); idx = k.set(idx, grad_quadstep); idx = k.set(idx, qangle_step); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(grad)); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(qangle)); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(gauss_w_lut)); idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(block_hists)); idx = k.set(idx, (void*)NULL, (size_t)smem); return k.run(2, globalThreads, localThreads, false); } static int power_2up(unsigned int n) { for (unsigned int i = 1; i<=1024; i<<=1) if (n < i) return i; return -1; // Input is too big } static bool ocl_normalize_hists(int nbins, int block_stride_x, int block_stride_y, int height, int width, UMat block_hists, float threshold) { int block_hist_size = nbins * CELLS_PER_BLOCK_X * CELLS_PER_BLOCK_Y; int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / block_stride_x; int img_block_height = (height - CELLS_PER_BLOCK_Y * CELL_HEIGHT + block_stride_y) / block_stride_y; int nthreads; size_t globalThreads[3] = { 1, 1, 1 }; size_t localThreads[3] = { 1, 1, 1 }; int idx = 0; bool is_cpu = cv::ocl::Device::getDefault().type() == cv::ocl::Device::TYPE_CPU; cv::String opts; ocl::Kernel k; if (nbins == 9) { k.create("normalize_hists_36_kernel", ocl::objdetect::objdetect_hog_oclsrc, ""); if (k.empty()) return false; if (is_cpu) opts = "-D CPU "; else opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); k.create("normalize_hists_36_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); if (k.empty()) return false; int blocks_in_group = NTHREADS / block_hist_size; nthreads = blocks_in_group * block_hist_size; int num_groups = (img_block_width * img_block_height + blocks_in_group - 1)/blocks_in_group; globalThreads[0] = nthreads * num_groups; localThreads[0] = nthreads; } else { k.create("normalize_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); if (k.empty()) return false; if (is_cpu) opts = "-D CPU "; else opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); k.create("normalize_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); if (k.empty()) return false; nthreads = power_2up(block_hist_size); globalThreads[0] = img_block_width * nthreads; globalThreads[1] = img_block_height; localThreads[0] = nthreads; if ((nthreads < 32) || (nthreads > 512)) return false; idx = k.set(idx, nthreads); idx = k.set(idx, block_hist_size); idx = k.set(idx, img_block_width); } idx = k.set(idx, ocl::KernelArg::PtrReadWrite(block_hists)); idx = k.set(idx, threshold); idx = k.set(idx, (void*)NULL, nthreads * sizeof(float)); return k.run(2, globalThreads, localThreads, false); } static bool ocl_extract_descrs_by_rows(int win_height, int win_width, int block_stride_y, int block_stride_x, int win_stride_y, int win_stride_x, int height, int width, UMat block_hists, UMat descriptors, int block_hist_size, int descr_size, int descr_width) { ocl::Kernel k("extract_descrs_by_rows_kernel", ocl::objdetect::objdetect_hog_oclsrc); if (k.empty()) return false; int win_block_stride_x = win_stride_x / block_stride_x; int win_block_stride_y = win_stride_y / block_stride_y; int img_win_width = (width - win_width + win_stride_x) / win_stride_x; int img_win_height = (height - win_height + win_stride_y) / win_stride_y; int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / block_stride_x; int descriptors_quadstep = (int)descriptors.step >> 2; size_t globalThreads[3] = { (size_t)img_win_width * NTHREADS, (size_t)img_win_height, 1 }; size_t localThreads[3] = { NTHREADS, 1, 1 }; int idx = 0; idx = k.set(idx, block_hist_size); idx = k.set(idx, descriptors_quadstep); idx = k.set(idx, descr_size); idx = k.set(idx, descr_width); idx = k.set(idx, img_block_width); idx = k.set(idx, win_block_stride_x); idx = k.set(idx, win_block_stride_y); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(block_hists)); idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(descriptors)); return k.run(2, globalThreads, localThreads, false); } static bool ocl_extract_descrs_by_cols(int win_height, int win_width, int block_stride_y, int block_stride_x, int win_stride_y, int win_stride_x, int height, int width, UMat block_hists, UMat descriptors, int block_hist_size, int descr_size, int nblocks_win_x, int nblocks_win_y) { ocl::Kernel k("extract_descrs_by_cols_kernel", ocl::objdetect::objdetect_hog_oclsrc); if (k.empty()) return false; int win_block_stride_x = win_stride_x / block_stride_x; int win_block_stride_y = win_stride_y / block_stride_y; int img_win_width = (width - win_width + win_stride_x) / win_stride_x; int img_win_height = (height - win_height + win_stride_y) / win_stride_y; int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / block_stride_x; int descriptors_quadstep = (int)descriptors.step >> 2; size_t globalThreads[3] = { (size_t)img_win_width * NTHREADS, (size_t)img_win_height, 1 }; size_t localThreads[3] = { NTHREADS, 1, 1 }; int idx = 0; idx = k.set(idx, block_hist_size); idx = k.set(idx, descriptors_quadstep); idx = k.set(idx, descr_size); idx = k.set(idx, nblocks_win_x); idx = k.set(idx, nblocks_win_y); idx = k.set(idx, img_block_width); idx = k.set(idx, win_block_stride_x); idx = k.set(idx, win_block_stride_y); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(block_hists)); idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(descriptors)); return k.run(2, globalThreads, localThreads, false); } static bool ocl_compute(InputArray _img, Size win_stride, std::vector& _descriptors, int descr_format, Size blockSize, Size cellSize, int nbins, Size blockStride, Size winSize, float sigma, bool gammaCorrection, double L2HysThreshold, bool signedGradient) { Size imgSize = _img.size(); Size effect_size = imgSize; UMat grad(imgSize, CV_32FC2); int qangle_type = ocl::Device::getDefault().isIntel() ? CV_32SC2 : CV_8UC2; UMat qangle(imgSize, qangle_type); const size_t block_hist_size = getBlockHistogramSize(blockSize, cellSize, nbins); const Size blocks_per_img = numPartsWithin(imgSize, blockSize, blockStride); UMat block_hists(1, static_cast(block_hist_size * blocks_per_img.area()) + 256, CV_32F); Size wins_per_img = numPartsWithin(imgSize, winSize, win_stride); UMat labels(1, wins_per_img.area(), CV_8U); float scale = 1.f / (2.f * sigma * sigma); Mat gaussian_lut(1, 512, CV_32FC1); int idx = 0; for (int i=-8; i<8; i++) for (int j=-8; j<8; j++) gaussian_lut.at(idx++) = std::exp(-(j * j + i * i) * scale); for (int i=-8; i<8; i++) for (int j=-8; j<8; j++) gaussian_lut.at(idx++) = (8.f - fabs(j + 0.5f)) * (8.f - fabs(i + 0.5f)) / 64.f; if (!ocl_computeGradient(_img, grad, qangle, nbins, effect_size, gammaCorrection, signedGradient)) return false; UMat gauss_w_lut; gaussian_lut.copyTo(gauss_w_lut); if (!ocl_compute_hists(nbins, blockStride.width, blockStride.height, effect_size.height, effect_size.width, grad, qangle, gauss_w_lut, block_hists, block_hist_size)) return false; if (!ocl_normalize_hists(nbins, blockStride.width, blockStride.height, effect_size.height, effect_size.width, block_hists, (float)L2HysThreshold)) return false; Size blocks_per_win = numPartsWithin(winSize, blockSize, blockStride); wins_per_img = numPartsWithin(effect_size, winSize, win_stride); int descr_size = blocks_per_win.area()*(int)block_hist_size; int descr_width = (int)block_hist_size*blocks_per_win.width; UMat descriptors(wins_per_img.area(), static_cast(blocks_per_win.area() * block_hist_size), CV_32F); switch (descr_format) { case DESCR_FORMAT_ROW_BY_ROW: if (!ocl_extract_descrs_by_rows(winSize.height, winSize.width, blockStride.height, blockStride.width, win_stride.height, win_stride.width, effect_size.height, effect_size.width, block_hists, descriptors, (int)block_hist_size, descr_size, descr_width)) return false; break; case DESCR_FORMAT_COL_BY_COL: if (!ocl_extract_descrs_by_cols(winSize.height, winSize.width, blockStride.height, blockStride.width, win_stride.height, win_stride.width, effect_size.height, effect_size.width, block_hists, descriptors, (int)block_hist_size, descr_size, blocks_per_win.width, blocks_per_win.height)) return false; break; default: return false; } descriptors.reshape(1, (int)descriptors.total()).getMat(ACCESS_READ).copyTo(_descriptors); return true; } #endif //HAVE_OPENCL void HOGDescriptor::compute(InputArray _img, std::vector& descriptors, Size winStride, Size padding, const std::vector& locations) const { CV_INSTRUMENT_REGION(); if (winStride == Size()) winStride = cellSize; Size cacheStride(gcd(winStride.width, blockStride.width), gcd(winStride.height, blockStride.height)); Size imgSize = _img.size(); size_t nwindows = locations.size(); padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); Size paddedImgSize(imgSize.width + padding.width*2, imgSize.height + padding.height*2); CV_OCL_RUN(_img.dims() <= 2 && _img.type() == CV_8UC1 && _img.isUMat(), ocl_compute(_img, winStride, descriptors, DESCR_FORMAT_COL_BY_COL, blockSize, cellSize, nbins, blockStride, winSize, (float)getWinSigma(), gammaCorrection, L2HysThreshold, signedGradient)) Mat img = _img.getMat(); HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); if (!nwindows) nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); const HOGCache::BlockData* blockData = &cache.blockData[0]; int nblocks = cache.nblocks.area(); int blockHistogramSize = cache.blockHistogramSize; size_t dsize = getDescriptorSize(); descriptors.resize(dsize*nwindows); // for each window for (size_t i = 0; i < nwindows; i++) { float* descriptor = &descriptors[i*dsize]; Point pt0; if (!locations.empty()) { pt0 = locations[i]; if (pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height) continue; } else { pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); // CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); } for (int j = 0; j < nblocks; j++) { const HOGCache::BlockData& bj = blockData[j]; Point pt = pt0 + bj.imgOffset; float* dst = descriptor + bj.histOfs; const float* src = cache.getBlock(pt, dst); if (src != dst) memcpy(dst, src, blockHistogramSize * sizeof(float)); } } } void HOGDescriptor::detect(const Mat& img, std::vector& hits, std::vector& weights, double hitThreshold, Size winStride, Size padding, const std::vector& locations) const { CV_INSTRUMENT_REGION(); hits.clear(); weights.clear(); if (svmDetector.empty()) return; if (winStride == Size()) winStride = cellSize; Size cacheStride(gcd(winStride.width, blockStride.width), gcd(winStride.height, blockStride.height)); size_t nwindows = locations.size(); padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); if (!nwindows) nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); const HOGCache::BlockData* blockData = &cache.blockData[0]; int nblocks = cache.nblocks.area(); int blockHistogramSize = cache.blockHistogramSize; size_t dsize = getDescriptorSize(); double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0; std::vector blockHist(blockHistogramSize); #if CV_SIMD128 float partSum[4]; #endif for (size_t i = 0; i < nwindows; i++) { Point pt0; if (!locations.empty()) { pt0 = locations[i]; if (pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height) continue; } else { pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); } double s = rho; const float* svmVec = &svmDetector[0]; int j, k; for (j = 0; j < nblocks; j++, svmVec += blockHistogramSize) { const HOGCache::BlockData& bj = blockData[j]; Point pt = pt0 + bj.imgOffset; const float* vec = cache.getBlock(pt, &blockHist[0]); #if CV_SIMD128 v_float32x4 _vec = v_load(vec); v_float32x4 _svmVec = v_load(svmVec); v_float32x4 sum = _svmVec * _vec; for (k = 4; k <= blockHistogramSize - 4; k += 4) { _vec = v_load(vec + k); _svmVec = v_load(svmVec + k); sum += _vec * _svmVec; } v_store(partSum, sum); double t0 = partSum[0] + partSum[1]; double t1 = partSum[2] + partSum[3]; s += t0 + t1; #else for (k = 0; k <= blockHistogramSize - 4; k += 4) s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] + vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3]; #endif for ( ; k < blockHistogramSize; k++) s += vec[k]*svmVec[k]; } if (s >= hitThreshold) { hits.push_back(pt0); weights.push_back(s); } } } void HOGDescriptor::detect(const Mat& img, std::vector& hits, double hitThreshold, Size winStride, Size padding, const std::vector& locations) const { CV_INSTRUMENT_REGION(); std::vector weightsV; detect(img, hits, weightsV, hitThreshold, winStride, padding, locations); } class HOGInvoker : public ParallelLoopBody { public: HOGInvoker( const HOGDescriptor* _hog, const Mat& _img, double _hitThreshold, const Size& _winStride, const Size& _padding, const double* _levelScale, std::vector * _vec, Mutex* _mtx, std::vector* _weights=0, std::vector* _scales=0 ) { hog = _hog; img = _img; hitThreshold = _hitThreshold; winStride = _winStride; padding = _padding; levelScale = _levelScale; vec = _vec; weights = _weights; scales = _scales; mtx = _mtx; } void operator()(const Range& range) const CV_OVERRIDE { int i, i1 = range.start, i2 = range.end; double minScale = i1 > 0 ? levelScale[i1] : i2 > 1 ? levelScale[i1+1] : std::max(img.cols, img.rows); Size maxSz(cvCeil(img.cols/minScale), cvCeil(img.rows/minScale)); Mat smallerImgBuf(maxSz, img.type()); std::vector locations; std::vector hitsWeights; for (i = i1; i < i2; i++) { double scale = levelScale[i]; Size sz(cvRound(img.cols/scale), cvRound(img.rows/scale)); Mat smallerImg(sz, img.type(), smallerImgBuf.ptr()); if (sz == img.size()) smallerImg = Mat(sz, img.type(), img.data, img.step); else resize(img, smallerImg, sz, 0, 0, INTER_LINEAR_EXACT); hog->detect(smallerImg, locations, hitsWeights, hitThreshold, winStride, padding); Size scaledWinSize = Size(cvRound(hog->winSize.width*scale), cvRound(hog->winSize.height*scale)); mtx->lock(); for (size_t j = 0; j < locations.size(); j++) { vec->push_back(Rect(cvRound(locations[j].x*scale), cvRound(locations[j].y*scale), scaledWinSize.width, scaledWinSize.height)); if (scales) scales->push_back(scale); } mtx->unlock(); if (weights && (!hitsWeights.empty())) { mtx->lock(); for (size_t j = 0; j < locations.size(); j++) weights->push_back(hitsWeights[j]); mtx->unlock(); } } } private: const HOGDescriptor* hog; Mat img; double hitThreshold; Size winStride; Size padding; const double* levelScale; std::vector* vec; std::vector* weights; std::vector* scales; Mutex* mtx; }; #ifdef HAVE_OPENCL static bool ocl_classify_hists(int win_height, int win_width, int block_stride_y, int block_stride_x, int win_stride_y, int win_stride_x, int height, int width, const UMat& block_hists, UMat detector, float free_coef, float threshold, UMat& labels, Size descr_size, int block_hist_size) { int nthreads; bool is_cpu = cv::ocl::Device::getDefault().type() == cv::ocl::Device::TYPE_CPU; cv::String opts; ocl::Kernel k; int idx = 0; switch (descr_size.width) { case 180: nthreads = 180; k.create("classify_hists_180_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); if (k.empty()) return false; if (is_cpu) opts = "-D CPU "; else opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); k.create("classify_hists_180_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); if (k.empty()) return false; idx = k.set(idx, descr_size.width); idx = k.set(idx, descr_size.height); break; case 252: nthreads = 256; k.create("classify_hists_252_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); if (k.empty()) return false; if (is_cpu) opts = "-D CPU "; else opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); k.create("classify_hists_252_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); if (k.empty()) return false; idx = k.set(idx, descr_size.width); idx = k.set(idx, descr_size.height); break; default: nthreads = 256; k.create("classify_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, "-D WAVE_SIZE=32"); if (k.empty()) return false; if (is_cpu) opts = "-D CPU "; else opts = cv::format("-D WAVE_SIZE=%d", k.preferedWorkGroupSizeMultiple()); k.create("classify_hists_kernel", ocl::objdetect::objdetect_hog_oclsrc, opts); if (k.empty()) return false; idx = k.set(idx, descr_size.area()); idx = k.set(idx, descr_size.height); } int win_block_stride_x = win_stride_x / block_stride_x; int win_block_stride_y = win_stride_y / block_stride_y; int img_win_width = (width - win_width + win_stride_x) / win_stride_x; int img_win_height = (height - win_height + win_stride_y) / win_stride_y; int img_block_width = (width - CELLS_PER_BLOCK_X * CELL_WIDTH + block_stride_x) / block_stride_x; size_t globalThreads[3] = { (size_t)img_win_width * nthreads, (size_t)img_win_height, 1 }; size_t localThreads[3] = { (size_t)nthreads, 1, 1 }; idx = k.set(idx, block_hist_size); idx = k.set(idx, img_win_width); idx = k.set(idx, img_block_width); idx = k.set(idx, win_block_stride_x); idx = k.set(idx, win_block_stride_y); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(block_hists)); idx = k.set(idx, ocl::KernelArg::PtrReadOnly(detector)); idx = k.set(idx, free_coef); idx = k.set(idx, threshold); idx = k.set(idx, ocl::KernelArg::PtrWriteOnly(labels)); return k.run(2, globalThreads, localThreads, false); } static bool ocl_detect(InputArray img, std::vector &hits, double hit_threshold, Size win_stride, const UMat& oclSvmDetector, Size blockSize, Size cellSize, int nbins, Size blockStride, Size winSize, bool gammaCorrection, double L2HysThreshold, float sigma, float free_coef, bool signedGradient) { hits.clear(); if (oclSvmDetector.empty()) return false; Size imgSize = img.size(); Size effect_size = imgSize; UMat grad(imgSize, CV_32FC2); int qangle_type = ocl::Device::getDefault().isIntel() ? CV_32SC2 : CV_8UC2; UMat qangle(imgSize, qangle_type); const size_t block_hist_size = getBlockHistogramSize(blockSize, cellSize, nbins); const Size blocks_per_img = numPartsWithin(imgSize, blockSize, blockStride); UMat block_hists(1, static_cast(block_hist_size * blocks_per_img.area()) + 256, CV_32F); Size wins_per_img = numPartsWithin(imgSize, winSize, win_stride); UMat labels(1, wins_per_img.area(), CV_8U); float scale = 1.f / (2.f * sigma * sigma); Mat gaussian_lut(1, 512, CV_32FC1); int idx = 0; for (int i=-8; i<8; i++) for (int j=-8; j<8; j++) gaussian_lut.at(idx++) = std::exp(-(j * j + i * i) * scale); for (int i=-8; i<8; i++) for (int j=-8; j<8; j++) gaussian_lut.at(idx++) = (8.f - fabs(j + 0.5f)) * (8.f - fabs(i + 0.5f)) / 64.f; if (!ocl_computeGradient(img, grad, qangle, nbins, effect_size, gammaCorrection, signedGradient)) return false; UMat gauss_w_lut; gaussian_lut.copyTo(gauss_w_lut); if (!ocl_compute_hists(nbins, blockStride.width, blockStride.height, effect_size.height, effect_size.width, grad, qangle, gauss_w_lut, block_hists, block_hist_size)) return false; if (!ocl_normalize_hists(nbins, blockStride.width, blockStride.height, effect_size.height, effect_size.width, block_hists, (float)L2HysThreshold)) return false; Size blocks_per_win = numPartsWithin(winSize, blockSize, blockStride); Size descr_size((int)block_hist_size*blocks_per_win.width, blocks_per_win.height); if (!ocl_classify_hists(winSize.height, winSize.width, blockStride.height, blockStride.width, win_stride.height, win_stride.width, effect_size.height, effect_size.width, block_hists, oclSvmDetector, free_coef, (float)hit_threshold, labels, descr_size, (int)block_hist_size)) return false; Mat labels_host = labels.getMat(ACCESS_READ); unsigned char *vec = labels_host.ptr(); for (int i = 0; i < wins_per_img.area(); i++) { int y = i / wins_per_img.width; int x = i - wins_per_img.width * y; if (vec[i]) { hits.push_back(Point(x * win_stride.width, y * win_stride.height)); } } return true; } static bool ocl_detectMultiScale(InputArray _img, std::vector &found_locations, std::vector& level_scale, double hit_threshold, Size win_stride, double group_threshold, const UMat& oclSvmDetector, Size blockSize, Size cellSize, int nbins, Size blockStride, Size winSize, bool gammaCorrection, double L2HysThreshold, float sigma, float free_coef, bool signedGradient) { std::vector all_candidates; std::vector locations; UMat image_scale; Size imgSize = _img.size(); image_scale.create(imgSize, _img.type()); for (size_t i = 0; i& foundLocations, std::vector& foundWeights, double hitThreshold, Size winStride, Size padding, double scale0, double finalThreshold, bool useMeanshiftGrouping) const { CV_INSTRUMENT_REGION(); double scale = 1.; int levels = 0; Size imgSize = _img.size(); std::vector levelScale; for (levels = 0; levels < nlevels; levels++) { levelScale.push_back(scale); if (cvRound(imgSize.width/scale) < winSize.width || cvRound(imgSize.height/scale) < winSize.height || scale0 <= 1) break; scale *= scale0; } levels = std::max(levels, 1); levelScale.resize(levels); if (winStride == Size()) winStride = blockStride; CV_OCL_RUN(_img.dims() <= 2 && _img.type() == CV_8UC1 && scale0 > 1 && winStride.width % blockStride.width == 0 && winStride.height % blockStride.height == 0 && padding == Size(0,0) && _img.isUMat(), ocl_detectMultiScale(_img, foundLocations, levelScale, hitThreshold, winStride, finalThreshold, oclSvmDetector, blockSize, cellSize, nbins, blockStride, winSize, gammaCorrection, L2HysThreshold, (float)getWinSigma(), free_coef, signedGradient)); std::vector allCandidates; std::vector tempScales; std::vector tempWeights; std::vector foundScales; Mutex mtx; Mat img = _img.getMat(); Range range(0, (int)levelScale.size()); HOGInvoker invoker(this, img, hitThreshold, winStride, padding, &levelScale[0], &allCandidates, &mtx, &tempWeights, &tempScales); parallel_for_(range, invoker); std::copy(tempScales.begin(), tempScales.end(), back_inserter(foundScales)); foundLocations.clear(); std::copy(allCandidates.begin(), allCandidates.end(), back_inserter(foundLocations)); foundWeights.clear(); std::copy(tempWeights.begin(), tempWeights.end(), back_inserter(foundWeights)); if (useMeanshiftGrouping) groupRectangles_meanshift(foundLocations, foundWeights, foundScales, finalThreshold, winSize); else groupRectangles(foundLocations, foundWeights, (int)finalThreshold, 0.2); clipObjects(imgSize, foundLocations, 0, &foundWeights); } void HOGDescriptor::detectMultiScale(InputArray img, std::vector& foundLocations, double hitThreshold, Size winStride, Size padding, double scale0, double finalThreshold, bool useMeanshiftGrouping) const { CV_INSTRUMENT_REGION(); std::vector foundWeights; detectMultiScale(img, foundLocations, foundWeights, hitThreshold, winStride, padding, scale0, finalThreshold, useMeanshiftGrouping); } template struct RTTIImpl { public: static int isInstance(const void* ptr) { static _ClsName dummy; static void* dummyp = &dummy; union { const void* p; const void** pp; } a, b; a.p = dummyp; b.p = ptr; return *a.pp == *b.pp; } static void release(void** dbptr) { if (dbptr && *dbptr) { delete (_ClsName*)*dbptr; *dbptr = 0; } } static void* read(CvFileStorage* fs, CvFileNode* n) { FileNode fn(fs, n); _ClsName* obj = new _ClsName; if (obj->read(fn)) return obj; delete obj; return 0; } static void write(CvFileStorage* _fs, const char* name, const void* ptr, CvAttrList) { if (ptr && _fs) { FileStorage fs(_fs, false); ((const _ClsName*)ptr)->write(fs, String(name)); } } static void* clone(const void* ptr) { if (!ptr) return 0; return new _ClsName(*(const _ClsName*)ptr); } }; typedef RTTIImpl HOGRTTI; CvType hog_type( CV_TYPE_NAME_HOG_DESCRIPTOR, HOGRTTI::isInstance, HOGRTTI::release, HOGRTTI::read, HOGRTTI::write, HOGRTTI::clone); std::vector HOGDescriptor::getDefaultPeopleDetector() { static const float detector[] = { 0.05359386f, -0.14721455f, -0.05532170f, 0.05077307f, 0.11547081f, -0.04268804f, 0.04635834f, -0.05468199f, 0.08232084f, 0.10424068f, -0.02294518f, 0.01108519f, 0.01378693f, 0.11193510f, 0.01268418f, 0.08528346f, -0.06309239f, 0.13054633f, 0.08100729f, -0.05209739f, -0.04315529f, 0.09341384f, 0.11035026f, -0.07596218f, -0.05517511f, -0.04465296f, 0.02947334f, 0.04555536f, -3.55954492e-003f, 0.07818956f, 0.07730991f, 0.07890715f, 0.06222893f, 0.09001380f, -0.03574381f, 0.03414327f, 0.05677258f, -0.04773581f, 0.03746637f, -0.03521175f, 0.06955440f, -0.03849038f, 0.01052293f, 0.01736112f, 0.10867710f, 0.08748853f, 3.29739624e-003f, 0.10907028f, 0.07913758f, 0.10393070f, 0.02091867f, 0.11594022f, 0.13182420f, 0.09879354f, 0.05362710f, -0.06745391f, -7.01260753e-003f, 5.24702156e-003f, 0.03236255f, 0.01407916f, 0.02207983f, 0.02537322f, 0.04547948f, 0.07200756f, 0.03129894f, -0.06274468f, 0.02107014f, 0.06035208f, 0.08636236f, 4.53164103e-003f, 0.02193363f, 0.02309801f, 0.05568166f, -0.02645093f, 0.04448695f, 0.02837519f, 0.08975694f, 0.04461516f, 0.08975355f, 0.07514391f, 0.02306982f, 0.10410084f, 0.06368385f, 0.05943464f, 4.58420580e-003f, 0.05220337f, 0.06675851f, 0.08358569f, 0.06712101f, 0.06559004f, -0.03930482f, -9.15936660e-003f, -0.05897915f, 0.02816453f, 0.05032348f, 0.06780671f, 0.03377650f, -6.09417039e-004f, -0.01795146f, -0.03083684f, -0.01302475f, -0.02972313f, 7.88706727e-003f, -0.03525961f, -2.50397739e-003f, 0.05245084f, 0.11791293f, -0.02167498f, 0.05299332f, 0.06640524f, 0.05190265f, -8.27316567e-003f, 0.03033127f, 0.05842173f, -4.01050318e-003f, -6.25105947e-003f, 0.05862958f, -0.02465461f, 0.05546781f, -0.08228195f, -0.07234028f, 0.04640540f, -0.01308254f, -0.02506191f, 0.03100746f, -0.04665651f, -0.04591486f, 0.02949927f, 0.06035462f, 0.02244646f, -0.01698639f, 0.01040041f, 0.01131170f, 0.05419579f, -0.02130277f, -0.04321722f, -0.03665198f, 0.01126490f, -0.02606488f, -0.02228328f, -0.02255680f, -0.03427236f, -7.75165204e-003f, -0.06195229f, 8.21638294e-003f, 0.09535975f, -0.03709979f, -0.06942501f, 0.14579427f, -0.05448192f, -0.02055904f, 0.05747357f, 0.02781788f, -0.07077577f, -0.05178314f, -0.10429011f, -0.11235505f, 0.07529039f, -0.07559302f, -0.08786739f, 0.02983843f, 0.02667585f, 0.01382199f, -0.01797496f, -0.03141199f, -0.02098101f, 0.09029204f, 0.04955018f, 0.13718739f, 0.11379953f, 1.80019124e-003f, -0.04577610f, -1.11108483e-003f, -0.09470536f, -0.11596080f, 0.04489342f, 0.01784211f, 3.06850672e-003f, 0.10781866f, 3.36498418e-003f, -0.10842580f, -0.07436839f, -0.10535070f, -0.01866805f, 0.16057891f, -5.07316366e-003f, -0.04295658f, -5.90488780e-003f, 8.82003549e-003f, -0.01492646f, -0.05029279f, -0.12875880f, 8.78831954e-004f, -0.01297184f, -0.07592774f, -0.02668831f, -6.93787413e-004f, 0.02406698f, -0.01773298f, -0.03855745f, -0.05877856f, 0.03259695f, 0.12826584f, 0.06292590f, -4.10733931e-003f, 0.10996531f, 0.01332991f, 0.02088735f, 0.04037504f, -0.05210760f, 0.07760046f, 0.06399347f, -0.05751930f, -0.10053057f, 0.07505023f, -0.02139782f, 0.01796176f, 2.34400877e-003f, -0.04208319f, 0.07355055f, 0.05093350f, -0.02996780f, -0.02219072f, 0.03355330f, 0.04418742f, -0.05580705f, -0.05037573f, -0.04548179f, 0.01379514f, 0.02150671f, -0.02194211f, -0.13682702f, 0.05464972f, 0.01608082f, 0.05309116f, 0.04701022f, 1.33690401e-003f, 0.07575664f, 0.09625306f, 8.92647635e-003f, -0.02819123f, 0.10866830f, -0.03439325f, -0.07092371f, -0.06004780f, -0.02712298f, -7.07467366e-003f, -0.01637020f, 0.01336790f, -0.10313606f, 0.04906582f, -0.05732445f, -0.02731079f, 0.01042235f, -0.08340668f, 0.03686501f, 0.06108340f, 0.01322748f, -0.07809529f, 0.03774724f, -0.03413248f, -0.06096525f, -0.04212124f, -0.07982176f, -1.25973229e-003f, -0.03045501f, -0.01236493f, -0.06312395f, 0.04789570f, -0.04602066f, 0.08576570f, 0.02521080f, 0.02988098f, 0.10314583f, 0.07060035f, 0.04520544f, -0.04426654f, 0.13146530f, 0.08386490f, 0.02164590f, -2.12280243e-003f, -0.03686353f, -0.02074944f, -0.03829959f, -0.01530596f, 0.02689708f, 0.11867401f, -0.06043470f, -0.02785023f, -0.04775074f, 0.04878745f, 0.06350956f, 0.03494788f, 0.01467400f, 1.17890188e-003f, 0.04379614f, 2.03681854e-003f, -0.03958609f, -0.01072688f, 6.43705716e-003f, 0.02996500f, -0.03418507f, -0.01960307f, -0.01219154f, -4.37000440e-003f, -0.02549453f, 0.02646318f, -0.01632513f, 6.46516960e-003f, -0.01929734f, 4.78711911e-003f, 0.04962371f, 0.03809111f, 0.07265724f, 0.05758125f, -0.03741554f, 0.01648608f, -8.45285598e-003f, 0.03996826f, -0.08185477f, 0.02638875f, -0.04026615f, -0.02744674f, -0.04071517f, 1.05096330e-003f, -0.04741232f, -0.06733172f, 8.70434940e-003f, -0.02192543f, 1.35350740e-003f, -0.03056974f, -0.02975521f, -0.02887780f, -0.01210713f, -0.04828526f, -0.09066251f, -0.09969629f, -0.03665164f, -8.88111943e-004f, -0.06826669f, -0.01866150f, -0.03627640f, -0.01408288f, 0.01874239f, -0.02075835f, 0.09145175f, -0.03547291f, 0.05396780f, 0.04198981f, 0.01301925f, -0.03384354f, -0.12201976f, 0.06830920f, -0.03715654f, 9.55848210e-003f, 5.05685573e-003f, 0.05659294f, 3.90764466e-003f, 0.02808490f, -0.05518097f, -0.03711621f, -0.02835565f, -0.04420464f, -0.01031947f, 0.01883466f, -8.49525444e-003f, -0.09419250f, -0.01269387f, -0.02133371f, -0.10190815f, -0.07844430f, 2.43644323e-003f, -4.09610150e-003f, 0.01202551f, -0.06452291f, -0.10593818f, -0.02464746f, -0.02199699f, -0.07401930f, 0.07285886f, 8.87513801e-004f, 9.97662079e-003f, 8.46779719e-003f, 0.03730333f, -0.02905126f, 0.03573337f, -0.04393689f, -0.12014472f, 0.03176554f, -2.76015815e-003f, 0.10824566f, 0.05090732f, -3.30179278e-003f, -0.05123822f, 5.04784798e-003f, -0.05664124f, -5.99415926e-003f, -0.05341901f, -0.01221393f, 0.01291318f, 9.91760660e-003f, -7.56987557e-003f, -0.06193124f, -2.24549137e-003f, 0.01987562f, -0.02018840f, -0.06975540f, -0.06601523f, -0.03349112f, -0.08910118f, -0.03371435f, -0.07406893f, -0.02248047f, -0.06159951f, 2.77751544e-003f, -0.05723337f, -0.04792468f, 0.07518548f, 2.77279224e-003f, 0.04211938f, 0.03100502f, 0.05278448f, 0.03954679f, -0.03006846f, -0.03851741f, -0.02792403f, -0.02875333f, 0.01531280f, 0.02186953f, -0.01989829f, 2.50679464e-003f, -0.10258728f, -0.04785743f, -0.02887216f, 3.85063468e-003f, 0.01112236f, 8.29218887e-003f, -0.04822981f, -0.04503597f, -0.03713100f, -0.06988008f, -0.11002295f, -2.69209221e-003f, 1.85383670e-003f, -0.05921049f, -0.06105053f, -0.08458050f, -0.04527602f, 8.90329306e-004f, -0.05875023f, -2.68602883e-003f, -0.01591195f, 0.03631859f, 0.05493166f, 0.07300330f, 5.53333294e-003f, 0.06400407f, 0.01847740f, -5.76280477e-003f, -0.03210877f, 4.25160583e-003f, 0.01166520f, -1.44864211e-003f, 0.02253744f, -0.03367080f, 0.06983195f, -4.22323542e-003f, -8.89401045e-003f, -0.07943393f, 0.05199728f, 0.06065201f, 0.04133492f, 1.44032843e-003f, -0.09585235f, -0.03964731f, 0.04232114f, 0.01750465f, -0.04487902f, -7.59733608e-003f, 0.02011171f, 0.04673622f, 0.09011173f, -0.07869188f, -0.04682482f, -0.05080139f, -3.99383716e-003f, -0.05346331f, 0.01085723f, -0.03599333f, -0.07097908f, 0.03551549f, 0.02680387f, 0.03471529f, 0.01790393f, 0.05471273f, 9.62048303e-003f, -0.03180215f, 0.05864431f, 0.02330614f, 0.01633144f, -0.05616681f, -0.10245429f, -0.08302189f, 0.07291322f, -0.01972590f, -0.02619633f, -0.02485327f, -0.04627592f, 1.48853404e-003f, 0.05514185f, -0.01270860f, -0.01948900f, 0.06373586f, 0.05002292f, -0.03009798f, 8.76216311e-003f, -0.02474238f, -0.05504891f, 1.74034527e-003f, -0.03333667f, 0.01524987f, 0.11663762f, -1.32344989e-003f, -0.06608453f, 0.05687166f, -6.89525274e-004f, -0.04402352f, 0.09450210f, -0.04222684f, -0.05360983f, 0.01779531f, 0.02561388f, -0.11075410f, -8.77790991e-003f, -0.01099504f, -0.10380266f, 0.03103457f, -0.02105741f, -0.07371717f, 0.05146710f, 0.10581432f, -0.08617968f, -0.02892107f, 0.01092199f, 0.14551543f, -2.24320893e-003f, -0.05818033f, -0.07390742f, 0.05701261f, 0.12937020f, -0.04986651f, 0.10182415f, 0.05028650f, 0.12515625f, 0.09175041f, 0.06404983f, 0.01523394f, 0.09460562f, 0.06106631f, -0.14266998f, -0.02926703f, 0.02762171f, 0.02164151f, -9.58488265e-004f, -0.04231362f, -0.09866509f, 0.04322244f, 0.05872034f, -0.04838847f, 0.06319253f, 0.02443798f, -0.03606876f, 9.38737206e-003f, 0.04289991f, -0.01027411f, 0.08156885f, 0.08751175f, -0.13191354f, 8.16054735e-003f, -0.01452161f, 0.02952677f, 0.03615945f, -2.09128903e-003f, 0.02246693f, 0.09623287f, 0.09412123f, -0.02924758f, -0.07815186f, -0.02203079f, -2.02566991e-003f, 0.01094733f, -0.01442332f, 0.02838561f, 0.11882371f, 7.28798332e-003f, -0.10345965f, 0.07561217f, -0.02049661f, 4.44177445e-003f, 0.01609347f, -0.04893158f, -0.08758243f, -7.67420698e-003f, 0.08862378f, 0.06098121f, 0.06565887f, 7.32981879e-003f, 0.03558407f, -0.03874352f, -0.02490055f, -0.06771075f, 0.09939223f, -0.01066077f, 0.01382995f, -0.07289080f, 7.47184316e-003f, 0.10621431f, -0.02878659f, 0.02383525f, -0.03274646f, 0.02137008f, 0.03837290f, 0.02450992f, -0.04296818f, -0.02895143f, 0.05327370f, 0.01499020f, 0.04998732f, 0.12938657f, 0.09391870f, 0.04292390f, -0.03359194f, -0.06809492f, 0.01125796f, 0.17290455f, -0.03430733f, -0.06255233f, -0.01813114f, 0.11726857f, -0.06127599f, -0.08677909f, -0.03429872f, 0.04684938f, 0.08161420f, 0.03538774f, 0.01833884f, 0.11321855f, 0.03261845f, -0.04826299f, 0.01752407f, -0.01796414f, -0.10464549f, -3.30041884e-003f, 2.29343961e-004f, 0.01457292f, -0.02132982f, -0.02602923f, -9.87351313e-003f, 0.04273872f, -0.02103316f, -0.07994065f, 0.02614958f, -0.02111666f, -0.06964913f, -0.13453490f, -0.06861878f, -6.09341264e-003f, 0.08251446f, 0.15612499f, 2.46531400e-003f, 8.88424646e-003f, -0.04152999f, 0.02054853f, 0.05277953f, -0.03087788f, 0.02817579f, 0.13939077f, 0.07641046f, -0.03627627f, -0.03015098f, -0.04041540f, -0.01360690f, -0.06227205f, -0.02738223f, 0.13577610f, 0.15235767f, -0.05392922f, -0.11175954f, 0.02157129f, 0.01146481f, -0.05264937f, -0.06595174f, -0.02749175f, 0.11812254f, 0.17404149f, -0.06137035f, -0.11003478f, -0.01351621f, -0.01745916f, -0.08577441f, -0.04469909f, -0.06106115f, 0.10559758f, 0.20806813f, -0.09174948f, 7.09621934e-004f, 0.03579374f, 0.07215115f, 0.02221742f, 0.01827742f, -7.90785067e-003f, 0.01489554f, 0.14519960f, -0.06425831f, 0.02990399f, -1.80181325e-003f, -0.01401528f, -0.04171134f, -3.70530109e-003f, -0.09090481f, 0.09520713f, 0.08845516f, -0.02651753f, -0.03016730f, 0.02562448f, 0.03563816f, -0.03817881f, 0.01433385f, 0.02256983f, 0.02872120f, 0.01001934f, -0.06332260f, 0.04338406f, 0.07001807f, -0.04705722f, -0.07318907f, 0.02630457f, 0.03106382f, 0.06648342f, 0.10913180f, -0.01630815f, 0.02910308f, 0.02895109f, 0.08040254f, 0.06969310f, 0.06797734f, 6.08639978e-003f, 4.16588830e-003f, 0.08926726f, -0.03123648f, 0.02700146f, 0.01168734f, -0.01631594f, 4.61015804e-003f, 8.51359498e-003f, -0.03544224f, 0.03571994f, 4.29766066e-003f, -0.01970077f, -8.79793242e-003f, 0.09607988f, 0.01544222f, -0.03923707f, 0.07308586f, 0.06061262f, 1.31683104e-004f, -7.98222050e-003f, 0.02399261f, -0.06084389f, -0.02743429f, -0.05475523f, -0.04131311f, 0.03559756f, 0.03055342f, 0.02981433f, 0.14860515f, 0.01766787f, 0.02945257f, 0.04898238f, 0.01026922f, 0.02811658f, 0.08267091f, 0.02732154f, -0.01237693f, 0.11760156f, 0.03802063f, -0.03309754f, 5.24957618e-003f, -0.02460510f, 0.02691451f, 0.05399988f, -0.10133506f, 0.06385437f, -0.01818005f, 0.02259503f, 0.03573135f, 0.01042848f, -0.04153402f, -0.04043029f, 0.01643575f, 0.08326677f, 4.61383024e-004f, -0.05308095f, -0.08536223f, -1.61011645e-003f, -0.02163720f, -0.01783352f, 0.03859637f, 0.08498885f, -0.01725216f, 0.08625131f, 0.10995087f, 0.09177644f, 0.08498347f, 0.07646490f, 0.05580502f, 0.02693516f, 0.09996913f, 0.09070327f, 0.06667200f, 0.05873008f, -0.02247842f, 0.07772321f, 0.12408436f, 0.12629253f, -8.41997913e-004f, 0.01477783f, 0.09165990f, -2.98401713e-003f, -0.06466447f, -0.07057302f, 2.09516948e-004f, 0.02210209f, -0.02158809f, -0.08602506f, -0.02284836f, 4.01876355e-003f, 9.56660323e-003f, -0.02073978f, -0.04635138f, -7.59423291e-003f, -0.01377393f, -0.04559359f, -0.13284740f, -0.08671406f, -0.03654395f, 0.01142869f, 0.03287891f, -0.04392983f, 0.06142959f, 0.17710890f, 0.10385257f, 0.01329137f, 0.10067633f, 0.12450829f, -0.04476709f, 0.09049144f, 0.04589312f, 0.11167907f, 0.08587538f, 0.04767583f, 1.67188141e-003f, 0.02359802f, -0.03808852f, 0.03126272f, -0.01919029f, -0.05698918f, -0.02365112f, -0.06519032f, -0.05599358f, -0.07097308f, -0.03301812f, -0.04719102f, -0.02566297f, 0.01324074f, -0.09230672f, -0.05518232f, -0.04712864f, -0.03380903f, -0.06719479f, 0.01183908f, -0.09326738f, 0.01642865f, 0.03789867f, -6.61567831e-003f, 0.07796386f, 0.07246574f, 0.04706347f, -0.02523437f, -0.01696830f, -0.08068866f, 0.06030888f, 0.10527060f, -0.06611756f, 0.02977346f, 0.02621830f, 0.01913855f, -0.08479366f, -0.06322418f, -0.13570616f, -0.07644490f, 9.31900274e-003f, -0.08095149f, -0.10197903f, -0.05204025f, 0.01413151f, -0.07800411f, -0.01885122f, -0.07509381f, -0.10136326f, -0.05212355f, -0.09944065f, -1.33606605e-003f, -0.06342617f, -0.04178550f, -0.12373723f, -0.02832736f, -0.06057501f, 0.05830070f, 0.07604282f, -0.06462587f, 8.02447461e-003f, 0.11580125f, 0.12332212f, 0.01978462f, -2.72378162e-003f, 0.05850752f, -0.04674481f, 0.05148062f, -2.62542837e-003f, 0.11253355f, 0.09893716f, 0.09785093f, -0.04659257f, -0.01102429f, -0.07002308f, 0.03088913f, -0.02565549f, -0.07671449f, 3.17443861e-003f, -0.10783514f, -0.02314270f, -0.11089555f, -0.01024768f, 0.03116021f, -0.04964825f, 0.02281825f, 5.50005678e-003f, -0.08427856f, -0.14685495f, -0.07719755f, -0.13342668f, -0.04525511f, -0.09914210f, 0.02588859f, 0.03469279f, 0.04664020f, 0.11688190f, 0.09647275f, 0.10857815f, -0.01448726f, 0.04299758f, -0.06763151f, 1.33257592e-003f, 0.14331576f, 0.07574340f, 0.09166205f, 0.05674926f, 0.11325553f, -0.01106494f, 0.02062161f, -0.11484840f, -0.07492137f, -0.02864293f, -0.01275638f, -0.06946032f, -0.10101652f, -0.04113498f, -0.02214783f, -0.01273942f, -0.07480393f, -0.10556041f, -0.07622112f, -0.09988393f, -0.11453961f, -0.12073903f, -0.09412795f, -0.07146588f, -0.04054537f, -0.06127083f, 0.04221122f, 0.07688113f, 0.04099256f, 0.12663734f, 0.14683802f, 0.21761774f, 0.12525328f, 0.18431792f, -1.66402373e-003f, 2.37777247e-003f, 0.01445475f, 0.03509416f, 0.02654697f, 0.01716739f, 0.05374011f, 0.02944174f, 0.11323927f, -0.01485456f, -0.01611330f, -1.85554172e-003f, -0.01708549f, -0.05435753f, -0.05302101f, 0.05260378f, -0.03582945f, -3.42867890e-004f, 1.36076682e-003f, -0.04436073f, -0.04228432f, 0.03281291f, -0.05480836f, -0.10197772f, -0.07206279f, -0.10741059f, -0.02366946f, 0.10278475f, -2.74783419e-003f, -0.03242477f, 0.02308955f, 0.02835869f, 0.10348799f, 0.19580358f, 0.10252027f, 0.08039929f, 0.05525554f, -0.13250865f, -0.14395352f, 3.13586881e-003f, -0.03387071f, 8.94669443e-003f, 0.05406157f, -4.97324532e-003f, -0.01189114f, 2.82919413e-004f, -0.03901557f, -0.04898705f, 0.02164520f, -0.01382906f, -0.01850416f, 0.01869347f, -0.02450060f, 0.02291678f, 0.08196463f, 0.03309153f, -0.10629974f, 0.02473924f, 0.05344394f, -0.02404823f, -0.03243643f, -5.55244600e-003f, -0.08009996f, 0.02811539f, 0.04235742f, 0.01859004f, 0.04902123f, -0.01438252f, -0.01526853f, 0.02044195f, -0.05008660f, 0.04244113f, 0.07611816f, 0.04950470f, -0.06020549f, -4.26026015e-003f, 0.13133512f, -0.01438738f, -0.01958807f, -0.04044152f, -0.12425045f, 2.84353318e-003f, -0.05042776f, -0.09121484f, 7.34345755e-003f, 0.09388847f, 0.11800314f, 4.72295098e-003f, 4.44378285e-003f, -0.07984917f, -0.03613737f, 0.04490915f, -0.02246483f, 0.04681071f, 0.05240871f, 0.02157206f, -0.04603431f, -0.01197929f, -0.02748779f, 0.13621049f, 0.08812155f, -0.07802048f, 4.86458559e-003f, -0.01598836f, 0.01024450f, -0.03463517f, -0.02304239f, -0.08692665f, 0.06655128f, 0.05785803f, -0.12640759f, 0.02307472f, 0.07337402f, 0.07525434f, 0.04943763f, -0.02241034f, -0.09978238f, 0.14487994f, -0.06570521f, -0.07855482f, 0.02830222f, -5.29603509e-004f, -0.04669895f, -0.11822784f, -0.12246452f, -0.15365660f, -0.02969127f, 0.08078201f, 0.13512598f, 0.11505685f, 0.04740673f, 0.01376022f, -0.05852978f, -0.01537809f, -0.05541119f, 0.02491065f, -0.02870786f, 0.02760978f, 0.23836176f, 0.22347429f, 0.10306466f, -0.06919070f, -0.10132039f, -0.20198342f, -0.05040560f, 0.27163076f, 0.36987007f, 0.34540465f, 0.29095781f, 0.05649706f, 0.04125737f, 0.07505883f, -0.02737836f, -8.43431335e-003f, 0.07368195f, 0.01653876f, -0.09402955f, -0.09574359f, 0.01474337f, -0.07128561f, -0.03460737f, 0.11438941f, 0.13752601f, -0.06385452f, -0.06310338f, 8.19548313e-003f, 0.11622470f, 5.05133113e-003f, -0.07602754f, 0.06695660f, 0.25723928f, 0.09037900f, 0.28826267f, 0.13165380f, -0.05312614f, -0.02137198f, -0.03442232f, -0.06255679f, 0.03899667f, 0.18391028f, 0.26016650f, 0.03374462f, 0.01860465f, 0.19077586f, 0.18160543f, 3.43634398e-003f, -0.03036782f, 0.19683038f, 0.35378191f, 0.24968483f, -0.03222649f, 0.28972381f, 0.43091634f, 0.30778357f, 0.02335266f, -0.09877399f, -6.85245218e-003f, 0.08945240f, -0.08150686f, 0.02792493f, 0.24806842f, 0.17338486f, 0.06231801f, -0.10432383f, -0.16653322f, -0.13197899f, -0.08531576f, -0.19271527f, -0.13536365f, 0.22240199f, 0.39219588f, 0.26597717f, -0.01231649f, 0.01016179f, 0.13379875f, 0.12018334f, -0.04852953f, -0.07915270f, 0.07036012f, 3.87723115e-003f, -0.06126805f, -0.15015170f, -0.11406515f, -0.08556531f, -0.07429333f, -0.16115491f, 0.13214062f, 0.25691369f, 0.05697750f, 0.06861912f, -6.02903729e-003f, -7.94562511e-003f, 0.04799571f, 0.06695165f, -0.01926842f, 0.06206308f, 0.13450983f, -0.06381495f, -2.98370165e-003f, -0.03482971f, 7.53991678e-003f, 0.03895611f, 0.11464261f, 0.01669971f, 8.27818643e-003f, -7.49160210e-003f, -0.11712562f, -0.10650621f, -0.10353880f, -0.04994106f, -7.65618810e-004f, 0.03023767f, -0.04759270f, -0.07302686f, -0.05825012f, -0.13156348f, -0.10639747f, -0.19393684f, -0.09973683f, -0.07918908f, 4.63177625e-004f, -6.61382044e-004f, 0.15853868f, 0.08561199f, -0.07660093f, -0.08015265f, -0.06164073f, 0.01882577f, -7.29908410e-004f, 0.06840892f, 0.03843764f, 0.20274927f, 0.22028814f, -5.26101235e-003f, 0.01452435f, -0.06331623f, 0.02865064f, 0.05673740f, 0.12171564f, 0.03837196f, 0.03555467f, -0.02662914f, -0.10280123f, -0.06526285f, -0.11066351f, -0.08988424f, -0.10103678f, 8.10526591e-003f, 5.95238712e-003f, 0.02617721f, -0.01705742f, -0.10897956f, -0.08004991f, -0.11271993f, -0.06185647f, -0.06103712f, 0.01597041f, -0.05923606f, 0.09410726f, 0.22858568f, 0.03263380f, 0.06772990f, -0.09003516f, 0.01017870f, 0.01931688f, 0.08628357f, -0.01430009f, 0.10954945f, 0.16612452f, -0.02434544f, -0.03310068f, -0.04236627f, 0.01212392f, -6.15046406e-003f, 0.06954194f, 0.03015283f, 0.01787957f, 0.02781667f, -0.05561153f, -8.96244217e-003f, -0.04971489f, 0.07510284f, 0.01775282f, 0.05889897f, -0.07981427f, 0.03647643f, -3.73833324e-003f, -0.08894575f, -0.06429435f, -0.08068276f, 0.03567704f, -0.07131936f, -7.21910037e-003f, -0.09566668f, 0.17886090f, 0.14911725f, 0.02070032f, -0.05017120f, -0.04992622f, 0.01570143f, -0.09906903f, 0.06456193f, 0.15329507f, 0.18820767f, 0.11689861f, -0.01178513f, -0.02225163f, -0.01905318f, 0.10271224f, -7.27029052e-003f, 0.11664233f, 0.14796902f, 0.07771893f, 0.02400013f, -0.05361797f, -0.01972888f, 0.01376177f, 0.06740040f, -0.06525395f, 0.05726178f, -0.02404981f, -0.14018567f, -0.02074987f, -0.04621970f, -0.04688627f, -0.01842059f, 0.07722727f, -0.04852883f, 0.01529004f, -0.19639495f, 0.10817073f, 0.03795860f, -0.09435206f, -0.07984378f, -0.03383440f, 0.11081333f, 0.02237366f, 0.12703256f, 0.21613893f, 0.02918790f, 4.66472283e-003f, -0.10274266f, -0.04854131f, -3.46305710e-003f, 0.08652268f, 0.02251546f, 0.09636052f, 0.17180754f, -0.09272388f, 4.59174305e-004f, -0.11723048f, -0.12210111f, -0.15547538f, 0.07218186f, -0.05297846f, 0.03779940f, 0.05150875f, -0.03802310f, 0.03870645f, -0.15250699f, -0.08696499f, -0.02021560f, 0.04118926f, -0.15177974f, 0.01577647f, 0.10249301f, 7.50041893e-003f, 0.01721806f, -0.06828983f, -0.02397596f, -0.06598977f, -0.04317593f, -0.08064980f, 6.66632550e-003f, 0.03333484f, 0.07093620f, 0.08231064f, -0.06577903f, -0.06698844f, -0.06984019f, -0.06508023f, -0.14145090f, -0.02393239f, 0.06485303f, 8.83263443e-003f, 0.09251080f, -0.07557579f, -0.05067699f, -0.09798748f, -0.06703258f, -0.14056294f, 0.03245994f, 0.12554143f, 0.01761621f, 0.12980327f, -0.04081950f, -0.11906909f, -0.14813015f, -0.08376863f, -0.12200681f, 0.04988137f, 0.05424247f, -3.90952639e-003f, 0.03255733f, -0.12717837f, -0.07461493f, -0.05703964f, -0.01736189f, -0.08026433f, -0.05433894f, -0.01719359f, 0.02886275f, 0.01772653f, -0.09163518f, 3.57789593e-003f, -0.10129993f, -0.02653764f, -0.08131415f, -0.03847986f, -7.62157550e-004f, 0.06486648f, 0.19675669f, -0.04919156f, -0.07059129f, -0.04857785f, -0.01042383f, -0.08328653f, 0.03660302f, -0.03696846f, 0.04969259f, 0.08241162f, -0.12514858f, -0.06122676f, -0.03750202f, 6.52989605e-003f, -0.10247213f, 0.02568346f, 4.51781414e-003f, -0.03734229f, -0.01131264f, -0.05412074f, 8.89345480e-004f, -0.12388977f, -0.05959237f, -0.12418608f, -0.06151643f, -0.07310260f, 0.02441575f, 0.07023528f, -0.07548289f, -7.57147965e-004f, -0.09061348f, -0.08112976f, -0.06920306f, 9.54394229e-003f, -0.01219902f, 1.21273217e-003f, -8.88989680e-003f, -0.08309301f, -0.04552661f, -0.10739882f, -0.05691034f, -0.13928030f, 0.09027749f, 0.15123098f, 0.03175976f, 0.17763577f, 3.29913251e-004f, 0.05151888f, -0.09844074f, -0.09475287f, -0.08571247f, 0.16241577f, 0.19336018f, 8.57454538e-003f, 0.11474732f, -0.01493934f, 0.03352379f, -0.08966240f, -0.02322310f, 0.02663568f, 0.05448750f, -0.03536883f, -0.07210463f, -0.06807277f, -0.03121621f, -0.05932408f, -0.17282860f, -0.15873498f, -0.04956378f, 0.01603377f, -0.12385946f, 0.13878587f, 0.21468069f, 0.13510075f, 0.20992437f, 0.08845878f, 0.08104013f, 0.03754176f, 0.12173114f, 0.11103114f, 0.10643122f, 0.13941477f, 0.11640384f, 0.14786847f, 0.01218238f, 0.01160753f, 0.03547940f, 0.08794311f, -0.01695384f, -0.07692261f, -0.08236158f, 6.79194089e-003f, -0.02458403f, 0.13022894f, 0.10953187f, 0.09857773f, 0.04735930f, -0.04353498f, -0.15173385f, -0.17904443f, -0.10450364f, -0.13418166f, -0.06633098f, -0.03170381f, -0.06839000f, -0.11350126f, -0.06983913f, 0.19083543f, 0.17604128f, 0.07730632f, 0.10022651f, 0.36428109f, 0.28291923f, 0.12688625f, 0.15942036f, 0.14064661f, -0.11201853f, -0.13969108f, -0.09088077f, -0.14107047f, 0.05117374f, -2.63348082e-003f, -0.10794610f, -0.09715455f, -0.05284977f, 0.01565668f, 0.05031200f, 0.07021113f, -0.02963028f, 0.01766960f, 0.08333644f, -0.03211382f, 4.90096770e-003f, 0.05186674f, -0.05045737f, -0.09624767f, -0.02525997f, 0.06916669f, 0.01213916f, 0.05333899f, -0.03443280f, -0.10055527f, -0.06291115f, 5.42851724e-003f, -6.30360236e-003f, 0.02270257f, -0.01769792f, 0.03273688f, 0.07746078f, 7.77099328e-003f, 0.05041346f, 0.01648103f, -0.02321534f, -0.09930186f, -0.02293853f, 0.02034990f, -0.08324204f, 0.08510064f, -0.03732836f, -0.06465405f, -0.06086946f, 0.13680504f, -0.11469388f, -0.03896406f, -0.07142810f, 2.67581246e-003f, -0.03639632f, -0.09849060f, -0.11014334f, 0.17489147f, 0.17610909f, -0.16091567f, -0.07248894f, 0.01567141f, 0.23742996f, 0.07552249f, -0.06270349f, -0.07303379f, 0.25442186f, 0.16903116f, -0.08168741f, -0.05913896f, -0.03954096f, 6.81776879e-003f, -0.05615319f, -0.07303037f, -0.12176382f, 0.12385108f, 0.22084464f, -0.05543206f, -0.03310431f, 0.05731593f, 0.19481890f, 0.04016430f, -0.06480758f, -0.12353460f, 0.18733442f, -0.09631214f, -0.11192076f, 0.12404587f, 0.15671748f, 0.19256128f, 0.10895617f, 0.03391477f, -0.13032004f, -0.05626907f, -0.09025607f, 0.23485197f, 0.27812332f, 0.26725492f, 0.07255980f, 0.16565137f, 0.22388470f, 0.07441066f, -0.21003133f, -0.08075339f, -0.15031935f, 0.07023834f, 0.10872041f, 0.18156518f, 0.20037253f, 0.13571967f, -0.11915682f, -0.11131983f, -0.18878011f, 0.06074620f, 0.20578890f, 0.12413109f, 0.03930207f, 0.29176015f, 0.29502738f, 0.27856228f, -0.01803601f, 0.16646385f, 0.19268319f, 0.01900682f, 0.06026287f, 2.35868432e-003f, 0.01558199f, 0.02707230f, 0.11383014f, 0.12103992f, 0.03907350f, 0.04637353f, 0.09020995f, 0.11919726f, -3.63007211e-003f, 0.02220155f, 0.10336831f, 0.17351882f, 0.12259731f, 0.18983354f, 0.15736865f, 0.01160725f, -0.01690723f, -9.69582412e-004f, 0.07213813f, 0.01161613f, 0.17864859f, 0.24486147f, 0.18208991f, 0.20177495f, 0.05972528f, -8.93934630e-003f, -0.02316955f, 0.14436610f, 0.14114498f, 0.05520950f, 0.06353590f, -0.19124921f, 0.10174713f, 0.29414919f, 0.26448128f, 0.09344960f, 0.15284036f, 0.19797507f, 0.11369792f, -0.12722753f, -0.21396367f, -0.02008235f, -0.06566695f, -0.01662150f, -0.03937003f, 0.04778343f, 0.05017274f, -0.02299062f, -0.20208496f, -0.06395898f, 0.13721776f, 0.22544557f, 0.14888357f, 0.08687132f, 0.27088094f, 0.32206613f, 0.09782200f, -0.18523243f, -0.17232181f, -0.01041531f, 0.04008654f, 0.04199702f, -0.08081299f, -0.03755421f, -0.04809646f, -0.05222081f, -0.21709201f, -0.06622940f, 0.02945281f, -0.04600435f, -0.05256077f, -0.08432942f, 0.02848100f, 0.03490564f, 8.28621630e-003f, -0.11051246f, -0.11210597f, -0.01998289f, -0.05369405f, -0.08869293f, -0.18799506f, -0.05436598f, -0.05011634f, -0.05419716f, -0.06151857f, -0.10827805f, 0.04346735f, 0.04016083f, 0.01520820f, -0.12173316f, -0.04880285f, -0.01101406f, 0.03250847f, -0.06009551f, -0.03082932f, -0.02295134f, -0.06856834f, -0.08775249f, -0.23793389f, -0.09174541f, -0.05538322f, -0.04321031f, -0.11874759f, -0.04221844f, -0.06070468f, 0.01194489f, 0.02608565f, -0.03892140f, -0.01643151f, -0.02602034f, -0.01305472f, 0.03920100f, -0.06514261f, 0.01126918f, -6.27710763e-003f, -0.02720047f, -0.11133634f, 0.03300330f, 0.02398472f, 0.04079665f, -0.10564448f, 0.05966159f, 0.01195221f, -0.03179441f, -0.01692590f, -0.06177841f, 0.01841576f, -5.51078189e-003f, -0.06821765f, -0.03191888f, -0.09545476f, 0.03030550f, -0.04896152f, -0.02914624f, -0.13283344f, -0.04783419f, 6.07836898e-003f, -0.01449538f, -0.13358212f, -0.09687774f, -0.02813793f, 0.01213498f, 0.06650011f, -0.02039067f, 0.13356198f, 0.05986415f, -9.12760664e-003f, -0.18780160f, -0.11992817f, -0.06342237f, 0.01229534f, 0.07143231f, 0.10713009f, 0.11085765f, 0.06569190f, -0.02956399f, -0.16288325f, -0.13993549f, -0.01292515f, 0.03833013f, 0.09130384f, -0.05086257f, 0.05617329f, -0.03896667f, -0.06282311f, -0.11490010f, -0.14264110f, -0.04530499f, 0.01598189f, 0.09167797f, 0.08663294f, 0.04885277f, -0.05741219f, -0.07565769f, -0.17136464f, -0.02619422f, -0.02477579f, 0.02679587f, 0.11621952f, 0.08788391f, 0.15520640f, 0.04709549f, 0.04504483f, -0.10214074f, -0.12293372f, -0.04820546f, -0.05484834f, 0.05473754f, 0.07346445f, 0.05577277f, -0.08209965f, 0.03462975f, -0.20962234f, -0.09324598f, 3.79481679e-003f, 0.03617633f, 0.16742408f, 0.07058107f, 0.10204960f, -0.06795346f, 3.22807301e-003f, -0.12589309f, -0.17496960f, 0.02078314f, -0.07694324f, 0.12184640f, 0.08997164f, 0.04793497f, -0.11383379f, -0.08046359f, -0.25716835f, -0.08080962f, 6.80711539e-003f, -0.02930280f, -3.04938294e-003f, -0.11106286f, -0.04628860f, -0.07821649f, 7.70127494e-003f, -0.10247706f, 1.21042714e-003f, 0.20573859f, -0.03241005f, 8.42972286e-003f, 0.01946464f, -0.01197973f, -0.14579976f, 0.04233614f, -4.14096704e-003f, -0.06866436f, -0.02431862f, -0.13529138f, 1.25891645e-003f, -0.11425111f, -0.04303651f, -0.01694815f, 0.05720210f, -0.16040207f, 0.02772896f, 0.05498345f, -0.15010567f, 0.01450866f, 0.02350303f, -0.04301004f, -0.04951802f, 0.21702233f, -0.03159155f, -0.01963303f, 0.18232647f, -0.03263875f, -2.88476888e-003f, 0.01587562f, -1.94303901e-003f, -0.07789494f, 0.04674156f, -6.25576358e-003f, 0.08925962f, 0.21353747f, 0.01254677f, -0.06999976f, -0.05931328f, -0.01884327f, -0.04306272f, 0.11794136f, 0.03842728f, -0.03907030f, 0.05636114f, -0.09766009f, -0.02104000f, 8.72711372e-003f, -0.02736877f, -0.05112274f, 0.16996814f, 0.02955785f, 0.02094014f, 0.08414304f, -0.03335762f, -0.03617457f, -0.05808248f, -0.08872101f, 0.02927705f, 0.27077839f, 0.06075108f, 0.07478261f, 0.15282831f, -0.03908454f, -0.05101782f, -9.51998029e-003f, -0.03272416f, -0.08735625f, 0.07633440f, -0.07185312f, 0.13841286f, 0.07812646f, -0.12901451f, -0.05488589f, -0.05644578f, -0.03290703f, -0.11184757f, 0.03751570f, -0.05978153f, -0.09155276f, 0.05657315f, -0.04328186f, -0.03047933f, -0.01413135f, -0.10181040f, -0.01384013f, 0.20132534f, -0.01536873f, -0.07641169f, 0.05906778f, -0.07833145f, -0.01523801f, -0.07502609f, -0.09461885f, -0.15013233f, 0.16050665f, 0.09021381f, 0.08473236f, 0.03386267f, -0.09147339f, -0.09170618f, -0.08498498f, -0.05119187f, -0.10431040f, 0.01041618f, -0.03064913f, 0.09340212f, 0.06448522f, -0.03881054f, -0.04985436f, -0.14794017f, -0.05200112f, -0.02144495f, 0.04000821f, 0.12420804f, -0.01851651f, -0.04116732f, -0.11951703f, -0.04879033f, -0.08722515f, -0.08454733f, -0.10549165f, 0.11251976f, 0.10766345f, 0.19201984f, 0.06128913f, -0.02734615f, -0.08834923f, -0.16999826f, -0.03548348f, -5.36092324e-003f, 0.08297954f, 0.07226378f, 0.04194529f, 0.04668673f, 8.73902347e-003f, 0.06980139f, 0.05652480f, 0.05879445f, 0.02477076f, 0.02451423f, 0.12433673f, 0.05600227f, 0.06886370f, 0.03863076f, 0.07459056f, 0.02264139f, 0.01495469f, 0.06344220f, 0.06945208f, 0.02931899f, 0.11719371f, 0.04527427f, 0.03248192f, 2.08271481e-003f, 0.02044626f, 0.11403449f, 0.04303892f, 0.06444661f, 0.04959024f, 0.08174094f, 0.09240247f, 0.04894639f, 0.02252937f, -0.01652530f, 0.07587013f, 0.06064249f, 0.13954395f, 0.02772832f, 0.07093039f, 0.08501238f, 0.01701301f, 0.09055722f, 0.33421436f, 0.20163782f, 0.09821030f, 0.07951369f, 0.08695120f, -0.12757730f, -0.13865978f, -0.06610068f, -0.10985506f, 0.03406816f, -0.01116336f, -0.07281768f, -0.13525715f, -0.12844718f, 0.08956250f, 0.09171610f, 0.10092317f, 0.23385370f, 0.34489515f, 0.09901748f, 0.02002922f, 0.12335990f, 0.07606190f, -0.14899330f, -0.15634622f, -0.06494618f, -0.01760547f, 0.03404277f, -0.13208845f, -0.12101169f, -0.18294574f, -0.16560709f, 0.02183887f, -0.02752613f, 0.01813638f, 0.02000757f, 0.01319924f, 0.08030242f, 0.01220535f, 2.98233377e-003f, -0.01307070f, 0.05970297f, -0.05345284f, -0.03381982f, -9.87543724e-003f, -0.06869387f, 0.03956730f, -0.03108176f, -0.05732809f, 0.02172386f, 0.04159765f, 2.62783933e-003f, 0.04813229f, 0.09358983f, -8.18389002e-003f, 0.01724574f, -0.02547474f, -0.04967288f, -0.02390376f, 0.06640504f, -0.06306566f, 0.01137518f, 0.05589378f, -0.08237787f, 0.02455001f, -0.03059422f, -0.08953978f, 0.06851497f, 0.07190268f, -0.07610799f, 7.87237938e-003f, -7.85830803e-003f, 0.06006952f, -0.01126728f, -2.85743061e-003f, -0.04772895f, 0.01884944f, 0.15005857f, -0.06268821f, -0.01989072f, 0.01138399f, 0.08760451f, 0.03879007f, -9.66926850e-003f, -0.08012961f, 0.06414555f, -0.01362950f, -0.09135523f, 0.01755159f, 0.04459474f, 0.09650917f, 0.05219948f, -2.19440833e-003f, -0.07037939f, -0.01599054f, 0.13103317f, -0.02492603f, -0.01032540f, -0.02903307f, 0.04489160f, 0.05148086f, 0.01858173f, -0.02919228f, 0.08299296f, -0.04590359f, -0.15745632f, -0.09068198f, -0.02972453f, 0.12985018f, 0.22320485f, 0.24261914f, 0.03642650f, -0.05506422f, 2.67413049e-003f, -0.03834032f, 0.06449424f, 0.03834866f, 0.03816991f, 0.25039271f, 0.34212017f, 0.32433882f, 0.18824573f, -0.08599839f, -0.17599408f, -0.15317015f, -0.09913155f, -0.02856072f, -0.05304699f, -1.06437842e-003f, -0.06641813f, -0.07509298f, 0.01463361f, -0.07551918f, -0.04510373f, -8.44620075e-003f, 0.01772176f, 0.04068235f, 0.20295307f, 0.15719447f, 0.05712103f, 0.26296997f, 0.14657754f, 0.01547317f, -0.05052776f, -0.03881342f, -0.01437883f, -0.04930177f, 0.11719568f, 0.24098417f, 0.26468599f, 0.31698579f, 0.10103608f, -0.01096375f, -0.01367013f, 0.17104232f, 0.20065314f, 2.67622480e-003f, -0.01190034f, 0.18301608f, 0.09459770f, -0.06357619f, -0.06473801f, 0.01377906f, -0.10032775f, -0.06388740f, 3.80393048e-003f, 0.06206078f, 0.10349120f, 0.26804337f, 8.17918684e-003f, -0.02314351f, 9.34422202e-003f, 0.09198381f, 0.03681326f, -8.77339672e-003f, -0.09662418f, -0.02715708f, 0.13503517f, 0.08962728f, -6.57071499e-003f, -0.03201199f, 0.28510824f, 0.32095715f, 0.18512695f, -0.14230858f, -0.14048551f, -0.07181299f, -0.08575408f, -0.08661680f, -0.17416079f, 7.54326640e-004f, 0.05601677f, 0.13585392f, -0.04960437f, -0.07708392f, 0.10676333f, -0.04407546f, -0.07209078f, 0.03663663f, 0.28949317f, 0.41127121f, 0.27431169f, -0.06900328f, -0.21474190f, -0.15578632f, -0.19555484f, -0.15209621f, -0.11269179f, 0.07416003f, 0.18991330f, 0.26858172f, 0.01952259f, 0.01017922f, 0.02159843f, -4.95165400e-003f, -0.04368168f, -0.12721671f, -0.06673957f, -0.11275250f, 0.04413409f, 0.05578312f, 0.03896771f, 0.03566417f, -0.05871816f, -0.07388090f, -0.17965563f, -0.08570268f, -0.15273231f, -0.06022318f, -0.06999847f, -6.81510568e-003f, 0.06294262f, -6.54901436e-004f, -0.01128654f, -0.02289657f, 0.04849290f, 0.04140804f, 0.23681939f, 0.14545733f, 0.01989965f, 0.12032662f, 3.87463090e-003f, -6.02597650e-003f, -0.05919775f, -0.03067224f, -0.07787777f, 0.10834727f, 0.02153730f, 0.02765649f, 0.03975543f, -0.12182906f, -0.04900113f, -0.09940100f, -0.06453611f, -0.13757215f, -0.03721382f, 0.02827376f, -0.04351249f, 0.01907038f, -0.10284120f, -0.05671160f, -0.10760647f, -0.09624009f, -0.09565596f, -0.01303654f, 0.03080539f, 0.01416511f, 0.05846142f, -5.42971538e-003f, 0.06221476f, -0.03320325f, -0.06791797f, -0.05791342f, 0.12851369f, 0.14990346f, 0.03634374f, 0.14262885f, 0.04330391f, 0.05032569f, -0.05631914f, 0.01606137f, 0.04387223f, 0.22344995f, 0.15722635f, -0.04693628f, 0.03006579f, -2.52882647e-003f, 0.05717621f, -0.07529724f, -0.02848588f, -0.06868757f, -4.51729307e-003f, 0.06466042f, -0.05935378f, -0.04704857f, -0.07363959f, 0.04843248f, -0.13421375f, -0.09789340f, -0.10255270f, 0.03509852f, 0.04751543f, -0.03822323f, 0.09740467f, 0.04762916f, 0.03940146f, -0.08283259f, 0.09552965f, 0.05038739f, 0.21258622f, 0.09646992f, 0.03241193f, 0.05167701f, 0.04614570f, 0.04330090f, -0.02671840f, -0.06259909f, -0.02301898f, 0.18829170f, 0.10522786f, 0.04313190f, 0.01670948f, -0.08421925f, 0.05911417f, -0.10582602f, -0.04855484f, -0.08373898f, 0.07775915f, 0.03723533f, -0.12047344f, 4.86345543e-003f, -0.10520902f, 0.06571782f, -0.07528137f, -0.03245651f, -0.09869066f, -0.02917477f, -0.18293270f, 0.14810945f, 9.24033765e-003f, -0.04354914f, 0.02266885f, -0.11872729f, -0.04016589f, 0.02830229f, 0.22539048f, 0.20565644f, 0.16701797f, 0.09019924f, 0.01300652f, 0.09760600f, -0.03675831f, -0.01935448f, -0.06894835f, 0.08077277f, 0.19047537f, 0.11312226f, 0.04106043f, -0.11187182f, 0.04312806f, -0.18548580f, -0.11287174f, -0.08794551f, 0.02078281f, -0.15295486f, 0.11806386f, -0.01103218f, -0.15971117f, 0.02153538f, -0.05232147f, -0.10835317f, -0.13910367f, 0.05920752f, -0.10122602f, 0.20174250f, 0.09105796f, -0.01881348f, 0.09559010f, -0.03725745f, -0.09442931f, -0.09763174f, 0.05854454f, 0.08287182f, 0.12919849f, 0.08594352f, -2.49806582e-003f, 0.02398440f, 5.67950122e-003f, -0.06296340f, -0.12993270f, 0.03855852f, 0.05186560f, 0.10839908f, -0.03380463f, -0.12654832f, -0.05399339f, -0.07456800f, -0.04736232f, -0.10164231f, 0.07496139f, 0.08125214f, 0.07656177f, -0.04999603f, -0.12823077f, -0.07692395f, -0.11317524f, -0.09118655f, -0.05695669f, 0.10477209f, 0.07468581f, 0.01630048f, -8.00961629e-003f, -0.06582128f, -0.04019095f, -0.04682907f, -0.01907842f, -0.10997720f, 0.04911406f, 0.02931030f, 0.04197735f, -0.05773980f, -0.09670641f, -0.03594951f, -0.03402121f, -0.07149299f, -0.10566200f, 0.10601286f, 0.06340689f, -0.01518632f, -5.96402306e-003f, -0.07628012f, -3.52779147e-003f, -0.02683854f, -0.10265494f, -0.02680815f, 0.16338381f, 0.03103515f, 0.02296976f, 0.01624348f, -0.10831620f, -0.02314233f, -0.04789969f, -0.05530700f, -0.06461314f, 0.10494506f, 0.04642856f, -0.07592955f, -0.06197905f, -0.09042154f, -0.01445521f, -0.04297818f, -0.11262015f, -0.11430512f, 0.03174541f, -0.03677487f, -0.02963996f, -0.06610169f, -0.13292049f, -0.07059067f, -0.08444111f, -0.02640536f, -0.07136250f, 0.04559967f, 0.01459980f, 0.17989251f, 0.04435328f, -0.12464730f, -0.02871115f, -0.10752209f, -0.03393742f, -0.03791408f, 0.02548251f, 0.01956050f, 0.19245651f, 0.13963254f, -0.05904696f, -0.07424626f, -0.10411884f, 1.54176133e-003f, 0.01797429f, 0.13025844f, 0.04547642f, -0.05710349f, -0.10697161f, -0.13489437f, -0.06515755f, -0.06406886f, -4.08572936e-003f, -0.01336483f, 0.04368737f, -0.11259720f, -0.05701635f, -0.06469971f, -0.08346602f, -0.04166770f, -0.05795543f, -0.08247511f, -0.05742628f, 0.08452254f, -0.03350224f, 0.13980860f, 0.13252275f, 0.07589617f, 0.07539988f, 0.12155797f, 0.19087289f, 0.15050751f, 0.21250245f, 0.14206800f, 0.01298489f, 0.07450245f, 0.06559097f, 0.01700557f, 0.04512971f, 0.16950700f, 0.10261577f, 0.16389982f, 0.05505059f, -0.03453077f, 0.08622462f, 0.07935954f, 0.03976260f, 0.02036091f, 3.95744899e-003f, 0.03267065f, 0.15235919f, 0.01297494f, -0.08109194f, 0.01407558f, 4.40693414e-003f, -0.15157418f, -0.11390478f, -0.07487597f, -7.81322457e-003f, -0.02749545f, -0.10181408f, 0.13755716f, 0.14007211f, 0.13482562f, 0.27517235f, 0.34251109f, 0.07639657f, 0.07268607f, 0.19823882f, 0.16135791f, -0.04186463f, -0.12784107f, -0.09846287f, 0.03169041f, 0.10974082f, -0.15051922f, -0.08916726f, -0.07138767f, -0.04153349f, 6.25418453e-003f, 0.01266654f, 0.10533249f, 0.12749144f, 0.15148053f, 0.01498513f, 0.06305949f, -0.01247123f, -0.08778401f, -0.08551880f, -0.11955146f, -0.08493572f, -0.02901620f, -0.02394859f, -0.13427313f, -0.11053200f, -0.14413260f, -0.15203285f, 0.03972760f, -3.72127310e-004f, -0.04200919f, 0.06105104f, 0.01904975f, -0.01106191f, -7.27445772e-003f, -0.01520341f, 1.10228511e-003f, -0.04949187f, -0.08013099f, 5.72071038e-003f, 0.08415454f, -0.06523152f, 0.03664081f, -0.02673042f, -0.12066154f, -0.03702074f, 0.06006580f, 0.01628682f, -6.17772620e-003f, 0.08192339f, -3.41629819e-003f, 0.02870512f, 0.05807141f, 0.04959986f, 0.04618251f, -0.04901629f, -0.10579574f, 0.02274442f, 0.12070961f, 2.23597488e-003f, 0.09831765f, -0.03019848f, -0.11181970f, -0.04961075f, 0.02498928f, -0.03714991f, -0.01619653f, 0.02643486f, -7.62964319e-003f, -0.02882290f, -0.06242594f, -0.08439861f, 0.07220893f, 0.07263952f, 0.01561574f, 0.03091968f, 0.01708712f, -0.03797151f, -3.18561122e-003f, 0.01624021f, -0.02828573f, 0.11284444f, -1.32280716e-003f, -0.07784860f, -0.07209100f, 0.03372242f, 0.12154529f, 0.02278104f, -0.05275500f, -0.01918484f, 0.12989293f, 0.05424401f, 0.02333086f, 0.04029022f, 0.12392918f, 0.09495489f, 0.09190340f, 0.07935889f, 8.76816828e-003f, 0.17148446f, -8.51302687e-003f, -0.08011249f, -0.06796283f, 0.04884845f, 0.01112272f, -0.07835306f, -1.14811445e-003f, -0.03440760f, 0.02845243f, 0.07695542f, -0.07069533f, -0.01151784f, -8.53884313e-003f, -0.01662786f, -0.04163864f, 0.05400505f, 0.02859163f, 0.02921852f, 0.05003135f, -6.85718050e-003f, -0.01632611f, 0.07780217f, 0.04042810f, -0.01216440f, 3.60914599e-003f, -0.06322435f, 0.09516726f, 0.12877031f, -9.69162490e-003f, 0.01031179f, 0.05180895f, -9.34659224e-003f, -0.01644533f, -0.04849347f, -0.04343236f, 0.10514783f, 0.08046635f, -0.04615205f, -0.03975486f, -0.01485525f, 0.13096830f, -0.01517950f, -0.06571898f, -0.04016372f, 0.01849786f, 0.02439670f, 0.08067258f, 1.74824719e-003f, 0.07053747f, 0.08819518f, -5.08352555e-003f, -0.06550863f, -0.08266170f, -0.07780605f, 0.01453450f, -0.08756890f, 0.01096501f, -8.71319138e-003f, 0.10110464f, 0.02420769f, -0.06708383f, 0.02007811f, 5.93133038e-003f, 0.05398923f, 0.07538138f, 0.02049227f, 0.02242589f, 0.04011070f, -1.44875818e-003f, -4.19115182e-003f, 0.06367654f, 0.02506934f, 0.02434536f, 0.05879405f, -8.22952855e-003f, -0.01242441f, 0.04224926f, -0.01754923f, 0.05958161f, 0.03818886f, -0.01830363f, -0.04308917f, -0.04422197f, -0.02432721f, 0.02264866f, 2.03751423e-003f, 0.01197031f, 0.04439203f, 0.12169247f, 0.03602713f, -0.02599251f, -1.98226492e-003f, 0.02046336f, -0.02639058f, -1.91242550e-003f, -0.09334669f, -0.03595153f, -9.88179818e-003f, -0.06848445f, -0.04666303f, -0.09955736f, -0.04206430f, 0.02609075f, 9.09005292e-003f, -0.07138551f, -4.22313227e-004f, 0.01766645f, 0.02756404f, 0.01308276f, 0.04052891f, 0.02387515f, 0.05337298f, 0.02500631f, -0.04970853f, -0.12467445f, 0.17604403f, 0.12256411f, -0.07512254f, 8.70451052e-003f, -0.05697548f, -0.03626474f, -8.76623299e-003f, -0.01210897f, -0.09451522f, 0.07490732f, -0.02008001f, -0.02681278f, -0.06463405f, -0.01517507f, 7.33757764e-003f, 6.07147906e-003f, -0.09316964f, -0.04575328f, 0.13261597f, 0.15424870f, -0.01655918f, -0.02772390f, -0.05243644f, -0.02356456f, -0.02351753f, -0.10211615f, -0.12873036f, 0.14549787f, 0.12519856f, 4.38762689e-003f, 0.02795992f, 0.05170322f, 0.09223596f, 0.05890015f, 0.02376701f, -0.02777346f, 0.09506908f, 0.02328936f, -0.02319928f, -0.03218696f, -0.01527841f, -0.01016694f, -0.02674719f, 0.05137179f, 0.01980666f, 0.06544447f, -0.01746171f, 0.01026380f, 0.01561806f, 7.97004555e-004f, 0.07601810f, 0.01907250f, -0.03083035f, -0.05987392f, 0.09242783f, 0.14555025f, 0.01035827f, 0.03092401f, -0.09562709f, -0.03802354f, 0.02531144f, 0.03079449f, -0.07100715f, 0.03330721f, -2.69116857e-003f, 0.03167490f, 0.05744999f, 0.03259895f, 1.91266940e-003f, 0.03194578f, 0.07389776f, 0.02198060f, 0.07633314f, 0.03293105f, -0.09103648f, 0.04718142f, 0.06102672f, -0.01003063f, 5.85481385e-003f, -0.01522574f, 0.02323526f, 0.10584345f, 4.35879454e-003f, 0.06107873f, 0.05868603f, -0.03115531f, 0.01214679f, 0.08567052f, 3.93926632e-003f, -0.02521488f, -1.88425183e-003f, 0.02038053f, -6.26854831e-004f, 0.04897438f, -0.04280585f, -0.04819689f, -0.04812867f, -0.01451186f, 0.05101469f, -9.01125465e-003f, -0.03333859f, 0.03917955f, 0.04196448f, 0.04292135f, 0.02809529f, 0.02999715f, 0.04081348f, 9.10039060e-003f, 0.09703232f, 0.10379741f, 0.02348725f, -4.72756615e-003f, 0.01027325f, 0.10402658f, 0.12071823f, 0.09817299f, -0.02612033f, 0.03638414f, 0.05896405f, 0.04865025f, 0.04793910f, -0.03882321f, -0.02962117f, -0.01222268f, 0.04071597f, 0.01922777f, -0.02287866f, 0.03328381f, 0.01859092f, 0.09024994f, 0.03804455f, -0.01424510f, 0.01953739f, 0.02509617f, -0.03390914f, -0.05663941f, -0.01641979f, 0.05848591f, 0.04639670f, 0.02092116f, 0.12911791f, 0.19918139f, 0.07739855f, -7.25806039e-003f, 0.04074838f, 0.03183993f, 1.39251316e-003f, -0.01428625f, 0.01865480f, 0.08529541f, 0.13547510f, 0.11189661f, 0.03998901f, 0.09575938f, -0.02631102f, -0.03458253f, -0.04749985f, -0.06070716f, 4.71884012e-003f, 0.06445789f, -0.02450038f, -0.05483776f, -0.04657237f, -0.02030717f, -0.03480766f, -0.09397731f, -0.06399718f, -0.01804585f, 5.62348310e-003f, -6.64811488e-003f, -0.06517869f, 6.96210237e-003f, -0.01860148f, -0.04245830f, -0.05850367f, -3.24417115e-003f, 0.07700698f, 0.11290991f, 0.09923030f, -0.02970599f, 0.05592411f, 0.04813979f, -0.09811195f, -0.09357996f, -0.03276114f, 0.05218338f, 0.04141375f, 3.92977800e-003f, -0.05047480f, 0.15960084f, 0.04612800f, -0.03114098f, -0.04650044f, -0.03249795f, -0.02425641f, -0.04311355f, 0.04307659f, -0.09401883f, -0.04742785f, -0.01254499f, -0.06598741f, 3.41369561e-003f, -0.05620445f, -7.28127593e-003f, -0.05998361f, -0.03274450f, -0.07376868f, 3.19015374e-003f, -0.07733069f, 0.05815864f, -0.02471071f, 0.03850617f, 0.13838784f, 0.15399861f, 0.01731321f, -0.01477586f, 0.10393341f, 0.05159833f, -0.01945555f, -0.03427503f, -0.04867341f, 0.09237480f, 0.10732719f, 0.06071450f, -0.01355071f, 0.01844356f, -0.03480803f, -0.03796671f, 2.15628621e-004f, -0.05440186f, 0.01889855f, -0.01443413f, -0.02607902f, -0.02938001f, 0.02720689f, -0.06228397f, -0.02970936f, -0.03426210f, -0.10280876f, -0.06739304f, -0.05227850f, 0.03360292f, -0.11278441f, -0.06966180f, -0.13937433f, 9.10932291e-003f, 2.52020749e-004f, -4.07359656e-003f, 0.12310639f, 0.09343060f, 0.07302511f, 0.03222093f, 0.07532879f, 0.03792387f, -0.04985180f, 0.01804602f, 0.02694195f, 0.13481498f, 0.04601225f, 0.04106982f, 0.08511057f, 0.12314661f, 0.01320830f, 0.05044121f, -5.52943908e-003f, -0.08992624f, -0.02249301f, -0.08181777f, 0.06165213f, -0.03256603f, -0.01068920f, -0.01323473f, -0.11970232f, -0.04616347f, -0.12088681f, -0.06762606f, -0.08676834f, -0.06434575f, 0.01772529f, 0.03469615f, -0.10926618f, 0.03013873f, 0.14030397f, 0.16130108f, 0.17985588f, 0.11281928f, 0.10530639f, 0.08905948f, 0.07733764f, 0.06695238f, 0.02142088f, 0.06438877f, 0.09794453f, 0.05745072f, 0.02788557f, 0.02632830f, 0.07985807f, 4.24902979e-003f, 8.47890321e-003f, -0.02679466f, -5.28812688e-003f, -0.02162580f, -0.07490715f, -0.08251337f, -0.02056576f, -0.01026194f, -1.15492963e-003f, -5.75720915e-004f, -0.07210591f, -0.07320981f, -0.04883312f, -0.10897151f, -0.07477258f, -0.08867134f, -0.09222437f, -0.10924666f, -0.10430276f, 0.07953499f, 0.02767959f, 0.11393359f, 0.18779543f, 0.03313421f, 0.02143700f, 0.05852016f, -2.12067598e-003f, -3.76984011e-003f, 0.02774167f, -0.03124610f, 0.01465141f, 0.01616004f, -0.01391913f, -0.04404102f, -0.05444227f, -0.14684731f, -0.15016587f, 0.04509468f, 1.29563001e-003f, 0.01398350f, 0.05610404f, -0.04868806f, -0.04776716f, -8.16873740e-003f, -2.30126386e-003f, -0.02286313f, 0.11983398f, -0.04703261f, -0.08814441f, -0.07585249f, -0.10799607f, -0.03232087f, 0.01509786f, -0.04843464f, -0.03967846f, 0.09589416f, 0.01352560f, -0.01458119f, 0.01050829f, -0.03038946f, 0.01608388f, 1.11975556e-003f, -0.01250656f, 2.86211423e-003f, 0.04333691f, -0.14603497f, -0.01946543f, -0.02327525f, -0.01973944f, 0.07944400f, -0.02224544f, -0.06701808f, 0.03476532f, 0.11505594f, -0.02712801f, -0.01665113f, 0.06315716f, -0.08205860f, 0.07431999f, 0.04915778f, -0.04468752f, -0.01490402f, 0.07400476f, -0.11650901f, 0.05102430f, 0.04559118f, -0.05916039f, 0.08840760f, -0.01587902f, -0.14890194f, 0.07857784f, 0.04710254f, -0.05381983f, -0.07331945f, -0.03604643f, 0.15611970f, 0.07649943f, -0.05959348f, -0.02776607f, 0.11098688f, 0.03758875f, -0.04446875f, 0.04933187f, 0.01345535f, 0.06921103f, 0.07364785f, 0.05518956f, 0.02899585f, 0.09375840f, 0.10518434f, -0.04420241f, 0.01915282f, -3.56386811e-003f, 0.14586878f, 0.10286101f, -0.04360626f, -0.12723237f, 0.09076386f, 0.11119842f, -0.06035013f, 0.09674817f, 0.08938243f, 0.07065924f, 0.02603180f, 5.84815582e-003f, -0.05922065f, 0.12360309f, 3.59695964e-003f, 2.99844006e-003f, 0.03697936f, 0.02043072f, 0.04168725f, 0.01025975f, -0.01359980f, -0.01600920f, 0.02581056f, 0.02329250f, 2.98100687e-003f, 0.01629762f, 0.06652115f, 0.05855627f, 0.01237463f, -0.01297135f, 0.01761587f, 0.05090865f, 0.06549342f, -0.04425945f, 2.43203156e-003f, 3.07327788e-003f, 0.06678630f, -0.04303836f, 0.01082393f, -0.06476044f, 0.04077786f, 0.12441979f, 0.08237778f, 0.07424165f, 0.04065890f, 0.06905543f, 0.09556347f, 0.12724875f, -0.02132082f, 0.08514154f, -0.04175328f, -0.02666954f, 0.01897836f, 0.03317382f, 9.45465732e-003f, -0.01238974f, -0.04242500f, -0.01419479f, -0.03545213f, -0.02440874f, 0.08684119f, 0.04212951f, 0.02462858f, -0.01104825f, -5.01706870e-003f, 0.02968982f, 0.02597476f, -0.01568939f, 0.04514892f, 0.06974549f, 0.08670278f, 0.06828108f, 0.10238872f, 0.05405957f, 0.06548470f, -0.03763957f, 0.01366090f, 0.07069602f, 0.05363748f, 0.04798120f, 0.11706422f, 0.05466456f, -0.01869259f, 0.06344382f, 0.03106543f, 0.08432506f, -0.02061096f, 0.03821088f, -6.92190882e-003f, 6.40467042e-003f, -0.01271779f, 6.89014705e-005f, 0.04541415f, -0.01899539f, -0.05020239f, 0.03000903f, 0.01090422f, 4.52452758e-003f, 0.02573632f, -0.02388454f, -0.04200457f, 1.72783900e-003f, -0.05978370f, -0.02720562f, 0.06573715f, 0.01154317f, 0.01265615f, 0.07375994f, -9.19828378e-003f, -0.04914120f, 0.02124831f, 0.06455322f, 0.04372910f, -0.03310043f, 0.03605788f, -6.78055827e-003f, 9.36202332e-003f, 0.01747596f, -0.06406314f, -0.06812935f, 0.08080816f, -0.02778088f, 0.02735260f, 0.06393493f, 0.06652229f, 0.05676993f, 0.08640018f, -7.59188086e-003f, -0.02012847f, -0.04741159f, -0.01657069f, -0.01624399f, 0.05547778f, -2.33309763e-003f, 0.01120033f, 0.06141156f, -0.06285004f, -0.08732341f, -0.09313398f, -0.04267832f, 5.57443965e-003f, 0.04809862f, 0.01773641f, 5.37361018e-003f, 0.14842421f, -0.06298012f, -0.02935147f, 0.11443478f, -0.05034208f, 5.65494271e-003f, 0.02076526f, -0.04577984f, -0.04735741f, 0.02961071f, -0.09307127f, -0.04417921f, -0.04990027f, -0.03940028f, 0.01306016f, 0.06267900f, 0.03758737f, 0.08460117f, 0.13858789f, 0.04862388f, -0.06319809f, -0.05655516f, 0.01885816f, -0.03285607f, 0.03371567f, -0.07040928f, -0.04514049f, 0.01392166f, 0.08184422f, -0.07230316f, 0.02386871f, 0.02184591f, 0.02605764f, -0.01033954f, 9.29878280e-003f, 7.67351175e-003f, 0.15189242f, 0.02069071f, -0.09738296f, -0.08894105f, -0.07768748f, 0.02332268f, -0.01778995f, -0.03258888f, -0.08180822f, -0.08492987f, 0.02290156f, -0.11368170f, -0.03554465f, -0.04533844f, -0.02861580f, 0.06782424f, 0.01113123f, 0.02453644f, 0.12721945f, 0.08084814f, -0.03607795f, 0.01109122f, 0.04803548f, -0.03489929f, 0.03399536f, -0.05682014f, 8.59533902e-003f, -4.27904585e-003f, 0.03230887f, -0.01300198f, -0.01038137f, -0.07930113f, 8.33097473e-003f, 0.02296994f, -0.01306500f, -0.01881626f, 0.04413369f, 0.05729880f, -0.03761553f, 0.01942326f, 1.64540811e-003f, -0.03811319f, 0.04190650f, -0.14978096f, -0.04514487f, 0.01209545f, -5.46460645e-003f, -0.01647195f, 7.63064111e-003f, -0.07494587f, 0.08415288f, 0.10020141f, -0.01228561f, 0.06553826f, 0.04554005f, 0.07890417f, 0.03041138f, 0.01752007f, 0.09208256f, -3.74419295e-004f, 0.10549527f, 0.04686913f, 0.01894833f, -0.02651412f, -4.34682379e-003f, 5.44942822e-003f, 0.01444484f, 0.05882156f, -0.03336544f, 0.04603891f, -0.10432546f, 0.01923928f, 0.01842845f, -0.01712168f, -0.02222766f, 0.04693324f, -0.06202956f, -0.01422159f, 0.08732220f, -0.07706107f, 0.02661049f, -0.04300238f, -0.03092422f, -0.03552184f, -0.01886088f, -0.04979934f, 0.03906401f, 0.04608644f, 0.04966111f, 0.04275464f, -0.04621769f, -0.02653212f, 8.57011229e-003f, 0.03839684f, 0.05818764f, 0.03880796f, -2.76100676e-004f, 0.03076511f, -0.03266929f, -0.05374557f, 0.04986527f, -9.45429131e-003f, 0.03582499f, -2.64564669e-003f, -1.07461517e-003f, 0.02962313f, -0.01483363f, 0.03060869f, 0.02448327f, 0.01845641f, 0.03282966f, -0.03534438f, -0.01084059f, -0.01119136f, -1.85360224e-003f, -5.94652840e-004f, -0.04451817f, 2.98327743e-003f, 0.06272484f, -0.02152076f, -3.05971340e-003f, -0.05070828f, 0.01531762f, 0.01282815f, 0.05167150f, 9.46266949e-003f, -3.34558333e-003f, 0.11442288f, -0.03906701f, -2.67325155e-003f, 0.03069184f, -0.01134165f, 0.02949462f, 0.02879886f, 0.03855566f, -0.03450781f, 0.09142872f, -0.02156654f, 0.06075062f, -0.06220816f, 0.01944680f, 6.68372354e-003f, -0.06656796f, 8.70784000e-003f, 0.03456013f, 0.02434320f, -0.13236357f, -0.04177035f, -0.02069627f, 0.01068112f, 0.01505432f, -0.07517391f, -3.83571628e-003f, -0.06298508f, -0.02881260f, -0.13101046f, -0.07221562f, -5.79945277e-003f, -8.57300125e-003f, 0.03782469f, 0.02762164f, 0.04942456f, -0.02936396f, 0.09597211f, 0.01921411f, 0.06101191f, -0.04787507f, -0.01379578f, -7.40224449e-003f, -0.02220136f, -0.01313756f, 7.77558051e-003f, 0.12296968f, 0.02939998f, 0.03594062f, -0.07788624f, -0.01133144f, 3.99316690e-004f, -0.06090347f, -0.01122066f, -4.68682544e-003f, 0.07633100f, -0.06748922f, -0.05640298f, -0.05265681f, -0.01139122f, -0.01624347f, -0.04715714f, -0.01099092f, 0.01048561f, 3.28499987e-003f, -0.05810167f, -0.07699911f, -0.03330683f, 0.04185145f, 0.03478536f, 0.02275165f, 0.02304766f, 6.66040834e-003f, 0.10968148f, -5.93013782e-003f, -0.04858336f, -0.04203213f, -0.09316786f, -6.13074889e-003f, -0.02544625f, 0.01366201f, 9.18555818e-003f, -0.01846578f, -0.05622401f, -0.03989377f, -0.07810296f, 6.91275718e-003f, 0.05957597f, -0.03901334f, 0.01572002f, -0.01193903f, -6.89400872e-003f, -0.03093356f, -0.04136098f, -0.01562869f, -0.04604580f, 0.02865234f, -0.08678447f, -0.03232484f, -0.05364593f, -0.01445016f, -0.07003860f, -0.08669746f, -0.04520775f, 0.04274122f, 0.03117515f, 0.08175703f, 0.01081109f, 0.06379741f, 0.06199206f, 0.02865988f, 0.02360346f, 0.06725410f, -0.03248780f, -9.37702879e-003f, 0.08265898f, -0.02245839f, 0.05125763f, -0.01862395f, 0.01973453f, -0.01994494f, -0.10770868f, 0.03180375f, 3.23935156e-003f, -0.02142080f, -0.04256190f, 0.04760900f, 0.04282863f, 0.05635953f, -0.01870849f, 0.05540622f, -0.03042666f, 0.01455277f, -0.06630179f, -0.05843807f, -0.03739681f, -0.09739155f, -0.03220233f, -0.05620182f, -0.10381401f, 0.07400211f, 4.20676917e-003f, 0.03258535f, 2.14308966e-003f, 0.05121966f, -0.01274337f, 0.02384761f, 0.06335578f, -0.07905591f, 0.08375625f, -0.07898903f, -0.06508528f, -0.02498444f, 0.06535810f, 0.03970535f, 0.04895468f, -0.01169566f, -0.03980601f, 0.05682293f, 0.05925463f, -0.01165808f, -0.07936699f, -0.04208954f, 0.01333987f, 0.09051196f, 0.10098671f, -0.03974256f, 0.01238771f, -0.07501741f, -0.03655440f, -0.04301528f, 0.09216860f, 4.63579083e-004f, 0.02851115f, 0.02142735f, 1.28244064e-004f, 0.02879687f, -0.08554889f, -0.04838862f, 0.08135369f, -0.05756533f, 0.01413900f, 0.03451880f, -0.06619488f, -0.03053130f, 0.02961676f, -0.07384635f, 0.01135692f, 0.05283910f, -0.07778034f, -0.02107482f, -0.05511716f, -0.13473752f, 0.03030157f, 0.06722020f, -0.06218817f, -0.05826827f, 0.06254654f, 0.02895772f, -0.01664000f, -0.03620280f, -0.01612278f, -1.46097376e-003f, 0.14013411f, -8.96181818e-003f, -0.03250246f, 3.38630192e-003f, 2.64779478e-003f, 0.03359732f, -0.02411991f, -0.04229729f, 0.10666174f, -6.66579151f }; return std::vector(detector, detector + sizeof(detector)/sizeof(detector[0])); } // This function renurn 1981 SVM coeffs obtained from daimler's base. // To use these coeffs the detection window size should be (48,96) std::vector HOGDescriptor::getDaimlerPeopleDetector() { static const float detector[] = { 0.294350f, -0.098796f, -0.129522f, 0.078753f, 0.387527f, 0.261529f, 0.145939f, 0.061520f, 0.328699f, 0.227148f, -0.066467f, -0.086723f, 0.047559f, 0.106714f, 0.037897f, 0.111461f, -0.024406f, 0.304769f, 0.254676f, -0.069235f, 0.082566f, 0.147260f, 0.326969f, 0.148888f, 0.055270f, -0.087985f, 0.261720f, 0.143442f, 0.026812f, 0.238212f, 0.194020f, 0.056341f, -0.025854f, -0.034444f, -0.156631f, 0.205174f, 0.089008f, -0.139811f, -0.100147f, -0.037830f, -0.029230f, -0.055641f, 0.033248f, -0.016512f, 0.155244f, 0.247315f, -0.124694f, -0.048414f, -0.062219f, 0.193683f, 0.004574f, 0.055089f, 0.093565f, 0.167712f, 0.167581f, 0.018895f, 0.215258f, 0.122609f, 0.090520f, -0.067219f, -0.049029f, -0.099615f, 0.241804f, -0.094893f, -0.176248f, 0.001727f, -0.134473f, 0.104442f, 0.050942f, 0.081165f, 0.072156f, 0.121646f, 0.002656f, -0.297974f, -0.133587f, -0.060121f, -0.092515f, -0.048974f, -0.084754f, -0.180111f, -0.038590f, 0.086283f, -0.134636f, -0.107249f, 0.132890f, 0.141556f, 0.249425f, 0.130273f, -0.030031f, 0.073212f, -0.008155f, 0.019931f, 0.071688f, 0.000300f, -0.019525f, -0.021725f, -0.040993f, -0.086841f, 0.070124f, 0.240033f, 0.265350f, 0.043208f, 0.166754f, 0.091453f, 0.060916f, -0.036972f, -0.091043f, 0.079873f, 0.219781f, 0.158102f, -0.140618f, -0.043016f, 0.124802f, 0.093668f, 0.103208f, 0.094872f, 0.080541f, 0.137711f, 0.160566f, -0.169231f, 0.013983f, 0.309508f, -0.004217f, -0.057200f, -0.064489f, 0.014066f, 0.361009f, 0.251328f, -0.080983f, -0.044183f, 0.061436f, -0.037381f, -0.078786f, 0.030993f, 0.066314f, 0.037683f, 0.152325f, -0.091683f, 0.070203f, 0.217856f, 0.036435f, -0.076462f, 0.006254f, -0.094431f, 0.154829f, -0.023038f, -0.196961f, -0.024594f, 0.178465f, -0.050139f, -0.045932f, -0.000965f, 0.109112f, 0.046165f, -0.159373f, -0.008713f, 0.041307f, 0.097129f, -0.057211f, -0.064599f, 0.077165f, 0.176167f, 0.138322f, 0.065753f, -0.104950f, 0.017933f, 0.136255f, -0.011598f, 0.047007f, 0.080550f, 0.068619f, 0.084661f, -0.035493f, -0.091314f, -0.041411f, 0.060971f, -0.101912f, -0.079870f, -0.085977f, -0.022686f, 0.079788f, -0.098064f, -0.054603f, 0.040383f, 0.300794f, 0.128603f, 0.094844f, 0.047407f, 0.101825f, 0.061832f, -0.162160f, -0.204553f, -0.035165f, 0.101450f, -0.016641f, -0.027140f, -0.134392f, -0.008743f, 0.102331f, 0.114853f, 0.009644f, 0.062823f, 0.237339f, 0.167843f, 0.053066f, -0.012592f, 0.043158f, 0.002305f, 0.065001f, -0.038929f, -0.020356f, 0.152343f, 0.043469f, -0.029967f, -0.042948f, 0.032481f, 0.068488f, -0.110840f, -0.111083f, 0.111980f, -0.002072f, -0.005562f, 0.082926f, 0.006635f, -0.108153f, 0.024242f, -0.086464f, -0.189884f, -0.017492f, 0.191456f, -0.007683f, -0.128769f, -0.038017f, -0.132380f, 0.091926f, 0.079696f, -0.106728f, -0.007656f, 0.172744f, 0.011576f, 0.009883f, 0.083258f, -0.026516f, 0.145534f, 0.153924f, -0.130290f, -0.108945f, 0.124490f, -0.003186f, -0.100485f, 0.015024f, -0.060512f, 0.026288f, -0.086713f, -0.169012f, 0.076517f, 0.215778f, 0.043701f, -0.131642f, -0.012585f, -0.045181f, -0.118183f, -0.241544f, -0.167293f, -0.020107f, -0.019917f, -0.101827f, -0.107096f, -0.010503f, 0.044938f, 0.189680f, 0.217119f, -0.046086f, 0.044508f, 0.199716f, -0.036004f, -0.148927f, 0.013355f, -0.078279f, 0.030451f, 0.056301f, -0.024609f, 0.083224f, 0.099533f, -0.039432f, -0.138880f, 0.005482f, -0.024120f, -0.140468f, -0.066381f, -0.017057f, 0.009260f, -0.058004f, -0.028486f, -0.061610f, 0.007483f, -0.158309f, -0.150687f, -0.044595f, -0.105121f, -0.045763f, -0.006618f, -0.024419f, -0.117713f, -0.119366f, -0.175941f, -0.071542f, 0.119027f, 0.111362f, 0.043080f, 0.034889f, 0.093003f, 0.007842f, 0.057368f, -0.108834f, -0.079968f, 0.230959f, 0.020205f, 0.011470f, 0.098877f, 0.101310f, -0.030215f, -0.018018f, -0.059552f, -0.106157f, 0.021866f, -0.036471f, 0.080051f, 0.041165f, -0.082101f, 0.117726f, 0.030961f, -0.054763f, -0.084102f, -0.185778f, -0.061305f, -0.038089f, -0.110728f, -0.264010f, 0.076675f, -0.077111f, -0.137644f, 0.036232f, 0.277995f, 0.019116f, 0.107738f, 0.144003f, 0.080304f, 0.215036f, 0.228897f, 0.072713f, 0.077773f, 0.120168f, 0.075324f, 0.062730f, 0.122478f, -0.049008f, 0.164912f, 0.162450f, 0.041246f, 0.009891f, -0.097827f, -0.038700f, -0.023027f, -0.120020f, 0.203364f, 0.248474f, 0.149810f, -0.036276f, -0.082814f, -0.090343f, -0.027143f, -0.075689f, -0.320310f, -0.000500f, -0.143334f, -0.065077f, -0.186936f, 0.129372f, 0.116431f, 0.181699f, 0.170436f, 0.418854f, 0.460045f, 0.333719f, 0.230515f, 0.047822f, -0.044954f, -0.068086f, 0.140179f, -0.044821f, 0.085550f, 0.092483f, -0.107296f, -0.130670f, -0.206629f, 0.114601f, -0.317869f, -0.076663f, 0.038680f, 0.212753f, -0.016059f, -0.126526f, -0.163602f, 0.210154f, 0.099887f, -0.126366f, 0.118453f, 0.019309f, -0.021611f, -0.096499f, -0.111809f, -0.200489f, 0.142854f, 0.228840f, -0.353346f, -0.179151f, 0.116834f, 0.252389f, -0.031728f, -0.188135f, -0.158998f, 0.386523f, 0.122315f, 0.209944f, 0.394023f, 0.359030f, 0.260717f, 0.170335f, 0.013683f, -0.142596f, -0.026138f, -0.011878f, -0.150519f, 0.047159f, -0.107062f, -0.147347f, -0.187689f, -0.186027f, -0.208048f, 0.058468f, -0.073026f, -0.236556f, -0.079788f, -0.146216f, -0.058563f, -0.101361f, -0.071294f, -0.071093f, 0.116919f, 0.234304f, 0.306781f, 0.321866f, 0.240000f, 0.073261f, -0.012173f, 0.026479f, 0.050173f, 0.166127f, 0.228955f, 0.061905f, 0.156460f, 0.205990f, 0.120672f, 0.037350f, 0.167884f, 0.290099f, 0.420900f, -0.012601f, 0.189839f, 0.306378f, 0.118383f, -0.095598f, -0.072360f, -0.132496f, -0.224259f, -0.126021f, 0.022714f, 0.284039f, 0.051369f, -0.000927f, -0.058735f, -0.083354f, -0.141254f, -0.187578f, -0.202669f, 0.048902f, 0.246597f, 0.441863f, 0.342519f, 0.066979f, 0.215286f, 0.188191f, -0.072240f, -0.208142f, -0.030196f, 0.178141f, 0.136985f, -0.043374f, -0.181098f, 0.091815f, 0.116177f, -0.126690f, -0.386625f, 0.368165f, 0.269149f, -0.088042f, -0.028823f, 0.092961f, 0.024099f, 0.046112f, 0.176756f, 0.135849f, 0.124955f, 0.195467f, -0.037218f, 0.167217f, 0.188938f, 0.053528f, -0.066561f, 0.133721f, -0.070565f, 0.115898f, 0.152435f, -0.116993f, -0.110592f, -0.179005f, 0.026668f, 0.080530f, 0.075084f, -0.070401f, 0.012497f, 0.021849f, -0.139764f, -0.022020f, -0.096301f, -0.064954f, -0.127446f, -0.013806f, -0.108315f, 0.156285f, 0.149867f, -0.011382f, 0.064532f, 0.029168f, 0.027393f, 0.069716f, 0.153735f, 0.038459f, 0.230714f, 0.253840f, 0.059522f, -0.045053f, 0.014083f, 0.071103f, 0.068747f, 0.095887f, 0.005832f, 0.144887f, 0.026357f, -0.067359f, -0.044151f, -0.123283f, -0.019911f, 0.005318f, 0.109208f, -0.003201f, -0.021734f, 0.142025f, -0.066907f, -0.120070f, -0.188639f, 0.012472f, -0.048704f, -0.012366f, -0.184828f, 0.168591f, 0.267166f, 0.058208f, -0.044101f, 0.033500f, 0.178558f, 0.104550f, 0.122418f, 0.080177f, 0.173246f, 0.298537f, 0.064173f, 0.053397f, 0.174341f, 0.230984f, 0.117025f, 0.166242f, 0.227781f, 0.120623f, 0.176952f, -0.011393f, -0.086483f, -0.008270f, 0.051700f, -0.153369f, -0.058837f, -0.057639f, -0.060115f, 0.026349f, -0.160745f, -0.037894f, -0.048575f, 0.041052f, -0.022112f, 0.060365f, 0.051906f, 0.162657f, 0.138519f, -0.050185f, -0.005938f, 0.071301f, 0.127686f, 0.062342f, 0.144400f, 0.072600f, 0.198436f, 0.246219f, -0.078185f, -0.036169f, 0.075934f, 0.047328f, -0.013601f, 0.087205f, 0.019900f, 0.022606f, -0.015365f, -0.092506f, 0.075275f, -0.116375f, 0.050500f, 0.045118f, 0.166567f, 0.072073f, 0.060371f, 0.131747f, -0.169863f, -0.039352f, -0.047486f, -0.039797f, -0.204312f, 0.021710f, 0.129443f, -0.021173f, 0.173416f, -0.070794f, -0.063986f, 0.069689f, -0.064099f, -0.123201f, -0.017372f, -0.206870f, 0.065863f, 0.113226f, 0.024707f, -0.071341f, -0.066964f, -0.098278f, -0.062927f, 0.075840f, 0.014716f, 0.019378f, 0.132699f, -0.074191f, -0.089557f, -0.078446f, -0.197488f, -0.173665f, 0.052583f, 0.044361f, 0.113549f, 0.098492f, 0.077379f, -0.011146f, -0.192593f, -0.164435f, 0.045568f, 0.205699f, 0.049187f, -0.082281f, 0.134874f, 0.185499f, 0.034968f, -0.119561f, -0.112372f, -0.115091f, -0.054042f, -0.183816f, -0.078100f, 0.190695f, 0.091617f, 0.004257f, -0.041135f, -0.061453f, -0.141592f, -0.194809f, -0.120638f, 0.020168f, 0.109672f, 0.067398f, -0.015238f, -0.239145f, -0.264671f, -0.185176f, 0.050472f, 0.020793f, 0.035678f, 0.022839f, -0.052055f, -0.127968f, -0.113049f, -0.228416f, -0.258281f, -0.053437f, 0.076424f, 0.061450f, 0.237478f, 0.003618f, -0.055865f, -0.108087f, -0.028937f, 0.045585f, 0.052829f, -0.001471f, 0.022826f, 0.059565f, -0.104430f, -0.077266f, -0.211882f, -0.212078f, 0.028074f, 0.075846f, 0.016265f, 0.161879f, 0.134477f, 0.008935f, -0.048041f, 0.074692f, 0.004928f, -0.025156f, 0.192874f, 0.074410f, 0.308732f, 0.267400f, 0.094208f, -0.005251f, 0.042041f, -0.032148f, 0.015588f, 0.252869f, 0.175302f, 0.022892f, 0.081673f, 0.063208f, 0.162626f, 0.194426f, 0.233890f, 0.262292f, 0.186930f, 0.084079f, -0.286388f, -0.213034f, -0.048867f, -0.207669f, -0.170050f, 0.011673f, -0.092958f, -0.192786f, -0.273536f, 0.230904f, 0.266732f, 0.320519f, 0.297155f, 0.548169f, 0.304922f, 0.132687f, 0.247333f, 0.212488f, -0.271472f, -0.142105f, -0.002627f, -0.119215f, 0.128383f, 0.100079f, -0.057490f, -0.121902f, -0.228892f, 0.202292f, -0.399795f, -0.371326f, -0.095836f, -0.063626f, -0.161375f, -0.311180f, -0.294797f, 0.242122f, 0.011788f, 0.095573f, 0.322523f, 0.511840f, 0.322880f, 0.313259f, 0.173331f, 0.002542f, -0.029802f, 0.324766f, -0.326170f, -0.340547f, -0.138288f, -0.002963f, -0.114060f, -0.377312f, -0.442570f, 0.212446f, -0.007759f, -0.011576f, 0.169711f, 0.308689f, 0.317348f, 0.539390f, 0.332845f, 0.057331f, -0.068180f, 0.101994f, 0.266995f, 0.209570f, 0.355730f, 0.091635f, 0.170238f, 0.125215f, 0.274154f, 0.070223f, 0.025515f, 0.049946f, -0.000550f, 0.043715f, -0.141843f, 0.020844f, 0.129871f, 0.256588f, 0.105015f, 0.148339f, 0.170682f, 0.028792f, 0.074037f, 0.160042f, 0.405137f, 0.246187f, 0.352160f, 0.168951f, 0.222263f, 0.264439f, 0.065945f, 0.021963f, -0.075084f, 0.093105f, 0.027318f, 0.098864f, 0.057566f, -0.080282f, 0.185032f, 0.314419f, 0.333727f, 0.125798f, 0.294919f, 0.386002f, 0.217619f, -0.183517f, -0.278622f, -0.002342f, -0.027821f, -0.134266f, -0.331843f, -0.008296f, 0.124564f, 0.053712f, -0.369016f, -0.095036f, 0.209381f, 0.423760f, 0.371760f, 0.106397f, 0.369408f, 0.485608f, 0.231201f, -0.138685f, -0.349208f, -0.070083f, 0.028991f, -0.081630f, -0.395992f, -0.146791f, -0.027354f, 0.063396f, -0.272484f, 0.058299f, 0.338207f, 0.110767f, -0.052642f, -0.233848f, -0.027448f, 0.030328f, 0.155572f, -0.093826f, 0.019331f, 0.120638f, 0.006292f, -0.106083f, -0.236290f, -0.140933f, -0.088067f, -0.025138f, -0.208395f, -0.025502f, 0.144192f, -0.048353f, -0.106144f, -0.305121f, -0.114147f, 0.090963f, 0.327727f, 0.035606f, -0.093779f, 0.002651f, -0.171081f, -0.188131f, -0.216571f, -0.209101f, -0.054402f, 0.157147f, -0.057127f, 0.066584f, 0.008988f, 0.041191f, 0.034456f, -0.078255f, 0.052099f, -0.022239f, 0.066981f, -0.117520f, -0.072637f, 0.062512f, 0.037570f, -0.057544f, -0.312359f, 0.034357f, -0.031549f, 0.002566f, -0.207375f, -0.070654f, -0.018786f, -0.044815f, -0.012814f, -0.076320f, 0.078183f, 0.023877f, 0.117078f, 0.022292f, -0.205424f, -0.060430f, -0.017296f, -0.004827f, -0.321036f, -0.092155f, 0.038837f, 0.073190f, -0.067513f, 0.026521f, 0.171945f, 0.087318f, 0.034495f, -0.034089f, 0.154410f, -0.061431f, 0.007435f, -0.111094f, -0.095976f, 0.014741f, -0.132324f, -0.029517f, -0.192160f, 0.098667f, 0.020762f, 0.177050f, -0.064510f, -0.054437f, -0.058678f, -0.001858f, 0.167602f, 0.015735f, 0.054338f, 0.016477f, 0.186381f, -0.010667f, 0.054692f, 0.126742f, 0.013140f, 0.090353f, -0.133608f, -0.018017f, -0.152619f, 0.027600f, -0.138700f, -0.050274f, 0.045141f, -0.118731f, 0.094797f, -0.167605f, 0.097461f, -0.009131f, 0.199920f, -0.052976f, 0.158194f, 0.178568f, -0.107600f, 0.009671f, -0.084072f, -0.040258f, -0.205673f, 0.102891f, 0.223511f, 0.042699f, 0.118548f, -0.021274f, 0.110997f, -0.155121f, 0.027696f, -0.149968f, 0.051552f, -0.129219f, 0.173524f, 0.073972f, -0.189045f, -0.034523f, -0.106655f, -0.011843f, -0.197381f, 0.219413f, 0.183197f, -0.054920f, 0.144955f, 0.036517f, -0.085412f, -0.229070f, -0.143710f, -0.049486f, 0.156634f, -0.008673f, -0.064778f, 0.082344f, 0.145673f, 0.002912f, -0.210121f, -0.116564f, 0.078425f, 0.220908f, -0.067594f, 0.048610f, 0.084912f, -0.066202f, -0.112515f, -0.217767f, -0.082640f, -0.017414f, 0.230265f, -0.070735f, 0.066073f, 0.215256f, 0.071157f, -0.087220f, -0.202235f, -0.011918f, 0.099562f, 0.174716f, -0.063845f, -0.121055f, 0.014367f, 0.132709f, -0.005060f, -0.244606f, -0.179693f, -0.134690f, 0.023239f, -0.193116f, -0.076975f, -0.021164f, -0.001938f, -0.163799f, -0.111437f, -0.210362f, -0.166376f, 0.034754f, 0.010036f, -0.021917f, 0.068014f, -0.086893f, -0.251746f, -0.267171f, 0.037383f, 0.003966f, 0.033571f, -0.151506f, 0.025437f, -0.020626f, -0.308454f, -0.343143f, -0.092263f, -0.026261f, -0.028345f, 0.036036f, 0.035169f, 0.129470f, 0.122205f, 0.015661f, -0.070612f, -0.094333f, -0.066055f, -0.041083f, 0.159146f, 0.073184f, 0.110044f, 0.174471f, 0.078069f, -0.014881f, 0.008116f, 0.013209f, 0.075857f, 0.195605f, 0.062714f, 0.067955f, 0.056544f, -0.153908f, -0.141749f, -0.072550f, 0.033523f, -0.024665f, 0.134487f, 0.079076f, 0.133562f, 0.227130f, 0.018054f, 0.004928f, 0.169162f, 0.065152f, 0.072160f, 0.131631f, 0.096303f, 0.054288f, 0.106256f, 0.114632f, 0.119038f, 0.515200f, 0.247429f, 0.199134f, 0.211957f, 0.127558f, -0.294684f, -0.194890f, -0.049988f, -0.112247f, -0.008122f, -0.006176f, 0.037035f, -0.110881f, -0.249989f, 0.152434f, 0.234621f, 0.153340f, 0.349283f, 0.683049f, 0.157174f, 0.124844f, 0.099136f, 0.064407f, -0.248400f, -0.155323f, -0.026498f, -0.023450f, 0.049051f, -0.114187f, 0.007195f, -0.176825f, -0.376926f, 0.366159f, -0.179938f, -0.148508f, 0.006043f, 0.170048f, 0.097866f, -0.102658f, -0.260430f, 0.248868f, 0.037019f, -0.118111f, 0.078176f, 0.194171f, 0.211328f, 0.368612f, 0.361213f, 0.130013f, 0.094650f, 0.227396f, -0.178058f, -0.114782f, -0.008093f, 0.231080f, -0.011843f, -0.097917f, -0.325788f, 0.141879f, 0.119738f, -0.230427f, -0.117419f, -0.114153f, 0.037903f, 0.116383f, 0.218773f, -0.101884f, 0.059466f, 0.119255f, 0.010874f, -0.031449f, 0.045996f, 0.119931f, 0.273760f, 0.311700f, 0.261794f, 0.194809f, 0.339829f, 0.239449f, 0.064140f, 0.077597f, 0.098996f, 0.143534f, 0.184602f, 0.037507f, 0.225494f, 0.096142f, -0.147370f, -0.207833f, -0.174742f, -0.086391f, -0.038942f, 0.159577f, -0.088492f, -0.000989f, 0.108154f, -0.025890f, -0.072713f, 0.025997f, -0.006803f, -0.086879f, -0.011290f, -0.269200f, -0.103450f, -0.124910f, -0.116340f, 0.141459f, 0.208800f, 0.042268f, 0.265034f, 0.516474f, 0.217591f, -0.018843f, -0.313328f, -0.168363f, 0.047129f, 0.090480f, -0.109852f, -0.018761f, 0.210669f, 0.281269f, -0.043591f, -0.034147f, -0.237772f, -0.134843f, -0.072481f, -0.103831f, 0.038355f, 0.308619f, 0.148023f, -0.045867f, -0.123950f, -0.210860f, -0.064973f, -0.036308f, -0.046731f, -0.022099f, 0.095776f, 0.409423f, 0.060635f, -0.065196f, 0.051828f, 0.027981f, -0.009609f, -0.137681f, -0.095011f, -0.019045f, 0.177278f, 0.009759f, -0.092119f, -0.016958f, -0.133860f, -0.118421f, -0.032039f, -0.006214f, -0.084541f, 0.063971f, -0.073642f, 0.165676f, 0.110443f, 0.044131f, 0.046568f, 0.053292f, -0.055466f, 0.015512f, 0.371947f, 0.232102f, -0.016923f, 0.103979f, -0.091758f, 0.005907f, 0.209100f, 0.157433f, 0.030518f, 0.250366f, 0.062322f, 0.036720f, 0.094676f, 0.017306f, -0.010328f, -0.079012f, 0.016781f, -0.112435f, 0.061795f, 0.042543f, -0.126799f, -0.009975f, -0.056760f, 0.046424f, -0.194712f, -0.139399f, -0.037731f, 0.157989f, -0.016261f, 0.123345f, 0.230563f, 0.083300f, -0.016392f, 0.059567f, -0.016035f, -0.064767f, 0.231945f, 0.156629f, 0.034602f, 0.145628f, 0.041315f, 0.034535f, 0.019967f, -0.089188f, -0.012091f, 0.307857f, 0.211405f, -0.025091f, -0.148249f, -0.129384f, 0.063536f, -0.068603f, -0.067941f, -0.035104f, 0.210832f, 0.063810f, 0.062764f, -0.089889f, -0.030554f, 0.014791f, -0.053362f, -0.037818f, -0.196640f, 0.008388f, -0.082654f, 0.143056f, 0.064221f, 0.069795f, 0.191040f, 0.097321f, -0.028679f, 0.075794f, 0.313154f, 0.086240f, 0.207643f, 0.017809f, 0.122867f, 0.224586f, 0.167403f, -0.023884f, 0.047434f, 0.344091f, 0.187745f, 0.136177f, 0.141738f, 0.063799f, 0.045233f, -0.077342f, -0.003525f, -0.165041f, -0.025616f, -0.073745f, 0.164439f, 0.011200f, -0.145896f, -0.027954f, -0.061987f, -0.039874f, -0.142775f, 0.151042f, -0.038238f, 0.053152f, 0.078615f, 0.086061f, 0.100593f, 0.128046f, -0.071006f, -0.116558f, 0.208445f, 0.051086f, 0.076843f, 0.023191f, -0.084781f, -0.011790f, 0.147807f, -0.048554f, -0.113932f, 0.283322f, 0.190934f, 0.092789f, 0.033018f, -0.142428f, -0.142480f, -0.099023f, -0.041020f, -0.042760f, 0.203295f, -0.053475f, 0.042424f, 0.222839f, -0.019167f, -0.133176f, -0.276216f, -0.031998f, 0.117290f, 0.177827f, -0.059973f, -0.064744f, -0.117040f, -0.155482f, -0.099531f, 0.164121f, -0.026682f, -0.093810f, 0.238993f, -0.006506f, 0.007830f, 0.065819f, -0.203643f, -0.100925f, -0.053652f, -0.130770f, 0.026277f, 0.131796f, 0.032742f, 0.127186f, 0.116694f, -0.161122f, -0.279773f, -0.252515f, -0.002638f, 0.042812f, 0.096776f, -0.123280f, 0.064858f, -0.010455f, -0.219760f, -0.239331f, -0.104363f, -0.058022f, -0.053584f, 0.025611f, 0.005129f, -0.100418f, -0.045712f, -0.194418f, -0.126366f, -0.030530f, 0.051168f, 0.215959f, 0.172402f, -0.054700f, -0.185995f, -0.278360f, -0.193693f, -0.040309f, 0.003735f, -0.007770f, 0.123556f, 0.190179f, -0.077315f, 0.117403f, 0.212942f, 0.012160f, 0.000113f, 0.027331f, 0.040202f, 0.033293f, 0.219438f, 0.184174f, 0.259349f, 0.311206f, 0.082547f, -0.047875f, -0.078417f, 0.010746f, 0.082620f, 0.311931f, 0.307605f, 0.003863f, 0.021405f, -0.026388f, -0.019572f, 0.020582f, -0.059353f, 0.025199f, 0.261319f, 0.086316f, 0.143614f, 0.107780f, 0.003900f, -0.188397f, -0.038563f, -0.106045f, -0.125154f, -0.010509f, 0.054021f, 0.242130f, 0.279152f, 0.215546f, 0.346995f, 0.440856f, 0.237452f, 0.234154f, 0.301646f, 0.168929f, -0.208358f, -0.126848f, 0.010260f, 0.121018f, -0.062975f, -0.052848f, 0.050341f, -0.061103f, -0.266482f, 0.107186f, 0.140221f, 0.280065f, 0.287889f, 0.373198f, 0.151596f, 0.013593f, 0.115616f, 0.014616f, -0.281710f, -0.237597f, -0.117305f, -0.000034f, -0.136739f, -0.196275f, -0.095225f, -0.125310f, -0.250514f, 0.236804f, -0.071805f, -0.037421f, 0.048230f, 0.321596f, 0.063632f, 0.024039f, -0.029133f, 0.230983f, 0.160593f, -0.154355f, -0.013086f, -0.079929f, 0.094692f, 0.160391f, 0.180239f, 0.053895f, 0.100759f, 0.288631f, 0.038191f, 0.181692f, 0.229682f, 0.440166f, 0.063401f, 0.006273f, 0.020865f, 0.338695f, 0.256244f, -0.043927f, 0.115617f, 0.003296f, 0.173965f, 0.021318f, -0.040936f, -0.118932f, 0.182380f, 0.235922f, -0.053233f, -0.015053f, -0.101057f, 0.095341f, 0.051111f, 0.161831f, 0.032614f, 0.159496f, 0.072375f, 0.025089f, 0.023748f, 0.029151f, 0.161284f, -0.117717f, -0.036191f, -0.176822f, -0.162006f, 0.226542f, -0.078329f, 0.043079f, -0.119172f, 0.054614f, -0.101365f, -0.064541f, -0.115304f, 0.135170f, 0.298872f, 0.098060f, 0.089428f, -0.007497f, 0.110391f, -0.028824f, 0.020835f, -0.036804f, 0.125411f, 0.192105f, -0.048931f, 0.003086f, -0.010681f, 0.074698f, -0.016263f, 0.096063f, 0.060267f, -0.007277f, 0.139139f, -0.080635f, 0.036628f, 0.086058f, 0.131979f, 0.085707f, 0.025301f, 0.226094f, 0.194759f, 0.042193f, -0.157846f, -0.068402f, -0.141450f, -0.112659f, -0.076305f, -0.069085f, -0.114332f, -0.102005f, 0.132193f, -0.067042f, 0.106643f, 0.198964f, 0.171616f, 0.167237f, -0.033730f, -0.026755f, 0.083621f, 0.149459f, -0.002799f, -0.000318f, 0.011753f, 0.065889f, -0.089375f, -0.049610f, 0.224579f, 0.216548f, -0.034908f, -0.017851f, -0.088144f, 0.007530f, 0.240268f, 0.073270f, 0.013263f, 0.175323f, 0.012082f, 0.093993f, 0.015282f, 0.105854f, 0.107990f, 0.077798f, -0.096166f, -0.079607f, 0.177820f, 0.142392f, 0.033337f, -0.078100f, -0.081616f, -0.046993f, 0.139459f, 0.020272f, -0.123161f, 0.175269f, 0.105217f, 0.057328f, 0.080909f, -0.012612f, -0.097081f, 0.082060f, -0.096716f, -0.063921f, 0.201884f, 0.128166f, -0.035051f, -0.032227f, -0.068139f, -0.115915f, 0.095080f, -0.086007f, -0.067543f, 0.030776f, 0.032712f, 0.088937f, 0.054336f, -0.039329f, -0.114022f, 0.171672f, -0.112321f, -0.217646f, 0.065186f, 0.060223f, 0.192174f, 0.055580f, -0.131107f, -0.144338f, 0.056730f, -0.034707f, -0.081616f, -0.135298f, -0.000614f, 0.087189f, 0.014614f, 0.067709f, 0.107689f, 0.225780f, 0.084361f, -0.008544f, 0.051649f, -0.048369f, -0.037739f, -0.060710f, 0.002654f, 0.016935f, 0.085563f, -0.015961f, -0.019265f, 0.111788f, 0.062376f, 0.202019f, 0.047713f, 0.042261f, 0.069716f, 0.242913f, 0.021052f, -0.072812f, -0.155920f, -0.026436f, 0.035621f, -0.079300f, -0.028787f, -0.048329f, 0.084718f, -0.060565f, -0.083750f, -0.164075f, -0.040742f, -0.086219f, 0.015271f, -0.005204f, -0.016038f, 0.045816f, -0.050433f, -0.077652f, 0.117109f, 0.009611f, -0.009045f, -0.008634f, -0.055373f, -0.085968f, 0.028527f, -0.054736f, -0.168089f, 0.175839f, 0.071205f, -0.023603f, 0.037907f, -0.004561f, -0.022634f, 0.123831f, 0.094469f, -0.072920f, -0.133642f, -0.014032f, -0.142754f, -0.026999f, -0.199409f, 0.013268f, 0.226989f, 0.048650f, -0.170988f, -0.050141f, 0.007880f, 0.061880f, 0.019078f, -0.043578f, -0.038139f, 0.134814f, 0.054097f, -0.081670f, 0.176838f, 0.047920f, -0.038176f, 0.050406f, -0.107181f, -0.036279f, 0.027060f, 0.081594f, -0.002820f, 0.090507f, -0.033338f, -0.059571f, 0.013404f, -0.099860f, 0.073371f, 0.342805f, 0.098305f, -0.150910f, -0.020822f, -0.056960f, 0.046262f, -0.043413f, -0.149405f, -0.129105f, -0.010899f, -0.014229f, -0.179949f, -0.113044f, -0.049468f, -0.065513f, 0.090269f, -0.011919f, 0.087846f, 0.095796f, 0.146127f, 0.101599f, 0.078066f, -0.084348f, -0.100002f, -0.020134f, -0.050169f, 0.062122f, 0.014640f, 0.019143f, 0.036543f, 0.180924f, -0.013976f, -0.066768f, -0.001090f, -0.070419f, -0.004839f, -0.001504f, 0.034483f, -0.044954f, -0.050336f, -0.088638f, -0.174782f, -0.116082f, -0.205507f, 0.015587f, -0.042839f, -0.096879f, -0.144097f, -0.050268f, -0.196796f, 0.109639f, 0.271411f, 0.173732f, 0.108070f, 0.156437f, 0.124255f, 0.097242f, 0.238693f, 0.083941f, 0.109105f, 0.223940f, 0.267188f, 0.027385f, 0.025819f, 0.125070f, 0.093738f, 0.040353f, 0.038645f, -0.012730f, 0.144063f, 0.052931f, -0.009138f, 0.084193f, 0.160272f, -0.041366f, 0.011951f, -0.121446f, -0.106713f, -0.047566f, 0.047984f, -0.255224f, -0.076116f, 0.098685f, -0.150845f, -0.171513f, -0.156590f, 0.058331f, 0.187493f, 0.413018f, 0.554265f, 0.372242f, 0.237943f, 0.124571f, 0.110829f, 0.010322f, -0.174477f, -0.067627f, -0.001979f, 0.142913f, 0.040597f, 0.019907f, 0.025963f, -0.043585f, -0.120732f, 0.099937f, 0.091059f, 0.247307f, 0.204226f, -0.042753f, -0.068580f, -0.119002f, 0.026722f, 0.034853f, -0.060934f, -0.025054f, -0.093026f, -0.035372f, -0.233209f, -0.049869f, -0.039151f, -0.022279f, -0.065380f, -9.063785f}; return std::vector(detector, detector + sizeof(detector)/sizeof(detector[0])); } class HOGConfInvoker : public ParallelLoopBody { public: HOGConfInvoker( const HOGDescriptor* _hog, const Mat& _img, double _hitThreshold, const Size& _padding, std::vector* locs, std::vector* _vec, Mutex* _mtx ) { hog = _hog; img = _img; hitThreshold = _hitThreshold; padding = _padding; locations = locs; vec = _vec; mtx = _mtx; } void operator()(const Range& range) const CV_OVERRIDE { CV_INSTRUMENT_REGION(); int i, i1 = range.start, i2 = range.end; Size maxSz(cvCeil(img.cols/(*locations)[0].scale), cvCeil(img.rows/(*locations)[0].scale)); Mat smallerImgBuf(maxSz, img.type()); std::vector dets; for (i = i1; i < i2; i++) { double scale = (*locations)[i].scale; Size sz(cvRound(img.cols / scale), cvRound(img.rows / scale)); Mat smallerImg(sz, img.type(), smallerImgBuf.ptr()); if (sz == img.size()) smallerImg = Mat(sz, img.type(), img.data, img.step); else resize(img, smallerImg, sz, 0, 0, INTER_LINEAR_EXACT); hog->detectROI(smallerImg, (*locations)[i].locations, dets, (*locations)[i].confidences, hitThreshold, Size(), padding); Size scaledWinSize = Size(cvRound(hog->winSize.width*scale), cvRound(hog->winSize.height*scale)); mtx->lock(); for (size_t j = 0; j < dets.size(); j++) vec->push_back(Rect(cvRound(dets[j].x*scale), cvRound(dets[j].y*scale), scaledWinSize.width, scaledWinSize.height)); mtx->unlock(); } } const HOGDescriptor* hog; Mat img; double hitThreshold; std::vector* locations; Size padding; std::vector* vec; Mutex* mtx; }; void HOGDescriptor::detectROI(const cv::Mat& img, const std::vector &locations, CV_OUT std::vector& foundLocations, CV_OUT std::vector& confidences, double hitThreshold, cv::Size winStride, cv::Size padding) const { CV_INSTRUMENT_REGION(); foundLocations.clear(); confidences.clear(); if (svmDetector.empty() || locations.empty()) return; if (winStride == Size()) winStride = cellSize; Size cacheStride(gcd(winStride.width, blockStride.width), gcd(winStride.height, blockStride.height)); size_t nwindows = locations.size(); padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); // HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); HOGCache cache(this, img, padding, padding, true, cacheStride); if (!nwindows) nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); const HOGCache::BlockData* blockData = &cache.blockData[0]; int nblocks = cache.nblocks.area(); int blockHistogramSize = cache.blockHistogramSize; size_t dsize = getDescriptorSize(); double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0; std::vector blockHist(blockHistogramSize); #if CV_SIMD128 float partSum[4]; #endif for (size_t i = 0; i < nwindows; i++) { Point pt0; pt0 = locations[i]; if (pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height) { // out of image confidences.push_back(-10.0); continue; } double s = rho; const float* svmVec = &svmDetector[0]; int j, k; for (j = 0; j < nblocks; j++, svmVec += blockHistogramSize) { const HOGCache::BlockData& bj = blockData[j]; Point pt = pt0 + bj.imgOffset; // need to divide this into 4 parts! const float* vec = cache.getBlock(pt, &blockHist[0]); #if CV_SIMD128 v_float32x4 _vec = v_load(vec); v_float32x4 _svmVec = v_load(svmVec); v_float32x4 sum = _svmVec * _vec; for (k = 4; k <= blockHistogramSize - 4; k += 4) { _vec = v_load(vec + k); _svmVec = v_load(svmVec + k); sum += _vec * _svmVec; } v_store(partSum, sum); double t0 = partSum[0] + partSum[1]; double t1 = partSum[2] + partSum[3]; s += t0 + t1; #else for (k = 0; k <= blockHistogramSize - 4; k += 4) s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] + vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3]; #endif for ( ; k < blockHistogramSize; k++) s += vec[k]*svmVec[k]; } confidences.push_back(s); if (s >= hitThreshold) foundLocations.push_back(pt0); } } void HOGDescriptor::detectMultiScaleROI(const cv::Mat& img, CV_OUT std::vector& foundLocations, std::vector& locations, double hitThreshold, int groupThreshold) const { CV_INSTRUMENT_REGION(); std::vector allCandidates; Mutex mtx; parallel_for_(Range(0, (int)locations.size()), HOGConfInvoker(this, img, hitThreshold, Size(8, 8), &locations, &allCandidates, &mtx)); foundLocations.resize(allCandidates.size()); std::copy(allCandidates.begin(), allCandidates.end(), foundLocations.begin()); cv::groupRectangles(foundLocations, groupThreshold, 0.2); } void HOGDescriptor::readALTModel(String modelfile) { // read model from SVMlight format.. FILE *modelfl; if ((modelfl = fopen(modelfile.c_str(), "rb")) == NULL) { String eerr("file not exist"); String efile(__FILE__); String efunc(__FUNCTION__); throw Exception(Error::StsError, eerr, efile, efunc, __LINE__); } char version_buffer[10]; if (!fread (&version_buffer,sizeof(char),10,modelfl)) { String eerr("version?"); String efile(__FILE__); String efunc(__FUNCTION__); fclose(modelfl); throw Exception(Error::StsError, eerr, efile, efunc, __LINE__); } if (strcmp(version_buffer,"V6.01")) { String eerr("version does not match"); String efile(__FILE__); String efunc(__FUNCTION__); fclose(modelfl); throw Exception(Error::StsError, eerr, efile, efunc, __LINE__); } /* read version number */ int version = 0; if (!fread (&version,sizeof(int),1,modelfl)) { fclose(modelfl); throw Exception(); } if (version < 200) { String eerr("version does not match"); String efile(__FILE__); String efunc(__FUNCTION__); fclose(modelfl); throw Exception(); } int kernel_type; size_t nread; nread=fread(&(kernel_type),sizeof(int),1,modelfl); {// ignore these int poly_degree; nread=fread(&(poly_degree),sizeof(int),1,modelfl); double rbf_gamma; nread=fread(&(rbf_gamma),sizeof(double), 1, modelfl); double coef_lin; nread=fread(&(coef_lin),sizeof(double),1,modelfl); double coef_const; nread=fread(&(coef_const),sizeof(double),1,modelfl); int l; nread=fread(&l,sizeof(int),1,modelfl); CV_Assert(l >= 0 && l < 0xFFFF); char* custom = new char[l]; nread=fread(custom,sizeof(char),l,modelfl); delete[] custom; } int totwords; nread=fread(&(totwords),sizeof(int),1,modelfl); {// ignore these int totdoc; nread=fread(&(totdoc),sizeof(int),1,modelfl); int sv_num; nread=fread(&(sv_num), sizeof(int),1,modelfl); } double linearbias; nread=fread(&linearbias, sizeof(double), 1, modelfl); std::vector detector; detector.clear(); if (kernel_type == 0) { /* linear kernel */ /* save linear wts also */ CV_Assert(totwords + 1 > 0 && totwords < 0xFFFF); double *linearwt = new double[totwords+1]; int length = totwords; nread = fread(linearwt, sizeof(double), totwords + 1, modelfl); if (nread != static_cast(length) + 1) { delete[] linearwt; fclose(modelfl); throw Exception(); } for (int i = 0; i < length; i++) detector.push_back((float)linearwt[i]); detector.push_back((float)-linearbias); setSVMDetector(detector); delete[] linearwt; } else { fclose(modelfl); throw Exception(); } fclose(modelfl); } void HOGDescriptor::groupRectangles(std::vector& rectList, std::vector& weights, int groupThreshold, double eps) const { CV_INSTRUMENT_REGION(); if (groupThreshold <= 0 || rectList.empty()) { return; } CV_Assert(rectList.size() == weights.size()); std::vector labels; int nclasses = partition(rectList, labels, SimilarRects(eps)); std::vector> rrects(nclasses); std::vector numInClass(nclasses, 0); std::vector foundWeights(nclasses, -std::numeric_limits::max()); int i, j, nlabels = (int)labels.size(); for (i = 0; i < nlabels; i++) { int cls = labels[i]; rrects[cls].x += rectList[i].x; rrects[cls].y += rectList[i].y; rrects[cls].width += rectList[i].width; rrects[cls].height += rectList[i].height; foundWeights[cls] = max(foundWeights[cls], weights[i]); numInClass[cls]++; } for (i = 0; i < nclasses; i++) { // find the average of all ROI in the cluster cv::Rect_ r = rrects[i]; double s = 1.0/numInClass[i]; rrects[i] = cv::Rect_(cv::saturate_cast(r.x*s), cv::saturate_cast(r.y*s), cv::saturate_cast(r.width*s), cv::saturate_cast(r.height*s)); } rectList.clear(); weights.clear(); for (i = 0; i < nclasses; i++) { cv::Rect r1 = rrects[i]; int n1 = numInClass[i]; double w1 = foundWeights[i]; if (n1 <= groupThreshold) continue; // filter out small rectangles inside large rectangles for (j = 0; j < nclasses; j++) { int n2 = numInClass[j]; if (j == i || n2 <= groupThreshold) continue; cv::Rect r2 = rrects[j]; int dx = cv::saturate_cast( r2.width * eps ); int dy = cv::saturate_cast( r2.height * eps ); if (r1.x >= r2.x - dx && r1.y >= r2.y - dy && r1.x + r1.width <= r2.x + r2.width + dx && r1.y + r1.height <= r2.y + r2.height + dy && (n2 > std::max(3, n1) || n1 < 3)) break; } if (j == nclasses) { rectList.push_back(r1); weights.push_back(w1); } } } } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-15939/opencv2/000077500000000000000000000000001417746362400222155ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-15939/opencv2/objdetect.hpp000066400000000000000000001116301417746362400246730ustar00rootroot00000000000000/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #ifndef OPENCV_OBJDETECT_HPP #define OPENCV_OBJDETECT_HPP #include "opencv2/core.hpp" /** @defgroup objdetect Object Detection Haar Feature-based Cascade Classifier for Object Detection ---------------------------------------------------------- The object detector described below has been initially proposed by Paul Viola @cite Viola01 and improved by Rainer Lienhart @cite Lienhart02 . First, a classifier (namely a *cascade of boosted classifiers working with haar-like features*) is trained with a few hundred sample views of a particular object (i.e., a face or a car), called positive examples, that are scaled to the same size (say, 20x20), and negative examples - arbitrary images of the same size. After a classifier is trained, it can be applied to a region of interest (of the same size as used during the training) in an input image. The classifier outputs a "1" if the region is likely to show the object (i.e., face/car), and "0" otherwise. To search for the object in the whole image one can move the search window across the image and check every location using the classifier. The classifier is designed so that it can be easily "resized" in order to be able to find the objects of interest at different sizes, which is more efficient than resizing the image itself. So, to find an object of an unknown size in the image the scan procedure should be done several times at different scales. The word "cascade" in the classifier name means that the resultant classifier consists of several simpler classifiers (*stages*) that are applied subsequently to a region of interest until at some stage the candidate is rejected or all the stages are passed. The word "boosted" means that the classifiers at every stage of the cascade are complex themselves and they are built out of basic classifiers using one of four different boosting techniques (weighted voting). Currently Discrete Adaboost, Real Adaboost, Gentle Adaboost and Logitboost are supported. The basic classifiers are decision-tree classifiers with at least 2 leaves. Haar-like features are the input to the basic classifiers, and are calculated as described below. The current algorithm uses the following Haar-like features: ![image](pics/haarfeatures.png) The feature used in a particular classifier is specified by its shape (1a, 2b etc.), position within the region of interest and the scale (this scale is not the same as the scale used at the detection stage, though these two scales are multiplied). For example, in the case of the third line feature (2c) the response is calculated as the difference between the sum of image pixels under the rectangle covering the whole feature (including the two white stripes and the black stripe in the middle) and the sum of the image pixels under the black stripe multiplied by 3 in order to compensate for the differences in the size of areas. The sums of pixel values over a rectangular regions are calculated rapidly using integral images (see below and the integral description). To see the object detector at work, have a look at the facedetect demo: The following reference is for the detection part only. There is a separate application called opencv_traincascade that can train a cascade of boosted classifiers from a set of samples. @note In the new C++ interface it is also possible to use LBP (local binary pattern) features in addition to Haar-like features. .. [Viola01] Paul Viola and Michael J. Jones. Rapid Object Detection using a Boosted Cascade of Simple Features. IEEE CVPR, 2001. The paper is available online at @{ @defgroup objdetect_c C API @} */ typedef struct CvHaarClassifierCascade CvHaarClassifierCascade; namespace cv { //! @addtogroup objdetect //! @{ ///////////////////////////// Object Detection //////////////////////////// //! class for grouping object candidates, detected by Cascade Classifier, HOG etc. //! instance of the class is to be passed to cv::partition (see cxoperations.hpp) class CV_EXPORTS SimilarRects { public: SimilarRects(double _eps) : eps(_eps) {} inline bool operator()(const Rect& r1, const Rect& r2) const { double delta = eps * ((std::min)(r1.width, r2.width) + (std::min)(r1.height, r2.height)) * 0.5; return std::abs(r1.x - r2.x) <= delta && std::abs(r1.y - r2.y) <= delta && std::abs(r1.x + r1.width - r2.x - r2.width) <= delta && std::abs(r1.y + r1.height - r2.y - r2.height) <= delta; } double eps; }; /** @brief Groups the object candidate rectangles. @param rectList Input/output vector of rectangles. Output vector includes retained and grouped rectangles. (The Python list is not modified in place.) @param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it. @param eps Relative difference between sides of the rectangles to merge them into a group. The function is a wrapper for the generic function partition . It clusters all the input rectangles using the rectangle equivalence criteria that combines rectangles with similar sizes and similar locations. The similarity is defined by eps. When eps=0 , no clustering is done at all. If \f$\texttt{eps}\rightarrow +\inf\f$ , all the rectangles are put in one cluster. Then, the small clusters containing less than or equal to groupThreshold rectangles are rejected. In each other cluster, the average rectangle is computed and put into the output rectangle list. */ CV_EXPORTS void groupRectangles(std::vector& rectList, int groupThreshold, double eps = 0.2); /** @overload */ CV_EXPORTS_W void groupRectangles(CV_IN_OUT std::vector& rectList, CV_OUT std::vector& weights, int groupThreshold, double eps = 0.2); /** @overload */ CV_EXPORTS void groupRectangles(std::vector& rectList, int groupThreshold, double eps, std::vector* weights, std::vector* levelWeights ); /** @overload */ CV_EXPORTS void groupRectangles(std::vector& rectList, std::vector& rejectLevels, std::vector& levelWeights, int groupThreshold, double eps = 0.2); /** @overload */ CV_EXPORTS void groupRectangles_meanshift(std::vector& rectList, std::vector& foundWeights, std::vector& foundScales, double detectThreshold = 0.0, Size winDetSize = Size(64, 128)); template<> struct DefaultDeleter{ CV_EXPORTS void operator ()(CvHaarClassifierCascade* obj) const; }; enum { CASCADE_DO_CANNY_PRUNING = 1, CASCADE_SCALE_IMAGE = 2, CASCADE_FIND_BIGGEST_OBJECT = 4, CASCADE_DO_ROUGH_SEARCH = 8 }; class CV_EXPORTS_W BaseCascadeClassifier : public Algorithm { public: virtual ~BaseCascadeClassifier(); virtual bool empty() const CV_OVERRIDE = 0; virtual bool load( const String& filename ) = 0; virtual void detectMultiScale( InputArray image, CV_OUT std::vector& objects, double scaleFactor, int minNeighbors, int flags, Size minSize, Size maxSize ) = 0; virtual void detectMultiScale( InputArray image, CV_OUT std::vector& objects, CV_OUT std::vector& numDetections, double scaleFactor, int minNeighbors, int flags, Size minSize, Size maxSize ) = 0; virtual void detectMultiScale( InputArray image, CV_OUT std::vector& objects, CV_OUT std::vector& rejectLevels, CV_OUT std::vector& levelWeights, double scaleFactor, int minNeighbors, int flags, Size minSize, Size maxSize, bool outputRejectLevels ) = 0; virtual bool isOldFormatCascade() const = 0; virtual Size getOriginalWindowSize() const = 0; virtual int getFeatureType() const = 0; virtual void* getOldCascade() = 0; class CV_EXPORTS MaskGenerator { public: virtual ~MaskGenerator() {} virtual Mat generateMask(const Mat& src)=0; virtual void initializeMask(const Mat& /*src*/) { } }; virtual void setMaskGenerator(const Ptr& maskGenerator) = 0; virtual Ptr getMaskGenerator() = 0; }; /** @example samples/cpp/facedetect.cpp This program demonstrates usage of the Cascade classifier class \image html Cascade_Classifier_Tutorial_Result_Haar.jpg "Sample screenshot" width=321 height=254 */ /** @brief Cascade classifier class for object detection. */ class CV_EXPORTS_W CascadeClassifier { public: CV_WRAP CascadeClassifier(); /** @brief Loads a classifier from a file. @param filename Name of the file from which the classifier is loaded. */ CV_WRAP CascadeClassifier(const String& filename); ~CascadeClassifier(); /** @brief Checks whether the classifier has been loaded. */ CV_WRAP bool empty() const; /** @brief Loads a classifier from a file. @param filename Name of the file from which the classifier is loaded. The file may contain an old HAAR classifier trained by the haartraining application or a new cascade classifier trained by the traincascade application. */ CV_WRAP bool load( const String& filename ); /** @brief Reads a classifier from a FileStorage node. @note The file may contain a new cascade classifier (trained traincascade application) only. */ CV_WRAP bool read( const FileNode& node ); /** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list of rectangles. @param image Matrix of the type CV_8U containing an image where objects are detected. @param objects Vector of rectangles where each rectangle contains the detected object, the rectangles may be partially outside the original image. @param scaleFactor Parameter specifying how much the image size is reduced at each image scale. @param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have to retain it. @param flags Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade. @param minSize Minimum possible object size. Objects smaller than that are ignored. @param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale. The function is parallelized with the TBB library. @note - (Python) A face detection example using cascade classifiers can be found at opencv_source_code/samples/python/facedetect.py */ CV_WRAP void detectMultiScale( InputArray image, CV_OUT std::vector& objects, double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(), Size maxSize = Size() ); /** @overload @param image Matrix of the type CV_8U containing an image where objects are detected. @param objects Vector of rectangles where each rectangle contains the detected object, the rectangles may be partially outside the original image. @param numDetections Vector of detection numbers for the corresponding objects. An object's number of detections is the number of neighboring positively classified rectangles that were joined together to form the object. @param scaleFactor Parameter specifying how much the image size is reduced at each image scale. @param minNeighbors Parameter specifying how many neighbors each candidate rectangle should have to retain it. @param flags Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade. @param minSize Minimum possible object size. Objects smaller than that are ignored. @param maxSize Maximum possible object size. Objects larger than that are ignored. If `maxSize == minSize` model is evaluated on single scale. */ CV_WRAP_AS(detectMultiScale2) void detectMultiScale( InputArray image, CV_OUT std::vector& objects, CV_OUT std::vector& numDetections, double scaleFactor=1.1, int minNeighbors=3, int flags=0, Size minSize=Size(), Size maxSize=Size() ); /** @overload This function allows you to retrieve the final stage decision certainty of classification. For this, one needs to set `outputRejectLevels` on true and provide the `rejectLevels` and `levelWeights` parameter. For each resulting detection, `levelWeights` will then contain the certainty of classification at the final stage. This value can then be used to separate strong from weaker classifications. A code sample on how to use it efficiently can be found below: @code Mat img; vector weights; vector levels; vector detections; CascadeClassifier model("/path/to/your/model.xml"); model.detectMultiScale(img, detections, levels, weights, 1.1, 3, 0, Size(), Size(), true); cerr << "Detection " << detections[0] << " with weight " << weights[0] << endl; @endcode */ CV_WRAP_AS(detectMultiScale3) void detectMultiScale( InputArray image, CV_OUT std::vector& objects, CV_OUT std::vector& rejectLevels, CV_OUT std::vector& levelWeights, double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(), Size maxSize = Size(), bool outputRejectLevels = false ); CV_WRAP bool isOldFormatCascade() const; CV_WRAP Size getOriginalWindowSize() const; CV_WRAP int getFeatureType() const; void* getOldCascade(); CV_WRAP static bool convert(const String& oldcascade, const String& newcascade); void setMaskGenerator(const Ptr& maskGenerator); Ptr getMaskGenerator(); Ptr cc; }; CV_EXPORTS Ptr createFaceDetectionMaskGenerator(); //////////////// HOG (Histogram-of-Oriented-Gradients) Descriptor and Object Detector ////////////// //! struct for detection region of interest (ROI) struct DetectionROI { //! scale(size) of the bounding box double scale; //! set of requested locations to be evaluated std::vector locations; //! vector that will contain confidence values for each location std::vector confidences; }; /**@brief Implementation of HOG (Histogram of Oriented Gradients) descriptor and object detector. the HOG descriptor algorithm introduced by Navneet Dalal and Bill Triggs @cite Dalal2005 . useful links: https://hal.inria.fr/inria-00548512/document/ https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients https://software.intel.com/en-us/ipp-dev-reference-histogram-of-oriented-gradients-hog-descriptor http://www.learnopencv.com/histogram-of-oriented-gradients http://www.learnopencv.com/handwritten-digits-classification-an-opencv-c-python-tutorial */ struct CV_EXPORTS_W HOGDescriptor { public: enum HistogramNormType { L2Hys = 0 //!< Default histogramNormType }; enum { DEFAULT_NLEVELS = 64 //!< Default nlevels value. }; enum DescriptorStorageFormat { DESCR_FORMAT_COL_BY_COL, DESCR_FORMAT_ROW_BY_ROW }; /**@brief Creates the HOG descriptor and detector with default params. aqual to HOGDescriptor(Size(64,128), Size(16,16), Size(8,8), Size(8,8), 9 ) */ CV_WRAP HOGDescriptor() : winSize(64,128), blockSize(16,16), blockStride(8,8), cellSize(8,8), nbins(9), derivAperture(1), winSigma(-1), histogramNormType(HOGDescriptor::L2Hys), L2HysThreshold(0.2), gammaCorrection(true), free_coef(-1.f), nlevels(HOGDescriptor::DEFAULT_NLEVELS), signedGradient(false) {} /** @overload @param _winSize sets winSize with given value. @param _blockSize sets blockSize with given value. @param _blockStride sets blockStride with given value. @param _cellSize sets cellSize with given value. @param _nbins sets nbins with given value. @param _derivAperture sets derivAperture with given value. @param _winSigma sets winSigma with given value. @param _histogramNormType sets histogramNormType with given value. @param _L2HysThreshold sets L2HysThreshold with given value. @param _gammaCorrection sets gammaCorrection with given value. @param _nlevels sets nlevels with given value. @param _signedGradient sets signedGradient with given value. */ CV_WRAP HOGDescriptor(Size _winSize, Size _blockSize, Size _blockStride, Size _cellSize, int _nbins, int _derivAperture=1, double _winSigma=-1, HOGDescriptor::HistogramNormType _histogramNormType=HOGDescriptor::L2Hys, double _L2HysThreshold=0.2, bool _gammaCorrection=false, int _nlevels=HOGDescriptor::DEFAULT_NLEVELS, bool _signedGradient=false) : winSize(_winSize), blockSize(_blockSize), blockStride(_blockStride), cellSize(_cellSize), nbins(_nbins), derivAperture(_derivAperture), winSigma(_winSigma), histogramNormType(_histogramNormType), L2HysThreshold(_L2HysThreshold), gammaCorrection(_gammaCorrection), free_coef(-1.f), nlevels(_nlevels), signedGradient(_signedGradient) {} /** @overload @param filename The file name containing HOGDescriptor properties and coefficients for the linear SVM classifier. */ CV_WRAP HOGDescriptor(const String& filename) { load(filename); } /** @overload @param d the HOGDescriptor which cloned to create a new one. */ HOGDescriptor(const HOGDescriptor& d) { d.copyTo(*this); } /**@brief Default destructor. */ virtual ~HOGDescriptor() {} /**@brief Returns the number of coefficients required for the classification. */ CV_WRAP size_t getDescriptorSize() const; /** @brief Checks if detector size equal to descriptor size. */ CV_WRAP bool checkDetectorSize() const; /** @brief Returns winSigma value */ CV_WRAP double getWinSigma() const; /**@example samples/cpp/peopledetect.cpp */ /**@brief Sets coefficients for the linear SVM classifier. @param svmdetector coefficients for the linear SVM classifier. */ CV_WRAP virtual void setSVMDetector(InputArray svmdetector); /** @brief Reads HOGDescriptor parameters from a cv::FileNode. @param fn File node */ virtual bool read(FileNode& fn); /** @brief Stores HOGDescriptor parameters in a cv::FileStorage. @param fs File storage @param objname Object name */ virtual void write(FileStorage& fs, const String& objname) const; /** @brief loads HOGDescriptor parameters and coefficients for the linear SVM classifier from a file. @param filename Path of the file to read. @param objname The optional name of the node to read (if empty, the first top-level node will be used). */ CV_WRAP virtual bool load(const String& filename, const String& objname = String()); /** @brief saves HOGDescriptor parameters and coefficients for the linear SVM classifier to a file @param filename File name @param objname Object name */ CV_WRAP virtual void save(const String& filename, const String& objname = String()) const; /** @brief clones the HOGDescriptor @param c cloned HOGDescriptor */ virtual void copyTo(HOGDescriptor& c) const; /**@example samples/cpp/train_HOG.cpp */ /** @brief Computes HOG descriptors of given image. @param img Matrix of the type CV_8U containing an image where HOG features will be calculated. @param descriptors Matrix of the type CV_32F @param winStride Window stride. It must be a multiple of block stride. @param padding Padding @param locations Vector of Point */ CV_WRAP virtual void compute(InputArray img, CV_OUT std::vector& descriptors, Size winStride = Size(), Size padding = Size(), const std::vector& locations = std::vector()) const; /** @brief Performs object detection without a multi-scale window. @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. @param foundLocations Vector of point where each point contains left-top corner point of detected object boundaries. @param weights Vector that will contain confidence values for each detected object. @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here. @param winStride Window stride. It must be a multiple of block stride. @param padding Padding @param searchLocations Vector of Point includes set of requested locations to be evaluated. */ CV_WRAP virtual void detect(InputArray img, CV_OUT std::vector& foundLocations, CV_OUT std::vector& weights, double hitThreshold = 0, Size winStride = Size(), Size padding = Size(), const std::vector& searchLocations = std::vector()) const; /** @brief Performs object detection without a multi-scale window. @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. @param foundLocations Vector of point where each point contains left-top corner point of detected object boundaries. @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here. @param winStride Window stride. It must be a multiple of block stride. @param padding Padding @param searchLocations Vector of Point includes locations to search. */ virtual void detect(InputArray img, CV_OUT std::vector& foundLocations, double hitThreshold = 0, Size winStride = Size(), Size padding = Size(), const std::vector& searchLocations=std::vector()) const; /** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list of rectangles. @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. @param foundLocations Vector of rectangles where each rectangle contains the detected object. @param foundWeights Vector that will contain confidence values for each detected object. @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here. @param winStride Window stride. It must be a multiple of block stride. @param padding Padding @param scale Coefficient of the detection window increase. @param finalThreshold Final threshold @param useMeanshiftGrouping indicates grouping algorithm */ CV_WRAP virtual void detectMultiScale(InputArray img, CV_OUT std::vector& foundLocations, CV_OUT std::vector& foundWeights, double hitThreshold = 0, Size winStride = Size(), Size padding = Size(), double scale = 1.05, double finalThreshold = 2.0,bool useMeanshiftGrouping = false) const; /** @brief Detects objects of different sizes in the input image. The detected objects are returned as a list of rectangles. @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. @param foundLocations Vector of rectangles where each rectangle contains the detected object. @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here. @param winStride Window stride. It must be a multiple of block stride. @param padding Padding @param scale Coefficient of the detection window increase. @param finalThreshold Final threshold @param useMeanshiftGrouping indicates grouping algorithm */ virtual void detectMultiScale(InputArray img, CV_OUT std::vector& foundLocations, double hitThreshold = 0, Size winStride = Size(), Size padding = Size(), double scale = 1.05, double finalThreshold = 2.0, bool useMeanshiftGrouping = false) const; /** @brief Computes gradients and quantized gradient orientations. @param img Matrix contains the image to be computed @param grad Matrix of type CV_32FC2 contains computed gradients @param angleOfs Matrix of type CV_8UC2 contains quantized gradient orientations @param paddingTL Padding from top-left @param paddingBR Padding from bottom-right */ CV_WRAP virtual void computeGradient(InputArray img, InputOutputArray grad, InputOutputArray angleOfs, Size paddingTL = Size(), Size paddingBR = Size()) const; /** @brief Returns coefficients of the classifier trained for people detection (for 64x128 windows). */ CV_WRAP static std::vector getDefaultPeopleDetector(); /**@example samples/tapi/hog.cpp */ /** @brief Returns coefficients of the classifier trained for people detection (for 48x96 windows). */ CV_WRAP static std::vector getDaimlerPeopleDetector(); //! Detection window size. Align to block size and block stride. Default value is Size(64,128). CV_PROP Size winSize; //! Block size in pixels. Align to cell size. Default value is Size(16,16). CV_PROP Size blockSize; //! Block stride. It must be a multiple of cell size. Default value is Size(8,8). CV_PROP Size blockStride; //! Cell size. Default value is Size(8,8). CV_PROP Size cellSize; //! Number of bins used in the calculation of histogram of gradients. Default value is 9. CV_PROP int nbins; //! not documented CV_PROP int derivAperture; //! Gaussian smoothing window parameter. CV_PROP double winSigma; //! histogramNormType CV_PROP HOGDescriptor::HistogramNormType histogramNormType; //! L2-Hys normalization method shrinkage. CV_PROP double L2HysThreshold; //! Flag to specify whether the gamma correction preprocessing is required or not. CV_PROP bool gammaCorrection; //! coefficients for the linear SVM classifier. CV_PROP std::vector svmDetector; //! coefficients for the linear SVM classifier used when OpenCL is enabled UMat oclSvmDetector; //! not documented float free_coef; //! Maximum number of detection window increases. Default value is 64 CV_PROP int nlevels; //! Indicates signed gradient will be used or not CV_PROP bool signedGradient; /** @brief evaluate specified ROI and return confidence value for each location @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. @param locations Vector of Point @param foundLocations Vector of Point where each Point is detected object's top-left point. @param confidences confidences @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here @param winStride winStride @param padding padding */ virtual void detectROI(InputArray img, const std::vector &locations, CV_OUT std::vector& foundLocations, CV_OUT std::vector& confidences, double hitThreshold = 0, cv::Size winStride = Size(), cv::Size padding = Size()) const; /** @brief evaluate specified ROI and return confidence value for each location in multiple scales @param img Matrix of the type CV_8U or CV_8UC3 containing an image where objects are detected. @param foundLocations Vector of rectangles where each rectangle contains the detected object. @param locations Vector of DetectionROI @param hitThreshold Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should be specified in the detector coefficients (as the last free coefficient). But if the free coefficient is omitted (which is allowed), you can specify it manually here. @param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it. */ virtual void detectMultiScaleROI(InputArray img, CV_OUT std::vector& foundLocations, std::vector& locations, double hitThreshold = 0, int groupThreshold = 0) const; /** @brief Groups the object candidate rectangles. @param rectList Input/output vector of rectangles. Output vector includes retained and grouped rectangles. (The Python list is not modified in place.) @param weights Input/output vector of weights of rectangles. Output vector includes weights of retained and grouped rectangles. (The Python list is not modified in place.) @param groupThreshold Minimum possible number of rectangles minus 1. The threshold is used in a group of rectangles to retain it. @param eps Relative difference between sides of the rectangles to merge them into a group. */ void groupRectangles(std::vector& rectList, std::vector& weights, int groupThreshold, double eps) const; }; class CV_EXPORTS_W QRCodeDetector { public: CV_WRAP QRCodeDetector(); ~QRCodeDetector(); /** @brief sets the epsilon used during the horizontal scan of QR code stop marker detection. @param epsX Epsilon neighborhood, which allows you to determine the horizontal pattern of the scheme 1:1:3:1:1 according to QR code standard. */ CV_WRAP void setEpsX(double epsX); /** @brief sets the epsilon used during the vertical scan of QR code stop marker detection. @param epsY Epsilon neighborhood, which allows you to determine the vertical pattern of the scheme 1:1:3:1:1 according to QR code standard. */ CV_WRAP void setEpsY(double epsY); /** @brief Detects QR code in image and returns the quadrangle containing the code. @param img grayscale or color (BGR) image containing (or not) QR code. @param points Output vector of vertices of the minimum-area quadrangle containing the code. */ CV_WRAP bool detect(InputArray img, OutputArray points) const; /** @brief Decodes QR code in image once it's found by the detect() method. Returns UTF8-encoded output string or empty string if the code cannot be decoded. @param img grayscale or color (BGR) image containing QR code. @param points Quadrangle vertices found by detect() method (or some other algorithm). @param straight_qrcode The optional output image containing rectified and binarized QR code */ CV_WRAP std::string decode(InputArray img, InputArray points, OutputArray straight_qrcode = noArray()); /** @brief Both detects and decodes QR code @param img grayscale or color (BGR) image containing QR code. @param points optional output array of vertices of the found QR code quadrangle. Will be empty if not found. @param straight_qrcode The optional output image containing rectified and binarized QR code */ CV_WRAP std::string detectAndDecode(InputArray img, OutputArray points=noArray(), OutputArray straight_qrcode = noArray()); /** @brief Detects QR codes in image and returns the vector of the quadrangles containing the codes. @param img grayscale or color (BGR) image containing (or not) QR codes. @param points Output vector of vector of vertices of the minimum-area quadrangle containing the codes. */ CV_WRAP bool detectMulti(InputArray img, OutputArray points) const; /** @brief Decodes QR codes in image once it's found by the detect() method. @param img grayscale or color (BGR) image containing QR codes. @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded. @param points vector of Quadrangle vertices found by detect() method (or some other algorithm). @param straight_qrcode The optional output vector of images containing rectified and binarized QR codes */ CV_WRAP bool decodeMulti( InputArray img, InputArray points, CV_OUT std::vector& decoded_info, OutputArrayOfArrays straight_qrcode = noArray() ) const; /** @brief Both detects and decodes QR codes @param img grayscale or color (BGR) image containing QR codes. @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded. @param points optional output vector of vertices of the found QR code quadrangles. Will be empty if not found. @param straight_qrcode The optional output vector of images containing rectified and binarized QR codes */ CV_WRAP bool detectAndDecodeMulti( InputArray img, CV_OUT std::vector& decoded_info, OutputArray points = noArray(), OutputArrayOfArrays straight_qrcode = noArray() ) const; protected: struct Impl; Ptr p; }; //! @} objdetect } #include "opencv2/objdetect/detection_based_tracker.hpp" #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-15939/precomp.hpp000066400000000000000000000045301417746362400230210ustar00rootroot00000000000000/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #ifndef __OPENCV_PRECOMP_H__ #define __OPENCV_PRECOMP_H__ #include "opencv2/objdetect.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/core/utility.hpp" #include "opencv2/core/ocl.hpp" #include "opencv2/core/private.hpp" #endif cppcheck-2.7/test/bug-hunting/cve/CVE-2019-16168/000077500000000000000000000000001417746362400206345ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-16168/expected.txt000066400000000000000000000000411417746362400231710ustar00rootroot00000000000000where.c:2673:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-16168/where.c000066400000000000000000006624061417746362400221300ustar00rootroot00000000000000/* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This module contains C code that generates VDBE code used to process ** the WHERE clause of SQL statements. This module is responsible for ** generating the code that loops through a table looking for applicable ** rows. Indices are selected and used to speed the search when doing ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". */ #include "sqliteInt.h" #include "whereInt.h" /* ** Extra information appended to the end of sqlite3_index_info but not ** visible to the xBestIndex function, at least not directly. The ** sqlite3_vtab_collation() interface knows how to reach it, however. ** ** This object is not an API and can be changed from one release to the ** next. As long as allocateIndexInfo() and sqlite3_vtab_collation() ** agree on the structure, all will be well. */ typedef struct HiddenIndexInfo HiddenIndexInfo; struct HiddenIndexInfo { WhereClause *pWC; /* The Where clause being analyzed */ Parse *pParse; /* The parsing context */ }; /* Forward declaration of methods */ static int whereLoopResize(sqlite3*, WhereLoop*, int); /* Test variable that can be set to enable WHERE tracing */ #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) /***/ int sqlite3WhereTrace = 0; #endif /* ** Return the estimated number of output rows from a WHERE clause */ LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){ return pWInfo->nRowOut; } /* ** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this ** WHERE clause returns outputs for DISTINCT processing. */ int sqlite3WhereIsDistinct(WhereInfo *pWInfo){ return pWInfo->eDistinct; } /* ** Return TRUE if the WHERE clause returns rows in ORDER BY order. ** Return FALSE if the output needs to be sorted. */ int sqlite3WhereIsOrdered(WhereInfo *pWInfo){ return pWInfo->nOBSat; } /* ** In the ORDER BY LIMIT optimization, if the inner-most loop is known ** to emit rows in increasing order, and if the last row emitted by the ** inner-most loop did not fit within the sorter, then we can skip all ** subsequent rows for the current iteration of the inner loop (because they ** will not fit in the sorter either) and continue with the second inner ** loop - the loop immediately outside the inner-most. ** ** When a row does not fit in the sorter (because the sorter already ** holds LIMIT+OFFSET rows that are smaller), then a jump is made to the ** label returned by this function. ** ** If the ORDER BY LIMIT optimization applies, the jump destination should ** be the continuation for the second-inner-most loop. If the ORDER BY ** LIMIT optimization does not apply, then the jump destination should ** be the continuation for the inner-most loop. ** ** It is always safe for this routine to return the continuation of the ** inner-most loop, in the sense that a correct answer will result. ** Returning the continuation the second inner loop is an optimization ** that might make the code run a little faster, but should not change ** the final answer. */ int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){ WhereLevel *pInner; if (!pWInfo->bOrderedInnerLoop) { /* The ORDER BY LIMIT optimization does not apply. Jump to the ** continuation of the inner-most loop. */ return pWInfo->iContinue; } pInner = &pWInfo->a[pWInfo->nLevel-1]; assert( pInner->addrNxt!=0 ); return pInner->addrNxt; } /* ** Return the VDBE address or label to jump to in order to continue ** immediately with the next row of a WHERE clause. */ int sqlite3WhereContinueLabel(WhereInfo *pWInfo){ assert( pWInfo->iContinue!=0 ); return pWInfo->iContinue; } /* ** Return the VDBE address or label to jump to in order to break ** out of a WHERE loop. */ int sqlite3WhereBreakLabel(WhereInfo *pWInfo){ return pWInfo->iBreak; } /* ** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to ** operate directly on the rowis returned by a WHERE clause. Return ** ONEPASS_SINGLE (1) if the statement can operation directly because only ** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass ** optimization can be used on multiple ** ** If the ONEPASS optimization is used (if this routine returns true) ** then also write the indices of open cursors used by ONEPASS ** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data ** table and iaCur[1] gets the cursor used by an auxiliary index. ** Either value may be -1, indicating that cursor is not used. ** Any cursors returned will have been opened for writing. ** ** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is ** unable to use the ONEPASS optimization. */ int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){ memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2); #ifdef WHERETRACE_ENABLED if (sqlite3WhereTrace && pWInfo->eOnePass!=ONEPASS_OFF) { sqlite3DebugPrintf("%s cursors: %d %d\n", pWInfo->eOnePass==ONEPASS_SINGLE ? "ONEPASS_SINGLE" : "ONEPASS_MULTI", aiCur[0], aiCur[1]); } #endif return pWInfo->eOnePass; } /* ** Move the content of pSrc into pDest */ static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){ pDest->n = pSrc->n; memcpy(pDest->a, pSrc->a, pDest->n*sizeof(pDest->a[0])); } /* ** Try to insert a new prerequisite/cost entry into the WhereOrSet pSet. ** ** The new entry might overwrite an existing entry, or it might be ** appended, or it might be discarded. Do whatever is the right thing ** so that pSet keeps the N_OR_COST best entries seen so far. */ static int whereOrInsert( WhereOrSet *pSet, /* The WhereOrSet to be updated */ Bitmask prereq, /* Prerequisites of the new entry */ LogEst rRun, /* Run-cost of the new entry */ LogEst nOut /* Number of outputs for the new entry */ ){ u16 i; WhereOrCost *p; for (i=pSet->n, p=pSet->a; i>0; i--, p++) { if (rRun<=p->rRun && (prereq & p->prereq)==prereq) { goto whereOrInsert_done; } if (p->rRun<=rRun && (p->prereq & prereq)==p->prereq) { return 0; } } if (pSet->na[pSet->n++]; p->nOut = nOut; } else { p = pSet->a; for (i=1; in; i++) { if (p->rRun>pSet->a[i].rRun) p = pSet->a + i; } if (p->rRun<=rRun) return 0; } whereOrInsert_done: p->prereq = prereq; p->rRun = rRun; if (p->nOut>nOut) p->nOut = nOut; return 1; } /* ** Return the bitmask for the given cursor number. Return 0 if ** iCursor is not in the set. */ Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){ int i; assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); for (i=0; in; i++) { if (pMaskSet->ix[i]==iCursor) { return MASKBIT(i); } } return 0; } /* ** Create a new mask for cursor iCursor. ** ** There is one cursor per table in the FROM clause. The number of ** tables in the FROM clause is limited by a test early in the ** sqlite3WhereBegin() routine. So we know that the pMaskSet->ix[] ** array will never overflow. */ static void createMask(WhereMaskSet *pMaskSet, int iCursor){ assert( pMaskSet->n < ArraySize(pMaskSet->ix)); pMaskSet->ix[pMaskSet->n++] = iCursor; } /* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). ** Return NULL if there are no more matching WhereTerms. */ static WhereTerm *whereScanNext(WhereScan *pScan){ int iCur; /* The cursor on the LHS of the term */ i16 iColumn; /* The column on the LHS of the term. -1 for IPK */ Expr *pX; /* An expression being tested */ WhereClause *pWC; /* Shorthand for pScan->pWC */ WhereTerm *pTerm; /* The term being tested */ int k = pScan->k; /* Where to start scanning */ assert( pScan->iEquiv<=pScan->nEquiv ); pWC = pScan->pWC; while (1) { iColumn = pScan->aiColumn[pScan->iEquiv-1]; iCur = pScan->aiCur[pScan->iEquiv-1]; assert( pWC!=0 ); do{ for (pTerm=pWC->a+k; knTerm; k++, pTerm++) { if (pTerm->leftCursor==iCur && pTerm->u.leftColumn==iColumn && (iColumn!=XN_EXPR || sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, pScan->pIdxExpr,iCur)==0) && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin)) ) { if ((pTerm->eOperator & WO_EQUIV)!=0 && pScan->nEquivaiCur) && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN ) { int j; for (j=0; jnEquiv; j++) { if (pScan->aiCur[j]==pX->iTable && pScan->aiColumn[j]==pX->iColumn) { break; } } if (j==pScan->nEquiv) { pScan->aiCur[j] = pX->iTable; pScan->aiColumn[j] = pX->iColumn; pScan->nEquiv++; } } if ((pTerm->eOperator & pScan->opMask)!=0) { /* Verify the affinity and collating sequence match */ if (pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0) { CollSeq *pColl; Parse *pParse = pWC->pWInfo->pParse; pX = pTerm->pExpr; if (!sqlite3IndexAffinityOk(pX, pScan->idxaff)) { continue; } assert(pX->pLeft); pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); if (pColl==0) pColl = pParse->db->pDfltColl; if (sqlite3StrICmp(pColl->zName, pScan->zCollName)) { continue; } } if ((pTerm->eOperator & (WO_EQ|WO_IS))!=0 && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN && pX->iTable==pScan->aiCur[0] && pX->iColumn==pScan->aiColumn[0] ) { testcase( pTerm->eOperator & WO_IS ); continue; } pScan->pWC = pWC; pScan->k = k+1; return pTerm; } } } pWC = pWC->pOuter; k = 0; }while (pWC!=0); if (pScan->iEquiv>=pScan->nEquiv) break; pWC = pScan->pOrigWC; k = 0; pScan->iEquiv++; } return 0; } /* ** This is whereScanInit() for the case of an index on an expression. ** It is factored out into a separate tail-recursion subroutine so that ** the normal whereScanInit() routine, which is a high-runner, does not ** need to push registers onto the stack as part of its prologue. */ static SQLITE_NOINLINE WhereTerm *whereScanInitIndexExpr(WhereScan *pScan){ pScan->idxaff = sqlite3ExprAffinity(pScan->pIdxExpr); return whereScanNext(pScan); } /* ** Initialize a WHERE clause scanner object. Return a pointer to the ** first match. Return NULL if there are no matches. ** ** The scanner will be searching the WHERE clause pWC. It will look ** for terms of the form "X " where X is column iColumn of table ** iCur. Or if pIdx!=0 then X is column iColumn of index pIdx. pIdx ** must be one of the indexes of table iCur. ** ** The must be one of the operators described by opMask. ** ** If the search is for X and the WHERE clause contains terms of the ** form X=Y then this routine might also return terms of the form ** "Y ". The number of levels of transitivity is limited, ** but is enough to handle most commonly occurring SQL statements. ** ** If X is not the INTEGER PRIMARY KEY then X must be compatible with ** index pIdx. */ static WhereTerm *whereScanInit( WhereScan *pScan, /* The WhereScan object being initialized */ WhereClause *pWC, /* The WHERE clause to be scanned */ int iCur, /* Cursor to scan for */ int iColumn, /* Column to scan for */ u32 opMask, /* Operator(s) to scan for */ Index *pIdx /* Must be compatible with this index */ ){ pScan->pOrigWC = pWC; pScan->pWC = pWC; pScan->pIdxExpr = 0; pScan->idxaff = 0; pScan->zCollName = 0; pScan->opMask = opMask; pScan->k = 0; pScan->aiCur[0] = iCur; pScan->nEquiv = 1; pScan->iEquiv = 1; if (pIdx) { int j = iColumn; iColumn = pIdx->aiColumn[j]; if (iColumn==XN_EXPR) { pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; pScan->zCollName = pIdx->azColl[j]; pScan->aiColumn[0] = XN_EXPR; return whereScanInitIndexExpr(pScan); } else if (iColumn==pIdx->pTable->iPKey) { iColumn = XN_ROWID; } else if (iColumn>=0) { pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; pScan->zCollName = pIdx->azColl[j]; } } else if (iColumn==XN_EXPR) { return 0; } pScan->aiColumn[0] = iColumn; return whereScanNext(pScan); } /* ** Search for a term in the WHERE clause that is of the form "X " ** where X is a reference to the iColumn of table iCur or of index pIdx ** if pIdx!=0 and is one of the WO_xx operator codes specified by ** the op parameter. Return a pointer to the term. Return 0 if not found. ** ** If pIdx!=0 then it must be one of the indexes of table iCur. ** Search for terms matching the iColumn-th column of pIdx ** rather than the iColumn-th column of table iCur. ** ** The term returned might by Y= if there is another constraint in ** the WHERE clause that specifies that X=Y. Any such constraints will be ** identified by the WO_EQUIV bit in the pTerm->eOperator field. The ** aiCur[]/iaColumn[] arrays hold X and all its equivalents. There are 11 ** slots in aiCur[]/aiColumn[] so that means we can look for X plus up to 10 ** other equivalent values. Hence a search for X will return if X=A1 ** and A1=A2 and A2=A3 and ... and A9=A10 and A10=. ** ** If there are multiple terms in the WHERE clause of the form "X " ** then try for the one with no dependencies on - in other words where ** is a constant expression of some kind. Only return entries of ** the form "X Y" where Y is a column in another table if no terms of ** the form "X " exist. If no terms with a constant RHS ** exist, try to return a term that does not use WO_EQUIV. */ WhereTerm *sqlite3WhereFindTerm( WhereClause *pWC, /* The WHERE clause to be searched */ int iCur, /* Cursor number of LHS */ int iColumn, /* Column number of LHS */ Bitmask notReady, /* RHS must not overlap with this mask */ u32 op, /* Mask of WO_xx values describing operator */ Index *pIdx /* Must be compatible with this index, if not NULL */ ){ WhereTerm *pResult = 0; WhereTerm *p; WhereScan scan; p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx); op &= WO_EQ|WO_IS; while (p) { if ((p->prereqRight & notReady)==0) { if (p->prereqRight==0 && (p->eOperator&op)!=0) { testcase( p->eOperator & WO_IS ); return p; } if (pResult==0) pResult = p; } p = whereScanNext(&scan); } return pResult; } /* ** This function searches pList for an entry that matches the iCol-th column ** of index pIdx. ** ** If such an expression is found, its index in pList->a[] is returned. If ** no expression is found, -1 is returned. */ static int findIndexCol( Parse *pParse, /* Parse context */ ExprList *pList, /* Expression list to search */ int iBase, /* Cursor for table associated with pIdx */ Index *pIdx, /* Index to match column of */ int iCol /* Column of index to match */ ){ int i; const char *zColl = pIdx->azColl[iCol]; for (i=0; inExpr; i++) { Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr); if (p->op==TK_COLUMN && p->iColumn==pIdx->aiColumn[iCol] && p->iTable==iBase ) { CollSeq *pColl = sqlite3ExprNNCollSeq(pParse, pList->a[i].pExpr); if (0==sqlite3StrICmp(pColl->zName, zColl)) { return i; } } } return -1; } /* ** Return TRUE if the iCol-th column of index pIdx is NOT NULL */ static int indexColumnNotNull(Index *pIdx, int iCol){ int j; assert( pIdx!=0 ); assert( iCol>=0 && iColnColumn ); j = pIdx->aiColumn[iCol]; if (j>=0) { return pIdx->pTable->aCol[j].notNull; } else if (j==(-1)) { return 1; } else { assert( j==(-2)); return 0; /* Assume an indexed expression can always yield a NULL */ } } /* ** Return true if the DISTINCT expression-list passed as the third argument ** is redundant. ** ** A DISTINCT list is redundant if any subset of the columns in the ** DISTINCT list are collectively unique and individually non-null. */ static int isDistinctRedundant( Parse *pParse, /* Parsing context */ SrcList *pTabList, /* The FROM clause */ WhereClause *pWC, /* The WHERE clause */ ExprList *pDistinct /* The result set that needs to be DISTINCT */ ){ Table *pTab; Index *pIdx; int i; int iBase; /* If there is more than one table or sub-select in the FROM clause of ** this query, then it will not be possible to show that the DISTINCT ** clause is redundant. */ if (pTabList->nSrc!=1) return 0; iBase = pTabList->a[0].iCursor; pTab = pTabList->a[0].pTab; /* If any of the expressions is an IPK column on table iBase, then return ** true. Note: The (p->iTable==iBase) part of this test may be false if the ** current SELECT is a correlated sub-query. */ for (i=0; inExpr; i++) { Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr); if (p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0) return 1; } /* Loop through all indices on the table, checking each to see if it makes ** the DISTINCT qualifier redundant. It does so if: ** ** 1. The index is itself UNIQUE, and ** ** 2. All of the columns in the index are either part of the pDistinct ** list, or else the WHERE clause contains a term of the form "col=X", ** where X is a constant value. The collation sequences of the ** comparison and select-list expressions must match those of the index. ** ** 3. All of those index columns for which the WHERE clause does not ** contain a "col=X" term are subject to a NOT NULL constraint. */ for (pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext) { if (!IsUniqueIndex(pIdx)) continue; for (i=0; inKeyCol; i++) { if (0==sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx)) { if (findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0) break; if (indexColumnNotNull(pIdx, i)==0) break; } } if (i==pIdx->nKeyCol) { /* This index implies that the DISTINCT qualifier is redundant. */ return 1; } } return 0; } /* ** Estimate the logarithm of the input value to base 2. */ static LogEst estLog(LogEst N){ return N<=10 ? 0 : sqlite3LogEst(N) - 33; } /* ** Convert OP_Column opcodes to OP_Copy in previously generated code. ** ** This routine runs over generated VDBE code and translates OP_Column ** opcodes into OP_Copy when the table is being accessed via co-routine ** instead of via table lookup. ** ** If the iAutoidxCur is not zero, then any OP_Rowid instructions on ** cursor iTabCur are transformed into OP_Sequence opcode for the ** iAutoidxCur cursor, in order to generate unique rowids for the ** automatic index being generated. */ static void translateColumnToCopy( Parse *pParse, /* Parsing context */ int iStart, /* Translate from this opcode to the end */ int iTabCur, /* OP_Column/OP_Rowid references to this table */ int iRegister, /* The first column is in this register */ int iAutoidxCur /* If non-zero, cursor of autoindex being generated */ ){ Vdbe *v = pParse->pVdbe; VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart); int iEnd = sqlite3VdbeCurrentAddr(v); if (pParse->db->mallocFailed) return; for (; iStartp1!=iTabCur) continue; if (pOp->opcode==OP_Column) { pOp->opcode = OP_Copy; pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; } else if (pOp->opcode==OP_Rowid) { if (iAutoidxCur) { pOp->opcode = OP_Sequence; pOp->p1 = iAutoidxCur; } else { pOp->opcode = OP_Null; pOp->p1 = 0; pOp->p3 = 0; } } } } /* ** Two routines for printing the content of an sqlite3_index_info ** structure. Used for testing and debugging only. If neither ** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines ** are no-ops. */ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) static void TRACE_IDX_INPUTS(sqlite3_index_info *p){ int i; if (!sqlite3WhereTrace) return; for (i=0; inConstraint; i++) { sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n", i, p->aConstraint[i].iColumn, p->aConstraint[i].iTermOffset, p->aConstraint[i].op, p->aConstraint[i].usable); } for (i=0; inOrderBy; i++) { sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n", i, p->aOrderBy[i].iColumn, p->aOrderBy[i].desc); } } static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){ int i; if (!sqlite3WhereTrace) return; for (i=0; inConstraint; i++) { sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", i, p->aConstraintUsage[i].argvIndex, p->aConstraintUsage[i].omit); } sqlite3DebugPrintf(" idxNum=%d\n", p->idxNum); sqlite3DebugPrintf(" idxStr=%s\n", p->idxStr); sqlite3DebugPrintf(" orderByConsumed=%d\n", p->orderByConsumed); sqlite3DebugPrintf(" estimatedCost=%g\n", p->estimatedCost); sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); } #else #define TRACE_IDX_INPUTS(A) #define TRACE_IDX_OUTPUTS(A) #endif #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** Return TRUE if the WHERE clause term pTerm is of a form where it ** could be used with an index to access pSrc, assuming an appropriate ** index existed. */ static int termCanDriveIndex( WhereTerm *pTerm, /* WHERE clause term to check */ struct SrcList_item *pSrc, /* Table we are trying to access */ Bitmask notReady /* Tables in outer loops of the join */ ){ char aff; if (pTerm->leftCursor!=pSrc->iCursor) return 0; if ((pTerm->eOperator & (WO_EQ|WO_IS))==0) return 0; if ((pSrc->fg.jointype & JT_LEFT) && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) && (pTerm->eOperator & WO_IS) ) { /* Cannot use an IS term from the WHERE clause as an index driver for ** the RHS of a LEFT JOIN. Such a term can only be used if it is from ** the ON clause. */ return 0; } if ((pTerm->prereqRight & notReady)!=0) return 0; if (pTerm->u.leftColumn<0) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; if (!sqlite3IndexAffinityOk(pTerm->pExpr, aff)) return 0; testcase( pTerm->pExpr->op==TK_IS ); return 1; } #endif #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* ** Generate code to construct the Index object for an automatic index ** and to set up the WhereLevel object pLevel so that the code generator ** makes use of the automatic index. */ static void constructAutomaticIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to get the next index */ Bitmask notReady, /* Mask of cursors that are not available */ WhereLevel *pLevel /* Write new index here */ ){ int nKeyCol; /* Number of columns in the constructed index */ WhereTerm *pTerm; /* A single term of the WHERE clause */ WhereTerm *pWCEnd; /* End of pWC->a[] */ Index *pIdx; /* Object describing the transient index */ Vdbe *v; /* Prepared statement under construction */ int addrInit; /* Address of the initialization bypass jump */ Table *pTable; /* The table being indexed */ int addrTop; /* Top of the index fill loop */ int regRecord; /* Register holding an index record */ int n; /* Column counter */ int i; /* Loop counter */ int mxBitCol; /* Maximum column in pSrc->colUsed */ CollSeq *pColl; /* Collating sequence to on a column */ WhereLoop *pLoop; /* The Loop object */ char *zNotUsed; /* Extra space on the end of pIdx */ Bitmask idxCols; /* Bitmap of columns used for indexing */ Bitmask extraCols; /* Bitmap of additional columns */ u8 sentWarning = 0; /* True if a warnning has been issued */ Expr *pPartial = 0; /* Partial Index Expression */ int iContinue = 0; /* Jump here to skip excluded rows */ struct SrcList_item *pTabItem; /* FROM clause term being indexed */ int addrCounter = 0; /* Address where integer counter is initialized */ int regBase; /* Array of registers where record is assembled */ /* Generate code to skip over the creation and initialization of the ** transient index on 2nd and subsequent iterations of the loop. */ v = pParse->pVdbe; assert( v!=0 ); addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); /* Count the number of columns that will be added to the index ** and used to match WHERE clause constraints */ nKeyCol = 0; pTable = pSrc->pTab; pWCEnd = &pWC->a[pWC->nTerm]; pLoop = pLevel->pWLoop; idxCols = 0; for (pTerm=pWC->a; pTermpExpr; assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ if (pLoop->prereq==0 && (pTerm->wtFlags & TERM_VIRTUAL)==0 && !ExprHasProperty(pExpr, EP_FromJoin) && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor)) { pPartial = sqlite3ExprAnd(pParse, pPartial, sqlite3ExprDup(pParse->db, pExpr, 0)); } if (termCanDriveIndex(pTerm, pSrc, notReady)) { int iCol = pTerm->u.leftColumn; Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); testcase( iCol==BMS ); testcase( iCol==BMS-1 ); if (!sentWarning) { sqlite3_log(SQLITE_WARNING_AUTOINDEX, "automatic index on %s(%s)", pTable->zName, pTable->aCol[iCol].zName); sentWarning = 1; } if ((idxCols & cMask)==0) { if (whereLoopResize(pParse->db, pLoop, nKeyCol+1)) { goto end_auto_index_create; } pLoop->aLTerm[nKeyCol++] = pTerm; idxCols |= cMask; } } } assert( nKeyCol>0 ); pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol; pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED | WHERE_AUTO_INDEX; /* Count the number of additional columns needed to create a ** covering index. A "covering index" is an index that contains all ** columns that are needed by the query. With a covering index, the ** original table never needs to be accessed. Automatic indices must ** be a covering index because the index will not be updated if the ** original table changes and the index and table cannot both be used ** if they go out of sync. */ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); mxBitCol = MIN(BMS-1,pTable->nCol); testcase( pTable->nCol==BMS-1 ); testcase( pTable->nCol==BMS-2 ); for (i=0; icolUsed & MASKBIT(BMS-1)) { nKeyCol += pTable->nCol - BMS + 1; } /* Construct the Index object to describe this index */ pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed); if (pIdx==0) goto end_auto_index_create; pLoop->u.btree.pIndex = pIdx; pIdx->zName = "auto-index"; pIdx->pTable = pTable; n = 0; idxCols = 0; for (pTerm=pWC->a; pTermu.leftColumn; Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol); testcase( iCol==BMS-1 ); testcase( iCol==BMS ); if ((idxCols & cMask)==0) { Expr *pX = pTerm->pExpr; idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY; n++; } } } assert((u32)n==pLoop->u.btree.nEq ); /* Add additional columns needed to make the automatic index into ** a covering index */ for (i=0; iaiColumn[n] = i; pIdx->azColl[n] = sqlite3StrBINARY; n++; } } if (pSrc->colUsed & MASKBIT(BMS-1)) { for (i=BMS-1; inCol; i++) { pIdx->aiColumn[n] = i; pIdx->azColl[n] = sqlite3StrBINARY; n++; } } assert( n==nKeyCol ); pIdx->aiColumn[n] = XN_ROWID; pIdx->azColl[n] = sqlite3StrBINARY; /* Create the automatic index */ assert( pLevel->iIdxCur>=0 ); pLevel->iIdxCur = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); /* Fill the automatic index with content */ pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; if (pTabItem->fg.viaCoroutine) { int regYield = pTabItem->regReturn; addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); VdbeCoverage(v); VdbeComment((v, "next row of %s", pTabItem->pTab->zName)); } else { addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); } if (pPartial) { iContinue = sqlite3VdbeMakeLabel(pParse); sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL); pLoop->wsFlags |= WHERE_PARTIALIDX; } regRecord = sqlite3GetTempReg(pParse); regBase = sqlite3GenerateIndexKey( pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0 ); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if (pPartial) sqlite3VdbeResolveLabel(v, iContinue); if (pTabItem->fg.viaCoroutine) { sqlite3VdbeChangeP2(v, addrCounter, regBase+n); testcase( pParse->db->mallocFailed ); assert( pLevel->iIdxCur>0 ); translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, pTabItem->regResult, pLevel->iIdxCur); sqlite3VdbeGoto(v, addrTop); pTabItem->fg.viaCoroutine = 0; } else { sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); } sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); sqlite3VdbeJumpHere(v, addrTop); sqlite3ReleaseTempReg(pParse, regRecord); /* Jump here when skipping the initialization */ sqlite3VdbeJumpHere(v, addrInit); end_auto_index_create: sqlite3ExprDelete(pParse->db, pPartial); } #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Allocate and populate an sqlite3_index_info structure. It is the ** responsibility of the caller to eventually release the structure ** by passing the pointer returned by this function to sqlite3_free(). */ static sqlite3_index_info *allocateIndexInfo( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause being analyzed */ Bitmask mUnusable, /* Ignore terms with these prereqs */ struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */ ExprList *pOrderBy, /* The ORDER BY clause */ u16 *pmNoOmit /* Mask of terms not to omit */ ){ int i, j; int nTerm; struct sqlite3_index_constraint *pIdxCons; struct sqlite3_index_orderby *pIdxOrderBy; struct sqlite3_index_constraint_usage *pUsage; struct HiddenIndexInfo *pHidden; WhereTerm *pTerm; int nOrderBy; sqlite3_index_info *pIdxInfo; u16 mNoOmit = 0; /* Count the number of possible WHERE clause constraints referring ** to this virtual table */ for (i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++) { if (pTerm->leftCursor != pSrc->iCursor) continue; if (pTerm->prereqRight & mUnusable) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV)); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_ALL ); if ((pTerm->eOperator & ~(WO_EQUIV))==0) continue; if (pTerm->wtFlags & TERM_VNULL) continue; assert( pTerm->u.leftColumn>=(-1)); nTerm++; } /* If the ORDER BY clause contains only columns in the current ** virtual table then allocate space for the aOrderBy part of ** the sqlite3_index_info structure. */ nOrderBy = 0; if (pOrderBy) { int n = pOrderBy->nExpr; for (i=0; ia[i].pExpr; if (pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor) break; } if (i==n) { nOrderBy = n; } } /* Allocate the sqlite3_index_info structure */ pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)); if (pIdxInfo==0) { sqlite3ErrorMsg(pParse, "out of memory"); return 0; } /* Initialize the structure. The sqlite3_index_info structure contains ** many fields that are declared "const" to prevent xBestIndex from ** changing them. We have to do some funky casting in order to ** initialize those fields. */ pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1]; pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm]; pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; *(int*)&pIdxInfo->nConstraint = nTerm; *(int*)&pIdxInfo->nOrderBy = nOrderBy; *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint = pIdxCons; *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy; *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage = pUsage; pHidden->pWC = pWC; pHidden->pParse = pParse; for (i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++) { u16 op; if (pTerm->leftCursor != pSrc->iCursor) continue; if (pTerm->prereqRight & mUnusable) continue; assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV)); testcase( pTerm->eOperator & WO_IN ); testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_ALL ); if ((pTerm->eOperator & ~(WO_EQUIV))==0) continue; if (pTerm->wtFlags & TERM_VNULL) continue; if ((pSrc->fg.jointype & JT_LEFT)!=0 && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) && (pTerm->eOperator & (WO_IS|WO_ISNULL)) ) { /* An "IS" term in the WHERE clause where the virtual table is the rhs ** of a LEFT JOIN. Do not pass this term to the virtual table ** implementation, as this can lead to incorrect results from SQL such ** as: ** ** "LEFT JOIN vtab WHERE vtab.col IS NULL" */ testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IS ); continue; } assert( pTerm->u.leftColumn>=(-1)); pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; op = pTerm->eOperator & WO_ALL; if (op==WO_IN) op = WO_EQ; if (op==WO_AUX) { pIdxCons[j].op = pTerm->eMatchOp; } else if (op & (WO_ISNULL|WO_IS)) { if (op==WO_ISNULL) { pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_ISNULL; } else { pIdxCons[j].op = SQLITE_INDEX_CONSTRAINT_IS; } } else { pIdxCons[j].op = (u8)op; /* The direct assignment in the previous line is possible only because ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The ** following asserts verify this fact. */ assert( WO_EQ==SQLITE_INDEX_CONSTRAINT_EQ ); assert( WO_LT==SQLITE_INDEX_CONSTRAINT_LT ); assert( WO_LE==SQLITE_INDEX_CONSTRAINT_LE ); assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX)); if (op & (WO_LT|WO_LE|WO_GT|WO_GE) && sqlite3ExprIsVector(pTerm->pExpr->pRight) ) { if (i<16) mNoOmit |= (1 << i); if (op==WO_LT) pIdxCons[j].op = WO_LE; if (op==WO_GT) pIdxCons[j].op = WO_GE; } } j++; } for (i=0; ia[i].pExpr; pIdxOrderBy[i].iColumn = pExpr->iColumn; pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder; } *pmNoOmit = mNoOmit; return pIdxInfo; } /* ** The table object reference passed as the second argument to this function ** must represent a virtual table. This function invokes the xBestIndex() ** method of the virtual table with the sqlite3_index_info object that ** comes in as the 3rd argument to this function. ** ** If an error occurs, pParse is populated with an error message and an ** appropriate error code is returned. A return of SQLITE_CONSTRAINT from ** xBestIndex is not considered an error. SQLITE_CONSTRAINT indicates that ** the current configuration of "unusable" flags in sqlite3_index_info can ** not result in a valid plan. ** ** Whether or not an error is returned, it is the responsibility of the ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates ** that this is required. */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int rc; TRACE_IDX_INPUTS(p); rc = pVtab->pModule->xBestIndex(pVtab, p); TRACE_IDX_OUTPUTS(p); if (rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT) { if (rc==SQLITE_NOMEM) { sqlite3OomFault(pParse->db); } else if (!pVtab->zErrMsg) { sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc)); } else { sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); } } sqlite3_free(pVtab->zErrMsg); pVtab->zErrMsg = 0; return rc; } #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ #ifdef SQLITE_ENABLE_STAT4 /* ** Estimate the location of a particular key among all keys in an ** index. Store the results in aStat as follows: ** ** aStat[0] Est. number of rows less than pRec ** aStat[1] Est. number of rows equal to pRec ** ** Return the index of the sample that is the smallest sample that ** is greater than or equal to pRec. Note that this index is not an index ** into the aSample[] array - it is an index into a virtual set of samples ** based on the contents of aSample[] and the number of fields in record ** pRec. */ static int whereKeyStats( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ UnpackedRecord *pRec, /* Vector of values to consider */ int roundUp, /* Round up if true. Round down if false */ tRowcnt *aStat /* OUT: stats written here */ ){ IndexSample *aSample = pIdx->aSample; int iCol; /* Index of required stats in anEq[] etc. */ int i; /* Index of first sample >= pRec */ int iSample; /* Smallest sample larger than or equal to pRec */ int iMin = 0; /* Smallest sample not yet tested */ int iTest; /* Next sample to test */ int res; /* Result of comparison operation */ int nField; /* Number of fields in pRec */ tRowcnt iLower = 0; /* anLt[] + anEq[] of largest sample pRec is > */ #ifndef SQLITE_DEBUG UNUSED_PARAMETER( pParse ); #endif assert( pRec!=0 ); assert( pIdx->nSample>0 ); assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); /* Do a binary search to find the first sample greater than or equal ** to pRec. If pRec contains a single field, the set of samples to search ** is simply the aSample[] array. If the samples in aSample[] contain more ** than one fields, all fields following the first are ignored. ** ** If pRec contains N fields, where N is more than one, then as well as the ** samples in aSample[] (truncated to N fields), the search also has to ** consider prefixes of those samples. For example, if the set of samples ** in aSample is: ** ** aSample[0] = (a, 5) ** aSample[1] = (a, 10) ** aSample[2] = (b, 5) ** aSample[3] = (c, 100) ** aSample[4] = (c, 105) ** ** Then the search space should ideally be the samples above and the ** unique prefixes [a], [b] and [c]. But since that is hard to organize, ** the code actually searches this set: ** ** 0: (a) ** 1: (a, 5) ** 2: (a, 10) ** 3: (a, 10) ** 4: (b) ** 5: (b, 5) ** 6: (c) ** 7: (c, 100) ** 8: (c, 105) ** 9: (c, 105) ** ** For each sample in the aSample[] array, N samples are present in the ** effective sample array. In the above, samples 0 and 1 are based on ** sample aSample[0]. Samples 2 and 3 on aSample[1] etc. ** ** Often, sample i of each block of N effective samples has (i+1) fields. ** Except, each sample may be extended to ensure that it is greater than or ** equal to the previous sample in the array. For example, in the above, ** sample 2 is the first sample of a block of N samples, so at first it ** appears that it should be 1 field in size. However, that would make it ** smaller than sample 1, so the binary search would not work. As a result, ** it is extended to two fields. The duplicates that this creates do not ** cause any problems. */ nField = pRec->nField; iCol = 0; iSample = pIdx->nSample * nField; do{ int iSamp; /* Index in aSample[] of test sample */ int n; /* Number of fields in test sample */ iTest = (iMin+iSample)/2; iSamp = iTest / nField; if (iSamp>0) { /* The proposed effective sample is a prefix of sample aSample[iSamp]. ** Specifically, the shortest prefix of at least (1 + iTest%nField) ** fields that is greater than the previous effective sample. */ for (n=(iTest % nField) + 1; nnField = n; res = sqlite3VdbeRecordCompare(aSample[iSamp].n, aSample[iSamp].p, pRec); if (res<0) { iLower = aSample[iSamp].anLt[n-1] + aSample[iSamp].anEq[n-1]; iMin = iTest+1; } else if (res==0 && ndb->mallocFailed==0) { if (res==0) { /* If (res==0) is true, then pRec must be equal to sample i. */ assert( inSample ); assert( iCol==nField-1 ); pRec->nField = nField; assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) || pParse->db->mallocFailed ); } else { /* Unless i==pIdx->nSample, indicating that pRec is larger than ** all samples in the aSample[] array, pRec must be smaller than the ** (iCol+1) field prefix of sample i. */ assert( i<=pIdx->nSample && i>=0 ); pRec->nField = iCol+1; assert( i==pIdx->nSample || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 || pParse->db->mallocFailed ); /* if i==0 and iCol==0, then record pRec is smaller than all samples ** in the aSample[] array. Otherwise, if (iCol>0) then pRec must ** be greater than or equal to the (iCol) field prefix of sample i. ** If (i>0), then pRec must also be greater than sample (i-1). */ if (iCol>0) { pRec->nField = iCol; assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0 || pParse->db->mallocFailed ); } if (i>0) { pRec->nField = nField; assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 || pParse->db->mallocFailed ); } } } #endif /* ifdef SQLITE_DEBUG */ if (res==0) { /* Record pRec is equal to sample i */ assert( iCol==nField-1 ); aStat[0] = aSample[i].anLt[iCol]; aStat[1] = aSample[i].anEq[iCol]; } else { /* At this point, the (iCol+1) field prefix of aSample[i] is the first ** sample that is greater than pRec. Or, if i==pIdx->nSample then pRec ** is larger than all samples in the array. */ tRowcnt iUpper, iGap; if (i>=pIdx->nSample) { iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); } else { iUpper = aSample[i].anLt[iCol]; } if (iLower>=iUpper) { iGap = 0; } else { iGap = iUpper - iLower; } if (roundUp) { iGap = (iGap*2)/3; } else { iGap = iGap/3; } aStat[0] = iLower + iGap; aStat[1] = pIdx->aAvgEq[nField-1]; } /* Restore the pRec->nField value before returning. */ pRec->nField = nField; return i; } #endif /* SQLITE_ENABLE_STAT4 */ /* ** If it is not NULL, pTerm is a term that provides an upper or lower ** bound on a range scan. Without considering pTerm, it is estimated ** that the scan will visit nNew rows. This function returns the number ** estimated to be visited after taking pTerm into account. ** ** If the user explicitly specified a likelihood() value for this term, ** then the return value is the likelihood multiplied by the number of ** input rows. Otherwise, this function assumes that an "IS NOT NULL" term ** has a likelihood of 0.50, and any other term a likelihood of 0.25. */ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){ LogEst nRet = nNew; if (pTerm) { if (pTerm->truthProb<=0) { nRet += pTerm->truthProb; } else if ((pTerm->wtFlags & TERM_VNULL)==0) { nRet -= 20; assert( 20==sqlite3LogEst(4)); } } return nRet; } #ifdef SQLITE_ENABLE_STAT4 /* ** Return the affinity for a single column of an index. */ char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){ assert( iCol>=0 && iColnColumn ); if (!pIdx->zColAff) { if (sqlite3IndexAffinityStr(db, pIdx)==0) return SQLITE_AFF_BLOB; } assert( pIdx->zColAff[iCol]!=0 ); return pIdx->zColAff[iCol]; } #endif #ifdef SQLITE_ENABLE_STAT4 /* ** This function is called to estimate the number of rows visited by a ** range-scan on a skip-scan index. For example: ** ** CREATE INDEX i1 ON t1(a, b, c); ** SELECT * FROM t1 WHERE a=? AND c BETWEEN ? AND ?; ** ** Value pLoop->nOut is currently set to the estimated number of rows ** visited for scanning (a=? AND b=?). This function reduces that estimate ** by some factor to account for the (c BETWEEN ? AND ?) expression based ** on the stat4 data for the index. this scan will be peformed multiple ** times (once for each (a,b) combination that matches a=?) is dealt with ** by the caller. ** ** It does this by scanning through all stat4 samples, comparing values ** extracted from pLower and pUpper with the corresponding column in each ** sample. If L and U are the number of samples found to be less than or ** equal to the values extracted from pLower and pUpper respectively, and ** N is the total number of samples, the pLoop->nOut value is adjusted ** as follows: ** ** nOut = nOut * ( min(U - L, 1) / N ) ** ** If pLower is NULL, or a value cannot be extracted from the term, L is ** set to zero. If pUpper is NULL, or a value cannot be extracted from it, ** U is set to N. ** ** Normally, this function sets *pbDone to 1 before returning. However, ** if no value can be extracted from either pLower or pUpper (and so the ** estimate of the number of rows delivered remains unchanged), *pbDone ** is left as is. ** ** If an error occurs, an SQLite error code is returned. Otherwise, ** SQLITE_OK. */ static int whereRangeSkipScanEst( Parse *pParse, /* Parsing & code generating context */ WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ WhereLoop *pLoop, /* Update the .nOut value of this loop */ int *pbDone /* Set to true if at least one expr. value extracted */ ){ Index *p = pLoop->u.btree.pIndex; int nEq = pLoop->u.btree.nEq; sqlite3 *db = pParse->db; int nLower = -1; int nUpper = p->nSample+1; int rc = SQLITE_OK; u8 aff = sqlite3IndexColumnAffinity(db, p, nEq); CollSeq *pColl; sqlite3_value *p1 = 0; /* Value extracted from pLower */ sqlite3_value *p2 = 0; /* Value extracted from pUpper */ sqlite3_value *pVal = 0; /* Value extracted from record */ pColl = sqlite3LocateCollSeq(pParse, p->azColl[nEq]); if (pLower) { rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight, aff, &p1); nLower = 0; } if (pUpper && rc==SQLITE_OK) { rc = sqlite3Stat4ValueFromExpr(pParse, pUpper->pExpr->pRight, aff, &p2); nUpper = p2 ? 0 : p->nSample; } if (p1 || p2) { int i; int nDiff; for (i=0; rc==SQLITE_OK && inSample; i++) { rc = sqlite3Stat4Column(db, p->aSample[i].p, p->aSample[i].n, nEq, &pVal); if (rc==SQLITE_OK && p1) { int res = sqlite3MemCompare(p1, pVal, pColl); if (res>=0) nLower++; } if (rc==SQLITE_OK && p2) { int res = sqlite3MemCompare(p2, pVal, pColl); if (res>=0) nUpper++; } } nDiff = (nUpper - nLower); if (nDiff<=0) nDiff = 1; /* If there is both an upper and lower bound specified, and the ** comparisons indicate that they are close together, use the fallback ** method (assume that the scan visits 1/64 of the rows) for estimating ** the number of rows visited. Otherwise, estimate the number of rows ** using the method described in the header comment for this function. */ if (nDiff!=1 || pUpper==0 || pLower==0) { int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff)); pLoop->nOut -= nAdjust; *pbDone = 1; WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n", nLower, nUpper, nAdjust* -1, pLoop->nOut)); } } else { assert( *pbDone==0 ); } sqlite3ValueFree(p1); sqlite3ValueFree(p2); sqlite3ValueFree(pVal); return rc; } #endif /* SQLITE_ENABLE_STAT4 */ /* ** This function is used to estimate the number of rows that will be visited ** by scanning an index for a range of values. The range may have an upper ** bound, a lower bound, or both. The WHERE clause terms that set the upper ** and lower bounds are represented by pLower and pUpper respectively. For ** example, assuming that index p is on t1(a): ** ** ... FROM t1 WHERE a > ? AND a < ? ... ** |_____| |_____| ** | | ** pLower pUpper ** ** If either of the upper or lower bound is not present, then NULL is passed in ** place of the corresponding WhereTerm. ** ** The value in (pBuilder->pNew->u.btree.nEq) is the number of the index ** column subject to the range constraint. Or, equivalently, the number of ** equality constraints optimized by the proposed index scan. For example, ** assuming index p is on t1(a, b), and the SQL query is: ** ** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ... ** ** then nEq is set to 1 (as the range restricted column, b, is the second ** left-most column of the index). Or, if the query is: ** ** ... FROM t1 WHERE a > ? AND a < ? ... ** ** then nEq is set to 0. ** ** When this function is called, *pnOut is set to the sqlite3LogEst() of the ** number of rows that the index scan is expected to visit without ** considering the range constraints. If nEq is 0, then *pnOut is the number of ** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced) ** to account for the range constraints pLower and pUpper. ** ** In the absence of sqlite_stat4 ANALYZE data, or if such data cannot be ** used, a single range inequality reduces the search space by a factor of 4. ** and a pair of constraints (x>? AND x123" Might be NULL */ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ WhereLoop *pLoop /* Modify the .nOut and maybe .rRun fields */ ){ int rc = SQLITE_OK; int nOut = pLoop->nOut; LogEst nNew; #ifdef SQLITE_ENABLE_STAT4 Index *p = pLoop->u.btree.pIndex; int nEq = pLoop->u.btree.nEq; if (p->nSample>0 && ALWAYS(nEqnSampleCol) && OptimizationEnabled(pParse->db, SQLITE_Stat4) ) { if (nEq==pBuilder->nRecValid) { UnpackedRecord *pRec = pBuilder->pRec; tRowcnt a[2]; int nBtm = pLoop->u.btree.nBtm; int nTop = pLoop->u.btree.nTop; /* Variable iLower will be set to the estimate of the number of rows in ** the index that are less than the lower bound of the range query. The ** lower bound being the concatenation of $P and $L, where $P is the ** key-prefix formed by the nEq values matched against the nEq left-most ** columns of the index, and $L is the value in pLower. ** ** Or, if pLower is NULL or $L cannot be extracted from it (because it ** is not a simple variable or literal value), the lower bound of the ** range is $P. Due to a quirk in the way whereKeyStats() works, even ** if $L is available, whereKeyStats() is called for both ($P) and ** ($P:$L) and the larger of the two returned values is used. ** ** Similarly, iUpper is to be set to the estimate of the number of rows ** less than the upper bound of the range query. Where the upper bound ** is either ($P) or ($P:$U). Again, even if $U is available, both values ** of iUpper are requested of whereKeyStats() and the smaller used. ** ** The number of rows between the two bounds is then just iUpper-iLower. */ tRowcnt iLower; /* Rows less than the lower bound */ tRowcnt iUpper; /* Rows less than the upper bound */ int iLwrIdx = -2; /* aSample[] for the lower bound */ int iUprIdx = -1; /* aSample[] for the upper bound */ if (pRec) { testcase( pRec->nField!=pBuilder->nRecValid ); pRec->nField = pBuilder->nRecValid; } /* Determine iLower and iUpper using ($P) only. */ if (nEq==0) { iLower = 0; iUpper = p->nRowEst0; } else { /* Note: this call could be optimized away - since the same values must ** have been requested when testing key $P in whereEqualScanEst(). */ whereKeyStats(pParse, p, pRec, 0, a); iLower = a[0]; iUpper = a[0] + a[1]; } assert( pLower==0 || (pLower->eOperator & (WO_GT|WO_GE))!=0 ); assert( pUpper==0 || (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); assert( p->aSortOrder!=0 ); if (p->aSortOrder[nEq]) { /* The roles of pLower and pUpper are swapped for a DESC index */ SWAP(WhereTerm*, pLower, pUpper); SWAP(int, nBtm, nTop); } /* If possible, improve on the iLower estimate using ($P:$L). */ if (pLower) { int n; /* Values extracted from pExpr */ Expr *pExpr = pLower->pExpr->pRight; rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nBtm, nEq, &n); if (rc==SQLITE_OK && n) { tRowcnt iNew; u16 mask = WO_GT|WO_LE; if (sqlite3ExprVectorSize(pExpr)>n) mask = (WO_LE|WO_LT); iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a); iNew = a[0] + ((pLower->eOperator & mask) ? a[1] : 0); if (iNew>iLower) iLower = iNew; nOut--; pLower = 0; } } /* If possible, improve on the iUpper estimate using ($P:$U). */ if (pUpper) { int n; /* Values extracted from pExpr */ Expr *pExpr = pUpper->pExpr->pRight; rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nTop, nEq, &n); if (rc==SQLITE_OK && n) { tRowcnt iNew; u16 mask = WO_GT|WO_LE; if (sqlite3ExprVectorSize(pExpr)>n) mask = (WO_LE|WO_LT); iUprIdx = whereKeyStats(pParse, p, pRec, 1, a); iNew = a[0] + ((pUpper->eOperator & mask) ? a[1] : 0); if (iNewpRec = pRec; if (rc==SQLITE_OK) { if (iUpper>iLower) { nNew = sqlite3LogEst(iUpper - iLower); /* TUNING: If both iUpper and iLower are derived from the same ** sample, then assume they are 4x more selective. This brings ** the estimated selectivity more in line with what it would be ** if estimated without the use of STAT4 tables. */ if (iLwrIdx==iUprIdx) nNew -= 20; assert( 20==sqlite3LogEst(4)); } else { nNew = 10; assert( 10==sqlite3LogEst(2)); } if (nNewwtFlags & TERM_VNULL)==0 ); nNew = whereRangeAdjust(pLower, nOut); nNew = whereRangeAdjust(pUpper, nNew); /* TUNING: If there is both an upper and lower limit and neither limit ** has an application-defined likelihood(), assume the range is ** reduced by an additional 75%. This means that, by default, an open-ended ** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the ** index. While a closed range (e.g. col BETWEEN ? AND ?) is estimated to ** match 1/64 of the index. */ if (pLower && pLower->truthProb>0 && pUpper && pUpper->truthProb>0) { nNew -= 20; } nOut -= (pLower!=0) + (pUpper!=0); if (nNew<10) nNew = 10; if (nNewnOut>nOut) { WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n", pLoop->nOut, nOut)); } #endif pLoop->nOut = (LogEst)nOut; return rc; } #ifdef SQLITE_ENABLE_STAT4 /* ** Estimate the number of rows that will be returned based on ** an equality constraint x=VALUE and where that VALUE occurs in ** the histogram data. This only works when x is the left-most ** column of an index and sqlite_stat4 histogram data is available ** for that index. When pExpr==NULL that means the constraint is ** "x IS NULL" instead of "x=VALUE". ** ** Write the estimated row count into *pnRow and return SQLITE_OK. ** If unable to make an estimate, leave *pnRow unchanged and return ** non-zero. ** ** This routine can fail if it is unable to load a collating sequence ** required for string comparison, or if unable to allocate memory ** for a UTF conversion required for comparison. The error is stored ** in the pParse structure. */ static int whereEqualScanEst( Parse *pParse, /* Parsing & code generating context */ WhereLoopBuilder *pBuilder, Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ tRowcnt *pnRow /* Write the revised row estimate here */ ){ Index *p = pBuilder->pNew->u.btree.pIndex; int nEq = pBuilder->pNew->u.btree.nEq; UnpackedRecord *pRec = pBuilder->pRec; int rc; /* Subfunction return code */ tRowcnt a[2]; /* Statistics */ int bOk; assert( nEq>=1 ); assert( nEq<=p->nColumn ); assert( p->aSample!=0 ); assert( p->nSample>0 ); assert( pBuilder->nRecValidnRecValid<(nEq-1)) { return SQLITE_NOTFOUND; } /* This is an optimization only. The call to sqlite3Stat4ProbeSetValue() ** below would return the same value. */ if (nEq>=p->nColumn) { *pnRow = 1; return SQLITE_OK; } rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, 1, nEq-1, &bOk); pBuilder->pRec = pRec; if (rc!=SQLITE_OK) return rc; if (bOk==0) return SQLITE_NOTFOUND; pBuilder->nRecValid = nEq; whereKeyStats(pParse, p, pRec, 0, a); WHERETRACE(0x10,("equality scan regions %s(%d): %d\n", p->zName, nEq-1, (int)a[1])); *pnRow = a[1]; return rc; } #endif /* SQLITE_ENABLE_STAT4 */ #ifdef SQLITE_ENABLE_STAT4 /* ** Estimate the number of rows that will be returned based on ** an IN constraint where the right-hand side of the IN operator ** is a list of values. Example: ** ** WHERE x IN (1,2,3,4) ** ** Write the estimated row count into *pnRow and return SQLITE_OK. ** If unable to make an estimate, leave *pnRow unchanged and return ** non-zero. ** ** This routine can fail if it is unable to load a collating sequence ** required for string comparison, or if unable to allocate memory ** for a UTF conversion required for comparison. The error is stored ** in the pParse structure. */ static int whereInScanEst( Parse *pParse, /* Parsing & code generating context */ WhereLoopBuilder *pBuilder, ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ tRowcnt *pnRow /* Write the revised row estimate here */ ){ Index *p = pBuilder->pNew->u.btree.pIndex; i64 nRow0 = sqlite3LogEstToInt(p->aiRowLogEst[0]); int nRecValid = pBuilder->nRecValid; int rc = SQLITE_OK; /* Subfunction return code */ tRowcnt nEst; /* Number of rows for a single term */ tRowcnt nRowEst = 0; /* New estimate of the number of rows */ int i; /* Loop counter */ assert( p->aSample!=0 ); for (i=0; rc==SQLITE_OK && inExpr; i++) { nEst = nRow0; rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst); nRowEst += nEst; pBuilder->nRecValid = nRecValid; } if (rc==SQLITE_OK) { if (nRowEst > nRow0) nRowEst = nRow0; *pnRow = nRowEst; WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst)); } assert( pBuilder->nRecValid==nRecValid ); return rc; } #endif /* SQLITE_ENABLE_STAT4 */ #ifdef WHERETRACE_ENABLED /* ** Print the content of a WhereTerm object */ static void whereTermPrint(WhereTerm *pTerm, int iTerm){ if (pTerm==0) { sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm); } else { char zType[4]; char zLeft[50]; memcpy(zType, "...", 4); if (pTerm->wtFlags & TERM_VIRTUAL) zType[0] = 'V'; if (pTerm->eOperator & WO_EQUIV) zType[1] = 'E'; if (ExprHasProperty(pTerm->pExpr, EP_FromJoin)) zType[2] = 'L'; if (pTerm->eOperator & WO_SINGLE) { sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", pTerm->leftCursor, pTerm->u.leftColumn); } else if ((pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0) { sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld", pTerm->u.pOrInfo->indexable); } else { sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor); } sqlite3DebugPrintf( "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x", iTerm, pTerm, zType, zLeft, pTerm->truthProb, pTerm->eOperator, pTerm->wtFlags); if (pTerm->iField) { sqlite3DebugPrintf(" iField=%d\n", pTerm->iField); } else { sqlite3DebugPrintf("\n"); } sqlite3TreeViewExpr(0, pTerm->pExpr, 0); } } #endif #ifdef WHERETRACE_ENABLED /* ** Show the complete content of a WhereClause */ void sqlite3WhereClausePrint(WhereClause *pWC){ int i; for (i=0; inTerm; i++) { whereTermPrint(&pWC->a[i], i); } } #endif #ifdef WHERETRACE_ENABLED /* ** Print a WhereLoop object for debugging purposes */ static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){ WhereInfo *pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pTab; Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias : pTab->zName); if ((p->wsFlags & WHERE_VIRTUALTABLE)==0) { const char *zName; if (p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0) { if (strncmp(zName, "sqlite_autoindex_", 17)==0) { int i = sqlite3Strlen30(zName) - 1; while (zName[i]!='_') i--; zName += i; } sqlite3DebugPrintf(".%-16s %2d", zName, p->u.btree.nEq); } else { sqlite3DebugPrintf("%20s",""); } } else { char *z; if (p->u.vtab.idxStr) { z = sqlite3_mprintf("(%d,\"%s\",%x)", p->u.vtab.idxNum, p->u.vtab.idxStr, p->u.vtab.omitMask); } else { z = sqlite3_mprintf("(%d,%x)", p->u.vtab.idxNum, p->u.vtab.omitMask); } sqlite3DebugPrintf(" %-19s", z); sqlite3_free(z); } if (p->wsFlags & WHERE_SKIPSCAN) { sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip); } else { sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm); } sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut); if (p->nLTerm && (sqlite3WhereTrace & 0x100)!=0) { int i; for (i=0; inLTerm; i++) { whereTermPrint(p->aLTerm[i], i); } } } #endif /* ** Convert bulk memory into a valid WhereLoop that can be passed ** to whereLoopClear harmlessly. */ static void whereLoopInit(WhereLoop *p){ p->aLTerm = p->aLTermSpace; p->nLTerm = 0; p->nLSlot = ArraySize(p->aLTermSpace); p->wsFlags = 0; } /* ** Clear the WhereLoop.u union. Leave WhereLoop.pLTerm intact. */ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){ if (p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX)) { if ((p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree) { sqlite3_free(p->u.vtab.idxStr); p->u.vtab.needFree = 0; p->u.vtab.idxStr = 0; } else if ((p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0) { sqlite3DbFree(db, p->u.btree.pIndex->zColAff); sqlite3DbFreeNN(db, p->u.btree.pIndex); p->u.btree.pIndex = 0; } } } /* ** Deallocate internal memory used by a WhereLoop object */ static void whereLoopClear(sqlite3 *db, WhereLoop *p){ if (p->aLTerm!=p->aLTermSpace) sqlite3DbFreeNN(db, p->aLTerm); whereLoopClearUnion(db, p); whereLoopInit(p); } /* ** Increase the memory allocation for pLoop->aLTerm[] to be at least n. */ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){ WhereTerm **paNew; if (p->nLSlot>=n) return SQLITE_OK; n = (n+7)&~7; paNew = sqlite3DbMallocRawNN(db, sizeof(p->aLTerm[0])*n); if (paNew==0) return SQLITE_NOMEM_BKPT; memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot); if (p->aLTerm!=p->aLTermSpace) sqlite3DbFreeNN(db, p->aLTerm); p->aLTerm = paNew; p->nLSlot = n; return SQLITE_OK; } /* ** Transfer content from the second pLoop into the first. */ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){ whereLoopClearUnion(db, pTo); if (whereLoopResize(db, pTo, pFrom->nLTerm)) { memset(&pTo->u, 0, sizeof(pTo->u)); return SQLITE_NOMEM_BKPT; } memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ); memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0])); if (pFrom->wsFlags & WHERE_VIRTUALTABLE) { pFrom->u.vtab.needFree = 0; } else if ((pFrom->wsFlags & WHERE_AUTO_INDEX)!=0) { pFrom->u.btree.pIndex = 0; } return SQLITE_OK; } /* ** Delete a WhereLoop object */ static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ whereLoopClear(db, p); sqlite3DbFreeNN(db, p); } /* ** Free a WhereInfo structure */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ int i; assert( pWInfo!=0 ); for (i=0; inLevel; i++) { WhereLevel *pLevel = &pWInfo->a[i]; if (pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE)) { sqlite3DbFree(db, pLevel->u.in.aInLoop); } } sqlite3WhereClauseClear(&pWInfo->sWC); while (pWInfo->pLoops) { WhereLoop *p = pWInfo->pLoops; pWInfo->pLoops = p->pNextLoop; whereLoopDelete(db, p); } sqlite3DbFreeNN(db, pWInfo); } /* ** Return TRUE if all of the following are true: ** ** (1) X has the same or lower cost that Y ** (2) X uses fewer WHERE clause terms than Y ** (3) Every WHERE clause term used by X is also used by Y ** (4) X skips at least as many columns as Y ** (5) If X is a covering index, than Y is too ** ** Conditions (2) and (3) mean that X is a "proper subset" of Y. ** If X is a proper subset of Y then Y is a better choice and ought ** to have a lower cost. This routine returns TRUE when that cost ** relationship is inverted and needs to be adjusted. Constraint (4) ** was added because if X uses skip-scan less than Y it still might ** deserve a lower cost even if it is a proper subset of Y. Constraint (5) ** was added because a covering index probably deserves to have a lower cost ** than a non-covering index even if it is a proper subset. */ static int whereLoopCheaperProperSubset( const WhereLoop *pX, /* First WhereLoop to compare */ const WhereLoop *pY /* Compare against this WhereLoop */ ){ int i, j; if (pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip) { return 0; /* X is not a subset of Y */ } if (pY->nSkip > pX->nSkip) return 0; if (pX->rRun >= pY->rRun) { if (pX->rRun > pY->rRun) return 0; /* X costs more than Y */ if (pX->nOut > pY->nOut) return 0; /* X costs more than Y */ } for (i=pX->nLTerm-1; i>=0; i--) { if (pX->aLTerm[i]==0) continue; for (j=pY->nLTerm-1; j>=0; j--) { if (pY->aLTerm[j]==pX->aLTerm[i]) break; } if (j<0) return 0; /* X not a subset of Y since term X[i] not used by Y */ } if ((pX->wsFlags&WHERE_IDX_ONLY)!=0 && (pY->wsFlags&WHERE_IDX_ONLY)==0) { return 0; /* Constraint (5) */ } return 1; /* All conditions meet */ } /* ** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so ** that: ** ** (1) pTemplate costs less than any other WhereLoops that are a proper ** subset of pTemplate ** ** (2) pTemplate costs more than any other WhereLoops for which pTemplate ** is a proper subset. ** ** To say "WhereLoop X is a proper subset of Y" means that X uses fewer ** WHERE clause terms than Y and that every WHERE clause term used by X is ** also used by Y. */ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){ if ((pTemplate->wsFlags & WHERE_INDEXED)==0) return; for (; p; p=p->pNextLoop) { if (p->iTab!=pTemplate->iTab) continue; if ((p->wsFlags & WHERE_INDEXED)==0) continue; if (whereLoopCheaperProperSubset(p, pTemplate)) { /* Adjust pTemplate cost downward so that it is cheaper than its ** subset p. */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1)); pTemplate->rRun = p->rRun; pTemplate->nOut = p->nOut - 1; } else if (whereLoopCheaperProperSubset(pTemplate, p)) { /* Adjust pTemplate cost upward so that it is costlier than p since ** pTemplate is a proper subset of p */ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n", pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1)); pTemplate->rRun = p->rRun; pTemplate->nOut = p->nOut + 1; } } } /* ** Search the list of WhereLoops in *ppPrev looking for one that can be ** replaced by pTemplate. ** ** Return NULL if pTemplate does not belong on the WhereLoop list. ** In other words if pTemplate ought to be dropped from further consideration. ** ** If pX is a WhereLoop that pTemplate can replace, then return the ** link that points to pX. ** ** If pTemplate cannot replace any existing element of the list but needs ** to be added to the list as a new entry, then return a pointer to the ** tail of the list. */ static WhereLoop **whereLoopFindLesser( WhereLoop **ppPrev, const WhereLoop *pTemplate ){ WhereLoop *p; for (p=(*ppPrev); p; ppPrev=&p->pNextLoop, p=*ppPrev) { if (p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx) { /* If either the iTab or iSortIdx values for two WhereLoop are different ** then those WhereLoops need to be considered separately. Neither is ** a candidate to replace the other. */ continue; } /* In the current implementation, the rSetup value is either zero ** or the cost of building an automatic index (NlogN) and the NlogN ** is the same for compatible WhereLoops. */ assert( p->rSetup==0 || pTemplate->rSetup==0 || p->rSetup==pTemplate->rSetup ); /* whereLoopAddBtree() always generates and inserts the automatic index ** case first. Hence compatible candidate WhereLoops never have a larger ** rSetup. Call this SETUP-INVARIANT */ assert( p->rSetup>=pTemplate->rSetup ); /* Any loop using an appliation-defined index (or PRIMARY KEY or ** UNIQUE constraint) with one or more == constraints is better ** than an automatic index. Unless it is a skip-scan. */ if ((p->wsFlags & WHERE_AUTO_INDEX)!=0 && (pTemplate->nSkip)==0 && (pTemplate->wsFlags & WHERE_INDEXED)!=0 && (pTemplate->wsFlags & WHERE_COLUMN_EQ)!=0 && (p->prereq & pTemplate->prereq)==pTemplate->prereq ) { break; } /* If existing WhereLoop p is better than pTemplate, pTemplate can be ** discarded. WhereLoop p is better if: ** (1) p has no more dependencies than pTemplate, and ** (2) p has an equal or lower cost than pTemplate */ if ((p->prereq & pTemplate->prereq)==p->prereq /* (1) */ && p->rSetup<=pTemplate->rSetup /* (2a) */ && p->rRun<=pTemplate->rRun /* (2b) */ && p->nOut<=pTemplate->nOut /* (2c) */ ) { return 0; /* Discard pTemplate */ } /* If pTemplate is always better than p, then cause p to be overwritten ** with pTemplate. pTemplate is better than p if: ** (1) pTemplate has no more dependences than p, and ** (2) pTemplate has an equal or lower cost than p. */ if ((p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */ && p->rRun>=pTemplate->rRun /* (2a) */ && p->nOut>=pTemplate->nOut /* (2b) */ ) { assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */ break; /* Cause p to be overwritten by pTemplate */ } } return ppPrev; } /* ** Insert or replace a WhereLoop entry using the template supplied. ** ** An existing WhereLoop entry might be overwritten if the new template ** is better and has fewer dependencies. Or the template will be ignored ** and no insert will occur if an existing WhereLoop is faster and has ** fewer dependencies than the template. Otherwise a new WhereLoop is ** added based on the template. ** ** If pBuilder->pOrSet is not NULL then we care about only the ** prerequisites and rRun and nOut costs of the N best loops. That ** information is gathered in the pBuilder->pOrSet object. This special ** processing mode is used only for OR clause processing. ** ** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we ** still might overwrite similar loops with the new template if the ** new template is better. Loops may be overwritten if the following ** conditions are met: ** ** (1) They have the same iTab. ** (2) They have the same iSortIdx. ** (3) The template has same or fewer dependencies than the current loop ** (4) The template has the same or lower cost than the current loop */ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ WhereLoop **ppPrev, *p; WhereInfo *pWInfo = pBuilder->pWInfo; sqlite3 *db = pWInfo->pParse->db; int rc; /* Stop the search once we hit the query planner search limit */ if (pBuilder->iPlanLimit==0) { WHERETRACE(0xffffffff,("=== query planner search limit reached ===\n")); if (pBuilder->pOrSet) pBuilder->pOrSet->n = 0; return SQLITE_DONE; } pBuilder->iPlanLimit--; /* If pBuilder->pOrSet is defined, then only keep track of the costs ** and prereqs. */ if (pBuilder->pOrSet!=0) { if (pTemplate->nLTerm) { #if WHERETRACE_ENABLED u16 n = pBuilder->pOrSet->n; int x = #endif whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun, pTemplate->nOut); #if WHERETRACE_ENABLED /* 0x8 */ if (sqlite3WhereTrace & 0x8) { sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n); whereLoopPrint(pTemplate, pBuilder->pWC); } #endif } return SQLITE_OK; } /* Look for an existing WhereLoop to replace with pTemplate */ whereLoopAdjustCost(pWInfo->pLoops, pTemplate); ppPrev = whereLoopFindLesser(&pWInfo->pLoops, pTemplate); if (ppPrev==0) { /* There already exists a WhereLoop on the list that is better ** than pTemplate, so just ignore pTemplate */ #if WHERETRACE_ENABLED /* 0x8 */ if (sqlite3WhereTrace & 0x8) { sqlite3DebugPrintf(" skip: "); whereLoopPrint(pTemplate, pBuilder->pWC); } #endif return SQLITE_OK; } else { p = *ppPrev; } /* If we reach this point it means that either p[] should be overwritten ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new ** WhereLoop and insert it. */ #if WHERETRACE_ENABLED /* 0x8 */ if (sqlite3WhereTrace & 0x8) { if (p!=0) { sqlite3DebugPrintf("replace: "); whereLoopPrint(p, pBuilder->pWC); sqlite3DebugPrintf(" with: "); } else { sqlite3DebugPrintf(" add: "); } whereLoopPrint(pTemplate, pBuilder->pWC); } #endif if (p==0) { /* Allocate a new WhereLoop to add to the end of the list */ *ppPrev = p = sqlite3DbMallocRawNN(db, sizeof(WhereLoop)); if (p==0) return SQLITE_NOMEM_BKPT; whereLoopInit(p); p->pNextLoop = 0; } else { /* We will be overwriting WhereLoop p[]. But before we do, first ** go through the rest of the list and delete any other entries besides ** p[] that are also supplated by pTemplate */ WhereLoop **ppTail = &p->pNextLoop; WhereLoop *pToDel; while (*ppTail) { ppTail = whereLoopFindLesser(ppTail, pTemplate); if (ppTail==0) break; pToDel = *ppTail; if (pToDel==0) break; *ppTail = pToDel->pNextLoop; #if WHERETRACE_ENABLED /* 0x8 */ if (sqlite3WhereTrace & 0x8) { sqlite3DebugPrintf(" delete: "); whereLoopPrint(pToDel, pBuilder->pWC); } #endif whereLoopDelete(db, pToDel); } } rc = whereLoopXfer(db, p, pTemplate); if ((p->wsFlags & WHERE_VIRTUALTABLE)==0) { Index *pIndex = p->u.btree.pIndex; if (pIndex && pIndex->idxType==SQLITE_IDXTYPE_IPK) { p->u.btree.pIndex = 0; } } return rc; } /* ** Adjust the WhereLoop.nOut value downward to account for terms of the ** WHERE clause that reference the loop but which are not used by an ** index. * ** For every WHERE clause term that is not used by the index ** and which has a truth probability assigned by one of the likelihood(), ** likely(), or unlikely() SQL functions, reduce the estimated number ** of output rows by the probability specified. ** ** TUNING: For every WHERE clause term that is not used by the index ** and which does not have an assigned truth probability, heuristics ** described below are used to try to estimate the truth probability. ** TODO --> Perhaps this is something that could be improved by better ** table statistics. ** ** Heuristic 1: Estimate the truth probability as 93.75%. The 93.75% ** value corresponds to -1 in LogEst notation, so this means decrement ** the WhereLoop.nOut field for every such WHERE clause term. ** ** Heuristic 2: If there exists one or more WHERE clause terms of the ** form "x==EXPR" and EXPR is not a constant 0 or 1, then make sure the ** final output row estimate is no greater than 1/4 of the total number ** of rows in the table. In other words, assume that x==EXPR will filter ** out at least 3 out of 4 rows. If EXPR is -1 or 0 or 1, then maybe the ** "x" column is boolean or else -1 or 0 or 1 is a common default value ** on the "x" column and so in that case only cap the output row estimate ** at 1/2 instead of 1/4. */ static void whereLoopOutputAdjust( WhereClause *pWC, /* The WHERE clause */ WhereLoop *pLoop, /* The loop to adjust downward */ LogEst nRow /* Number of rows in the entire table */ ){ WhereTerm *pTerm, *pX; Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf); int i, j, k; LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */ assert((pLoop->wsFlags & WHERE_AUTO_INDEX)==0 ); for (i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++) { assert( pTerm!=0 ); if ((pTerm->wtFlags & TERM_VIRTUAL)!=0) break; if ((pTerm->prereqAll & pLoop->maskSelf)==0) continue; if ((pTerm->prereqAll & notAllowed)!=0) continue; for (j=pLoop->nLTerm-1; j>=0; j--) { pX = pLoop->aLTerm[j]; if (pX==0) continue; if (pX==pTerm) break; if (pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm) break; } if (j<0) { if (pTerm->truthProb<=0) { /* If a truth probability is specified using the likelihood() hints, ** then use the probability provided by the application. */ pLoop->nOut += pTerm->truthProb; } else { /* In the absence of explicit truth probabilities, use heuristics to ** guess a reasonable truth probability. */ pLoop->nOut--; if (pTerm->eOperator&(WO_EQ|WO_IS)) { Expr *pRight = pTerm->pExpr->pRight; testcase( pTerm->pExpr->op==TK_IS ); if (sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1) { k = 10; } else { k = 20; } if (iReducenOut > nRow-iReduce) pLoop->nOut = nRow - iReduce; } /* ** Term pTerm is a vector range comparison operation. The first comparison ** in the vector can be optimized using column nEq of the index. This ** function returns the total number of vector elements that can be used ** as part of the range comparison. ** ** For example, if the query is: ** ** WHERE a = ? AND (b, c, d) > (?, ?, ?) ** ** and the index: ** ** CREATE INDEX ... ON (a, b, c, d, e) ** ** then this function would be invoked with nEq=1. The value returned in ** this case is 3. */ static int whereRangeVectorLen( Parse *pParse, /* Parsing context */ int iCur, /* Cursor open on pIdx */ Index *pIdx, /* The index to be used for a inequality constraint */ int nEq, /* Number of prior equality constraints on same index */ WhereTerm *pTerm /* The vector inequality constraint */ ){ int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft); int i; nCmp = MIN(nCmp, (pIdx->nColumn - nEq)); for (i=1; ipExpr->pLeft->x.pList->a[i].pExpr; Expr *pRhs = pTerm->pExpr->pRight; if (pRhs->flags & EP_xIsSelect) { pRhs = pRhs->x.pSelect->pEList->a[i].pExpr; } else { pRhs = pRhs->x.pList->a[i].pExpr; } /* Check that the LHS of the comparison is a column reference to ** the right column of the right source table. And that the sort ** order of the index column is the same as the sort order of the ** leftmost index column. */ if (pLhs->op!=TK_COLUMN || pLhs->iTable!=iCur || pLhs->iColumn!=pIdx->aiColumn[i+nEq] || pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq] ) { break; } testcase( pLhs->iColumn==XN_ROWID ); aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs)); idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn); if (aff!=idxaff) break; pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); if (pColl==0) break; if (sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq])) break; } return i; } /* ** Adjust the cost C by the costMult facter T. This only occurs if ** compiled with -DSQLITE_ENABLE_COSTMULT */ #ifdef SQLITE_ENABLE_COSTMULT # define ApplyCostMultiplier(C,T) C += T #else # define ApplyCostMultiplier(C,T) #endif /* ** We have so far matched pBuilder->pNew->u.btree.nEq terms of the ** index pIndex. Try to match one more. ** ** When this function is called, pBuilder->pNew->nOut contains the ** number of rows expected to be visited by filtering using the nEq ** terms only. If it is modified, this value is restored before this ** function returns. ** ** If pProbe->idxType==SQLITE_IDXTYPE_IPK, that means pIndex is ** a fake index used for the INTEGER PRIMARY KEY. */ static int whereLoopAddBtreeIndex( WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ struct SrcList_item *pSrc, /* FROM clause term being analyzed */ Index *pProbe, /* An index on pSrc */ LogEst nInMul /* log(Number of iterations due to IN) */ ){ WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection malloc context */ WhereLoop *pNew; /* Template WhereLoop under construction */ WhereTerm *pTerm; /* A WhereTerm under consideration */ int opMask; /* Valid operators for constraints */ WhereScan scan; /* Iterator for WHERE terms */ Bitmask saved_prereq; /* Original value of pNew->prereq */ u16 saved_nLTerm; /* Original value of pNew->nLTerm */ u16 saved_nEq; /* Original value of pNew->u.btree.nEq */ u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */ u16 saved_nTop; /* Original value of pNew->u.btree.nTop */ u16 saved_nSkip; /* Original value of pNew->nSkip */ u32 saved_wsFlags; /* Original value of pNew->wsFlags */ LogEst saved_nOut; /* Original value of pNew->nOut */ int rc = SQLITE_OK; /* Return code */ LogEst rSize; /* Number of rows in the table */ LogEst rLogSize; /* Logarithm of table size */ WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */ pNew = pBuilder->pNew; if (db->mallocFailed) return SQLITE_NOMEM_BKPT; WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d\n", pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq)); assert((pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); assert((pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); if (pNew->wsFlags & WHERE_BTM_LIMIT) { opMask = WO_LT|WO_LE; } else { assert( pNew->u.btree.nBtm==0 ); opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; } if (pProbe->bUnordered) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); assert( pNew->u.btree.nEqnColumn ); saved_nEq = pNew->u.btree.nEq; saved_nBtm = pNew->u.btree.nBtm; saved_nTop = pNew->u.btree.nTop; saved_nSkip = pNew->nSkip; saved_nLTerm = pNew->nLTerm; saved_wsFlags = pNew->wsFlags; saved_prereq = pNew->prereq; saved_nOut = pNew->nOut; pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, saved_nEq, opMask, pProbe); pNew->rSetup = 0; rSize = pProbe->aiRowLogEst[0]; rLogSize = estLog(rSize); for (; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)) { u16 eOp = pTerm->eOperator; /* Shorthand for pTerm->eOperator */ LogEst rCostIdx; LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */ int nIn = 0; #ifdef SQLITE_ENABLE_STAT4 int nRecValid = pBuilder->nRecValid; #endif if ((eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0) && indexColumnNotNull(pProbe, saved_nEq) ) { continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */ } if (pTerm->prereqRight & pNew->maskSelf) continue; /* Do not allow the upper bound of a LIKE optimization range constraint ** to mix with a lower range bound from some other source */ if (pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT) continue; /* Do not allow constraints from the WHERE clause to be used by the ** right table of a LEFT JOIN. Only constraints in the ON clause are ** allowed */ if ((pSrc->fg.jointype & JT_LEFT)!=0 && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) { continue; } if (IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1) { pBuilder->bldFlags |= SQLITE_BLDF_UNIQUE; } else { pBuilder->bldFlags |= SQLITE_BLDF_INDEXED; } pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; pNew->u.btree.nBtm = saved_nBtm; pNew->u.btree.nTop = saved_nTop; pNew->nLTerm = saved_nLTerm; if (whereLoopResize(db, pNew, pNew->nLTerm+1)) break; /* OOM */ pNew->aLTerm[pNew->nLTerm++] = pTerm; pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf; assert( nInMul==0 || (pNew->wsFlags & WHERE_COLUMN_NULL)!=0 || (pNew->wsFlags & WHERE_COLUMN_IN)!=0 || (pNew->wsFlags & WHERE_SKIPSCAN)!=0 ); if (eOp & WO_IN) { Expr *pExpr = pTerm->pExpr; if (ExprHasProperty(pExpr, EP_xIsSelect)) { /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */ int i; nIn = 46; assert( 46==sqlite3LogEst(25)); /* The expression may actually be of the form (x, y) IN (SELECT...). ** In this case there is a separate term for each of (x) and (y). ** However, the nIn multiplier should only be applied once, not once ** for each such term. The following loop checks that pTerm is the ** first such term in use, and sets nIn back to 0 if it is not. */ for (i=0; inLTerm-1; i++) { if (pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr) nIn = 0; } } else if (ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr)) { /* "x IN (value, value, ...)" */ nIn = sqlite3LogEst(pExpr->x.pList->nExpr); assert( nIn>0 ); /* RHS always has 2 or more terms... The parser ** changes "x IN (?)" into "x=?". */ } if (pProbe->hasStat1) { LogEst M, logK, safetyMargin; /* Let: ** N = the total number of rows in the table ** K = the number of entries on the RHS of the IN operator ** M = the number of rows in the table that match terms to the ** to the left in the same index. If the IN operator is on ** the left-most index column, M==N. ** ** Given the definitions above, it is better to omit the IN operator ** from the index lookup and instead do a scan of the M elements, ** testing each scanned row against the IN operator separately, if: ** ** M*log(K) < K*log(N) ** ** Our estimates for M, K, and N might be inaccurate, so we build in ** a safety margin of 2 (LogEst: 10) that favors using the IN operator ** with the index, as using an index has better worst-case behavior. ** If we do not have real sqlite_stat1 data, always prefer to use ** the index. */ M = pProbe->aiRowLogEst[saved_nEq]; logK = estLog(nIn); safetyMargin = 10; /* TUNING: extra weight for indexed IN */ if (M + logK + safetyMargin < nIn + rLogSize) { WHERETRACE(0x40, ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n", saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); continue; } else { WHERETRACE(0x40, ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n", saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize)); } } pNew->wsFlags |= WHERE_COLUMN_IN; } else if (eOp & (WO_EQ|WO_IS)) { int iCol = pProbe->aiColumn[saved_nEq]; pNew->wsFlags |= WHERE_COLUMN_EQ; assert( saved_nEq==pNew->u.btree.nEq ); if (iCol==XN_ROWID || (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1) ) { if (iCol==XN_ROWID || pProbe->uniqNotNull || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ) ) { pNew->wsFlags |= WHERE_ONEROW; } else { pNew->wsFlags |= WHERE_UNQ_WANTED; } } } else if (eOp & WO_ISNULL) { pNew->wsFlags |= WHERE_COLUMN_NULL; } else if (eOp & (WO_GT|WO_GE)) { testcase( eOp & WO_GT ); testcase( eOp & WO_GE ); pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; pNew->u.btree.nBtm = whereRangeVectorLen( pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm ); pBtm = pTerm; pTop = 0; if (pTerm->wtFlags & TERM_LIKEOPT) { /* Range contraints that come from the LIKE optimization are ** always used in pairs. */ pTop = &pTerm[1]; assert((pTop-(pTerm->pWC->a))pWC->nTerm ); assert( pTop->wtFlags & TERM_LIKEOPT ); assert( pTop->eOperator==WO_LT ); if (whereLoopResize(db, pNew, pNew->nLTerm+1)) break; /* OOM */ pNew->aLTerm[pNew->nLTerm++] = pTop; pNew->wsFlags |= WHERE_TOP_LIMIT; pNew->u.btree.nTop = 1; } } else { assert( eOp & (WO_LT|WO_LE)); testcase( eOp & WO_LT ); testcase( eOp & WO_LE ); pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT; pNew->u.btree.nTop = whereRangeVectorLen( pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm ); pTop = pTerm; pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ? pNew->aLTerm[pNew->nLTerm-2] : 0; } /* At this point pNew->nOut is set to the number of rows expected to ** be visited by the index scan before considering term pTerm, or the ** values of nIn and nInMul. In other words, assuming that all ** "x IN(...)" terms are replaced with "x = ?". This block updates ** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */ assert( pNew->nOut==saved_nOut ); if (pNew->wsFlags & WHERE_COLUMN_RANGE) { /* Adjust nOut using stat4 data. Or, if there is no stat4 ** data, using some other estimate. */ whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew); } else { int nEq = ++pNew->u.btree.nEq; assert( eOp & (WO_ISNULL|WO_EQ|WO_IN|WO_IS)); assert( pNew->nOut==saved_nOut ); if (pTerm->truthProb<=0 && pProbe->aiColumn[saved_nEq]>=0) { assert((eOp & WO_IN) || nIn==0 ); testcase( eOp & WO_IN ); pNew->nOut += pTerm->truthProb; pNew->nOut -= nIn; } else { #ifdef SQLITE_ENABLE_STAT4 tRowcnt nOut = 0; if (nInMul==0 && pProbe->nSample && pNew->u.btree.nEq<=pProbe->nSampleCol && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect)) && OptimizationEnabled(db, SQLITE_Stat4) ) { Expr *pExpr = pTerm->pExpr; if ((eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0) { testcase( eOp & WO_EQ ); testcase( eOp & WO_IS ); testcase( eOp & WO_ISNULL ); rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut); } else { rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut); } if (rc==SQLITE_NOTFOUND) rc = SQLITE_OK; if (rc!=SQLITE_OK) break; /* Jump out of the pTerm loop */ if (nOut) { pNew->nOut = sqlite3LogEst(nOut); if (pNew->nOut>saved_nOut) pNew->nOut = saved_nOut; pNew->nOut -= nIn; } } if (nOut==0) #endif { pNew->nOut += (pProbe->aiRowLogEst[nEq] - pProbe->aiRowLogEst[nEq-1]); if (eOp & WO_ISNULL) { /* TUNING: If there is no likelihood() value, assume that a ** "col IS NULL" expression matches twice as many rows ** as (col=?). */ pNew->nOut += 10; } } } } /* Set rCostIdx to the cost of visiting selected rows in index. Add ** it to pNew->rRun, which is currently set to the cost of the index ** seek only. Then, if this is a non-covering index, add the cost of ** visiting the rows in the main table. */ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx); if ((pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0) { pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); } ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult); nOutUnadjusted = pNew->nOut; pNew->rRun += nInMul + nIn; pNew->nOut += nInMul + nIn; whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize); rc = whereLoopInsert(pBuilder, pNew); if (pNew->wsFlags & WHERE_COLUMN_RANGE) { pNew->nOut = saved_nOut; } else { pNew->nOut = nOutUnadjusted; } if ((pNew->wsFlags & WHERE_TOP_LIMIT)==0 && pNew->u.btree.nEqnColumn ) { whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); } pNew->nOut = saved_nOut; #ifdef SQLITE_ENABLE_STAT4 pBuilder->nRecValid = nRecValid; #endif } pNew->prereq = saved_prereq; pNew->u.btree.nEq = saved_nEq; pNew->u.btree.nBtm = saved_nBtm; pNew->u.btree.nTop = saved_nTop; pNew->nSkip = saved_nSkip; pNew->wsFlags = saved_wsFlags; pNew->nOut = saved_nOut; pNew->nLTerm = saved_nLTerm; /* Consider using a skip-scan if there are no WHERE clause constraints ** available for the left-most terms of the index, and if the average ** number of repeats in the left-most terms is at least 18. ** ** The magic number 18 is selected on the basis that scanning 17 rows ** is almost always quicker than an index seek (even though if the index ** contains fewer than 2^17 rows we assume otherwise in other parts of ** the code). And, even if it is not, it should not be too much slower. ** On the other hand, the extra seeks could end up being significantly ** more expensive. */ assert( 42==sqlite3LogEst(18)); if (saved_nEq==saved_nSkip && saved_nEq+1nKeyCol && pProbe->noSkipScan==0 && OptimizationEnabled(db, SQLITE_SkipScan) && pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */ && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK ) { LogEst nIter; pNew->u.btree.nEq++; pNew->nSkip++; pNew->aLTerm[pNew->nLTerm++] = 0; pNew->wsFlags |= WHERE_SKIPSCAN; nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1]; pNew->nOut -= nIter; /* TUNING: Because uncertainties in the estimates for skip-scan queries, ** add a 1.375 fudge factor to make skip-scan slightly less likely. */ nIter += 5; whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul); pNew->nOut = saved_nOut; pNew->u.btree.nEq = saved_nEq; pNew->nSkip = saved_nSkip; pNew->wsFlags = saved_wsFlags; } WHERETRACE(0x800, ("END %s.addBtreeIdx(%s), nEq=%d, rc=%d\n", pProbe->pTable->zName, pProbe->zName, saved_nEq, rc)); return rc; } /* ** Return True if it is possible that pIndex might be useful in ** implementing the ORDER BY clause in pBuilder. ** ** Return False if pBuilder does not contain an ORDER BY clause or ** if there is no way for pIndex to be useful in implementing that ** ORDER BY clause. */ static int indexMightHelpWithOrderBy( WhereLoopBuilder *pBuilder, Index *pIndex, int iCursor ){ ExprList *pOB; ExprList *aColExpr; int ii, jj; if (pIndex->bUnordered) return 0; if ((pOB = pBuilder->pWInfo->pOrderBy)==0) return 0; for (ii=0; iinExpr; ii++) { Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr); if (pExpr->op==TK_COLUMN && pExpr->iTable==iCursor) { if (pExpr->iColumn<0) return 1; for (jj=0; jjnKeyCol; jj++) { if (pExpr->iColumn==pIndex->aiColumn[jj]) return 1; } } else if ((aColExpr = pIndex->aColExpr)!=0) { for (jj=0; jjnKeyCol; jj++) { if (pIndex->aiColumn[jj]!=XN_EXPR) continue; if (sqlite3ExprCompareSkip(pExpr,aColExpr->a[jj].pExpr,iCursor)==0) { return 1; } } } } return 0; } /* Check to see if a partial index with pPartIndexWhere can be used ** in the current query. Return true if it can be and false if not. */ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ int i; WhereTerm *pTerm; Parse *pParse = pWC->pWInfo->pParse; while (pWhere->op==TK_AND) { if (!whereUsablePartialIndex(iTab,pWC,pWhere->pLeft)) return 0; pWhere = pWhere->pRight; } if (pParse->db->flags & SQLITE_EnableQPSG) pParse = 0; for (i=0, pTerm=pWC->a; inTerm; i++, pTerm++) { Expr *pExpr = pTerm->pExpr; if ((!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) ) { return 1; } } return 0; } /* ** Add all WhereLoop objects for a single table of the join where the table ** is identified by pBuilder->pNew->iTab. That table is guaranteed to be ** a b-tree table, not a virtual table. ** ** The costs (WhereLoop.rRun) of the b-tree loops added by this function ** are calculated as follows: ** ** For a full scan, assuming the table (or index) contains nRow rows: ** ** cost = nRow * 3.0 // full-table scan ** cost = nRow * K // scan of covering index ** cost = nRow * (K+3.0) // scan of non-covering index ** ** where K is a value between 1.1 and 3.0 set based on the relative ** estimated average size of the index and table records. ** ** For an index scan, where nVisit is the number of index rows visited ** by the scan, and nSeek is the number of seek operations required on ** the index b-tree: ** ** cost = nSeek * (log(nRow) + K * nVisit) // covering index ** cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index ** ** Normally, nSeek is 1. nSeek values greater than 1 come about if the ** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when ** implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans. ** ** The estimated values (nRow, nVisit, nSeek) often contain a large amount ** of uncertainty. For this reason, scoring is designed to pick plans that ** "do the least harm" if the estimates are inaccurate. For example, a ** log(nRow) factor is omitted from a non-covering index scan in order to ** bias the scoring in favor of using an index, since the worst-case ** performance of using an index is far better than the worst-case performance ** of a full table scan. */ static int whereLoopAddBtree( WhereLoopBuilder *pBuilder, /* WHERE clause information */ Bitmask mPrereq /* Extra prerequesites for using this table */ ){ WhereInfo *pWInfo; /* WHERE analysis context */ Index *pProbe; /* An index we are evaluating */ Index sPk; /* A fake index object for the primary key */ LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */ i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */ SrcList *pTabList; /* The FROM clause */ struct SrcList_item *pSrc; /* The FROM clause btree term to add */ WhereLoop *pNew; /* Template WhereLoop object */ int rc = SQLITE_OK; /* Return code */ int iSortIdx = 1; /* Index number */ int b; /* A boolean value */ LogEst rSize; /* number of rows in the table */ LogEst rLogSize; /* Logarithm of the number of rows in the table */ WhereClause *pWC; /* The parsed WHERE clause */ Table *pTab; /* Table being queried */ pNew = pBuilder->pNew; pWInfo = pBuilder->pWInfo; pTabList = pWInfo->pTabList; pSrc = pTabList->a + pNew->iTab; pTab = pSrc->pTab; pWC = pBuilder->pWC; assert( !IsVirtual(pSrc->pTab)); if (pSrc->pIBIndex) { /* An INDEXED BY clause specifies a particular index to use */ pProbe = pSrc->pIBIndex; } else if (!HasRowid(pTab)) { pProbe = pTab->pIndex; } else { /* There is no INDEXED BY clause. Create a fake Index object in local ** variable sPk to represent the rowid primary key index. Make this ** fake index the first in a chain of Index objects with all of the real ** indices to follow */ Index *pFirst; /* First of real indices on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nKeyCol = 1; sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowLogEst = aiRowEstPk; sPk.onError = OE_Replace; sPk.pTable = pTab; sPk.szIdxRow = pTab->szTabRow; sPk.idxType = SQLITE_IDXTYPE_IPK; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; pFirst = pSrc->pTab->pIndex; if (pSrc->fg.notIndexed==0) { /* The real indices of the table are only considered if the ** NOT INDEXED qualifier is omitted from the FROM clause */ sPk.pNext = pFirst; } pProbe = &sPk; } rSize = pTab->nRowLogEst; rLogSize = estLog(rSize); #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* Automatic indexes */ if (!pBuilder->pOrSet /* Not part of an OR optimization */ && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */ && !pSrc->fg.isCorrelated /* Not a correlated subquery */ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ ) { /* Generate auto-index WhereLoops */ WhereTerm *pTerm; WhereTerm *pWCEnd = pWC->a + pWC->nTerm; for (pTerm=pWC->a; rc==SQLITE_OK && pTermprereqRight & pNew->maskSelf) continue; if (termCanDriveIndex(pTerm, pSrc, 0)) { pNew->u.btree.nEq = 1; pNew->nSkip = 0; pNew->u.btree.pIndex = 0; pNew->nLTerm = 1; pNew->aLTerm[0] = pTerm; /* TUNING: One-time cost for computing the automatic index is ** estimated to be X*N*log2(N) where N is the number of rows in ** the table being indexed and where X is 7 (LogEst=28) for normal ** tables or 0.5 (LogEst=-10) for views and subqueries. The value ** of X is smaller for views and subqueries so that the query planner ** will be more aggressive about generating automatic indexes for ** those objects, since there is no opportunity to add schema ** indexes on subqueries and views. */ pNew->rSetup = rLogSize + rSize; if (pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0) { pNew->rSetup += 28; } else { pNew->rSetup -= 10; } ApplyCostMultiplier(pNew->rSetup, pTab->costMult); if (pNew->rSetup<0) pNew->rSetup = 0; /* TUNING: Each index lookup yields 20 rows in the table. This ** is more than the usual guess of 10 rows, since we have no way ** of knowing how selective the index will ultimately be. It would ** not be unreasonable to make this value much larger. */ pNew->nOut = 43; assert( 43==sqlite3LogEst(20)); pNew->rRun = sqlite3LogEstAdd(rLogSize,pNew->nOut); pNew->wsFlags = WHERE_AUTO_INDEX; pNew->prereq = mPrereq | pTerm->prereqRight; rc = whereLoopInsert(pBuilder, pNew); } } } #endif /* SQLITE_OMIT_AUTOMATIC_INDEX */ /* Loop over all indices. If there was an INDEXED BY clause, then only ** consider index pProbe. */ for (; rc==SQLITE_OK && pProbe; pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++ ) { if (pProbe->pPartIdxWhere!=0 && !whereUsablePartialIndex(pSrc->iCursor, pWC, pProbe->pPartIdxWhere)) { testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */ continue; /* Partial index inappropriate for this query */ } if (pProbe->bNoQuery) continue; rSize = pProbe->aiRowLogEst[0]; pNew->u.btree.nEq = 0; pNew->u.btree.nBtm = 0; pNew->u.btree.nTop = 0; pNew->nSkip = 0; pNew->nLTerm = 0; pNew->iSortIdx = 0; pNew->rSetup = 0; pNew->prereq = mPrereq; pNew->nOut = rSize; pNew->u.btree.pIndex = pProbe; b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ assert((pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); if (pProbe->idxType==SQLITE_IDXTYPE_IPK) { /* Integer primary key index */ pNew->wsFlags = WHERE_IPK; /* Full table scan */ pNew->iSortIdx = b ? iSortIdx : 0; /* TUNING: Cost of full table scan is (N*3.0). */ pNew->rRun = rSize + 16; ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); rc = whereLoopInsert(pBuilder, pNew); pNew->nOut = rSize; if (rc) break; } else { Bitmask m; if (pProbe->isCovering) { pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; m = 0; } else { m = pSrc->colUsed & pProbe->colNotIdxed; pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED; } /* Full scan via index */ if (b || !HasRowid(pTab) || pProbe->pPartIdxWhere!=0 || (m==0 && pProbe->bUnordered==0 && (pProbe->szIdxRowszTabRow) && (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 && sqlite3GlobalConfig.bUseCis && OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan) ) ) { pNew->iSortIdx = b ? iSortIdx : 0; /* The cost of visiting the index rows is N*K, where K is ** between 1.1 and 3.0, depending on the relative sizes of the ** index and table rows. */ pNew->rRun = rSize + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow; if (m!=0) { /* If this is a non-covering index scan, add in the cost of ** doing table lookups. The cost will be 3x the number of ** lookups. Take into account WHERE clause terms that can be ** satisfied using just the index, and that do not require a ** table lookup. */ LogEst nLookup = rSize + 16; /* Base cost: N*3 */ int ii; int iCur = pSrc->iCursor; WhereClause *pWC2 = &pWInfo->sWC; for (ii=0; iinTerm; ii++) { WhereTerm *pTerm = &pWC2->a[ii]; if (!sqlite3ExprCoveredByIndex(pTerm->pExpr, iCur, pProbe)) { break; } /* pTerm can be evaluated using just the index. So reduce ** the expected number of table lookups accordingly */ if (pTerm->truthProb<=0) { nLookup += pTerm->truthProb; } else { nLookup--; if (pTerm->eOperator & (WO_EQ|WO_IS)) nLookup -= 19; } } pNew->rRun = sqlite3LogEstAdd(pNew->rRun, nLookup); } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); rc = whereLoopInsert(pBuilder, pNew); pNew->nOut = rSize; if (rc) break; } } pBuilder->bldFlags = 0; rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); if (pBuilder->bldFlags==SQLITE_BLDF_INDEXED) { /* If a non-unique index is used, or if a prefix of the key for ** unique index is used (making the index functionally non-unique) ** then the sqlite_stat1 data becomes important for scoring the ** plan */ pTab->tabFlags |= TF_StatsUsed; } #ifdef SQLITE_ENABLE_STAT4 sqlite3Stat4ProbeFree(pBuilder->pRec); pBuilder->nRecValid = 0; pBuilder->pRec = 0; #endif } return rc; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Argument pIdxInfo is already populated with all constraints that may ** be used by the virtual table identified by pBuilder->pNew->iTab. This ** function marks a subset of those constraints usable, invokes the ** xBestIndex method and adds the returned plan to pBuilder. ** ** A constraint is marked usable if: ** ** * Argument mUsable indicates that its prerequisites are available, and ** ** * It is not one of the operators specified in the mExclude mask passed ** as the fourth argument (which in practice is either WO_IN or 0). ** ** Argument mPrereq is a mask of tables that must be scanned before the ** virtual table in question. These are added to the plans prerequisites ** before it is added to pBuilder. ** ** Output parameter *pbIn is set to true if the plan added to pBuilder ** uses one or more WO_IN terms, or false otherwise. */ static int whereLoopAddVirtualOne( WhereLoopBuilder *pBuilder, Bitmask mPrereq, /* Mask of tables that must be used. */ Bitmask mUsable, /* Mask of usable tables */ u16 mExclude, /* Exclude terms using these operators */ sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */ u16 mNoOmit, /* Do not omit these constraints */ int *pbIn /* OUT: True if plan uses an IN(...) op */ ){ WhereClause *pWC = pBuilder->pWC; struct sqlite3_index_constraint *pIdxCons; struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage; int i; int mxTerm; int rc = SQLITE_OK; WhereLoop *pNew = pBuilder->pNew; Parse *pParse = pBuilder->pWInfo->pParse; struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab]; int nConstraint = pIdxInfo->nConstraint; assert((mUsable & mPrereq)==mPrereq ); *pbIn = 0; pNew->prereq = mPrereq; /* Set the usable flag on the subset of constraints identified by ** arguments mUsable and mExclude. */ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; for (i=0; ia[pIdxCons->iTermOffset]; pIdxCons->usable = 0; if ((pTerm->prereqRight & mUsable)==pTerm->prereqRight && (pTerm->eOperator & mExclude)==0 ) { pIdxCons->usable = 1; } } /* Initialize the output fields of the sqlite3_index_info structure */ memset(pUsage, 0, sizeof(pUsage[0])*nConstraint); assert( pIdxInfo->needToFreeIdxStr==0 ); pIdxInfo->idxStr = 0; pIdxInfo->idxNum = 0; pIdxInfo->orderByConsumed = 0; pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; /* Invoke the virtual table xBestIndex() method */ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); if (rc) { if (rc==SQLITE_CONSTRAINT) { /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means ** that the particular combination of parameters provided is unusable. ** Make no entries in the loop table. */ WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n")); return SQLITE_OK; } return rc; } mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); for (i=0; iaLTerm[i] = 0; pNew->u.vtab.omitMask = 0; pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; for (i=0; i=0) { WhereTerm *pTerm; int j = pIdxCons->iTermOffset; if (iTerm>=nConstraint || j<0 || j>=pWC->nTerm || pNew->aLTerm[iTerm]!=0 || pIdxCons->usable==0 ) { sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); testcase( pIdxInfo->needToFreeIdxStr ); return SQLITE_ERROR; } testcase( iTerm==nConstraint-1 ); testcase( j==0 ); testcase( j==pWC->nTerm-1 ); pTerm = &pWC->a[j]; pNew->prereq |= pTerm->prereqRight; assert( iTermnLSlot ); pNew->aLTerm[iTerm] = pTerm; if (iTerm>mxTerm) mxTerm = iTerm; testcase( iTerm==15 ); testcase( iTerm==16 ); if (iTerm<16 && pUsage[i].omit) pNew->u.vtab.omitMask |= 1<eOperator & WO_IN)!=0) { /* A virtual table that is constrained by an IN clause may not ** consume the ORDER BY clause because (1) the order of IN terms ** is not necessarily related to the order of output terms and ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert((mExclude & WO_IN)==0 ); } } } pNew->u.vtab.omitMask &= ~mNoOmit; pNew->nLTerm = mxTerm+1; for (i=0; i<=mxTerm; i++) { if (pNew->aLTerm[i]==0) { /* The non-zero argvIdx values must be contiguous. Raise an ** error if they are not */ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName); testcase( pIdxInfo->needToFreeIdxStr ); return SQLITE_ERROR; } } assert( pNew->nLTerm<=pNew->nLSlot ); pNew->u.vtab.idxNum = pIdxInfo->idxNum; pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; pIdxInfo->needToFreeIdxStr = 0; pNew->u.vtab.idxStr = pIdxInfo->idxStr; pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? pIdxInfo->nOrderBy : 0); pNew->rSetup = 0; pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated ** that the scan will visit at most one row. Clear it otherwise. */ if (pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE) { pNew->wsFlags |= WHERE_ONEROW; } else { pNew->wsFlags &= ~WHERE_ONEROW; } rc = whereLoopInsert(pBuilder, pNew); if (pNew->u.vtab.needFree) { sqlite3_free(pNew->u.vtab.idxStr); pNew->u.vtab.needFree = 0; } WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n", *pbIn, (sqlite3_uint64)mPrereq, (sqlite3_uint64)(pNew->prereq & ~mPrereq))); return rc; } /* ** If this function is invoked from within an xBestIndex() callback, it ** returns a pointer to a buffer containing the name of the collation ** sequence associated with element iCons of the sqlite3_index_info.aConstraint ** array. Or, if iCons is out of range or there is no active xBestIndex ** call, return NULL. */ const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; const char *zRet = 0; if (iCons>=0 && iConsnConstraint) { CollSeq *pC = 0; int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset; Expr *pX = pHidden->pWC->a[iTerm].pExpr; if (pX->pLeft) { pC = sqlite3BinaryCompareCollSeq(pHidden->pParse, pX->pLeft, pX->pRight); } zRet = (pC ? pC->zName : sqlite3StrBINARY); } return zRet; } /* ** Add all WhereLoop objects for a table of the join identified by ** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. ** ** If there are no LEFT or CROSS JOIN joins in the query, both mPrereq and ** mUnusable are set to 0. Otherwise, mPrereq is a mask of all FROM clause ** entries that occur before the virtual table in the FROM clause and are ** separated from it by at least one LEFT or CROSS JOIN. Similarly, the ** mUnusable mask contains all FROM clause entries that occur after the ** virtual table and are separated from it by at least one LEFT or ** CROSS JOIN. ** ** For example, if the query were: ** ** ... FROM t1, t2 LEFT JOIN t3, t4, vt CROSS JOIN t5, t6; ** ** then mPrereq corresponds to (t1, t2) and mUnusable to (t5, t6). ** ** All the tables in mPrereq must be scanned before the current virtual ** table. So any terms for which all prerequisites are satisfied by ** mPrereq may be specified as "usable" in all calls to xBestIndex. ** Conversely, all tables in mUnusable must be scanned after the current ** virtual table, so any terms for which the prerequisites overlap with ** mUnusable should always be configured as "not-usable" for xBestIndex. */ static int whereLoopAddVirtual( WhereLoopBuilder *pBuilder, /* WHERE clause information */ Bitmask mPrereq, /* Tables that must be scanned before this one */ Bitmask mUnusable /* Tables that must be scanned after this one */ ){ int rc = SQLITE_OK; /* Return code */ WhereInfo *pWInfo; /* WHERE analysis context */ Parse *pParse; /* The parsing context */ WhereClause *pWC; /* The WHERE clause */ struct SrcList_item *pSrc; /* The FROM clause term to search */ sqlite3_index_info *p; /* Object to pass to xBestIndex() */ int nConstraint; /* Number of constraints in p */ int bIn; /* True if plan uses IN(...) operator */ WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ u16 mNoOmit; assert((mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; pParse = pWInfo->pParse; pWC = pBuilder->pWC; pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; assert( IsVirtual(pSrc->pTab)); p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, &mNoOmit); if (p==0) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; pNew->nLTerm = 0; pNew->u.vtab.needFree = 0; nConstraint = p->nConstraint; if (whereLoopResize(pParse->db, pNew, nConstraint)) { sqlite3DbFree(pParse->db, p); return SQLITE_NOMEM_BKPT; } /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); /* If the call to xBestIndex() with all terms enabled produced a plan ** that does not require any source tables (IOW: a plan with mBest==0) ** and does not use an IN(...) operator, then there is no point in making ** any further calls to xBestIndex() since they will all return the same ** result (if the xBestIndex() implementation is sane). */ if (rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn)) { int seenZero = 0; /* True if a plan with no prereqs seen */ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */ Bitmask mPrev = 0; Bitmask mBestNoIn = 0; /* If the plan produced by the earlier call uses an IN(...) term, call ** xBestIndex again, this time with IN(...) terms disabled. */ if (bIn) { WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn); assert( bIn==0 ); mBestNoIn = pNew->prereq & ~mPrereq; if (mBestNoIn==0) { seenZero = 1; seenZeroNoIN = 1; } } /* Call xBestIndex once for each distinct value of (prereqRight & ~mPrereq) ** in the set of terms that apply to the current virtual table. */ while (rc==SQLITE_OK) { int i; Bitmask mNext = ALLBITS; assert( mNext>0 ); for (i=0; ia[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq ); if (mThis>mPrev && mThisprereq==mPrereq) { seenZero = 1; if (bIn==0) seenZeroNoIN = 1; } } /* If the calls to xBestIndex() in the above loop did not find a plan ** that requires no source tables at all (i.e. one guaranteed to be ** usable), make a call here with all source tables disabled */ if (rc==SQLITE_OK && seenZero==0) { WHERETRACE(0x40, (" VirtualOne: all disabled\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn); if (bIn==0) seenZeroNoIN = 1; } /* If the calls to xBestIndex() have so far failed to find a plan ** that requires no source tables at all and does not use an IN(...) ** operator, make a final call to obtain one here. */ if (rc==SQLITE_OK && seenZeroNoIN==0) { WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn); } } if (p->needToFreeIdxStr) sqlite3_free(p->idxStr); sqlite3DbFreeNN(pParse->db, p); WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Add WhereLoop entries to handle OR terms. This works for either ** btrees or virtual tables. */ static int whereLoopAddOr( WhereLoopBuilder *pBuilder, Bitmask mPrereq, Bitmask mUnusable ){ WhereInfo *pWInfo = pBuilder->pWInfo; WhereClause *pWC; WhereLoop *pNew; WhereTerm *pTerm, *pWCEnd; int rc = SQLITE_OK; int iCur; WhereClause tempWC; WhereLoopBuilder sSubBuild; WhereOrSet sSum, sCur; struct SrcList_item *pItem; pWC = pBuilder->pWC; pWCEnd = pWC->a + pWC->nTerm; pNew = pBuilder->pNew; memset(&sSum, 0, sizeof(sSum)); pItem = pWInfo->pTabList->a + pNew->iTab; iCur = pItem->iCursor; for (pTerm=pWC->a; pTermeOperator & WO_OR)!=0 && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0 ) { WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; WhereTerm *pOrTerm; int once = 1; int i, j; sSubBuild = *pBuilder; sSubBuild.pOrderBy = 0; sSubBuild.pOrSet = &sCur; WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); for (pOrTerm=pOrWC->a; pOrTermeOperator & WO_AND)!=0) { sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc; } else if (pOrTerm->leftCursor==iCur) { tempWC.pWInfo = pWC->pWInfo; tempWC.pOuter = pWC; tempWC.op = TK_AND; tempWC.nTerm = 1; tempWC.a = pOrTerm; sSubBuild.pWC = &tempWC; } else { continue; } sCur.n = 0; #ifdef WHERETRACE_ENABLED WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n", (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); if (sqlite3WhereTrace & 0x400) { sqlite3WhereClausePrint(sSubBuild.pWC); } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE if (IsVirtual(pItem->pTab)) { rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable); } else #endif { rc = whereLoopAddBtree(&sSubBuild, mPrereq); } if (rc==SQLITE_OK) { rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable); } assert( rc==SQLITE_OK || sCur.n==0 ); if (sCur.n==0) { sSum.n = 0; break; } else if (once) { whereOrMove(&sSum, &sCur); once = 0; } else { WhereOrSet sPrev; whereOrMove(&sPrev, &sSum); sSum.n = 0; for (i=0; inLTerm = 1; pNew->aLTerm[0] = pTerm; pNew->wsFlags = WHERE_MULTI_OR; pNew->rSetup = 0; pNew->iSortIdx = 0; memset(&pNew->u, 0, sizeof(pNew->u)); for (i=0; rc==SQLITE_OK && irRun = sSum.a[i].rRun + 1; pNew->nOut = sSum.a[i].nOut; pNew->prereq = sSum.a[i].prereq; rc = whereLoopInsert(pBuilder, pNew); } WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm)); } } return rc; } /* ** Add all WhereLoop objects for all tables */ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ WhereInfo *pWInfo = pBuilder->pWInfo; Bitmask mPrereq = 0; Bitmask mPrior = 0; int iTab; SrcList *pTabList = pWInfo->pTabList; struct SrcList_item *pItem; struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel]; sqlite3 *db = pWInfo->pParse->db; int rc = SQLITE_OK; WhereLoop *pNew; u8 priorJointype = 0; /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; whereLoopInit(pNew); pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; for (iTab=0, pItem=pTabList->a; pItemiTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); if (((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0) { /* This condition is true when pItem is the FROM clause term on the ** right-hand-side of a LEFT or CROSS JOIN. */ mPrereq = mPrior; } priorJointype = pItem->fg.jointype; #ifndef SQLITE_OMIT_VIRTUALTABLE if (IsVirtual(pItem->pTab)) { struct SrcList_item *p; for (p=&pItem[1]; pfg.jointype & (JT_LEFT|JT_CROSS))) { mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); } } rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable); } else #endif /* SQLITE_OMIT_VIRTUALTABLE */ { rc = whereLoopAddBtree(pBuilder, mPrereq); } if (rc==SQLITE_OK && pBuilder->pWC->hasOr) { rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable); } mPrior |= pNew->maskSelf; if (rc || db->mallocFailed) { if (rc==SQLITE_DONE) { /* We hit the query planner search limit set by iPlanLimit */ sqlite3_log(SQLITE_WARNING, "abbreviated query algorithm search"); rc = SQLITE_OK; } else { break; } } } whereLoopClear(db, pNew); return rc; } /* ** Examine a WherePath (with the addition of the extra WhereLoop of the 6th ** parameters) to see if it outputs rows in the requested ORDER BY ** (or GROUP BY) without requiring a separate sort operation. Return N: ** ** N>0: N terms of the ORDER BY clause are satisfied ** N==0: No terms of the ORDER BY clause are satisfied ** N<0: Unknown yet how many terms of ORDER BY might be satisfied. ** ** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as ** strict. With GROUP BY and DISTINCT the only requirement is that ** equivalent rows appear immediately adjacent to one another. GROUP BY ** and DISTINCT do not require rows to appear in any particular order as long ** as equivalent rows are grouped together. Thus for GROUP BY and DISTINCT ** the pOrderBy terms can be matched in any order. With ORDER BY, the ** pOrderBy terms must be matched in strict left-to-right order. */ static i8 wherePathSatisfiesOrderBy( WhereInfo *pWInfo, /* The WHERE clause */ ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */ WherePath *pPath, /* The WherePath to check */ u16 wctrlFlags, /* WHERE_GROUPBY or _DISTINCTBY or _ORDERBY_LIMIT */ u16 nLoop, /* Number of entries in pPath->aLoop[] */ WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */ Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */ ){ u8 revSet; /* True if rev is known */ u8 rev; /* Composite sort order */ u8 revIdx; /* Index sort order */ u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */ u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */ u8 isMatch; /* iColumn matches a term of the ORDER BY clause */ u16 eqOpMask; /* Allowed equality operators */ u16 nKeyCol; /* Number of key columns in pIndex */ u16 nColumn; /* Total number of ordered columns in the index */ u16 nOrderBy; /* Number terms in the ORDER BY clause */ int iLoop; /* Index of WhereLoop in pPath being processed */ int i, j; /* Loop counters */ int iCur; /* Cursor number for current WhereLoop */ int iColumn; /* A column number within table iCur */ WhereLoop *pLoop = 0; /* Current WhereLoop being processed. */ WhereTerm *pTerm; /* A single term of the WHERE clause */ Expr *pOBExpr; /* An expression from the ORDER BY clause */ CollSeq *pColl; /* COLLATE function from an ORDER BY clause term */ Index *pIndex; /* The index associated with pLoop */ sqlite3 *db = pWInfo->pParse->db; /* Database connection */ Bitmask obSat = 0; /* Mask of ORDER BY terms satisfied so far */ Bitmask obDone; /* Mask of all ORDER BY terms */ Bitmask orderDistinctMask; /* Mask of all well-ordered loops */ Bitmask ready; /* Mask of inner loops */ /* ** We say the WhereLoop is "one-row" if it generates no more than one ** row of output. A WhereLoop is one-row if all of the following are true: ** (a) All index columns match with WHERE_COLUMN_EQ. ** (b) The index is unique ** Any WhereLoop with an WHERE_COLUMN_EQ constraint on the rowid is one-row. ** Every one-row WhereLoop will have the WHERE_ONEROW bit set in wsFlags. ** ** We say the WhereLoop is "order-distinct" if the set of columns from ** that WhereLoop that are in the ORDER BY clause are different for every ** row of the WhereLoop. Every one-row WhereLoop is automatically ** order-distinct. A WhereLoop that has no columns in the ORDER BY clause ** is not order-distinct. To be order-distinct is not quite the same as being ** UNIQUE since a UNIQUE column or index can have multiple rows that ** are NULL and NULL values are equivalent for the purpose of order-distinct. ** To be order-distinct, the columns must be UNIQUE and NOT NULL. ** ** The rowid for a table is always UNIQUE and NOT NULL so whenever the ** rowid appears in the ORDER BY clause, the corresponding WhereLoop is ** automatically order-distinct. */ assert( pOrderBy!=0 ); if (nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin)) return 0; nOrderBy = pOrderBy->nExpr; testcase( nOrderBy==BMS-1 ); if (nOrderBy>BMS-1) return 0; /* Cannot optimize overly large ORDER BYs */ isOrderDistinct = 1; obDone = MASKBIT(nOrderBy)-1; orderDistinctMask = 0; ready = 0; eqOpMask = WO_EQ | WO_IS | WO_ISNULL; if (wctrlFlags & WHERE_ORDERBY_LIMIT) eqOpMask |= WO_IN; for (iLoop=0; isOrderDistinct && obSat0) ready |= pLoop->maskSelf; if (iLoopaLoop[iLoop]; if (wctrlFlags & WHERE_ORDERBY_LIMIT) continue; } else { pLoop = pLast; } if (pLoop->wsFlags & WHERE_VIRTUALTABLE) { if (pLoop->u.vtab.isOrdered) obSat = obDone; break; } else if (wctrlFlags & WHERE_DISTINCTBY) { pLoop->u.btree.nDistinctCol = 0; } iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; /* Mark off any ORDER BY term X that is a column in the table of ** the current loop for which there is term in the WHERE ** clause of the form X IS NULL or X=? that reference only outer ** loops. */ for (i=0; ia[i].pExpr); if (pOBExpr->op!=TK_COLUMN) continue; if (pOBExpr->iTable!=iCur) continue; pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn, ~ready, eqOpMask, 0); if (pTerm==0) continue; if (pTerm->eOperator==WO_IN) { /* IN terms are only valid for sorting in the ORDER BY LIMIT ** optimization, and then only if they are actually used ** by the query plan */ assert( wctrlFlags & WHERE_ORDERBY_LIMIT ); for (j=0; jnLTerm && pTerm!=pLoop->aLTerm[j]; j++) {} if (j>=pLoop->nLTerm) continue; } if ((pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0) { if (sqlite3ExprCollSeqMatch(pWInfo->pParse, pOrderBy->a[i].pExpr, pTerm->pExpr)==0) { continue; } testcase( pTerm->pExpr->op==TK_IS ); } obSat |= MASKBIT(i); } if ((pLoop->wsFlags & WHERE_ONEROW)==0) { if (pLoop->wsFlags & WHERE_IPK) { pIndex = 0; nKeyCol = 0; nColumn = 1; } else if ((pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered) { return 0; } else { nKeyCol = pIndex->nKeyCol; nColumn = pIndex->nColumn; assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable)); assert( pIndex->aiColumn[nColumn-1]==XN_ROWID || !HasRowid(pIndex->pTable)); isOrderDistinct = IsUniqueIndex(pIndex) && (pLoop->wsFlags & WHERE_SKIPSCAN)==0; } /* Loop through all columns of the index and deal with the ones ** that are not constrained by == or IN. */ rev = revSet = 0; distinctColumns = 0; for (j=0; j=pLoop->u.btree.nEq || (pLoop->aLTerm[j]==0)==(jnSkip) ); if (ju.btree.nEq && j>=pLoop->nSkip) { u16 eOp = pLoop->aLTerm[j]->eOperator; /* Skip over == and IS and ISNULL terms. (Also skip IN terms when ** doing WHERE_ORDERBY_LIMIT processing). ** ** If the current term is a column of an ((?,?) IN (SELECT...)) ** expression for which the SELECT returns more than one column, ** check that it is the only column used by this loop. Otherwise, ** if it is one of two or more, none of the columns can be ** considered to match an ORDER BY term. */ if ((eOp & eqOpMask)!=0) { if (eOp & WO_ISNULL) { testcase( isOrderDistinct ); isOrderDistinct = 0; } continue; } else if (ALWAYS(eOp & WO_IN)) { /* ALWAYS() justification: eOp is an equality operator due to the ** ju.btree.nEq constraint above. Any equality other ** than WO_IN is captured by the previous "if". So this one ** always has to be WO_IN. */ Expr *pX = pLoop->aLTerm[j]->pExpr; for (i=j+1; iu.btree.nEq; i++) { if (pLoop->aLTerm[i]->pExpr==pX) { assert((pLoop->aLTerm[i]->eOperator & WO_IN)); bOnce = 0; break; } } } } /* Get the column number in the table (iColumn) and sort order ** (revIdx) for the j-th column of the index. */ if (pIndex) { iColumn = pIndex->aiColumn[j]; revIdx = pIndex->aSortOrder[j]; if (iColumn==pIndex->pTable->iPKey) iColumn = XN_ROWID; } else { iColumn = XN_ROWID; revIdx = 0; } /* An unconstrained column that might be NULL means that this ** WhereLoop is not well-ordered */ if (isOrderDistinct && iColumn>=0 && j>=pLoop->u.btree.nEq && pIndex->pTable->aCol[iColumn].notNull==0 ) { isOrderDistinct = 0; } /* Find the ORDER BY term that corresponds to the j-th column ** of the index and mark that ORDER BY term off */ isMatch = 0; for (i=0; bOnce && ia[i].pExpr); testcase( wctrlFlags & WHERE_GROUPBY ); testcase( wctrlFlags & WHERE_DISTINCTBY ); if ((wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0) bOnce = 0; if (iColumn>=XN_ROWID) { if (pOBExpr->op!=TK_COLUMN) continue; if (pOBExpr->iTable!=iCur) continue; if (pOBExpr->iColumn!=iColumn) continue; } else { Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr; if (sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur)) { continue; } } if (iColumn!=XN_ROWID) { pColl = sqlite3ExprNNCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr); if (sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0) continue; } if (wctrlFlags & WHERE_DISTINCTBY) { pLoop->u.btree.nDistinctCol = j+1; } isMatch = 1; break; } if (isMatch && (wctrlFlags & WHERE_GROUPBY)==0) { /* Make sure the sort order is compatible in an ORDER BY clause. ** Sort order is irrelevant for a GROUP BY clause. */ if (revSet) { if ((rev ^ revIdx)!=pOrderBy->a[i].sortOrder) isMatch = 0; } else { rev = revIdx ^ pOrderBy->a[i].sortOrder; if (rev) *pRevMask |= MASKBIT(iLoop); revSet = 1; } } if (isMatch) { if (iColumn==XN_ROWID) { testcase( distinctColumns==0 ); distinctColumns = 1; } obSat |= MASKBIT(i); if ((wctrlFlags & WHERE_ORDERBY_MIN) && j==pLoop->u.btree.nEq) { pLoop->wsFlags |= WHERE_MIN_ORDERED; } } else { /* No match found */ if (j==0 || jmaskSelf; for (i=0; ia[i].pExpr; mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); if (mTerm==0 && !sqlite3ExprIsConstant(p)) continue; if ((mTerm&~orderDistinctMask)==0) { obSat |= MASKBIT(i); } } } } /* End the loop over all WhereLoops from outer-most down to inner-most */ if (obSat==obDone) return (i8)nOrderBy; if (!isOrderDistinct) { for (i=nOrderBy-1; i>0; i--) { Bitmask m = MASKBIT(i) - 1; if ((obSat&m)==m) return i; } return 0; } return -1; } /* ** If the WHERE_GROUPBY flag is set in the mask passed to sqlite3WhereBegin(), ** the planner assumes that the specified pOrderBy list is actually a GROUP ** BY clause - and so any order that groups rows as required satisfies the ** request. ** ** Normally, in this case it is not possible for the caller to determine ** whether or not the rows are really being delivered in sorted order, or ** just in some other order that provides the required grouping. However, ** if the WHERE_SORTBYGROUP flag is also passed to sqlite3WhereBegin(), then ** this function may be called on the returned WhereInfo object. It returns ** true if the rows really will be sorted in the specified order, or false ** otherwise. ** ** For example, assuming: ** ** CREATE INDEX i1 ON t1(x, Y); ** ** then ** ** SELECT * FROM t1 GROUP BY x,y ORDER BY x,y; -- IsSorted()==1 ** SELECT * FROM t1 GROUP BY y,x ORDER BY y,x; -- IsSorted()==0 */ int sqlite3WhereIsSorted(WhereInfo *pWInfo){ assert( pWInfo->wctrlFlags & WHERE_GROUPBY ); assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP ); return pWInfo->sorted; } #ifdef WHERETRACE_ENABLED /* For debugging use only: */ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){ static char zName[65]; int i; for (i=0; iaLoop[i]->cId; } if (pLast) zName[i++] = pLast->cId; zName[i] = 0; return zName; } #endif /* ** Return the cost of sorting nRow rows, assuming that the keys have ** nOrderby columns and that the first nSorted columns are already in ** order. */ static LogEst whereSortingCost( WhereInfo *pWInfo, LogEst nRow, int nOrderBy, int nSorted ){ /* TUNING: Estimated cost of a full external sort, where N is ** the number of rows to sort is: ** ** cost = (3.0 * N * log(N)). ** ** Or, if the order-by clause has X terms but only the last Y ** terms are out of order, then block-sorting will reduce the ** sorting cost to: ** ** cost = (3.0 * N * log(N)) * (Y/X) ** ** The (Y/X) term is implemented using stack variable rScale ** below. */ LogEst rScale, rSortCost; assert( nOrderBy>0 && 66==sqlite3LogEst(100)); rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66; rSortCost = nRow + rScale + 16; /* Multiple by log(M) where M is the number of output rows. ** Use the LIMIT for M if it is smaller */ if ((pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimitiLimit; } rSortCost += estLog(nRow); return rSortCost; } /* ** Given the list of WhereLoop objects at pWInfo->pLoops, this routine ** attempts to find the lowest cost path that visits each WhereLoop ** once. This path is then loaded into the pWInfo->a[].pWLoop fields. ** ** Assume that the total number of output rows that will need to be sorted ** will be nRowEst (in the 10*log2 representation). Or, ignore sorting ** costs if nRowEst==0. ** ** Return SQLITE_OK on success or SQLITE_NOMEM of a memory allocation ** error occurs. */ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ int mxChoice; /* Maximum number of simultaneous paths tracked */ int nLoop; /* Number of terms in the join */ Parse *pParse; /* Parsing context */ sqlite3 *db; /* The database connection */ int iLoop; /* Loop counter over the terms of the join */ int ii, jj; /* Loop counters */ int mxI = 0; /* Index of next entry to replace */ int nOrderBy; /* Number of ORDER BY clause terms */ LogEst mxCost = 0; /* Maximum cost of a set of paths */ LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */ int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */ WherePath *aFrom; /* All nFrom paths at the previous level */ WherePath *aTo; /* The nTo best paths at the current level */ WherePath *pFrom; /* An element of aFrom[] that we are working on */ WherePath *pTo; /* An element of aTo[] that we are working on */ WhereLoop *pWLoop; /* One of the WhereLoop objects */ WhereLoop **pX; /* Used to divy up the pSpace memory */ LogEst *aSortCost = 0; /* Sorting and partial sorting costs */ char *pSpace; /* Temporary memory used by this routine */ int nSpace; /* Bytes of space allocated at pSpace */ pParse = pWInfo->pParse; db = pParse->db; nLoop = pWInfo->nLevel; /* TUNING: For simple queries, only the best path is tracked. ** For 2-way joins, the 5 best paths are followed. ** For joins of 3 or more tables, track the 10 best paths */ mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10); assert( nLoop<=pWInfo->pTabList->nSrc ); WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst)); /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this ** case the purpose of this call is to estimate the number of rows returned ** by the overall query. Once this estimate has been obtained, the caller ** will invoke this function a second time, passing the estimate as the ** nRowEst parameter. */ if (pWInfo->pOrderBy==0 || nRowEst==0) { nOrderBy = 0; } else { nOrderBy = pWInfo->pOrderBy->nExpr; } /* Allocate and initialize space for aTo, aFrom and aSortCost[] */ nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2; nSpace += sizeof(LogEst) * nOrderBy; pSpace = sqlite3DbMallocRawNN(db, nSpace); if (pSpace==0) return SQLITE_NOMEM_BKPT; aTo = (WherePath*)pSpace; aFrom = aTo+mxChoice; memset(aFrom, 0, sizeof(aFrom[0])); pX = (WhereLoop**)(aFrom+mxChoice); for (ii=mxChoice*2, pFrom=aTo; ii>0; ii--, pFrom++, pX += nLoop) { pFrom->aLoop = pX; } if (nOrderBy) { /* If there is an ORDER BY clause and it is not being ignored, set up ** space for the aSortCost[] array. Each element of the aSortCost array ** is either zero - meaning it has not yet been initialized - or the ** cost of sorting nRowEst rows of data where the first X terms of ** the ORDER BY clause are already in order, where X is the array ** index. */ aSortCost = (LogEst*)pX; memset(aSortCost, 0, sizeof(LogEst) * nOrderBy); } assert( aSortCost==0 || &pSpace[nSpace]==(char*)&aSortCost[nOrderBy] ); assert( aSortCost!=0 || &pSpace[nSpace]==(char*)pX ); /* Seed the search with a single WherePath containing zero WhereLoops. ** ** TUNING: Do not let the number of iterations go above 28. If the cost ** of computing an automatic index is not paid back within the first 28 ** rows, then do not use the automatic index. */ aFrom[0].nRow = MIN(pParse->nQueryLoop, 48); assert( 48==sqlite3LogEst(28)); nFrom = 1; assert( aFrom[0].isOrdered==0 ); if (nOrderBy) { /* If nLoop is zero, then there are no FROM terms in the query. Since ** in this case the query may return a maximum of one row, the results ** are already in the requested order. Set isOrdered to nOrderBy to ** indicate this. Or, if nLoop is greater than zero, set isOrdered to ** -1, indicating that the result set may or may not be ordered, ** depending on the loops added to the current plan. */ aFrom[0].isOrdered = nLoop>0 ? -1 : nOrderBy; } /* Compute successively longer WherePaths using the previous generation ** of WherePaths as the basis for the next. Keep track of the mxChoice ** best paths at each generation */ for (iLoop=0; iLooppLoops; pWLoop; pWLoop=pWLoop->pNextLoop) { LogEst nOut; /* Rows visited by (pFrom+pWLoop) */ LogEst rCost; /* Cost of path (pFrom+pWLoop) */ LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */ Bitmask maskNew; /* Mask of src visited by (..) */ Bitmask revMask = 0; /* Mask of rev-order loops for (..) */ if ((pWLoop->prereq & ~pFrom->maskLoop)!=0) continue; if ((pWLoop->maskSelf & pFrom->maskLoop)!=0) continue; if ((pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<3) { /* Do not use an automatic index if the this loop is expected ** to run less than 1.25 times. It is tempting to also exclude ** automatic index usage on an outer loop, but sometimes an automatic ** index is useful in the outer loop of a correlated subquery. */ assert( 10==sqlite3LogEst(2)); continue; } /* At this point, pWLoop is a candidate to be the next loop. ** Compute its cost */ rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow); rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); nOut = pFrom->nRow + pWLoop->nOut; maskNew = pFrom->maskLoop | pWLoop->maskSelf; if (isOrdered<0) { isOrdered = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags, iLoop, pWLoop, &revMask); } else { revMask = pFrom->revLoop; } if (isOrdered>=0 && isOrderedisOrdered^isOrdered)&0x80)==0" is equivalent ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range ** of legal values for isOrdered, -1..64. */ for (jj=0, pTo=aTo; jjmaskLoop==maskNew && ((pTo->isOrdered^isOrdered)&0x80)==0 ) { testcase( jj==nTo-1 ); break; } } if (jj>=nTo) { /* None of the existing best-so-far paths match the candidate. */ if (nTo>=mxChoice && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted)) ) { /* The current candidate is no better than any of the mxChoice ** paths currently in the best-so-far buffer. So discard ** this candidate as not viable. */ #ifdef WHERETRACE_ENABLED /* 0x4 */ if (sqlite3WhereTrace&0x4) { sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, isOrdered>=0 ? isOrdered+'0' : '?'); } #endif continue; } /* If we reach this points it means that the new candidate path ** needs to be added to the set of best-so-far paths. */ if (nTo=0 ? isOrdered+'0' : '?'); } #endif } else { /* Control reaches here if best-so-far path pTo=aTo[jj] covers the ** same set of loops and has the same isOrdered setting as the ** candidate path. Check to see if the candidate should replace ** pTo or if the candidate should be skipped. ** ** The conditional is an expanded vector comparison equivalent to: ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted) */ if (pTo->rCostrCost==rCost && (pTo->nRownRow==nOut && pTo->rUnsorted<=rUnsorted) ) ) ) { #ifdef WHERETRACE_ENABLED /* 0x4 */ if (sqlite3WhereTrace&0x4) { sqlite3DebugPrintf( "Skip %s cost=%-3d,%3d,%3d order=%c", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif /* Discard the candidate path from further consideration */ testcase( pTo->rCost==rCost ); continue; } testcase( pTo->rCost==rCost+1 ); /* Control reaches here if the candidate path is better than the ** pTo path. Replace pTo with the candidate. */ #ifdef WHERETRACE_ENABLED /* 0x4 */ if (sqlite3WhereTrace&0x4) { sqlite3DebugPrintf( "Update %s cost=%-3d,%3d,%3d order=%c", wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted, isOrdered>=0 ? isOrdered+'0' : '?'); sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n", wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow, pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?'); } #endif } /* pWLoop is a winner. Add it to the set of best so far */ pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf; pTo->revLoop = revMask; pTo->nRow = nOut; pTo->rCost = rCost; pTo->rUnsorted = rUnsorted; pTo->isOrdered = isOrdered; memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop); pTo->aLoop[iLoop] = pWLoop; if (nTo>=mxChoice) { mxI = 0; mxCost = aTo[0].rCost; mxUnsorted = aTo[0].nRow; for (jj=1, pTo=&aTo[1]; jjrCost>mxCost || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted) ) { mxCost = pTo->rCost; mxUnsorted = pTo->rUnsorted; mxI = jj; } } } } } #ifdef WHERETRACE_ENABLED /* >=2 */ if (sqlite3WhereTrace & 0x02) { sqlite3DebugPrintf("---- after round %d ----\n", iLoop); for (ii=0, pTo=aTo; iirCost, pTo->nRow, pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?'); if (pTo->isOrdered>0) { sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop); } else { sqlite3DebugPrintf("\n"); } } } #endif /* Swap the roles of aFrom and aTo for the next generation */ pFrom = aTo; aTo = aFrom; aFrom = pFrom; nFrom = nTo; } if (nFrom==0) { sqlite3ErrorMsg(pParse, "no query solution"); sqlite3DbFreeNN(db, pSpace); return SQLITE_ERROR; } /* Find the lowest cost path. pFrom will be left pointing to that path */ pFrom = aFrom; for (ii=1; iirCost>aFrom[ii].rCost) pFrom = &aFrom[ii]; } assert( pWInfo->nLevel==nLoop ); /* Load the lowest cost path into pWInfo */ for (iLoop=0; iLoopa + iLoop; pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop]; pLevel->iFrom = pWLoop->iTab; pLevel->iTabCur = pWInfo->pTabList->a[pLevel->iFrom].iCursor; } if ((pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 && (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0 && pWInfo->eDistinct==WHERE_DISTINCT_NOOP && nRowEst ) { Bitmask notUsed; int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pResultSet, pFrom, WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used); if (rc==pWInfo->pResultSet->nExpr) { pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } } pWInfo->bOrderedInnerLoop = 0; if (pWInfo->pOrderBy) { if (pWInfo->wctrlFlags & WHERE_DISTINCTBY) { if (pFrom->isOrdered==pWInfo->pOrderBy->nExpr) { pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } } else { pWInfo->nOBSat = pFrom->isOrdered; pWInfo->revMask = pFrom->revLoop; if (pWInfo->nOBSat<=0) { pWInfo->nOBSat = 0; if (nLoop>0) { u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags; if ((wsFlags & WHERE_ONEROW)==0 && (wsFlags&(WHERE_IPK|WHERE_COLUMN_IN))!=(WHERE_IPK|WHERE_COLUMN_IN) ) { Bitmask m = 0; int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom, WHERE_ORDERBY_LIMIT, nLoop-1, pFrom->aLoop[nLoop-1], &m); testcase( wsFlags & WHERE_IPK ); testcase( wsFlags & WHERE_COLUMN_IN ); if (rc==pWInfo->pOrderBy->nExpr) { pWInfo->bOrderedInnerLoop = 1; pWInfo->revMask = m; } } } } } if ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP) && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0 ) { Bitmask revMask = 0; int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &revMask ); assert( pWInfo->sorted==0 ); if (nOrder==pWInfo->pOrderBy->nExpr) { pWInfo->sorted = 1; pWInfo->revMask = revMask; } } } pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ sqlite3DbFreeNN(db, pSpace); return SQLITE_OK; } /* ** Most queries use only a single table (they are not joins) and have ** simple == constraints against indexed fields. This routine attempts ** to plan those simple cases using much less ceremony than the ** general-purpose query planner, and thereby yield faster sqlite3_prepare() ** times for the common case. ** ** Return non-zero on success, if this query can be handled by this ** no-frills query planner. Return zero if this query needs the ** general-purpose query planner. */ static int whereShortCut(WhereLoopBuilder *pBuilder){ WhereInfo *pWInfo; struct SrcList_item *pItem; WhereClause *pWC; WhereTerm *pTerm; WhereLoop *pLoop; int iCur; int j; Table *pTab; Index *pIdx; pWInfo = pBuilder->pWInfo; if (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; pTab = pItem->pTab; if (IsVirtual(pTab)) return 0; if (pItem->fg.isIndexedBy) return 0; iCur = pItem->iCursor; pWC = &pWInfo->sWC; pLoop = pBuilder->pNew; pLoop->wsFlags = 0; pLoop->nSkip = 0; pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); if (pTerm) { testcase( pTerm->eOperator & WO_IS ); pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW; pLoop->aLTerm[0] = pTerm; pLoop->nLTerm = 1; pLoop->u.btree.nEq = 1; /* TUNING: Cost of a rowid lookup is 10 */ pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */ } else { for (pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext) { int opMask; assert( pLoop->aLTermSpace==pLoop->aLTerm ); if (!IsUniqueIndex(pIdx) || pIdx->pPartIdxWhere!=0 || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace) ) continue; opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ; for (j=0; jnKeyCol; j++) { pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx); if (pTerm==0) break; testcase( pTerm->eOperator & WO_IS ); pLoop->aLTerm[j] = pTerm; } if (j!=pIdx->nKeyCol) continue; pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED; if (pIdx->isCovering || (pItem->colUsed & pIdx->colNotIdxed)==0) { pLoop->wsFlags |= WHERE_IDX_ONLY; } pLoop->nLTerm = j; pLoop->u.btree.nEq = j; pLoop->u.btree.pIndex = pIdx; /* TUNING: Cost of a unique index lookup is 15 */ pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */ break; } } if (pLoop->wsFlags) { pLoop->nOut = (LogEst)1; pWInfo->a[0].pWLoop = pLoop; assert( pWInfo->sMaskSet.n==1 && iCur==pWInfo->sMaskSet.ix[0] ); pLoop->maskSelf = 1; /* sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); */ pWInfo->a[0].iTabCur = iCur; pWInfo->nRowOut = 1; if (pWInfo->pOrderBy) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr; if (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) { pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } #ifdef SQLITE_DEBUG pLoop->cId = '0'; #endif return 1; } return 0; } /* ** Helper function for exprIsDeterministic(). */ static int exprNodeIsDeterministic(Walker *pWalker, Expr *pExpr){ if (pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_ConstFunc)==0) { pWalker->eCode = 0; return WRC_Abort; } return WRC_Continue; } /* ** Return true if the expression contains no non-deterministic SQL ** functions. Do not consider non-deterministic SQL functions that are ** part of sub-select statements. */ static int exprIsDeterministic(Expr *p){ Walker w; memset(&w, 0, sizeof(w)); w.eCode = 1; w.xExprCallback = exprNodeIsDeterministic; w.xSelectCallback = sqlite3SelectWalkFail; sqlite3WalkExpr(&w, p); return w.eCode; } /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains ** information needed to terminate the loop. Later, the calling routine ** should invoke sqlite3WhereEnd() with the return value of this function ** in order to complete the WHERE clause processing. ** ** If an error occurs, this routine returns NULL. ** ** The basic idea is to do a nested loop, one loop for each table in ** the FROM clause of a select. (INSERT and UPDATE statements are the ** same as a SELECT with only a single table in the FROM clause.) For ** example, if the SQL is this: ** ** SELECT * FROM t1, t2, t3 WHERE ...; ** ** Then the code generated is conceptually like the following: ** ** foreach row1 in t1 do \ Code generated ** foreach row2 in t2 do |-- by sqlite3WhereBegin() ** foreach row3 in t3 do / ** ... ** end \ Code generated ** end |-- by sqlite3WhereEnd() ** end / ** ** Note that the loops might not be nested in the order in which they ** appear in the FROM clause if a different order is better able to make ** use of indices. Note also that when the IN operator appears in ** the WHERE clause, it might result in additional nested loops for ** scanning through all values on the right-hand side of the IN. ** ** There are Btree cursors associated with each table. t1 uses cursor ** number pTabList->a[0].iCursor. t2 uses the cursor pTabList->a[1].iCursor. ** And so forth. This routine generates code to open those VDBE cursors ** and sqlite3WhereEnd() generates the code to close them. ** ** The code that sqlite3WhereBegin() generates leaves the cursors named ** in pTabList pointing at their appropriate entries. The [...] code ** can use OP_Column and OP_Rowid opcodes on these cursors to extract ** data from the various tables of the loop. ** ** If the WHERE clause is empty, the foreach loops must each scan their ** entire tables. Thus a three-way join is an O(N^3) operation. But if ** the tables have indices and there are terms in the WHERE clause that ** refer to those indices, a complete table scan can be avoided and the ** code will run much faster. Most of the work of this routine is checking ** to see if there are indices that can be used to speed up the loop. ** ** Terms of the WHERE clause are also used to limit which rows actually ** make it to the "..." in the middle of the loop. After each "foreach", ** terms of the WHERE clause that use only terms in that loop and outer ** loops are evaluated and if false a jump is made around all subsequent ** inner loops (or around the "..." if the test occurs within the inner- ** most loop) ** ** OUTER JOINS ** ** An outer join of tables t1 and t2 is conceptally coded as follows: ** ** foreach row1 in t1 do ** flag = 0 ** foreach row2 in t2 do ** start: ** ... ** flag = 1 ** end ** if flag==0 then ** move the row2 cursor to a null row ** goto start ** fi ** end ** ** ORDER BY CLAUSE PROCESSING ** ** pOrderBy is a pointer to the ORDER BY clause (or the GROUP BY clause ** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement ** if there is one. If there is no ORDER BY clause or if this routine ** is called from an UPDATE or DELETE statement, then pOrderBy is NULL. ** ** The iIdxCur parameter is the cursor number of an index. If ** WHERE_OR_SUBCLAUSE is set, iIdxCur is the cursor number of an index ** to use for OR clause processing. The WHERE clause should use this ** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is ** the first cursor in an array of cursors for all indices. iIdxCur should ** be used to compute the appropriate cursor depending on which index is ** used. */ WhereInfo *sqlite3WhereBegin( Parse *pParse, /* The parser context */ SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */ Expr *pWhere, /* The WHERE clause */ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */ ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */ int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number ** If WHERE_USE_LIMIT, then the limit amount */ ){ int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ int nTabList; /* Number of elements in pTabList */ WhereInfo *pWInfo; /* Will become the return value of this function */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */ Bitmask notReady; /* Cursors that are not yet positioned */ WhereLoopBuilder sWLB; /* The WhereLoop builder */ WhereMaskSet *pMaskSet; /* The expression mask set */ WhereLevel *pLevel; /* A single level in pWInfo->a[] */ WhereLoop *pLoop; /* Pointer to a single WhereLoop object */ int ii; /* Loop counter */ sqlite3 *db; /* Database connection */ int rc; /* Return code */ u8 bFordelete = 0; /* OPFLAG_FORDELETE or zero, as appropriate */ assert((wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || ( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 )); /* Only one of WHERE_OR_SUBCLAUSE or WHERE_USE_LIMIT */ assert((wctrlFlags & WHERE_OR_SUBCLAUSE)==0 || (wctrlFlags & WHERE_USE_LIMIT)==0 ); /* Variable initialization */ db = pParse->db; memset(&sWLB, 0, sizeof(sWLB)); /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); if (pOrderBy && pOrderBy->nExpr>=BMS) pOrderBy = 0; sWLB.pOrderBy = pOrderBy; /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */ if (OptimizationDisabled(db, SQLITE_DistinctOpt)) { wctrlFlags &= ~WHERE_WANT_DISTINCT; } /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask */ testcase( pTabList->nSrc==BMS ); if (pTabList->nSrc>BMS) { sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS); return 0; } /* This function normally generates a nested loop for all tables in ** pTabList. But if the WHERE_OR_SUBCLAUSE flag is set, then we should ** only generate code for the first table in pTabList and assume that ** any cursors associated with subsequent tables are uninitialized. */ nTabList = (wctrlFlags & WHERE_OR_SUBCLAUSE) ? 1 : pTabList->nSrc; /* Allocate and initialize the WhereInfo structure that will become the ** return value. A single allocation is used to store the WhereInfo ** struct, the contents of WhereInfo.a[], the WhereClause structure ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte ** field (type Bitmask) it must be aligned on an 8-byte boundary on ** some architectures. Hence the ROUND8() below. */ nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); if (db->mallocFailed) { sqlite3DbFree(db, pWInfo); pWInfo = 0; goto whereBeginError; } pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->pOrderBy = pOrderBy; pWInfo->pWhere = pWhere; pWInfo->pResultSet = pResultSet; pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; pWInfo->nLevel = nTabList; pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(pParse); pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; memset(&pWInfo->nOBSat, 0, offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat)); memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel)); assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */ pMaskSet = &pWInfo->sMaskSet; sWLB.pWInfo = pWInfo; sWLB.pWC = &pWInfo->sWC; sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo); assert( EIGHT_BYTE_ALIGNMENT(sWLB.pNew)); whereLoopInit(sWLB.pNew); #ifdef SQLITE_DEBUG sWLB.pNew->cId = '*'; #endif /* Split the WHERE clause into separate subexpressions where each ** subexpression is separated by an AND operator. */ initMaskSet(pMaskSet); sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo); sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND); /* Special case: No FROM clause */ if (nTabList==0) { if (pOrderBy) pWInfo->nOBSat = pOrderBy->nExpr; if (wctrlFlags & WHERE_WANT_DISTINCT) { pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); } else { /* Assign a bit from the bitmask to every term in the FROM clause. ** ** The N-th term of the FROM clause is assigned a bitmask of 1<nSrc tables in ** pTabList, not just the first nTabList tables. nTabList is normally ** equal to pTabList->nSrc but might be shortened to 1 if the ** WHERE_OR_SUBCLAUSE flag is set. */ ii = 0; do{ createMask(pMaskSet, pTabList->a[ii].iCursor); sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC); }while ((++ii)nSrc); #ifdef SQLITE_DEBUG { Bitmask mx = 0; for (ii=0; iinSrc; ii++) { Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor); assert( m>=mx ); mx = m; } } #endif } /* Analyze all of the subexpressions. */ sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC); if (db->mallocFailed) goto whereBeginError; /* Special case: WHERE terms that do not refer to any tables in the join ** (constant expressions). Evaluate each such term, and jump over all the ** generated code if the result is not true. ** ** Do not do this if the expression contains non-deterministic functions ** that are not within a sub-select. This is not strictly required, but ** preserves SQLite's legacy behaviour in the following two cases: ** ** FROM ... WHERE random()>0; -- eval random() once per row ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall */ for (ii=0; iinTerm; ii++) { WhereTerm *pT = &sWLB.pWC->a[ii]; if (pT->wtFlags & TERM_VIRTUAL) continue; if (pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr))) { sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL); pT->wtFlags |= TERM_CODED; } } if (wctrlFlags & WHERE_WANT_DISTINCT) { if (isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet)) { /* The DISTINCT marking is pointless. Ignore it. */ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } else if (pOrderBy==0) { /* Try to ORDER BY the result set to make distinct processing easier */ pWInfo->wctrlFlags |= WHERE_DISTINCTBY; pWInfo->pOrderBy = pResultSet; } } /* Construct the WhereLoop objects */ #if defined(WHERETRACE_ENABLED) if (sqlite3WhereTrace & 0xffff) { sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags); if (wctrlFlags & WHERE_USE_LIMIT) { sqlite3DebugPrintf(", limit: %d", iAuxArg); } sqlite3DebugPrintf(")\n"); if (sqlite3WhereTrace & 0x100) { Select sSelect; memset(&sSelect, 0, sizeof(sSelect)); sSelect.selFlags = SF_WhereBegin; sSelect.pSrc = pTabList; sSelect.pWhere = pWhere; sSelect.pOrderBy = pOrderBy; sSelect.pEList = pResultSet; sqlite3TreeViewSelect(0, &sSelect, 0); } } if (sqlite3WhereTrace & 0x100) { /* Display all terms of the WHERE clause */ sqlite3WhereClausePrint(sWLB.pWC); } #endif if (nTabList!=1 || whereShortCut(&sWLB)==0) { rc = whereLoopAddAll(&sWLB); if (rc) goto whereBeginError; #ifdef WHERETRACE_ENABLED if (sqlite3WhereTrace) { /* Display all of the WhereLoop objects */ WhereLoop *p; int i; static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; for (p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++) { p->cId = zLabel[i%(sizeof(zLabel)-1)]; whereLoopPrint(p, sWLB.pWC); } } #endif wherePathSolver(pWInfo, 0); if (db->mallocFailed) goto whereBeginError; if (pWInfo->pOrderBy) { wherePathSolver(pWInfo, pWInfo->nRowOut+1); if (db->mallocFailed) goto whereBeginError; } } if (pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0) { pWInfo->revMask = ALLBITS; } if (pParse->nErr || NEVER(db->mallocFailed)) { goto whereBeginError; } #ifdef WHERETRACE_ENABLED if (sqlite3WhereTrace) { sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); if (pWInfo->nOBSat>0) { sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); } switch (pWInfo->eDistinct) { case WHERE_DISTINCT_UNIQUE: { sqlite3DebugPrintf(" DISTINCT=unique"); break; } case WHERE_DISTINCT_ORDERED: { sqlite3DebugPrintf(" DISTINCT=ordered"); break; } case WHERE_DISTINCT_UNORDERED: { sqlite3DebugPrintf(" DISTINCT=unordered"); break; } } sqlite3DebugPrintf("\n"); for (ii=0; iinLevel; ii++) { whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC); } } #endif /* Attempt to omit tables from the join that do not affect the result. ** For a table to not affect the result, the following must be true: ** ** 1) The query must not be an aggregate. ** 2) The table must be the RHS of a LEFT JOIN. ** 3) Either the query must be DISTINCT, or else the ON or USING clause ** must contain a constraint that limits the scan of the table to ** at most a single row. ** 4) The table must not be referenced by any part of the query apart ** from its own USING or ON clause. ** ** For example, given: ** ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); ** ** then table t2 can be omitted from the following: ** ** SELECT v1, v3 FROM t1 ** LEFT JOIN t2 USING (t1.ipk=t2.ipk) ** LEFT JOIN t3 USING (t1.ipk=t3.ipk) ** ** or from: ** ** SELECT DISTINCT v1, v3 FROM t1 ** LEFT JOIN t2 ** LEFT JOIN t3 USING (t1.ipk=t3.ipk) */ notReady = ~(Bitmask)0; if (pWInfo->nLevel>=2 && pResultSet!=0 /* guarantees condition (1) above */ && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ) { int i; Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet); if (sWLB.pOrderBy) { tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); } for (i=pWInfo->nLevel-1; i>=1; i--) { WhereTerm *pTerm, *pEnd; struct SrcList_item *pItem; pLoop = pWInfo->a[i].pWLoop; pItem = &pWInfo->pTabList->a[pLoop->iTab]; if ((pItem->fg.jointype & JT_LEFT)==0) continue; if ((wctrlFlags & WHERE_WANT_DISTINCT)==0 && (pLoop->wsFlags & WHERE_ONEROW)==0 ) { continue; } if ((tabUsed & pLoop->maskSelf)!=0) continue; pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; for (pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0) { if (!ExprHasProperty(pTerm->pExpr, EP_FromJoin) || pTerm->pExpr->iRightJoinTable!=pItem->iCursor ) { break; } } } if (pTerm drop loop %c not used\n", pLoop->cId)); notReady &= ~pLoop->maskSelf; for (pTerm=sWLB.pWC->a; pTermprereqAll & pLoop->maskSelf)!=0) { pTerm->wtFlags |= TERM_CODED; } } if (i!=pWInfo->nLevel-1) { int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel); memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte); } pWInfo->nLevel--; nTabList--; } } WHERETRACE(0xffff,("*** Optimizer Finished ***\n")); pWInfo->pParse->nQueryLoop += pWInfo->nRowOut; /* If the caller is an UPDATE or DELETE statement that is requesting ** to use a one-pass algorithm, determine if this is appropriate. ** ** A one-pass approach can be used if the caller has requested one ** and either (a) the scan visits at most one row or (b) each ** of the following are true: ** ** * the caller has indicated that a one-pass approach can be used ** with multiple rows (by setting WHERE_ONEPASS_MULTIROW), and ** * the table is not a virtual table, and ** * either the scan does not use the OR optimization or the caller ** is a DELETE operation (WHERE_DUPLICATES_OK is only specified ** for DELETE). ** ** The last qualification is because an UPDATE statement uses ** WhereInfo.aiCurOnePass[1] to determine whether or not it really can ** use a one-pass approach, and this is not set accurately for scans ** that use the OR optimization. */ assert((wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 ); if ((wctrlFlags & WHERE_ONEPASS_DESIRED)!=0) { int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab)); if (bOnerow || ( 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) && !IsVirtual(pTabList->a[0].pTab) && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) )) { pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; if (HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY)) { if (wctrlFlags & WHERE_ONEPASS_MULTIROW) { bFordelete = OPFLAG_FORDELETE; } pWInfo->a[0].pWLoop->wsFlags = (wsFlags & ~WHERE_IDX_ONLY); } } } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ for (ii=0, pLevel=pWInfo->a; iia[pLevel->iFrom]; pTab = pTabItem->pTab; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); pLoop = pLevel->pWLoop; if ((pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect) { /* Do nothing */ } else #ifndef SQLITE_OMIT_VIRTUALTABLE if ((pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0) { const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); int iCur = pTabItem->iCursor; sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); } else if (IsVirtual(pTab)) { /* noop */ } else #endif if ((pLoop->wsFlags & WHERE_IDX_ONLY)==0 && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0) { int op = OP_OpenRead; if (pWInfo->eOnePass!=ONEPASS_OFF) { op = OP_OpenWrite; pWInfo->aiCurOnePass[0] = pTabItem->iCursor; }; sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op); assert( pTabItem->iCursor==pLevel->iTabCur ); testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS-1 ); testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS ); if (pWInfo->eOnePass==ONEPASS_OFF && pTab->nColcolUsed; int n = 0; for (; b; b=b>>1, n++) {} sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32); assert( n<=pTab->nCol ); } #ifdef SQLITE_ENABLE_CURSOR_HINTS if (pLoop->u.btree.pIndex!=0) { sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); } else #endif { sqlite3VdbeChangeP5(v, bFordelete); } #ifdef SQLITE_ENABLE_COLUMN_USED_MASK sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0, (const u8*)&pTabItem->colUsed, P4_INT64); #endif } else { sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); } if (pLoop->wsFlags & WHERE_INDEXED) { Index *pIx = pLoop->u.btree.pIndex; int iIndexCur; int op = OP_OpenRead; /* iAuxArg is always set to a positive value if ONEPASS is possible */ assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 ); if (!HasRowid(pTab) && IsPrimaryKeyIndex(pIx) && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ) { /* This is one term of an OR-optimization using the PRIMARY KEY of a ** WITHOUT ROWID table. No need for a separate index */ iIndexCur = pLevel->iTabCur; op = 0; } else if (pWInfo->eOnePass!=ONEPASS_OFF) { Index *pJ = pTabItem->pTab->pIndex; iIndexCur = iAuxArg; assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); while (ALWAYS(pJ) && pJ!=pIx) { iIndexCur++; pJ = pJ->pNext; } op = OP_OpenWrite; pWInfo->aiCurOnePass[1] = iIndexCur; } else if (iAuxArg && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0) { iIndexCur = iAuxArg; op = OP_ReopenIdx; } else { iIndexCur = pParse->nTab++; } pLevel->iIdxCur = iIndexCur; assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); if (op) { sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); if ((pLoop->wsFlags & WHERE_CONSTRAINT)!=0 && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 && pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED ) { sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */ } VdbeComment((v, "%s", pIx->zName)); #ifdef SQLITE_ENABLE_COLUMN_USED_MASK { u64 colUsed = 0; int ii, jj; for (ii=0; iinColumn; ii++) { jj = pIx->aiColumn[ii]; if (jj<0) continue; if (jj>63) jj = 63; if ((pTabItem->colUsed & MASKBIT(jj))==0) continue; colUsed |= ((u64)1)<<(ii<63 ? ii : 63); } sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, iIndexCur, 0, 0, (u8*)&colUsed, P4_INT64); } #endif /* SQLITE_ENABLE_COLUMN_USED_MASK */ } } if (iDb>=0) sqlite3CodeVerifySchema(pParse, iDb); } pWInfo->iTop = sqlite3VdbeCurrentAddr(v); if (db->mallocFailed) goto whereBeginError; /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ for (ii=0; iia[ii]; wsFlags = pLevel->pWLoop->wsFlags; #ifndef SQLITE_OMIT_AUTOMATIC_INDEX if ((pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0) { constructAutomaticIndex(pParse, &pWInfo->sWC, &pTabList->a[pLevel->iFrom], notReady, pLevel); if (db->mallocFailed) goto whereBeginError; } #endif addrExplain = sqlite3WhereExplainOneScan( pParse, pTabList, pLevel, wctrlFlags ); pLevel->addrBody = sqlite3VdbeCurrentAddr(v); notReady = sqlite3WhereCodeOneLoopStart(pParse,v,pWInfo,ii,pLevel,notReady); pWInfo->iContinue = pLevel->addrCont; if ((wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0) { sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain); } } /* Done. */ VdbeModuleComment((v, "Begin WHERE-core")); return pWInfo; /* Jump here if malloc fails */ whereBeginError: if (pWInfo) { pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); } return 0; } /* ** Part of sqlite3WhereEnd() will rewrite opcodes to reference the ** index rather than the main table. In SQLITE_DEBUG mode, we want ** to trace those changes if PRAGMA vdbe_addoptrace=on. This routine ** does that. */ #ifndef SQLITE_DEBUG # define OpcodeRewriteTrace(D,K,P) /* no-op */ #else # define OpcodeRewriteTrace(D,K,P) sqlite3WhereOpcodeRewriteTrace(D,K,P) static void sqlite3WhereOpcodeRewriteTrace( sqlite3 *db, int pc, VdbeOp *pOp ){ if ((db->flags & SQLITE_VdbeAddopTrace)==0) return; sqlite3VdbePrintOp(0, pc, pOp); } #endif /* ** Generate the end of the WHERE loop. See comments on ** sqlite3WhereBegin() for additional information. */ void sqlite3WhereEnd(WhereInfo *pWInfo){ Parse *pParse = pWInfo->pParse; Vdbe *v = pParse->pVdbe; int i; WhereLevel *pLevel; WhereLoop *pLoop; SrcList *pTabList = pWInfo->pTabList; sqlite3 *db = pParse->db; /* Generate loop termination code. */ VdbeModuleComment((v, "End WHERE-core")); for (i=pWInfo->nLevel-1; i>=0; i--) { int addr; pLevel = &pWInfo->a[i]; pLoop = pLevel->pWLoop; if (pLevel->op!=OP_Noop) { #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT int addrSeek = 0; Index *pIdx; int n; if (pWInfo->eDistinct==WHERE_DISTINCT_ORDERED && i==pWInfo->nLevel-1 /* Ticket [ef9318757b152e3] 2017-10-21 */ && (pLoop->wsFlags & WHERE_INDEXED)!=0 && (pIdx = pLoop->u.btree.pIndex)->hasStat1 && (n = pLoop->u.btree.nDistinctCol)>0 && pIdx->aiRowLogEst[n]>=36 ) { int r1 = pParse->nMem+1; int j, op; for (j=0; jiIdxCur, j, r1+j); } pParse->nMem += n+1; op = pLevel->op==OP_Prev ? OP_SeekLT : OP_SeekGT; addrSeek = sqlite3VdbeAddOp4Int(v, op, pLevel->iIdxCur, 0, r1, n); VdbeCoverageIf(v, op==OP_SeekLT); VdbeCoverageIf(v, op==OP_SeekGT); sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); } #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ /* The common case: Advance to the next row */ sqlite3VdbeResolveLabel(v, pLevel->addrCont); sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); sqlite3VdbeChangeP5(v, pLevel->p5); VdbeCoverage(v); VdbeCoverageIf(v, pLevel->op==OP_Next); VdbeCoverageIf(v, pLevel->op==OP_Prev); VdbeCoverageIf(v, pLevel->op==OP_VNext); #ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT if (addrSeek) sqlite3VdbeJumpHere(v, addrSeek); #endif } else { sqlite3VdbeResolveLabel(v, pLevel->addrCont); } if (pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0) { struct InLoop *pIn; int j; sqlite3VdbeResolveLabel(v, pLevel->addrNxt); for (j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--) { sqlite3VdbeJumpHere(v, pIn->addrInTop+1); if (pIn->eEndLoopOp!=OP_Noop) { if (pIn->nPrefix) { assert( pLoop->wsFlags & WHERE_IN_EARLYOUT ); sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur, sqlite3VdbeCurrentAddr(v)+2, pIn->iBase, pIn->nPrefix); VdbeCoverage(v); } sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); VdbeCoverage(v); VdbeCoverageIf(v, pIn->eEndLoopOp==OP_Prev); VdbeCoverageIf(v, pIn->eEndLoopOp==OP_Next); } sqlite3VdbeJumpHere(v, pIn->addrInTop-1); } } sqlite3VdbeResolveLabel(v, pLevel->addrBrk); if (pLevel->addrSkip) { sqlite3VdbeGoto(v, pLevel->addrSkip); VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName)); sqlite3VdbeJumpHere(v, pLevel->addrSkip); sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); } #ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS if (pLevel->addrLikeRep) { sqlite3VdbeAddOp2(v, OP_DecrJumpZero, (int)(pLevel->iLikeRepCntr>>1), pLevel->addrLikeRep); VdbeCoverage(v); } #endif if (pLevel->iLeftJoin) { int ws = pLoop->wsFlags; addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert((ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if ((ws & WHERE_IDX_ONLY)==0) { assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor ); sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if ((ws & WHERE_INDEXED) || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx) ) { sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); } if (pLevel->op==OP_Return) { sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst); } else { sqlite3VdbeGoto(v, pLevel->addrFirst); } sqlite3VdbeJumpHere(v, addr); } VdbeModuleComment((v, "End WHERE-loop%d: %s", i, pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } /* The "break" point is here, just past the end of the outer loop. ** Set it. */ sqlite3VdbeResolveLabel(v, pWInfo->iBreak); assert( pWInfo->nLevel<=pTabList->nSrc ); for (i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++) { int k, last; VdbeOp *pOp; Index *pIdx = 0; struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ if (pTabItem->fg.viaCoroutine) { testcase( pParse->db->mallocFailed ); translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, pTabItem->regResult, 0); continue; } #ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE /* Close all of the cursors that were opened by sqlite3WhereBegin. ** Except, do not close cursors that will be reused by the OR optimization ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors ** created for the ONEPASS optimization. */ if ((pTab->tabFlags & TF_Ephemeral)==0 && pTab->pSelect==0 && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ) { int ws = pLoop->wsFlags; if (pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0) { sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); } if ((ws & WHERE_INDEXED)!=0 && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] ) { sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); } } #endif /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases ** this optimization prevents the table from ever being read, which can ** yield a significant performance boost. ** ** Calls to the code generator in between sqlite3WhereBegin and ** sqlite3WhereEnd will have created code that references the table ** directly. This loop scans all that code looking for opcodes ** that reference the table and converts them into opcodes that ** reference the index. */ if (pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY)) { pIdx = pLoop->u.btree.pIndex; } else if (pLoop->wsFlags & WHERE_MULTI_OR) { pIdx = pLevel->u.pCovidx; } if (pIdx && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable)) && !db->mallocFailed ) { last = sqlite3VdbeCurrentAddr(v); k = pLevel->addrBody; #ifdef SQLITE_DEBUG if (db->flags & SQLITE_VdbeAddopTrace) { printf("TRANSLATE opcodes in range %d..%d\n", k, last-1); } #endif pOp = sqlite3VdbeGetOp(v, k); for (; kp1!=pLevel->iTabCur) continue; if (pOp->opcode==OP_Column #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC || pOp->opcode==OP_Offset #endif ) { int x = pOp->p2; assert( pIdx->pTable==pTab ); if (!HasRowid(pTab)) { Index *pPk = sqlite3PrimaryKeyIndex(pTab); x = pPk->aiColumn[x]; assert( x>=0 ); } x = sqlite3ColumnOfIndex(pIdx, x); if (x>=0) { pOp->p2 = x; pOp->p1 = pLevel->iIdxCur; OpcodeRewriteTrace(db, k, pOp); } assert((pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 || pWInfo->eOnePass ); } else if (pOp->opcode==OP_Rowid) { pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; OpcodeRewriteTrace(db, k, pOp); } else if (pOp->opcode==OP_IfNullRow) { pOp->p1 = pLevel->iIdxCur; OpcodeRewriteTrace(db, k, pOp); } } #ifdef SQLITE_DEBUG if (db->flags & SQLITE_VdbeAddopTrace) printf("TRANSLATE complete\n"); #endif } } /* Final cleanup */ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); return; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-19334/000077500000000000000000000000001417746362400206325ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-19334/cmd.txt000066400000000000000000000000361417746362400221350ustar00rootroot00000000000000-DLY_CHECK_ERR_RETURN(A,B,C)= cppcheck-2.7/test/bug-hunting/cve/CVE-2019-19334/expected.txt000066400000000000000000000001171417746362400231730ustar00rootroot00000000000000parser.c:1024:bughuntingBufferOverflow parser.c:1026:bughuntingBufferOverflow cppcheck-2.7/test/bug-hunting/cve/CVE-2019-19334/parser.c000066400000000000000000004230251417746362400223000ustar00rootroot00000000000000/** * @file parser.c * @author Radek Krejci * @brief common libyang parsers routines implementations * * Copyright (c) 2015-2017 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "context.h" #include "libyang.h" #include "parser.h" #include "resolve.h" #include "tree_internal.h" #include "parser_yang.h" #include "xpath.h" #define LYP_URANGE_LEN 19 static char *lyp_ublock2urange[][2] = { {"BasicLatin", "[\\x{0000}-\\x{007F}]"}, {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"}, {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"}, {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"}, {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"}, {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"}, {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"}, {"Greek", "[\\x{0370}-\\x{03FF}]"}, {"Cyrillic", "[\\x{0400}-\\x{04FF}]"}, {"Armenian", "[\\x{0530}-\\x{058F}]"}, {"Hebrew", "[\\x{0590}-\\x{05FF}]"}, {"Arabic", "[\\x{0600}-\\x{06FF}]"}, {"Syriac", "[\\x{0700}-\\x{074F}]"}, {"Thaana", "[\\x{0780}-\\x{07BF}]"}, {"Devanagari", "[\\x{0900}-\\x{097F}]"}, {"Bengali", "[\\x{0980}-\\x{09FF}]"}, {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"}, {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"}, {"Oriya", "[\\x{0B00}-\\x{0B7F}]"}, {"Tamil", "[\\x{0B80}-\\x{0BFF}]"}, {"Telugu", "[\\x{0C00}-\\x{0C7F}]"}, {"Kannada", "[\\x{0C80}-\\x{0CFF}]"}, {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"}, {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"}, {"Thai", "[\\x{0E00}-\\x{0E7F}]"}, {"Lao", "[\\x{0E80}-\\x{0EFF}]"}, {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"}, {"Myanmar", "[\\x{1000}-\\x{109F}]"}, {"Georgian", "[\\x{10A0}-\\x{10FF}]"}, {"HangulJamo", "[\\x{1100}-\\x{11FF}]"}, {"Ethiopic", "[\\x{1200}-\\x{137F}]"}, {"Cherokee", "[\\x{13A0}-\\x{13FF}]"}, {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"}, {"Ogham", "[\\x{1680}-\\x{169F}]"}, {"Runic", "[\\x{16A0}-\\x{16FF}]"}, {"Khmer", "[\\x{1780}-\\x{17FF}]"}, {"Mongolian", "[\\x{1800}-\\x{18AF}]"}, {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"}, {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"}, {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"}, {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"}, {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"}, {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"}, {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"}, {"NumberForms", "[\\x{2150}-\\x{218F}]"}, {"Arrows", "[\\x{2190}-\\x{21FF}]"}, {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"}, {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"}, {"ControlPictures", "[\\x{2400}-\\x{243F}]"}, {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"}, {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"}, {"BoxDrawing", "[\\x{2500}-\\x{257F}]"}, {"BlockElements", "[\\x{2580}-\\x{259F}]"}, {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"}, {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"}, {"Dingbats", "[\\x{2700}-\\x{27BF}]"}, {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"}, {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"}, {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"}, {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"}, {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"}, {"Hiragana", "[\\x{3040}-\\x{309F}]"}, {"Katakana", "[\\x{30A0}-\\x{30FF}]"}, {"Bopomofo", "[\\x{3100}-\\x{312F}]"}, {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"}, {"Kanbun", "[\\x{3190}-\\x{319F}]"}, {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"}, {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"}, {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"}, {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"}, {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"}, {"YiSyllables", "[\\x{A000}-\\x{A48F}]"}, {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"}, {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"}, {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"}, {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"}, {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"}, {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"}, {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"}, {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"}, {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"}, {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"}, {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"}, {NULL, NULL} }; const char *ly_stmt_str[] = { [LY_STMT_UNKNOWN] = "", [LY_STMT_ARGUMENT] = "argument", [LY_STMT_BASE] = "base", [LY_STMT_BELONGSTO] = "belongs-to", [LY_STMT_CONTACT] = "contact", [LY_STMT_DEFAULT] = "default", [LY_STMT_DESCRIPTION] = "description", [LY_STMT_ERRTAG] = "error-app-tag", [LY_STMT_ERRMSG] = "error-message", [LY_STMT_KEY] = "key", [LY_STMT_NAMESPACE] = "namespace", [LY_STMT_ORGANIZATION] = "organization", [LY_STMT_PATH] = "path", [LY_STMT_PREFIX] = "prefix", [LY_STMT_PRESENCE] = "presence", [LY_STMT_REFERENCE] = "reference", [LY_STMT_REVISIONDATE] = "revision-date", [LY_STMT_UNITS] = "units", [LY_STMT_VALUE] = "value", [LY_STMT_VERSION] = "yang-version", [LY_STMT_MODIFIER] = "modifier", [LY_STMT_REQINSTANCE] = "require-instance", [LY_STMT_YINELEM] = "yin-element", [LY_STMT_CONFIG] = "config", [LY_STMT_MANDATORY] = "mandatory", [LY_STMT_ORDEREDBY] = "ordered-by", [LY_STMT_STATUS] = "status", [LY_STMT_DIGITS] = "fraction-digits", [LY_STMT_MAX] = "max-elements", [LY_STMT_MIN] = "min-elements", [LY_STMT_POSITION] = "position", [LY_STMT_UNIQUE] = "unique", [LY_STMT_MODULE] = "module", [LY_STMT_SUBMODULE] = "submodule", [LY_STMT_ACTION] = "action", [LY_STMT_ANYDATA] = "anydata", [LY_STMT_ANYXML] = "anyxml", [LY_STMT_CASE] = "case", [LY_STMT_CHOICE] = "choice", [LY_STMT_CONTAINER] = "container", [LY_STMT_GROUPING] = "grouping", [LY_STMT_INPUT] = "input", [LY_STMT_LEAF] = "leaf", [LY_STMT_LEAFLIST] = "leaf-list", [LY_STMT_LIST] = "list", [LY_STMT_NOTIFICATION] = "notification", [LY_STMT_OUTPUT] = "output", [LY_STMT_RPC] = "rpc", [LY_STMT_USES] = "uses", [LY_STMT_TYPEDEF] = "typedef", [LY_STMT_TYPE] = "type", [LY_STMT_BIT] = "bit", [LY_STMT_ENUM] = "enum", [LY_STMT_REFINE] = "refine", [LY_STMT_AUGMENT] = "augment", [LY_STMT_DEVIATE] = "deviate", [LY_STMT_DEVIATION] = "deviation", [LY_STMT_EXTENSION] = "extension", [LY_STMT_FEATURE] = "feature", [LY_STMT_IDENTITY] = "identity", [LY_STMT_IFFEATURE] = "if-feature", [LY_STMT_IMPORT] = "import", [LY_STMT_INCLUDE] = "include", [LY_STMT_LENGTH] = "length", [LY_STMT_MUST] = "must", [LY_STMT_PATTERN] = "pattern", [LY_STMT_RANGE] = "range", [LY_STMT_WHEN] = "when", [LY_STMT_REVISION] = "revision" }; int lyp_is_rpc_action(struct lys_node *node) { assert(node); while (lys_parent(node)) { node = lys_parent(node); if (node->nodetype == LYS_ACTION) { break; } } if (node->nodetype & (LYS_RPC | LYS_ACTION)) { return 1; } else { return 0; } } int lyp_data_check_options(struct ly_ctx *ctx, int options, const char *func) { int x = options & LYD_OPT_TYPEMASK; /* LYD_OPT_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */ if (options & LYD_OPT_WHENAUTODEL) { if ((x == LYD_OPT_EDIT) || (x == LYD_OPT_NOTIF_FILTER)) { LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG)", func, options); return 1; } } if (options & (LYD_OPT_DATA_ADD_YANGLIB | LYD_OPT_DATA_NO_YANGLIB)) { if (x != LYD_OPT_DATA) { LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_*_YANGLIB can be used only with LYD_OPT_DATA)", func, options); return 1; } } /* "is power of 2" algorithm, with 0 exception */ if (x && !(x && !(x & (x - 1)))) { LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (multiple data type flags set).", func, options); return 1; } return 0; } int lyp_mmap(struct ly_ctx *ctx, int fd, size_t addsize, size_t *length, void **addr) { struct stat sb; long pagesize; size_t m; assert(fd >= 0); if (fstat(fd, &sb) == -1) { LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno)); return 1; } if (!S_ISREG(sb.st_mode)) { LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file."); return 1; } if (!sb.st_size) { *addr = NULL; return 0; } pagesize = sysconf(_SC_PAGESIZE); ++addsize; /* at least one additional byte for terminating NULL byte */ m = sb.st_size % pagesize; if (m && pagesize - m >= addsize) { /* there will be enough space after the file content mapping to provide zeroed additional bytes */ *length = sb.st_size + addsize; *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); } else { /* there will not be enough bytes after the file content mapping for the additional bytes and some of them * would overflow into another page that would not be zeroed and any access into it would generate SIGBUS. * Therefore we have to do the following hack with double mapping. First, the required number of bytes * (including the additional bytes) is required as anonymous and thus they will be really provided (actually more * because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address * where the anonymous mapping starts. */ *length = sb.st_size + pagesize; *addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); *addr = mmap(*addr, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0); } if (*addr == MAP_FAILED) { LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno)); return 1; } return 0; } int lyp_munmap(void *addr, size_t length) { return munmap(addr, length); } int lyp_add_ietf_netconf_annotations_config(struct lys_module *mod) { void *reallocated; struct lys_ext_instance_complex *op; struct lys_type **type; struct lys_node_anydata *anyxml; int i; struct ly_ctx *ctx = mod->ctx; /* shortcut */ reallocated = realloc(mod->ext, (mod->ext_size + 3) * sizeof *mod->ext); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), EXIT_FAILURE); mod->ext = reallocated; /* 1) edit-config's operation */ op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; op->arg_value = lydict_insert(ctx, "operation", 9); op->def = &ctx->models.list[0]->extensions[0]; op->ext_type = LYEXT_COMPLEX; op->module = op->parent = mod; op->parent_type = LYEXT_PAR_MODULE; op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; op->nodetype = LYS_EXT; type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ *type = calloc(1, sizeof(struct lys_type)); LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); (*type)->base = LY_TYPE_ENUM; (*type)->der = ly_types[LY_TYPE_ENUM]; (*type)->parent = (struct lys_tpdf *)op; (*type)->info.enums.count = 5; (*type)->info.enums.enm = calloc(5, sizeof *(*type)->info.enums.enm); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[0].value = 0; (*type)->info.enums.enm[0].name = lydict_insert(ctx, "merge", 5); (*type)->info.enums.enm[1].value = 1; (*type)->info.enums.enm[1].name = lydict_insert(ctx, "replace", 7); (*type)->info.enums.enm[2].value = 2; (*type)->info.enums.enm[2].name = lydict_insert(ctx, "create", 6); (*type)->info.enums.enm[3].value = 3; (*type)->info.enums.enm[3].name = lydict_insert(ctx, "delete", 6); (*type)->info.enums.enm[4].value = 4; (*type)->info.enums.enm[4].name = lydict_insert(ctx, "remove", 6); mod->ext_size++; /* 2) filter's type */ op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; op->arg_value = lydict_insert(ctx, "type", 4); op->def = &ctx->models.list[0]->extensions[0]; op->ext_type = LYEXT_COMPLEX; op->module = op->parent = mod; op->parent_type = LYEXT_PAR_MODULE; op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; op->nodetype = LYS_EXT; type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ *type = calloc(1, sizeof(struct lys_type)); LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); (*type)->base = LY_TYPE_ENUM; (*type)->der = ly_types[LY_TYPE_ENUM]; (*type)->parent = (struct lys_tpdf *)op; (*type)->info.enums.count = 2; (*type)->info.enums.enm = calloc(2, sizeof *(*type)->info.enums.enm); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[0].value = 0; (*type)->info.enums.enm[0].name = lydict_insert(ctx, "subtree", 7); (*type)->info.enums.enm[1].value = 1; (*type)->info.enums.enm[1].name = lydict_insert(ctx, "xpath", 5); for (i = mod->features_size; i > 0; i--) { if (!strcmp(mod->features[i - 1].name, "xpath")) { (*type)->info.enums.enm[1].iffeature_size = 1; (*type)->info.enums.enm[1].iffeature = calloc(1, sizeof(struct lys_feature)); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[1].iffeature[0].expr = malloc(sizeof(uint8_t)); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].expr, LOGMEM(ctx), EXIT_FAILURE); *(*type)->info.enums.enm[1].iffeature[0].expr = 3; /* LYS_IFF_F */ (*type)->info.enums.enm[1].iffeature[0].features = malloc(sizeof(struct lys_feature*)); LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].features, LOGMEM(ctx), EXIT_FAILURE); (*type)->info.enums.enm[1].iffeature[0].features[0] = &mod->features[i - 1]; break; } } mod->ext_size++; /* 3) filter's select */ op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t)); LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE); mod->ext[mod->ext_size] = (struct lys_ext_instance *)op; op->arg_value = lydict_insert(ctx, "select", 6); op->def = &ctx->models.list[0]->extensions[0]; op->ext_type = LYEXT_COMPLEX; op->module = op->parent = mod; op->parent_type = LYEXT_PAR_MODULE; op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt; op->nodetype = LYS_EXT; type = (struct lys_type**)&op->content; /* type is stored at offset 0 */ *type = calloc(1, sizeof(struct lys_type)); LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE); (*type)->base = LY_TYPE_STRING; (*type)->der = ly_types[LY_TYPE_STRING]; (*type)->parent = (struct lys_tpdf *)op; mod->ext_size++; /* 4) URL config */ anyxml = calloc(1, sizeof *anyxml); LY_CHECK_ERR_RETURN(!anyxml, LOGMEM(ctx), EXIT_FAILURE); anyxml->nodetype = LYS_ANYXML; anyxml->prev = (struct lys_node *)anyxml; anyxml->name = lydict_insert(ctx, "config", 0); anyxml->module = mod; anyxml->flags = LYS_CONFIG_W; if (lys_node_addchild(NULL, mod, (struct lys_node *)anyxml, 0)) { return EXIT_FAILURE; } return EXIT_SUCCESS; } /* logs directly * base: 0 - to accept decimal, octal, hexadecimal (in default value) * 10 - to accept only decimal (instance value) */ static int parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret, struct lyd_node *node) { char *strptr; assert(node); if (!val_str || !val_str[0]) { goto error; } /* convert to 64-bit integer, all the redundant characters are handled */ errno = 0; strptr = NULL; /* parse the value */ *ret = strtoll(val_str, &strptr, base); if (errno || (*ret < min) || (*ret > max)) { goto error; } else if (strptr && *strptr) { while (isspace(*strptr)) { ++strptr; } if (*strptr) { goto error; } } return EXIT_SUCCESS; error: LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); return EXIT_FAILURE; } /* logs directly * base: 0 - to accept decimal, octal, hexadecimal (in default value) * 10 - to accept only decimal (instance value) */ static int parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret, struct lyd_node *node) { char *strptr; uint64_t u; assert(node); if (!val_str || !val_str[0]) { goto error; } errno = 0; strptr = NULL; u = strtoull(val_str, &strptr, base); if (errno || (u > max)) { goto error; } else if (strptr && *strptr) { while (isspace(*strptr)) { ++strptr; } if (*strptr) { goto error; } } else if (u != 0 && val_str[0] == '-') { goto error; } *ret = u; return EXIT_SUCCESS; error: LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name); return EXIT_FAILURE; } /* logs directly * * kind == 0 - unsigned (unum used), 1 - signed (snum used), 2 - floating point (fnum used) */ static int validate_length_range(uint8_t kind, uint64_t unum, int64_t snum, int64_t fnum, uint8_t fnum_dig, struct lys_type *type, const char *val_str, struct lyd_node *node) { struct lys_restr *restr = NULL; struct len_ran_intv *intv = NULL, *tmp_intv; struct lys_type *cur_type; struct ly_ctx *ctx = type->parent->module->ctx; int match; if (resolve_len_ran_interval(ctx, NULL, type, &intv)) { /* already done during schema parsing */ LOGINT(ctx); return EXIT_FAILURE; } if (!intv) { return EXIT_SUCCESS; } /* I know that all intervals belonging to a single restriction share one type pointer */ tmp_intv = intv; cur_type = intv->type; do { match = 0; for (; tmp_intv && (tmp_intv->type == cur_type); tmp_intv = tmp_intv->next) { if (match) { /* just iterate through the rest of this restriction intervals */ continue; } if (((kind == 0) && (unum < tmp_intv->value.uval.min)) || ((kind == 1) && (snum < tmp_intv->value.sval.min)) || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) < 0))) { break; } if (((kind == 0) && (unum >= tmp_intv->value.uval.min) && (unum <= tmp_intv->value.uval.max)) || ((kind == 1) && (snum >= tmp_intv->value.sval.min) && (snum <= tmp_intv->value.sval.max)) || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) > -1) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.max, cur_type->info.dec64.dig) < 1))) { match = 1; } } if (!match) { break; } else if (tmp_intv) { cur_type = tmp_intv->type; } } while (tmp_intv); while (intv) { tmp_intv = intv->next; free(intv); intv = tmp_intv; } if (!match) { switch (cur_type->base) { case LY_TYPE_BINARY: restr = cur_type->info.binary.length; break; case LY_TYPE_DEC64: restr = cur_type->info.dec64.range; break; case LY_TYPE_INT8: case LY_TYPE_INT16: case LY_TYPE_INT32: case LY_TYPE_INT64: case LY_TYPE_UINT8: case LY_TYPE_UINT16: case LY_TYPE_UINT32: case LY_TYPE_UINT64: restr = cur_type->info.num.range; break; case LY_TYPE_STRING: restr = cur_type->info.str.length; break; default: LOGINT(ctx); return EXIT_FAILURE; } LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, (val_str ? val_str : ""), restr ? restr->expr : ""); if (restr && restr->emsg) { ly_vlog_str(ctx, LY_VLOG_PREV, restr->emsg); } if (restr && restr->eapptag) { ly_err_last_set_apptag(ctx, restr->eapptag); } return EXIT_FAILURE; } return EXIT_SUCCESS; } /* logs directly */ static int validate_pattern(struct ly_ctx *ctx, const char *val_str, struct lys_type *type, struct lyd_node *node) { int rc; unsigned int i; #ifndef LY_ENABLED_CACHE pcre *precomp; #endif assert(ctx && (type->base == LY_TYPE_STRING)); if (!val_str) { val_str = ""; } if (type->der && validate_pattern(ctx, val_str, &type->der->type, node)) { return EXIT_FAILURE; } #ifdef LY_ENABLED_CACHE /* there is no cache, build it */ if (!type->info.str.patterns_pcre && type->info.str.pat_count) { type->info.str.patterns_pcre = malloc(2 * type->info.str.pat_count * sizeof *type->info.str.patterns_pcre); LY_CHECK_ERR_RETURN(!type->info.str.patterns_pcre, LOGMEM(ctx), -1); for (i = 0; i < type->info.str.pat_count; ++i) { if (lyp_precompile_pattern(ctx, &type->info.str.patterns[i].expr[1], (pcre**)&type->info.str.patterns_pcre[i * 2], (pcre_extra**)&type->info.str.patterns_pcre[i * 2 + 1])) { return EXIT_FAILURE; } } } #endif for (i = 0; i < type->info.str.pat_count; ++i) { #ifdef LY_ENABLED_CACHE rc = pcre_exec((pcre *)type->info.str.patterns_pcre[2 * i], (pcre_extra *)type->info.str.patterns_pcre[2 * i + 1], val_str, strlen(val_str), 0, 0, NULL, 0); #else if (lyp_check_pattern(ctx, &type->info.str.patterns[i].expr[1], &precomp)) { return EXIT_FAILURE; } rc = pcre_exec(precomp, NULL, val_str, strlen(val_str), 0, 0, NULL, 0); free(precomp); #endif if ((rc && type->info.str.patterns[i].expr[0] == 0x06) || (!rc && type->info.str.patterns[i].expr[0] == 0x15)) { LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, val_str, &type->info.str.patterns[i].expr[1]); if (type->info.str.patterns[i].emsg) { ly_vlog_str(ctx, LY_VLOG_PREV, type->info.str.patterns[i].emsg); } if (type->info.str.patterns[i].eapptag) { ly_err_last_set_apptag(ctx, type->info.str.patterns[i].eapptag); } return EXIT_FAILURE; } } return EXIT_SUCCESS; } static void check_number(const char *str_num, const char **num_end, LY_DATA_TYPE base) { if (!isdigit(str_num[0]) && (str_num[0] != '-') && (str_num[0] != '+')) { *num_end = str_num; return; } if ((str_num[0] == '-') || (str_num[0] == '+')) { ++str_num; } while (isdigit(str_num[0])) { ++str_num; } if ((base != LY_TYPE_DEC64) || (str_num[0] != '.') || !isdigit(str_num[1])) { *num_end = str_num; return; } ++str_num; while (isdigit(str_num[0])) { ++str_num; } *num_end = str_num; } /** * @brief Checks the syntax of length or range statement, * on success checks the semantics as well. Does not log. * * @param[in] expr Length or range expression. * @param[in] type Type with the restriction. * * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. */ int lyp_check_length_range(struct ly_ctx *ctx, const char *expr, struct lys_type *type) { struct len_ran_intv *intv = NULL, *tmp_intv; const char *c = expr, *tail; int ret = EXIT_FAILURE, flg = 1; /* first run flag */ assert(expr); lengthpart: while (isspace(*c)) { c++; } /* lower boundary or explicit number */ if (!strncmp(c, "max", 3)) { max: c += 3; while (isspace(*c)) { c++; } if (*c != '\0') { goto error; } goto syntax_ok; } else if (!strncmp(c, "min", 3)) { if (!flg) { /* min cannot be used elsewhere than in the first length-part */ goto error; } else { flg = 0; } c += 3; while (isspace(*c)) { c++; } if (*c == '|') { c++; /* process next length-part */ goto lengthpart; } else if (*c == '\0') { goto syntax_ok; } else if (!strncmp(c, "..", 2)) { upper: c += 2; while (isspace(*c)) { c++; } if (*c == '\0') { goto error; } /* upper boundary */ if (!strncmp(c, "max", 3)) { goto max; } check_number(c, &tail, type->base); if (c == tail) { goto error; } c = tail; while (isspace(*c)) { c++; } if (*c == '\0') { goto syntax_ok; } else if (*c == '|') { c++; /* process next length-part */ goto lengthpart; } else { goto error; } } else { goto error; } } else if (isdigit(*c) || (*c == '-') || (*c == '+')) { /* number */ check_number(c, &tail, type->base); if (c == tail) { goto error; } c = tail; while (isspace(*c)) { c++; } if (*c == '|') { c++; /* process next length-part */ goto lengthpart; } else if (*c == '\0') { goto syntax_ok; } else if (!strncmp(c, "..", 2)) { goto upper; } } else { goto error; } syntax_ok: if (resolve_len_ran_interval(ctx, expr, type, &intv)) { goto error; } ret = EXIT_SUCCESS; error: while (intv) { tmp_intv = intv->next; free(intv); intv = tmp_intv; } return ret; } /** * @brief Checks pattern syntax. Logs directly. * * @param[in] pattern Pattern to check. * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL. * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise. */ int lyp_check_pattern(struct ly_ctx *ctx, const char *pattern, pcre **pcre_precomp) { int idx, idx2, start, end, err_offset, count; char *perl_regex, *ptr; const char *err_msg, *orig_ptr; pcre *precomp; /* * adjust the expression to a Perl equivalent * * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs */ /* we need to replace all "$" with "\$", count them now */ for (count = 0, ptr = strchr(pattern, '$'); ptr; ++count, ptr = strchr(ptr + 1, '$')); perl_regex = malloc((strlen(pattern) + 4 + count) * sizeof(char)); LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx), EXIT_FAILURE); perl_regex[0] = '\0'; ptr = perl_regex; if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) { /* we wil add line-end anchoring */ ptr[0] = '('; ++ptr; } for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) { if (orig_ptr[0] == '$') { ptr += sprintf(ptr, "\\$"); } else { ptr[0] = orig_ptr[0]; ++ptr; } } if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) { ptr += sprintf(ptr, ")$"); } else { ptr[0] = '\0'; ++ptr; } /* substitute Unicode Character Blocks with exact Character Ranges */ while ((ptr = strstr(perl_regex, "\\p{Is"))) { start = ptr - perl_regex; ptr = strchr(ptr, '}'); if (!ptr) { LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 2, "unterminated character property"); free(perl_regex); return EXIT_FAILURE; } end = (ptr - perl_regex) + 1; /* need more space */ if (end - start < LYP_URANGE_LEN) { perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (LYP_URANGE_LEN - (end - start)) + 1); LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx); free(perl_regex), EXIT_FAILURE); } /* find our range */ for (idx = 0; lyp_ublock2urange[idx][0]; ++idx) { if (!strncmp(perl_regex + start + 5, lyp_ublock2urange[idx][0], strlen(lyp_ublock2urange[idx][0]))) { break; } } if (!lyp_ublock2urange[idx][0]) { LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 5, "unknown block name"); free(perl_regex); return EXIT_FAILURE; } /* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */ for (idx2 = 0, count = 0; idx2 < start; ++idx2) { if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { ++count; } if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) { --count; } } if (count) { /* skip brackets */ memmove(perl_regex + start + (LYP_URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1); memcpy(perl_regex + start, lyp_ublock2urange[idx][1] + 1, LYP_URANGE_LEN - 2); } else { memmove(perl_regex + start + LYP_URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1); memcpy(perl_regex + start, lyp_ublock2urange[idx][1], LYP_URANGE_LEN); } } /* must return 0, already checked during parsing */ precomp = pcre_compile(perl_regex, PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE, &err_msg, &err_offset, NULL); if (!precomp) { LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + err_offset, err_msg); free(perl_regex); return EXIT_FAILURE; } free(perl_regex); if (pcre_precomp) { *pcre_precomp = precomp; } else { free(precomp); } return EXIT_SUCCESS; } int lyp_precompile_pattern(struct ly_ctx *ctx, const char *pattern, pcre** pcre_cmp, pcre_extra **pcre_std) { const char *err_msg = NULL; if (lyp_check_pattern(ctx, pattern, pcre_cmp)) { return EXIT_FAILURE; } if (pcre_std && pcre_cmp) { (*pcre_std) = pcre_study(*pcre_cmp, 0, &err_msg); if (err_msg) { LOGWRN(ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg); } } return EXIT_SUCCESS; } /** * @brief Change the value into its canonical form. In libyang, additionally to the RFC, * all identities have their module as a prefix in their canonical form. * * @param[in] ctx * @param[in] type Type of the value. * @param[in,out] value Original and then canonical value. * @param[in] data1 If \p type is #LY_TYPE_BITS: (struct lys_type_bit **) type bit field, * #LY_TYPE_DEC64: (int64_t *) parsed digits of the number itself without floating point, * #LY_TYPE_IDENT: (const char *) local module name (identityref node module), * #LY_TYPE_INT*: (int64_t *) parsed int number itself, * #LY_TYPE_UINT*: (uint64_t *) parsed uint number itself, * otherwise ignored. * @param[in] data2 If \p type is #LY_TYPE_BITS: (int *) type bit field length, * #LY_TYPE_DEC64: (uint8_t *) number of fraction digits (position of the floating point), * otherwise ignored. * @return 1 if a conversion took place, 0 if the value was kept the same, -1 on error. */ static int make_canonical(struct ly_ctx *ctx, int type, const char **value, void *data1, void *data2) { const uint16_t buf_len = 511; char buf[buf_len + 1]; struct lys_type_bit **bits = NULL; struct lyxp_expr *exp; const char *module_name, *cur_expr, *end; int i, j, count; int64_t num; uint64_t unum; uint8_t c; #define LOGBUF(str) LOGERR(ctx, LY_EINVAL, "Value \"%s\" is too long.", str) switch (type) { case LY_TYPE_BITS: bits = (struct lys_type_bit **)data1; count = *((int *)data2); /* in canonical form, the bits are ordered by their position */ buf[0] = '\0'; for (i = 0; i < count; i++) { if (!bits[i]) { /* bit not set */ continue; } if (buf[0]) { LY_CHECK_ERR_RETURN(strlen(buf) + 1 + strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); sprintf(buf + strlen(buf), " %s", bits[i]->name); } else { LY_CHECK_ERR_RETURN(strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1); strcpy(buf, bits[i]->name); } } break; case LY_TYPE_IDENT: module_name = (const char *)data1; /* identity must always have a prefix */ if (!strchr(*value, ':')) { sprintf(buf, "%s:%s", module_name, *value); } else { strcpy(buf, *value); } break; case LY_TYPE_INST: exp = lyxp_parse_expr(ctx, *value); LY_CHECK_ERR_RETURN(!exp, LOGINT(ctx), -1); module_name = NULL; count = 0; for (i = 0; (unsigned)i < exp->used; ++i) { cur_expr = &exp->expr[exp->expr_pos[i]]; /* copy WS */ if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) { if (count + (cur_expr - end) > buf_len) { lyxp_expr_free(exp); LOGBUF(end); return -1; } strncpy(&buf[count], end, cur_expr - end); count += cur_expr - end; } if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) { /* get the module name with ":" */ ++end; j = end - cur_expr; if (!module_name || strncmp(cur_expr, module_name, j)) { /* print module name with colon, it does not equal to the parent one */ if (count + j > buf_len) { lyxp_expr_free(exp); LOGBUF(cur_expr); return -1; } strncpy(&buf[count], cur_expr, j); count += j; } module_name = cur_expr; /* copy the rest */ if (count + (exp->tok_len[i] - j) > buf_len) { lyxp_expr_free(exp); LOGBUF(end); return -1; } strncpy(&buf[count], end, exp->tok_len[i] - j); count += exp->tok_len[i] - j; } else { if (count + exp->tok_len[i] > buf_len) { lyxp_expr_free(exp); LOGBUF(&exp->expr[exp->expr_pos[i]]); return -1; } strncpy(&buf[count], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]); count += exp->tok_len[i]; } } if (count > buf_len) { LOGINT(ctx); lyxp_expr_free(exp); return -1; } buf[count] = '\0'; lyxp_expr_free(exp); break; case LY_TYPE_DEC64: num = *((int64_t *)data1); c = *((uint8_t *)data2); if (num) { count = sprintf(buf, "%" PRId64 " ", num); if ((num > 0 && (count - 1) <= c) || (count - 2) <= c) { /* we have 0. value, print the value with the leading zeros * (one for 0. and also keep the correct with of num according * to fraction-digits value) * for (num<0) - extra character for '-' sign */ count = sprintf(buf, "%0*" PRId64 " ", (num > 0) ? (c + 1) : (c + 2), num); } for (i = c, j = 1; i > 0; i--) { if (j && i > 1 && buf[count - 2] == '0') { /* we have trailing zero to skip */ buf[count - 1] = '\0'; } else { j = 0; buf[count - 1] = buf[count - 2]; } count--; } buf[count - 1] = '.'; } else { /* zero */ sprintf(buf, "0.0"); } break; case LY_TYPE_INT8: case LY_TYPE_INT16: case LY_TYPE_INT32: case LY_TYPE_INT64: num = *((int64_t *)data1); sprintf(buf, "%" PRId64, num); break; case LY_TYPE_UINT8: case LY_TYPE_UINT16: case LY_TYPE_UINT32: case LY_TYPE_UINT64: unum = *((uint64_t *)data1); sprintf(buf, "%" PRIu64, unum); break; default: /* should not be even called - just do nothing */ return 0; } if (strcmp(buf, *value)) { lydict_remove(ctx, *value); *value = lydict_insert(ctx, buf, 0); return 1; } return 0; #undef LOGBUF } static const char * ident_val_add_module_prefix(const char *value, const struct lyxml_elem *xml, struct ly_ctx *ctx) { const struct lyxml_ns *ns; const struct lys_module *mod; char *str; do { LY_TREE_FOR((struct lyxml_ns *)xml->attr, ns) { if ((ns->type == LYXML_ATTR_NS) && !ns->prefix) { /* match */ break; } } if (!ns) { xml = xml->parent; } } while (!ns && xml); if (!ns) { /* no default namespace */ LOGINT(ctx); return NULL; } /* find module */ mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL, 1); if (!mod) { LOGINT(ctx); return NULL; } if (asprintf(&str, "%s:%s", mod->name, value) == -1) { LOGMEM(ctx); return NULL; } lydict_remove(ctx, value); return lydict_insert_zc(ctx, str); } /* * xml - optional for converting instance-identifier and identityref into JSON format * leaf - mandatory to know the context (necessary e.g. for prefixes in idenitytref values) * attr - alternative to leaf in case of parsing value in annotations (attributes) * local_mod - optional if the local module dos not match the module of leaf/attr * store - flag for union resolution - we do not want to store the result, we are just learning the type * dflt - whether the value is a default value from the schema * trusted - whether the value is trusted to be valid (but may not be canonical, so it is canonized) */ struct lys_type * lyp_parse_value(struct lys_type *type, const char **value_, struct lyxml_elem *xml, struct lyd_node_leaf_list *leaf, struct lyd_attr *attr, struct lys_module *local_mod, int store, int dflt, int trusted) { struct lys_type *ret = NULL, *t; struct lys_tpdf *tpdf; enum int_log_opts prev_ilo; int c, len, found = 0; unsigned int i, j; int64_t num; uint64_t unum, uind, u = 0; const char *ptr, *value = *value_, *itemname, *old_val_str = NULL; struct lys_type_bit **bits = NULL; struct lys_ident *ident; lyd_val *val, old_val; LY_DATA_TYPE *val_type, old_val_type; uint8_t *val_flags, old_val_flags; struct lyd_node *contextnode; struct ly_ctx *ctx = type->parent->module->ctx; assert(leaf || attr); if (leaf) { assert(!attr); if (!local_mod) { local_mod = leaf->schema->module; } val = &leaf->value; val_type = &leaf->value_type; val_flags = &leaf->value_flags; contextnode = (struct lyd_node *)leaf; itemname = leaf->schema->name; } else { assert(!leaf); if (!local_mod) { local_mod = attr->annotation->module; } val = &attr->value; val_type = &attr->value_type; val_flags = &attr->value_flags; contextnode = attr->parent; itemname = attr->name; } /* fully clear the value */ if (store) { old_val_str = lydict_insert(ctx, *value_, 0); lyd_free_value(*val, *val_type, *val_flags, type, old_val_str, &old_val, &old_val_type, &old_val_flags); *val_flags &= ~LY_VALUE_UNRES; } switch (type->base) { case LY_TYPE_BINARY: /* get number of octets for length validation */ unum = 0; ptr = NULL; if (value) { /* silently skip leading/trailing whitespaces */ for (uind = 0; isspace(value[uind]); ++uind); ptr = &value[uind]; u = strlen(ptr); while (u && isspace(ptr[u - 1])) { --u; } unum = u; for (uind = 0; uind < u; ++uind) { if (ptr[uind] == '\n') { unum--; } else if ((ptr[uind] < '/' && ptr[uind] != '+') || (ptr[uind] > '9' && ptr[uind] < 'A') || (ptr[uind] > 'Z' && ptr[uind] < 'a') || ptr[uind] > 'z') { if (ptr[uind] == '=') { /* padding */ if (uind == u - 2 && ptr[uind + 1] == '=') { found = 2; uind++; } else if (uind == u - 1) { found = 1; } } if (!found) { /* error */ LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYD, contextnode, ptr[uind], &ptr[uind]); LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid Base64 character."); goto error; } } } } if (unum & 3) { /* base64 length must be multiple of 4 chars */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Base64 encoded value length must be divisible by 4."); goto error; } /* length of the encoded string */ len = ((unum / 4) * 3) - found; if (!trusted && validate_length_range(0, len, 0, 0, 0, type, value, contextnode)) { goto error; } if (value && (ptr != value || ptr[u] != '\0')) { /* update the changed value */ ptr = lydict_insert(ctx, ptr, u); lydict_remove(ctx, *value_); *value_ = ptr; } if (store) { /* store the result */ val->binary = value; *val_type = LY_TYPE_BINARY; } break; case LY_TYPE_BITS: /* locate bits structure with the bits definitions * since YANG 1.1 allows restricted bits, it is the first * bits type with some explicit bit specification */ for (; !type->info.bits.count; type = &type->der->type); if (value || store) { /* allocate the array of pointers to bits definition */ bits = calloc(type->info.bits.count, sizeof *bits); LY_CHECK_ERR_GOTO(!bits, LOGMEM(ctx), error); } if (!value) { /* no bits set */ if (store) { /* store empty array */ val->bit = bits; *val_type = LY_TYPE_BITS; } break; } c = 0; i = 0; while (value[c]) { /* skip leading whitespaces */ while (isspace(value[c])) { c++; } if (!value[c]) { /* trailing white spaces */ break; } /* get the length of the bit identifier */ for (len = 0; value[c] && !isspace(value[c]); c++, len++); /* go back to the beginning of the identifier */ c = c - len; /* find bit definition, identifiers appear ordered by their position */ for (found = i = 0; i < type->info.bits.count; i++) { if (!strncmp(type->info.bits.bit[i].name, &value[c], len) && !type->info.bits.bit[i].name[len]) { /* we have match, check if the value is enabled ... */ for (j = 0; !trusted && (j < type->info.bits.bit[i].iffeature_size); j++) { if (!resolve_iffeature(&type->info.bits.bit[i].iffeature[j])) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Bit \"%s\" is disabled by its %d. if-feature condition.", type->info.bits.bit[i].name, j + 1); free(bits); goto error; } } /* check that the value was not already set */ if (bits[i]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Bit \"%s\" used multiple times.", type->info.bits.bit[i].name); free(bits); goto error; } /* ... and then store the pointer */ bits[i] = &type->info.bits.bit[i]; /* stop searching */ found = 1; break; } } if (!found) { /* referenced bit value does not exist */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } free(bits); goto error; } c = c + len; } if (make_canonical(ctx, LY_TYPE_BITS, value_, bits, &type->info.bits.count) == -1) { free(bits); goto error; } if (store) { /* store the result */ val->bit = bits; *val_type = LY_TYPE_BITS; } else { free(bits); } break; case LY_TYPE_BOOL: if (value && !strcmp(value, "true")) { if (store) { val->bln = 1; } } else if (!value || strcmp(value, "false")) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); } goto error; } else { if (store) { val->bln = 0; } } if (store) { *val_type = LY_TYPE_BOOL; } break; case LY_TYPE_DEC64: if (!value || !value[0]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } ptr = value; if (parse_range_dec64(&ptr, type->info.dec64.dig, &num) || ptr[0]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } goto error; } if (!trusted && validate_length_range(2, 0, 0, num, type->info.dec64.dig, type, value, contextnode)) { goto error; } if (make_canonical(ctx, LY_TYPE_DEC64, value_, &num, &type->info.dec64.dig) == -1) { goto error; } if (store) { /* store the result */ val->dec64 = num; *val_type = LY_TYPE_DEC64; } break; case LY_TYPE_EMPTY: if (value && value[0]) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } goto error; } if (store) { *val_type = LY_TYPE_EMPTY; } break; case LY_TYPE_ENUM: /* locate enums structure with the enumeration definitions, * since YANG 1.1 allows restricted enums, it is the first * enum type with some explicit enum specification */ for (; !type->info.enums.count; type = &type->der->type); /* find matching enumeration value */ for (i = found = 0; i < type->info.enums.count; i++) { if (value && !strcmp(value, type->info.enums.enm[i].name)) { /* we have match, check if the value is enabled ... */ for (j = 0; !trusted && (j < type->info.enums.enm[i].iffeature_size); j++) { if (!resolve_iffeature(&type->info.enums.enm[i].iffeature[j])) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value); } LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Enum \"%s\" is disabled by its %d. if-feature condition.", value, j + 1); goto error; } } /* ... and store pointer to the definition */ if (store) { val->enm = &type->info.enums.enm[i]; *val_type = LY_TYPE_ENUM; } found = 1; break; } } if (!found) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, value ? value : ""); } goto error; } break; case LY_TYPE_IDENT: if (!value) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } if (xml) { ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* first, convert value into the json format, silently */ value = transform_xml2json(ctx, value, xml, 0, 0); ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!value) { /* invalid identityref format */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } /* the value has no prefix (default namespace), but the element's namespace has a prefix, find default namespace */ if (!strchr(value, ':') && xml->ns->prefix) { value = ident_val_add_module_prefix(value, xml, ctx); if (!value) { goto error; } } } else if (dflt) { ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* the value actually uses module's prefixes instead of the module names as in JSON format, * we have to convert it */ value = transform_schema2json(local_mod, value); ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!value) { /* invalid identityref format or it was already transformed, so ignore the error here */ value = lydict_insert(ctx, *value_, 0); } } else { value = lydict_insert(ctx, *value_, 0); } /* value is now in the dictionary, whether it differs from *value_ or not */ ident = resolve_identref(type, value, contextnode, local_mod, dflt); if (!ident) { lydict_remove(ctx, value); goto error; } else if (store) { /* store the result */ val->ident = ident; *val_type = LY_TYPE_IDENT; } /* the value is always changed and includes prefix */ if (dflt) { type->parent->flags |= LYS_DFLTJSON; } if (make_canonical(ctx, LY_TYPE_IDENT, &value, (void*)lys_main_module(local_mod)->name, NULL) == -1) { lydict_remove(ctx, value); goto error; } /* replace the old value with the new one (even if they may be the same) */ lydict_remove(ctx, *value_); *value_ = value; break; case LY_TYPE_INST: if (!value) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } if (xml) { ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* first, convert value into the json format, silently */ value = transform_xml2json(ctx, value, xml, 1, 1); ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!value) { /* invalid instance-identifier format */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } else if (ly_strequal(value, *value_, 1)) { /* we have actually created the same expression (prefixes are the same as the module names) * so we have just increased dictionary's refcount - fix it */ lydict_remove(ctx, value); } } else if (dflt) { /* turn logging off */ ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); /* the value actually uses module's prefixes instead of the module names as in JSON format, * we have to convert it */ value = transform_schema2json(local_mod, value); if (!value) { /* invalid identityref format or it was already transformed, so ignore the error here */ value = *value_; } else if (ly_strequal(value, *value_, 1)) { /* we have actually created the same expression (prefixes are the same as the module names) * so we have just increased dictionary's refcount - fix it */ lydict_remove(ctx, value); } /* turn logging back on */ ly_ilo_restore(NULL, prev_ilo, NULL, 0); } else { if ((c = make_canonical(ctx, LY_TYPE_INST, &value, NULL, NULL))) { if (c == -1) { goto error; } /* if a change occurred, value was removed from the dictionary so fix the pointers */ *value_ = value; } } if (store) { /* note that the data node is an unresolved instance-identifier */ val->instance = NULL; *val_type = LY_TYPE_INST; *val_flags |= LY_VALUE_UNRES; } if (!ly_strequal(value, *value_, 1)) { /* update the changed value */ lydict_remove(ctx, *value_); *value_ = value; /* we have to remember the conversion into JSON format to be able to print it in correct form */ if (dflt) { type->parent->flags |= LYS_DFLTJSON; } } break; case LY_TYPE_LEAFREF: if (!value) { if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, ""); } goto error; } /* it is called not only to get the final type, but mainly to update value to canonical or JSON form * if needed */ t = lyp_parse_value(&type->info.lref.target->type, value_, xml, leaf, attr, NULL, store, dflt, trusted); value = *value_; /* refresh possibly changed value */ if (!t) { /* already logged */ goto error; } if (store) { /* make the note that the data node is an unresolved leafref (value union was already filled) */ *val_flags |= LY_VALUE_UNRES; } type = t; break; case LY_TYPE_STRING: if (!trusted && validate_length_range(0, (value ? ly_strlen_utf8(value) : 0), 0, 0, 0, type, value, contextnode)) { goto error; } if (!trusted && validate_pattern(ctx, value, type, contextnode)) { goto error; } /* special handling of ietf-yang-types xpath1.0 */ for (tpdf = type->der; tpdf->module && (strcmp(tpdf->name, "xpath1.0") || strcmp(tpdf->module->name, "ietf-yang-types")); tpdf = tpdf->type.der); if (tpdf->module && xml) { /* convert value into the json format */ value = transform_xml2json(ctx, value ? value : "", xml, 1, 1); if (!value) { /* invalid instance-identifier format */ if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } if (!ly_strequal(value, *value_, 1)) { /* update the changed value */ lydict_remove(ctx, *value_); *value_ = value; } } if (store) { /* store the result */ val->string = value; *val_type = LY_TYPE_STRING; } break; case LY_TYPE_INT8: if (parse_int(value, __INT64_C(-128), __INT64_C(127), dflt ? 0 : 10, &num, contextnode) || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_INT8, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int8 = (int8_t)num; *val_type = LY_TYPE_INT8; } break; case LY_TYPE_INT16: if (parse_int(value, __INT64_C(-32768), __INT64_C(32767), dflt ? 0 : 10, &num, contextnode) || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_INT16, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int16 = (int16_t)num; *val_type = LY_TYPE_INT16; } break; case LY_TYPE_INT32: if (parse_int(value, __INT64_C(-2147483648), __INT64_C(2147483647), dflt ? 0 : 10, &num, contextnode) || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_INT32, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int32 = (int32_t)num; *val_type = LY_TYPE_INT32; } break; case LY_TYPE_INT64: if (parse_int(value, __INT64_C(-9223372036854775807) - __INT64_C(1), __INT64_C(9223372036854775807), dflt ? 0 : 10, &num, contextnode) || (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_INT64, value_, &num, NULL) == -1) { goto error; } if (store) { /* store the result */ val->int64 = num; *val_type = LY_TYPE_INT64; } break; case LY_TYPE_UINT8: if (parse_uint(value, __UINT64_C(255), dflt ? 0 : 10, &unum, contextnode) || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT8, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint8 = (uint8_t)unum; *val_type = LY_TYPE_UINT8; } break; case LY_TYPE_UINT16: if (parse_uint(value, __UINT64_C(65535), dflt ? 0 : 10, &unum, contextnode) || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT16, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint16 = (uint16_t)unum; *val_type = LY_TYPE_UINT16; } break; case LY_TYPE_UINT32: if (parse_uint(value, __UINT64_C(4294967295), dflt ? 0 : 10, &unum, contextnode) || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT32, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint32 = (uint32_t)unum; *val_type = LY_TYPE_UINT32; } break; case LY_TYPE_UINT64: if (parse_uint(value, __UINT64_C(18446744073709551615), dflt ? 0 : 10, &unum, contextnode) || (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) { goto error; } if (make_canonical(ctx, LY_TYPE_UINT64, value_, &unum, NULL) == -1) { goto error; } if (store) { /* store the result */ val->uint64 = unum; *val_type = LY_TYPE_UINT64; } break; case LY_TYPE_UNION: if (store) { /* unresolved union type */ memset(val, 0, sizeof(lyd_val)); *val_type = LY_TYPE_UNION; } if (type->info.uni.has_ptr_type) { /* we are not resolving anything here, only parsing, and in this case we cannot decide * the type without resolving it -> we return the union type (resolve it with resolve_union()) */ if (xml) { /* in case it should resolve into a instance-identifier, we can only do the JSON conversion here */ ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); val->string = transform_xml2json(ctx, value, xml, 1, 1); ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!val->string) { /* invalid instance-identifier format, likely some other type */ val->string = lydict_insert(ctx, value, 0); } } break; } t = NULL; found = 0; /* turn logging off, we are going to try to validate the value with all the types in order */ ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL); while ((t = lyp_get_next_union_type(type, t, &found))) { found = 0; ret = lyp_parse_value(t, value_, xml, leaf, attr, NULL, store, dflt, 0); if (ret) { /* we have the result */ type = ret; break; } if (store) { /* erase possible present and invalid value data */ lyd_free_value(*val, *val_type, *val_flags, t, *value_, NULL, NULL, NULL); memset(val, 0, sizeof(lyd_val)); } } /* turn logging back on */ ly_ilo_restore(NULL, prev_ilo, NULL, 0); if (!t) { /* not found */ if (store) { *val_type = 0; } if (leaf) { LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_ ? *value_ : "", itemname); } else { LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "", itemname, *value_); } goto error; } break; default: LOGINT(ctx); goto error; } /* search user types in case this value is supposed to be stored in a custom way */ if (store && type->der && type->der->module) { c = lytype_store(type->der->module, type->der->name, value_, val); if (c == -1) { goto error; } else if (!c) { *val_flags |= LY_VALUE_USER; } } /* free backup */ if (store) { lyd_free_value(old_val, old_val_type, old_val_flags, type, old_val_str, NULL, NULL, NULL); lydict_remove(ctx, old_val_str); } return type; error: /* restore the backup */ if (store) { *val = old_val; *val_type = old_val_type; *val_flags = old_val_flags; lydict_remove(ctx, old_val_str); } return NULL; } /* does not log, cannot fail */ struct lys_type * lyp_get_next_union_type(struct lys_type *type, struct lys_type *prev_type, int *found) { unsigned int i; struct lys_type *ret = NULL; while (!type->info.uni.count) { assert(type->der); /* at least the direct union type has to have type specified */ type = &type->der->type; } for (i = 0; i < type->info.uni.count; ++i) { if (type->info.uni.types[i].base == LY_TYPE_UNION) { ret = lyp_get_next_union_type(&type->info.uni.types[i], prev_type, found); if (ret) { break; } continue; } if (!prev_type || *found) { ret = &type->info.uni.types[i]; break; } if (&type->info.uni.types[i] == prev_type) { *found = 1; } } return ret; } /* ret 0 - ret set, ret 1 - ret not set, no log, ret -1 - ret not set, fatal error */ int lyp_fill_attr(struct ly_ctx *ctx, struct lyd_node *parent, const char *module_ns, const char *module_name, const char *attr_name, const char *attr_value, struct lyxml_elem *xml, int options, struct lyd_attr **ret) { const struct lys_module *mod = NULL; const struct lys_submodule *submod = NULL; struct lys_type **type; struct lyd_attr *dattr; int pos, i, j, k; /* first, get module where the annotation should be defined */ if (module_ns) { mod = (struct lys_module *)ly_ctx_get_module_by_ns(ctx, module_ns, NULL, 0); } else if (module_name) { mod = (struct lys_module *)ly_ctx_get_module(ctx, module_name, NULL, 0); } else { LOGINT(ctx); return -1; } if (!mod) { return 1; } /* then, find the appropriate annotation definition */ pos = -1; for (i = 0, j = 0; i < mod->ext_size; i = i + j + 1) { j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &mod->ext[i], mod->ext_size - i); if (j == -1) { break; } if (ly_strequal(mod->ext[i + j]->arg_value, attr_name, 0)) { pos = i + j; break; } } /* try submodules */ if (pos == -1) { for (k = 0; k < mod->inc_size; ++k) { submod = mod->inc[k].submodule; for (i = 0, j = 0; i < submod->ext_size; i = i + j + 1) { j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &submod->ext[i], submod->ext_size - i); if (j == -1) { break; } if (ly_strequal(submod->ext[i + j]->arg_value, attr_name, 0)) { pos = i + j; break; } } } } if (pos == -1) { return 1; } /* allocate and fill the data attribute structure */ dattr = calloc(1, sizeof *dattr); LY_CHECK_ERR_RETURN(!dattr, LOGMEM(ctx), -1); dattr->parent = parent; dattr->next = NULL; dattr->annotation = submod ? (struct lys_ext_instance_complex *)submod->ext[pos] : (struct lys_ext_instance_complex *)mod->ext[pos]; dattr->name = lydict_insert(ctx, attr_name, 0); dattr->value_str = lydict_insert(ctx, attr_value, 0); /* the value is here converted to a JSON format if needed in case of LY_TYPE_IDENT and LY_TYPE_INST or to a * canonical form of the value */ type = lys_ext_complex_get_substmt(LY_STMT_TYPE, dattr->annotation, NULL); if (!type || !lyp_parse_value(*type, &dattr->value_str, xml, NULL, dattr, NULL, 1, 0, options & LYD_OPT_TRUSTED)) { lydict_remove(ctx, dattr->name); lydict_remove(ctx, dattr->value_str); free(dattr); return -1; } *ret = dattr; return 0; } int lyp_check_edit_attr(struct ly_ctx *ctx, struct lyd_attr *attr, struct lyd_node *parent, int *editbits) { struct lyd_attr *last = NULL; int bits = 0; /* 0x01 - insert attribute present * 0x02 - insert is relative (before or after) * 0x04 - value attribute present * 0x08 - key attribute present * 0x10 - operation attribute present * 0x20 - operation not allowing insert attribute (delete or remove) */ LY_TREE_FOR(attr, attr) { last = NULL; if (!strcmp(attr->annotation->arg_value, "operation") && !strcmp(attr->annotation->module->name, "ietf-netconf")) { if (bits & 0x10) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "operation attributes", parent->schema->name); return -1; } bits |= 0x10; if (attr->value.enm->value >= 3) { /* delete or remove */ bits |= 0x20; } } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ !strcmp(attr->annotation->arg_value, "insert")) { /* 'insert' attribute present */ if (!(parent->schema->flags & LYS_USERORDERED)) { /* ... but it is not expected */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); return -1; } if (bits & 0x01) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "insert attributes", parent->schema->name); return -1; } bits |= 0x01; if (attr->value.enm->value >= 2) { /* before or after */ bits |= 0x02; } last = attr; } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ !strcmp(attr->annotation->arg_value, "value")) { if (bits & 0x04) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "value attributes", parent->schema->name); return -1; } else if (parent->schema->nodetype & LYS_LIST) { LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); return -1; } bits |= 0x04; last = attr; } else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */ !strcmp(attr->annotation->arg_value, "key")) { if (bits & 0x08) { LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "key attributes", parent->schema->name); return -1; } else if (parent->schema->nodetype & LYS_LEAFLIST) { LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name); return -1; } bits |= 0x08; last = attr; } } /* report errors */ if (last && (!(parent->schema->nodetype & (LYS_LEAFLIST | LYS_LIST)) || !(parent->schema->flags & LYS_USERORDERED))) { /* moving attributes in wrong elements (not an user ordered list or not a list at all) */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, last->name); return -1; } else if (bits == 3) { /* 0x01 | 0x02 - relative position, but value/key is missing */ if (parent->schema->nodetype & LYS_LIST) { LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "key", parent->schema->name); } else { /* LYS_LEAFLIST */ LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "value", parent->schema->name); } return -1; } else if ((bits & (0x04 | 0x08)) && !(bits & 0x02)) { /* key/value without relative position */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, (bits & 0x04) ? "value" : "key"); return -1; } else if ((bits & 0x21) == 0x21) { /* insert in delete/remove */ LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert"); return -1; } if (editbits) { *editbits = bits; } return 0; } /* does not log */ static int dup_identity_check(const char *id, struct lys_ident *ident, uint32_t size) { uint32_t i; for (i = 0; i < size; i++) { if (ly_strequal(id, ident[i].name, 1)) { /* name collision */ return EXIT_FAILURE; } } return EXIT_SUCCESS; } int dup_identities_check(const char *id, struct lys_module *module) { struct lys_module *mainmod; int i; if (dup_identity_check(id, module->ident, module->ident_size)) { LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); return EXIT_FAILURE; } /* check identity in submodules */ mainmod = lys_main_module(module); for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; ++i) { if (dup_identity_check(id, mainmod->inc[i].submodule->ident, mainmod->inc[i].submodule->ident_size)) { LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id); return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* does not log */ int dup_typedef_check(const char *type, struct lys_tpdf *tpdf, int size) { int i; for (i = 0; i < size; i++) { if (!strcmp(type, tpdf[i].name)) { /* name collision */ return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* does not log */ static int dup_feature_check(const char *id, struct lys_module *module) { int i; for (i = 0; i < module->features_size; i++) { if (!strcmp(id, module->features[i].name)) { return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* does not log */ static int dup_prefix_check(const char *prefix, struct lys_module *module) { int i; if (module->prefix && !strcmp(module->prefix, prefix)) { return EXIT_FAILURE; } for (i = 0; i < module->imp_size; i++) { if (!strcmp(module->imp[i].prefix, prefix)) { return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* logs directly */ int lyp_check_identifier(struct ly_ctx *ctx, const char *id, enum LY_IDENT type, struct lys_module *module, struct lys_node *parent) { int i, j; int size; struct lys_tpdf *tpdf; struct lys_node *node; struct lys_module *mainmod; struct lys_submodule *submod; assert(ctx && id); /* check id syntax */ if (!(id[0] >= 'A' && id[0] <= 'Z') && !(id[0] >= 'a' && id[0] <= 'z') && id[0] != '_') { LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid start character"); return EXIT_FAILURE; } for (i = 1; id[i]; i++) { if (!(id[i] >= 'A' && id[i] <= 'Z') && !(id[i] >= 'a' && id[i] <= 'z') && !(id[i] >= '0' && id[i] <= '9') && id[i] != '_' && id[i] != '-' && id[i] != '.') { LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid character"); return EXIT_FAILURE; } } if (i > 64) { LOGWRN(ctx, "Identifier \"%s\" is long, you should use something shorter.", id); } switch (type) { case LY_IDENT_NAME: /* check uniqueness of the node within its siblings */ if (!parent) { break; } LY_TREE_FOR(parent->child, node) { if (ly_strequal(node->name, id, 1)) { LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "name duplication"); return EXIT_FAILURE; } } break; case LY_IDENT_TYPE: assert(module); mainmod = lys_main_module(module); /* check collision with the built-in types */ if (!strcmp(id, "binary") || !strcmp(id, "bits") || !strcmp(id, "boolean") || !strcmp(id, "decimal64") || !strcmp(id, "empty") || !strcmp(id, "enumeration") || !strcmp(id, "identityref") || !strcmp(id, "instance-identifier") || !strcmp(id, "int8") || !strcmp(id, "int16") || !strcmp(id, "int32") || !strcmp(id, "int64") || !strcmp(id, "leafref") || !strcmp(id, "string") || !strcmp(id, "uint8") || !strcmp(id, "uint16") || !strcmp(id, "uint32") || !strcmp(id, "uint64") || !strcmp(id, "union")) { LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, id, "typedef"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Typedef name duplicates a built-in type."); return EXIT_FAILURE; } /* check locally scoped typedefs (avoid name shadowing) */ for (; parent; parent = lys_parent(parent)) { switch (parent->nodetype) { case LYS_CONTAINER: size = ((struct lys_node_container *)parent)->tpdf_size; tpdf = ((struct lys_node_container *)parent)->tpdf; break; case LYS_LIST: size = ((struct lys_node_list *)parent)->tpdf_size; tpdf = ((struct lys_node_list *)parent)->tpdf; break; case LYS_GROUPING: size = ((struct lys_node_grp *)parent)->tpdf_size; tpdf = ((struct lys_node_grp *)parent)->tpdf; break; default: continue; } if (dup_typedef_check(id, tpdf, size)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); return EXIT_FAILURE; } } /* check top-level names */ if (dup_typedef_check(id, module->tpdf, module->tpdf_size)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); return EXIT_FAILURE; } /* check submodule's top-level names */ for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { if (dup_typedef_check(id, mainmod->inc[i].submodule->tpdf, mainmod->inc[i].submodule->tpdf_size)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id); return EXIT_FAILURE; } } break; case LY_IDENT_PREFIX: assert(module); /* check the module itself */ if (dup_prefix_check(id, module)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "prefix", id); return EXIT_FAILURE; } break; case LY_IDENT_FEATURE: assert(module); mainmod = lys_main_module(module); /* check feature name uniqueness*/ /* check features in the current module */ if (dup_feature_check(id, module)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); return EXIT_FAILURE; } /* and all its submodules */ for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) { if (dup_feature_check(id, (struct lys_module *)mainmod->inc[i].submodule)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id); return EXIT_FAILURE; } } break; case LY_IDENT_EXTENSION: assert(module); mainmod = lys_main_module(module); /* check extension name uniqueness in the main module ... */ for (i = 0; i < mainmod->extensions_size; i++) { if (ly_strequal(id, mainmod->extensions[i].name, 1)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); return EXIT_FAILURE; } } /* ... and all its submodules */ for (j = 0; j < mainmod->inc_size && mainmod->inc[j].submodule; j++) { submod = mainmod->inc[j].submodule; /* shortcut */ for (i = 0; i < submod->extensions_size; i++) { if (ly_strequal(id, submod->extensions[i].name, 1)) { LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id); return EXIT_FAILURE; } } } break; default: /* no check required */ break; } return EXIT_SUCCESS; } /* logs directly */ int lyp_check_date(struct ly_ctx *ctx, const char *date) { int i; struct tm tm, tm_; char *r; assert(date); /* check format */ for (i = 0; i < LY_REV_SIZE - 1; i++) { if (i == 4 || i == 7) { if (date[i] != '-') { goto error; } } else if (!isdigit(date[i])) { goto error; } } /* check content, e.g. 2018-02-31 */ memset(&tm, 0, sizeof tm); r = strptime(date, "%Y-%m-%d", &tm); if (!r || r != &date[LY_REV_SIZE - 1]) { goto error; } /* set some arbitrary non-0 value in case DST changes, it could move the day otherwise */ tm.tm_hour = 12; memcpy(&tm_, &tm, sizeof tm); mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */ if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */ /* checking days is enough, since other errors * have been checked by strptime() */ goto error; } return EXIT_SUCCESS; error: LOGVAL(ctx, LYE_INDATE, LY_VLOG_NONE, NULL, date); return EXIT_FAILURE; } /** * @return * NULL - success * root - not yet resolvable * other node - mandatory node under the root */ static const struct lys_node * lyp_check_mandatory_(const struct lys_node *root) { int mand_flag = 0; const struct lys_node *iter = NULL; while ((iter = lys_getnext(iter, root, NULL, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHUSES | LYS_GETNEXT_INTOUSES | LYS_GETNEXT_INTONPCONT | LYS_GETNEXT_NOSTATECHECK))) { if (iter->nodetype == LYS_USES) { if (!((struct lys_node_uses *)iter)->grp) { /* not yet resolved uses */ return root; } else { /* go into uses */ continue; } } if (iter->nodetype == LYS_CHOICE) { /* skip it, it was already checked for direct mandatory node in default */ continue; } if (iter->nodetype == LYS_LIST) { if (((struct lys_node_list *)iter)->min) { mand_flag = 1; } } else if (iter->nodetype == LYS_LEAFLIST) { if (((struct lys_node_leaflist *)iter)->min) { mand_flag = 1; } } else if (iter->flags & LYS_MAND_TRUE) { mand_flag = 1; } if (mand_flag) { return iter; } } return NULL; } /* logs directly */ int lyp_check_mandatory_augment(struct lys_node_augment *aug, const struct lys_node *target) { const struct lys_node *node; if (aug->when || target->nodetype == LYS_CHOICE) { /* - mandatory nodes in new cases are ok; * clarification from YANG 1.1 - augmentation can add mandatory nodes when it is * conditional with a when statement */ return EXIT_SUCCESS; } if ((node = lyp_check_mandatory_((struct lys_node *)aug))) { if (node != (struct lys_node *)aug) { LOGVAL(target->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); LOGVAL(target->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Mandatory node \"%s\" appears in augment of \"%s\" without when condition.", node->name, aug->target_name); return -1; } return EXIT_FAILURE; } return EXIT_SUCCESS; } /** * @brief check that a mandatory node is not directly under the default case. * @param[in] node choice with default node * @return EXIT_SUCCESS if the constraint is fulfilled, EXIT_FAILURE otherwise */ int lyp_check_mandatory_choice(struct lys_node *node) { const struct lys_node *mand, *dflt = ((struct lys_node_choice *)node)->dflt; if ((mand = lyp_check_mandatory_(dflt))) { if (mand != dflt) { LOGVAL(node->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory"); LOGVAL(node->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Mandatory node \"%s\" is directly under the default case \"%s\" of the \"%s\" choice.", mand->name, dflt->name, node->name); return -1; } return EXIT_FAILURE; } return EXIT_SUCCESS; } /** * @brief Check status for invalid combination. * * @param[in] flags1 Flags of the referencing node. * @param[in] mod1 Module of the referencing node, * @param[in] name1 Schema node name of the referencing node. * @param[in] flags2 Flags of the referenced node. * @param[in] mod2 Module of the referenced node, * @param[in] name2 Schema node name of the referenced node. * @return EXIT_SUCCES on success, EXIT_FAILURE on invalid reference. */ int lyp_check_status(uint16_t flags1, struct lys_module *mod1, const char *name1, uint16_t flags2, struct lys_module *mod2, const char *name2, const struct lys_node *node) { uint16_t flg1, flg2; flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR; flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR; if ((flg1 < flg2) && (lys_main_module(mod1) == lys_main_module(mod2))) { LOGVAL(mod1->ctx, LYE_INSTATUS, node ? LY_VLOG_LYS : LY_VLOG_NONE, node, flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1, "references", flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2); return EXIT_FAILURE; } return EXIT_SUCCESS; } void lyp_del_includedup(struct lys_module *mod, int free_subs) { struct ly_modules_list *models = &mod->ctx->models; uint8_t i; assert(mod && !mod->type); if (models->parsed_submodules_count) { for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); if (models->parsed_submodules[i] == mod) { if (free_subs) { for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i) { lys_sub_module_remove_devs_augs((struct lys_module *)models->parsed_submodules[i]); lys_submodule_module_data_free((struct lys_submodule *)models->parsed_submodules[i]); lys_submodule_free((struct lys_submodule *)models->parsed_submodules[i], NULL); } } models->parsed_submodules_count = i; if (!models->parsed_submodules_count) { free(models->parsed_submodules); models->parsed_submodules = NULL; } } } } static void lyp_add_includedup(struct lys_module *sub_mod, struct lys_submodule *parsed_submod) { struct ly_modules_list *models = &sub_mod->ctx->models; int16_t i; /* store main module if first include */ if (models->parsed_submodules_count) { for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i); } else { i = -1; } if ((i == -1) || (models->parsed_submodules[i] != lys_main_module(sub_mod))) { ++models->parsed_submodules_count; models->parsed_submodules = ly_realloc(models->parsed_submodules, models->parsed_submodules_count * sizeof *models->parsed_submodules); LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); models->parsed_submodules[models->parsed_submodules_count - 1] = lys_main_module(sub_mod); } /* store parsed submodule */ ++models->parsed_submodules_count; models->parsed_submodules = ly_realloc(models->parsed_submodules, models->parsed_submodules_count * sizeof *models->parsed_submodules); LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), ); models->parsed_submodules[models->parsed_submodules_count - 1] = (struct lys_module *)parsed_submod; } /* * types: 0 - include, 1 - import */ static int lyp_check_circmod(struct lys_module *module, const char *value, int type) { LY_ECODE code = type ? LYE_CIRC_IMPORTS : LYE_CIRC_INCLUDES; struct ly_modules_list *models = &module->ctx->models; uint8_t i; /* include/import itself */ if (ly_strequal(module->name, value, 1)) { LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); return -1; } /* currently parsed modules */ for (i = 0; i < models->parsing_sub_modules_count; i++) { if (ly_strequal(models->parsing_sub_modules[i]->name, value, 1)) { LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value); return -1; } } return 0; } int lyp_check_circmod_add(struct lys_module *module) { struct ly_modules_list *models = &module->ctx->models; /* storing - enlarge the list of modules being currently parsed */ ++models->parsing_sub_modules_count; models->parsing_sub_modules = ly_realloc(models->parsing_sub_modules, models->parsing_sub_modules_count * sizeof *models->parsing_sub_modules); LY_CHECK_ERR_RETURN(!models->parsing_sub_modules, LOGMEM(module->ctx), -1); models->parsing_sub_modules[models->parsing_sub_modules_count - 1] = module; return 0; } void lyp_check_circmod_pop(struct ly_ctx *ctx) { if (!ctx->models.parsing_sub_modules_count) { LOGINT(ctx); return; } /* update the list of currently being parsed modules */ ctx->models.parsing_sub_modules_count--; if (!ctx->models.parsing_sub_modules_count) { free(ctx->models.parsing_sub_modules); ctx->models.parsing_sub_modules = NULL; } } /* * -1 - error - invalid duplicities) * 0 - success, no duplicity * 1 - success, valid duplicity found and stored in *sub */ static int lyp_check_includedup(struct lys_module *mod, const char *name, struct lys_include *inc, struct lys_submodule **sub) { struct lys_module **parsed_sub = mod->ctx->models.parsed_submodules; uint8_t i, parsed_sub_count = mod->ctx->models.parsed_submodules_count; assert(sub); for (i = 0; i < mod->inc_size; ++i) { if (ly_strequal(mod->inc[i].submodule->name, name, 1)) { /* the same module is already included in the same module - error */ LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Submodule \"%s\" included twice in the same module \"%s\".", name, mod->name); return -1; } } if (parsed_sub_count) { assert(!parsed_sub[0]->type); for (i = parsed_sub_count - 1; parsed_sub[i]->type; --i) { if (ly_strequal(parsed_sub[i]->name, name, 1)) { /* check revisions, including multiple revisions of a single module is error */ if (inc->rev[0] && (!parsed_sub[i]->rev_size || strcmp(parsed_sub[i]->rev[0].date, inc->rev))) { /* the already included submodule has * - no revision, but here we require some * - different revision than the one required here */ LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include"); LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Including multiple revisions of submodule \"%s\".", name); return -1; } /* the same module is already included in some other submodule, return it */ (*sub) = (struct lys_submodule *)parsed_sub[i]; return 1; } } } /* no duplicity found */ return 0; } /* returns: * 0 - inc successfully filled * -1 - error */ int lyp_check_include(struct lys_module *module, const char *value, struct lys_include *inc, struct unres_schema *unres) { int i; /* check that the submodule was not included yet */ i = lyp_check_includedup(module, value, inc, &inc->submodule); if (i == -1) { return -1; } else if (i == 1) { return 0; } /* submodule is not yet loaded */ /* circular include check */ if (lyp_check_circmod(module, value, 0)) { return -1; } /* try to load the submodule */ inc->submodule = (struct lys_submodule *)ly_ctx_load_sub_module(module->ctx, module, value, inc->rev[0] ? inc->rev : NULL, 1, unres); /* check the result */ if (!inc->submodule) { if (ly_errno != LY_EVALID) { LOGVAL(module->ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "include"); } LOGERR(module->ctx, LY_EVALID, "Including \"%s\" module into \"%s\" failed.", value, module->name); return -1; } /* check the revision */ if (inc->rev[0] && inc->submodule->rev_size && strcmp(inc->rev, inc->submodule->rev[0].date)) { LOGERR(module->ctx, LY_EVALID, "\"%s\" include of submodule \"%s\" in revision \"%s\" not found.", module->name, value, inc->rev); unres_schema_free((struct lys_module *)inc->submodule, &unres, 0); lys_sub_module_remove_devs_augs((struct lys_module *)inc->submodule); lys_submodule_module_data_free((struct lys_submodule *)inc->submodule); lys_submodule_free(inc->submodule, NULL); inc->submodule = NULL; return -1; } /* store the submodule as successfully parsed */ lyp_add_includedup(module, inc->submodule); return 0; } static int lyp_check_include_missing_recursive(struct lys_module *main_module, struct lys_submodule *sub) { uint8_t i, j; void *reallocated; int ret = 0, tmp; struct ly_ctx *ctx = main_module->ctx; for (i = 0; i < sub->inc_size; i++) { /* check that the include is also present in the main module */ for (j = 0; j < main_module->inc_size; j++) { if (main_module->inc[j].submodule == sub->inc[i].submodule) { break; } } if (j == main_module->inc_size) { /* match not found */ if (main_module->version >= LYS_VERSION_1_1) { LOGVAL(ctx, LYE_MISSSTMT, LY_VLOG_NONE, NULL, "include"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", main_module->name, sub->inc[i].submodule->name, sub->name); /* now we should return error, but due to the issues with freeing the module, we actually have * to go through the all includes and, as in case of 1.0, add them into the main module and fail * at the end when all the includes are in the main module and we can free them */ ret = 1; } else { /* not strictly an error in YANG 1.0 */ LOGWRN(ctx, "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".", main_module->name, sub->inc[i].submodule->name, sub->name); LOGWRN(ctx, "To avoid further issues, adding submodule \"%s\" into the main module \"%s\".", sub->inc[i].submodule->name, main_module->name); /* but since it is a good practise and because we expect all the includes in the main module * when searching it and also when freeing the module, put it into it */ } main_module->inc_size++; reallocated = realloc(main_module->inc, main_module->inc_size * sizeof *main_module->inc); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), 1); main_module->inc = reallocated; memset(&main_module->inc[main_module->inc_size - 1], 0, sizeof *main_module->inc); /* to avoid unexpected consequences, copy just a link to the submodule and the revision, * all other substatements of the include are ignored */ memcpy(&main_module->inc[main_module->inc_size - 1].rev, sub->inc[i].rev, LY_REV_SIZE - 1); main_module->inc[main_module->inc_size - 1].submodule = sub->inc[i].submodule; } /* recursion */ tmp = lyp_check_include_missing_recursive(main_module, sub->inc[i].submodule); if (!ret && tmp) { ret = 1; } } return ret; } int lyp_check_include_missing(struct lys_module *main_module) { int ret = 0; uint8_t i; /* in YANG 1.1, all the submodules must be in the main module, check it even for * 1.0 where it will be printed as warning and the include will be added into the main module */ for (i = 0; i < main_module->inc_size; i++) { if (lyp_check_include_missing_recursive(main_module, main_module->inc[i].submodule)) { ret = 1; } } return ret; } /* returns: * 0 - imp successfully filled * -1 - error, imp not cleaned */ int lyp_check_import(struct lys_module *module, const char *value, struct lys_import *imp) { int i; struct lys_module *dup = NULL; struct ly_ctx *ctx = module->ctx; /* check for importing a single module in multiple revisions */ for (i = 0; i < module->imp_size; i++) { if (!module->imp[i].module) { /* skip the not yet filled records */ continue; } if (ly_strequal(module->imp[i].module->name, value, 1)) { /* check revisions, including multiple revisions of a single module is error */ if (imp->rev[0] && (!module->imp[i].module->rev_size || strcmp(module->imp[i].module->rev[0].date, imp->rev))) { /* the already imported module has * - no revision, but here we require some * - different revision than the one required here */ LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); return -1; } else if (!imp->rev[0]) { /* no revision, remember the duplication, but check revisions after loading the module * because the current revision can be the same (then it is ok) or it can differ (then it * is error */ dup = module->imp[i].module; break; } /* there is duplication, but since prefixes differs (checked in caller of this function), * it is ok */ imp->module = module->imp[i].module; return 0; } } /* circular import check */ if (lyp_check_circmod(module, value, 1)) { return -1; } /* load module - in specific situations it tries to get the module from the context */ imp->module = (struct lys_module *)ly_ctx_load_sub_module(module->ctx, NULL, value, imp->rev[0] ? imp->rev : NULL, module->ctx->models.flags & LY_CTX_ALLIMPLEMENTED ? 1 : 0, NULL); /* check the result */ if (!imp->module) { LOGERR(ctx, LY_EVALID, "Importing \"%s\" module into \"%s\" failed.", value, module->name); return -1; } if (imp->rev[0] && imp->module->rev_size && strcmp(imp->rev, imp->module->rev[0].date)) { LOGERR(ctx, LY_EVALID, "\"%s\" import of module \"%s\" in revision \"%s\" not found.", module->name, value, imp->rev); return -1; } if (dup) { /* check the revisions */ if ((dup != imp->module) || (dup->rev_size != imp->module->rev_size && (!dup->rev_size || imp->module->rev_size)) || (dup->rev_size && strcmp(dup->rev[0].date, imp->module->rev[0].date))) { /* - modules are not the same * - one of modules has no revision (except they both has no revision) * - revisions of the modules are not the same */ LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import"); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value); return -1; } else { LOGWRN(ctx, "Module \"%s\" is imported by \"%s\" multiple times with different prefixes.", dup->name, module->name); } } return 0; } /* * put the newest revision to the first position */ void lyp_sort_revisions(struct lys_module *module) { uint8_t i, r; struct lys_revision rev; for (i = 1, r = 0; i < module->rev_size; i++) { if (strcmp(module->rev[i].date, module->rev[r].date) > 0) { r = i; } } if (r) { /* the newest revision is not on position 0, switch them */ memcpy(&rev, &module->rev[0], sizeof rev); memcpy(&module->rev[0], &module->rev[r], sizeof rev); memcpy(&module->rev[r], &rev, sizeof rev); } } void lyp_ext_instance_rm(struct ly_ctx *ctx, struct lys_ext_instance ***ext, uint8_t *size, uint8_t index) { uint8_t i; lys_extension_instances_free(ctx, (*ext)[index]->ext, (*ext)[index]->ext_size, NULL); lydict_remove(ctx, (*ext)[index]->arg_value); free((*ext)[index]); /* move the rest of the array */ for (i = index + 1; i < (*size); i++) { (*ext)[i - 1] = (*ext)[i]; } /* clean the last cell in the array structure */ (*ext)[(*size) - 1] = NULL; /* the array is not reallocated here, just change its size */ (*size) = (*size) - 1; if (!(*size)) { /* ext array is empty */ free((*ext)); ext = NULL; } } static int lyp_rfn_apply_ext_(struct lys_refine *rfn, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) { struct ly_ctx *ctx; int m, n; struct lys_ext_instance *new; void *reallocated; ctx = target->module->ctx; /* shortcut */ m = n = -1; while ((m = lys_ext_iter(rfn->ext, rfn->ext_size, m + 1, substmt)) != -1) { /* refine's substatement includes extensions, copy them to the target, replacing the previous * substatement's extensions if any. In case of refining the extension itself, we are going to * replace only the same extension (pointing to the same definition) */ if (substmt == LYEXT_SUBSTMT_SELF && rfn->ext[m]->def != extdef) { continue; } /* get the index of the extension to replace in the target node */ do { n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); /* TODO cover complex extension instances */ if (n == -1) { /* nothing to replace, we are going to add it - reallocate */ new = malloc(sizeof **target->ext); LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); target->ext = reallocated; target->ext_size++; /* init */ n = target->ext_size - 1; target->ext[n] = new; target->ext[n]->parent = target; target->ext[n]->parent_type = LYEXT_PAR_NODE; target->ext[n]->flags = 0; target->ext[n]->insubstmt = substmt; target->ext[n]->priv = NULL; target->ext[n]->nodetype = LYS_EXT; target->ext[n]->module = target->module; } else { /* replacing - first remove the allocated data from target */ lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); lydict_remove(ctx, target->ext[n]->arg_value); } /* common part for adding and replacing */ target->ext[n]->def = rfn->ext[m]->def; /* parent and parent_type do not change */ target->ext[n]->arg_value = lydict_insert(ctx, rfn->ext[m]->arg_value, 0); /* flags do not change */ target->ext[n]->ext_size = rfn->ext[m]->ext_size; lys_ext_dup(ctx, target->module, rfn->ext[m]->ext, rfn->ext[m]->ext_size, target, LYEXT_PAR_NODE, &target->ext[n]->ext, 0, NULL); /* substmt does not change, but the index must be taken from the refine */ target->ext[n]->insubstmt_index = rfn->ext[m]->insubstmt_index; } /* remove the rest of extensions belonging to the original substatement in the target node */ while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { if (substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef) { /* keep this extension */ continue; } /* remove the item */ lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); --n; } return EXIT_SUCCESS; } /* * apply extension instances defined under refine's substatements. * It cannot be done immediately when applying the refine because there can be * still unresolved data (e.g. type) and mainly the targeted extension instances. */ int lyp_rfn_apply_ext(struct lys_module *module) { int i, k, a = 0; struct lys_node *root, *nextroot, *next, *node; struct lys_node *target; struct lys_node_uses *uses; struct lys_refine *rfn; struct ly_set *extset; /* refines in uses */ LY_TREE_FOR_SAFE(module->data, nextroot, root) { /* go through the data tree of the module and all the defined augments */ LY_TREE_DFS_BEGIN(root, next, node) { if (node->nodetype == LYS_USES) { uses = (struct lys_node_uses *)node; for (i = 0; i < uses->refine_size; i++) { if (!uses->refine[i].ext_size) { /* no extensions in refine */ continue; } rfn = &uses->refine[i]; /* shortcut */ /* get the target node */ target = NULL; resolve_descendant_schema_nodeid(rfn->target_name, uses->child, LYS_NO_RPC_NOTIF_NODE | LYS_ACTION | LYS_NOTIF, 0, (const struct lys_node **)&target); if (!target) { /* it should always succeed since the target_name was already resolved at least * once when the refine itself was being resolved */ LOGINT(module->ctx);; return EXIT_FAILURE; } /* extensions */ extset = ly_set_new(); k = -1; while ((k = lys_ext_iter(rfn->ext, rfn->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { ly_set_add(extset, rfn->ext[k]->def, 0); } for (k = 0; (unsigned int)k < extset->number; k++) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { ly_set_free(extset); return EXIT_FAILURE; } } ly_set_free(extset); /* description */ if (rfn->dsc && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DESCRIPTION, NULL)) { return EXIT_FAILURE; } /* reference */ if (rfn->ref && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_REFERENCE, NULL)) { return EXIT_FAILURE; } /* config, in case of notification or rpc/action{notif, the config is not applicable * (there is no config status) */ if ((rfn->flags & LYS_CONFIG_MASK) && (target->flags & LYS_CONFIG_MASK)) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_CONFIG, NULL)) { return EXIT_FAILURE; } } /* default value */ if (rfn->dflt_size && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { return EXIT_FAILURE; } /* mandatory */ if (rfn->flags & LYS_MAND_MASK) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { return EXIT_FAILURE; } } /* presence */ if ((target->nodetype & LYS_CONTAINER) && rfn->mod.presence) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_PRESENCE, NULL)) { return EXIT_FAILURE; } } /* min/max */ if (rfn->flags & LYS_RFN_MINSET) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MIN, NULL)) { return EXIT_FAILURE; } } if (rfn->flags & LYS_RFN_MAXSET) { if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MAX, NULL)) { return EXIT_FAILURE; } } /* must and if-feature contain extensions on their own, not needed to be solved here */ if (target->ext_size) { /* the allocated target's extension array can be now longer than needed in case * there is less refine substatement's extensions than in original. Since we are * going to reduce or keep the same memory, it is not necessary to test realloc's result */ target->ext = realloc(target->ext, target->ext_size * sizeof *target->ext); } } } LY_TREE_DFS_END(root, next, node) } if (!nextroot && a < module->augment_size) { nextroot = module->augment[a].child; a++; } } return EXIT_SUCCESS; } /* * check mandatory substatements defined under extension instances. */ int lyp_mand_check_ext(struct lys_ext_instance_complex *ext, const char *ext_name) { void *p; int i; struct ly_ctx *ctx = ext->module->ctx; /* check for mandatory substatements */ for (i = 0; ext->substmt[i].stmt; i++) { if (ext->substmt[i].cardinality == LY_STMT_CARD_OPT || ext->substmt[i].cardinality == LY_STMT_CARD_ANY) { /* not a mandatory */ continue; } else if (ext->substmt[i].cardinality == LY_STMT_CARD_SOME) { goto array; } /* * LY_STMT_ORDEREDBY - not checked, has a default value which is the same as explicit system order * LY_STMT_MODIFIER, LY_STMT_STATUS, LY_STMT_MANDATORY, LY_STMT_CONFIG - checked, but mandatory requirement * does not make sense since there is also a default value specified */ switch (ext->substmt[i].stmt) { case LY_STMT_ORDEREDBY: /* always ok */ break; case LY_STMT_REQINSTANCE: case LY_STMT_DIGITS: case LY_STMT_MODIFIER: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!*(uint8_t*)p) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; case LY_STMT_STATUS: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(uint16_t*)p & LYS_STATUS_MASK)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; case LY_STMT_MANDATORY: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(uint16_t*)p & LYS_MAND_MASK)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; case LY_STMT_CONFIG: p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(uint16_t*)p & LYS_CONFIG_MASK)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; default: array: /* stored as a pointer */ p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL); if (!(*(void**)p)) { LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name); goto error; } break; } } return EXIT_SUCCESS; error: return EXIT_FAILURE; } static int lyp_deviate_del_ext(struct lys_node *target, struct lys_ext_instance *ext) { int n = -1, found = 0; char *path; while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, ext->insubstmt)) != -1) { if (target->ext[n]->def != ext->def) { continue; } if (ext->def->argument) { /* check matching arguments */ if (!ly_strequal(target->ext[n]->arg_value, ext->arg_value, 1)) { continue; } } /* we have the matching extension - remove it */ ++found; lyp_ext_instance_rm(target->module->ctx, &target->ext, &target->ext_size, n); --n; } if (!found) { path = lys_path(target, LYS_PATH_FIRST_PREFIX); LOGERR(target->module->ctx, LY_EVALID, "Extension deviation: extension \"%s\" to delete not found in \"%s\".", ext->def->name, path) free(path); return EXIT_FAILURE; } return EXIT_SUCCESS; } static int lyp_deviate_apply_ext(struct lys_deviate *dev, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef) { struct ly_ctx *ctx; int m, n; struct lys_ext_instance *new; void *reallocated; /* LY_DEVIATE_ADD and LY_DEVIATE_RPL are very similar so they are implement the same way - in replacing, * there can be some extension instances in the target, in case of adding, there should not be any so we * will be just adding. */ ctx = target->module->ctx; /* shortcut */ m = n = -1; while ((m = lys_ext_iter(dev->ext, dev->ext_size, m + 1, substmt)) != -1) { /* deviate and its substatements include extensions, copy them to the target, replacing the previous * extensions if any. In case of deviating extension itself, we have to deviate only the same type * of the extension as specified in the deviation */ if (substmt == LYEXT_SUBSTMT_SELF && dev->ext[m]->def != extdef) { continue; } if (substmt == LYEXT_SUBSTMT_SELF && dev->mod == LY_DEVIATE_ADD) { /* in case of adding extension, we will be replacing only the inherited extensions */ do { n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); } while (n != -1 && (target->ext[n]->def != extdef || !(target->ext[n]->flags & LYEXT_OPT_INHERIT))); } else { /* get the index of the extension to replace in the target node */ do { n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt); /* if we are applying extension deviation, we have to deviate only the same type of the extension */ } while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef); } if (n == -1) { /* nothing to replace, we are going to add it - reallocate */ new = malloc(sizeof **target->ext); LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext); LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE); target->ext = reallocated; target->ext_size++; n = target->ext_size - 1; } else { /* replacing - the original set of extensions is actually backuped together with the * node itself, so we are supposed only to free the allocated data here ... */ lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL); lydict_remove(ctx, target->ext[n]->arg_value); free(target->ext[n]); /* and prepare the new structure */ new = malloc(sizeof **target->ext); LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE); } /* common part for adding and replacing - fill the newly created / replaced cell */ target->ext[n] = new; target->ext[n]->def = dev->ext[m]->def; target->ext[n]->arg_value = lydict_insert(ctx, dev->ext[m]->arg_value, 0); target->ext[n]->flags = 0; target->ext[n]->parent = target; target->ext[n]->parent_type = LYEXT_PAR_NODE; target->ext[n]->insubstmt = substmt; target->ext[n]->insubstmt_index = dev->ext[m]->insubstmt_index; target->ext[n]->ext_size = dev->ext[m]->ext_size; lys_ext_dup(ctx, target->module, dev->ext[m]->ext, dev->ext[m]->ext_size, target, LYEXT_PAR_NODE, &target->ext[n]->ext, 1, NULL); target->ext[n]->nodetype = LYS_EXT; target->ext[n]->module = target->module; target->ext[n]->priv = NULL; /* TODO cover complex extension instances */ } /* remove the rest of extensions belonging to the original substatement in the target node, * due to possible reverting of the deviation effect, they are actually not removed, just moved * to the backup of the original node when the original node is backuped, here we just have to * free the replaced / deleted originals */ while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) { if (substmt == LYEXT_SUBSTMT_SELF) { /* if we are applying extension deviation, we are going to remove only * - the same type of the extension in case of replacing * - the same type of the extension which was inherited in case of adding * note - delete deviation is covered in lyp_deviate_del_ext */ if (target->ext[n]->def != extdef || (dev->mod == LY_DEVIATE_ADD && !(target->ext[n]->flags & LYEXT_OPT_INHERIT))) { /* keep this extension */ continue; } } /* remove the item */ lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n); --n; } return EXIT_SUCCESS; } /* * not-supported deviations are not processed since they affect the complete node, not just their substatements */ int lyp_deviation_apply_ext(struct lys_module *module) { int i, j, k; struct lys_deviate *dev; struct lys_node *target; struct ly_set *extset; for (i = 0; i < module->deviation_size; i++) { target = NULL; extset = NULL; j = resolve_schema_nodeid(module->deviation[i].target_name, NULL, module, &extset, 0, 0); if (j == -1) { return EXIT_FAILURE; } else if (!extset) { /* LY_DEVIATE_NO */ ly_set_free(extset); continue; } target = extset->set.s[0]; ly_set_free(extset); for (j = 0; j < module->deviation[i].deviate_size; j++) { dev = &module->deviation[i].deviate[j]; if (!dev->ext_size) { /* no extensions in deviate and its substatement, nothing to do here */ continue; } /* extensions */ if (dev->mod == LY_DEVIATE_DEL) { k = -1; while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { if (lyp_deviate_del_ext(target, dev->ext[k])) { return EXIT_FAILURE; } } /* In case of LY_DEVIATE_DEL, we are applying only extension deviation, removing * of the substatement's extensions was already done when the substatement was applied. * Extension deviation could not be applied by the parser since the extension could be unresolved, * which is not the issue of the other substatements. */ continue; } else { extset = ly_set_new(); k = -1; while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) { ly_set_add(extset, dev->ext[k]->def, 0); } for (k = 0; (unsigned int)k < extset->number; k++) { if (lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) { ly_set_free(extset); return EXIT_FAILURE; } } ly_set_free(extset); } /* unique */ if (dev->unique_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNIQUE, NULL)) { return EXIT_FAILURE; } /* units */ if (dev->units && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNITS, NULL)) { return EXIT_FAILURE; } /* default */ if (dev->dflt_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_DEFAULT, NULL)) { return EXIT_FAILURE; } /* config */ if ((dev->flags & LYS_CONFIG_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_CONFIG, NULL)) { return EXIT_FAILURE; } /* mandatory */ if ((dev->flags & LYS_MAND_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MANDATORY, NULL)) { return EXIT_FAILURE; } /* min/max */ if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MIN, NULL)) { return EXIT_FAILURE; } if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MAX, NULL)) { return EXIT_FAILURE; } /* type and must contain extension instances in their structures */ } } return EXIT_SUCCESS; } int lyp_ctx_check_module(struct lys_module *module) { struct ly_ctx *ctx; int i, match_i = -1, to_implement; const char *last_rev = NULL; assert(module); to_implement = 0; ctx = module->ctx; /* find latest revision */ for (i = 0; i < module->rev_size; ++i) { if (!last_rev || (strcmp(last_rev, module->rev[i].date) < 0)) { last_rev = module->rev[i].date; } } for (i = 0; i < ctx->models.used; i++) { /* check name (name/revision) and namespace uniqueness */ if (!strcmp(ctx->models.list[i]->name, module->name)) { if (to_implement) { if (i == match_i) { continue; } LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); return -1; } else if (!ctx->models.list[i]->rev_size && module->rev_size) { LOGERR(ctx, LY_EINVAL, "Module \"%s\" without revision already in context.", module->name); return -1; } else if (ctx->models.list[i]->rev_size && !module->rev_size) { LOGERR(ctx, LY_EINVAL, "Module \"%s\" with revision \"%s\" already in context.", module->name, ctx->models.list[i]->rev[0].date); return -1; } else if ((!module->rev_size && !ctx->models.list[i]->rev_size) || !strcmp(ctx->models.list[i]->rev[0].date, last_rev)) { LOGVRB("Module \"%s@%s\" already in context.", module->name, last_rev ? last_rev : ""); /* if disabled, enable first */ if (ctx->models.list[i]->disabled) { lys_set_enabled(ctx->models.list[i]); } to_implement = module->implemented; match_i = i; if (to_implement && !ctx->models.list[i]->implemented) { /* check first that it is okay to change it to implemented */ i = -1; continue; } return 1; } else if (module->implemented && ctx->models.list[i]->implemented) { LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.", module->name, last_rev ? last_rev : "", ctx->models.list[i]->rev[0].date); return -1; } /* else keep searching, for now the caller is just adding * another revision of an already present schema */ } else if (!strcmp(ctx->models.list[i]->ns, module->ns)) { LOGERR(ctx, LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\".", ctx->models.list[i]->name, module->name, module->ns); return -1; } } if (to_implement) { if (lys_set_implemented(ctx->models.list[match_i])) { return -1; } return 1; } return 0; } int lyp_ctx_add_module(struct lys_module *module) { struct lys_module **newlist = NULL; int i; assert(!lyp_ctx_check_module(module)); #ifndef NDEBUG int j; /* check that all augments are resolved */ for (i = 0; i < module->augment_size; ++i) { assert(module->augment[i].target); } for (i = 0; i < module->inc_size; ++i) { for (j = 0; j < module->inc[i].submodule->augment_size; ++j) { assert(module->inc[i].submodule->augment[j].target); } } #endif /* add to the context's list of modules */ if (module->ctx->models.used == module->ctx->models.size) { newlist = realloc(module->ctx->models.list, (2 * module->ctx->models.size) * sizeof *newlist); LY_CHECK_ERR_RETURN(!newlist, LOGMEM(module->ctx), -1); for (i = module->ctx->models.size; i < module->ctx->models.size * 2; i++) { newlist[i] = NULL; } module->ctx->models.size *= 2; module->ctx->models.list = newlist; } module->ctx->models.list[module->ctx->models.used++] = module; module->ctx->models.module_set_id++; return 0; } /** * Store UTF-8 character specified as 4byte integer into the dst buffer. * Returns number of written bytes (4 max), expects that dst has enough space. * * UTF-8 mapping: * 00000000 -- 0000007F: 0xxxxxxx * 00000080 -- 000007FF: 110xxxxx 10xxxxxx * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * * Includes checking for valid characters (following RFC 7950, sec 9.4) */ unsigned int pututf8(struct ly_ctx *ctx, char *dst, int32_t value) { if (value < 0x80) { /* one byte character */ if (value < 0x20 && value != 0x09 && value != 0x0a && value != 0x0d) { goto error; } dst[0] = value; return 1; } else if (value < 0x800) { /* two bytes character */ dst[0] = 0xc0 | (value >> 6); dst[1] = 0x80 | (value & 0x3f); return 2; } else if (value < 0xfffe) { /* three bytes character */ if (((value & 0xf800) == 0xd800) || (value >= 0xfdd0 && value <= 0xfdef)) { /* exclude surrogate blocks %xD800-DFFF */ /* exclude noncharacters %xFDD0-FDEF */ goto error; } dst[0] = 0xe0 | (value >> 12); dst[1] = 0x80 | ((value >> 6) & 0x3f); dst[2] = 0x80 | (value & 0x3f); return 3; } else if (value < 0x10fffe) { if ((value & 0xffe) == 0xffe) { /* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ goto error; } /* four bytes character */ dst[0] = 0xf0 | (value >> 18); dst[1] = 0x80 | ((value >> 12) & 0x3f); dst[2] = 0x80 | ((value >> 6) & 0x3f); dst[3] = 0x80 | (value & 0x3f); return 4; } error: /* out of range */ LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, NULL); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); return 0; } unsigned int copyutf8(struct ly_ctx *ctx, char *dst, const char *src) { uint32_t value; /* unicode characters */ if (!(src[0] & 0x80)) { /* one byte character */ if (src[0] < 0x20 && src[0] != 0x09 && src[0] != 0x0a && src[0] != 0x0d) { LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x", src[0]); return 0; } dst[0] = src[0]; return 1; } else if (!(src[0] & 0x20)) { /* two bytes character */ dst[0] = src[0]; dst[1] = src[1]; return 2; } else if (!(src[0] & 0x10)) { /* three bytes character */ value = ((uint32_t)(src[0] & 0xf) << 12) | ((uint32_t)(src[1] & 0x3f) << 6) | (src[2] & 0x3f); if (((value & 0xf800) == 0xd800) || (value >= 0xfdd0 && value <= 0xfdef) || (value & 0xffe) == 0xffe) { /* exclude surrogate blocks %xD800-DFFF */ /* exclude noncharacters %xFDD0-FDEF */ /* exclude noncharacters %xFFFE-FFFF */ LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); return 0; } dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; return 3; } else if (!(src[0] & 0x08)) { /* four bytes character */ value = ((uint32_t)(src[0] & 0x7) << 18) | ((uint32_t)(src[1] & 0x3f) << 12) | ((uint32_t)(src[2] & 0x3f) << 6) | (src[3] & 0x3f); if ((value & 0xffe) == 0xffe) { /* exclude noncharacters %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF, * %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF, * %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */ LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value); return 0; } dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; return 4; } else { LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src); LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 leading byte 0x%02x", src[0]); return 0; } } const struct lys_module * lyp_get_module(const struct lys_module *module, const char *prefix, int pref_len, const char *name, int name_len, int in_data) { const struct lys_module *main_module; char *str; int i; assert(!prefix || !name); if (prefix && !pref_len) { pref_len = strlen(prefix); } if (name && !name_len) { name_len = strlen(name); } main_module = lys_main_module(module); /* module own prefix, submodule own prefix, (sub)module own name */ if ((!prefix || (!module->type && !strncmp(main_module->prefix, prefix, pref_len) && !main_module->prefix[pref_len]) || (module->type && !strncmp(module->prefix, prefix, pref_len) && !module->prefix[pref_len])) && (!name || (!strncmp(main_module->name, name, name_len) && !main_module->name[name_len]))) { return main_module; } /* standard import */ for (i = 0; i < module->imp_size; ++i) { if ((!prefix || (!strncmp(module->imp[i].prefix, prefix, pref_len) && !module->imp[i].prefix[pref_len])) && (!name || (!strncmp(module->imp[i].module->name, name, name_len) && !module->imp[i].module->name[name_len]))) { return module->imp[i].module; } } /* module required by a foreign grouping, deviation, or submodule */ if (name) { str = strndup(name, name_len); if (!str) { LOGMEM(module->ctx); return NULL; } main_module = ly_ctx_get_module(module->ctx, str, NULL, 0); /* try data callback */ if (!main_module && in_data && module->ctx->data_clb) { main_module = module->ctx->data_clb(module->ctx, str, NULL, 0, module->ctx->data_clb_data); } free(str); return main_module; } return NULL; } const struct lys_module * lyp_get_import_module_ns(const struct lys_module *module, const char *ns) { int i; const struct lys_module *mod = NULL; assert(module && ns); if (module->type) { /* the module is actually submodule and to get the namespace, we need the main module */ if (ly_strequal(((struct lys_submodule *)module)->belongsto->ns, ns, 0)) { return ((struct lys_submodule *)module)->belongsto; } } else { /* module's own namespace */ if (ly_strequal(module->ns, ns, 0)) { return module; } } /* imported modules */ for (i = 0; i < module->imp_size; ++i) { if (ly_strequal(module->imp[i].module->ns, ns, 0)) { return module->imp[i].module; } } return mod; } const char * lyp_get_yang_data_template_name(const struct lyd_node *node) { struct lys_node *snode; snode = lys_parent(node->schema); while (snode && snode->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE)) { snode = lys_parent(snode); } if (snode && snode->nodetype == LYS_EXT && strcmp(((struct lys_ext_instance_complex *)snode)->def->name, "yang-data") == 0) { return ((struct lys_ext_instance_complex *)snode)->arg_value; } else { return NULL; } } const struct lys_node * lyp_get_yang_data_template(const struct lys_module *module, const char *yang_data_name, int yang_data_name_len) { int i, j; const struct lys_node *ret = NULL; const struct lys_submodule *submodule; for (i = 0; i < module->ext_size; ++i) { if (!strcmp(module->ext[i]->def->name, "yang-data") && !strncmp(module->ext[i]->arg_value, yang_data_name, yang_data_name_len) && !module->ext[i]->arg_value[yang_data_name_len]) { ret = (struct lys_node *)module->ext[i]; break; } } for (j = 0; !ret && j < module->inc_size; ++j) { submodule = module->inc[j].submodule; for (i = 0; i < submodule->ext_size; ++i) { if (!strcmp(submodule->ext[i]->def->name, "yang-data") && !strncmp(submodule->ext[i]->arg_value, yang_data_name, yang_data_name_len) && !submodule->ext[i]->arg_value[yang_data_name_len]) { ret = (struct lys_node *)submodule->ext[i]; break; } } } return ret; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-19888/000077500000000000000000000000001417746362400206505ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-19888/expected.txt000066400000000000000000000000371417746362400232120ustar00rootroot00000000000000jfif.c:430:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-19888/jfif.c000066400000000000000000000624361417746362400217450ustar00rootroot00000000000000/* ͷļ */ #include #include #include #include "stdefine.h" #include "bitstr.h" #include "huffman.h" #include "quant.h" #include "zigzag.h" #include "dct.h" #include "bmp.h" #include "color.h" #include "jfif.h" // Ԥ뿪 #define DEBUG_JFIF 0 // ڲͶ typedef struct { // width & height int width; int height; // quantization table int *pqtab[16]; // huffman codec ac HUFCODEC *phcac[16]; // huffman codec dc HUFCODEC *phcdc[16]; // components int comp_num; struct { int id; int samp_factor_v; int samp_factor_h; int qtab_idx; int htab_idx_ac; int htab_idx_dc; } comp_info[4]; int datalen; BYTE *databuf; } JFIF; /* ڲʵ */ #if DEBUG_JFIF static void jfif_dump(JFIF *jfif) { int i, j; printf("++ jfif dump ++\n"); printf("width : %d\n", jfif->width ); printf("height: %d\n", jfif->height); printf("\n"); for (i=0; i<16; i++) { if (!jfif->pqtab[i]) continue; printf("qtab%d\n", i); for (j=0; j<64; j++) { printf("%3d,%c", jfif->pqtab[i][j], j%8 == 7 ? '\n' : ' '); } printf("\n"); } for (i=0; i<16; i++) { int size = 16; if (!jfif->phcac[i]) continue; printf("htabac%d\n", i); for (j=0; j<16; j++) { size += jfif->phcac[i]->huftab[j]; } for (j=0; jphcac[i]->huftab[j], j%16 == 15 ? '\n' : ' '); } printf("\n\n"); } for (i=0; i<16; i++) { int size = 16; if (!jfif->phcdc[i]) continue; printf("htabdc%d\n", i); for (j=0; j<16; j++) { size += jfif->phcdc[i]->huftab[j]; } for (j=0; jphcdc[i]->huftab[j], j%16 == 15 ? '\n' : ' '); } printf("\n\n"); } printf("comp_num : %d\n", jfif->comp_num); for (i=0; icomp_num; i++) { printf("id:%d samp_factor_v:%d samp_factor_h:%d qtab_idx:%d htab_idx_ac:%d htab_idx_dc:%d\n", jfif->comp_info[i].id, jfif->comp_info[i].samp_factor_v, jfif->comp_info[i].samp_factor_h, jfif->comp_info[i].qtab_idx, jfif->comp_info[i].htab_idx_ac, jfif->comp_info[i].htab_idx_dc); } printf("\n"); printf("datalen : %d\n", jfif->datalen); printf("-- jfif dump --\n"); } static void dump_du(int *du) { int i; for (i=0; i<64; i++) { printf("%3d%c", du[i], i % 8 == 7 ? '\n' : ' '); } printf("\n"); } #endif static int ALIGN(int x, int y) { // y must be a power of 2. return (x + y - 1) & ~(y - 1); } static void category_encode(int *code, int *size) { unsigned absc = abs(*code); unsigned mask = (1 << 15); int i = 15; if (absc == 0) { *size = 0; return; } while (i && !(absc & mask)) { mask >>= 1; i--; } *size = i + 1; if (*code < 0) *code = (1 << *size) - absc - 1; } static int category_decode(int code, int size) { return code >= (1 << (size - 1)) ? code : code - (1 << size) + 1; } /* ʵ */ void* jfif_load(char *file) { JFIF *jfif = NULL; FILE *fp = NULL; int header = 0; int type = 0; WORD size = 0; BYTE *buf = NULL; BYTE *end = NULL; BYTE *dqt, *dht; int ret =-1; long offset = 0; int i; jfif = calloc(1, sizeof(JFIF)); buf = calloc(1, 0x10000); end = buf + 0x10000; if (!jfif || !buf) goto done; fp = fopen(file, "rb"); if (!fp) goto done; while (1) { do { header = fgetc(fp); } while (header != EOF && header != 0xff); // get header do { type = fgetc(fp); } while (type != EOF && type == 0xff); // get type if (header == EOF || type == EOF) { printf("file eof !\n"); break; } if ((type == 0xd8) || (type == 0xd9) || (type == 0x01) || (type >= 0xd0 && type <= 0xd7)) { size = 0; } else { size = fgetc(fp) << 8; size |= fgetc(fp) << 0; size -= 2; } size = fread(buf, 1, size, fp); switch (type) { case 0xc0: // SOF0 jfif->width = (buf[3] << 8) | (buf[4] << 0); jfif->height = (buf[1] << 8) | (buf[2] << 0); jfif->comp_num = buf[5] < 4 ? buf[5] : 4; for (i=0; icomp_num; i++) { jfif->comp_info[i].id = buf[6 + i * 3]; jfif->comp_info[i].samp_factor_v = (buf[7 + i * 3] >> 0) & 0x0f; jfif->comp_info[i].samp_factor_h = (buf[7 + i * 3] >> 4) & 0x0f; jfif->comp_info[i].qtab_idx = buf[8 + i * 3] & 0x0f; } break; case 0xda: // SOS jfif->comp_num = buf[0] < 4 ? buf[0] : 4; for (i=0; icomp_num; i++) { jfif->comp_info[i].id = buf[1 + i * 2]; jfif->comp_info[i].htab_idx_ac = (buf[2 + i * 2] >> 0) & 0x0f; jfif->comp_info[i].htab_idx_dc = (buf[2 + i * 2] >> 4) & 0x0f; } offset = ftell(fp); ret = 0; goto read_data; case 0xdb: // DQT dqt = buf; while (size > 0 && dqt < end) { int idx = dqt[0] & 0x0f; int f16 = dqt[0] & 0xf0; if (!jfif->pqtab[idx]) jfif->pqtab[idx] = malloc(64 * sizeof(int)); if (!jfif->pqtab[idx]) break; if (dqt + 1 + 64 + (f16 ? 64 : 0) < end) { for (i=0; i<64; i++) { jfif->pqtab[idx][ZIGZAG[i]] = f16 ? ((dqt[1 + i * 2] << 8) | (dqt[2 + i * 2] << 0)) : dqt[1 + i]; } } dqt += 1 + 64 + (f16 ? 64 : 0); size-= 1 + 64 + (f16 ? 64 : 0); } break; case 0xc4: // DHT dht = buf; while (size > 0 && dht + 17 < end) { int idx = dht[0] & 0x0f; int fac = dht[0] & 0xf0; int len = 0; for (i=1; i<1+16; i++) len += dht[i]; if (len > end - dht - 17) len = end - dht - 17; if (len > 256) len = 256; if (fac) { if (!jfif->phcac[idx]) jfif->phcac[idx] = calloc(1, sizeof(HUFCODEC)); if (jfif->phcac[idx]) memcpy(jfif->phcac[idx]->huftab, &dht[1], 16 + len); } else { if (!jfif->phcdc[idx]) jfif->phcdc[idx] = calloc(1, sizeof(HUFCODEC)); if (jfif->phcdc[idx]) memcpy(jfif->phcdc[idx]->huftab, &dht[1], 16 + len); } dht += 17 + len; size-= 17 + len; } break; } } read_data: fseek(fp, 0, SEEK_END); jfif->datalen = ftell(fp) - offset; jfif->databuf = malloc(jfif->datalen); if (jfif->databuf) { fseek(fp, offset, SEEK_SET); fread(jfif->databuf, 1, jfif->datalen, fp); } done: if (buf) free (buf); if (fp) fclose(fp ); if (ret == -1) { jfif_free(jfif); jfif = NULL; } return jfif; } int jfif_save(void *ctxt, char *file) { JFIF *jfif = (JFIF*)ctxt; FILE *fp = NULL; int len = 0; int i, j; int ret = -1; fp = fopen(file, "wb"); if (!fp) goto done; // output SOI fputc(0xff, fp); fputc(0xd8, fp); // output DQT for (i=0; i<16; i++) { if (!jfif->pqtab[i]) continue; len = 2 + 1 + 64; fputc(0xff, fp); fputc(0xdb, fp); fputc(len >> 8, fp); fputc(len >> 0, fp); fputc(i, fp); for (j=0; j<64; j++) { fputc(jfif->pqtab[i][ZIGZAG[j]], fp); } } // output SOF0 len = 2 + 1 + 2 + 2 + 1 + 3 * jfif->comp_num; fputc(0xff, fp); fputc(0xc0, fp); fputc(len >> 8, fp); fputc(len >> 0, fp); fputc(8, fp); // precision 8bit fputc(jfif->height >> 8, fp); // height fputc(jfif->height >> 0, fp); // height fputc(jfif->width >> 8, fp); // width fputc(jfif->width >> 0, fp); // width fputc(jfif->comp_num, fp); for (i=0; icomp_num; i++) { fputc(jfif->comp_info[i].id, fp); fputc((jfif->comp_info[i].samp_factor_v << 0)|(jfif->comp_info[i].samp_factor_h << 4), fp); fputc(jfif->comp_info[i].qtab_idx, fp); } // output DHT AC for (i=0; i<16; i++) { if (!jfif->phcac[i]) continue; fputc(0xff, fp); fputc(0xc4, fp); len = 2 + 1 + 16; for (j=0; j<16; j++) len += jfif->phcac[i]->huftab[j]; fputc(len >> 8, fp); fputc(len >> 0, fp); fputc(i + 0x10, fp); fwrite(jfif->phcac[i]->huftab, len - 3, 1, fp); } // output DHT DC for (i=0; i<16; i++) { if (!jfif->phcdc[i]) continue; fputc(0xff, fp); fputc(0xc4, fp); len = 2 + 1 + 16; for (j=0; j<16; j++) len += jfif->phcdc[i]->huftab[j]; fputc(len >> 8, fp); fputc(len >> 0, fp); fputc(i + 0x00, fp); fwrite(jfif->phcdc[i]->huftab, len - 3, 1, fp); } // output SOS len = 2 + 1 + 2 * jfif->comp_num + 3; fputc(0xff, fp); fputc(0xda, fp); fputc(len >> 8, fp); fputc(len >> 0, fp); fputc(jfif->comp_num, fp); for (i=0; icomp_num; i++) { fputc(jfif->comp_info[i].id, fp); fputc((jfif->comp_info[i].htab_idx_ac << 0)|(jfif->comp_info[i].htab_idx_dc << 4), fp); } fputc(0x00, fp); fputc(0x00, fp); fputc(0x00, fp); // output data if (jfif->databuf) { fwrite(jfif->databuf, jfif->datalen, 1, fp); } ret = 0; done: if (fp) fclose(fp); return ret; } void jfif_free(void *ctxt) { JFIF *jfif = (JFIF*)ctxt; int i; if (!jfif) return; for (i=0; i<16; i++) { if (jfif->pqtab[i]) free(jfif->pqtab[i]); if (jfif->phcac[i]) free(jfif->phcac[i]); if (jfif->phcdc[i]) free(jfif->phcdc[i]); } if (jfif->databuf) free(jfif->databuf); free(jfif); } int jfif_decode(void *ctxt, BMP *pb) { JFIF *jfif = (JFIF*)ctxt; void *bs = NULL; int *ftab[16]= {0}; int dc[4] = {0}; int mcuw, mcuh, mcuc, mcur, mcui, jw, jh; int i, j, c, h, v, x, y; int sfh_max = 0; int sfv_max = 0; int yuv_stride[3] = {0}; int yuv_height[3] = {0}; int *yuv_datbuf[3] = {0}; int *idst, *isrc; int *ysrc, *usrc, *vsrc; BYTE *bdst; int ret = -1; if (!ctxt || !pb) { printf("invalid input params !\n"); return -1; } // init dct module init_dct_module(); //++ init ftab for (i=0; i<16; i++) { if (jfif->pqtab[i]) { ftab[i] = malloc(64 * sizeof(int)); if (ftab[i]) { init_idct_ftab(ftab[i], jfif->pqtab[i]); } else { goto done; } } } //-- init ftab //++ calculate mcu info for (c=0; ccomp_num; c++) { if (sfh_max < jfif->comp_info[c].samp_factor_h) { sfh_max = jfif->comp_info[c].samp_factor_h; } if (sfv_max < jfif->comp_info[c].samp_factor_v) { sfv_max = jfif->comp_info[c].samp_factor_v; } } mcuw = sfh_max * 8; mcuh = sfv_max * 8; jw = ALIGN(jfif->width, mcuw); jh = ALIGN(jfif->height, mcuh); mcuc = jw / mcuw; mcur = jh / mcuh; //-- calculate mcu info // create yuv buffer for decoding yuv_stride[0] = jw; yuv_stride[1] = jw * jfif->comp_info[1].samp_factor_h / sfh_max; yuv_stride[2] = jw * jfif->comp_info[2].samp_factor_h / sfh_max; yuv_height[0] = jh; yuv_height[1] = jh * jfif->comp_info[1].samp_factor_v / sfv_max; yuv_height[2] = jh * jfif->comp_info[2].samp_factor_v / sfv_max; yuv_datbuf[0] = malloc(yuv_stride[0] * yuv_height[0] * sizeof(int)); yuv_datbuf[1] = malloc(yuv_stride[1] * yuv_height[1] * sizeof(int)); yuv_datbuf[2] = malloc(yuv_stride[2] * yuv_height[2] * sizeof(int)); if (!yuv_datbuf[0] || !yuv_datbuf[1] || !yuv_datbuf[2]) { goto done; } // open bit stream bs = bitstr_open(jfif->databuf, "mem", jfif->datalen); if (!bs) { printf("failed to open bitstr for jfif_decode !"); return -1; } // init huffman codec for (i=0; i<16; i++) { if (jfif->phcac[i]) { jfif->phcac[i]->input = bs; huffman_decode_init(jfif->phcac[i]); } if (jfif->phcdc[i]) { jfif->phcdc[i]->input = bs; huffman_decode_init(jfif->phcdc[i]); } } for (mcui=0; mcuicomp_num; c++) { for (v=0; vcomp_info[c].samp_factor_v; v++) { for (h=0; hcomp_info[c].samp_factor_h; h++) { HUFCODEC *hcac = jfif->phcac[jfif->comp_info[c].htab_idx_ac]; HUFCODEC *hcdc = jfif->phcdc[jfif->comp_info[c].htab_idx_dc]; int fidx = jfif->comp_info[c].qtab_idx; int size, znum, code; int du[64] = {0}; //+ decode dc size = huffman_decode_step(hcdc) & 0xf; if (size) { code = bitstr_get_bits(bs, size); code = category_decode(code, size); } else { code = 0; } dc[c] += code; du[0] = dc[c]; //- decode dc //+ decode ac for (i=1; i<64;) { code = huffman_decode_step(hcac); if (code <= 0) break; size = (code >> 0) & 0xf; znum = (code >> 4) & 0xf; i += znum; code = bitstr_get_bits(bs, size); code = category_decode(code, size); if (i < 64) du[i++] = code; } //- decode ac // de-zigzag zigzag_decode(du); // idct idct2d8x8(du, ftab[fidx]); // copy du to yuv buffer x = ((mcui % mcuc) * mcuw + h * 8) * jfif->comp_info[c].samp_factor_h / sfh_max; y = ((mcui / mcuc) * mcuh + v * 8) * jfif->comp_info[c].samp_factor_v / sfv_max; idst = yuv_datbuf[c] + y * yuv_stride[c] + x; isrc = du; for (i=0; i<8; i++) { memcpy(idst, isrc, 8 * sizeof(int)); idst += yuv_stride[c]; isrc += 8; } } } } } // close huffman codec for (i=0; i<16; i++) { if (jfif->phcac[i]) huffman_decode_done(jfif->phcac[i]); if (jfif->phcdc[i]) huffman_decode_done(jfif->phcdc[i]); } // close bit stream bitstr_close(bs); // create bitmap, and convert yuv to rgb bmp_create(pb, jfif->width, jfif->height); bdst = (BYTE*)pb->pdata; ysrc = yuv_datbuf[0]; for (i=0; iheight; i++) { int uy = i * jfif->comp_info[1].samp_factor_v / sfv_max; int vy = i * jfif->comp_info[2].samp_factor_v / sfv_max; for (j=0; jwidth; j++) { int ux = j * jfif->comp_info[1].samp_factor_h / sfh_max; int vx = j * jfif->comp_info[2].samp_factor_h / sfh_max; usrc = yuv_datbuf[2] + uy * yuv_stride[2] + ux; vsrc = yuv_datbuf[1] + vy * yuv_stride[1] + vx; yuv_to_rgb(*ysrc, *vsrc, *usrc, bdst + 2, bdst + 1, bdst + 0); bdst += 3; ysrc += 1; } bdst -= jfif->width * 3; bdst += pb->stride; ysrc -= jfif->width * 1; ysrc += yuv_stride[0]; } // success ret = 0; done: if (yuv_datbuf[0]) free(yuv_datbuf[0]); if (yuv_datbuf[1]) free(yuv_datbuf[1]); if (yuv_datbuf[2]) free(yuv_datbuf[2]); //++ free ftab for (i=0; i<16; i++) { if (ftab[i]) { free(ftab[i]); } } //-- free ftab return ret; } #define DU_TYPE_LUMIN 0 #define DU_TYPE_CHROM 1 typedef struct { unsigned runlen : 4; unsigned codesize : 4; unsigned codedata : 16; } RLEITEM; static void jfif_encode_du(JFIF *jfif, int type, int du[64], int *dc) { HUFCODEC *hfcac = jfif->phcac[type]; HUFCODEC *hfcdc = jfif->phcdc[type]; int *pqtab = jfif->pqtab[type]; void *bs = hfcac->output; int diff, code, size; RLEITEM rlelist[63]; int i, j, n, eob; // fdct fdct2d8x8(du, NULL); // quant quant_encode(du, pqtab); // zigzag zigzag_encode(du); // dc diff = du[0] - *dc; *dc = du[0]; // category encode for dc code = diff; category_encode(&code, &size); // huffman encode for dc huffman_encode_step(hfcdc, size); bitstr_put_bits(bs, code, size); // rle encode for ac for (i=1, j=0, n=0, eob=0; i<64 && j<63; i++) { if (du[i] == 0 && n < 15) { n++; } else { code = du[i]; size = 0; category_encode(&code, &size); rlelist[j].runlen = n; rlelist[j].codesize = size; rlelist[j].codedata = code; n = 0; j++; if (size != 0) eob = j; } } // set eob if (du[63] == 0) { rlelist[eob].runlen = 0; rlelist[eob].codesize = 0; rlelist[eob].codedata = 0; j = eob + 1; } // huffman encode for ac for (i=0; iwidth = pb->width; jfif->height = pb->height; jfif->pqtab[0] = malloc(64*sizeof(int)); jfif->pqtab[1] = malloc(64*sizeof(int)); jfif->phcac[0] = calloc(1, sizeof(HUFCODEC)); jfif->phcac[1] = calloc(1, sizeof(HUFCODEC)); jfif->phcdc[0] = calloc(1, sizeof(HUFCODEC)); jfif->phcdc[1] = calloc(1, sizeof(HUFCODEC)); jfif->datalen = jfif->width * jfif->height * 2; jfif->databuf = malloc(jfif->datalen); if (!jfif->pqtab[0] || !jfif->pqtab[1] || !jfif->phcac[0] || !jfif->phcac[1] || !jfif->phcdc[0] || !jfif->phcdc[1] || !jfif->databuf) { goto done; } // init qtab memcpy(jfif->pqtab[0], STD_QUANT_TAB_LUMIN, 64*sizeof(int)); memcpy(jfif->pqtab[1], STD_QUANT_TAB_CHROM, 64*sizeof(int)); // open bit stream bs = bitstr_open(jfif->databuf, "mem", jfif->datalen); if (!bs) { printf("failed to open bitstr for jfif_decode !"); goto done; } // init huffman codec memcpy(jfif->phcac[0]->huftab, STD_HUFTAB_LUMIN_AC, MAX_HUFFMAN_CODE_LEN + 256); memcpy(jfif->phcac[1]->huftab, STD_HUFTAB_CHROM_AC, MAX_HUFFMAN_CODE_LEN + 256); memcpy(jfif->phcdc[0]->huftab, STD_HUFTAB_LUMIN_DC, MAX_HUFFMAN_CODE_LEN + 256); memcpy(jfif->phcdc[1]->huftab, STD_HUFTAB_CHROM_DC, MAX_HUFFMAN_CODE_LEN + 256); jfif->phcac[0]->output = bs; huffman_encode_init(jfif->phcac[0], 1); jfif->phcac[1]->output = bs; huffman_encode_init(jfif->phcac[1], 1); jfif->phcdc[0]->output = bs; huffman_encode_init(jfif->phcdc[0], 1); jfif->phcdc[1]->output = bs; huffman_encode_init(jfif->phcdc[1], 1); // init comp_num & comp_info jfif->comp_num = 3; jfif->comp_info[0].id = 1; jfif->comp_info[0].samp_factor_v = 2; jfif->comp_info[0].samp_factor_h = 2; jfif->comp_info[0].qtab_idx = 0; jfif->comp_info[0].htab_idx_ac = 0; jfif->comp_info[0].htab_idx_dc = 0; jfif->comp_info[1].id = 2; jfif->comp_info[1].samp_factor_v = 1; jfif->comp_info[1].samp_factor_h = 1; jfif->comp_info[1].qtab_idx = 1; jfif->comp_info[1].htab_idx_ac = 1; jfif->comp_info[1].htab_idx_dc = 1; jfif->comp_info[2].id = 3; jfif->comp_info[2].samp_factor_v = 1; jfif->comp_info[2].samp_factor_h = 1; jfif->comp_info[2].qtab_idx = 1; jfif->comp_info[2].htab_idx_ac = 1; jfif->comp_info[2].htab_idx_dc = 1; // init jw & jw, init yuv data buffer jw = ALIGN(pb->width, 16); jh = ALIGN(pb->height, 16); yuv_datbuf[0] = calloc(1, jw * jh / 1 * sizeof(int)); yuv_datbuf[1] = calloc(1, jw * jh / 4 * sizeof(int)); yuv_datbuf[2] = calloc(1, jw * jh / 4 * sizeof(int)); if (!yuv_datbuf[0] || !yuv_datbuf[1] || !yuv_datbuf[2]) { goto done; } // convert rgb to yuv bsrc = pb->pdata; ydst = yuv_datbuf[0]; udst = yuv_datbuf[1]; vdst = yuv_datbuf[2]; for (i=0; iheight; i++) { for (j=0; jwidth; j++) { rgb_to_yuv(bsrc[2], bsrc[1], bsrc[0], ydst, udst, vdst); bsrc += 3; ydst += 1; if (j & 1) { udst += 1; vdst += 1; } } bsrc -= pb->width * 3; bsrc += pb->stride; ydst -= pb->width * 1; ydst += jw; udst -= pb->width / 2; vdst -= pb->width / 2; if (i & 1) { udst += jw / 2; vdst += jw / 2; } } for (m=0; mphcac[0]); huffman_encode_done(jfif->phcac[1]); huffman_encode_done(jfif->phcdc[0]); huffman_encode_done(jfif->phcdc[1]); jfif->datalen = bitstr_tell(bs); // close bit stream bitstr_close(bs); // if failed free context if (failed) { jfif_free(jfif); jfif = NULL; } // return context return jfif; } cppcheck-2.7/test/bug-hunting/cve/CVE-2019-7156/000077500000000000000000000000001417746362400205515ustar00rootroot00000000000000cppcheck-2.7/test/bug-hunting/cve/CVE-2019-7156/expected.txt000066400000000000000000000001331417746362400231100ustar00rootroot00000000000000ole.c:398:bughuntingDivByZero ole.c:399:bughuntingDivByZero ole.c:400:bughuntingDivByZero cppcheck-2.7/test/bug-hunting/cve/CVE-2019-7156/ole.c000066400000000000000000000434461417746362400215070ustar00rootroot00000000000000/** * @file ole.c * @author Alex Ott, Victor B Wagner * @date Wed Jun 11 12:33:01 2003 * Version: $Id: ole.c,v 1.2 2006/02/25 15:28:14 vitus Exp $ * Copyright: Victor B Wagner, 1996-2003 Alex Ott, 2003 * * @brief Parsing structure of MS Office compound document * * This file is part of catdoc project * and distributed under GNU Public License * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "catdoc.h" #define min(a,b) ((a) < (b) ? (a) : (b)) const static unsigned char ole_sign[]={0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1,0}; /** * Initializes ole structure * * @param f (FILE *) compound document file, positioned at bufSize * byte. Might be pipe or socket * @param buffer (void *) bytes already read from f * @param bufSize number of bytes already read from f should be less * than 512 * * @return */ FILE* ole_init(FILE *f, void *buffer, size_t bufSize, struct ole_params_t *ole_params) { unsigned char oleBuf[BBD_BLOCK_SIZE]; unsigned char *tmpBuf; FILE *newfile; int ret=0, i; long int sbdMaxLen, sbdCurrent, propMaxLen, propCurrent, mblock, msat_size; oleEntry *tEntry; long int sectorSize; long int shortSectorSize; long int bbdNumBlocks; /* deleting old data (if it was allocated) */ ole_finish(ole_params); if (fseek(f,0,SEEK_SET) == -1) { if (errno == ESPIPE) { /* We got non-seekable file, create temp file */ if ((newfile=tmpfile()) == NULL) { return NULL; } if (bufSize > 0) { ret=fwrite(buffer, 1, bufSize, newfile); if (ret != bufSize) { return NULL; } } while (!feof(f)) { ret=fread(oleBuf,1,BBD_BLOCK_SIZE,f); fwrite(oleBuf, 1, ret, newfile); } fseek(newfile,0,SEEK_SET); } else { return NULL; } } else { newfile=f; } fseek(newfile,0,SEEK_END); ole_params->fileLength=ftell(newfile); fseek(newfile,0,SEEK_SET); ret=fread(oleBuf,1,BBD_BLOCK_SIZE,newfile); if (ret != BBD_BLOCK_SIZE) { return NULL; } if (strncmp(oleBuf,ole_sign,8) != 0) { return NULL; } ole_params->sectorSize = 1<sectorSize == 0) { return NULL; } sectorSize = ole_params->sectorSize; ole_params->shortSectorSize = 1<shortSectorSize; if (shortSectorSize > sectorSize) { return NULL; } /* Read BBD into memory */ ole_params->bbdNumBlocks = getulong(oleBuf,0x2c); bbdNumBlocks = ole_params->bbdNumBlocks; if ((ole_params->BBD=malloc(bbdNumBlocks*sectorSize)) == NULL) { return NULL; } if ((tmpBuf=malloc(MSAT_ORIG_SIZE)) == NULL) { return NULL; } memcpy(tmpBuf,oleBuf+0x4c,MSAT_ORIG_SIZE); mblock=getlong(oleBuf,0x44); msat_size=getlong(oleBuf,0x48); /* fprintf(stderr, "msat_size=%ld\n", msat_size); */ i=0; while ((mblock >= 0) && (i < msat_size)) { unsigned char *newbuf; /* fprintf(stderr, "i=%d mblock=%ld\n", i, mblock); */ if ((newbuf=realloc(tmpBuf, sectorSize*(i+1)+MSAT_ORIG_SIZE)) != NULL) { tmpBuf=newbuf; } else { free(tmpBuf); ole_finish(ole_params); return NULL; } fseek(newfile, 512+mblock*sectorSize, SEEK_SET); if (fread(tmpBuf+MSAT_ORIG_SIZE+(sectorSize-4)*i, 1, sectorSize, newfile) != sectorSize) { ole_finish(ole_params); return NULL; } i++; mblock=getlong(tmpBuf, MSAT_ORIG_SIZE+(sectorSize-4)*i); } /* fprintf(stderr, "bbdNumBlocks=%ld\n", bbdNumBlocks); */ for (i=0; i< bbdNumBlocks; i++) { long int bbdSector=getlong(tmpBuf,4*i); if (bbdSector >= ole_params->fileLength/sectorSize || bbdSector < 0) { errno = EINVAL; ole_finish(ole_params); return NULL; } fseek(newfile, 512+bbdSector*sectorSize, SEEK_SET); if (fread(ole_params->BBD+i*sectorSize, 1, sectorSize, newfile) != sectorSize) { free(tmpBuf); ole_finish(ole_params); return NULL; } } free(tmpBuf); /* Read SBD into memory */ ole_params->sbdLen=0; sbdMaxLen=10; sbdCurrent = ole_params->sbdStart = getlong(oleBuf,0x3c); if (ole_params->sbdStart > 0) { if ((ole_params->SBD=malloc(sectorSize*sbdMaxLen)) == NULL) { ole_finish(ole_params); return NULL; } while (1) { fseek(newfile, 512+sbdCurrent*sectorSize, SEEK_SET); fread(ole_params->SBD+ole_params->sbdLen*sectorSize, 1, sectorSize, newfile); ole_params->sbdLen++; if (ole_params->sbdLen >= sbdMaxLen) { unsigned char *newSBD; sbdMaxLen+=5; if ((newSBD=realloc(ole_params->SBD, sectorSize*sbdMaxLen)) != NULL) { ole_params->SBD=newSBD; } else { ole_finish(ole_params); return NULL; } } if (sbdCurrent < 0 || sbdCurrent * 4 >= bbdNumBlocks * sectorSize) { break; } sbdCurrent = getlong(ole_params->BBD, sbdCurrent*4); if (sbdCurrent < 0 || sbdCurrent >= ole_params->fileLength/sectorSize) break; } ole_params->sbdNumber = (ole_params->sbdLen*sectorSize)/shortSectorSize; } else { ole_params->SBD=NULL; } /* Read property catalog into memory */ ole_params->propLen = 0; propMaxLen = 5; propCurrent = ole_params->propStart = getlong(oleBuf,0x30); if (ole_params->propStart >= 0) { if ((ole_params->properties=malloc(propMaxLen*sectorSize)) == NULL) { ole_finish(ole_params); return NULL; } while (1) { /* fprintf(stderr, "propCurrent=%ld\n",propCurrent); */ fseek(newfile, 512+propCurrent*sectorSize, SEEK_SET); fread(ole_params->properties+ole_params->propLen*sectorSize, 1, sectorSize, newfile); (ole_params->propLen)++; if (ole_params->propLen >= propMaxLen) { unsigned char *newProp; propMaxLen+=5; if ((newProp=realloc(ole_params->properties, propMaxLen*sectorSize)) != NULL) ole_params->properties=newProp; else { ole_finish(ole_params); return NULL; } } propCurrent = getlong(ole_params->BBD, propCurrent*4); if (propCurrent < 0 || propCurrent >= ole_params->fileLength/sectorSize) { break; } } ole_params->propNumber = (ole_params->propLen*sectorSize)/PROP_BLOCK_SIZE; ole_params->propCurNumber = 0; } else { ole_finish(ole_params); ole_params->properties = NULL; return NULL; } /* Find Root Entry */ while ((tEntry=(oleEntry*)ole_readdir(newfile, ole_params)) != NULL) { if (tEntry->type == oleRootDir) { ole_params->rootEntry=tEntry; break; } ole_close((FILE*)tEntry); } ole_params->propCurNumber = 0; fseek(newfile, 0, SEEK_SET); if (!ole_params->rootEntry) { errno = EINVAL; ole_finish(ole_params); return NULL; } return newfile; } /** * * * @param oleBuf * * @return */ int rightOleType(unsigned char *oleBuf) { return (oleBuf[0x42] == 1 || oleBuf[0x42] == 2 || oleBuf[0x42] == 3 || oleBuf[0x42] == 5); } /** * * * @param oleBuf * * @return */ oleType getOleType(unsigned char *oleBuf) { return (oleType)((unsigned char)oleBuf[0x42]); } /** * Reads next directory entry from file * * @param name buffer for name converted to us-ascii should be at least 33 chars long * @param size size of file * * @return 0 if everything is ok -1 on error */ FILE *ole_readdir(FILE *f, struct ole_params_t *ole_params) { int i, nLen; unsigned char *oleBuf; oleEntry *e=NULL; long int chainMaxLen, chainCurrent; if (ole_params->properties == NULL || ole_params->propCurNumber >= ole_params->propNumber || f == NULL) return NULL; oleBuf=ole_params->properties + ole_params->propCurNumber*PROP_BLOCK_SIZE; if (!rightOleType(oleBuf)) return NULL; if ((e = (oleEntry*)malloc(sizeof(oleEntry))) == NULL) { return NULL; } e->dirPos=oleBuf; e->type=getOleType(oleBuf); e->file=f; e->startBlock=getlong(oleBuf,0x74); e->blocks=NULL; nLen=getshort(oleBuf,0x40); for (i=0; i < nLen/2 && i < OLENAMELENGHT; i++) e->name[i]=(char)oleBuf[i*2]; e->name[i]='\0'; (ole_params->propCurNumber)++; e->length=getulong(oleBuf,0x78); /* Read sector chain for object */ chainMaxLen = 25; e->numOfBlocks = 0; chainCurrent = e->startBlock; e->isBigBlock = (e->length >= 0x1000) || !strcmp(e->name, "Root Entry"); /* fprintf(stderr, "e->name=%s e->length=%ld\n", e->name, e->length); */ /* fprintf(stderr, "e->startBlock=%ld BBD=%p\n", e->startBlock, BBD); */ if (e->startBlock >= 0 && e->length >= 0 && (e->startBlock <= ole_params->fileLength/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { if ((e->blocks=malloc(chainMaxLen*sizeof(long int))) == NULL) { return NULL; } while (1) { /* fprintf(stderr, "chainCurrent=%ld\n", chainCurrent); */ e->blocks[e->numOfBlocks++] = chainCurrent; if (e->numOfBlocks >= chainMaxLen) { long int *newChain; chainMaxLen+=25; if ((newChain=realloc(e->blocks, chainMaxLen*sizeof(long int))) != NULL) e->blocks=newChain; else { free(e->blocks); e->blocks=NULL; return NULL; } } if (e->isBigBlock) { chainCurrent = getlong(ole_params->BBD, chainCurrent*4); } else if (ole_params->SBD != NULL) { chainCurrent = getlong(ole_params->SBD, chainCurrent*4); } else { chainCurrent=-1; } if (chainCurrent <= 0 || chainCurrent >= (e->isBigBlock ? ((ole_params->bbdNumBlocks*ole_params->sectorSize)/4) : ((ole_params->sbdNumber*ole_params->shortSectorSize)/4)) || (e->numOfBlocks > e->length/(e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize))) { /* fprintf(stderr, "chain End=%ld\n", chainCurrent); */ break; } } } if (e->length > (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks) e->length = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize)*e->numOfBlocks; /* fprintf(stderr, "READDIR: e->name=%s e->numOfBlocks=%ld length=%ld\n", */ /* e->name, e->numOfBlocks, e->length); */ return (FILE*)e; } /** * Open stream, which correspond to directory entry last read by * ole_readdir * * * @return opaque pointer to pass to ole_read, casted to (FILE *) */ int ole_open(FILE *stream) { oleEntry *e=(oleEntry *)stream; if (e->type != oleStream) return -2; e->ole_offset=0; e->file_offset= ftell(e->file); return 0; } /** * * * @param e * @param blk * * @return */ long int calcFileBlockOffset(oleEntry *e, long int blk, struct ole_params_t *ole_params) { long int res; if (e->isBigBlock) { res=512+e->blocks[blk]*ole_params->sectorSize; } else { long int sbdPerSector=(ole_params->sectorSize)/(ole_params->shortSectorSize); long int sbdSecNum=e->blocks[blk]/sbdPerSector; long int sbdSecMod=e->blocks[blk]%sbdPerSector; res=512 + ole_params->rootEntry->blocks[sbdSecNum]*ole_params->sectorSize + sbdSecMod*ole_params->shortSectorSize; } return res; } /** * Reads block from open ole stream interface-compatible with fread * * @param ptr pointer to buffer for read to * @param size size of block * @param nmemb size in blocks * @param stream pointer to FILE* structure * * @return number of readed blocks */ size_t ole_read(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) { oleEntry *e = (oleEntry*)stream; long int llen = size*nmemb, rread=0, i; long int blockNumber, modBlock, toReadBlocks, toReadBytes, bytesInBlock; long int ssize; /**< Size of block */ long int newoffset; unsigned char *cptr = ptr; if (e->ole_offset+llen > e->length) llen= e->length - e->ole_offset; ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); blockNumber=e->ole_offset/ssize; /* fprintf(stderr, "blockNumber=%ld e->numOfBlocks=%ld llen=%ld\n", */ /* blockNumber, e->numOfBlocks, llen); */ if (blockNumber >= e->numOfBlocks || llen <=0) return 0; modBlock=e->ole_offset%ssize; bytesInBlock = ssize - modBlock; if (bytesInBlock < llen) { toReadBlocks = (llen-bytesInBlock)/ssize; toReadBytes = (llen-bytesInBlock)%ssize; } else { toReadBlocks = toReadBytes = 0; } /* fprintf(stderr, "llen=%ld toReadBlocks=%ld toReadBytes=%ld bytesInBlock=%ld blockNumber=%ld modBlock=%ld\n", */ /* llen, toReadBlocks, toReadBytes, bytesInBlock, blockNumber, modBlock); */ newoffset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; if (e->file_offset != newoffset) { fseek(e->file, e->file_offset=newoffset, SEEK_SET); } rread=fread(ptr, 1, min(llen,bytesInBlock), e->file); e->file_offset += rread; for (i=0; ifile_offset); fseek(e->file, e->file_offset=newoffset, SEEK_SET); readbytes=fread(cptr+rread, 1, min(llen-rread, ssize), e->file); rread +=readbytes; e->file_offset +=readbytes; } if (toReadBytes > 0) { int readbytes; blockNumber++; newoffset = calcFileBlockOffset(e,blockNumber, ole_params); fseek(e->file, e->file_offset=newoffset, SEEK_SET); readbytes=fread(cptr+rread, 1, toReadBytes,e->file); rread +=readbytes; e->file_offset +=readbytes; } /* fprintf(stderr, "ole_offset=%ld rread=%ld llen=%ld\n", e->ole_offset, rread, llen);*/ e->ole_offset+=rread; return rread; } /** * * * @param stream * * @return */ int ole_eof(FILE *stream) { oleEntry *e=(oleEntry*)stream; /* fprintf(stderr, "EOF: e->ole_offset=%ld e->length=%ld\n", e->ole_offset, e->length);*/ return (e->ole_offset >= e->length); } /** * * */ void ole_finish(struct ole_params_t *ole_params) { if (ole_params->BBD != NULL) free(ole_params->BBD); if (ole_params->SBD != NULL) free(ole_params->SBD); if (ole_params->properties != NULL) free(ole_params->properties); if (ole_params->rootEntry != NULL) ole_close((FILE*)(ole_params->rootEntry)); ole_params->properties = ole_params->SBD = ole_params->BBD = NULL; ole_params->rootEntry = NULL; } /** * * * @param stream * * @return */ int ole_close(FILE *stream) { oleEntry *e=(oleEntry*)stream; if (e == NULL) return -1; if (e->blocks != NULL) free(e->blocks); free(e); return 0; } /** * * * @param stream pointer to OLE stream structure * @param offset * @param whence * * @return */ int ole_seek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) { oleEntry *e=(oleEntry*)stream; long int new_ole_offset=0, new_file_offset; int ssize, modBlock, blockNumber; switch (whence) { case SEEK_SET: new_ole_offset=offset; break; case SEEK_CUR: new_ole_offset=e->ole_offset+offset; break; case SEEK_END: new_ole_offset=e->length+offset; break; default: errno=EINVAL; return -1; } if (new_ole_offset<0) new_ole_offset=0; if (new_ole_offset >= e->length) new_ole_offset=e->length; ssize = (e->isBigBlock ? ole_params->sectorSize : ole_params->shortSectorSize); blockNumber=new_ole_offset/ssize; if (blockNumber >= e->numOfBlocks) return -1; modBlock=new_ole_offset%ssize; new_file_offset = calcFileBlockOffset(e,blockNumber, ole_params)+modBlock; fseek(e->file, e->file_offset=new_file_offset, SEEK_SET); e->ole_offset=new_ole_offset; return 0; } /** * Tell position inside OLE stream * * @param stream pointer to OLE stream * * @return current position inside OLE stream */ long ole_tell(FILE *stream) { oleEntry *e=(oleEntry*)stream; return e->ole_offset; } void set_ole_func(struct io_funcs_t *io_funcs) { io_funcs->catdoc_read=ole_read; io_funcs->catdoc_eof=ole_eof; io_funcs->catdoc_seek=ole_seek; io_funcs->catdoc_tell=ole_tell; } size_t my_fread(void *ptr, size_t size, size_t nmemb, FILE *stream, struct ole_params_t *ole_params) { return fread(ptr, size, nmemb, stream); } int my_fseek(FILE *stream, long offset, int whence, struct ole_params_t *ole_params) { return fseek(stream, offset, whence); } void set_std_func(struct io_funcs_t *io_funcs) { io_funcs->catdoc_read=my_fread; io_funcs->catdoc_eof=feof; io_funcs->catdoc_seek=my_fseek; io_funcs->catdoc_tell=ftell; } cppcheck-2.7/test/bug-hunting/itc.py000066400000000000000000000051431417746362400175220ustar00rootroot00000000000000# Test if --bug-hunting works using the itc testsuite # The itc test suite can be downloaded here: # https://github.com/regehr/itc-benchmarks import os import re import shutil import sys import subprocess if sys.argv[0] in ('test/bug-hunting/itc.py', './test/bug-hunting/itc.py'): CPPCHECK_PATH = './cppcheck' else: CPPCHECK_PATH = '../../cppcheck' if len(sys.argv) >= 2 and sys.argv[-1] != '--clang': TESTFILES = [sys.argv[-1]] else: TESTFILES = [os.path.expanduser('~/itc/01.w_Defects/zero_division.c'), os.path.expanduser('~/itc/01.w_Defects/uninit_var.c')] if not os.path.isfile(TESTFILES[0]): print('ERROR: %s is not a file' % TESTFILES[0]) sys.exit(1) RUN_CLANG = ('--clang' in sys.argv) def get_error_lines(filename): ret = [] f = open(filename, 'rt') lines = f.readlines() for linenr, line in enumerate(lines): if line.find('/* ERROR:') > 0 or line.find('/*ERROR:') > 0: linenr += 1 if testfile.find('uninit_') >= 0: if linenr == 177: linenr = 176 elif linenr == 241: linenr = 242 # warn about usage ret.append(linenr) return ret def check(filename): cmd = [CPPCHECK_PATH, '--bug-hunting', '--bug-hunting-check-function-max-time=10' '--platform=unix64', filename] if RUN_CLANG: cmd.append('--clang') print(' '.join(cmd)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() stdout = comm[0].decode(encoding='utf-8', errors='ignore') stderr = comm[1].decode(encoding='utf-8', errors='ignore') if RUN_CLANG: shutil.rmtree('itc-build-dir') if filename.find('zero_division.c') >= 0: w = r'.*zero_division.c:([0-9]+):[0-9]+: error: There is division.*' elif filename.find('uninit_') >= 0: w = r'.*c:([0-9]+):[0-9]+: error: .*bughuntingUninit.*' else: w = r'.*c:([0-9]+):[0-9]+: error: .*bughunting.*' ret = [] for line in stderr.split('\n'): res = re.match(w, line) if res is None: continue linenr = int(res.group(1)) if linenr not in ret: ret.append(linenr) return ret for testfile in TESTFILES: wanted = get_error_lines(testfile) actual = check(testfile) missing = [] for w in wanted: if w not in actual: missing.append(w) if len(missing) > 0: print('wanted:' + str(wanted)) print('actual:' + str(actual)) print('missing:' + str(missing)) sys.exit(1) cppcheck-2.7/test/bug-hunting/juliet.py000066400000000000000000000043651417746362400202440ustar00rootroot00000000000000# Test if --bug-hunting works using the juliet testsuite # The Juliet test suite can be downloaded from: # https://samate.nist.gov/SRD/testsuite.php import glob import os import re import shutil import sys import subprocess JULIET_PATH = os.path.expanduser('~/juliet') if sys.argv[0] in ('test/bug-hunting/juliet.py', './test/bug-hunting/juliet.py'): CPPCHECK_PATH = './cppcheck' else: CPPCHECK_PATH = '../../cppcheck' RUN_CLANG = ('--clang' in sys.argv) def get_files(juliet_path:str, test_cases:str): ret = [] g = os.path.join(juliet_path, test_cases) print(g) for f in sorted(glob.glob(g)): res = re.match(r'(.*[0-9][0-9])[a-x]?.(cp*)$', f) if res is None: print('Non-match!! ' + f) sys.exit(1) f = res.group(1) + '*.' + res.group(2) if f not in ret: ret.append(f) return ret def check(tc:str, warning_id:str): num_ok = 0 num_failed = 0 for f in get_files(JULIET_PATH, tc): cmd = [CPPCHECK_PATH, '-I' + os.path.join(JULIET_PATH, 'C/testcasesupport'), '-DOMIT_GOOD', '-DAF_INET=1', '-DINADDR_ANY=1', '--library=posix', '--bug-hunting', '--platform=unix64'] if RUN_CLANG: cmd += ['--clang', '--cppcheck-build-dir=juliet-build-dir'] if not os.path.isdir('juliet-build-dir'): os.mkdir('juliet-build-dir') cmd += glob.glob(f) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() stdout = comm[0].decode(encoding='utf-8', errors='ignore') stderr = comm[1].decode(encoding='utf-8', errors='ignore') if RUN_CLANG: shutil.rmtree('juliet-build-dir') if warning_id in stderr: num_ok += 1 else: print('fail: ' + ' '.join(cmd)) num_failed += 1 cwepos = tc.find('CWE') cwe = tc[cwepos:cwepos+6] print('%s ok:%i, fail:%i' % (cwe, num_ok, num_failed)) if num_failed != 0: sys.exit(1) check('C/testcases/CWE369_Divide_by_Zero/s*/*.c', 'bughuntingDivByZero') #check('C/testcases/CWE457_Use_of_Uninitialized_Variable/s*/*.c', 'bughuntingUninit') cppcheck-2.7/test/cfg/000077500000000000000000000000001417746362400146765ustar00rootroot00000000000000cppcheck-2.7/test/cfg/boost.cpp000066400000000000000000000037431417746362400165370ustar00rootroot00000000000000 // Test library configuration for boost.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/boost.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include BOOST_FORCEINLINE void boost_forceinline_test() {} BOOST_NOINLINE void boost_noinline_test() {} BOOST_NORETURN void boost_noreturn_test() {} void print_hello() { printf("hello"); } void valid_code(boost::function &pf_print_hello) { if (BOOST_LIKELY(1)) {} if (BOOST_UNLIKELY(0)) {} int int1 = 5; boost::endian::endian_reverse_inplace(int1); boost::bind(print_hello)(); pf_print_hello = boost::bind(print_hello); } void ignoredReturnValue(char * buf) { // cppcheck-suppress ignoredReturnValue boost::math::round(1.5); // cppcheck-suppress ignoredReturnValue boost::math::iround(1.5); // cppcheck-suppress ignoredReturnValue boost::math::lround(1.5); // cppcheck-suppress ignoredReturnValue boost::math::llround(1.5); // cppcheck-suppress ignoredReturnValue boost::endian::endian_reverse(1); } void uninitvar() { int intUninit1; int intUninit2; // cppcheck-suppress uninitvar boost::endian::endian_reverse_inplace(intUninit1); // cppcheck-suppress uninitvar (void)boost::math::round(intUninit2); } void throwexception(int * buf) { if (!buf) boost::throw_exception(std::bad_alloc()); *buf = 0; } void throwexception2(int * buf) { if (!buf) BOOST_THROW_EXCEPTION(std::bad_alloc()); *buf = 0; } void macros() { #define DECL(z, n, text) text ## n = n; BOOST_PP_REPEAT(5, DECL, int x) BOOST_SCOPED_ENUM_DECLARE_BEGIN(future_errc) { no_state } BOOST_SCOPED_ENUM_DECLARE_END(future_errc) } cppcheck-2.7/test/cfg/bsd.c000066400000000000000000000045011417746362400156120ustar00rootroot00000000000000// Test library configuration for bsd.cfg // // Usage: // $ cppcheck --library=bsd --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/bsd.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include // #9323, #9331 void verify_timercmp(struct timeval t) { (void)timercmp(&t, &t, <); (void)timercmp(&t, &t, <=); (void)timercmp(&t, &t, ==); (void)timercmp(&t, &t, !=); (void)timercmp(&t, &t, >=); (void)timercmp(&t, &t, >); } // False negative: #9346 void uninitvar_timercmp(struct timeval t) { struct timeval uninit; (void)timercmp(&t, &uninit, <); (void)timercmp(&uninit, &t, <=); (void)timercmp(&uninit, &uninit, ==); } void nullPointer_timercmp(struct timeval t) { struct timeval *p=0; // cppcheck-suppress nullPointer (void)timercmp(&t, p, <); // cppcheck-suppress nullPointer (void)timercmp(p, &t, <=); // cppcheck-suppress nullPointer (void)timercmp(p, p, ==); } // size_t strlcat(char *dst, const char *src, size_t size); void uninitvar_strlcat(char *Ct, const char *S, size_t N) { char *ct1, *ct2; char *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)strlcat(ct1,s1,n1); // cppcheck-suppress uninitvar (void)strlcat(ct2,S,N); // cppcheck-suppress uninitvar (void)strlcat(Ct,s2,N); // cppcheck-suppress uninitvar (void)strlcat(Ct,S,n2); // no warning is expected for (void)strlcat(Ct,S,N); } void bufferAccessOutOfBounds(void) { uint16_t uint16Buf[4]; // cppcheck-suppress bufferAccessOutOfBounds arc4random_buf(uint16Buf, 9); // valid arc4random_buf(uint16Buf, 8); } void ignoredReturnValue(void) { // cppcheck-suppress ignoredReturnValue arc4random(); // cppcheck-suppress ignoredReturnValue arc4random_uniform(10); } void invalidFunctionArg() { // cppcheck-suppress invalidFunctionArg (void) arc4random_uniform(1); // valid (void) arc4random_uniform(2); } void nullPointer(void) { // cppcheck-suppress nullPointer arc4random_buf(NULL, 5); } void uninitvar(void) { uint32_t uint32Uninit; // cppcheck-suppress uninitvar (void) arc4random_uniform(uint32Uninit); } cppcheck-2.7/test/cfg/cairo.c000066400000000000000000000013411417746362400161360ustar00rootroot00000000000000 // Test library configuration for cairo.cfg // // Usage: // $ cppcheck --check-library --library=cairo --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/cairo.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include void validCode(cairo_surface_t *target) { cairo_t * cairo1 = cairo_create(target); cairo_move_to(cairo1, 1.0, 2.0); cairo_line_to(cairo1, 5.0, 6.0); cairo_destroy(cairo1); } void ignoredReturnValue(cairo_surface_t *target) { // cppcheck-suppress ignoredReturnValue cairo_create(target); // cppcheck-suppress ignoredReturnValue cairo_status_to_string(CAIRO_STATUS_READ_ERROR); } cppcheck-2.7/test/cfg/cppunit.cpp000066400000000000000000000031071417746362400170650ustar00rootroot00000000000000// Test library configuration for cppunit.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/cppunit.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include void cppunit_assert_equal(int x, double y) { CPPUNIT_ASSERT(true); CPPUNIT_ASSERT(false); CPPUNIT_ASSERT(1 < 2); CPPUNIT_ASSERT_MESSAGE("Test failed", true); CPPUNIT_ASSERT_MESSAGE("Test failed", false); CPPUNIT_ASSERT_MESSAGE("Test failed", 1 < 2); CPPUNIT_ASSERT_EQUAL(1, 2); CPPUNIT_ASSERT_EQUAL(true, 3 == x); CPPUNIT_ASSERT_EQUAL_MESSAGE("Test failed", 1, 4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Test failed", true, 4 == x); CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, 2.0, 1e-7); CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, y, 1e-7); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Test failed", 1.0, 2.0, 1e-7); CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Test failed", 1.0, y, 1e-7); } void cppunit_throw() { CPPUNIT_ASSERT_NO_THROW(1 + 1); CPPUNIT_ASSERT_NO_THROW_MESSAGE("Unexpected throw", 1 + 1); CPPUNIT_ASSERT_THROW(1 + 1, CPPUNIT_NS::Exception); CPPUNIT_ASSERT_THROW_MESSAGE("Did not throw", 1 + 1, CPPUNIT_NS::Exception); } void cppunit_assertion_assert() { CPPUNIT_ASSERT_ASSERTION_FAIL(true); CPPUNIT_ASSERT_ASSERTION_FAIL_MESSAGE("hello", false); CPPUNIT_ASSERT_ASSERTION_PASS(false); CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE("goodbye", true); } void cppunit_assert_fail() { CPPUNIT_FAIL("This fails"); } cppcheck-2.7/test/cfg/generate-cfg-tests.sh000077500000000000000000000015601417746362400207260ustar00rootroot00000000000000#!/bin/bash cd ~/cppcheck || exit 1 make generate_cfg_tests ./generate_cfg_tests cfg/avr.cfg > test/cfg/generated-cfg-tests-avr.cpp ./generate_cfg_tests cfg/bsd.cfg > test/cfg/generated-cfg-tests-bsd.cpp ./generate_cfg_tests cfg/gnu.cfg > test/cfg/generated-cfg-tests-gnu.cpp ./generate_cfg_tests cfg/motif.cfg > test/cfg/generated-cfg-tests-motif.cpp ./generate_cfg_tests cfg/posix.cfg > test/cfg/generated-cfg-tests-posix.cpp ./generate_cfg_tests cfg/qt.cfg > test/cfg/generated-cfg-tests-qt.cpp ./generate_cfg_tests cfg/sdl.cfg > test/cfg/generated-cfg-tests-sdl.cpp ./generate_cfg_tests cfg/sfml.cfg > test/cfg/generated-cfg-tests-sfml.cpp ./generate_cfg_tests cfg/std.cfg > test/cfg/generated-cfg-tests-std.cpp ./generate_cfg_tests cfg/windows.cfg > test/cfg/generated-cfg-tests-windows.cpp ./generate_cfg_tests cfg/wxwidgets.cfg > test/cfg/generated-cfg-tests-wxwidgets.cpp cppcheck-2.7/test/cfg/gnu.c000066400000000000000000000163761417746362400156500ustar00rootroot00000000000000 // Test library configuration for gnu.cfg // // Usage: // $ cppcheck --check-library --library=gnu --enable=information --enable=style --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/gnu.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #ifndef __CYGWIN__ #include #endif // #9323, #9331 void syntaxError_timercmp(struct timeval t) { (void)timercmp(&t, &t, <); (void)timercmp(&t, &t, <=); (void)timercmp(&t, &t, ==); (void)timercmp(&t, &t, !=); (void)timercmp(&t, &t, >=); (void)timercmp(&t, &t, >); } // False negative: #9346 void uninitvar_timercmp(struct timeval t) { struct timeval uninit; (void)timercmp(&t, &uninit, <); (void)timercmp(&uninit, &t, <=); (void)timercmp(&uninit, &uninit, ==); } void nullPointer_timercmp(struct timeval t) { struct timeval *p=0; // cppcheck-suppress nullPointer (void)timercmp(&t, p, <); // cppcheck-suppress nullPointer (void)timercmp(p, &t, <=); // cppcheck-suppress nullPointer (void)timercmp(p, p, ==); } // Declaration necessary because there is no specific / portable header. extern void *xcalloc(size_t nmemb, size_t size); extern void *xmalloc(size_t size); extern void *xrealloc(void *block, size_t newsize); extern void xfree(void *ptr); void resourceLeak_mkostemps(char *template, int suffixlen, int flags) { // cppcheck-suppress unreadVariable int fp = mkostemps(template, suffixlen, flags); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkostemps_01(char *template, int suffixlen, int flags) { int fp = mkostemps(template, suffixlen, flags); close(fp); } int no_resourceLeak_mkostemps_02(char *template, int suffixlen, int flags) { return mkostemps(template, suffixlen, flags); } void resourceLeak_mkstemps(char *template, int suffixlen) { // cppcheck-suppress unreadVariable int fp = mkstemps(template, suffixlen); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkstemps_01(char *template, int suffixlen) { int fp = mkstemps(template, suffixlen); close(fp); } int no_resourceLeak_mkstemps_02(char *template, int suffixlen) { return mkstemps(template, suffixlen); } void resourceLeak_mkostemp(char *template, int flags) { // cppcheck-suppress unreadVariable int fp = mkostemp(template, flags); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkostemp_01(char *template, int flags) { int fp = mkostemp(template, flags); close(fp); } int no_resourceLeak_mkostemp_02(char *template, int flags) { return mkostemp(template, flags); } void valid_code(int argInt1, va_list valist_arg, int * parg) { char *p; if (__builtin_expect(argInt1, 0)) {} if (__builtin_expect_with_probability(argInt1 + 1, 2, 0.5)) {} if (__glibc_unlikely(argInt1 != 0)) {} if (__glibc_likely(parg != NULL)) {} void *ax1 = __builtin_assume_aligned(parg, 16); printf("%p", ax1); void *ax2 = __builtin_assume_aligned(parg, 32, 8); printf("%p", ax2); p = (char *)malloc(10); free(p); p = (char *)malloc(5); xfree(p); p = (char *)xmalloc(10); free(p); p = (char *)xmalloc(5); xfree(p); // cppcheck-suppress allocaCalled p = __builtin_alloca(5); p[0] = 1; // TODO cppcheck-suppress arrayIndexOutOfBounds p[5] = 1; __builtin_prefetch(p, 0, 1); if (__builtin_types_compatible_p(int, char)) {} char * pStr = NULL; if (vasprintf(&pStr, "%d %d", valist_arg) != -1) { free(pStr); } printf("%d", 0b010); printf("%d", __extension__ 0b10001000); if (__alignof__(int) == 4) {} void * p_mmap = mmap(NULL, 1, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); printf("%p", p_mmap); munmap(p_mmap, 1); uint16_t i16_1 = 0, i16_2; // cppcheck-suppress unreadVariable i16_2 = __builtin_bswap16(i16_1++); uint32_t i32_1 = 0, i32_2; // cppcheck-suppress unreadVariable i32_2 = __builtin_bswap32(i32_1++); uint64_t i64_1 = 0, i64_2; // cppcheck-suppress unreadVariable i64_2 = __builtin_bswap64(i64_1++); // cppcheck-suppress zerodiv // cppcheck-suppress unreadVariable i16_1 /= bswap_16(0x1234) - 0x3412; // cppcheck-suppress zerodiv // cppcheck-suppress unreadVariable i32_1 /= bswap_32(0x12345678) - 0x78563412; // cppcheck-suppress zerodiv // cppcheck-suppress unreadVariable i64_1 /= bswap_64(0x023456789abcde0f) - 0x0fdebc9a78563402; } void ignoreleak(void) { char *p = (char *)malloc(10); __builtin_memset(&(p[0]), 0, 10); // cppcheck-suppress memleak } void memleak_asprintf(char **ptr, const char *fmt, const int arg) { // No warning is expected for if (-1 != asprintf(ptr,fmt,arg)) { free(ptr); } if (-1 != asprintf(ptr,fmt,arg)) { // TODO: Related to #8980 cppcheck-suppress memleak } } void memleak_xmalloc() { char *p = (char*)xmalloc(10); p[9] = 0; // cppcheck-suppress memleak } void memleak_mmap() { void * p_mmap = mmap(NULL, 1, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); printf("%p", p_mmap); // cppcheck-suppress memleak } void uninitvar__builtin_memset(void) { void *s; int c; size_t n; // cppcheck-suppress uninitvar (void)__builtin_memset(s,c,n); } void bufferAccessOutOfBounds__builtin_memset(void) { uint8_t buf[42]; // cppcheck-suppress bufferAccessOutOfBounds (void)__builtin_memset(buf,0,1000); } void bufferAccessOutOfBounds() { char buf[2] = "a"; // This is valid sethostname(buf, 2); // cppcheck-suppress bufferAccessOutOfBounds sethostname(buf, 4); char * pAlloc1 = xcalloc(2, 4); memset(pAlloc1, 0, 8); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc1, 0, 9); free(pAlloc1); char * pAlloc2 = xmalloc(4); memset(pAlloc2, 0, 4); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc2, 0, 5); pAlloc2 = xrealloc(pAlloc2, 10); memset(pAlloc2, 0, 10); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc2, 0, 11); free(pAlloc2); } void leakReturnValNotUsed() { // cppcheck-suppress unreadVariable char* ptr = (char*)strdupa("test"); // cppcheck-suppress ignoredReturnValue strdupa("test"); // cppcheck-suppress unreadVariable char* ptr2 = (char*)strndupa("test", 1); // cppcheck-suppress ignoredReturnValue strndupa("test", 1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcasestr("test", NULL); // FIXME cppcheck-suppress knownConditionTrueFalse // cppcheck-suppress duplicateExpression if (42 == __builtin_expect(42, 0)) return; } #ifndef __CYGWIN__ int nullPointer_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { // no warning is expected (void)epoll_ctl(epfd, op, fd, event); // No nullpointer warning is expected in case op is set to EPOLL_CTL_DEL // EPOLL_CTL_DEL // Remove (deregister) the target file descriptor fd from the // epoll instance referred to by epfd. The event is ignored and // can be NULL. return epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); } #endif cppcheck-2.7/test/cfg/googletest.cpp000066400000000000000000000040451417746362400175610ustar00rootroot00000000000000 // Test library configuration for googletest.cfg // // Usage: // $ cppcheck --check-library --library=googletest --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/googletest.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include namespace ExampleNamespace { constexpr long long TOLERANCE = 10; // #9397 syntaxError when MATCHER_P is not known MATCHER_P(ExampleMatcherPTest, expected, "") { return ((arg <= (expected + TOLERANCE)) && (arg >= (expected - TOLERANCE))); } // syntaxError when MATCHER is not known MATCHER(ExampleMatcherTest, "") { return (arg == TOLERANCE); } } TEST(ASSERT, ASSERT) { int *a = (int*)calloc(10,sizeof(int)); ASSERT_TRUE(a != nullptr); a[0] = 10; free(a); } // Avoid syntax error: https://sourceforge.net/p/cppcheck/discussion/general/thread/6ccc7283e2/ TEST(test_cppcheck, cppcheck) { TestStruct it; ASSERT_THROW(it.operator->(), std::out_of_range); } // #9964 - avoid compareBoolExpressionWithInt false positive TEST(Test, assert_false_fp) { ASSERT_FALSE(errno < 0); } // Check that conditions in the ASSERT_* macros are processed correctly. TEST(Test, warning_in_assert_macros) { int i = 5; int j = 6; // cppcheck-suppress knownConditionTrueFalse ASSERT_TRUE(i == 5); // cppcheck-suppress knownConditionTrueFalse ASSERT_FALSE(i != 5); // cppcheck-suppress duplicateExpression ASSERT_EQ(i, i); ASSERT_NE(i, j); // expected knownConditionTrueFalse... ASSERT_LT(i, j); // expected knownConditionTrueFalse... // cppcheck-suppress duplicateExpression ASSERT_LE(i, i); ASSERT_GT(j, i); // expected knownConditionTrueFalse // cppcheck-suppress duplicateExpression ASSERT_GE(i, i); unsigned int u = errno; // cppcheck-suppress unsignedPositive ASSERT_GE(u, 0); // cppcheck-suppress unsignedLessThanZero ASSERT_LT(u, 0); } cppcheck-2.7/test/cfg/gtk.c000066400000000000000000000225151417746362400156340ustar00rootroot00000000000000 // Test library configuration for gtk.cfg // // Usage: // $ cppcheck --check-library --enable=information --inconclusive --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr --library=gtk test/cfg/gtk.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include void validCode(int argInt, GHashTableIter * hash_table_iter, GHashTable * hash_table) { g_assert_cmpint(4 + 1, >=, 5); g_assert_cmpstr("test", ==, "test"); // if G_UNLIKELY is not defined this results in a syntax error if G_UNLIKELY(argInt == 1) {} else if (G_UNLIKELY(argInt == 2)) {} if G_LIKELY(argInt == 0) {} else if (G_LIKELY(argInt == -1)) {} printf("%s", _("test")); printf("%s", Q_("a|test")); printf("%s", N_("test")); gpointer gpt = g_malloc(4); printf("%p", gpt); g_free(gpt); g_assert(gpt); if (!gpt) { // cppcheck-suppress checkLibraryNoReturn g_assert_not_reached(); } gpointer p = GINT_TO_POINTER(1); int i = GPOINTER_TO_INT(p); // cppcheck-suppress knownConditionTrueFalse if (i == 1) {} g_print("test"); g_print("%d", 1); g_printerr("err"); GString * pGStr1 = g_string_new("test"); g_string_append(pGStr1, "a"); g_string_free(pGStr1, TRUE); gchar * pGchar1 = g_strconcat("a", "b", NULL); printf("%s", pGchar1); g_free(pGchar1); GError * pGerror = g_error_new(1, -2, "a %d", 1); g_error_free(pGerror); static gsize init_val = 0; if (g_once_init_enter(&init_val)) { gsize result_val = 1; g_once_init_leave(&init_val, result_val); } g_hash_table_iter_replace(hash_table_iter, g_strdup("test")); g_hash_table_insert(hash_table, g_strdup("key"), g_strdup("value")); g_hash_table_replace(hash_table, g_strdup("key"), g_strdup("value")); // NULL is handled graciously char* str = g_strdup(NULL); if (g_strcmp0(str, NULL) || g_strcmp0(NULL, str)) printf("%s", str); g_free(str); } void g_malloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc(8); gpointer gpt = g_malloc(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_malloc0_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc0(8); gpointer gpt = g_malloc0(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_malloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc_n(8, 1); gpointer gpt = g_malloc_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_malloc0_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc0_n(8, 1); gpointer gpt = g_malloc0_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc(8); gpointer gpt = g_try_malloc(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc0_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc0(8); gpointer gpt = g_try_malloc0(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc_n(8, 1); gpointer gpt = g_try_malloc_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc0_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc0_n(8, 1); gpointer gpt = g_try_malloc0_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_realloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_realloc(NULL, 1); gpointer gpt = g_malloc(1); gpt = g_realloc(gpt, 2); // No memleakOnRealloc since g_realloc aborts if it fails printf("%p", gpt); // cppcheck-suppress memleak } void g_realloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_realloc_n(NULL, 1, 2); gpointer gpt = g_malloc_n(1, 2); gpt = g_realloc_n(gpt, 2, 3); // No memleakOnRealloc since g_realloc_n aborts if it fails printf("%p", gpt); // cppcheck-suppress memleak } void g_try_realloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_realloc(NULL, 1); gpointer gpt = g_try_malloc(1); // cppcheck-suppress memleakOnRealloc gpt = g_try_realloc(gpt, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_realloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_realloc_n(NULL, 1, 2); gpointer gpt = g_try_malloc_n(1, 2); // cppcheck-suppress memleakOnRealloc gpt = g_try_realloc_n(gpt, 2, 3); printf("%p", gpt); // cppcheck-suppress memleak } void g_assert_test() { int a; // cppcheck-suppress checkLibraryNoReturn // cppcheck-suppress assignmentInAssert g_assert(a = 5); } void g_print_test() { // cppcheck-suppress invalidPrintfArgType_uint g_print("%u", -1); // cppcheck-suppress invalidPrintfArgType_uint g_printerr("%x", "a"); } void g_alloca_test() { // cppcheck-suppress allocaCalled char * pBuf1 = g_alloca(5); pBuf1[0] = '\0'; } void g_new_test() { struct a { int b; }; // valid struct a * pNew1 = g_new(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_new(struct a, 1); struct a * pNew2 = g_new(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_new_if_test() { struct a { int b; }; struct a * pNew3; if (pNew3 = g_new(struct a, 6)) { printf("%p", pNew3); } // cppcheck-suppress memleak } void g_new0_test() { struct a { int b; }; // valid struct a * pNew1 = g_new0(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_new0(struct a, 1); struct a * pNew2 = g_new0(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_try_new_test() { struct a { int b; }; // valid struct a * pNew1 = g_try_new(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_try_new(struct a, 1); struct a * pNew2 = g_try_new(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_try_new0_test() { struct a { int b; }; // valid struct a * pNew1 = g_try_new0(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_try_new0(struct a, 1); struct a * pNew2 = g_try_new0(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_renew_test() { struct a { int b; }; // cppcheck-suppress leakReturnValNotUsed g_renew(struct a, NULL, 1); struct a * pNew = g_new(struct a, 1); pNew = g_renew(struct a, pNew, 2); // No memleakOnRealloc since g_renew aborts if it fails printf("%p", pNew); // cppcheck-suppress memleak } void g_try_renew_test() { struct a { int b; }; // cppcheck-suppress leakReturnValNotUsed g_try_renew(struct a, NULL, 1); struct a * pNew = g_try_new(struct a, 1); // cppcheck-suppress memleakOnRealloc pNew = g_try_renew(struct a, pNew, 2); printf("%p", pNew); // cppcheck-suppress memleak } void g_error_new_test() { // valid GError * pNew1 = g_error_new(1, -2, "a %d", 1); printf("%p", pNew1); g_error_free(pNew1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_error_new(1, -2, "a %d", 1); GError * pNew2 = g_error_new(1, -2, "a %d", 1); printf("%p", pNew2); // cppcheck-suppress memleak } void g_once_init_enter_leave_test() { static gsize init_val; if (g_once_init_enter(&init_val)) { gsize result_val = 0; // cppcheck-suppress invalidFunctionArg g_once_init_leave(&init_val, result_val); } gsize init_val2; // cppcheck-suppress uninitvar // cppcheck-suppress ignoredReturnValue g_once_init_enter(&init_val2); gsize * init_val3 = NULL; // cppcheck-suppress nullPointer if (g_once_init_enter(init_val3)) { gsize* init_val31 = NULL; // cppcheck-suppress nullPointer g_once_init_leave(init_val31, 1); } gsize * init_val4; // cppcheck-suppress uninitvar if (g_once_init_enter(init_val4)) { gsize * init_val5; // cppcheck-suppress uninitvar g_once_init_leave(init_val5, 1); } } void g_strchug_g_strchomp_test(gchar * str1) { g_strchug(str1); g_strchomp(str1); g_strchug(g_strchomp(str1)); gchar * str2; // cppcheck-suppress uninitvar g_strchug(str2); gchar * str3; // cppcheck-suppress uninitvar g_strchomp(str3); } void g_abort_test() { g_abort(); //cppcheck-suppress unreachableCode printf("Never reached"); } cppcheck-2.7/test/cfg/kde.cpp000066400000000000000000000017041417746362400161470ustar00rootroot00000000000000 // Test library configuration for kde.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/kde.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include class k_global_static_testclass1 {}; K_GLOBAL_STATIC(k_global_static_testclass1, k_global_static_testinstance1); void valid_code(const KConfigGroup& cfgGroup) { k_global_static_testclass1 * pk_global_static_testclass1 = k_global_static_testinstance1; printf("%p", pk_global_static_testclass1); bool entryTest = cfgGroup.readEntry("test", false); if (entryTest) {} } void ignoredReturnValue(const KConfigGroup& cfgGroup) { // cppcheck-suppress ignoredReturnValue cfgGroup.readEntry("test", "default"); // cppcheck-suppress ignoredReturnValue cfgGroup.readEntry("test"); } cppcheck-2.7/test/cfg/libcurl.c000066400000000000000000000041701417746362400165000ustar00rootroot00000000000000 // Test library configuration for libcurl.cfg // // Usage: // $ cppcheck --check-library --library=libcurl --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/libcurl.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode() { CURL *curl = curl_easy_init(); if (curl) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); res = curl_easy_perform(curl); if (res != CURLE_OK) { printf("error"); } else { long response_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); printf("%ld", response_code); char * pStr = curl_easy_escape(curl, "a", 1); if (pStr) printf("%s", pStr); curl_free(pStr); curl_easy_reset(curl); } curl_easy_cleanup(curl); } } void ignoredReturnValue(CURL * handle) { // cppcheck-suppress ignoredReturnValue curl_easy_strerror(1); } void resourceLeak_curl_easy_init() { CURL *curl = curl_easy_init(); printf("%p", curl); // cppcheck-suppress resourceLeak } void resourceLeak_curl_easy_duphandle(CURL * handle) { CURL *curl = curl_easy_duphandle(handle); printf("%p", curl); // cppcheck-suppress resourceLeak } void memleak_curl_easy_escape(CURL * handle) { char * pStr = curl_easy_escape(handle, "a", 1); if (pStr) printf("%s", pStr); // cppcheck-suppress memleak } void nullPointer(CURL * handle) { char * buf[10] = {0}; size_t len; curl_easy_recv(handle, buf, 10, &len); // cppcheck-suppress nullPointer curl_easy_recv(handle, buf, 10, NULL); curl_easy_send(handle, buf, 10, &len); // cppcheck-suppress nullPointer curl_easy_send(handle, buf, 10, NULL); } void uninitvar(CURL * handle) { char * bufInit[10] = {0}; char * bufUninit; size_t len; curl_easy_send(handle, bufInit, 10, &len); // cppcheck-suppress uninitvar curl_easy_send(handle, bufUninit, 10, &len); } cppcheck-2.7/test/cfg/libsigc++.cpp000066400000000000000000000012661417746362400171510ustar00rootroot00000000000000 // Test library configuration for libsigc++.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/libsigc++.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include struct struct1 : public sigc::trackable { void func1(int) const {} }; void valid_code() { const struct1 my_sruct1; sigc::slot sl = sigc::mem_fun(my_sruct1, &struct1::func1); if (sl) {} } void ignoredReturnValue() { const struct1 my_sruct1; // cppcheck-suppress ignoredReturnValue sigc::mem_fun(my_sruct1, &struct1::func1); } cppcheck-2.7/test/cfg/lua.c000066400000000000000000000014021417746362400156200ustar00rootroot00000000000000 // Test library configuration for lua.cfg // // Usage: // $ cppcheck --check-library --library=lua --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/lua.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode(lua_State *L) { int a = lua_gettop(L); printf("%d", a); lua_pushnil(L); lua_pop(L, 1); } void ignoredReturnValue(lua_State *L) { // cppcheck-suppress ignoredReturnValue lua_tonumber(L, 1); // cppcheck-suppress ignoredReturnValue lua_tostring(L, 1); // cppcheck-suppress ignoredReturnValue lua_isboolean(L, 1); // cppcheck-suppress ignoredReturnValue lua_isnil(L, 1); } cppcheck-2.7/test/cfg/opencv2.cpp000066400000000000000000000020211417746362400167510ustar00rootroot00000000000000 // Test library configuration for opencv2.cfg // // Usage: // $ cppcheck --check-library --library=cairo --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/opencv2.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode(char* argStr) { cv::Mat image; image = cv::imread(argStr, cv::IMREAD_COLOR); if (!image.data) { printf("No image data \n"); return; } cv::namedWindow("Display Image", cv::WINDOW_AUTOSIZE); cv::imshow("Display Image", image); cv::waitKey(0); cv::String cvStr("Hello"); cvStr += " World"; std::cout << cvStr; char * pBuf = (char *)cv::fastMalloc(20); cv::fastFree(pBuf); } void ignoredReturnValue() { // cppcheck-suppress ignoredReturnValue cv::imread("42.png"); } void memleak() { char * pBuf = (char *)cv::fastMalloc(1000); std::cout << pBuf; // cppcheck-suppress memleak } cppcheck-2.7/test/cfg/openmp.c000066400000000000000000000014471417746362400163460ustar00rootroot00000000000000 // Test library configuration for openmp.cfg // // Usage: // $ cppcheck --check-library --library=openmp --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/openmp.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode() { int arr[20] = { 0 }; #pragma omp parallel for for (int i = 0; i < 20; ++i) { // cppcheck-suppress unreadVariable arr[i] = i * i; } char * pChars = (char *) omp_target_alloc(4, 1); printf("pChars: %p", pChars); omp_target_free(pChars, 1); } void memleak_omp_target_alloc() { char * pChars = (char *) omp_target_alloc(2, 0); printf("pChars: %p", pChars); // cppcheck-suppress memleak } cppcheck-2.7/test/cfg/openssl.c000066400000000000000000000033211417746362400165240ustar00rootroot00000000000000 // Test library configuration for openssl.cfg // // Usage: // $ cppcheck --check-library --library=openssl --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/openssl.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include void valid_code(BIO * bio) { BIO_printf(bio, "%d\n", 1); } // Example for encrypting a string using IDEA (from https://www.openssl.org/docs/man1.1.1/man3/EVP_CIPHER_CTX_new.html) int valid_code_do_crypt(char *outfile) { unsigned char outbuf[1024]; int outlen, tmplen; unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; unsigned char iv[] = {1,2,3,4,5,6,7,8}; char intext[] = "Some Crypto Text"; EVP_CIPHER_CTX *ctx; FILE *out; ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); if (!EVP_EncryptUpdate(ctx, outbuf, &outlen, intext, strlen(intext))) { /* Error */ EVP_CIPHER_CTX_free(ctx); return 0; } if (!EVP_EncryptFinal_ex(ctx, outbuf + outlen, &tmplen)) { /* Error */ EVP_CIPHER_CTX_free(ctx); return 0; } outlen += tmplen; EVP_CIPHER_CTX_free(ctx); out = fopen(outfile, "wb"); if (out == NULL) { /* Error */ return 0; } fwrite(outbuf, 1, outlen, out); fclose(out); return 1; } void invalidPrintfArgType_test(BIO * bio) { // cppcheck-suppress invalidPrintfArgType_sint BIO_printf(bio, "%d\n", 5U); } void EVP_CIPHER_CTX_new_test() { EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new(); printf("%p", ctx); // cppcheck-suppress resourceLeak } cppcheck-2.7/test/cfg/posix.c000066400000000000000000000370721417746362400162150ustar00rootroot00000000000000 // Test library configuration for posix.cfg // // Usage: // $ cppcheck --check-library --library=posix --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/posix.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include // <- FILE #include #include #include #include #include #include #include // unavailable on some linux systems #include #include #include #include #include #include #include #include #include #include #include #include char * overlappingWriteFunction_stpcpy(char *src, char *dest) { // No warning shall be shown: (void) stpcpy(dest, src); // cppcheck-suppress overlappingWriteFunction return stpcpy(src, src); } void overlappingWriteFunction_bcopy(char *buf, const size_t count) { // No warning shall be shown: // cppcheck-suppress bcopyCalled bcopy(&buf[0], &buf[3], count); // size is not known // cppcheck-suppress bcopyCalled bcopy(&buf[0], &buf[3], 3U); // no-overlap // cppcheck-suppress bcopyCalled // cppcheck-suppress overlappingWriteFunction bcopy(&buf[0], &buf[3], 4U); } void overlappingWriteFunction_memccpy(unsigned char *src, unsigned char *dest, int c, size_t count) { // No warning shall be shown: (void)memccpy(dest, src, c, count); (void)memccpy(dest, src, 42, count); // cppcheck-suppress overlappingWriteFunction (void)memccpy(dest, dest, c, 4); // cppcheck-suppress overlappingWriteFunction (void)memccpy(dest, dest+3, c, 4); } void overlappingWriteFunction_stpncpy(char *src, char *dest, ssize_t n) { // No warning shall be shown: (void) stpncpy(dest, src, n); // cppcheck-suppress overlappingWriteFunction (void)stpncpy(src, src+3, 4); } wchar_t* overlappingWriteFunction_wcpncpy(wchar_t *src, wchar_t *dest, ssize_t n) { // No warning shall be shown: (void) wcpncpy(dest, src, n); // cppcheck-suppress overlappingWriteFunction return wcpncpy(src, src+3, 4); } void overlappingWriteFunction_swab(char *src, char *dest, ssize_t n) { // No warning shall be shown: swab(dest, src, n); // cppcheck-suppress overlappingWriteFunction swab(src, src+3, 4); } bool invalidFunctionArgBool_isascii(bool b, int c) { // cppcheck-suppress invalidFunctionArgBool (void)isascii(b); // cppcheck-suppress invalidFunctionArgBool return isascii(c != 0); } void uninitvar_putenv(char * envstr) { // No warning is expected (void)putenv(envstr); char * p; // cppcheck-suppress uninitvar (void)putenv(p); } void nullPointer_putenv(char * envstr) { // No warning is expected (void)putenv(envstr); char * p=NULL; // cppcheck-suppress nullPointer (void)putenv(p); } void memleak_scandir(void) { struct dirent **namelist; int n = scandir(".", &namelist, NULL, alphasort); if (n == -1) { return; } // http://man7.org/linux/man-pages/man3/scandir.3.html /* The scandir() function scans the directory dirp, calling filter() on each directory entry. Entries for which filter() returns nonzero are stored in strings allocated via malloc(3), sorted using qsort(3) with the comparison function compar(), and collected in array namelist which is allocated via malloc(3). If filter is NULL, all entries are selected.*/ // TODO: cppcheck-suppress memleak } void no_memleak_scandir(void) { struct dirent **namelist; int n = scandir(".", &namelist, NULL, alphasort); if (n == -1) { return; } while (n--) { free(namelist[n]); } free(namelist); } void validCode(va_list valist_arg1, va_list valist_arg2) { void *ptr; if (posix_memalign(&ptr, sizeof(void *), sizeof(void *)) == 0) free(ptr); syslog(LOG_ERR, "err %u", 0U); syslog(LOG_WARNING, "warn %d %d", 5, 1); vsyslog(LOG_EMERG, "emerg %d", valist_arg1); vsyslog(LOG_INFO, "test %s %d %p", valist_arg2); void* handle = dlopen("/lib.so", RTLD_NOW); if (handle) { dlclose(handle); } } void bufferAccessOutOfBounds(int fd) { char a[5]; read(fd,a,5); // cppcheck-suppress bufferAccessOutOfBounds read(fd,a,6); write(fd,a,5); // cppcheck-suppress bufferAccessOutOfBounds write(fd,a,6); recv(fd,a,5,0); // cppcheck-suppress bufferAccessOutOfBounds recv(fd,a,6,0); recvfrom(fd,a,5,0,0x0,0x0); // cppcheck-suppress bufferAccessOutOfBounds recvfrom(fd,a,6,0,0x0,0x0); send(fd,a,5,0); // cppcheck-suppress bufferAccessOutOfBounds send(fd,a,6,0); sendto(fd,a,5,0,0x0,0x0); // cppcheck-suppress bufferAccessOutOfBounds sendto(fd,a,6,0,0x0,0x0); // cppcheck-suppress constStatement 0; readlink("path", a, 5); // cppcheck-suppress bufferAccessOutOfBounds readlink("path", a, 6); readlinkat(1, "path", a, 5); // cppcheck-suppress bufferAccessOutOfBounds readlinkat(1, "path", a, 6); // This is valid gethostname(a, 5); // cppcheck-suppress bufferAccessOutOfBounds gethostname(a, 6); } void nullPointer(char *p, int fd, pthread_mutex_t mutex) { // cppcheck-suppress ignoredReturnValue isatty(0); mkdir(p, 0); getcwd(0, 0); // cppcheck-suppress nullPointer // cppcheck-suppress readdirCalled readdir(0); // cppcheck-suppress nullPointer // cppcheck-suppress utimeCalled utime(NULL, NULL); // not implemented yet: cppcheck-suppress nullPointer read(fd,NULL,42); read(fd,NULL,0); // not implemented yet: cppcheck-suppress nullPointer write(fd,NULL,42); write(fd,NULL,0); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer open(NULL, 0); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer open(NULL, 0, 0); // cppcheck-suppress unreadVariable // cppcheck-suppress nullPointer int ret = access(NULL, 0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer fdopen(fd, NULL); // cppcheck-suppress strtokCalled // cppcheck-suppress nullPointer strtok(p, NULL); // cppcheck-suppress nullPointer pthread_mutex_init(NULL, NULL); // Second argument can be NULL pthread_mutex_init(&mutex, NULL); // cppcheck-suppress nullPointer pthread_mutex_destroy(NULL); // cppcheck-suppress nullPointer pthread_mutex_lock(NULL); // cppcheck-suppress nullPointer (void)pthread_mutex_trylock(NULL); // cppcheck-suppress nullPointer pthread_mutex_unlock(NULL); } void memleak_getaddrinfo() { //TODO: nothing to report yet, see http://sourceforge.net/p/cppcheck/discussion/general/thread/d9737d5d/ struct addrinfo * res=NULL; getaddrinfo("node", NULL, NULL, &res); freeaddrinfo(res); } void memleak_mmap(int fd) { // cppcheck-suppress unusedAllocatedMemory // cppcheck-suppress unreadVariable void *addr = mmap(NULL, 255, PROT_NONE, MAP_PRIVATE, fd, 0); // cppcheck-suppress memleak } void * memleak_mmap2() // #8327 { void * data = mmap(NULL, 10, PROT_READ, MAP_PRIVATE, 1, 0); if (data != MAP_FAILED) return data; return NULL; } void * identicalCondition_mmap(int fd, size_t size) // #9940 { void* buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buffer == MAP_FAILED) { return NULL; } return buffer; } void resourceLeak_fdopen(int fd) { // cppcheck-suppress unreadVariable FILE *f = fdopen(fd, "r"); // cppcheck-suppress resourceLeak } void resourceLeak_mkstemp(char *template) { // cppcheck-suppress unreadVariable int fp = mkstemp(template); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkstemp_01(char *template) { int fp = mkstemp(template); close(fp); } int no_resourceLeak_mkstemp_02(char *template) { return mkstemp(template); } void resourceLeak_fdopendir(int fd) { // cppcheck-suppress unreadVariable DIR* leak1 = fdopendir(fd); // cppcheck-suppress resourceLeak } void resourceLeak_opendir(void) { // cppcheck-suppress unreadVariable DIR* leak1 = opendir("abc"); // cppcheck-suppress resourceLeak } void resourceLeak_socket(void) { // cppcheck-suppress unreadVariable int s = socket(AF_INET, SOCK_STREAM, 0); // cppcheck-suppress resourceLeak } void resourceLeak_open1(void) { // cppcheck-suppress unreadVariable int fd = open("file", O_RDWR | O_CREAT); // cppcheck-suppress resourceLeak } void resourceLeak_open2(void) { // cppcheck-suppress unreadVariable int fd = open("file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); // cppcheck-suppress resourceLeak } void noleak(int x, int y, int z) { DIR *p1 = fdopendir(x); closedir(p1); DIR *p2 = opendir("abc"); closedir(p2); int s = socket(AF_INET,SOCK_STREAM,0); close(s); int fd1 = open("a", O_RDWR | O_CREAT); close(fd1); int fd2 = open("a", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); close(fd2); /* TODO: add configuration for open/fdopen // #2830 int fd = open("path", O_RDONLY); FILE *f = fdopen(fd, "rt"); fclose(f); */ } // unused return value void ignoredReturnValue(void *addr, int fd) { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed mmap(addr, 255, PROT_NONE, MAP_PRIVATE, fd, 0); // cppcheck-suppress ignoredReturnValue getuid(); // cppcheck-suppress ignoredReturnValue access("filename", 1); // no ignoredReturnValue shall be shown for setuid(42); } // valid range void invalidFunctionArg() { // cppcheck-suppress invalidFunctionArg // cppcheck-suppress usleepCalled usleep(-1); // cppcheck-suppress usleepCalled usleep(0); // cppcheck-suppress usleepCalled usleep(999999); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress usleepCalled usleep(1000000); } void invalidFunctionArg_close(int fd) { if (fd < 0) { // cppcheck-suppress invalidFunctionArg (void)close(fd); } } void uninitvar(int fd) { int x1, x2, x3, x4; char buf[2]; int decimal, sign; double d; void *p; pthread_mutex_t mutex, mutex1, mutex2, mutex3; // cppcheck-suppress uninitvar write(x1,"ab",2); // TODO cppcheck-suppress uninitvar write(fd,buf,2); // #6325 // cppcheck-suppress uninitvar write(fd,"ab",x2); // cppcheck-suppress uninitvar write(fd,p,2); /* int regcomp(regex_t *restrict preg, const char *restrict pattern, int cflags); */ regex_t reg; const char * pattern; int cflags1, cflags2; // cppcheck-suppress uninitvar regcomp(®, pattern, cflags1); pattern=""; // cppcheck-suppress uninitvar regcomp(®, pattern, cflags2); regerror(0, ®, 0, 0); #ifndef __CYGWIN__ // cppcheck-suppress uninitvar // cppcheck-suppress unreadVariable // cppcheck-suppress ecvtCalled char *buffer = ecvt(d, 11, &decimal, &sign); #endif // cppcheck-suppress gcvtCalled gcvt(3.141, 2, buf); char *filename1, *filename2; struct utimbuf *times; // cppcheck-suppress uninitvar // cppcheck-suppress utimeCalled utime(filename1, times); struct timeval times1[2]; // cppcheck-suppress uninitvar // cppcheck-suppress utimeCalled utime(filename2, times1); // cppcheck-suppress unreadVariable // cppcheck-suppress uninitvar int access_ret = access("file", x3); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar fdopen(x4, "rw"); char *strtok_arg1; // cppcheck-suppress strtokCalled // cppcheck-suppress uninitvar strtok(strtok_arg1, ";"); // cppcheck-suppress uninitvar pthread_mutex_lock(&mutex1); // cppcheck-suppress uninitvar (void)pthread_mutex_trylock(&mutex2); // cppcheck-suppress uninitvar pthread_mutex_unlock(&mutex3); // after initialization it must be OK to call lock, trylock and unlock for this mutex pthread_mutex_init(&mutex, NULL); pthread_mutex_lock(&mutex); (void)pthread_mutex_trylock(&mutex); pthread_mutex_unlock(&mutex); } void uninitvar_getcwd(void) { char *buf; size_t size; // cppcheck-suppress uninitvar (void)getcwd(buf,size); } void uninitvar_types(void) { // cppcheck-suppress unassignedVariable blkcnt_t b; // cppcheck-suppress uninitvar b + 1; struct dirent d; // TODO cppcheck-suppress uninitvar d.d_ino + 1; } void timet_h(struct timespec* ptp1) { clockid_t clk_id1, clk_id2, clk_id3; struct timespec* ptp; // cppcheck-suppress uninitvar clock_settime(CLOCK_REALTIME, ptp); // cppcheck-suppress uninitvar clock_settime(clk_id1, ptp); // cppcheck-suppress uninitvar clock_settime(clk_id2, ptp1); struct timespec tp; // TODO cppcheck-suppress uninitvar clock_settime(CLOCK_REALTIME, &tp); // #6577 - false negative // cppcheck-suppress uninitvar clock_settime(clk_id3, &tp); time_t clock = time(0); char buf[26]; // cppcheck-suppress ctime_rCalled ctime_r(&clock, buf); } void dl(const char* libname, const char* func) { void* lib = dlopen(libname, RTLD_NOW); // cppcheck-suppress redundantInitialization // cppcheck-suppress resourceLeak lib = dlopen(libname, RTLD_LAZY); const char* funcname; // cppcheck-suppress uninitvar // cppcheck-suppress unreadVariable void* sym = dlsym(lib, funcname); // cppcheck-suppress ignoredReturnValue dlsym(lib, "foo"); void* uninit; // cppcheck-suppress uninitvar dlclose(uninit); // cppcheck-suppress resourceLeak } void asctime_r_test(struct tm * tm, char * bufSizeUnknown) { struct tm tm_uninit_data; struct tm * tm_uninit_pointer; char bufSize5[5]; char bufSize25[25]; char bufSize26[26]; char bufSize100[100]; // cppcheck-suppress asctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds asctime_r(tm, bufSize5); // cppcheck-suppress asctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds asctime_r(tm, bufSize25); // cppcheck-suppress asctime_rCalled asctime_r(tm, bufSize26); // cppcheck-suppress asctime_rCalled asctime_r(tm, bufSize100); // cppcheck-suppress asctime_rCalled // cppcheck-suppress uninitvar asctime_r(&tm_uninit_data, bufSize100); // cppcheck-suppress asctime_rCalled // cppcheck-suppress uninitvar asctime_r(tm_uninit_pointer, bufSize100); // cppcheck-suppress asctime_rCalled asctime_r(tm, bufSizeUnknown); } void ctime_r_test(time_t * timep, char * bufSizeUnknown) { time_t time_t_uninit_data; time_t * time_t_uninit_pointer; char bufSize5[5]; char bufSize25[25]; char bufSize26[26]; char bufSize100[100]; // cppcheck-suppress ctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds ctime_r(timep, bufSize5); // cppcheck-suppress ctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds ctime_r(timep, bufSize25); // cppcheck-suppress ctime_rCalled ctime_r(timep, bufSize26); // cppcheck-suppress ctime_rCalled ctime_r(timep, bufSize100); // cppcheck-suppress ctime_rCalled // cppcheck-suppress uninitvar ctime_r(&time_t_uninit_data, bufSize100); // cppcheck-suppress ctime_rCalled // cppcheck-suppress uninitvar ctime_r(time_t_uninit_pointer, bufSize100); // cppcheck-suppress ctime_rCalled ctime_r(timep, bufSizeUnknown); } cppcheck-2.7/test/cfg/python.c000066400000000000000000000027061417746362400163700ustar00rootroot00000000000000 // Test library configuration for python.cfg // // Usage: // $ cppcheck --check-library --library=python --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/python.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #define PY_SSIZE_T_CLEAN #include // should be the first include #include void validCode(PyObject * pPyObjArg) { PyObject * pPyObjNULL = NULL; Py_Initialize(); Py_INCREF(pPyObjArg); Py_DECREF(pPyObjArg); Py_XINCREF(pPyObjArg); Py_XINCREF(pPyObjNULL); Py_XDECREF(pPyObjArg); Py_XDECREF(pPyObjNULL); Py_CLEAR(pPyObjArg); Py_CLEAR(pPyObjNULL); (void)PyErr_NewException("text", NULL, NULL); char * pBuf1 = PyMem_Malloc(5); PyMem_Free(pBuf1); int * pIntBuf1 = PyMem_New(int, 10); PyMem_Free(pIntBuf1); } void nullPointer() { // cppcheck-suppress nullPointer Py_INCREF(NULL); // cppcheck-suppress nullPointer Py_DECREF(NULL); } void PyMem_Malloc_memleak() { char * pBuf1 = PyMem_Malloc(1); printf("%p", pBuf1); // cppcheck-suppress memleak } void PyMem_Malloc_mismatchAllocDealloc() { // cppcheck-suppress unusedAllocatedMemory char * pBuf1 = PyMem_Malloc(10); // cppcheck-suppress mismatchAllocDealloc free(pBuf1); } void PyMem_New_memleak() { char * pBuf1 = PyMem_New(char, 5); printf("%p", pBuf1); // cppcheck-suppress memleak } cppcheck-2.7/test/cfg/qt.cpp000066400000000000000000000316511417746362400160340ustar00rootroot00000000000000 // Test library configuration for qt.cfg // // Usage: // $ cppcheck --check-library --enable=information --inconclusive --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr --library=qt test/cfg/qt.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #include #include #include #include #include void QString1(QString s) { for (int i = 0; i <= s.size(); ++i) { // cppcheck-suppress stlOutOfBounds s[i] = 'x'; } } int QString2() { QString s; // FIXME cppcheck-suppress reademptycontainer return s.size(); } QString::iterator QString3() { QString qstring1; QString qstring2; // cppcheck-suppress mismatchingContainers for (QString::iterator it = qstring1.begin(); it != qstring2.end(); ++it) {} QString::iterator it = qstring1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } void QString4() { // cppcheck-suppress unusedVariable QString qs; } // cppcheck-suppress passedByValue bool QString5(QString s) { // #10710 return s.isEmpty(); } // cppcheck-suppress passedByValue QStringList QString6(QString s) { return QStringList{ "*" + s + "*" }; } // cppcheck-suppress passedByValue bool QString7(QString s, const QString& l) { return l.startsWith(s); } void QByteArray1(QByteArray byteArrayArg) { for (int i = 0; i <= byteArrayArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds byteArrayArg[i] = 'x'; } // cppcheck-suppress containerOutOfBoundsIndexExpression byteArrayArg[byteArrayArg.length()] = 'a'; // cppcheck-suppress containerOutOfBoundsIndexExpression byteArrayArg[byteArrayArg.count()] = 'b'; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %c\n", byteArrayArg[byteArrayArg.size()]); QByteArray byteArray1{'a', 'b'}; (void)byteArray1[1]; // cppcheck-suppress ignoredReturnValue byteArray1.at(1); } void QList1(QList intListArg) { for (int i = 0; i <= intListArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds intListArg[i] = 1; } // cppcheck-suppress containerOutOfBoundsIndexExpression intListArg[intListArg.length()] = 5; // cppcheck-suppress containerOutOfBoundsIndexExpression intListArg[intListArg.count()] = 10; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %d\n", intListArg[intListArg.size()]); QList qstringList1{"one", "two"}; (void)qstringList1[1]; QList qstringList2 = {"one", "two"}; (void)qstringList2[1]; qstringList2.clear(); // cppcheck-suppress containerOutOfBounds (void)qstringList2[1]; QList qstringList3; qstringList3 << "one" << "two"; (void)qstringList3[1]; // FIXME: The following should have a containerOutOfBounds suppression #9242 (void)qstringList3[3]; // cppcheck-suppress ignoredReturnValue qstringList3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringList3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringList3.count(); // cppcheck-suppress ignoredReturnValue qstringList3.length(); // cppcheck-suppress ignoredReturnValue qstringList3.size(); // cppcheck-suppress ignoredReturnValue qstringList3.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringList3.at(-5); QList qstringList4; // cppcheck-suppress containerOutOfBounds (void)qstringList4[0]; qstringList4.append("a"); (void)qstringList4[0]; qstringList4.clear(); // cppcheck-suppress containerOutOfBounds (void)qstringList4[0]; } QList QList2() { // #10556 QList v; for (int i = 0; i < 4; ++i) { v.append(i); (void)v.at(i); } return v; } QList::iterator QList3() { QList qlist1; QList qlist2; // cppcheck-suppress mismatchingContainers for (QList::iterator it = qlist1.begin(); it != qlist2.end(); ++it) {} QList::iterator it = qlist1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } void QLinkedList1() { // cppcheck-suppress unreadVariable QLinkedList qstringLinkedList1{"one", "two"}; QLinkedList qstringLinkedList2 = {"one", "two"}; qstringLinkedList2.clear(); QLinkedList qstringLinkedList3; qstringLinkedList3 << "one" << "two"; // cppcheck-suppress ignoredReturnValue qstringLinkedList3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringLinkedList3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringLinkedList3.count(); // cppcheck-suppress ignoredReturnValue qstringLinkedList3.size(); QLinkedList qstringLinkedList4; qstringLinkedList4.append("a"); qstringLinkedList4.clear(); } QLinkedList::iterator QLinkedList3() { QLinkedList intQLinkedList1; QLinkedList intQLinkedList2; // cppcheck-suppress mismatchingContainers for (QLinkedList::iterator it = intQLinkedList1.begin(); it != intQLinkedList2.end(); ++it) {} QLinkedList::iterator it = intQLinkedList1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } void QStringList1(QStringList stringlistArg) { for (int i = 0; i <= stringlistArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds stringlistArg[i] = "abc"; } // cppcheck-suppress containerOutOfBoundsIndexExpression stringlistArg[stringlistArg.length()] = "ab"; stringlistArg[stringlistArg.length() - 1] = "ab"; // could be valid // cppcheck-suppress containerOutOfBoundsIndexExpression stringlistArg[stringlistArg.count()] = "12"; stringlistArg[stringlistArg.count() - 1] = "12"; // could be valid // cppcheck-suppress containerOutOfBoundsIndexExpression (void)stringlistArg[stringlistArg.size()]; (void)stringlistArg[stringlistArg.size() - 1]; // could be valid QStringList qstringlist1{"one", "two"}; (void)qstringlist1[1]; QStringList qstringlist2 = {"one", "two"}; (void)qstringlist2[1]; QStringList qstringlist3; qstringlist3 << "one" << "two"; (void)qstringlist3[1]; // FIXME: The following should have a containerOutOfBounds suppression #9242 (void)qstringlist3[3]; // cppcheck-suppress ignoredReturnValue qstringlist3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringlist3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringlist3.count(); // cppcheck-suppress ignoredReturnValue qstringlist3.length(); // cppcheck-suppress ignoredReturnValue qstringlist3.size(); // cppcheck-suppress ignoredReturnValue qstringlist3.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringlist3.at(-5); } QStringList::iterator QStringList2() { QStringList qstringlist1; QStringList qstringlist2; // cppcheck-suppress mismatchingContainers for (QStringList::iterator it = qstringlist1.begin(); it != qstringlist2.end(); ++it) {} QStringList::iterator it = qstringlist1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } void QVector1(QVector intVectorArg) { for (int i = 0; i <= intVectorArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds intVectorArg[i] = 1; } // cppcheck-suppress containerOutOfBoundsIndexExpression intVectorArg[intVectorArg.length()] = 5; // cppcheck-suppress containerOutOfBoundsIndexExpression intVectorArg[intVectorArg.count()] = 10; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %d\n", intVectorArg[intVectorArg.size()]); QVector qstringVector1{"one", "two"}; (void)qstringVector1[1]; QVector qstringVector2 = {"one", "two"}; (void)qstringVector2[1]; QVector qstringVector3; qstringVector3 << "one" << "two"; (void)qstringVector3[1]; // FIXME: The following should have a containerOutOfBounds suppression #9242 (void)qstringVector3[3]; // cppcheck-suppress ignoredReturnValue qstringVector3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringVector3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringVector3.count(); // cppcheck-suppress ignoredReturnValue qstringVector3.length(); // cppcheck-suppress ignoredReturnValue qstringVector3.size(); // cppcheck-suppress ignoredReturnValue qstringVector3.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringVector3.at(-5); } QVector::iterator QVector2() { QVector qvector1; QVector qvector2; // cppcheck-suppress mismatchingContainers for (QVector::iterator it = qvector1.begin(); it != qvector2.end(); ++it) {} QVector::iterator it = qvector1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } // cppcheck-suppress passedByValue void duplicateExpression_QString_Compare(QString style) //#8723 { // cppcheck-suppress duplicateExpression if (style.compare( "x", Qt::CaseInsensitive ) == 0 || style.compare( "x", Qt::CaseInsensitive ) == 0) {} } void QStack1(QStack intStackArg) { for (int i = 0; i <= intStackArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds intStackArg[i] = 1; } // cppcheck-suppress containerOutOfBoundsIndexExpression intStackArg[intStackArg.length()] = 5; // cppcheck-suppress containerOutOfBoundsIndexExpression intStackArg[intStackArg.count()] = 10; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %d\n", intStackArg[intStackArg.size()]); QStack qstringStack1; qstringStack1.push("one"); qstringStack1.push("two"); (void)qstringStack1[1]; QStack qstringStack2; qstringStack2 << "one" << "two"; (void)qstringStack2[1]; // FIXME: The following should have a containerOutOfBounds suppression #9242 (void)qstringStack2[3]; // cppcheck-suppress ignoredReturnValue qstringStack2.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringStack2.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringStack2.count(); // cppcheck-suppress ignoredReturnValue qstringStack2.length(); // cppcheck-suppress ignoredReturnValue qstringStack2.size(); // cppcheck-suppress ignoredReturnValue qstringStack2.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringStack2.at(-5); } QStack::iterator QStack2() { QStack qstack1; QStack qstack2; // cppcheck-suppress mismatchingContainers for (QStack::iterator it = qstack1.begin(); it != qstack2.end(); ++it) {} QStack::iterator it = qstack1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } void QStack3() { QStack intStack; intStack.push(1); // cppcheck-suppress ignoredReturnValue intStack.top(); intStack.pop(); } // Verify that Qt macros do not result in syntax errors, false positives or other issues. class MacroTest1 : public QObject { Q_OBJECT Q_PLUGIN_METADATA(IID "com.foo.bar" FILE "test.json") public: explicit MacroTest1(QObject *parent = 0); ~MacroTest1(); }; class MacroTest2 { Q_DECLARE_TR_FUNCTIONS(MacroTest2) public: MacroTest2(); ~MacroTest2(); }; void MacroTest2_test() { QString str = MacroTest2::tr("hello"); QByteArray ba = str.toLatin1(); printf(ba.data()); #ifndef QT_NO_DEPRECATED str = MacroTest2::trUtf8("test2"); ba = str.toLatin1(); printf(ba.data()); #endif } void validCode(int * pIntPtr, QString & qstrArg) { if (QFile::exists("test")) {} if (pIntPtr != Q_NULLPTR) { *pIntPtr = 5; } if (pIntPtr && *pIntPtr == 1) { forever { } } else if (pIntPtr && *pIntPtr == 2) { Q_FOREVER {} } if (Q_LIKELY(pIntPtr)) {} if (Q_UNLIKELY(!pIntPtr)) {} printf(QT_TR_NOOP("Hi")); Q_DECLARE_LOGGING_CATEGORY(logging_category_test); QT_FORWARD_DECLARE_CLASS(forwardDeclaredClass); QT_FORWARD_DECLARE_STRUCT(forwardDeclaredStruct); //#9650 QString qstr1(qstrArg); if (qstr1.length() == 1) {} else { qstr1.chop(1); if (qstr1.length() == 1) {} } if (qstr1.length() == 1) {} else { qstr1.remove(1); if (qstr1.length() == 1) {} } } void ignoredReturnValue() { // cppcheck-suppress ignoredReturnValue QFile::exists("test"); QFile file1("test"); // cppcheck-suppress ignoredReturnValue file1.exists(); } void nullPointer(int * pIntPtr) { int * pNullPtr = Q_NULLPTR; // cppcheck-suppress nullPointer *pNullPtr = 1; if (pIntPtr != Q_NULLPTR) { *pIntPtr = 2; } else { // cppcheck-suppress nullPointerRedundantCheck *pIntPtr = 3; } } cppcheck-2.7/test/cfg/runtests.sh000077500000000000000000000350021417746362400171240ustar00rootroot00000000000000#!/bin/bash set -e # abort on error #set -x # be verbose if [[ $(pwd) == */test/cfg ]] ; then # we are in test/cfg CPPCHECK="../../cppcheck" DIR="" CFG="../../cfg/" else # assume we are in repo root CPPCHECK="./cppcheck" DIR=test/cfg/ CFG="cfg/" fi # Cppcheck options CPPCHECK_OPT='--check-library --enable=information --enable=style --error-exitcode=-1 --suppress=missingIncludeSystem --inline-suppr --template="{file}:{line}:{severity}:{id}:{message}"' # Compiler settings CXX=g++ CXX_OPT='-fsyntax-only -std=c++0x -Wno-format -Wno-format-security -Wno-deprecated-declarations' CC=gcc CC_OPT='-Wno-format -Wno-nonnull -Wno-implicit-function-declaration -Wno-deprecated-declarations -Wno-format-security -Wno-nonnull -fsyntax-only' # posix.c ${CC} ${CC_OPT} ${DIR}posix.c ${CPPCHECK} ${CPPCHECK_OPT} --library=posix ${DIR}posix.c # gnu.c ${CC} ${CC_OPT} -D_GNU_SOURCE ${DIR}gnu.c ${CPPCHECK} ${CPPCHECK_OPT} --library=posix,gnu ${DIR}gnu.c # qt.cpp set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve Qt configuration is not available, skipping syntax check." else set +e QTCONFIG=$(pkg-config --cflags Qt5Core) QTCONFIG_RETURNCODE=$? set -e if [ $QTCONFIG_RETURNCODE -eq 0 ]; then QTBUILDCONFIG=$(pkg-config --variable=qt_config Qt5Core) [[ $QTBUILDCONFIG =~ (^|[[:space:]])reduce_relocations($|[[:space:]]) ]] && QTCONFIG="${QTCONFIG} -fPIC" set +e echo -e "#include " | ${CXX} ${CXX_OPT} ${QTCONFIG} -x c++ - QTCHECK_RETURNCODE=$? set -e if [ $QTCHECK_RETURNCODE -ne 0 ]; then echo "Qt not completely present or not working, skipping syntax check with ${CXX}." else echo "Qt found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${QTCONFIG} ${DIR}qt.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=qt ${DIR}qt.cpp # bsd.c ${CPPCHECK} ${CPPCHECK_OPT} --library=bsd ${DIR}bsd.c # std.c ${CC} ${CC_OPT} ${DIR}std.c ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive ${DIR}std.c ${CXX} ${CXX_OPT} ${DIR}std.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive ${DIR}std.cpp # windows.cpp # Syntax check via g++ does not work because it can not find a valid windows.h #${CXX} ${CXX_OPT} ${DIR}windows.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --platform=win32A ${DIR}windows.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --platform=win32W ${DIR}windows.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --platform=win64 ${DIR}windows.cpp # wxwidgets.cpp set +e WXCONFIG=$(wx-config --cxxflags) WXCONFIG_RETURNCODE=$? set -e if [ $WXCONFIG_RETURNCODE -ne 0 ]; then echo "wx-config does not work, skipping syntax check for wxWidgets tests." else set +e echo -e "#include \n#include \n#include \n#include \n#if wxVERSION_NUMBER<2950\n#error \"Old version\"\n#endif" | ${CXX} ${CXX_OPT} ${WXCONFIG} -x c++ - WXCHECK_RETURNCODE=$? set -e if [ $WXCHECK_RETURNCODE -ne 0 ]; then echo "wxWidgets not completely present (with GUI classes) or not working, skipping syntax check with ${CXX}." else echo "wxWidgets found, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${WXCONFIG} -Wno-deprecated-declarations ${DIR}wxwidgets.cpp fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=wxwidgets,windows -f ${DIR}wxwidgets.cpp # gtk.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve GTK+ configuration is not available, skipping syntax check." else set +e GTKCONFIG=$(pkg-config --cflags gtk+-3.0) GTKCONFIG_RETURNCODE=$? set -e if [ $GTKCONFIG_RETURNCODE -ne 0 ]; then set +e GTKCONFIG=$(pkg-config --cflags gtk+-2.0) GTKCONFIG_RETURNCODE=$? set -e fi if [ $GTKCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${GTKCONFIG} -x c - GTKCHECK_RETURNCODE=$? set -e if [ $GTKCHECK_RETURNCODE -ne 0 ]; then echo "GTK+ not completely present or not working, skipping syntax check with ${CXX}." else echo "GTK+ found and working, checking syntax with ${CXX} now." ${CC} ${CC_OPT} ${GTKCONFIG} ${DIR}gtk.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=gtk -f ${DIR}gtk.c # boost.cpp set +e echo -e "#include " | ${CXX} ${CXX_OPT} -x c++ - BOOSTCHECK_RETURNCODE=$? set -e if [ ${BOOSTCHECK_RETURNCODE} -ne 0 ]; then echo "Boost not completely present or not working, skipping syntax check with ${CXX}." else echo "Boost found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${DIR}boost.cpp fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=boost ${DIR}boost.cpp # sqlite3.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve SQLite3 configuration is not available, skipping syntax check." else set +e SQLITE3CONFIG=$(pkg-config --cflags sqlite3) SQLITE3CONFIG_RETURNCODE=$? set -e if [ $SQLITE3CONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${SQLITE3CONFIG} -x c - SQLITE3CHECK_RETURNCODE=$? set -e if [ $SQLITE3CHECK_RETURNCODE -ne 0 ]; then echo "SQLite3 not completely present or not working, skipping syntax check with ${CC}." else echo "SQLite3 found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${SQLITE3CONFIG} ${DIR}sqlite3.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=sqlite3 ${DIR}sqlite3.c # openmp.c ${CC} ${CC_OPT} -fopenmp ${DIR}openmp.c ${CPPCHECK} ${CPPCHECK_OPT} --library=openmp ${DIR}openmp.c # python.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve Python 3 configuration is not available, skipping syntax check." else set +e PYTHON3CONFIG=$(pkg-config --cflags python3) PYTHON3CONFIG_RETURNCODE=$? set -e if [ $PYTHON3CONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${PYTHON3CONFIG} -x c - PYTHON3CONFIG_RETURNCODE=$? set -e if [ $PYTHON3CONFIG_RETURNCODE -ne 0 ]; then echo "Python 3 not completely present or not working, skipping syntax check with ${CC}." else echo "Python 3 found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${PYTHON3CONFIG} ${DIR}python.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=python ${DIR}python.c # lua.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve Lua configuration is not available, skipping syntax check." else set +e LUACONFIG=$(pkg-config --cflags lua-5.3) LUACONFIG_RETURNCODE=$? set -e if [ $LUACONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${LUACONFIG} -x c - LUACONFIG_RETURNCODE=$? set -e if [ $LUACONFIG_RETURNCODE -ne 0 ]; then echo "Lua not completely present or not working, skipping syntax check with ${CC}." else echo "Lua found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${LUACONFIG} ${DIR}lua.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=lua ${DIR}lua.c # libcurl.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve libcurl configuration is not available, skipping syntax check." else set +e LIBCURLCONFIG=$(pkg-config --cflags libcurl) LIBCURLCONFIG_RETURNCODE=$? set -e if [ $LIBCURLCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${LIBCURLCONFIG} -x c - LIBCURLCONFIG_RETURNCODE=$? set -e if [ $LIBCURLCONFIG_RETURNCODE -ne 0 ]; then echo "libcurl not completely present or not working, skipping syntax check with ${CC}." else echo "libcurl found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${LIBCURLCONFIG} ${DIR}libcurl.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=libcurl ${DIR}libcurl.c # cairo.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve cairo configuration is not available, skipping syntax check." else set +e CAIROCONFIG=$(pkg-config --cflags cairo) CAIROCONFIG_RETURNCODE=$? set -e if [ $CAIROCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${CAIROCONFIG} -x c - CAIROCONFIG_RETURNCODE=$? set -e if [ $CAIROCONFIG_RETURNCODE -ne 0 ]; then echo "cairo not completely present or not working, skipping syntax check with ${CC}." else echo "cairo found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${CAIROCONFIG} ${DIR}cairo.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=cairo ${DIR}cairo.c ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=googletest ${DIR}googletest.cpp # kde.cpp set +e KDECONFIG=$(kde4-config --path include) KDECONFIG_RETURNCODE=$? set -e if [ $KDECONFIG_RETURNCODE -ne 0 ]; then echo "kde4-config does not work, skipping syntax check." else set +e KDEQTCONFIG=$(pkg-config --cflags QtCore) KDEQTCONFIG_RETURNCODE=$? set -e if [ $KDEQTCONFIG_RETURNCODE -ne 0 ]; then echo "Suitable Qt not present, Qt is necessary for KDE. Skipping syntax check." else set +e echo -e "#include \n" | ${CXX} ${CXX_OPT} -I${KDECONFIG} ${KDEQTCONFIG} -x c++ - KDECHECK_RETURNCODE=$? set -e if [ $KDECHECK_RETURNCODE -ne 0 ]; then echo "KDE headers not completely present or not working, skipping syntax check with ${CXX}." else echo "KDE found, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} -I${KDECONFIG} ${KDEQTCONFIG} ${DIR}kde.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=kde ${DIR}kde.cpp # libsigc++.cpp set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve libsigc++ configuration is not available, skipping syntax check." else set +e LIBSIGCPPCONFIG=$(pkg-config --cflags sigc++-2.0) LIBSIGCPPCONFIG_RETURNCODE=$? set -e if [ $LIBSIGCPPCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include \n" | ${CXX} ${CXX_OPT} ${LIBSIGCPPCONFIG} -x c++ - LIBSIGCPPCONFIG_RETURNCODE=$? set -e if [ $LIBSIGCPPCONFIG_RETURNCODE -ne 0 ]; then echo "libsigc++ not completely present or not working, skipping syntax check with ${CXX}." else echo "libsigc++ found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${LIBSIGCPPCONFIG} ${DIR}libsigc++.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=libsigc++ ${DIR}libsigc++.cpp # openssl.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve OpenSSL configuration is not available, skipping syntax check." else set +e OPENSSLCONFIG=$(pkg-config --cflags libssl) OPENSSLCONFIG_RETURNCODE=$? set -e if [ $OPENSSLCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${OPENSSLCONFIG} -x c - OPENSSLCONFIG_RETURNCODE=$? set -e if [ $OPENSSLCONFIG_RETURNCODE -ne 0 ]; then echo "OpenSSL not completely present or not working, skipping syntax check with ${CC}." else echo "OpenSSL found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${OPENSSLCONFIG} ${DIR}openssl.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=openssl ${DIR}openssl.c # opencv2.cpp set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve OpenCV configuration is not available, skipping syntax check." else set +e OPENCVCONFIG=$(pkg-config --cflags opencv) OPENCVCONFIG_RETURNCODE=$? set -e if [ $OPENCVCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include \n" | ${CXX} ${CXX_OPT} ${OPENCVCONFIG} -x c++ - OPENCVCONFIG_RETURNCODE=$? set -e if [ $OPENCVCONFIG_RETURNCODE -ne 0 ]; then echo "OpenCV not completely present or not working, skipping syntax check with ${CXX}." else echo "OpenCV found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${OPENCVCONFIG} ${DIR}opencv2.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=opencv2 ${DIR}opencv2.cpp # cppunit.cpp set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve cppunit configuration is not available, skipping syntax check." else if ! pkg-config cppunit; then echo "cppunit not found, skipping syntax check for cppunit" else echo "cppunit found, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} -Wno-deprecated-declarations ${DIR}cppunit.cpp fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=cppunit -f ${DIR}cppunit.cpp # Check the syntax of the defines in the configuration files set +e xmlstarlet --version XMLSTARLET_RETURNCODE=$? set -e if [ $XMLSTARLET_RETURNCODE -ne 0 ]; then echo "xmlstarlet needed to extract defines, skipping defines check." else for configfile in ${CFG}*.cfg; do echo "Checking defines in $configfile" # Disable debugging output temporarily since there could be many defines set +x # XMLStarlet returns 1 if no elements were found which is no problem here set +e EXTRACTED_DEFINES=$(xmlstarlet sel -t -m '//define' -c . -n <$configfile) set -e EXTRACTED_DEFINES=$(echo "$EXTRACTED_DEFINES" | sed 's///g') echo "$EXTRACTED_DEFINES" | gcc -fsyntax-only -xc -Werror - done fi echo SUCCESS cppcheck-2.7/test/cfg/sqlite3.c000066400000000000000000000023671417746362400164360ustar00rootroot00000000000000 // Test library configuration for sqlite3.cfg // // Usage: // $ cppcheck --check-library --library=sqlite3 --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/sqlite3.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode() { sqlite3 * db; int rc = sqlite3_open("/db", &db); if (rc != SQLITE_OK) { fprintf(stderr, "Error opening sqlite3 db: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); } else { sqlite3_close(db); } { char * buf = sqlite3_malloc(10); printf("size: %ull\n", sqlite3_msize(buf)); sqlite3_free(buf); } } void memleak_sqlite3_malloc() { char * buf = sqlite3_malloc(10); if (buf) { buf[0] = 0; } // cppcheck-suppress memleak } void resourceLeak_sqlite3_open() { sqlite3 * db; sqlite3_open("/db", &db); // TODO: cppcheck-suppress resourceLeak } void ignoredReturnValue(char * buf) { // cppcheck-suppress leakReturnValNotUsed sqlite3_malloc(10); // cppcheck-suppress leakReturnValNotUsed sqlite3_malloc64(5); // cppcheck-suppress ignoredReturnValue sqlite3_msize(buf); } cppcheck-2.7/test/cfg/std.c000066400000000000000000002701471417746362400156470ustar00rootroot00000000000000 // Test library configuration for std.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/std.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include // frexp #include #if defined(__STD_UTF_16__) || defined(__STD_UTF_32__) #include #endif #include #include #include #include #include #include #include #include #include void bufferAccessOutOfBounds(void) { char a[5]; fgets(a,5,stdin); // cppcheck-suppress bufferAccessOutOfBounds fgets(a,6,stdin); sprintf(a, "ab%s", "cd"); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy sprintf(a, "ab%s", "cde"); // TODO cppcheck-suppress redundantCopy snprintf(a, 5, "abcde%i", 1); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds snprintf(a, 6, "abcde%i", 1); // TODO cppcheck-suppress redundantCopy strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy strcpy(a, "abcde"); // cppcheck-suppress bufferAccessOutOfBounds strcpy_s(a, 10, "abcdefghij"); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress terminateStrncpy strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy strncpy(a,"abcde",6); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy strncpy(a,"a",6); // TODO cppcheck-suppress redundantCopy strncpy(a,"abcdefgh",4); // valid call strncpy_s(a,5,"abcd",5); // string will be truncated, error is returned, but no buffer overflow strncpy_s(a,5,"abcde",6); // TODO cppcheck-suppress bufferAccessOutOfBounds strncpy_s(a,5,"a",6); strncpy_s(a,5,"abcdefgh",4); // valid call strncat_s(a,5,"1",2); // cppcheck-suppress bufferAccessOutOfBounds strncat_s(a,10,"1",2); // TODO cppcheck-suppress bufferAccessOutOfBounds strncat_s(a,5,"1",5); fread(a,1,5,stdin); // cppcheck-suppress bufferAccessOutOfBounds fread(a,1,6,stdin); fwrite(a,1,5,stdout); // cppcheck-suppress bufferAccessOutOfBounds fread(a,1,6,stdout); char * pAlloc1 = aligned_alloc(8, 16); memset(pAlloc1, 0, 16); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc1, 0, 17); free(pAlloc1); } void memleak_aligned_alloc(void) { // cppcheck-suppress unusedAllocatedMemory // cppcheck-suppress unreadVariable char * alignedBuf = aligned_alloc(8, 16); // cppcheck-suppress memleak } void pointerLessThanZero_aligned_alloc(void) { char * alignedBuf = aligned_alloc(8, 16); // cppcheck-suppress pointerLessThanZero if (alignedBuf < 0) return; free(alignedBuf); // no warning is expected for alignedBuf = aligned_alloc(8, 16); if (alignedBuf == 0) return; free(alignedBuf); // no warning is expected for alignedBuf = aligned_alloc(8, 16); if (alignedBuf) free(alignedBuf); } void unusedRetVal_aligned_alloc(void) { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed aligned_alloc(8, 16); } void uninitvar_aligned_alloc(size_t alignment, size_t size) { size_t uninitVar1, uninitVar2, uninitVar3; // cppcheck-suppress uninitvar free(aligned_alloc(uninitVar1, size)); // cppcheck-suppress uninitvar free(aligned_alloc(alignment, uninitVar2)); // cppcheck-suppress uninitvar free(aligned_alloc(uninitVar3, uninitVar3)); // no warning is expected free(aligned_alloc(alignment, size)); } void bufferAccessOutOfBounds_libraryDirectionConfiguration(void) { // This tests whether the argument to isdigit() is configured with direction "in". This allows // Cppcheck to report the error without marking it as inconclusive. char arr[10]; char c = 'A'; (void)isdigit(c); // cppcheck-suppress arrayIndexOutOfBounds // cppcheck-suppress unreadVariable arr[c] = 'x'; } void arrayIndexOutOfBounds() { char * pAlloc1 = aligned_alloc(8, 16); pAlloc1[15] = '\0'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc1[16] = '1'; free(pAlloc1); char * pAlloc2 = malloc(9); pAlloc2[8] = 'a'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc2[9] = 'a'; // #1379 // cppcheck-suppress memleakOnRealloc pAlloc2 = realloc(pAlloc2, 8); pAlloc2[7] = 'b'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc2[8] = 0; // cppcheck-suppress memleakOnRealloc pAlloc2 = realloc(pAlloc2, 20); pAlloc2[19] = 'b'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc2[20] = 0; free(pAlloc2); char * pAlloc3 = calloc(2,3); pAlloc3[5] = 'a'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc3[6] = 1; // cppcheck-suppress memleakOnRealloc pAlloc3 = reallocarray(pAlloc3, 3,3); pAlloc3[8] = 'a'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc3[9] = 1; free(pAlloc3); } void resourceLeak_tmpfile(void) { // cppcheck-suppress unreadVariable FILE * fp = tmpfile(); // cppcheck-suppress resourceLeak } // memory leak void ignoreleak(void) { char *p = (char *)malloc(10); memset(&(p[0]), 0, 10); // cppcheck-suppress memleak } // null pointer void nullpointer(int value) { int res = 0; FILE *fp; wchar_t *pWcsUninit; #ifndef __CYGWIN__ // cppcheck-suppress nullPointer clearerr(0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer feof(0); #endif // cppcheck-suppress nullPointer (void)fgetc(0); // cppcheck-suppress nullPointer fclose(0); #ifndef __CYGWIN__ // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer ferror(0); #endif // cppcheck-suppress nullPointer (void)ftell(0); // cppcheck-suppress nullPointer puts(0); // cppcheck-suppress nullPointer fp=fopen(0,0); fclose(fp); fp = 0; // No FP fflush(0); fp = freopen(0,"abc",stdin); fclose(fp); fp = NULL; // cppcheck-suppress nullPointer fputc(0,0); // cppcheck-suppress nullPointer fputs(0,0); // cppcheck-suppress nullPointer fgetpos(0,0); // cppcheck-suppress nullPointer frexp(1.0,0); // cppcheck-suppress nullPointer fsetpos(0,0); // cppcheck-suppress nullPointer itoa(123,0,10); putchar(0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strchr(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcschr(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strlen(0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcslen(0); // cppcheck-suppress nullPointer strcpy(0,0); // cppcheck-suppress nullPointer wcscpy(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcsspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcscspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcoll(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcscoll(0,0); // cppcheck-suppress nullPointer strcat(0,0); // cppcheck-suppress nullPointer wcscat(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcmp(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcscmp(0,0); // cppcheck-suppress nullPointer strcpy_s(0,1,1); // cppcheck-suppress nullPointer strcpy_s(1,1,0); // cppcheck-suppress nullPointer strncpy(0,0,1); // cppcheck-suppress nullPointer strncpy_s(0,1,1,1); // cppcheck-suppress nullPointer strncpy_s(1,1,0,1); // cppcheck-suppress nullPointer wcsncpy(0,0,1); // cppcheck-suppress nullPointer strncat(0,0,1); // cppcheck-suppress nullPointer strncat_s(0,1,1,1); // cppcheck-suppress nullPointer strncat_s(1,1,0,1); // cppcheck-suppress nullPointer wcsncat(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strncmp(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcsncmp(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strstr(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcsstr(0,0); // cppcheck-suppress nullPointer strtoul(0,0,0); // cppcheck-suppress nullPointer wcstoul(0,0,0); // cppcheck-suppress nullPointer strtoull(0,0,0); // cppcheck-suppress nullPointer wcstoull(0,0,0); // cppcheck-suppress nullPointer strtol(0,0,0); // cppcheck-suppress nullPointer wcstol(0,0,0); // #6100 False positive nullPointer - calling mbstowcs(NULL,) res += mbstowcs(0,"",0); res += wcstombs(0,L"",0); strtok(NULL,"xyz"); wcstok(NULL,L"xyz",&pWcsUninit); strxfrm(0,"foo",0); // TODO: error message (#6306 and http://trac.cppcheck.net/changeset/d11eb4931aea51cf2cb74faccdcd2a3289b818d6/) strxfrm(0,"foo",42); wcsxfrm(0,L"foo",0); // TODO: error message when arg1==NULL and arg3!=0 #6306: https://trac.cppcheck.net/ticket/6306#comment:2 wcsxfrm(0,L"foo",42); snprintf(NULL, 0, "someformatstring"); // legal // cppcheck-suppress nullPointer snprintf(NULL, 42, "someformatstring"); // not legal scanf("%i", &res); // cppcheck-suppress nullPointer scanf("%i", NULL); wscanf(L"%i", &res); // cppcheck-suppress nullPointer wscanf(L"%i", NULL); } void nullpointerMemchr1(char *p, char *s) { p = memchr(s, 'p', strlen(s)); (void)p; } void nullpointerMemchr2(char *p, char *s) { p = memchr(s, 0, strlen(s)); (void)p; } void nullPointer_memchr(char *p) { char *s = 0; // cppcheck-suppress nullPointer p = memchr(s, 0, strlen(s)); (void)p; } void nullPointer_memcmp(char *p) { // cppcheck-suppress nullPointer (void)memcmp(p, 0, 123); } void nullPointer_wmemcmp(wchar_t *p) { // cppcheck-suppress nullPointer (void)wmemcmp(p, 0, 123); } void nullPointer_vsnprintf(const char * format, ...) { va_list args; // valid char buffer[256]; va_start(args, format); vsnprintf(buffer, 256, format, args); printf("%s", buffer); va_end(args); // valid va_start(args, format); vsnprintf(NULL, 0, format, args); va_end(args); // invalid va_start(args, format); // TODO #9410 cppcheck-suppress nullPointer vsnprintf(NULL, 10, format, args); va_end(args); } // uninit pointers void uninitvar_abs(void) { int i; // cppcheck-suppress uninitvar (void)abs(i); } void uninitvar_clearerr(void) { FILE *fp; // cppcheck-suppress uninitvar clearerr(fp); } void uninitvar_fclose(void) { FILE *fp; // cppcheck-suppress uninitvar fclose(fp); } void uninitvar_fopen(void) { const char *filename, *mode; FILE *fp; // cppcheck-suppress uninitvar fp = fopen(filename, "rt"); fclose(fp); // cppcheck-suppress uninitvar fp = fopen("filename.txt", mode); fclose(fp); } void uninitvar_feof(void) { FILE *fp1, *fp2; // cppcheck-suppress ignoredReturnValue // cppcheck-suppress uninitvar feof(fp1); // cppcheck-suppress uninitvar (void)feof(fp2); } void uninitvar_ferror(void) { FILE *fp1, *fp2; // cppcheck-suppress ignoredReturnValue // cppcheck-suppress uninitvar ferror(fp1); // cppcheck-suppress uninitvar (void)ferror(fp2); } void uninitvar_fflush(void) { FILE *fp; // cppcheck-suppress uninitvar fflush(fp); } void uninitvar_fgetc(void) { FILE *fp; // cppcheck-suppress uninitvar (void)fgetc(fp); } void uninitvar_fgetpos(void) { FILE *fp; fpos_t pos; fpos_t *ppos; // cppcheck-suppress uninitvar fgetpos(fp,&pos); fp = fopen("filename","rt"); // cppcheck-suppress uninitvar fgetpos(fp,ppos); fclose(fp); } void uninitvar_fsetpos(void) { FILE *fp; fpos_t pos; fpos_t *ppos; // cppcheck-suppress uninitvar fsetpos(fp,&pos); fp = fopen("filename","rt"); // cppcheck-suppress uninitvar fsetpos(fp,ppos); fclose(fp); } void uninitvar_fgets(void) { FILE *fp; char buf[10]; char *str; int n; fgets(buf,10,stdin); // cppcheck-suppress uninitvar fgets(str,10,stdin); // cppcheck-suppress uninitvar fgets(buf,10,fp); // cppcheck-suppress uninitvar fgets(buf,n,stdin); } void uninitvar_fputc(void) { int i; FILE *fp; fputc('a', stdout); // cppcheck-suppress uninitvar fputc(i, stdout); // cppcheck-suppress uninitvar fputc('a', fp); } void uninitvar_fputs(void) { const char *s; FILE *fp; fputs("a", stdout); // cppcheck-suppress uninitvar fputs(s, stdout); // cppcheck-suppress uninitvar fputs("a", fp); } void uninitvar_ftell(void) { FILE *fp; // cppcheck-suppress uninitvar (void)ftell(fp); } void uninitvar_puts(void) { const char *s; // cppcheck-suppress uninitvar puts(s); } void uninitvar_putchar(void) { char c; // cppcheck-suppress uninitvar putchar(c); } void uninitvar_cproj(void) // #6939 { float complex fc; // cppcheck-suppress uninitvar (void)cprojf(fc); double complex dc; // cppcheck-suppress uninitvar (void)cproj(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cprojl(ldc); } void uninitvar_creal(void) { float complex fc; // cppcheck-suppress uninitvar (void)crealf(fc); double complex dc; // cppcheck-suppress uninitvar (void)creal(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)creall(ldc); } void uninitvar_acos(void) { float f; // cppcheck-suppress uninitvar (void)acosf(f); double d; // cppcheck-suppress uninitvar (void)acos(d); long double ld; // cppcheck-suppress uninitvar (void)acosl(ld); } void uninitvar_acosh(void) { float f; // cppcheck-suppress uninitvar (void)acoshf(f); double d; // cppcheck-suppress uninitvar (void)acosh(d); long double ld; // cppcheck-suppress uninitvar (void)acoshl(ld); } void invalidFunctionArg_acosh(void) { float f = .999f; // cppcheck-suppress invalidFunctionArg (void)acoshf(f); f = 1.0f; (void)acoshf(f); double d = .999; // cppcheck-suppress invalidFunctionArg (void)acosh(d); d = 1.0; (void)acosh(d); long double ld = .999L; // cppcheck-suppress invalidFunctionArg (void)acoshl(ld); ld = 1.0; (void)acoshl(ld); } void invalidFunctionArg_atanh(void) { float f = 1.00001f; // cppcheck-suppress invalidFunctionArg (void)atanhf(f); f = 1.0f; (void)atanhf(f); f = -1.0f; (void)atanhf(f); f = -1.00001f; // cppcheck-suppress invalidFunctionArg (void)atanhf(f); double d = 1.00001; // cppcheck-suppress invalidFunctionArg (void)atanh(d); d = 1.0; (void)atanh(d); d = -1.0; (void)atanh(d); d = -1.00001; // cppcheck-suppress invalidFunctionArg (void)atanh(d); long double ld = 1.00001L; // cppcheck-suppress invalidFunctionArg (void)atanhl(ld); ld = 1.0L; (void)atanhl(ld); ld = -1.0L; (void)atanhl(ld); ld = -1.00001L; // cppcheck-suppress invalidFunctionArg (void)atanhl(ld); } void uninitvar_asctime(void) { const struct tm *tm; // cppcheck-suppress uninitvar // cppcheck-suppress asctimeCalled (void)asctime(tm); } void uninitvar_asctime_s(void) { const struct tm *tm; char buf[26]; // cppcheck-suppress uninitvar // cppcheck-suppress asctime_sCalled asctime_s(buf, sizeof(buf), tm); } void uninitvar_assert(void) { int i; // cppcheck-suppress checkLibraryNoReturn // cppcheck-suppress uninitvar assert(i); } void uninitvar_sqrt(void) { float f; // cppcheck-suppress uninitvar (void)sqrtf(f); double d; // cppcheck-suppress uninitvar (void)sqrt(d); long double ld; // cppcheck-suppress uninitvar (void)sqrtl(ld); } void uninitvar_csqrt(void) { float complex fc; // cppcheck-suppress uninitvar (void)csqrtf(fc); double complex dc; // cppcheck-suppress uninitvar (void)csqrt(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)csqrtl(ldc); } void uninitvar_sinh(void) { float f; // cppcheck-suppress uninitvar (void)sinhf(f); double d; // cppcheck-suppress uninitvar (void)sinh(d); long double ld; // cppcheck-suppress uninitvar (void)sinhl(ld); } void uninitvar_sin(void) { float f; // cppcheck-suppress uninitvar (void)sinf(f); double d; // cppcheck-suppress uninitvar (void)sin(d); long double ld; // cppcheck-suppress uninitvar (void)sinl(ld); } void uninitvar_csin(void) { float complex fd; // cppcheck-suppress uninitvar (void)csinf(fd); double complex dc; // cppcheck-suppress uninitvar (void)csin(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)csinl(ldc); } void uninitvar_csinh(void) { float complex fd; // cppcheck-suppress uninitvar (void)csinhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)csinh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)csinhl(ldc); } void uninitvar_asin(void) { float f; // cppcheck-suppress uninitvar (void)asinf(f); double d; // cppcheck-suppress uninitvar (void)asin(d); long double ld; // cppcheck-suppress uninitvar (void)asinl(ld); } void uninitvar_casin(void) { float complex fd; // cppcheck-suppress uninitvar (void)casinf(fd); double complex dc; // cppcheck-suppress uninitvar (void)casin(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)casinl(ldc); } void uninitvar_asinh(void) { float f; // cppcheck-suppress uninitvar (void)asinhf(f); double d; // cppcheck-suppress uninitvar (void)asinh(d); long double ld; // cppcheck-suppress uninitvar (void)asinhl(ld); } void uninitvar_casinh(void) { float complex fd; // cppcheck-suppress uninitvar (void)casinhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)casinh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)casinhl(ldc); } void uninitvar_wcsftime(wchar_t* ptr) { size_t maxsize; wchar_t* format; struct tm* timeptr; // cppcheck-suppress uninitvar (void)wcsftime(ptr, maxsize, format, timeptr); } void uninitvar_tan(void) { float f; // cppcheck-suppress uninitvar (void)tanf(f); double d; // cppcheck-suppress uninitvar (void)tan(d); long double ld; // cppcheck-suppress uninitvar (void)tanl(ld); } void uninitvar_ctan(void) { float complex fd; // cppcheck-suppress uninitvar (void)ctanf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ctan(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ctanl(ldc); } void uninitvar_tanh(void) { float f; // cppcheck-suppress uninitvar (void)tanhf(f); double d; // cppcheck-suppress uninitvar (void)tanh(d); long double ld; // cppcheck-suppress uninitvar (void)tanhl(ld); } void uninitvar_ctanh(void) { float complex fd; // cppcheck-suppress uninitvar (void)ctanhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ctanh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ctanhl(ldc); } void uninitvar_feclearexcept(void) { int i; // cppcheck-suppress uninitvar (void)feclearexcept(i); } void uninitvar_fegetexceptflag(fexcept_t* flagp) { int excepts; // cppcheck-suppress uninitvar (void)fegetexceptflag(flagp, excepts); } void uninitvar_feraiseexcept(void) { int excepts; // cppcheck-suppress uninitvar (void)feraiseexcept(excepts); } void uninitvar_fesetenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)fesetenv(envp); } void uninitvar_fesetround(void) { int i; // cppcheck-suppress uninitvar (void)fesetround(i); } void uninitvar_fetestexcept(void) { int i; // cppcheck-suppress uninitvar (void)fetestexcept(i); } void uninitvar_feupdateenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)feupdateenv(envp); } void uninitvar_atan(void) { float f; // cppcheck-suppress uninitvar (void)atanf(f); double d; // cppcheck-suppress uninitvar (void)atan(d); long double ld; // cppcheck-suppress uninitvar (void)atanl(ld); } void uninitvar_catan(void) { float complex fd; // cppcheck-suppress uninitvar (void)catanf(fd); double complex dc; // cppcheck-suppress uninitvar (void)catan(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)catanl(ldc); } void uninitvar_tgamma(void) { float f; // cppcheck-suppress uninitvar (void)tgammaf(f); double d; // cppcheck-suppress uninitvar (void)tgamma(d); long double ld; // cppcheck-suppress uninitvar (void)tgammal(ld); } void uninitvar_trunc(void) { float f; // cppcheck-suppress uninitvar (void)truncf(f); double d; // cppcheck-suppress uninitvar (void)trunc(d); long double ld; // cppcheck-suppress uninitvar (void)truncl(ld); } void uninitvar_atanh(void) { float f; // cppcheck-suppress uninitvar (void)atanhf(f); double d; // cppcheck-suppress uninitvar (void)atanh(d); long double ld; // cppcheck-suppress uninitvar (void)atanhl(ld); } void uninitvar_catanh(void) { float complex fd; // cppcheck-suppress uninitvar (void)catanhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)catanh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)catanhl(ldc); } void uninitvar_atan2(void) { float f1,f2; // cppcheck-suppress uninitvar (void)atan2f(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)atan2(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)atan2l(ld1,ld2); } void uninitvar_atof(void) { char * c; // cppcheck-suppress uninitvar (void)atof(c); } void uninitvar_atol(void) { char * c1, *c2, *c3; // cppcheck-suppress uninitvar (void)atoi(c1); // cppcheck-suppress uninitvar (void)atol(c2); // cppcheck-suppress uninitvar (void)atoll(c3); } void uninitvar_calloc(void) { size_t nitems; size_t size; // cppcheck-suppress unusedAllocatedMemory // cppcheck-suppress uninitvar int * p = (int*) calloc(nitems, size); free(p); } void uninitvar_ceil(void) { float f; // cppcheck-suppress uninitvar (void)ceilf(f); double d; // cppcheck-suppress uninitvar (void)ceil(d); long double ld; // cppcheck-suppress uninitvar (void)ceill(ld); } void uninitvar_copysign(void) { float f1, f2; // cppcheck-suppress uninitvar (void)copysignf(f1, f2); double d1, d2; // cppcheck-suppress uninitvar (void)copysign(d1, d2); long double ld1, ld2; // cppcheck-suppress uninitvar (void)copysignl(ld1, ld2); } void uninitvar_cbrt(void) { float f; // cppcheck-suppress uninitvar (void)cbrtf(f); double d; // cppcheck-suppress uninitvar (void)cbrt(d); long double ld; // cppcheck-suppress uninitvar (void)cbrtl(ld); } void uninitvar_cos(void) { float f; // cppcheck-suppress uninitvar (void)cosf(f); double d; // cppcheck-suppress uninitvar (void)cos(d); long double ld; // cppcheck-suppress uninitvar (void)cosl(ld); } void uninitvar_ccos(void) { float complex fd; // cppcheck-suppress uninitvar (void)ccosf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ccos(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ccosl(ldc); } void uninitvar_cosh(void) { float f; // cppcheck-suppress uninitvar (void)coshf(f); double d; // cppcheck-suppress uninitvar (void)cosh(d); long double ld; // cppcheck-suppress uninitvar (void)coshl(ld); } void uninitvar_ccosh(void) { float complex fd; // cppcheck-suppress uninitvar (void)ccoshf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ccosh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ccoshl(ldc); } void uninitvar_ctime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)ctime(tp); } void uninitvar_difftime(void) { time_t t1,t2; // cppcheck-suppress uninitvar (void)difftime(t1, t2); } void uninitvar_div(void) { int num; int denom; // cppcheck-suppress uninitvar (void)div(num,denom); } void uninitvar_exit(void) { int i; // cppcheck-suppress uninitvar exit(i); } void uninitvar_erf(void) { float f; // cppcheck-suppress uninitvar (void)erff(f); double d; // cppcheck-suppress uninitvar (void)erf(d); long double ld; // cppcheck-suppress uninitvar (void)erfl(ld); } void uninitvar_erfc(void) { float f; // cppcheck-suppress uninitvar (void)erfcf(f); double d; // cppcheck-suppress uninitvar (void)erfc(d); long double ld; // cppcheck-suppress uninitvar (void)erfcl(ld); } void uninitvar_carg(void) { float complex fd; // cppcheck-suppress uninitvar (void)cargf(fd); double complex dc; // cppcheck-suppress uninitvar (void)carg(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cargl(ldc); } void uninitvar_exp(void) { float f; // cppcheck-suppress uninitvar (void)expf(f); double d; // cppcheck-suppress uninitvar (void)exp(d); long double ld; // cppcheck-suppress uninitvar (void)expl(ld); } void uninitvar_cexp(void) { float complex fd; // cppcheck-suppress uninitvar (void)cexpf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cexp(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cexpl(ldc); } void uninitvar_cimag(void) { float complex fd; // cppcheck-suppress uninitvar (void)cimagf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cimag(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cimagl(ldc); } void uninitvar_exp2(void) { float f; // cppcheck-suppress uninitvar (void)exp2f(f); double d; // cppcheck-suppress uninitvar (void)exp2(d); long double ld; // cppcheck-suppress uninitvar (void)exp2l(ld); } void uninitvar_expm1(void) { float f; // cppcheck-suppress uninitvar (void)expm1f(f); double d; // cppcheck-suppress uninitvar (void)expm1(d); long double ld; // cppcheck-suppress uninitvar (void)expm1l(ld); } void uninitvar_fabs(void) { float f; // cppcheck-suppress uninitvar (void)fabsf(f); double d; // cppcheck-suppress uninitvar (void)fabs(d); long double ld; // cppcheck-suppress uninitvar (void)fabsl(ld); } void uninitvar_fdim(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fdimf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fdim(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fdiml(ld1,ld2); } void uninitvar_fgetwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)fgetwc(stream); } void uninitvar_floor(void) { float f; // cppcheck-suppress uninitvar (void)floorf(f); double d; // cppcheck-suppress uninitvar (void)floor(d); long double ld; // cppcheck-suppress uninitvar (void)floorl(ld); } void uninitvar_fma(void) { float f1,f2,f3; // cppcheck-suppress uninitvar (void)fmaf(f1,f2,f3); double d1,d2,d3; // cppcheck-suppress uninitvar (void)fma(d1,d2,d3); long double ld1,ld2,ld3; // cppcheck-suppress uninitvar (void)fmal(ld1,ld2,ld3); } void uninitvar_fmax(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fmaxf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fmax(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fmaxl(ld1,ld2); } void uninitvar_fmin(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fminf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fmin(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fminl(ld1,ld2); } void uninitvar_fmod(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fmodf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fmod(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fmodl(ld1,ld2); } void uninitvar_fprintf(FILE *Stream, char *Format, int Argument) { FILE *stream1, *stream2; char *format1, *format2; int argument1, argument2; // cppcheck-suppress uninitvar (void)fprintf(stream1, format1, argument1); // cppcheck-suppress uninitvar (void)fprintf(stream2, Format, Argument); // cppcheck-suppress uninitvar (void)fprintf(Stream, format2, Argument); // cppcheck-suppress uninitvar (void)fprintf(Stream, Format, argument2); // no warning is expected (void)fprintf(Stream, Format, Argument); } void uninitvar_vfprintf(FILE *Stream, const char *Format, va_list Arg) { FILE *stream1, *stream2; char *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfprintf(stream1, format1, arg); // cppcheck-suppress uninitvar (void)vfprintf(stream2, Format, Arg); // cppcheck-suppress uninitvar (void)vfprintf(Stream, format2, Arg); // no warning is expected (void)vfprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vfprintf(Stream, Format, arg); } void uninitvar_vfwprintf(FILE *Stream, wchar_t *Format, va_list Arg) { FILE *stream1, *stream2; wchar_t *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfwprintf(stream1, format1, arg); // cppcheck-suppress uninitvar (void)vfwprintf(stream2, Format, Arg); // cppcheck-suppress uninitvar (void)vfwprintf(Stream, format2, Arg); // no warning is expected (void)vfwprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vfwprintf(Stream, Format, arg); } void uninitvar_fputwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)fputwc(c,stream); } void uninitvar_fputws(void) { wchar_t *string; FILE *stream; // cppcheck-suppress uninitvar (void)fputws(string,stream); } void uninitvar_fread(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)fread(ptr,size,nobj,stream); } void uninitvar_free(void) { // cppcheck-suppress unassignedVariable void *block; // cppcheck-suppress uninitvar free(block); } void uninitvar_freopen(void) { char *filename; char *mode; FILE *stream; // cppcheck-suppress uninitvar FILE * p = freopen(filename,mode,stream); fclose(p); } void uninitvar_frexp(void) { float f1; int *i1; // cppcheck-suppress uninitvar (void)frexpf(f1,i1); double d1; int *i2; // cppcheck-suppress uninitvar (void)frexp(d1,i2); long double ld1; int *i3; // cppcheck-suppress uninitvar (void)frexpl(ld1,i3); } void uninitvar_hypot(void) { float f1,f2; // cppcheck-suppress uninitvar (void)hypotf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)hypot(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)hypotl(ld1,ld2); } void uninitvar_fscanf(void) { FILE *stream; char *format; int i; // cppcheck-suppress uninitvar (void)fscanf(stream,format,i); } void uninitvar_vfscanf(void) { FILE *stream; char * format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfscanf(stream,format,arg); } void uninitvar_vfwscanf(void) { FILE *stream; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfwscanf(stream,format,arg); } void uninitvar_fseek(void) { FILE* stream; long int offset; int origin; // cppcheck-suppress uninitvar (void)fseek(stream,offset,origin); } void uninitvar_fgetws(void) { wchar_t *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)fgetws(buffer,n,stream); } void uninitvar_fwide(void) { FILE *stream; int mode; // cppcheck-suppress uninitvar (void)fwide(stream,mode); } void uninitvar_fwrite(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)fwrite(ptr,size,nobj,stream); } void uninitvar_mblen(void) { char *string; size_t size; // cppcheck-suppress uninitvar (void)mblen(string,size); } void uninitvar_mbtowc(void) { wchar_t* pwc; char* pmb; size_t max; // cppcheck-suppress uninitvar (void)mbtowc(pwc,pmb,max); } void uninitvar_mbrlen(const char* p, size_t m, mbstate_t* s) { char* pmb1, *pmb2; size_t max1, max2; mbstate_t* ps1, *ps2; // cppcheck-suppress uninitvar (void)mbrlen(pmb1,max1,ps1); // cppcheck-suppress uninitvar (void)mbrlen(pmb2,m,s); // cppcheck-suppress uninitvar (void)mbrlen(p,max2,s); // cppcheck-suppress uninitvar (void)mbrlen(p,m,ps2); // no warning is expected (void)mbrlen(p,m,s); } void nullPointer_mbrlen(const char* p, size_t m, mbstate_t* s) { /* no warning is expected: A call to the function with a null pointer as pmb resets the shift state (and ignores parameter max). */ (void)mbrlen(NULL,m,s); (void)mbrlen(NULL,0,s); /* cppcheck-suppress nullPointer */ (void)mbrlen(p,m,NULL); } void uninitvar_btowc(void) { int c; // cppcheck-suppress uninitvar (void)btowc(c); } void uninitvar_mbsinit(void) { mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbsinit(ps); } void uninitvar_mbstowcs(wchar_t* d, const char* s, size_t m) { wchar_t *dest; char *src; size_t max; // cppcheck-suppress uninitvar (void)mbstowcs(dest,s,m); // cppcheck-suppress uninitvar (void)mbstowcs(d,src,m); // cppcheck-suppress uninitvar (void)mbstowcs(d,s,max); // No warning is expected (void)mbstowcs(d,s,m); wchar_t buf[100]; (void)mbstowcs(buf,s,100); } void uninitvar_mbsrtowcs(wchar_t* d, const char** s, size_t m, mbstate_t *p) { wchar_t* dest; const char* src; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbsrtowcs(dest,s,m,p); // cppcheck-suppress uninitvar (void)mbsrtowcs(d,&src,m,p); // cppcheck-suppress uninitvar (void)mbsrtowcs(d,s,max,p); // cppcheck-suppress uninitvar (void)mbsrtowcs(d,s,m,ps); // No warning is expected (void)mbsrtowcs(d,s,m,p); } void uninitvar_wctob(void) { wint_t wc; // cppcheck-suppress uninitvar (void)wctob(wc); } void uninitvar_wctomb(void) { char *s; wchar_t wc; // cppcheck-suppress uninitvar (void)wctomb(s,wc); } void uninitvar_wcstombs(void) { char *mbstr; wchar_t *wcstr; size_t n; // cppcheck-suppress uninitvar (void)wcstombs(mbstr,wcstr,n); } void uninitvar_getc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)getc(stream); } void uninitvar_getwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)getwc(stream); } void uninitvar_ungetc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)ungetc(c,stream); } void uninitvar_ungetwc(void) { wint_t c; FILE *stream; // cppcheck-suppress uninitvar (void)ungetwc(c,stream); } void uninitvar_getenv(void) { char *name; // cppcheck-suppress uninitvar (void)getenv(name); } void uninitvar_gets(void) { char *buffer; // cppcheck-suppress getsCalled // cppcheck-suppress uninitvar (void)gets(buffer); } void uninitvar_gmtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)gmtime(tp); } void uninitvar_isalnum(void) { int i; // cppcheck-suppress uninitvar (void)isalnum(i); } void uninitvar_iswalnum(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswalnum(i); } void uninitvar_isalpha(void) { int i; // cppcheck-suppress uninitvar (void)isalpha(i); } void uninitvar_iswalpha(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswalpha(i); } void uninitvar_isblank(void) { int i; // cppcheck-suppress uninitvar (void)isblank(i); } void uninitvar_iswblank(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswblank(i); } void uninitvar_iscntrl(void) { int i; // cppcheck-suppress uninitvar (void)iscntrl(i); } void uninitvar_iswcntrl(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswcntrl(i); } void uninitvar_iswctype(void) { wint_t c; wctype_t desc; // cppcheck-suppress uninitvar (void)iswctype(c,desc); } void uninitvar_isdigit(void) { int i; // cppcheck-suppress uninitvar (void)isdigit(i); } void uninitvar_iswdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswdigit(i); } void uninitvar_isgraph(void) { int i; // cppcheck-suppress uninitvar (void)isgraph(i); } void uninitvar_iswgraph(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswgraph(i); } void uninitvar_islower(void) { int i; // cppcheck-suppress uninitvar (void)islower(i); } void uninitvar_iswlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswlower(i); } void uninitvar_isprint(void) { int i; // cppcheck-suppress uninitvar (void)isprint(i); } void uninitvar_iswprint(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswprint(i); } void uninitvar_ispunct(void) { int i; // cppcheck-suppress uninitvar (void)ispunct(i); } void uninitvar_iswpunct(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswpunct(i); } void uninitvar_isspace(void) { int i; // cppcheck-suppress uninitvar (void)isspace(i); } void uninitvar_iswspace(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswspace(i); } void uninitvar_isupper(void) { int i; // cppcheck-suppress uninitvar (void)isupper(i); } void uninitvar_iswupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswupper(i); } void uninitvar_isxdigit(void) { int i; // cppcheck-suppress uninitvar (void)isxdigit(i); } void uninitvar_iswxdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswxdigit(i); } void uninitvar_towctrans(void) { wint_t c; wctrans_t desc; // cppcheck-suppress uninitvar (void)towctrans(c,desc); } void uninitvar_towlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)towlower(i); } void uninitvar_towupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)towupper(i); } void uninitvar_wctrans(void) { char* property; // cppcheck-suppress uninitvar (void)wctrans(property); } void uninitvar_wctype(void) { char* property; // cppcheck-suppress uninitvar (void)wctype(property); } void ignorereturn(void) { char szNumbers[] = "2001 60c0c0 -1101110100110100100000 0x6fffff"; char * pEnd; strtol(szNumbers,&pEnd,10); } void uninitvar_cabs(void) { float complex fd; // cppcheck-suppress uninitvar (void)cabsf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cabs(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cabsl(ldc); } void uninitvar_cacos(void) { float complex fd; // cppcheck-suppress uninitvar (void)cacosf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cacos(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cacosl(ldc); } void uninitvar_cacosh(void) { float complex fd; // cppcheck-suppress uninitvar (void)cacoshf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cacosh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cacoshl(ldc); } void uninitvar_labs(void) { long int li; // cppcheck-suppress uninitvar (void)labs(li); long long int lli; // cppcheck-suppress uninitvar (void)llabs(lli); } void uninitvar_ldexp(void) { float f; int e1; // cppcheck-suppress uninitvar (void)ldexpf(f,e1); double d; int e2; // cppcheck-suppress uninitvar (void)ldexp(d,e2); long double ld; int e3; // cppcheck-suppress uninitvar (void)ldexpl(ld,e3); } void uninitvar_lgamma(void) { float f; // cppcheck-suppress uninitvar (void)lgammaf(f); double d; // cppcheck-suppress uninitvar (void)lgamma(d); long double ld; // cppcheck-suppress uninitvar (void)lgammal(ld); } void uninitvar_rint(void) { float f; // cppcheck-suppress uninitvar (void)rintf(f); double d; // cppcheck-suppress uninitvar (void)rint(d); long double ld; // cppcheck-suppress uninitvar (void)rintl(ld); } void uninitvar_lrint(void) { float f; // cppcheck-suppress uninitvar (void)lrintf(f); double d; // cppcheck-suppress uninitvar (void)lrint(d); long double ld; // cppcheck-suppress uninitvar (void)lrintl(ld); } void uninitvar_llrint(void) { float f; // cppcheck-suppress uninitvar (void)llrintf(f); double d; // cppcheck-suppress uninitvar (void)llrint(d); long double ld; // cppcheck-suppress uninitvar (void)llrintl(ld); } void uninitvar_lround(void) { float f; // cppcheck-suppress uninitvar (void)lroundf(f); double d; // cppcheck-suppress uninitvar (void)lround(d); long double ld; // cppcheck-suppress uninitvar (void)lroundl(ld); } void uninitvar_llround(void) { float f; // cppcheck-suppress uninitvar (void)llroundf(f); double d; // cppcheck-suppress uninitvar (void)llround(d); long double ld; // cppcheck-suppress uninitvar (void)llroundl(ld); } void uninitvar_srand(void) { unsigned int seed; // cppcheck-suppress uninitvar (void)srand(seed); } void uninitvar_ldiv(void) { long int l1; long int l2; // cppcheck-suppress uninitvar (void)ldiv(l1,l2); long long int ll1; long long int ll2; // cppcheck-suppress uninitvar (void)lldiv(ll1,ll2); } void uninitvar_localtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)localtime(tp); } void uninitvar_log(void) { float f; // cppcheck-suppress uninitvar (void)logf(f); double d; // cppcheck-suppress uninitvar (void)log(d); long double ld; // cppcheck-suppress uninitvar (void)logl(ld); } void uninitvar_clog(void) { float complex fc; // cppcheck-suppress uninitvar (void)clogf(fc); double complex dc; // cppcheck-suppress uninitvar (void)clog(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)clogl(ldc); } void uninitvar_conj(void) { float complex fc; // cppcheck-suppress uninitvar (void)conjf(fc); double complex dc; // cppcheck-suppress uninitvar (void)conj(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)conjl(ldc); } void uninitvar_fpclassify(void) { float f; // cppcheck-suppress uninitvar (void)fpclassify(f); double d; // cppcheck-suppress uninitvar (void)fpclassify(d); long double ld; // cppcheck-suppress uninitvar (void)fpclassify(ld); } void uninitvar_isfinite(void) { float f; // cppcheck-suppress uninitvar (void)isfinite(f); double d; // cppcheck-suppress uninitvar (void)isfinite(d); long double ld; // cppcheck-suppress uninitvar (void)isfinite(ld); } void uninitvar_isgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)isgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)isgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)isgreater(ld1,ld2); } void uninitvar_isgreaterequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)isgreaterequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)isgreaterequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)isgreaterequal(ld1,ld2); } void uninitvar_isinf(void) { float f; // cppcheck-suppress uninitvar (void)isinf(f); double d; // cppcheck-suppress uninitvar (void)isinf(d); long double ld; // cppcheck-suppress uninitvar (void)isinf(ld); } void uninitvar_logb(void) { float f; // cppcheck-suppress uninitvar (void)logbf(f); double d; // cppcheck-suppress uninitvar (void)logb(d); long double ld; // cppcheck-suppress uninitvar (void)logbl(ld); } void uninitvar_isless(void) { float f1,f2; // cppcheck-suppress uninitvar (void)isless(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)isless(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)isless(ld1,ld2); } void uninitvar_islessequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)islessequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)islessequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)islessequal(ld1,ld2); } void uninitvar_islessgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)islessgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)islessgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)islessgreater(ld1,ld2); } void uninitvar_nan(void) { char *tagp1, *tagp2, *tagp3; // cppcheck-suppress uninitvar (void)nanf(tagp1); // cppcheck-suppress uninitvar (void)nan(tagp2); // cppcheck-suppress uninitvar (void)nanl(tagp3); } void uninitvar_isnan(void) { double d; // cppcheck-suppress uninitvar (void)isnan(d); } void uninitvar_isnormal(void) { double d; // cppcheck-suppress uninitvar (void)isnormal(d); } void uninitvar_isunordered(void) { double d1,d2; // cppcheck-suppress uninitvar (void)isunordered(d1,d2); } void uninitvar_ilogb(void) { float f; // cppcheck-suppress uninitvar (void)ilogbf(f); double d; // cppcheck-suppress uninitvar (void)ilogb(d); long double ld; // cppcheck-suppress uninitvar (void)ilogbl(ld); } void uninitvar_log10(void) { float f; // cppcheck-suppress uninitvar (void)log10f(f); double d; // cppcheck-suppress uninitvar (void)log10(d); long double ld; // cppcheck-suppress uninitvar (void)log10l(ld); } void uninitvar_log1p(void) { float f; // cppcheck-suppress uninitvar (void)log1pf(f); double d; // cppcheck-suppress uninitvar (void)log1p(d); long double ld; // cppcheck-suppress uninitvar (void)log1pl(ld); } void uninitvar_log2(void) { float f; // cppcheck-suppress uninitvar (void)log2f(f); double d; // cppcheck-suppress uninitvar (void)log2(d); long double ld; // cppcheck-suppress uninitvar (void)log2l(ld); } void uninitvar_nearbyint(void) { float f; // cppcheck-suppress uninitvar (void)nearbyintf(f); double d; // cppcheck-suppress uninitvar (void)nearbyint(d); long double ld; // cppcheck-suppress uninitvar (void)nearbyintl(ld); } void uninitvar_nextafter(void) { float f1,f2; // cppcheck-suppress uninitvar (void)nextafterf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)nextafter(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)nextafterl(ld1,ld2); } void uninitvar_nexttoward(void) { float f1,f2; // cppcheck-suppress uninitvar (void)nexttowardf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)nexttoward(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)nexttowardl(ld1,ld2); } void uninitvar_longjmp(void) { jmp_buf env; int val; // cppcheck-suppress uninitvar (void)longjmp(env,val); } void uninitvar_malloc(void) { size_t size; // cppcheck-suppress unusedAllocatedMemory // cppcheck-suppress uninitvar int *p = (int*)malloc(size); free(p); } void uninitvar_alloca(void) { size_t size; // cppcheck-suppress allocaCalled // cppcheck-suppress uninitvar (void)alloca(size); } void uninitvar_memchr(void) { void *cs; int c; size_t n; // cppcheck-suppress uninitvar (void)memchr(cs,c,n); } void uninitvar_wmemchr(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)wmemchr(cs,c,n); } void uninitvar_memcmp(void) { void *s1; void *s2; size_t n; // cppcheck-suppress uninitvar (void)memcmp(s1,s2,n); } void uninitvar_wmemcmp(void) { wchar_t *s1; wchar_t *s2; size_t n; // cppcheck-suppress uninitvar (void)wmemcmp(s1,s2,n); } void uninitvar_memcpy(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)memcpy(ct,cs,n); } void uninitvar_wmemcpy(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)wmemcpy(cs,c,n); } void uninitvar_memmove(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)memmove(ct,cs,n); } void uninitvar_wmemmove(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)wmemmove(cs,c,n); } void uninitvar_memset(void) { void *s; int c; size_t n; // cppcheck-suppress uninitvar (void)memset(s,c,n); } void uninitvar_wmemset(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)wmemset(cs,c,n); } void uninitvar_mktime(void) { struct tm *tp; // cppcheck-suppress uninitvar (void)mktime(tp); struct tmx *tpx; // cppcheck-suppress uninitvar (void)mkxtime(tpx); } void uninitvar_modf(void) { float f1; float *f2; // cppcheck-suppress uninitvar (void)modff(f1,f2); double d1; double *d2; // cppcheck-suppress uninitvar (void)modf(d1,d2); long double ld1; long double *ld2; // cppcheck-suppress uninitvar (void)modfl(ld1,ld2); } void uninitvar_perror(void) { char *string; // cppcheck-suppress uninitvar (void)perror(string); } void uninitvar_pow(void) { float f1,f2; // cppcheck-suppress uninitvar (void)powf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)pow(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)powl(ld1,ld2); } void uninitvar_cpow(void) { float complex f1,f2; // cppcheck-suppress uninitvar (void)cpowf(f1,f2); double complex d1,d2; // cppcheck-suppress uninitvar (void)cpow(d1,d2); long double complex ld1,ld2; // cppcheck-suppress uninitvar (void)cpowl(ld1,ld2); } void uninitvar_remainder(void) { float f1,f2; // cppcheck-suppress uninitvar (void)remainderf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)remainder(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)remainderl(ld1,ld2); } void uninitvar_remquo(void) { float f1,f2; int *i1; // cppcheck-suppress uninitvar (void)remquof(f1,f2,i1); double d1,d2; int *i2; // cppcheck-suppress uninitvar (void)remquo(d1,d2,i2); long double ld1,ld2; int *i3; // cppcheck-suppress uninitvar (void)remquol(ld1,ld2,i3); } void uninitvar_printf(char *Format, int Argument) { char * format_1, * format_2, * format_3; int argument1, argument2; // no warning is expected (void)printf("x"); // cppcheck-suppress uninitvar (void)printf(format_1,argument1); // cppcheck-suppress uninitvar (void)printf(Format,argument2); // cppcheck-suppress uninitvar (void)printf(format_2,Argument); // cppcheck-suppress uninitvar (void)printf(format_3,1); // no warning is expected (void)printf(Format,Argument); } void uninitvar_vprintf(char *Format, va_list Arg) { char * format1, *format2; va_list arg1, arg2; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vprintf(format1,arg1); // cppcheck-suppress uninitvar (void)vprintf(format2,Arg); // no warning is expected (void)vprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vprintf(Format,arg2); } void uninitvar_vwprintf(wchar_t *Format, va_list Arg) { wchar_t * format1, * format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vwprintf(format1,arg); // cppcheck-suppress uninitvar (void)vwprintf(format2,Arg); // no warning is expected (void)vwprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vwprintf(Format,arg); } void uninitvar_bsearch(void) { void* key; void* base; size_t num; size_t size; // cppcheck-suppress uninitvar (void)bsearch(key,base,num,size,(int (*)(const void*,const void*))strcmp); } void uninitvar_qsort(void) { void *base; size_t n; size_t size; // cppcheck-suppress uninitvar (void)qsort(base,n,size, (int (*)(const void*,const void*))strcmp); } void uninitvar_putc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)putc(c,stream); } void uninitvar_putwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)putc(c,stream); } void uninitvar_putwchar(void) { wchar_t c; // cppcheck-suppress uninitvar (void)putwchar(c); } void uninitvar_realloc(void) { void *block; size_t newsize; // cppcheck-suppress uninitvar void *p = realloc(block, newsize); free(p); } void uninitvar_remove(void) { char *s; // cppcheck-suppress uninitvar (void)remove(s); } void uninitvar_rename(void) { char *s1; char *s2; // cppcheck-suppress uninitvar (void)rename(s1,s2); } void uninitvar_rewind(void) { FILE *f; // cppcheck-suppress uninitvar (void)rewind(f); } void uninitvar_round(void) { float f; // cppcheck-suppress uninitvar (void)roundf(f); double d; // cppcheck-suppress uninitvar (void)round(d); long double ld; // cppcheck-suppress uninitvar (void)roundl(ld); } void uninitvar_scalbn(void) { float f; int i1; // cppcheck-suppress uninitvar (void)scalbnf(f,i1); double d; int i2; // cppcheck-suppress uninitvar (void)scalbn(d,i2); long double ld; int i3; // cppcheck-suppress uninitvar (void)scalbnl(ld,i3); } void uninitvar_scalbln(void) { float f; long int i1; // cppcheck-suppress uninitvar (void)scalblnf(f,i1); double d; long int i2; // cppcheck-suppress uninitvar (void)scalbln(d,i2); long double ld; long int i3; // cppcheck-suppress uninitvar (void)scalblnl(ld,i3); } void uninitvar_signbit(void) { double d; // cppcheck-suppress uninitvar (void)signbit(d); } void uninitvar_signal(void) { int i; // cppcheck-suppress uninitvar signal(i, exit); } void uninitvar_raise(void) { int i; // cppcheck-suppress uninitvar (void)raise(i); } void uninitvar_scanf(void) { char *format; char str[42]; // cppcheck-suppress uninitvar (void)scanf(format, str); // no warning is expected (#9347) int i; sscanf("0", "%d", &i); } void uninitvar_vsscanf(void) { char *s; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vsscanf(s,format,arg); } void uninitvar_vswscanf(void) { wchar_t *s; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vswscanf(s,format,arg); } void uninitvar_vscanf(void) { char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vscanf(format,arg); } void uninitvar_vwscanf(void) { wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vwscanf(format,arg); } void uninitvar_setbuf(void) { FILE *stream; char *buf; // cppcheck-suppress uninitvar (void)setbuf(stream,buf); } void uninitvar_setvbuf(void) { FILE *stream; char *buf; int mode; size_t size; // cppcheck-suppress uninitvar (void)setvbuf(stream,buf,mode,size); } void uninitvar_strcat(char *dest, const char * const source) { char *deststr1, *deststr2; char *srcstr1, *srcstr2; // cppcheck-suppress uninitvar (void)strcat(deststr1,srcstr1); // cppcheck-suppress uninitvar (void)strcat(dest,srcstr2); // cppcheck-suppress uninitvar (void)strcat(deststr2,source); // no warning shall be shown for (void)strcat(dest,source); } void bufferAccessOutOfBounds_strcat(char *dest, const char * const source) { char buf4[4] = {0}; const char * const srcstr3 = "123"; const char * const srcstr4 = "1234"; // @todo #8599 cppcheck-suppress bufferAccessOutOfBounds (void)strcat(buf4,srcstr4); // off by one issue: strcat is appends \0' at the end // no warning shall be shown for (void)strcat(dest,source); (void)strcat(buf4,srcstr3); // strcat appends '\0' at the end (void)strcat(dest,srcstr4); // Cppcheck does not know the length of 'dest' } void uninitvar_wcscat(wchar_t *dest, const wchar_t * const source) { wchar_t *deststr_1, *deststr_2; wchar_t *srcstr_1, *srcstr_2; // cppcheck-suppress uninitvar (void)wcscat(deststr_1,srcstr_1); // cppcheck-suppress uninitvar (void)wcscat(dest,srcstr_2); // cppcheck-suppress uninitvar (void)wcscat(deststr_2,source); // no warning shall be shown for (void)wcscat(dest,source); } void uninitvar_wcrtomb(void) { char *s; wchar_t wc; mbstate_t *ps; // cppcheck-suppress uninitvar (void)wcrtomb(s,wc,ps); } void uninitvar_strchr(void) { char *cs; int c; // cppcheck-suppress uninitvar (void)strchr(cs,c); } void invalidFunctionArg_strchr(char *cs, int c) { // cppcheck-suppress invalidFunctionArg (void)strchr(cs,-1); // No warning shall be issued for (void)strchr(cs, 0); (void)strchr(cs, 255); // cppcheck-suppress invalidFunctionArg (void)strchr(cs, 256); } void invalidFunctionArg_log10(float f, double d, const long double ld) { // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)log10f(0.0f); (void)log10f(1.4013e-45f); // note: calculated by nextafterf(0.0f, 1.0f); (void)log10f(f); (void)log10f(FLT_MAX); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)log10(0.0); (void)log10(4.94066e-324); // note: calculated by nextafterf(0.0, 1.0); (void)log10(d); (void)log10(DBL_MAX); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)log10l(0.0L); (void)log10l(4.94066e-324L); // note: calculated by nextafterf(0.0L, 1.0L); (void)log10l(ld); (void)log10l(LDBL_MAX); } void invalidFunctionArg_log(float f, double d, const long double ld) { // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)logf(0.0f); (void)logf(1.4013e-45f); // note: calculated by nextafterf(0.0f, 1.0f); (void)logf(f); (void)logf(FLT_MAX); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)log(0.0); (void)log(4.94066e-324); // note: calculated by nextafterf(0.0, 1.0); (void)log(d); (void)log(DBL_MAX); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)logl(0.0L); (void)logl(4.94066e-324L); // note: calculated by nextafterf(0.0L, 1.0L); (void)logl(ld); (void)logl(LDBL_MAX); } void invalidFunctionArg_log2(float f, double d, const long double ld) { // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)log2f(0.0f); (void)log2f(1.4013e-45f); // note: calculated by nextafterf(0.0f, 1.0f); (void)log2f(f); (void)log2f(FLT_MAX); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)log2(0.0); (void)log2(4.94066e-324); // note: calculated by nextafterf(0.0, 1.0); (void)log2(d); (void)log2(DBL_MAX); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress wrongmathcall (void)log2l(0.0L); (void)log2l(4.94066e-324L); // note: calculated by nextafterf(0.0L, 1.0L); (void)log2l(ld); (void)log2l(LDBL_MAX); } void uninitvar_wcschr(void) { wchar_t *cs; wchar_t c; // cppcheck-suppress uninitvar (void)wcschr(cs,c); } void uninitvar_strcmp(char *s1, char *s2) { char *str1; char *str2; char *str3; char *str4; // cppcheck-suppress uninitvar (void)strcmp(str1,s2); // cppcheck-suppress uninitvar (void)strcmp(s1,str2); // cppcheck-suppress uninitvar (void)strcmp(str3,str4); // No warning is expected (void)strcmp(s1,s2); } void uninitvar_wcscmp(wchar_t *s1, wchar_t *s2) { wchar_t *str1; wchar_t *str2; wchar_t *str3; wchar_t *str4; // cppcheck-suppress uninitvar (void)wcscmp(str1,s2); // cppcheck-suppress uninitvar (void)wcscmp(s1,str2); // cppcheck-suppress uninitvar (void)wcscmp(str3,str4); // No warning is expected (void)wcscmp(s1,s2); } void uninitvar_strcpy(char *d, char *s) { char *dest1, *dest2; char *src1, *src2; // cppcheck-suppress uninitvar (void)strcpy(dest1,s); // cppcheck-suppress uninitvar (void)strcpy(d,src1); // cppcheck-suppress uninitvar (void)strcpy(dest2,src2); // No warning is expected (void)strcpy(d,s); } void uninitvar_strcpy_s(char * strDest, ssize_t s, char *source) { char *strUninit1; char *strUninit2; ssize_t size; // cppcheck-suppress uninitvar (void)strcpy_s(strUninit1, 1, "a"); // cppcheck-suppress uninitvar (void)strcpy_s(strDest, 1, strUninit2); // cppcheck-suppress uninitvar (void)strcpy_s(strDest, size, "a"); // No warning is expected (void)strcpy_s(strDest, s, source); } void uninitvar_wcscpy(wchar_t *d, wchar_t*s) { wchar_t *dest1, *dest2; wchar_t *src1, *src2; // cppcheck-suppress uninitvar (void)wcscpy(dest1,s); // cppcheck-suppress uninitvar (void)wcscpy(d,src1); // cppcheck-suppress uninitvar (void)wcscpy(dest2,src2); // No warning is expected (void)wcscpy(d,s); } void uninitvar_strftime(void) { char *s; size_t max; char *fmt; struct tm *p; // cppcheck-suppress uninitvar (void)strftime(s,max,fmt,p); struct tmx *px; // cppcheck-suppress uninitvar (void)strfxtime(s,max,fmt,px); } void uninitvar_strlen(const char *str) { char *s; // cppcheck-suppress uninitvar (void)strlen(s); const char x; const char *xPtr = &x; // cppcheck-suppress uninitvar (void)strlen(xPtr); // No warning is expected (void)strlen(str); } void uninitvar_wcslen(void) { wchar_t *s; // cppcheck-suppress uninitvar (void)wcslen(s); } //char * strncpy ( char * destination, const char * source, size_t num ); void uninitvar_strncpy(char * dest, const char * src, size_t num) { char *d; char *s; size_t n; // cppcheck-suppress uninitvar (void)strncpy(d,src,num); // cppcheck-suppress uninitvar (void)strncpy(dest,s,num); // cppcheck-suppress uninitvar (void)strncpy(dest,src,n); // No warning is expected for (void)strncpy(dest,src,num); } void uninitvar_strncpy_s(char *Ct, size_t N1, char *S, size_t N2) { char dest[42]; char *s1, *s2; size_t n1; size_t n2; size_t n3; size_t n4; // cppcheck-suppress uninitvar (void)strncpy_s(dest,n1,s1,n2); // cppcheck-suppress uninitvar (void)strncpy_s(Ct,n3,S,N2); // cppcheck-suppress uninitvar (void)strncpy_s(Ct,N1,s2,N2); // cppcheck-suppress uninitvar (void)strncpy_s(Ct,N1,S,n4); // no warning is expected for (void)strncpy_s(Ct,N1,S,N2); (void)strncpy_s(dest,N1,S,N2); } void uninitvar_strpbrk(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strpbrk(cs,ct); } // char * strncat ( char * destination, const char * source, size_t num ); void uninitvar_strncat(char *d, char *s, size_t n) { char *dest; char *src; size_t num; // cppcheck-suppress uninitvar (void)strncat(dest,s,n); // cppcheck-suppress uninitvar (void)strncat(d,src,n); // cppcheck-suppress uninitvar (void)strncat(d,s,num); // no warning is expected for (void)strncat(d,s,n); } // errno_t strcat_s(char *restrict dest, rsize_t destsz, const char *restrict src); // since C11 void uninitvar_strcat_s(char *Ct, size_t N, char *S) { char *ct_1, *ct_2; char *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)strcat_s(ct_1,n1,s1); // cppcheck-suppress uninitvar (void)strcat_s(ct_2,N,S); // cppcheck-suppress uninitvar (void)strcat_s(Ct,N,s2); // cppcheck-suppress uninitvar (void)strcat_s(Ct,n2,S); // no warning is expected for (void) strcat_s(Ct,N,S); } // errno_t wcscat_s(wchar_t *restrict dest, rsize_t destsz, const wchar_t *restrict src); // since C11 void uninitvar_wcscat_s(wchar_t *Ct, size_t N, wchar_t *S) { wchar_t *ct_1, *ct_2; wchar_t *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)wcscat_s(ct_1,n1,s1); // cppcheck-suppress uninitvar (void)wcscat_s(ct_2,N,S); // cppcheck-suppress uninitvar (void)wcscat_s(Ct,N,s2); // cppcheck-suppress uninitvar (void)wcscat_s(Ct,n2,S); // no warning is expected for (void) wcscat_s(Ct,N,S); } void uninitvar_strncat_s(char *Ct, size_t N1, char *S, size_t N2) { char *ct_1, *ct_2; char *s1, *s2; size_t n1; size_t n2; size_t n3; size_t n4; // cppcheck-suppress uninitvar (void)strncat_s(ct_1,n1,s1,n2); // cppcheck-suppress uninitvar (void)strncat_s(ct_2,N1,S,N2); // cppcheck-suppress uninitvar (void)strncat_s(Ct,n3,S,N2); // cppcheck-suppress uninitvar (void)strncat_s(Ct,N1,s2,N2); // cppcheck-suppress uninitvar (void)strncat_s(Ct,N1,S,n4); // no warning is expected for (void)strncat_s(Ct,N1,S,N2); } void uninitvar_wcsncat(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct_1, *ct_2; wchar_t *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)wcsncat(ct_1,s1,n1); // cppcheck-suppress uninitvar (void)wcsncat(ct_2,S,N); // cppcheck-suppress uninitvar (void)wcsncat(Ct,s2,N); // cppcheck-suppress uninitvar (void)wcsncat(Ct,S,n2); // no warning is expected for (void)wcsncat(Ct,S,N); } void uninitvar_strncmp(char *Ct, char *S, size_t N) { char *ct; char *s; size_t n1; // cppcheck-suppress uninitvar (void)strncmp(ct,S,N); // cppcheck-suppress uninitvar (void)strncmp(Ct,s,N); // cppcheck-suppress uninitvar (void)strncmp(Ct,S,n1); // no warning is expected for (void)strncmp(Ct,S,N); } void uninitvar_wcsncmp(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct1, *ct2; wchar_t *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)wcsncmp(ct1,s1,n1); // cppcheck-suppress uninitvar (void)wcsncmp(ct2,S,N); // cppcheck-suppress uninitvar (void)wcsncmp(Ct,s2,N); // cppcheck-suppress uninitvar (void)wcsncmp(Ct,S,n2); // no warning is expected for (void)wcsncmp(Ct,S,N); } void uninitvar_strstr(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strstr(cs,ct); } void uninitvar_wcsstr(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcsstr(cs,ct); } void uninitvar_strspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strspn(cs,ct); } void uninitvar_strxfrm(void) { char *ds; char *ss; size_t n; // cppcheck-suppress uninitvar (void)strxfrm(ds,ss,n); } void bufferAccessOutOfBounds_strxfrm(void) { const char src[3] = "abc"; char dest[1] = "a"; (void)strxfrm(dest,src,1); // cppcheck-suppress bufferAccessOutOfBounds (void)strxfrm(dest,src,2); // cppcheck-suppress bufferAccessOutOfBounds (void)strxfrm(dest,src,3); } void bufferAccessOutOfBounds_strncmp(void) { const char src[3] = "abc"; char dest[1] = "a"; (void)strncmp(dest,src,1); // cppcheck-suppress bufferAccessOutOfBounds (void)strncmp(dest,src,2); // cppcheck-suppress bufferAccessOutOfBounds (void)strncmp(dest,src,3); } void uninitvar_wcsxfrm(void) { wchar_t *ds; wchar_t *ss; size_t n; // cppcheck-suppress uninitvar (void)wcsxfrm(ds,ss,n); } void uninitvar_wcsspn(void) { wchar_t *ds; wchar_t *ss; // cppcheck-suppress uninitvar (void)wcsspn(ds,ss); } void uninitvar_setlocale(void) { int category; char* locale; // cppcheck-suppress uninitvar (void)setlocale(category,locale); } void uninitvar_strerror(void) { int i; // cppcheck-suppress uninitvar (void)strerror(i); } void uninitvar_strcspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strcspn(cs,ct); } void uninitvar_wcscspn(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcscspn(cs,ct); } void uninitvar_wcspbrk(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcspbrk(cs,ct); } void uninitvar_wcsncpy(void) { wchar_t *cs; wchar_t *ct; size_t n; // cppcheck-suppress uninitvar (void)wcsncpy(cs,ct,n); } void uninitvar_strcoll(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strcoll(cs,ct); } void uninitvar_wcscoll(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcscoll(cs,ct); } //const char * strrchr ( const char * str, int character ); // char * strrchr ( char * str, int character ); void uninitvar_strrchr(const char * s, int c) { char * str; int character; // cppcheck-suppress uninitvar (void)strrchr(str,c); // cppcheck-suppress uninitvar (void)strrchr(s,character); // No warning is expected for (void)strrchr(s,c); } void uninitvar_wcsrchr(void) { wchar_t* ws; wchar_t wc; // cppcheck-suppress uninitvar (void)wcsrchr(ws,wc); } void uninitvar_wcsrtombs(void) { char *dst; const wchar_t * p;; size_t len; mbstate_t *ps; // cppcheck-suppress uninitvar (void)wcsrtombs(dst,&p,len,ps); } void uninitvar_strtok(void) { char *s; char *ct; // cppcheck-suppress uninitvar (void)strtok(s,ct); } void uninitvar_strtoimax(void) { const char *s1, *s2; char **endp1, **endp2; int base1, base2; // cppcheck-suppress uninitvar (void)strtoimax(s1,endp1,base1); // cppcheck-suppress uninitvar (void)strtoumax(s2,endp2,base2); } void uninitvar_strtof(void) { const char *s1, *s2, *s3; char **endp1, **endp2, **endp3; // cppcheck-suppress uninitvar (void)strtof(s1,endp1); // cppcheck-suppress uninitvar (void)strtod(s2,endp2); // cppcheck-suppress uninitvar (void)strtold(s3,endp3); } void uninitvar_strtol(void) { const char *s1, *s2, *s3, *s4; char **endp1, **endp2, **endp3, **endp4; int base1, base2, base3, base4; // cppcheck-suppress uninitvar (void)strtol(s1,endp1,base1); // cppcheck-suppress uninitvar (void)strtoll(s2,endp2,base2); // cppcheck-suppress uninitvar (void)strtoul(s3,endp3,base3); // cppcheck-suppress uninitvar (void)strtoull(s4,endp4,base4); } void uninitvar_time(void) { time_t *tp; // cppcheck-suppress uninitvar (void)time(tp); } void uninitvar_tmpnam(void) { char *s; // cppcheck-suppress uninitvar (void)tmpnam(s); } void uninitvar_tolower(int character) { int c1; // cppcheck-suppress uninitvar (void)tolower(c1); // cppcheck-suppress unassignedVariable int c2; int *pc=&c2; // cppcheck-suppress uninitvar (void)tolower(*pc); // No warning is expected (void)tolower(character); int *pChar = &character; // No warning is expected (void)tolower(*pChar); } void uninitvar_toupper(int character) { int c1; // cppcheck-suppress uninitvar (void)toupper(c1); // cppcheck-suppress unassignedVariable int c2; int *pc=&c2; // cppcheck-suppress uninitvar (void)toupper(*pc); // No warning is expected (void)toupper(character); int *pChar = &character; // No warning is expected (void)toupper(*pChar); } void uninitvar_wcstof(void) { const wchar_t *s1, *s2, *s3; wchar_t **endp1, **endp2, **endp3; // cppcheck-suppress uninitvar (void)wcstof(s1,endp1); // cppcheck-suppress uninitvar (void)wcstod(s2,endp2); // cppcheck-suppress uninitvar (void)wcstold(s3,endp3); } void uninitvar_mbrtowc(void) { wchar_t* pwc; const char* pmb; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbrtowc(pwc,pmb,max,ps); } void uninitvar_wcstok(void) { wchar_t *s; const wchar_t *ct; wchar_t **ptr; // cppcheck-suppress uninitvar (void)wcstok(s,ct,ptr); } void uninitvar_wcstoimax(void) { const wchar_t *s1, *s2; wchar_t ** endp1, **endp2; int base1, base2; // cppcheck-suppress uninitvar (void)wcstoimax(s1,endp1,base1); // cppcheck-suppress uninitvar (void)wcstoumax(s2,endp2,base2); } void uninitvar_wcstol(void) { const wchar_t *s1, *s2, *s3, *s4; wchar_t ** endp; int base1, base2, base3, base4; // cppcheck-suppress uninitvar (void)wcstol(s1,endp,base1); // cppcheck-suppress uninitvar (void)wcstoll(s2,endp,base2); // cppcheck-suppress uninitvar (void)wcstoul(s3,endp,base3); // cppcheck-suppress uninitvar (void)wcstoull(s4,endp,base4); } void uninitvar_wprintf(wchar_t *Format, int Argument) { const wchar_t *format1, *format2, *format3; int argument1, argument2; // cppcheck-suppress uninitvar (void)wprintf(format1,argument1); // cppcheck-suppress uninitvar (void)wprintf(format2); // cppcheck-suppress uninitvar (void)wprintf(Format,argument2); // cppcheck-suppress uninitvar (void)wprintf(format3,Argument); // no warning is expected (void)wprintf(Format,Argument); (void)wprintf(Format); } void uninitvar_sprintf(char *S, char *Format, int Argument) { char *s1, *s2; const char *format1, *format2; int argument1, argument2; // cppcheck-suppress uninitvar (void)sprintf(s1,format1,argument1); // cppcheck-suppress uninitvar (void)sprintf(s2,Format,Argument); // cppcheck-suppress uninitvar (void)sprintf(S,format2,Argument); // cppcheck-suppress uninitvar (void)sprintf(S,Format,argument2); // no warning is expected for (void)sprintf(S,Format,Argument); } void uninitvar_swprintf(void) { wchar_t *s; size_t n; const wchar_t *format; // cppcheck-suppress uninitvar (void)swprintf(s,n,format); } void uninitvar_vsprintf(void) { char *s; const char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vsprintf(s,format,arg); } void valid_vsprintf_helper(const char * format, ...) { char buffer[2]; va_list args; va_start(args, format); vsprintf(buffer, format, args); printf(buffer); va_end(args); } void valid_vsprintf() { // buffer will contain "2\0" => no bufferAccessOutOfBounds // cppcheck-suppress checkLibraryNoReturn valid_vsprintf_helper("%1.0f", 2.0f); } void uninitvar_vswprintf(void) { wchar_t *s; size_t n; const wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vswprintf(s,n,format,arg); } void uninitvar_fwprintf(void) { FILE* stream; const wchar_t* format; int i; // cppcheck-suppress uninitvar (void)fwprintf(stream,format,i); } void uninitvar_snprintf(char *S, size_t N, char *Format, int Int) { size_t n1,n2; char *format1, *format2; int i1, i2; char *s1, *s2; // cppcheck-suppress uninitvar (void)snprintf(s1,n1,format1,i1); // cppcheck-suppress uninitvar (void)snprintf(S,n2,Format,Int); // n is uninitialized // cppcheck-suppress uninitvar (void)snprintf(S,N,format2,Int); // format is uninitialized // cppcheck-suppress uninitvar (void)snprintf(S,N,Format,i2); // i is uninitialized // cppcheck-suppress uninitvar (void)snprintf(s2,N,Format,Int); // no warning is expected for (void)snprintf(S,N,Format,Int); } void uninitvar_vsnprintf(char *S, size_t N, char *Format, va_list Arg) { char *s1, *s2; size_t n1, n2; char *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vsnprintf(s1,n1,format1,arg); // cppcheck-suppress uninitvar (void)vsnprintf(s2,N,Format,Arg); // cppcheck-suppress uninitvar (void)vsnprintf(S,n2,Format,Arg); // cppcheck-suppress uninitvar (void)vsnprintf(S,N,format2,Arg); // no warning is expected for (void)vsnprintf(S,N,Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vsnprintf(S,N,Format,arg); } void uninitvar_wscanf(void) { wchar_t *format1, *format2; int i; // cppcheck-suppress uninitvar (void)wscanf(format1); // cppcheck-suppress uninitvar (void)wscanf(format2,&i); } void uninitvar_sscanf(char *s, const char *f, int i, int *ip) { char *string1, *string2, *string3; const char * format; int *pInteger; // cppcheck-suppress uninitvar (void)sscanf(string1,f); // cppcheck-suppress uninitvar (void)sscanf(string2,f,i); // cppcheck-suppress uninitvar (void)sscanf(string3,f,ip); // cppcheck-suppress uninitvar (void)sscanf(s,format,&i); // cppcheck-suppress uninitvar (void)sscanf(s,f,pInteger); // No warning is expected (void)sscanf(s,f,&i); (void)sscanf(s,f,ip); } void uninitvar_fwscanf(void) { FILE* stream; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)fwscanf(stream,format1); // cppcheck-suppress uninitvar (void)fwscanf(stream,format2,&i); } void uninitvar_swscanf(void) { wchar_t* s; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)swscanf(s,format1); // cppcheck-suppress uninitvar (void)swscanf(s,format2,&i); } void uninitvar_system(void) { char *c; // cppcheck-suppress uninitvar (void)system(c); } void uninitvar_zonetime(void) { time_t *tp; int zone; // cppcheck-suppress uninitvar (void)zonetime(tp,zone); } void uninitvar_itoa(void) { int value; char * str; int base; // cppcheck-suppress uninitvar (void)itoa(value,str,base); } #ifdef __STD_UTF_16__ void uninitvar_c16rtomb(void) { char * pmb; char16_t c16; mbstate_t * ps; // cppcheck-suppress uninitvar (void)c16rtomb(pmb,c16,ps); } void uninitvar_mbrtoc16(void) { char16_t * pc16; char * pmb; size_t max; mbstate_t * ps; // cppcheck-suppress uninitvar (void)mbrtoc16(pc16,pmb,max,ps); } #endif // __STD_UTF_16__ #ifdef __STD_UTF_32__ void uninitvar_c32rtomb(void) { char * pmb; char32_t c32; mbstate_t * ps; // cppcheck-suppress uninitvar (void)c32rtomb(pmb,c32,ps); } void uninitvar_mbrtoc32(void) { char32_t * pc32; char * pmb; size_t max; mbstate_t * ps; // cppcheck-suppress uninitvar (void)mbrtoc32(pc32,pmb,max,ps); } #endif // __STD_UTF_32__ void invalidFunctionArgBool_abs(bool b, double x, double y) { // cppcheck-suppress invalidFunctionArgBool (void)abs(true); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)abs(b); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)abs(x // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void invalidFunctionArg_std_string_substr(const std::string &str, std::size_t pos, std::size_t len) { // cppcheck-suppress invalidFunctionArg (void)str.substr(-1,len); // cppcheck-suppress invalidFunctionArg (void)str.substr(pos,-1); // no warning is expected for (void)str.substr(pos,len); (void)str.substr(pos, std::string::npos); } void invalidFunctionArg_std_wstring_substr(const std::wstring &str, std::size_t pos, std::size_t len) { // cppcheck-suppress invalidFunctionArg (void)str.substr(-1,len); // cppcheck-suppress invalidFunctionArg (void)str.substr(pos,-1); // no warning is expected for (void)str.substr(pos,len); (void)str.substr(pos, std::wstring::npos); } double invalidFunctionArg_log10(double d = 0.0) { // cppcheck-suppress invalidFunctionArg return log10(d); } void uninitvar_std_next(const std::vector &v, int count) { // No warning shall be shown: if (std::next(v.begin()) != v.end()) {} if (std::next(v.begin(), count) != v.end()) {} std::vector::iterator it; // TODO-cppcheck-suppress uninitvar if (std::next(it) != v.end()) {} std::vector::const_iterator const_it; // TODO-cppcheck-suppress uninitvar if (std::next(const_it) != v.end()) {} std::vector::reverse_iterator rit; // TODO-cppcheck-suppress uninitvar if (std::next(rit) != v.rend()) {} std::vector::const_reverse_iterator const_rit; // TODO-cppcheck-suppress uninitvar if (std::next(const_rit) != v.rend()) {} } void uninitvar_std_prev(const std::vector &v, int count) { // No warning shall be shown: if (std::prev(v.begin()) != v.end()) {} if (std::prev(v.begin(), count) != v.end()) {} std::vector::iterator it; // TODO-cppcheck-suppress uninitvar if (std::prev(it) != v.end()) {} std::vector::const_iterator const_it; // TODO-cppcheck-suppress uninitvar if (std::prev(const_it) != v.end()) {} std::vector::reverse_iterator rit; // TODO-cppcheck-suppress uninitvar if (std::prev(rit) != v.rend()) {} std::vector::const_reverse_iterator const_rit; // TODO-cppcheck-suppress uninitvar if (std::prev(const_rit) != v.rend()) {} } void overlappingWriteFunction_wcscat(wchar_t *src, wchar_t *dest) { // No warning shall be shown: (void)wcscat(dest, src); // cppcheck-suppress overlappingWriteFunction (void)wcscat(src, src); } char * overlappingWriteFunction_strcat(char *src, char *dest) { // No warning shall be shown: (void)strcat(dest, src); // cppcheck-suppress overlappingWriteFunction return strcat(src, src); } char * overlappingWriteFunction_strncat(char *src, char *dest, const std::size_t count) { // No warning shall be shown: (void)strncat(dest, src, 42); (void)strncat(dest, src, count); (void)strncat(dest, dest, count); // cppcheck-suppress overlappingWriteFunction (void)strncat(dest, dest+1, 2); char buffer[] = "strncat"; // cppcheck-suppress overlappingWriteFunction return strncat(buffer, buffer + 1, 3); } wchar_t * overlappingWriteFunction_wcsncat(wchar_t *src, wchar_t *dest, const std::size_t count) { // No warning shall be shown: (void)wcsncat(dest, src, 42); (void)wcsncat(dest, src, count); (void)wcsncat(dest, dest, count); // cppcheck-suppress overlappingWriteFunction (void)wcsncat(dest, dest+1, 2); wchar_t buffer[] = L"strncat"; // cppcheck-suppress overlappingWriteFunction return wcsncat(buffer, buffer + 1, 3); } wchar_t * overlappingWriteFunction_wcscpy(wchar_t *src, wchar_t *dest) { // No warning shall be shown: (void)wcscpy(dest, src); const wchar_t * destBuf = dest; // TODO-cppcheck-suppress overlappingWriteFunction #10355 (void)wcscpy(dest, destBuf); // cppcheck-suppress overlappingWriteFunction return wcscpy(src, src); } wchar_t * overlappingWriteFunction_wcsncpy(wchar_t *buf, const std::size_t count) { // No warning shall be shown: (void)wcsncpy(&buf[0], &buf[3], count); // size is not known (void)wcsncpy(&buf[0], &buf[3], 3U); // no-overlap // cppcheck-suppress overlappingWriteFunction return wcsncpy(&buf[0], &buf[3], 4U); } char * overlappingWriteFunction_strncpy(char *buf, const std::size_t count) { // No warning shall be shown: (void)strncpy(&buf[0], &buf[3], count); // size is not known (void)strncpy(&buf[0], &buf[3], 3U); // no-overlap // cppcheck-suppress overlappingWriteFunction return strncpy(&buf[0], &buf[3], 4U); } void * overlappingWriteFunction_memmove(void) { // No warning shall be shown: char str[] = "memmove handles overlapping data well"; return memmove(str,str+3,4); } std::bitset<10> std_bitset_test_ignoredReturnValue() { std::bitset<10> b1("1111010000"); // cppcheck-suppress ignoredReturnValue b1.test(2); return b1; } std::bitset<10> std_bitset_all_ignoredReturnValue() { std::bitset<10> b1("1111010000"); // cppcheck-suppress ignoredReturnValue b1.all(); return b1; } std::bitset<10> std_bitset_none_ignoredReturnValue() { std::bitset<10> b1("1111010000"); // cppcheck-suppress ignoredReturnValue b1.none(); return b1; } std::bitset<10> std_bitset_any_ignoredReturnValue() { std::bitset<10> b1("1111010000"); // cppcheck-suppress ignoredReturnValue b1.any(); return b1; } std::bitset<10> std_bitset_size_ignoredReturnValue() { std::bitset<10> b1("1111010000"); // cppcheck-suppress ignoredReturnValue b1.size(); return b1; } std::bitset<10> std_bitset_count_ignoredReturnValue() { std::bitset<10> b1("1111010000"); // cppcheck-suppress ignoredReturnValue b1.count(); return b1; } void valid_code() { std::vector vecInt{0, 1, 2}; std::fill_n(vecInt.begin(), 2, 0); vecInt.push_back(1); vecInt.pop_back(); } void returnValue_std_isgreater(void) { // cppcheck-suppress knownConditionTrueFalse if (std::isgreater(4,2) == 0) {} // @todo support floats if (std::isgreater(4.0f,2.0f) == 0) {} } void returnValue_std_isgreaterequal(void) { // cppcheck-suppress knownConditionTrueFalse if (std::isgreaterequal(4,2) == 0) {} // @todo support floats if (std::isgreaterequal(4.0f,2.0f) == 0) {} } void returnValue_std_isless(void) { // cppcheck-suppress knownConditionTrueFalse if (std::isless(4,2) == 0) {} // @todo support floats if (std::isless(4.0f,2.0f) == 0) {} } void returnValue_std_islessequal(void) { // cppcheck-suppress knownConditionTrueFalse if (std::islessequal(4,2) == 0) {} // @todo support floats if (std::islessequal(4.0f,2.0f) == 0) {} } void returnValue_std_islessgreater(void) { // cppcheck-suppress knownConditionTrueFalse if (std::islessgreater(4,2) == 0) {} // cppcheck-suppress knownConditionTrueFalse if (std::islessgreater(2,4) == 0) {} if (std::islessgreater(4.0f,2.0f) == 0) {} // @todo support floats if (std::islessgreater(2.0f,4.0f) == 0) {} // @todo support floats } void bufferAccessOutOfBounds(void) { char a[5]; std::strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy std::strcpy(a, "abcde"); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress terminateStrncpy std::strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy std::strncpy(a,"abcde",6); } void uninitvar_abs(void) { int i; // cppcheck-suppress uninitvar (void)std::abs(i); } void uninivar_imaxabs(void) { intmax_t i1, i2; // cppcheck-suppress uninitvar (void)std::imaxabs(i1); // cppcheck-suppress uninitvar (void)imaxabs(i2); } void uninitvar_isalnum(void) { int i; // cppcheck-suppress uninitvar (void)std::isalnum(i); } void uninitvar_isalpha(void) { int i; // cppcheck-suppress uninitvar (void)std::isalpha(i); } void uninitvar_iscntrl(void) { int i; // cppcheck-suppress uninitvar (void)std::iscntrl(i); } void uninitvar_isdigit(void) { int i; // cppcheck-suppress uninitvar (void)std::isdigit(i); } void uninitvar_isgraph(void) { int i; // cppcheck-suppress uninitvar (void)std::isgraph(i); } void uninitvar_islower(void) { int i; // cppcheck-suppress uninitvar (void)std::islower(i); } void uninitvar_isprint(void) { int i; // cppcheck-suppress uninitvar (void)std::isprint(i); } void uninitvar_isspace(void) { int i; // cppcheck-suppress uninitvar (void)std::isspace(i); } void uninitvar_isupper(void) { int i; // cppcheck-suppress uninitvar (void)std::isupper(i); } void uninitvar_isxdigit(void) { int i; // cppcheck-suppress uninitvar (void)std::isxdigit(i); } void uninitvar_proj(void) { double d; const std::complex dc(d,d); // TODO cppcheck-suppress uninitvar (void)std::proj(dc); } void uninitvar_acos(void) { float f; // cppcheck-suppress uninitvar (void)std::acos(f); double d; // cppcheck-suppress uninitvar (void)std::acos(d); long double ld; // cppcheck-suppress uninitvar (void)std::acos(ld); } void uninitvar_acosh(void) { float f; // cppcheck-suppress uninitvar (void)std::acoshf(f); double d; // cppcheck-suppress uninitvar (void)std::acosh(d); long double ld; // cppcheck-suppress uninitvar (void)std::acoshl(ld); } void uninitvar_asctime(void) { const struct tm *tm; // cppcheck-suppress uninitvar // cppcheck-suppress asctimeCalled (void)std::asctime(tm); } void uninitvar_sqrt(void) { float f; // cppcheck-suppress uninitvar (void)std::sqrt(f); double d; // cppcheck-suppress uninitvar (void)std::sqrt(d); long double ld; // cppcheck-suppress uninitvar (void)std::sqrt(ld); } void uninitvar_sinh(void) { float f; // cppcheck-suppress uninitvar (void)std::sinh(f); double d; // cppcheck-suppress uninitvar (void)std::sinh(d); long double ld; // cppcheck-suppress uninitvar (void)std::sinh(ld); } void uninitvar_sin(void) { float f; // cppcheck-suppress uninitvar (void)std::sin(f); double d; // cppcheck-suppress uninitvar (void)std::sin(d); long double ld; // cppcheck-suppress uninitvar (void)std::sin(ld); } void uninitvar_asin(void) { float f; // cppcheck-suppress uninitvar (void)std::asin(f); double d; // cppcheck-suppress uninitvar (void)std::asin(d); long double ld; // cppcheck-suppress uninitvar (void)std::asin(ld); } void uninitvar_asinh(void) { float f; // cppcheck-suppress uninitvar (void)std::asinhf(f); double d; // cppcheck-suppress uninitvar (void)std::asinh(d); long double ld; // cppcheck-suppress uninitvar (void)std::asinhl(ld); } void uninitvar_wcsftime(wchar_t* ptr) { size_t maxsize; wchar_t* format; struct tm* timeptr; // cppcheck-suppress uninitvar (void)std::wcsftime(ptr, maxsize, format, timeptr); } void uninitvar_tan(void) { float f; // cppcheck-suppress uninitvar (void)std::tan(f); double d; // cppcheck-suppress uninitvar (void)std::tan(d); long double ld; // cppcheck-suppress uninitvar (void)std::tan(ld); } void uninitvar_tanh(void) { float f; // cppcheck-suppress uninitvar (void)std::tanh(f); double d; // cppcheck-suppress uninitvar (void)std::tanh(d); long double ld; // cppcheck-suppress uninitvar (void)std::tanh(ld); } void uninitvar_atan(void) { float f; // cppcheck-suppress uninitvar (void)std::atan(f); double d; // cppcheck-suppress uninitvar (void)std::atan(d); long double ld; // cppcheck-suppress uninitvar (void)std::atan(ld); } void uninitvar_tgamma(void) { float f; // cppcheck-suppress uninitvar (void)std::tgammaf(f); double d; // cppcheck-suppress uninitvar (void)std::tgamma(d); long double ld; // cppcheck-suppress uninitvar (void)std::tgammal(ld); } void uninitvar_trunc(void) { float f; // cppcheck-suppress uninitvar (void)std::truncf(f); double d; // cppcheck-suppress uninitvar (void)std::trunc(d); long double ld; // cppcheck-suppress uninitvar (void)std::truncl(ld); } void uninitvar_atanh(void) { float f; // cppcheck-suppress uninitvar (void)std::atanhf(f); double d; // cppcheck-suppress uninitvar (void)std::atanh(d); long double ld; // cppcheck-suppress uninitvar (void)std::atanhl(ld); } void uninitvar_atan2(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::atan2(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::atan2(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::atan2(ld1,ld2); } void uninitvar_atof(void) { char * c; // cppcheck-suppress uninitvar (void)std::atof(c); } void uninitvar_atol(void) { char * c1, *c2, *c3; // cppcheck-suppress uninitvar (void)std::atoi(c1); // cppcheck-suppress uninitvar (void)std::atol(c2); // cppcheck-suppress uninitvar (void)std::atoll(c3); } void uninitvar_ceil(void) { float f; // cppcheck-suppress uninitvar (void)std::ceil(f); double d; // cppcheck-suppress uninitvar (void)std::ceil(d); long double ld; // cppcheck-suppress uninitvar (void)std::ceil(ld); } void uninitvar_copysign(void) { float f1, f2; // cppcheck-suppress uninitvar (void)std::copysignf(f1, f2); double d1, d2; // cppcheck-suppress uninitvar (void)std::copysign(d1, d2); long double ld1, ld2; // cppcheck-suppress uninitvar (void)std::copysignl(ld1, ld2); } void uninitvar_cbrt(void) { float f; // cppcheck-suppress uninitvar (void)std::cbrtf(f); double d; // cppcheck-suppress uninitvar (void)std::cbrt(d); long double ld; // cppcheck-suppress uninitvar (void)std::cbrtl(ld); } void uninitvar_cos(void) { float f; // cppcheck-suppress uninitvar (void)std::cos(f); double d; // cppcheck-suppress uninitvar (void)std::cos(d); long double ld; // cppcheck-suppress uninitvar (void)std::cos(ld); } void uninitvar_clearerr(void) { FILE * stream; // cppcheck-suppress uninitvar std::clearerr(stream); } void uninitvar_cosh(void) { float f; // cppcheck-suppress uninitvar (void)std::cosh(f); double d; // cppcheck-suppress uninitvar (void)std::cosh(d); long double ld; // cppcheck-suppress uninitvar (void)std::cosh(ld); } void uninitvar_feraiseexcept(void) { int expects; // cppcheck-suppress uninitvar (void)std::feraiseexcept(expects); } void uninitvar_fesetexceptflag(fexcept_t* flagp) { int expects; // cppcheck-suppress uninitvar (void)std::fesetexceptflag(flagp, expects); } void uninitvar_feclearexcept(void) { int i; // cppcheck-suppress uninitvar (void)std::feclearexcept(i); } void uninitvar_fesetenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)std::fesetenv(envp); } void uninitvar_fesetround(void) { int i; // cppcheck-suppress uninitvar (void)std::fesetround(i); } void uninitvar_fetestexcept(void) { int i; // cppcheck-suppress uninitvar (void)std::fetestexcept(i); } void uninitvar_feupdateenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)std::feupdateenv(envp); } void uninitvar_ctime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::ctime(tp); } void uninitvar_difftime(void) { time_t t1,t2; // cppcheck-suppress uninitvar (void)std::difftime(t1, t2); } void uninitvar_div(void) { int num; int denom; // cppcheck-suppress uninitvar (void)std::div(num,denom); } void uninitvar_imaxdiv(void) { intmax_t numer1, numer2; intmax_t denom1, denom2; // cppcheck-suppress uninitvar (void)std::imaxdiv(numer1,denom1); // cppcheck-suppress uninitvar (void)imaxdiv(numer2,denom2); } void uninitvar_exit(void) { int i; // cppcheck-suppress uninitvar std::exit(i); } void uninitvar_erf(void) { float f; // cppcheck-suppress uninitvar (void)std::erff(f); double d; // cppcheck-suppress uninitvar (void)std::erf(d); long double ld; // cppcheck-suppress uninitvar (void)std::erfl(ld); } void uninitvar_erfc(void) { float f; // cppcheck-suppress uninitvar (void)std::erfcf(f); double d; // cppcheck-suppress uninitvar (void)std::erfc(d); long double ld; // cppcheck-suppress uninitvar (void)std::erfcl(ld); } void uninitvar_exp(void) { float f; // cppcheck-suppress uninitvar (void)std::exp(f); double d; // cppcheck-suppress uninitvar (void)std::exp(d); long double ld; // cppcheck-suppress uninitvar (void)std::exp(ld); } void uninitvar_exp2(void) { float f; // cppcheck-suppress uninitvar (void)std::exp2f(f); double d; // cppcheck-suppress uninitvar (void)std::exp2(d); long double ld; // cppcheck-suppress uninitvar (void)std::exp2l(ld); } void uninitvar_expm1(void) { float f; // cppcheck-suppress uninitvar (void)std::expm1f(f); double d; // cppcheck-suppress uninitvar (void)std::expm1(d); long double ld; // cppcheck-suppress uninitvar (void)std::expm1l(ld); } void uninitvar_fabs(void) { float f; // cppcheck-suppress uninitvar (void)std::fabs(f); double d; // cppcheck-suppress uninitvar (void)std::fabs(d); long double ld; // cppcheck-suppress uninitvar (void)std::fabs(ld); } void uninitvar_fdim(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fdimf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fdim(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fdiml(ld1,ld2); } void uninitvar_fclose(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fclose(stream); } void uninitvar_ferror(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::ferror(stream); } void uninitvar_feof(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::feof(stream); } void uninitvar_fflush(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fflush(stream); } void uninitvar_fgetc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fgetc(stream); } void uninitvar_fgetwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fgetwc(stream); } void uninitvar_fgetpos(void) { FILE* stream; fpos_t *ptr; // cppcheck-suppress uninitvar (void)std::fgetpos(stream,ptr); } void uninitvar_floor(void) { float f; // cppcheck-suppress uninitvar (void)std::floor(f); double d; // cppcheck-suppress uninitvar (void)std::floor(d); long double ld; // cppcheck-suppress uninitvar (void)std::floor(ld); } void uninitvar_fma(void) { float f1,f2,f3; // cppcheck-suppress uninitvar (void)std::fmaf(f1,f2,f3); double d1,d2,d3; // cppcheck-suppress uninitvar (void)std::fma(d1,d2,d3); long double ld1,ld2,ld3; // cppcheck-suppress uninitvar (void)std::fmal(ld1,ld2,ld3); } void uninitvar_fmax(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fmaxf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fmax(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fmaxl(ld1,ld2); } void uninitvar_fmin(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fminf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fmin(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fminl(ld1,ld2); } void uninitvar_fmod(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fmod(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fmod(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fmod(ld1,ld2); } void uninitar_fopen(void) { char *filename; char *mode; // cppcheck-suppress uninitvar FILE * fp = std::fopen(filename, mode); fclose(fp); } void uninitar_fprintf(FILE *Stream, char *Format, int Argument) { FILE *stream1, *stream2; char *format1, *format2; int argument1, argument2; // cppcheck-suppress uninitvar (void)std::fprintf(stream1, format1, argument1); // cppcheck-suppress uninitvar (void)std::fprintf(stream2, Format, Argument); // cppcheck-suppress uninitvar (void)std::fprintf(Stream, format2, Argument); // cppcheck-suppress uninitvar (void)std::fprintf(Stream, Format, argument2); // no warning is expected (void)std::fprintf(Stream, Format, Argument); } void uninitar_vfprintf(FILE *Stream, const char *Format, va_list Arg) { FILE *stream1, *stream2; char *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfprintf(stream1, format1, arg); // cppcheck-suppress uninitvar (void)std::vfprintf(stream2, Format, Arg); // cppcheck-suppress uninitvar (void)std::vfprintf(Stream, format2, Arg); // no warning is expected (void)std::vfprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vfprintf(Stream, Format, arg); } void uninitar_vfwprintf(FILE *Stream, wchar_t *Format, va_list Arg) { FILE *stream1, *stream2; wchar_t *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfwprintf(stream1, format1, arg); // cppcheck-suppress uninitvar (void)std::vfwprintf(stream2, Format, Arg); // cppcheck-suppress uninitvar (void)std::vfwprintf(Stream, format2, Arg); // no warning is expected (void)std::vfwprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vfwprintf(Stream, Format, arg); } void uninitvar_fputc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputc(c,stream); } void uninitvar_fputwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputwc(c,stream); } void uninitvar_fputs(void) { char *string; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputs(string,stream); } void uninitvar_fputws(void) { wchar_t *string; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputws(string,stream); } void uninitvar_fread(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)std::fread(ptr,size,nobj,stream); } void uninitvar_free(void) { // cppcheck-suppress unassignedVariable void *block; // cppcheck-suppress uninitvar std::free(block); } void uninitvar_freopen(void) { char *filename; char *mode; FILE *stream; // cppcheck-suppress uninitvar FILE * p = std::freopen(filename,mode,stream); free(p); } void uninitvar_frexp(void) { float f1; int *i1; // cppcheck-suppress uninitvar (void)std::frexp(f1,i1); double d1; int *i2; // cppcheck-suppress uninitvar (void)std::frexp(d1,i2); long double ld1; int *i3; // cppcheck-suppress uninitvar (void)std::frexp(ld1,i3); } void uninitvar_hypot(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::hypotf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::hypot(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::hypotl(ld1,ld2); } void uninitvar_fscanf(void) { FILE *stream; char *format; int i; // cppcheck-suppress uninitvar (void)std::fscanf(stream,format,i); } void uninitvar_vfscanf(void) { FILE *stream; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfscanf(stream,format,arg); } void uninitvar_vfwscanf(void) { FILE *stream; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfwscanf(stream,format,arg); } void uninitvar_fseek(void) { FILE* stream; long int offset; int origin; // cppcheck-suppress uninitvar (void)std::fseek(stream,offset,origin); } void uninitvar_fsetpos(void) { FILE* stream; fpos_t *ptr; // cppcheck-suppress uninitvar (void)std::fsetpos(stream,ptr); } void uninitvar_fgets(void) { char *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)std::fgets(buffer,n,stream); } void uninitvar_fgetws(void) { wchar_t *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)std::fgetws(buffer,n,stream); } void uninitvar_ftell(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::ftell(stream); } void uninitvar_fwide(void) { FILE *stream; int mode; // cppcheck-suppress uninitvar (void)std::fwide(stream,mode); } void uninitvar_fwrite(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)std::fwrite(ptr,size,nobj,stream); } void uninitvar_mblen(void) { char *string; size_t size; // cppcheck-suppress uninitvar (void)std::mblen(string,size); } void uninitvar_mbtowc(void) { wchar_t* pwc; char* pmb; size_t max; // cppcheck-suppress uninitvar (void)std::mbtowc(pwc,pmb,max); } void uninitvar_mbrlen(const char* p, size_t m, mbstate_t* s) { char* pmb1, *pmb2; size_t max1, max2; mbstate_t* ps1, *ps2; // cppcheck-suppress uninitvar (void)std::mbrlen(pmb1,max1,ps1); // cppcheck-suppress uninitvar (void)std::mbrlen(pmb2,m,s); // cppcheck-suppress uninitvar (void)std::mbrlen(p,max2,s); // cppcheck-suppress uninitvar (void)std::mbrlen(p,m,ps2); // no warning is expected (void)std::mbrlen(p,m,s); } void nullPointer_mbrlen(const char* p, size_t m, mbstate_t* s) { // no warning is expected: A call to the function with a null pointer as pmb resets the shift state (and ignores parameter max). (void)std::mbrlen(NULL,m,s); (void)std::mbrlen(NULL,0,s); // cppcheck-suppress nullPointer (void)std::mbrlen(p,m,NULL); } void uninitvar_btowc(void) { int c; // cppcheck-suppress uninitvar (void)std::btowc(c); } void uninitvar_mbsinit(void) { mbstate_t* ps; // cppcheck-suppress uninitvar (void)std::mbsinit(ps); } void uninitvar_mbstowcs(void) { wchar_t *ws; char *s; size_t n; // cppcheck-suppress uninitvar (void)std::mbstowcs(ws,s,n); } void uninitvar_mbsrtowcs(void) { wchar_t* dest; const char* src; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)std::mbsrtowcs(dest,&src,max,ps); } void uninitvar_wctob(void) { wint_t wc; // cppcheck-suppress uninitvar (void)std::wctob(wc); } void uninitvar_wctomb(void) { char *s; wchar_t wc; // cppcheck-suppress uninitvar (void)std::wctomb(s,wc); } void uninitvar_wcstombs(void) { char *mbstr; wchar_t *wcstr; size_t n; // cppcheck-suppress uninitvar (void)std::wcstombs(mbstr,wcstr,n); } void uninitvar_getc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::getc(stream); } void uninitvar_getwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::getwc(stream); } void uninitvar_ungetc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)std::ungetc(c,stream); } void uninitvar_ungetwc(void) { wint_t c; FILE *stream; // cppcheck-suppress uninitvar (void)std::ungetwc(c,stream); } void uninitvar_getenv(void) { char *name; // cppcheck-suppress uninitvar (void)std::getenv(name); } void uninitvar_gmtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::gmtime(tp); } void uninitvar_iswalnum(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswalnum(i); } void uninitvar_iswalpha(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswalpha(i); } void uninitvar_isblank(void) { int i; // cppcheck-suppress uninitvar (void)std::isblank(i); } void uninitvar_iswblank(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswblank(i); } void uninitvar_iswcntrl(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswcntrl(i); } void uninitvar_iswctype(void) { wint_t c; wctype_t desc; // cppcheck-suppress uninitvar (void)std::iswctype(c,desc); } void uninitvar_iswdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswdigit(i); } void uninitvar_iswgraph(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswgraph(i); } void uninitvar_iswlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswlower(i); } void uninitvar_iswprint(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswprint(i); } void uninitvar_ispunct(void) { int i; // cppcheck-suppress uninitvar (void)std::ispunct(i); } void uninitvar_iswpunct(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswpunct(i); } void uninitvar_iswspace(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswspace(i); } void uninitvar_iswupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswupper(i); } void uninitvar_iswxdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswxdigit(i); } void uninitvar_towctrans(void) { wint_t c; wctrans_t desc; // cppcheck-suppress uninitvar (void)std::towctrans(c,desc); } void uninitvar_towlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::towlower(i); } void uninitvar_towupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::towupper(i); } void uninitvar_wctrans(void) { char* property; // cppcheck-suppress uninitvar (void)std::wctrans(property); } void uninitvar_wctype(void) { char* property; // cppcheck-suppress uninitvar (void)std::wctype(property); } void uninitvar_labs(void) { long int li; // cppcheck-suppress uninitvar (void)std::labs(li); long long int lli; // cppcheck-suppress uninitvar (void)std::llabs(lli); } void uninitvar_ldexp(void) { float fd; int e1; // cppcheck-suppress uninitvar (void)std::ldexp(fd,e1); double dc; int e2; // cppcheck-suppress uninitvar (void)std::ldexp(dc,e2); long double ldc; int e3; // cppcheck-suppress uninitvar (void)std::ldexp(ldc,e3); } void uninitvar_lgamma(void) { float f; // cppcheck-suppress uninitvar (void)std::lgammaf(f); double d; // cppcheck-suppress uninitvar (void)std::lgamma(d); long double ld; // cppcheck-suppress uninitvar (void)std::lgammal(ld); } void uninitvar_rint(void) { float f; // cppcheck-suppress uninitvar (void)std::rintf(f); double d; // cppcheck-suppress uninitvar (void)std::rint(d); long double ld; // cppcheck-suppress uninitvar (void)std::rintl(ld); } void uninitvar_lrint(void) { float f; // cppcheck-suppress uninitvar (void)std::lrintf(f); double d; // cppcheck-suppress uninitvar (void)std::lrint(d); long double ld; // cppcheck-suppress uninitvar (void)std::lrintl(ld); } void uninitvar_llrint(void) { float f; // cppcheck-suppress uninitvar (void)std::llrintf(f); double d; // cppcheck-suppress uninitvar (void)std::llrint(d); long double ld; // cppcheck-suppress uninitvar (void)std::llrintl(ld); } void uninitvar_lround(void) { float f; // cppcheck-suppress uninitvar (void)std::lroundf(f); double d; // cppcheck-suppress uninitvar (void)std::lround(d); long double ld; // cppcheck-suppress uninitvar (void)std::lroundl(ld); } void uninitvar_llround(void) { float f; // cppcheck-suppress uninitvar (void)std::llroundf(f); double d; // cppcheck-suppress uninitvar (void)std::llround(d); long double ld; // cppcheck-suppress uninitvar (void)std::llroundl(ld); } void uninitvar_srand(void) { unsigned int seed; // cppcheck-suppress uninitvar (void)std::srand(seed); } void uninitvar_ldiv(void) { long int l1; long int l2; // cppcheck-suppress uninitvar (void)std::ldiv(l1,l2); long long int ll1; long long int ll2; // cppcheck-suppress uninitvar (void)std::lldiv(ll1,ll2); } void uninitvar_localtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::localtime(tp); } void uninitvar_log(void) { float f; // cppcheck-suppress uninitvar (void)std::log(f); double d; // cppcheck-suppress uninitvar (void)std::log(d); long double ld; // cppcheck-suppress uninitvar (void)std::log(ld); } void uninitvar_fpclassify(void) { float f; // cppcheck-suppress uninitvar (void)std::fpclassify(f); double d; // cppcheck-suppress uninitvar (void)std::fpclassify(d); long double ld; // cppcheck-suppress uninitvar (void)std::fpclassify(ld); } void uninitvar_isfinite(void) { float f; // cppcheck-suppress uninitvar (void)std::isfinite(f); double d; // cppcheck-suppress uninitvar (void)std::isfinite(d); long double ld; // cppcheck-suppress uninitvar (void)std::isfinite(ld); } void uninitvar_isgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::isgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::isgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::isgreater(ld1,ld2); } void uninitvar_isgreaterequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::isgreaterequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::isgreaterequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::isgreaterequal(ld1,ld2); } void uninitvar_isinf(void) { double d; // cppcheck-suppress uninitvar (void)std::isinf(d); } void uninitvar_logb(void) { float f; // cppcheck-suppress uninitvar (void)std::logbf(f); double d; // cppcheck-suppress uninitvar (void)std::logb(d); long double ld; // cppcheck-suppress uninitvar (void)std::logbl(ld); } void uninitvar_isless(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::isless(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::isless(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::isless(ld1,ld2); } void uninitvar_islessequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::islessequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::islessequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::islessequal(ld1,ld2); } void uninitvar_islessgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::islessgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::islessgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::islessgreater(ld1,ld2); } void uninitvar_nan(void) { char *tagp1, *tagp2, *tagp3; // cppcheck-suppress uninitvar (void)std::nanf(tagp1); // cppcheck-suppress uninitvar (void)std::nan(tagp2); // cppcheck-suppress uninitvar (void)std::nanl(tagp3); } void uninitvar_isnan(void) { double d; // cppcheck-suppress uninitvar (void)std::isnan(d); } void uninitvar_isnormal(void) { double d; // cppcheck-suppress uninitvar (void)std::isnormal(d); } void uninitvar_isunordered(void) { double d1,d2; // cppcheck-suppress uninitvar (void)std::isunordered(d1,d2); } void uninitvar_ilogb(void) { float f; // cppcheck-suppress uninitvar (void)std::ilogb(f); double d; // cppcheck-suppress uninitvar (void)std::ilogb(d); long double ld; // cppcheck-suppress uninitvar (void)std::ilogb(ld); } void uninitvar_log10(void) { float f; // cppcheck-suppress uninitvar (void)std::log10(f); double d; // cppcheck-suppress uninitvar (void)std::log10(d); long double ld; // cppcheck-suppress uninitvar (void)std::log10(ld); } void uninitvar_log1p(void) { float f; // cppcheck-suppress uninitvar (void)std::log1pf(f); double d; // cppcheck-suppress uninitvar (void)std::log1p(d); long double ld; // cppcheck-suppress uninitvar (void)std::log1pl(ld); } void uninitvar_log2(void) { float f; // cppcheck-suppress uninitvar (void)std::log2f(f); double d; // cppcheck-suppress uninitvar (void)std::log2(d); long double ld; // cppcheck-suppress uninitvar (void)std::log2l(ld); } void uninitvar_nearbyint(void) { float f; // cppcheck-suppress uninitvar (void)std::nearbyintf(f); double d; // cppcheck-suppress uninitvar (void)std::nearbyint(d); long double ld; // cppcheck-suppress uninitvar (void)std::nearbyintl(ld); } void uninitvar_nextafter(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::nextafterf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::nextafter(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::nextafterl(ld1,ld2); } void uninitvar_nexttoward(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::nexttowardf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::nexttoward(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::nexttowardl(ld1,ld2); } void uninitvar_longjmp(void) { jmp_buf env; int val; // cppcheck-suppress uninitvar (void)std::longjmp(env,val); } void uninitvar_malloc(void) { size_t size; // cppcheck-suppress uninitvar int *p = (int*)std::malloc(size); free(p); } void uninitvar_memchr(void) { void *cs; int c; size_t n; // cppcheck-suppress uninitvar (void)std::memchr(cs,c,n); } void uninitvar_wmemchr(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemchr(cs,c,n); } void uninitvar_memcmp(void) { void *s1; void *s2; size_t n; // cppcheck-suppress uninitvar (void)std::memcmp(s1,s2,n); } void uninitvar_wmemcmp(void) { wchar_t *s1; wchar_t *s2; size_t n; // cppcheck-suppress uninitvar (void)std::wmemcmp(s1,s2,n); } void uninitvar_memcpy(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)std::memcpy(ct,cs,n); } void uninitvar_wmemcpy(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemcpy(cs,c,n); } void uninitvar_memmove(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)std::memmove(ct,cs,n); } void uninitvar_wmemmove(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemmove(cs,c,n); } void uninitvar_memset(void) { void *s; int c; size_t n; // cppcheck-suppress uninitvar (void)std::memset(s,c,n); } void uninitvar_wmemset(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemset(cs,c,n); } void uninitvar_mktime(void) { struct tm *tp; // cppcheck-suppress uninitvar (void)std::mktime(tp); } void uninivar_modf(void) { float f1; float *f2; // cppcheck-suppress uninitvar (void)std::modf(f1,f2); double d1; double *d2; // cppcheck-suppress uninitvar (void)std::modf(d1,d2); long double ld1; long double *ld2; // cppcheck-suppress uninitvar (void)std::modf(ld1,ld2); } void uninivar_perror(void) { char *string; // cppcheck-suppress uninitvar (void)std::perror(string); } void uninitvar_pow(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::pow(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::pow(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::pow(ld1,ld2); } void uninitvar_remainder(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::remainderf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::remainder(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::remainderl(ld1,ld2); } void uninitvar_remquo(void) { float f1,f2; int *i1; // cppcheck-suppress uninitvar (void)std::remquof(f1,f2,i1); double d1,d2; int *i2; // cppcheck-suppress uninitvar (void)std::remquo(d1,d2,i2); long double ld1,ld2; int *i3; // cppcheck-suppress uninitvar (void)std::remquol(ld1,ld2,i3); } void uninivar_printf(char *Format, int Argument) { char * format_1, * format_2, * format_3; int argument1, argument2; // no warning is expected (void)std::printf("x"); // cppcheck-suppress uninitvar (void)std::printf(format_1,argument1); // cppcheck-suppress uninitvar (void)std::printf(Format,argument2); // cppcheck-suppress uninitvar (void)std::printf(format_2,Argument); // cppcheck-suppress uninitvar (void)std::printf(format_3,1); // no warning is expected (void)std::printf(Format,Argument); } void uninivar_vprintf(char *Format, va_list Arg) { char * format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vprintf(format1,arg); // cppcheck-suppress uninitvar (void)std::vprintf(format2,Arg); // no warning is expected (void)std::vprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vprintf(Format,arg); } void uninivar_vwprintf(wchar_t *Format, va_list Arg) { wchar_t * format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vwprintf(format1,arg); // cppcheck-suppress uninitvar (void)std::vwprintf(format2,Arg); // no warning is expected (void)std::vwprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vwprintf(Format,arg); } void uninivar_bsearch(void) { void* key; void* base; size_t num; size_t size; // cppcheck-suppress uninitvar (void)std::bsearch(key,base,num,size,(int (*)(const void*,const void*))strcmp); } void minsize_bsearch(const void* key, const void* base, size_t num, size_t size, int (*compar)(const void*,const void*)) { int Base[3] = {42, 43, 44}; (void)std::bsearch(key,Base,2,size,(int (*)(const void*,const void*))strcmp); (void)std::bsearch(key,Base,3,size,(int (*)(const void*,const void*))strcmp); (void)std::bsearch(key,Base,4,size,(int (*)(const void*,const void*))strcmp); (void)std::bsearch(key,base,2,size,(int (*)(const void*,const void*))strcmp); } void uninitvar_qsort(void) { void *base; size_t n; size_t size; // cppcheck-suppress uninitvar (void)std::qsort(base,n,size, (int (*)(const void*,const void*))strcmp); } void uninitvar_putc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)std::putc(c,stream); } void uninitvar_putwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)std::putc(c,stream); } void uninitvar_putchar(void) { int c; // cppcheck-suppress uninitvar (void)std::putchar(c); } void uninitvar_putwchar(void) { wchar_t c; // cppcheck-suppress uninitvar (void)std::putwchar(c); } void uninitvar_puts(void) { char *s; // cppcheck-suppress uninitvar (void)std::puts(s); } void uninitvar_realloc(void) { void *block; size_t newsize; // cppcheck-suppress uninitvar void *p = std::realloc(block, newsize); free(p); } void uninitvar_remove(void) { char *s; // cppcheck-suppress uninitvar (void)std::remove(s); } void uninitvar_rename(void) { char *s1; char *s2; // cppcheck-suppress uninitvar (void)std::rename(s1,s2); } void uninitvar_rewind(void) { FILE *f; // cppcheck-suppress uninitvar (void)std::rewind(f); } void uninitvar_round(void) { float f; // cppcheck-suppress uninitvar (void)std::roundf(f); double d; // cppcheck-suppress uninitvar (void)std::round(d); long double ld; // cppcheck-suppress uninitvar (void)std::roundl(ld); } void uninivar_scalbn(void) { float f; int i1; // cppcheck-suppress uninitvar (void)std::scalbnf(f,i1); double d; int i2; // cppcheck-suppress uninitvar (void)std::scalbn(d,i2); long double ld; int i3; // cppcheck-suppress uninitvar (void)std::scalbnl(ld,i3); } void uninivar_scalbln(void) { float f; long int i1; // cppcheck-suppress uninitvar (void)std::scalblnf(f,i1); double d; long int i2; // cppcheck-suppress uninitvar (void)std::scalbln(d,i2); long double ld; long int i3; // cppcheck-suppress uninitvar (void)std::scalblnl(ld,i3); } void uninitvar_signbit(void) { double d; // cppcheck-suppress uninitvar (void)std::signbit(d); } void uninivar_signal(void) { int i; // cppcheck-suppress uninitvar std::signal(i, exit); } void uninivar_raise(void) { int i; // cppcheck-suppress uninitvar (void)std::raise(i); } void uninivar_scanf(void) { char *format; char str[42]; // cppcheck-suppress uninitvar (void)std::scanf(format, str); } void uninivar_vsscanf(void) { char *s; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vsscanf(s,format,arg); } void uninivar_vswscanf(void) { wchar_t *s; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vswscanf(s,format,arg); } void uninivar_vscanf(void) { char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vscanf(format,arg); } void uninivar_vwscanf(void) { wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vwscanf(format,arg); } void uninivar_setbuf(void) { FILE *stream; char *buf; // cppcheck-suppress uninitvar (void)std::setbuf(stream,buf); } void uninivar_setvbuf(void) { FILE *stream; char *buf; int mode; size_t size; // cppcheck-suppress uninitvar (void)std::setvbuf(stream,buf,mode,size); } void uninitvar_strcat(char *dest, const char * const source) { char *deststr1, *deststr2; char *srcstr1, *srcstr2; // cppcheck-suppress uninitvar (void)std::strcat(deststr1,srcstr1); // cppcheck-suppress uninitvar (void)std::strcat(dest,srcstr2); // cppcheck-suppress uninitvar (void)std::strcat(deststr2,source); // no warning shall be shown for (void)std::strcat(dest,source); } void uninitvar_wcscat(wchar_t *dest, const wchar_t * const source) { wchar_t *deststr_1, *deststr_2; wchar_t *srcstr1, *srcstr2; // cppcheck-suppress uninitvar (void)std::wcscat(deststr_1,srcstr1); // cppcheck-suppress uninitvar (void)std::wcscat(dest,srcstr2); // cppcheck-suppress uninitvar (void)std::wcscat(deststr_2,source); // no warning shall be shown for (void)std::wcscat(dest,source); } void uninivar_wcrtomb(void) { char *s; wchar_t wc; mbstate_t *ps; // cppcheck-suppress uninitvar (void)std::wcrtomb(s,wc,ps); } void uninivar_strchr(void) { char *cs; int c; // cppcheck-suppress uninitvar (void)std::strchr(cs,c); } void uninivar_wcschr(void) { wchar_t *cs; wchar_t c; // cppcheck-suppress uninitvar (void)std::wcschr(cs,c); } void uninivar_strcmp(void) { char *str1; char *str2; // cppcheck-suppress uninitvar (void)std::strcmp(str1,str2); } void uninivar_wcscmp(void) { wchar_t *str1; wchar_t *str2; // cppcheck-suppress uninitvar (void)std::wcscmp(str1,str2); } void uninivar_strcpy(void) { char *str1; char *str2; // cppcheck-suppress uninitvar (void)std::strcpy(str1,str2); } void uninivar_wcscpy(void) { wchar_t *str1; wchar_t *str2; // cppcheck-suppress uninitvar (void)std::wcscpy(str1,str2); } void uninivar_strftime(void) { char *s; size_t max; char *fmt; struct tm *p; // cppcheck-suppress uninitvar (void)std::strftime(s,max,fmt,p); } void uninivar_strlen(void) { char *s; // cppcheck-suppress uninitvar (void)std::strlen(s); } void uninivar_wcslen(void) { wchar_t *s; // cppcheck-suppress uninitvar (void)std::wcslen(s); } void uninivar_strncpy(void) { char *s; char *ct; size_t n; // cppcheck-suppress uninitvar (void)std::strncpy(s,ct,n); } void uninivar_strpbrk(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strpbrk(cs,ct); } void uninivar_strncat(char *Ct, char *S, size_t N) { char *ct_1, *ct_2; char *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::strncat(ct_1,s1,n1); // cppcheck-suppress uninitvar (void)std::strncat(ct_2,S,N); // cppcheck-suppress uninitvar (void)std::strncat(Ct,s2,N); // cppcheck-suppress uninitvar (void)std::strncat(Ct,S,n2); // no warning is expected for (void)std::strncat(Ct,S,N); } void uninivar_wcsncat(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct_1, *ct_2; wchar_t *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::wcsncat(ct_1,s1,n1); // cppcheck-suppress uninitvar (void)std::wcsncat(ct_2,S,N); // cppcheck-suppress uninitvar (void)std::wcsncat(Ct,s2,N); // cppcheck-suppress uninitvar (void)std::wcsncat(Ct,S,n2); // no warning is expected for (void)std::wcsncat(Ct,S,N); } void uninivar_strncmp(char *Ct, char *S, size_t N) { char *ct1, *ct2; char *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::strncmp(ct1,s1,n1); // cppcheck-suppress uninitvar (void)std::strncmp(ct2,S,N); // cppcheck-suppress uninitvar (void)std::strncmp(Ct,s2,N); // cppcheck-suppress uninitvar (void)std::strncmp(Ct,S,n2); // no warning is expected for (void)std::strncmp(Ct,S,N); } void uninivar_wcsncmp(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct1, *ct2; wchar_t *s1, *s2; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::wcsncmp(ct1,s1,n1); // cppcheck-suppress uninitvar (void)std::wcsncmp(ct2,S,N); // cppcheck-suppress uninitvar (void)std::wcsncmp(Ct,s2,N); // cppcheck-suppress uninitvar (void)std::wcsncmp(Ct,S,n2); // no warning is expected for (void)std::wcsncmp(Ct,S,N); } void uninivar_strstr(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strstr(cs,ct); } void uninivar_wcsstr(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcsstr(cs,ct); } void uninivar_strspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strspn(cs,ct); } void uninivar_strxfrm(void) { char *ds; char *ss; size_t n; // cppcheck-suppress uninitvar (void)std::strxfrm(ds,ss,n); } void uninivar_wcsxfrm(void) { wchar_t *ds; wchar_t *ss; size_t n; // cppcheck-suppress uninitvar (void)std::wcsxfrm(ds,ss,n); } void uninivar_wcsspn(void) { wchar_t *ds; wchar_t *ss; // cppcheck-suppress uninitvar (void)std::wcsspn(ds,ss); } void uninivar_setlocale(void) { int category; char* locale; // cppcheck-suppress uninitvar (void)std::setlocale(category,locale); } void uninivar_strerror(void) { int i; // cppcheck-suppress uninitvar (void)std::strerror(i); } void uninivar_strcspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strcspn(cs,ct); } void uninivar_wcscspn(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcscspn(cs,ct); } void uninivar_wcspbrk(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcspbrk(cs,ct); } void uninivar_wcsncpy(void) { wchar_t *cs; wchar_t *ct; size_t n; // cppcheck-suppress uninitvar (void)std::wcsncpy(cs,ct,n); } void uninivar_strcoll(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strcoll(cs,ct); } void uninivar_wcscoll(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcscoll(cs,ct); } void uninivar_strrchr(void) { char * str; int c; // cppcheck-suppress uninitvar (void)std::strrchr(str,c); } void uninivar_wcsrchr(void) { wchar_t* ws; wchar_t wc; // cppcheck-suppress uninitvar (void)std::wcsrchr(ws,wc); } void uninivar_wcsrtombs(void) { char *dst; const wchar_t * p;; size_t len; mbstate_t *ps; // cppcheck-suppress uninitvar (void)std::wcsrtombs(dst,&p,len,ps); } void uninivar_strtok(void) { char *s; char *ct; // cppcheck-suppress uninitvar (void)std::strtok(s,ct); } void uninivar_strtoimax(void) { const char *s1, *s2; char **endp1, **endp2; int base1, base2; // cppcheck-suppress uninitvar (void)std::strtoimax(s1,endp1,base1); // cppcheck-suppress uninitvar (void)std::strtoumax(s2,endp2,base2); } void uninivar_strtof(void) { const char *s1, *s2, *s3; char **endp1, **endp2, **endp3; // cppcheck-suppress uninitvar (void)std::strtof(s1,endp1); // cppcheck-suppress uninitvar (void)std::strtod(s2,endp2); // cppcheck-suppress uninitvar (void)std::strtold(s3,endp3); } void uninivar_strtol(void) { const char *s1,*s2,*s3,*s4,*s5,*s6,*s7,*s8; char **endp1, **endp2, **endp3, **endp4, **endp5, **endp6, **endp7, **endp8; int base1, base2, base3, base4, base5, base6, base7, base8; // cppcheck-suppress uninitvar (void)std::strtol(s1,endp1,base1); // cppcheck-suppress uninitvar (void)std::strtoll(s2,endp2,base2); // cppcheck-suppress uninitvar (void)std::strtoul(s3,endp3,base3); // cppcheck-suppress uninitvar (void)std::strtoull(s4,endp4,base4); // cppcheck-suppress uninitvar (void)std::strtoimax(s5,endp5,base5); // cppcheck-suppress uninitvar (void)strtoimax(s6,endp6,base6); // cppcheck-suppress uninitvar (void)std::strtoumax(s7,endp7,base7); // cppcheck-suppress uninitvar (void)strtoumax(s8,endp8,base8); } void uninitvar_time(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::time(tp); } void uninitvar_tmpnam(void) { char *s; // cppcheck-suppress uninitvar (void)std::tmpnam(s); } void uninivar_tolower(void) { int c; // cppcheck-suppress uninitvar (void)std::tolower(c); } void uninivar_toupper(void) { int c; // cppcheck-suppress uninitvar (void)std::toupper(c); } void uninivar_wcstof(void) { const wchar_t *s1, *s2, *s3; wchar_t **endp1, **endp2, **endp3; // cppcheck-suppress uninitvar (void)std::wcstof(s1,endp1); // cppcheck-suppress uninitvar (void)std::wcstod(s2,endp2); // cppcheck-suppress uninitvar (void)std::wcstold(s3,endp3); } void uninivar_stoX(void) { std::string str; std::wstring wstr; size_t* idx1; size_t* idx2; size_t* idx3; size_t* idx4; size_t* idx5; size_t* idx6; size_t* idx7; size_t* idx8; size_t* idx9; size_t* idx10; size_t* idx11; size_t* idx12; size_t* idx13; size_t* idx14; size_t* idx15; size_t* idx16; // cppcheck-suppress uninitvar (void)std::stod(str,idx1); // cppcheck-suppress uninitvar (void)std::stod(wstr,idx2); // cppcheck-suppress uninitvar (void)std::stof(str,idx3); // cppcheck-suppress uninitvar (void)std::stof(wstr,idx4); // cppcheck-suppress uninitvar (void)std::stoi(str,idx5); // cppcheck-suppress uninitvar (void)std::stoi(wstr,idx6); // cppcheck-suppress uninitvar (void)std::stol(str,idx7); // cppcheck-suppress uninitvar (void)std::stol(wstr,idx8); // cppcheck-suppress uninitvar (void)std::stold(str,idx9); // cppcheck-suppress uninitvar (void)std::stold(wstr,idx10); // cppcheck-suppress uninitvar (void)std::stoll(str,idx11); // cppcheck-suppress uninitvar (void)std::stoll(wstr,idx12); // cppcheck-suppress uninitvar (void)std::stoul(str,idx13); // cppcheck-suppress uninitvar (void)std::stoul(wstr,idx14); // cppcheck-suppress uninitvar (void)std::stoull(str,idx15); // cppcheck-suppress uninitvar (void)std::stoull(wstr,idx16); } void uninivar_to_string(void) { int i; long l; long long ll; unsigned u; unsigned long ul; unsigned long long ull; float f; double d; long double ld; // cppcheck-suppress uninitvar (void)std::to_string(i); // cppcheck-suppress uninitvar (void)std::to_string(l); // cppcheck-suppress uninitvar (void)std::to_string(ll); // cppcheck-suppress uninitvar (void)std::to_string(u); // cppcheck-suppress uninitvar (void)std::to_string(ul); // cppcheck-suppress uninitvar (void)std::to_string(ull); // cppcheck-suppress uninitvar (void)std::to_string(f); // cppcheck-suppress uninitvar (void)std::to_string(d); // cppcheck-suppress uninitvar (void)std::to_string(ld); } void uninivar_to_wstring(void) { int i; long l; long long ll; unsigned u; unsigned long ul; unsigned long long ull; float f; double d; long double ld; // cppcheck-suppress uninitvar (void)std::to_wstring(i); // cppcheck-suppress uninitvar (void)std::to_wstring(l); // cppcheck-suppress uninitvar (void)std::to_wstring(ll); // cppcheck-suppress uninitvar (void)std::to_wstring(u); // cppcheck-suppress uninitvar (void)std::to_wstring(ul); // cppcheck-suppress uninitvar (void)std::to_wstring(ull); // cppcheck-suppress uninitvar (void)std::to_wstring(f); // cppcheck-suppress uninitvar (void)std::to_wstring(d); // cppcheck-suppress uninitvar (void)std::to_wstring(ld); } void uninivar_mbrtowc(void) { wchar_t* pwc; const char* pmb; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)std::mbrtowc(pwc,pmb,max,ps); } void uninivar_wcstok(void) { wchar_t *s; const wchar_t *ct; wchar_t **ptr; // cppcheck-suppress uninitvar (void)std::wcstok(s,ct,ptr); } void uninivar_wcstoimax(void) { const wchar_t *s1, *s2; wchar_t ** endp1, **endp2; int base1, base2; // cppcheck-suppress uninitvar (void)std::wcstoimax(s1,endp1,base1); // cppcheck-suppress uninitvar (void)std::wcstoumax(s2,endp2,base2); } void uninivar_wcstol(void) { const wchar_t *s1,*s2,*s3,*s4,*s5,*s6,*s7,*s8; wchar_t **endp1, **endp2, **endp3, **endp4, **endp5, **endp6, **endp7, **endp8; int base1, base2, base3, base4, base5, base6, base7, base8; // cppcheck-suppress uninitvar (void)std::wcstol(s1,endp1,base1); // cppcheck-suppress uninitvar (void)std::wcstoll(s2,endp2,base2); // cppcheck-suppress uninitvar (void)std::wcstoul(s3,endp3,base3); // cppcheck-suppress uninitvar (void)std::wcstoull(s4,endp4,base4); // cppcheck-suppress uninitvar (void)std::wcstoimax(s5,endp5,base5); // cppcheck-suppress uninitvar (void)wcstoimax(s6,endp6,base6); // cppcheck-suppress uninitvar (void)std::wcstoumax(s7,endp7,base7); // cppcheck-suppress uninitvar (void)wcstoumax(s8,endp8,base8); } void uninitvar_wprintf(wchar_t *Format, int Argument) { const wchar_t *format1, *format2, *format3; int argument1, argument2; // cppcheck-suppress uninitvar (void)std::wprintf(format1,argument1); // cppcheck-suppress uninitvar (void)std::wprintf(format2); // cppcheck-suppress uninitvar (void)std::wprintf(Format,argument2); // cppcheck-suppress uninitvar (void)std::wprintf(format3,Argument); // no warning is expected (void)std::wprintf(Format,Argument); (void)std::wprintf(Format); } void uninitvar_sprintf(void) { char *s; const char *format; int i; // cppcheck-suppress uninitvar (void)std::sprintf(s,format,i); } void uninitvar_swprintf(void) { wchar_t *s; size_t n; const wchar_t *format; // cppcheck-suppress uninitvar (void)std::swprintf(s,n,format); } void uninitvar_vsprintf(void) { char *s; const char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vsprintf(s,format,arg); } void nullPointer_vsprintf(va_list arg,const char *format) { char *s = NULL; (void)std::vsprintf(s,format,arg); // Its allowed to provide 's' as NULL pointer // cppcheck-suppress nullPointer (void)std::vsprintf(s,NULL,arg); } void uninitvar_vswprintf(void) { wchar_t *s; size_t n; const wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vswprintf(s,n,format,arg); } void uninivar_fwprintf(void) { FILE* stream; const wchar_t* format; int i; // cppcheck-suppress uninitvar (void)std::fwprintf(stream,format,i); } void uninivar_snprintf(char *S, size_t N, char *Format, int Int) { size_t n1, n2; char *format1, *format2; int i1, i2; char *s1, *s2; // cppcheck-suppress uninitvar (void)std::snprintf(s1,n1,format1,i1); // cppcheck-suppress uninitvar (void)std::snprintf(S,n2,Format,Int); // n is uninitialized // cppcheck-suppress uninitvar (void)std::snprintf(S,N,format2,Int); // format is uninitialized // cppcheck-suppress uninitvar (void)std::snprintf(S,N,Format,i2); // i is uninitialized // cppcheck-suppress uninitvar (void)std::snprintf(s2,N,Format,Int); // no warning is expected for (void)std::snprintf(S,N,Format,Int); } void uninivar_vsnprintf(char *S, size_t N, char *Format, va_list Arg) { char *s1, *s2; size_t n1, n2; char *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vsnprintf(s1,n1,format1,arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(s2,N,Format,Arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(S,n2,Format,Arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(S,N,format2,Arg); // no warning is expected for (void)std::vsnprintf(S,N,Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vsnprintf(S,N,Format,arg); } void uninivar_wscanf(void) { wchar_t *format1, *format2; int i; // cppcheck-suppress uninitvar (void)std::wscanf(format1); // cppcheck-suppress uninitvar (void)std::wscanf(format2,&i); } void uninivar_sscanf(void) { char *string1, *string2; const char * format; int i; // cppcheck-suppress uninitvar (void)std::sscanf(string1,format); // cppcheck-suppress uninitvar (void)std::sscanf(string2,format,&i); } void uninivar_fwscanf(void) { FILE* stream; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)std::fwscanf(stream,format1); // cppcheck-suppress uninitvar (void)std::fwscanf(stream,format2,&i); } void uninivar_swscanf(void) { wchar_t* s; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)std::swscanf(s,format1); // cppcheck-suppress uninitvar (void)std::swscanf(s,format2,&i); } void uninitvar_system(void) { char *c; // cppcheck-suppress uninitvar (void)std::system(c); } void uninitvar_setw(void) { int i; // cppcheck-suppress uninitvar std::cout << std::setw(i); } void uninitvar_setiosflags(void) { std::ios_base::fmtflags mask; // TODO cppcheck-suppress uninitvar std::cout << std::setiosflags(mask); // #6987 - false negative } void uninitvar_resetiosflags(void) { std::ios_base::fmtflags mask; // TODO cppcheck-suppress uninitvar std::cout << std::resetiosflags(mask); // #6987 - false negative } void uninitvar_setfill(void) { char c; // cppcheck-suppress uninitvar std::cout << std::setfill(c); wchar_t wc; // cppcheck-suppress uninitvar std::wcout << std::setfill(wc); } void uninitvar_setprecision(void) { int p; // cppcheck-suppress uninitvar std::cout << std::setprecision(p); } void uninitvar_setbase(void) { int p; // cppcheck-suppress uninitvar std::cout << std::setbase(p); } // cppcheck-suppress passedByValue void uninitvar_find(std::string s) { // testing of size_t find (const string& str, size_t pos = 0) size_t pos1, pos2, pos3, pos4, pos5, pos6, pos7; // cppcheck-suppress uninitvar (void)s.find("find",pos1); // #6991 // testing of size_t find (const char* s, size_t pos = 0) const; char *pc, *pc2; // cppcheck-suppress uninitvar (void)s.find(pc,0); // cppcheck-suppress uninitvar (void)s.find(pc,pos2); // cppcheck-suppress uninitvar (void)s.find("test",pos3); // testing of size_t find (char c, size_t pos = 0) const; char c; // cppcheck-suppress uninitvar (void)s.find(c,pos4); // testing of size_t find (const char* pc, size_t pos, size_t n) const; size_t n1,n2,n3; // cppcheck-suppress uninitvar (void)s.find(pc,pos5,n1); // #6991 // cppcheck-suppress uninitvar (void)s.find("test",pos6,n2); // cppcheck-suppress uninitvar (void)s.find("test",1,n3); // cppcheck-suppress uninitvar (void)s.find("test",pos7,1); // cppcheck-suppress uninitvar (void)s.find(pc2,1,1); } void uninivar_ifstream_read(std::ifstream &f) { int size; char buffer[10]; // cppcheck-suppress uninitvar f.read(buffer, size); } void uninivar_istream_read(std::istream &f) { int size; char buffer[10]; // cppcheck-suppress uninitvar f.read(buffer, size); } void uninitvar_string_compare(std::string &teststr, std::wstring &testwstr) { char *pStrUninit; // cppcheck-suppress uninitvar (void)teststr.compare(pStrUninit); wchar_t *pWStrUninit; // cppcheck-suppress uninitvar (void)testwstr.compare(pWStrUninit); } void invalidFunctionArgBool_abs(bool b, double x, double y) { // cppcheck-suppress invalidFunctionArgBool (void)std::abs(true); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)std::abs(b); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)std::abs(x /////////////////////////////////////////////////////////////////////// #include #include #define pred [] (int i) {return i==0;} void stdalgorithm(const std::list &ints1, const std::list &ints2) { // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::find(ints1.begin(), ints2.end(), 123); // cppcheck-suppress mismatchingContainers if (std::find(ints1.begin(), ints1.end(), 123) == ints2.end()) {} // #9455 std::list::const_iterator uninitItBegin; std::list::const_iterator uninitItEnd; // @todo cppcheck-suppress uninitvar if (std::find(uninitItBegin, uninitItEnd, 123) == uninitItEnd) {} // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::find_if(ints1.begin(), ints2.end(), pred); // cppcheck-suppress mismatchingContainers if (std::find_if(ints1.begin(), ints1.end(), pred) == ints2.end()) {} // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::find_if_not(ints1.begin(), ints2.end(), pred); // cppcheck-suppress mismatchingContainers if (std::find_if_not(ints1.begin(), ints1.end(), pred) == ints2.end()) {} // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::all_of(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::any_of(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::none_of(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::count(ints1.begin(), ints2.end(), 123); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::count_if(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers std::for_each(ints1.begin(), ints2.end(), [](int i) {}); } void getline() { // #837 std::ifstream in("test1.txt"); char cBuf[10]; // cppcheck-suppress bufferAccessOutOfBounds in.getline(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.read(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.readsome(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.get(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.get(cBuf, 100, 'a'); // cppcheck-suppress bufferAccessOutOfBounds in.getline(cBuf, 100, 'a'); in.close(); } void stdstring() { std::string s; // cppcheck-suppress ignoredReturnValue s.size(); // valid s.assign("a"); } void stdvector() { int uninit1, uninit2, uninit3; std::vector v; // cppcheck-suppress ignoredReturnValue v.size(); // cppcheck-suppress ignoredReturnValue v.capacity(); // cppcheck-suppress uselessCallsEmpty // cppcheck-suppress ignoredReturnValue v.empty(); // cppcheck-suppress ignoredReturnValue v.max_size(); // cppcheck-suppress uninitvar v.push_back(uninit1); // cppcheck-suppress uninitvar v.reserve(uninit2); // cppcheck-suppress invalidFunctionArg v.reserve(-1); // no warning is expected for capacity 0 as it simply has no effect v.reserve(0); // cppcheck-suppress uninitvar v.resize(uninit3); // cppcheck-suppress invalidFunctionArg v.resize(-1); v.clear(); v.shrink_to_fit(); // no warning is expected for pop_back() v.push_back(42); v.pop_back(); v.push_back(42); // cppcheck-suppress ignoredReturnValue v.back(); // cppcheck-suppress ignoredReturnValue v.front(); } void stdbind_helper(int a) { printf("%d", a); } void stdbind() { using namespace std::placeholders; // TODO cppcheck-suppress ignoredReturnValue #9369 std::bind(stdbind_helper, 1); // cppcheck-suppress unreadVariable auto f1 = std::bind(stdbind_helper, _1); // cppcheck-suppress unreadVariable auto f2 = std::bind(stdbind_helper, 10); } cppcheck-2.7/test/cfg/windows.cpp000066400000000000000000001007561417746362400171050ustar00rootroot00000000000000 // Test library configuration for windows.cfg // // Usage: // $ cppcheck --check-library --library=windows --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/windows.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include unsigned char * overlappingWriteFunction__mbscat(unsigned char *src, unsigned char *dest) { // No warning shall be shown: (void)_mbscat(dest, src); // cppcheck-suppress overlappingWriteFunction return _mbscat(src, src); } unsigned char * overlappingWriteFunction__memccpy(unsigned char *src, unsigned char *dest, int c, size_t count) { // No warning shall be shown: (void)_memccpy(dest, src, c, count); (void)_memccpy(dest, src, 42, count); // cppcheck-suppress overlappingWriteFunction (void) _memccpy(dest, dest, c, 4); // cppcheck-suppress overlappingWriteFunction return _memccpy(dest, dest+3, c, 4); } unsigned char * overlappingWriteFunction__mbscpy(unsigned char *src, unsigned char *dest) { // No warning shall be shown: (void)_mbscpy(dest, src); // cppcheck-suppress overlappingWriteFunction return _mbscpy(src, src); } void overlappingWriteFunction__swab(char *src, char *dest, int n) { // No warning shall be shown: _swab(dest, src, n); // cppcheck-suppress overlappingWriteFunction _swab(src, src+3, 4); } SYSTEM_INFO uninitvar_GetSystemInfo(char * envstr) { // No warning is expected SYSTEM_INFO SystemInfo; GetSystemInfo(&SystemInfo); return SystemInfo; } void uninitvar__putenv(char * envstr) { // No warning is expected (void)_putenv(envstr); char * p; // cppcheck-suppress uninitvar (void)_putenv(p); } void nullPointer__putenv(char * envstr) { // No warning is expected (void)_putenv(envstr); char * p=NULL; // cppcheck-suppress nullPointer (void)_putenv(p); } void invalidFunctionArg__getcwd(char * buffer) { // Passing NULL as the buffer forces getcwd to allocate // memory for the path, which allows the code to support file paths // longer than _MAX_PATH, which are supported by NTFS. if ((buffer = _getcwd(NULL, 0)) == NULL) { return; } free(buffer); } // DWORD GetPrivateProfileString( // [in] LPCTSTR lpAppName, // [in] LPCTSTR lpKeyName, // [in] LPCTSTR lpDefault, // [out] LPTSTR lpReturnedString, // [in] DWORD nSize, // [in] LPCTSTR lpFileName) void nullPointer_GetPrivateProfileString(LPCTSTR lpAppName, LPCTSTR lpKeyName, LPCTSTR lpDefault, LPTSTR lpReturnedString, DWORD nSize, LPCTSTR lpFileName) { // No warning is expected (void)GetPrivateProfileString(lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize, lpFileName); // No warning is expected for 1st arg as nullptr (void)GetPrivateProfileString(nullptr, lpKeyName, lpDefault, lpReturnedString, nSize, lpFileName); // No warning is expected for 2nd arg as nullptr (void)GetPrivateProfileString(lpAppName, nullptr, lpDefault, lpReturnedString, nSize, lpFileName); } void nullPointer__get_timezone(long *sec) { // No warning is expected (void)_get_timezone(sec); long *pSec = NULL; // cppcheck-suppress nullPointer (void)_get_timezone(pSec); } void nullPointer__get_daylight(int *h) { // No warning is expected (void)_get_daylight(h); int *pHours = NULL; // cppcheck-suppress nullPointer (void)_get_daylight(pHours); } void validCode() { DWORD dwordInit = 0; WORD wordInit = 0; BYTE byteInit = 0; // Valid Semaphore usage, no leaks, valid arguments HANDLE hSemaphore1; hSemaphore1 = CreateSemaphore(NULL, 0, 1, NULL); CloseHandle(hSemaphore1); HANDLE hSemaphore2; hSemaphore2 = CreateSemaphoreEx(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); CloseHandle(hSemaphore2); HANDLE hSemaphore3; hSemaphore3 = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, "sem"); CloseHandle(hSemaphore3); // Valid lstrcat usage, but with warning because it is deprecated char buf[30] = "hello world"; // cppcheck-suppress lstrcatCalled lstrcat(buf, "test"); // cppcheck-suppress strlwrCalled strlwr(buf); // cppcheck-suppress struprCalled strupr(buf); // Valid Mutex usage, no leaks, valid arguments HANDLE hMutex1; hMutex1 = CreateMutex(NULL, TRUE, NULL); if (hMutex1) { ReleaseMutex(hMutex); } CloseHandle(hMutex1); HANDLE hMutex2; hMutex2 = CreateMutexEx(NULL, NULL, 0, MUTEX_ALL_ACCESS); CloseHandle(hMutex2); HANDLE hMutex3; hMutex3 = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "sem"); CloseHandle(hMutex3); // Valid Module usage, no leaks, valid arguments HMODULE hModule = GetModuleHandle(L"My.dll"); FreeLibrary(hModule); hModule = GetModuleHandle(TEXT("somedll")); FreeLibrary(hModule); hModule = GetModuleHandle(NULL); FreeLibrary(hModule); // Valid Event usage, no leaks, valid arguments HANDLE event; event = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL != event) { SetEvent(event); CloseHandle(event); } event = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"testevent"); if (NULL != event) { PulseEvent(event); SetEvent(event); CloseHandle(event); } event = CreateEventEx(NULL, L"testevent3", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); if (NULL != event) { ResetEvent(event); CloseHandle(event); } void *pMem1 = _malloca(1); _freea(pMem1); // Memory from _alloca must not be freed void *pMem2 = _alloca(10); memset(pMem2, 0, 10); SYSTEMTIME st; GetSystemTime(&st); DWORD lastError = GetLastError(); SetLastError(lastError); PSID pEveryoneSID = NULL; SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID); FreeSid(pEveryoneSID); LPVOID pMem = HeapAlloc(GetProcessHeap(), 0, 10); pMem = HeapReAlloc(GetProcessHeap(), 0, pMem, 0); HeapFree(GetProcessHeap(), 0, pMem); char bufC[50]; sprintf_s(bufC, "Hello"); printf("%s", bufC); sprintf_s(bufC, "%s", "test"); printf("%s", bufC); sprintf_s(bufC, _countof(bufC), "%s", "test"); printf("%s", bufC); wchar_t bufWC[50]; swprintf_s(bufWC, L"Hello"); wprintf(L"%s\n", bufWC); swprintf_s(bufWC, L"%s %d", L"swprintf_s", 3); wprintf(L"%s\n", bufWC); swprintf_s(bufWC, _countof(bufWC), L"%s %d", L"swprintf_s", 6); wprintf(L"%s\n", bufWC); TCHAR bufTC[50]; _stprintf(bufTC, TEXT("Hello")); _tprintf(TEXT("%s"), bufTC); _stprintf(bufTC, TEXT("%d"), 1); _tprintf(TEXT("%s"), bufTC); _stprintf(bufTC, _countof(bufTC), TEXT("%d"), 2); _tprintf(TEXT("%s"), bufTC); GetUserName(NULL, &dwordInit); dwordInit = 10; GetUserName(bufTC, _countof(bufTC)); WSADATA wsaData = {0}; WSAStartup(2, &wsaData); SOCKET sock = socket(1, 2, 3); u_long ulongInit = 0; ioctlsocket(sock, FIONBIO, &ulongInit); if (sock != INVALID_SOCKET) { closesocket(sock); } WSACleanup(); wordInit = MAKEWORD(1, 2); // cppcheck-suppress redundantAssignment dwordInit = MAKELONG(1, 2); // cppcheck-suppress redundantAssignment wordInit = LOWORD(dwordInit); byteInit = LOBYTE(wordInit); wordInit = HIWORD(dwordInit); // cppcheck-suppress redundantAssignment byteInit = HIBYTE(wordInit); if (byteInit) {} bool boolVar; uint8_t byteBuf[5] = {0}; uint8_t byteBuf2[10] = {0}; boolVar = RtlEqualMemory(byteBuf, byteBuf2, sizeof(byteBuf)); if (boolVar) {} boolVar = RtlCompareMemory(byteBuf, byteBuf2, sizeof(byteBuf)); if (boolVar) {} RtlMoveMemory(byteBuf, byteBuf2, sizeof(byteBuf)); RtlCopyMemory(byteBuf, byteBuf2, sizeof(byteBuf)); RtlZeroMemory(byteBuf, sizeof(byteBuf)); ZeroMemory(byteBuf, sizeof(byteBuf)); RtlSecureZeroMemory(byteBuf, sizeof(byteBuf)); SecureZeroMemory(byteBuf, sizeof(byteBuf)); RtlFillMemory(byteBuf, sizeof(byteBuf), 0xff); // cppcheck-suppress LocalAllocCalled HLOCAL pLocalAlloc = LocalAlloc(1, 2); LocalFree(pLocalAlloc); // cppcheck-suppress lstrlenCalled (void)lstrlen(bufTC); // cppcheck-suppress lstrlenCalled (void)lstrlen(NULL); // Intrinsics __noop(); __noop(1, "test", NULL); __nop(); void * pAlloc1 = _aligned_malloc(100, 2); _aligned_free(pAlloc1); ::PostMessage(nullptr, WM_QUIT, 0, 0); printf("%zu", __alignof(int)); printf("%zu", _alignof(double)); // Valid Library usage, no leaks, valid arguments HINSTANCE hInstLib = LoadLibrary(L"My.dll"); FreeLibrary(hInstLib); hInstLib = LoadLibraryA("My.dll"); FreeLibrary(hInstLib); hInstLib = LoadLibraryEx(L"My.dll", NULL, 0); FreeLibrary(hInstLib); hInstLib = LoadLibraryExW(L"My.dll", NULL, 0); FreeLibrary(hInstLib); hInstLib = ::LoadLibrary(L"My.dll"); FreeLibraryAndExitThread(hInstLib, 0); // Does not return! Must be at the end! } void bufferAccessOutOfBounds() { wchar_t buf[10]; // Verifying _countof macro configuration // Valid loop over array for (size_t i = 0; i < _countof(buf); ++i) { buf[i] = L'\0'; } // Wrong loop over array accessing one element past the end for (size_t i = 0; i <= _countof(buf); ++i) { // cppcheck-suppress arrayIndexOutOfBounds buf[i] = L'\0'; } uint8_t byteBuf[5] = {0}; uint8_t byteBuf2[10] = {0}; // cppcheck-suppress ignoredReturnValue // cppcheck-suppress bufferAccessOutOfBounds RtlEqualMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress bufferAccessOutOfBounds RtlCompareMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress bufferAccessOutOfBounds RtlMoveMemory(byteBuf, byteBuf2, 20); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds MoveMemory(byteBuf, byteBuf2, 20); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds RtlCopyMemory(byteBuf, byteBuf2, 20); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds CopyMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress bufferAccessOutOfBounds RtlZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds ZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds RtlSecureZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds SecureZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds RtlFillMemory(byteBuf, sizeof(byteBuf)+1, 0x01); // cppcheck-suppress bufferAccessOutOfBounds FillMemory(byteBuf, sizeof(byteBuf)+1, 0x01); char * pAlloc1 = _malloca(32); memset(pAlloc1, 0, 32); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc1, 0, 33); _freea(pAlloc1); } void mismatchAllocDealloc() { char * pChar = _aligned_malloc(100, 2); // cppcheck-suppress mismatchAllocDealloc free(pChar); pChar = _malloca(32); // cppcheck-suppress mismatchAllocDealloc _aligned_free(pChar); } void nullPointer() { HANDLE hSemaphore; // cppcheck-suppress nullPointer hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, NULL); CloseHandle(hSemaphore); // cppcheck-suppress lstrcatCalled // cppcheck-suppress nullPointer lstrcat(NULL, "test"); char buf[10] = "\0"; // cppcheck-suppress lstrcatCalled // cppcheck-suppress nullPointer lstrcat(buf, NULL); HANDLE hMutex; // cppcheck-suppress nullPointer hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, NULL); CloseHandle(hMutex); //Incorrect: 1. parameter, must not be null // cppcheck-suppress nullPointer FARPROC pAddr = GetProcAddress(NULL, "name"); (void)pAddr; HMODULE * phModule = NULL; // cppcheck-suppress nullPointer GetModuleHandleEx(0, NULL, phModule); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer OpenEvent(EVENT_ALL_ACCESS, FALSE, NULL); HANDLE hEvent = NULL; // cppcheck-suppress nullPointer PulseEvent(hEvent); // cppcheck-suppress nullPointer ResetEvent(hEvent); // cppcheck-suppress nullPointer SetEvent(hEvent); char *str = NULL; // cppcheck-suppress strlwrCalled // cppcheck-suppress nullPointer strlwr(str); // cppcheck-suppress struprCalled // cppcheck-suppress nullPointer strupr(str); // cppcheck-suppress nullPointer GetSystemTime(NULL); // cppcheck-suppress nullPointer GetLocalTime(NULL); // TODO: error message: arg1 must not be nullptr if variable pointed to by arg2 is not 0 DWORD dwordInit = 10; GetUserName(NULL, &dwordInit); TCHAR bufTC[10]; // cppcheck-suppress nullPointer GetUserName(bufTC, NULL); SOCKET socketInit = {0}; sockaddr sockaddrUninit; int intInit = 0; int *pIntNull = NULL; char charArray[] = "test"; // cppcheck-suppress nullPointer WSAStartup(1, NULL); // cppcheck-suppress nullPointer bind(socketInit, NULL, 5); // cppcheck-suppress nullPointer getpeername(socketInit, NULL, &intInit); // cppcheck-suppress nullPointer getpeername(socketInit, &sockaddrUninit, pIntNull); // cppcheck-suppress nullPointer getsockopt(sockInit, 1, 2, NULL, &intInit); // cppcheck-suppress nullPointer getsockopt(sockInit, 1, 2, charArray, pIntNull); } void memleak_malloca() { // cppcheck-suppress unusedAllocatedMemory // cppcheck-suppress unreadVariable void *pMem = _malloca(10); // cppcheck-suppress memleak } void memleak_AllocateAndInitializeSid() { PSID pEveryoneSID = NULL; SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID) // TODO: enable when #6994 is implemented cppcheck-suppress memleak } void memleak_HeapAlloc() { LPVOID pMem; pMem = HeapAlloc(GetProcessHeap(), 0, 10); HeapValidate(GetProcessHeap(), 0, pMem); // cppcheck-suppress unreadVariable SIZE_T memSize = HeapSize(GetProcessHeap(), 0, pMem); // cppcheck-suppress memleak } void memleak_LocalAlloc() { LPTSTR pszBuf; // cppcheck-suppress LocalAllocCalled pszBuf = (LPTSTR)LocalAlloc(LPTR, MAX_PATH*sizeof(TCHAR)); (void)LocalSize(pszBuf); (void)LocalFlags(pszBuf); LocalLock(pszBuf); LocalUnlock(pszBuf); // cppcheck-suppress memleak } void resourceLeak_CreateSemaphoreA() { HANDLE hSemaphore; // cppcheck-suppress unreadVariable hSemaphore = CreateSemaphoreA(NULL, 0, 1, "sem1"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateSemaphoreEx() { HANDLE hSemaphore; // cppcheck-suppress unreadVariable hSemaphore = CreateSemaphoreEx(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); // cppcheck-suppress resourceLeak } void resourceLeak_OpenSemaphore() { HANDLE hSemaphore; // cppcheck-suppress unreadVariable hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateMutexA() { HANDLE hMutex; // cppcheck-suppress unreadVariable hMutex = CreateMutexA(NULL, TRUE, "sem1"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateMutexEx() { HANDLE hMutex; // cppcheck-suppress unreadVariable hMutex = CreateMutexEx(NULL, "sem", 0, MUTEX_ALL_ACCESS); // cppcheck-suppress resourceLeak } void resourceLeak_OpenMutex() { HANDLE hMutex; // cppcheck-suppress unreadVariable hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress resourceLeak } void resourceLeak_LoadLibrary() { HINSTANCE hInstLib; hInstLib = ::LoadLibrary(L"My.dll"); typedef BOOL (WINAPI *fpFunc)(); // cppcheck-suppress unreadVariable fpFunc pFunc = GetProcAddress(hInstLib, "name"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateEvent() { HANDLE hEvent; hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); SetEvent(hEvent); // cppcheck-suppress resourceLeak } void resourceLeak_CreateEventExA() { HANDLE hEvent; // cppcheck-suppress unreadVariable hEvent = CreateEventExA(NULL, "test", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); // cppcheck-suppress resourceLeak } void resourceLeak_OpenEventW() { HANDLE hEvent; // cppcheck-suppress unreadVariable hEvent = OpenEventW(EVENT_ALL_ACCESS, TRUE, L"testevent"); // cppcheck-suppress resourceLeak } void resourceLeak_socket() { SOCKET sock; // cppcheck-suppress unreadVariable sock = socket(1, 2, 3); // cppcheck-suppress resourceLeak } void ignoredReturnValue() { // cppcheck-suppress leakReturnValNotUsed CreateSemaphoreW(NULL, 0, 1, NULL); // cppcheck-suppress leakReturnValNotUsed CreateSemaphoreExA(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); // cppcheck-suppress leakReturnValNotUsed OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress leakReturnValNotUsed CreateMutexW(NULL, FALSE, NULL); // cppcheck-suppress leakReturnValNotUsed CreateMutexExA(NULL, NULL, 1, MUTEX_ALL_ACCESS); // cppcheck-suppress leakReturnValNotUsed OpenMutexA(MUTEX_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress leakReturnValNotUsed LoadLibrary(L"My.dll"); // cppcheck-suppress leakReturnValNotUsed LoadLibraryEx(L"My.dll", NULL, 0); HINSTANCE hInstLib = LoadLibrary(L"My.dll"); // cppcheck-suppress ignoredReturnValue GetProcAddress(hInstLib, "name"); FreeLibrary(hInstLib); // cppcheck-suppress leakReturnValNotUsed CreateEvent(NULL, FALSE, FALSE, NULL); // cppcheck-suppress leakReturnValNotUsed OpenEvent(EVENT_ALL_ACCESS, FALSE, L"testevent"); // cppcheck-suppress leakReturnValNotUsed CreateEventEx(NULL, L"test", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed _malloca(10); // cppcheck-suppress ignoredReturnValue _alloca(5); // cppcheck-suppress ignoredReturnValue GetLastError(); // cppcheck-suppress ignoredReturnValue GetProcessHeap(); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed HeapAlloc(GetProcessHeap(), 0, 10); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed HeapReAlloc(GetProcessHeap(), 0, 1, 0); // cppcheck-suppress leakReturnValNotUsed socket(1, 2, 3); // cppcheck-suppress ignoredReturnValue _fileno(stdio); // cppcheck-suppress lstrlenCalled // cppcheck-suppress ignoredReturnValue lstrlen(TEXT("test")); } void invalidFunctionArg() { HANDLE hSemaphore; // cppcheck-suppress invalidFunctionArg hSemaphore = CreateSemaphore(NULL, 0, 0, NULL); CloseHandle(hSemaphore); // cppcheck-suppress invalidFunctionArgBool hSemaphore = CreateSemaphore(NULL, 0, 1, true); CloseHandle(hSemaphore); // cppcheck-suppress invalidFunctionArg hSemaphore = CreateSemaphoreEx(NULL, 0, 0, NULL, 0, SEMAPHORE_ALL_ACCESS); CloseHandle(hSemaphore); // cppcheck-suppress invalidFunctionArg hSemaphore = CreateSemaphoreEx(NULL, 0, 1, NULL, 1, SEMAPHORE_ALL_ACCESS); CloseHandle(hSemaphore); HANDLE hMutex; // cppcheck-suppress invalidFunctionArgBool hMutex = CreateMutex(NULL, TRUE, false); CloseHandle(hMutex); // cppcheck-suppress invalidFunctionArgBool hMutex = CreateMutex(NULL, FALSE, true); CloseHandle(hMutex); // cppcheck-suppress invalidFunctionArg hMutex = CreateMutexEx(NULL, NULL, 3, MUTEX_ALL_ACCESS); CloseHandle(hMutex); //Incorrect: 2. parameter to LoadLibraryEx() must be NULL // cppcheck-suppress invalidFunctionArg HINSTANCE hInstLib = LoadLibraryEx(L"My.dll", 1, 0); FreeLibrary(hInstLib); // cppcheck-suppress invalidFunctionArg void *pMem = _malloca(-1); _freea(pMem); // FIXME cppcheck-suppress unreadVariable // cppcheck-suppress invalidFunctionArg pMem = _alloca(-5); } void uninitvar() { HANDLE hSemaphore; // cppcheck-suppress uninitvar CloseHandle(hSemaphore); char buf[10]; // cppcheck-suppress lstrcatCalled // cppcheck-suppress uninitvar lstrcat(buf, "test"); buf[0] = '\0'; char buf2[2]; // cppcheck-suppress lstrcatCalled // cppcheck-suppress uninitvar lstrcat(buf, buf2); HANDLE hMutex1, hMutex2; // cppcheck-suppress uninitvar ReleaseMutex(hMutex1); // cppcheck-suppress uninitvar CloseHandle(hMutex2); HANDLE hEvent1, hEvent2, hEvent3, hEvent4; // cppcheck-suppress uninitvar PulseEvent(hEvent1); // cppcheck-suppress uninitvar ResetEvent(hEvent2); // cppcheck-suppress uninitvar SetEvent(hEvent3); // cppcheck-suppress uninitvar CloseHandle(hEvent4); char buf_uninit1[10]; char buf_uninit2[10]; // cppcheck-suppress strlwrCalled // cppcheck-suppress uninitvar strlwr(buf_uninit1); // cppcheck-suppress struprCalled // cppcheck-suppress uninitvar strupr(buf_uninit2); DWORD dwordUninit; // cppcheck-suppress uninitvar SetLastError(dwordUninit); DWORD dwordUninit; // cppcheck-suppress uninitvar GetUserName(NULL, &dwordUninit); FILE *pFileUninit; // cppcheck-suppress uninitvar // cppcheck-suppress ignoredReturnValue _fileno(pFileUninit); } void errorPrintf() { char bufC[50]; // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, _countof(bufC), "%s %d", "sprintf_s"); printf("%s\n", bufC); // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, "%s %d", "sprintf_s"); printf("%s\n", bufC); // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, _countof(bufC), "test", 0); printf("%s\n", bufC); // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, "test", "sprintf_s"); printf("%s\n", bufC); // cppcheck-suppress invalidPrintfArgType_s sprintf_s(bufC, _countof(bufC), "%s", 1); printf("%s\n", bufC); // cppcheck-suppress invalidPrintfArgType_s sprintf_s(bufC, "%s", 1); printf("%s\n", bufC); wchar_t bufWC[50]; // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, _countof(bufWC), L"%s %d", L"swprintf_s"); wprintf(L"%s\n", bufWC); // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, L"%s %d", L"swprintf_s"); wprintf(L"%s\n", bufWC); // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, _countof(bufWC), L"test", 0); wprintf(L"%s\n", bufWC); // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, L"test", L"swprintf_s"); wprintf(L"%s\n", bufWC); // cppcheck-suppress invalidPrintfArgType_s swprintf_s(bufWC, _countof(bufWC), L"%s", 1); wprintf(L"%s\n", bufWC); // cppcheck-suppress invalidPrintfArgType_s swprintf_s(bufWC, L"%s", 1); wprintf(L"%s\n", bufWC); TCHAR bufTC[50]; // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, _countof(bufTC), TEXT("%s %d"), TEXT("_stprintf_s")); _tprintf(L"%s\n", bufTC); // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, TEXT("%s %d"), TEXT("_stprintf_s")); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, _countof(bufTC), TEXT("test"), 0); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, TEXT("test"), TEXT("_stprintf_s")); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress invalidPrintfArgType_s _stprintf_s(bufTC, _countof(bufTC), TEXT("%s"), 1); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress invalidPrintfArgType_s _stprintf_s(bufTC, TEXT("%s"), 1); _tprintf(TEXT("%s\n"), bufTC); } void allocDealloc_GetModuleHandleEx() { // For GetModuleHandleEx it depends on the first argument if FreeLibrary // must be called or is not allowed to be called. // If the first argument is GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT // (0x00000002), a call to FreeLibrary is not allowed, otherwise it is // necessary. // Since this is not possible to configure at the moment cppcheck should // accept not calling FreeLibrary and also calling it for the handle. // TODO: Enhance cppcheck to conditionally check for alloc/dealloc issues. // No warning because of correct FreeLibrary on 'hModule' should be issued. HMODULE hModule1; if (GetModuleHandleEx(0, NULL, &hModule1)) { FreeLibrary(hModule1); } //This is a false negative, but it is not detected to avoid false positives. HMODULE hModule2; GetModuleHandleEx(0, NULL, &hModule2); } void uninitvar_tolower(_locale_t l) { int c1, c2; // cppcheck-suppress uninitvar (void)_tolower(c1); // cppcheck-suppress uninitvar (void)_tolower_l(c2, l); } void uninitvar_toupper(_locale_t l) { int c1, c2; // cppcheck-suppress uninitvar (void)_toupper(c1); // cppcheck-suppress uninitvar (void)_toupper_l(c2, l); } void uninitvar_towlower(_locale_t l) { wint_t i; // cppcheck-suppress uninitvar (void)_towlower_l(i, l); } void uninitvar_towupper(_locale_t l) { wint_t i; // cppcheck-suppress uninitvar (void)_towupper_l(i, l); } void oppositeInnerCondition_SUCCEEDED_FAILED(HRESULT hr) { if (SUCCEEDED(hr)) { // TODO ticket #8596 cppcheck-suppress oppositeInnerCondition if (FAILED(hr)) {} } } /*HANDLE WINAPI CreateThread( _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_opt_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_opt_ LPDWORD lpThreadId );*/ HANDLE test_CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) { // Create uninitialized variables LPSECURITY_ATTRIBUTES uninit_lpThreadAttributes; SIZE_T uninit_dwStackSize; LPTHREAD_START_ROUTINE uninit_lpStartAddress; LPVOID uninit_lpParameter; DWORD uninit_dwCreationFlags; // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar (void) CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, uninit_dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar (void) CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, uninit_lpParameter, dwCreationFlags, lpThreadId); // @todo uninitvar shall be reported // cppcheck-suppress leakReturnValNotUsed (void) CreateThread(lpThreadAttributes, dwStackSize, uninit_lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar (void) CreateThread(lpThreadAttributes, uninit_dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // @todo uninitvar shall be reported // cppcheck-suppress leakReturnValNotUsed (void) CreateThread(uninit_lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer (void) CreateThread(lpThreadAttributes, dwStackSize, 0, lpParameter, dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress invalidFunctionArg (void) CreateThread(lpThreadAttributes, -1, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // no warning shall be shown for return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); } // unsigned char *_mbscat(unsigned char *strDestination, const unsigned char *strSource); unsigned char * uninitvar_mbscat(unsigned char *strDestination, const unsigned char *strSource) { unsigned char *uninit_deststr; unsigned char *uninit_srcstr1, *uninit_srcstr2; // cppcheck-suppress uninitvar (void)_mbscat(uninit_deststr,uninit_srcstr1); // cppcheck-suppress uninitvar (void)_mbscat(strDestination,uninit_srcstr2); // no warning shall be shown for return _mbscat(strDestination,strSource); } // unsigned char *_mbscat(unsigned char *strDestination, const unsigned char *strSource); unsigned char * nullPointer_mbscat(unsigned char *strDestination, const unsigned char *strSource) { // cppcheck-suppress nullPointer (void)_mbscat(0,strSource); // cppcheck-suppress nullPointer (void)_mbscat(strDestination,0); // no warning shall be shown for return _mbscat(strDestination,strSource); } // errno_t _mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource ); error_t uninitvar_mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource) { unsigned char *uninit_strDestination; size_t uninit_numberOfElements; unsigned char *uninit_strSource; // cppcheck-suppress uninitvar (void)_mbscat_s(uninit_strDestination, numberOfElements, strSource); // cppcheck-suppress uninitvar (void)_mbscat_s(strDestination, uninit_numberOfElements, strSource); // cppcheck-suppress uninitvar (void)_mbscat_s(strDestination, numberOfElements, uninit_strSource); // no warning shall be shown for return _mbscat_s(strDestination, numberOfElements, strSource); } // errno_t _mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource ); error_t nullPointer_mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource) { // cppcheck-suppress nullPointer (void)_mbscat_s(0, numberOfElements, strSource); // cppcheck-suppress nullPointer (void)_mbscat_s(strDestination, numberOfElements, 0); // no warning shall be shown for return _mbscat_s(strDestination, numberOfElements, strSource); } // errno_t _strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale); error_t uninitvar__strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale) { size_t uninit_numberOfElements; const char *uninit_strSource; size_t uninit_count; _locale_t uninit_locale; // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, uninit_numberOfElements, strSource, count, locale); // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, numberOfElements, uninit_strSource, count, locale); // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, numberOfElements, strSource, uninit_count, locale); // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, numberOfElements, strSource, count, uninit_locale); // no warning shall be shown for return _strncpy_s_l(strDest, numberOfElements, strSource, count, locale); } // errno_t _strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale); error_t nullPointer__strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale) { // cppcheck-suppress nullPointer (void)_strncpy_s_l(0, numberOfElements, strSource, count, locale); // cppcheck-suppress nullPointer (void)_strncpy_s_l(strDest, numberOfElements, 0, count, locale); // no warning shall be shown for return _strncpy_s_l(strDest, numberOfElements, strSource, count, locale); } void GetShortPathName_validCode(TCHAR* lpszPath) { long length = GetShortPathName(lpszPath, NULL, 0); if (length == 0) { _tprintf(TEXT("error")); return; } TCHAR* buffer = new TCHAR[length]; length = GetShortPathName(lpszPath, buffer, length); if (length == 0) { delete[] buffer; _tprintf(TEXT("error")); return; } _tprintf(TEXT("long name = %s short name = %s"), lpszPath, buffer); delete[] buffer; } class MyClass : public CObject { DECLARE_DYNAMIC(MyClass) DECLARE_DYNCREATE(MyClass) DECLARE_SERIAL(MyClass) public: MyClass() {} }; IMPLEMENT_DYNAMIC(MyClass, CObject) IMPLEMENT_DYNCREATE(MyClass, CObject) IMPLEMENT_SERIAL(MyClass,CObject, 42) cppcheck-2.7/test/cfg/wxwidgets.cpp000066400000000000000000000252431417746362400174350ustar00rootroot00000000000000 // Test library configuration for wxwidgets.cfg // // Usage: // $ ./cppcheck --check-library --enable=information --enable=style --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr '--template="{file}:{line}:{severity}:{id}:{message}"' --inconclusive --library=wxwidgets -f test/cfg/wxwidgets.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __VISUALC__ // Ensure no duplicateBreak warning is issued after wxLogApiError() calls. // This function does not terminate execution. bool duplicateBreak_wxLogApiError(const wxString &msg, const HRESULT &hr, wxString &str) { if (hr) { wxLogApiError(msg,hr); str = "fail"; return false; } return true; } #endif void useRetval_wxString_MakeCapitalized(wxString &str) { // No warning is expected for str.MakeCapitalized(); } void useRetval_wxString_MakeLower(wxString &str) { // No warning is expected for str.MakeLower(); } void useRetval_wxString_MakeUpper(wxString &str) { // No warning is expected for str.MakeUpper(); } wxString containerOutOfBounds_wxArrayString(void) { wxArrayString a; a.Add("42"); a.Clear(); // TODO: wxArrayString is defined to be a vector // TODO: cppcheck-suppress containerOutOfBounds return a[0]; } int containerOutOfBounds_wxArrayInt(void) { wxArrayInt a; a.Add(42); a.Clear(); // TODO: wxArrayString is defined to be a vector // TODO: cppcheck-suppress containerOutOfBounds return a[0]; } void ignoredReturnValue_wxDC_GetSize(const wxDC &dc, wxCoord *width, wxCoord *height) { // No warning is expected for dc.GetSize(width, height); // No warning is expected for (void)dc.GetSize(); } void ignoredReturnValue_wxDC_GetSizeMM(const wxDC &dc, wxCoord *width, wxCoord *height) { // No warning is expected for dc.GetSizeMM(width, height); // Now warning is expected for (void)dc.GetSizeMM(); } wxSizerItem* invalidFunctionArgBool_wxSizer_Add(wxSizer *sizer, wxWindow * window, const wxSizerFlags &flags) { // No warning is expected for return sizer->Add(window,flags); } bool invalidFunctionArgBool_wxPGProperty_Hide(wxPGProperty *pg, bool hide, int flags) { // cppcheck-suppress invalidFunctionArgBool (void)pg->Hide(hide, true); // No warning is expected for return pg->Hide(hide, flags); } wxTextCtrlHitTestResult nullPointer_wxTextCtrl_HitTest(const wxTextCtrl& txtCtrl, const wxPoint& pos) { // no nullPointer-warning is expected return txtCtrl.HitTest(pos, NULL); } void validCode() { wxString str = wxGetCwd(); (void)str; wxLogGeneric(wxLOG_Message, "test %d", 0); wxLogMessage("test %s", "str"); wxString translation1 = _("text"); wxString translation2 = wxGetTranslation("text"); wxString translation3 = wxGetTranslation("string", "domain"); (void)translation1; (void)translation2; (void)translation3; } #if wxUSE_GUI==1 void validGuiCode() { #if wxUSE_SPINCTRL==1 extern wxSpinCtrl spinCtrlInstance; spinCtrlInstance.SetBase(10); spinCtrlInstance.SetBase(16); #endif } #endif void nullPointer(const wxString &str) { // cppcheck-suppress nullPointer wxLogGeneric(wxLOG_Message, (char*)NULL); // cppcheck-suppress nullPointer wxLogMessage((char*)NULL); double *doublePtr = NULL; // cppcheck-suppress nullPointer (void)str.ToDouble(doublePtr); double *doublePtr1 = NULL; // cppcheck-suppress nullPointer (void)str.ToCDouble(doublePtr1); long * longPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToLong(longPtr); long * longPtr1 = NULL; // cppcheck-suppress nullPointer (void)str.ToCLong(longPtr1); unsigned long * ulongPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToULong(ulongPtr); unsigned long * ulongPtr1 = NULL; // cppcheck-suppress nullPointer (void)str.ToCULong(ulongPtr1); long long * longLongPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToLongLong(longLongPtr); unsigned long long * ulongLongPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToULongLong(ulongLongPtr); } void nullPointer_wxSizer_Add(wxSizer &sizer, wxWindow *w) { wxWindow * const ptr = 0; // @todo cppcheck-suppress nullPointer sizer.Add(ptr); // No warning shall be issued for sizer.Add(w); } void uninitvar_wxSizer_Add(wxSizer &sizer, wxWindow *w,wxObject* userData) { int uninit1, uninit2, uninit3; // cppcheck-suppress uninitvar sizer.Add(w,uninit1); // cppcheck-suppress uninitvar sizer.Add(w,4,uninit2); // cppcheck-suppress uninitvar sizer.Add(w,4,2,uninit3,userData); } void ignoredReturnValue(const wxString &s) { // cppcheck-suppress ignoredReturnValue wxGetCwd(); // cppcheck-suppress ignoredReturnValue wxAtoi(s); // cppcheck-suppress ignoredReturnValue wxAtol(s); // cppcheck-suppress ignoredReturnValue wxAtof(s); } void invalidFunctionArg(const wxString &str) { #if wxUSE_SPINCTRL==1 extern wxSpinCtrl spinCtrlInstance; // cppcheck-suppress invalidFunctionArg spinCtrlInstance.SetBase(0); // cppcheck-suppress invalidFunctionArg spinCtrlInstance.SetBase(5); #endif long l; // cppcheck-suppress invalidFunctionArg (void)str.ToLong(&l, -1); // cppcheck-suppress invalidFunctionArg (void)str.ToLong(&l, 1); // cppcheck-suppress invalidFunctionArg (void)str.ToLong(&l, 37); } void uninitvar(wxWindow &w) { wxLogLevel logLevelUninit; char cBufUninit[10]; char *pcUninit; bool uninitBool; // cppcheck-suppress uninitvar wxLogGeneric(logLevelUninit, "test"); // cppcheck-suppress uninitvar wxLogMessage(cBufUninit); // cppcheck-suppress uninitvar wxLogMessage(pcUninit); // cppcheck-suppress uninitvar w.Close(uninitBool); } void uninitvar_wxStaticText(wxStaticText &s) { // no warning s.Wrap(-1); int uninitInt; // cppcheck-suppress uninitvar s.Wrap(uninitInt); } void uninitvar_wxString_NumberConversion(const wxString &str, const int numberBase) { int uninitInteger1; int uninitInteger2; int uninitInteger3; int uninitInteger4; int uninitInteger5; int uninitInteger6; long l; long long ll; unsigned long ul; unsigned long long ull; // cppcheck-suppress uninitvar (void)str.ToLong(&l, uninitInteger1); // cppcheck-suppress uninitvar (void)str.ToLongLong(&ll, uninitInteger2); // cppcheck-suppress uninitvar (void)str.ToULong(&ul, uninitInteger3); // cppcheck-suppress uninitvar (void)str.ToULongLong(&ull, uninitInteger4); // cppcheck-suppress uninitvar (void)str.ToCLong(&l, uninitInteger5); // cppcheck-suppress uninitvar (void)str.ToCULong(&ul, uninitInteger6); } void uninitvar_SetMenuBar(wxFrame * const framePtr, wxMenuBar * const menuBarPtr) { wxMenuBar *menuBar; // cppcheck-suppress uninitvar framePtr->SetMenuBar(menuBar); framePtr->SetMenuBar(menuBarPtr); } void uninitvar_wxMenuBarAppend(wxMenuBar * const menuBarPtr, wxMenu * const menuPtr, const wxString &title) { wxMenu *menu; // cppcheck-suppress uninitvar menuBarPtr->Append(menu, title); menuBarPtr->Append(menuPtr, title); } void deprecatedFunctions_wxDataViewCustomRenderer(wxDataViewCustomRenderer &dataViewCustomRenderer, wxPoint cursor, wxRect cell, wxDataViewModel *model, const wxDataViewItem &item, unsigned int col) { // cppcheck-suppress ActivateCalled dataViewCustomRenderer.Activate(cell, model, item, col); // cppcheck-suppress LeftClickCalled dataViewCustomRenderer.LeftClick(cursor, cell, model, item, col); } void deprecatedFunctions(wxApp &a, const wxString &s, wxArtProvider *artProvider, wxCalendarCtrl &calenderCtrl, wxComboCtrl &comboCtrl, wxChar * path) { #ifdef __WXOSX__ // cppcheck-suppress MacOpenFileCalled a.MacOpenFile(s); #endif #if wxCHECK_VERSION(3, 1, 0) // wxWidets-3.1.0 or higher: // Some functions are not available anymore in newer versions // @todo cppcheck-suppress ShowPopupCalled comboCtrl.ShowPopup(); #else // cppcheck-suppress InsertCalled wxArtProvider::Insert(artProvider); // cppcheck-suppress GetTextIndentCalled // cppcheck-suppress ignoredReturnValue comboCtrl.GetTextIndent(); // cppcheck-suppress HidePopupCalled comboCtrl.HidePopup(true); // cppcheck-suppress HidePopupCalled comboCtrl.HidePopup(false); // cppcheck-suppress HidePopupCalled comboCtrl.HidePopup(/*default=false*/); // cppcheck-suppress SetTextIndentCalled comboCtrl.SetTextIndent(0); #if wxUSE_DEBUG_CONTEXT==1 // cppcheck-suppress GetLevelCalled // cppcheck-suppress ignoredReturnValue wxDebugContext::GetLevel(); // cppcheck-suppress SetLevelCalled wxDebugContext::SetLevel(42); #endif // cppcheck-suppress wxDos2UnixFilenameCalled wxDos2UnixFilename(path); // cppcheck-suppress wxFileNameFromPathCalled // cppcheck-suppress ignoredReturnValue wxFileNameFromPath(wxT_2("../test.c")); #endif #if defined(__WXMSW__) || defined(__WXGTK__) // EnableYearChange() is not available on these GUI systems #else // cppcheck-suppress EnableYearChangeCalled calenderCtrl.EnableYearChange(false); // cppcheck-suppress EnableYearChangeCalled calenderCtrl.EnableYearChange(true); // cppcheck-suppress EnableYearChangeCalled calenderCtrl.EnableYearChange(/*default=yes*/); #endif } void wxString_test1(wxString s) { for (int i = 0; i <= s.size(); ++i) { // cppcheck-suppress stlOutOfBounds s[i] = 'x'; } } void wxString_test2() { wxString s; // cppcheck-suppress containerOutOfBounds s[1] = 'a'; s.append("abc"); s[1] = 'B'; printf("%s", static_cast(s.c_str())); wxPrintf("%s", s); wxPrintf("%s", s.c_str()); s.Clear(); } wxString::iterator wxString_test3() { wxString wxString1; wxString wxString2; // cppcheck-suppress mismatchingContainers for (wxString::iterator it = wxString1.begin(); it != wxString2.end(); ++it) {} wxString::iterator it = wxString1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } cppcheck-2.7/test/cli/000077500000000000000000000000001417746362400147065ustar00rootroot00000000000000cppcheck-2.7/test/cli/helloworld/000077500000000000000000000000001417746362400170615ustar00rootroot00000000000000cppcheck-2.7/test/cli/helloworld/helloworld.cppcheck000066400000000000000000000003121417746362400227320ustar00rootroot00000000000000 helloworld.sln false cppcheck-2.7/test/cli/helloworld/helloworld.sln000066400000000000000000000026311417746362400217540ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "helloworld", "helloworld.vcxproj", "{7319858B-261C-4F0D-B022-92BB896242DD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x64.ActiveCfg = Debug|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x64.Build.0 = Debug|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x86.ActiveCfg = Debug|Win32 {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x86.Build.0 = Debug|Win32 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x64.ActiveCfg = Release|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x64.Build.0 = Release|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x86.ActiveCfg = Release|Win32 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C787A78A-D377-4103-9389-2594C573ED55} EndGlobalSection EndGlobal cppcheck-2.7/test/cli/helloworld/helloworld.vcxproj000066400000000000000000000134721417746362400226600ustar00rootroot00000000000000 Debug Win32 Release Win32 Debug x64 Release x64 15.0 {7319858B-261C-4F0D-B022-92BB896242DD} helloworld 10.0.16299.0 Application true v141 MultiByte Application false v141 true MultiByte Application true v141 MultiByte Application false v141 true MultiByte Level3 Disabled true true Level3 Disabled true true Level3 MaxSpeed true true true true true true Level3 MaxSpeed true true true true true true cppcheck-2.7/test/cli/helloworld/main.c000066400000000000000000000002301417746362400201440ustar00rootroot00000000000000#include int main(void) { (void)printf("Hello world!\n"); x = 3 / 0; // ERROR return 0; } #ifdef SOME_CONFIG void foo(); #endif cppcheck-2.7/test/cli/proj-inline-suppress-unusedFunction/000077500000000000000000000000001417746362400240255ustar00rootroot00000000000000cppcheck-2.7/test/cli/proj-inline-suppress-unusedFunction/A.cpp000066400000000000000000000000721417746362400247100ustar00rootroot00000000000000#include "B.hpp" int main() { B b(); return 0; } cppcheck-2.7/test/cli/proj-inline-suppress-unusedFunction/B.cpp000066400000000000000000000001411417746362400247060ustar00rootroot00000000000000#include "B.hpp" B::B() {} // cppcheck-suppress unusedFunction void B::unusedFunctionTest() {} cppcheck-2.7/test/cli/proj-inline-suppress-unusedFunction/B.hpp000066400000000000000000000000761417746362400247220ustar00rootroot00000000000000class B { public: B(); void unusedFunctionTest(); }; cppcheck-2.7/test/cli/proj-inline-suppress/000077500000000000000000000000001417746362400210165ustar00rootroot00000000000000cppcheck-2.7/test/cli/proj-inline-suppress/1.c000066400000000000000000000000171417746362400213200ustar00rootroot00000000000000#include <1.h> cppcheck-2.7/test/cli/proj-inline-suppress/1.h000066400000000000000000000000711417746362400213250ustar00rootroot00000000000000 // cppcheck-suppress zerodiv const int x = 10000 / 0; cppcheck-2.7/test/cli/proj-inline-suppress/2.c000066400000000000000000000001451417746362400213230ustar00rootroot00000000000000// cppcheck-suppress some_warning_id ; there should be a unmatchedSuppression warning about this x; cppcheck-2.7/test/cli/proj-inline-suppress/3.cpp000066400000000000000000000002341417746362400216630ustar00rootroot00000000000000// From forum: https://sourceforge.net/p/cppcheck/discussion/general/thread/e67653efdb/ void foo() { // cppcheck-suppress zerodiv int x = 10000 / 0; } cppcheck-2.7/test/cli/proj-inline-suppress/4.c000066400000000000000000000001251417746362400213230ustar00rootroot00000000000000int main() { // cppcheck-suppress unreadVariable int i = 0; return 0; } cppcheck-2.7/test/cli/proj-suppress-syntaxError/000077500000000000000000000000001417746362400221005ustar00rootroot00000000000000cppcheck-2.7/test/cli/proj-suppress-syntaxError/1.c000066400000000000000000000002541417746362400224050ustar00rootroot00000000000000 void validCode(int argInt) { // if G_UNLIKELY is not defined this results in a syntax error if G_UNLIKELY(argInt == 1) {} else if (G_UNLIKELY(argInt == 2)) {} } cppcheck-2.7/test/cli/proj-suppress-syntaxError/2.c000066400000000000000000000000131417746362400223770ustar00rootroot00000000000000void f1(); cppcheck-2.7/test/cli/proj-suppress-syntaxError/3.c000066400000000000000000000000141417746362400224010ustar00rootroot00000000000000 void f2(); cppcheck-2.7/test/cli/proj2/000077500000000000000000000000001417746362400157425ustar00rootroot00000000000000cppcheck-2.7/test/cli/proj2/a/000077500000000000000000000000001417746362400161625ustar00rootroot00000000000000cppcheck-2.7/test/cli/proj2/a/a.c000066400000000000000000000000471417746362400165470ustar00rootroot00000000000000x = 3 / 0; #ifdef AAA void aa; #endif cppcheck-2.7/test/cli/proj2/b/000077500000000000000000000000001417746362400161635ustar00rootroot00000000000000cppcheck-2.7/test/cli/proj2/b/b.c000066400000000000000000000000141417746362400165430ustar00rootroot00000000000000x = 3 / 0; cppcheck-2.7/test/cli/proj2/proj2.cppcheck000066400000000000000000000002271417746362400205010ustar00rootroot00000000000000 compile_commands.json cppcheck-2.7/test/cli/proj2/proj2.sln000066400000000000000000000026171417746362400175220ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proj2", "proj2.vcxproj", "{B9ED3CF9-9DB9-4876-9923-6FAD501885D5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x64.ActiveCfg = Debug|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x64.Build.0 = Debug|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x86.ActiveCfg = Debug|Win32 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x86.Build.0 = Debug|Win32 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x64.ActiveCfg = Release|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x64.Build.0 = Release|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x86.ActiveCfg = Release|Win32 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DDBB0F1F-12C9-4A06-B09A-D55E5F0D0A43} EndGlobalSection EndGlobal cppcheck-2.7/test/cli/proj2/proj2.vcxproj000066400000000000000000000135271417746362400204230ustar00rootroot00000000000000 Debug Win32 Release Win32 Debug x64 Release x64 15.0 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5} proj2 10.0.16299.0 Application true v141 MultiByte Application false v141 true MultiByte Application true v141 MultiByte Application false v141 true MultiByte Level3 Disabled true true Level3 Disabled true true Level3 MaxSpeed true true true true true true Level3 MaxSpeed true true true true true true cppcheck-2.7/test/cli/readme.txt000066400000000000000000000004031417746362400167010ustar00rootroot00000000000000 Systemtesting of Cppcheck CLI on some projects addons base path exclude folders importing projects * visual studio * compile database - different generators bear/cmake/.. - different platforms suppressions Different paths: * relative * absolute cppcheck-2.7/test/cli/test-clang-import.py000066400000000000000000000076631417746362400206450ustar00rootroot00000000000000 # python -m pytest test-clang-import.py import os import re import subprocess import pytest from testutils import cppcheck try: subprocess.call(['clang', '--version']) except OSError: pytest.skip("'clang' does not exist", allow_module_level=True) def get_debug_section(title, stdout): s = re.sub(r'0x[0-9a-fA-F]+', '0x12345678', stdout) s = re.sub(r'nestedIn: Struct', 'nestedIn: Class', s) s = re.sub(r'classDef: struct', 'classDef: class', s) s = re.sub(r'isInline: [a-z]+', 'isInline: ---', s) s = re.sub(r'needInitialization: .*', 'needInitialization: ---', s) s = re.sub(r'functionOf: .*', 'functionOf: ---', s) s = re.sub(r'0x12345678 Struct', '0x12345678 Class', s) if title == '##AST': # TODO set types s = re.sub(r"return '[a-zA-Z0-9: *]+'", "return", s) pos1 = s.find(title) assert pos1 > 0, 'title not found' pos1 = s.find('\n', pos1) + 1 assert pos1 > 0 pos2 = s.find("\n##", pos1) if pos2 < 0: return s[pos1:] return s[pos1:pos2-1] def check_symbol_database(code): testfile = 'test.cpp' with open(testfile, 'w+t') as f: f.write(code) ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile]) ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile]) os.remove(testfile) assert 0 == ret1, stdout1 assert 0 == ret2, stdout2 assert get_debug_section('### Symbol database', stdout1) == get_debug_section('### Symbol database', stdout2) def check_ast(code): testfile = 'test.cpp' with open(testfile, 'w+t') as f: f.write(code) ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile]) ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile]) os.remove(testfile) assert 0 == ret1, stdout1 assert 0 == ret2, stdout1 assert get_debug_section('##AST', stdout1) == get_debug_section('##AST', stdout2) def todo_check_ast(code): testfile = 'test.cpp' with open(testfile, 'w+t') as f: f.write(code) ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile]) ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile]) os.remove(testfile) assert 0 == ret1, stdout1 assert 0 == ret2, stdout2 assert get_debug_section('##AST', stdout1) != get_debug_section('##AST', stdout2) def test_symbol_database_1(): check_symbol_database('int main(){return 0;}') def test_symbol_database_2(): check_symbol_database('struct Foo { void f(); }; void Foo::f() {}') def test_symbol_database_3(): check_symbol_database('struct Fred { int a; }; int b; void f(int c, int d) { int e; }') def test_symbol_database_4(): check_symbol_database('void f(const int x) {}') def test_symbol_database_5(): check_symbol_database('void f(int);') def test_symbol_database_6(): check_symbol_database('inline static int foo(int x) { return x; }') def test_symbol_database_7(): check_symbol_database('struct S {int x;}; void f(struct S *s) {}') def test_symbol_database_class_access_1(): check_symbol_database('class Fred { void foo ( ) {} } ;') def test_symbol_database_class_access_2(): check_symbol_database('class Fred { protected: void foo ( ) {} } ;') def test_symbol_database_class_access_3(): check_symbol_database('class Fred { public: void foo ( ) {} } ;') def test_symbol_database_operator(): check_symbol_database('struct Fred { void operator=(int x); };') def test_symbol_database_struct_1(): check_symbol_database('struct S {};') def test_ast_calculations(): check_ast('int x = 5; int y = (x + 4) * 2;') check_ast('long long dostuff(int x) { return x ? 3 : 5; }') def test_ast_control_flow(): check_ast('void foo(int x) { if (x > 5){} }') check_ast('int dostuff() { for (int x = 0; x < 10; x++); }') check_ast('void foo(int x) { switch (x) {case 1: break; } }') check_ast('void foo(int a, int b, int c) { foo(a,b,c); }') def test_ast(): check_ast('struct S { int x; }; S* foo() { return new S(); }') cppcheck-2.7/test/cli/test-helloworld.py000066400000000000000000000167211417746362400204170ustar00rootroot00000000000000 # python -m pytest test-helloworld.py import os import re from testutils import create_gui_project_file, cppcheck # Run Cppcheck from project path def cppcheck_local(args): cwd = os.getcwd() os.chdir('helloworld') ret, stdout, stderr = cppcheck(args) os.chdir(cwd) return ret, stdout, stderr def getRelativeProjectPath(): return 'helloworld' def getAbsoluteProjectPath(): return os.path.join(os.getcwd(), 'helloworld') # Get Visual Studio configurations checking a file # Checking {file} {config}... def getVsConfigs(stdout, filename): ret = [] for line in stdout.split('\n'): if not line.startswith('Checking %s ' % filename): continue if not line.endswith('...'): continue res = re.match(r'.* ([A-Za-z0-9|]+)...', line) if res: ret.append(res.group(1)) ret.sort() return ' '.join(ret) def test_relative_path(): ret, stdout, stderr = cppcheck(['--template=cppcheck1', 'helloworld']) filename = os.path.join('helloworld', 'main.c') assert ret == 0, stdout assert stderr == '[%s:5]: (error) Division by zero.\n' % filename def test_local_path(): ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '.']) assert ret == 0, stdout assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0, stdout assert stderr == '[%s:5]: (error) Division by zero.\n' % filename def test_addon_local_path(): ret, stdout, stderr = cppcheck_local(['--addon=misra', '--template=cppcheck1', '.']) assert ret == 0, stdout assert stderr == ('[main.c:5]: (error) Division by zero.\n' '[main.c:1]: (style) misra violation (use --rule-texts= to get proper output)\n') def test_addon_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--addon=misra', '--template=cppcheck1', prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0, stdout assert stderr == ('[%s:5]: (error) Division by zero.\n' '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) def test_addon_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--addon=misra', '--template=cppcheck1', prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0, stdout assert stdout == ('Checking %s ...\n' 'Checking %s: SOME_CONFIG...\n' % (filename, filename)) assert stderr == ('[%s:5]: (error) Division by zero.\n' '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) def test_addon_with_gui_project(): project_file = 'helloworld/test.cppcheck' create_gui_project_file(project_file, paths=['.'], addon='misra') ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + project_file]) filename = os.path.join('helloworld', 'main.c') assert ret == 0, stdout assert stdout == 'Checking %s ...\n' % filename assert stderr == ('[%s:5]: (error) Division by zero.\n' '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) def test_basepath_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck([prjpath, '--template=cppcheck1', '-rp=' + prjpath]) assert ret == 0, stdout assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_basepath_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', prjpath, '-rp=' + prjpath]) assert ret == 0, stdout assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_vs_project_local_path(): ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '--project=helloworld.vcxproj']) assert ret == 0, stdout assert getVsConfigs(stdout, 'main.c') == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_vs_project_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + os.path.join(prjpath, 'helloworld.vcxproj')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0, stdout assert getVsConfigs(stdout, filename) == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % filename def test_vs_project_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + os.path.join(prjpath, 'helloworld.vcxproj')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0, stdout assert getVsConfigs(stdout, filename) == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % filename def test_cppcheck_project_local_path(): ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '--platform=win64', '--project=helloworld.cppcheck']) assert ret == 0, stdout assert getVsConfigs(stdout, 'main.c') == 'Debug|x64' assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_cppcheck_project_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0, stdout assert getVsConfigs(stdout, filename) == 'Debug|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % filename def test_cppcheck_project_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0, stdout assert getVsConfigs(stdout, filename) == 'Debug|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % filename def test_suppress_command_line(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--suppress=zerodiv:' + os.path.join(prjpath, 'main.c'), prjpath]) assert ret == 0, stdout assert stderr == '' prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--suppress=zerodiv:' + os.path.join(prjpath, 'main.c'), prjpath]) assert ret == 0, stdout assert stderr == '' def test_suppress_project(): project_file = os.path.join('helloworld', 'test.cppcheck') create_gui_project_file(project_file, paths=['.'], suppressions=[{'fileName':'main.c', 'id':'zerodiv'}]) # Relative path ret, stdout, stderr = cppcheck(['--project=' + project_file]) assert ret == 0, stdout assert stderr == '' # Absolute path ret, stdout, stderr = cppcheck(['--project=' + os.path.join(os.getcwd(), 'helloworld', 'test.cppcheck')]) assert ret == 0, stdout assert stderr == '' def test_exclude(): prjpath = getRelativeProjectPath() ret, stdout, _ = cppcheck(['-i' + prjpath, '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) assert ret == 1 assert stdout == 'cppcheck: error: no C or C++ source files found.\n' cppcheck-2.7/test/cli/test-inline-suppress.py000066400000000000000000000056151417746362400214040ustar00rootroot00000000000000 # python -m pytest test-inline-suppress.py import json import os import tempfile from testutils import cppcheck def create_unused_function_compile_commands(): prjpath = os.path.realpath('proj-inline-suppress-unusedFunction') compile_commands = os.path.join(prjpath, 'compile_commands.json') j = [{'directory': prjpath, 'command': '/usr/bin/c++ -I"' + prjpath + '" -o "' + os.path.join(prjpath, 'B.cpp.o') + '" -c "' + os.path.join(prjpath, 'B.cpp') + '"', 'file': os.path.join(prjpath, 'B.cpp')}, {'directory': prjpath, 'command': '/usr/bin/c++ -I"' + prjpath + '" -o "' + os.path.join(prjpath, 'A.cpp.o') + '" -c "' + os.path.join(prjpath, 'A.cpp') + '"', 'file': os.path.join(prjpath, 'A.cpp')}] with open(compile_commands, 'wt') as f: f.write(json.dumps(j, indent=4)) def test1(): ret, stdout, stderr = cppcheck(['--inline-suppr', 'proj-inline-suppress']) assert ret == 0, stdout assert stderr == '' def test2(): ret, stdout, stderr = cppcheck(['proj-inline-suppress']) assert ret == 0, stdout assert len(stderr) > 0 def test_unmatched_suppression(): ret, stdout, stderr = cppcheck(['--inline-suppr', '--enable=information', '--error-exitcode=1', 'proj-inline-suppress/2.c']) assert ret == 1 assert 'Unmatched suppression: some_warning_id' in stderr def test_unmatched_suppression_path_with_extra_stuf(): ret, stdout, stderr = cppcheck(['--inline-suppr', '--enable=information', '--error-exitcode=1', './proj-inline-suppress/2.c']) assert ret == 1 assert 'Unmatched suppression: some_warning_id' in stderr def test_backwards_compatibility(): ret, stdout, stderr = cppcheck(['proj-inline-suppress/3.cpp']) assert '[zerodiv]' in stderr ret, stdout, stderr = cppcheck(['--inline-suppr', 'proj-inline-suppress/3.cpp']) assert ret == 0, stdout assert stderr == '' def test_compile_commands_unused_function(): create_unused_function_compile_commands() ret, stdout, stderr = cppcheck(['--enable=all', '--error-exitcode=1', '--project=./proj-inline-suppress-unusedFunction/compile_commands.json']) assert ret == 1 assert 'unusedFunction' in stderr def test_compile_commands_unused_function_suppression(): create_unused_function_compile_commands() ret, stdout, stderr = cppcheck(['--enable=all', '--inline-suppr', '--error-exitcode=1', '--project=./proj-inline-suppress-unusedFunction/compile_commands.json']) assert ret == 0, stdout assert 'unusedFunction' not in stderr def test_build_dir(): with tempfile.TemporaryDirectory() as tempdir: args = f'--cppcheck-build-dir={tempdir} --enable=all --inline-suppr proj-inline-suppress/4.c'.split() ret, stdout, stderr = cppcheck(args) assert ret == 0, stdout assert len(stderr) == 0 ret, stdout, stderr = cppcheck(args) assert ret == 0, stdout assert len(stderr) == 0 cppcheck-2.7/test/cli/test-more-projects.py000066400000000000000000000042371417746362400210340ustar00rootroot00000000000000# python -m pytest test-more-projects.py import json import os from testutils import cppcheck def test_project_force_U(tmpdir): # 10018 # -U does not work with compile_commands.json with open(os.path.join(tmpdir, 'bug1.cpp'), 'wt') as f: f.write(""" int x = 123 / 0; #ifdef MACRO1 int y = 1000 / 0; #endif """) compile_commands = os.path.join(tmpdir, 'compile_commands.json') compilation_db = [ {"directory": str(tmpdir), "command": "c++ -o bug1.o -c bug1.cpp", "file": "bug1.cpp", "output": "bug1.o"} ] with open(compile_commands, 'wt') as f: f.write(json.dumps(compilation_db)) # Without -U => both bugs are found ret, stdout, stderr = cppcheck(['--project=' + compile_commands, '--force', '-rp=' + str(tmpdir), '--template=cppcheck1']) assert ret == 0, stdout assert (stderr == '[bug1.cpp:2]: (error) Division by zero.\n' '[bug1.cpp:4]: (error) Division by zero.\n') # With -U => only first bug is found ret, stdout, stderr = cppcheck(['--project=' + compile_commands, '--force', '-UMACRO1', '-rp=' + str(tmpdir), '--template=cppcheck1']) assert ret == 0, stdout assert stderr == '[bug1.cpp:2]: (error) Division by zero.\n' def test_project_custom_platform(tmpdir): """ import cppcheck project that contains a custom platform file """ project_file = os.path.join(tmpdir, 'Project.cppcheck') with open(project_file, 'wt') as f: f.write(""" p1.xml """) with open(os.path.join(tmpdir, 'p1.xml'), 'wt') as f: f.write('\n') with open(os.path.join(tmpdir, '1.c'), 'wt') as f: f.write("int x;") ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) assert ret == 0, stdout assert stderr == '' cppcheck-2.7/test/cli/test-proj2.py000066400000000000000000000146741417746362400173050ustar00rootroot00000000000000 # python -m pytest test-proj2.py import json import os from testutils import create_gui_project_file, cppcheck COMPILE_COMMANDS_JSON = 'compile_commands.json' ERR_A = ('%s:1:7: error: Division by zero. [zerodiv]\n' + 'x = 3 / 0;\n' + ' ^\n') % os.path.join('a', 'a.c') ERR_B = ('%s:1:7: error: Division by zero. [zerodiv]\n' + 'x = 3 / 0;\n' + ' ^\n') % os.path.join('b', 'b.c') def realpath(s): return os.path.realpath(s).replace('\\', '/') def create_compile_commands(): j = [{'directory': realpath('proj2/a'), 'command': 'gcc -c a.c', 'file': 'a.c'}, {'directory': realpath('proj2'), 'command': 'gcc -c b/b.c', 'file': 'b/b.c'}] with open('proj2/' + COMPILE_COMMANDS_JSON, 'wt') as f: f.write(json.dumps(j)) # Run Cppcheck from project path def cppcheck_local(args): cwd = os.getcwd() os.chdir('proj2') ret, stdout, stderr = cppcheck(args) os.chdir(cwd) return ret, stdout, stderr def test_file_filter(): ret, stdout, stderr = cppcheck(['proj2/','--file-filter=proj2/a/*']) file1 = os.path.join('proj2', 'a', 'a.c') file2 = os.path.join('proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('Checking %s ...' % file1) >= 0 ret, stdout, stderr = cppcheck(['proj2/','--file-filter=proj2/b*']) assert ret == 0, stdout assert stdout.find('Checking %s ...' % file2) >= 0 def test_local_path(): create_compile_commands() ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json']) file1 = os.path.join('a', 'a.c') file2 = os.path.join('b', 'b.c') assert ret == 0, stdout assert stdout.find('Checking %s ...' % file1) >= 0 assert stdout.find('Checking %s ...' % file2) >= 0 def test_local_path_force(): create_compile_commands() ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json', '--force']) assert ret == 0, stdout assert stdout.find('AAA') >= 0 def test_local_path_maxconfigs(): create_compile_commands() ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json', '--max-configs=2']) assert ret == 0, stdout assert stdout.find('AAA') >= 0 def test_relative_path(): create_compile_commands() ret, stdout, stderr = cppcheck(['--project=proj2/' + COMPILE_COMMANDS_JSON]) file1 = os.path.join('proj2', 'a', 'a.c') file2 = os.path.join('proj2', 'b', 'b.c') assert ret == 0, stdout assert stdout.find('Checking %s ...' % file1) >= 0 assert stdout.find('Checking %s ...' % file2) >= 0 def test_absolute_path(): create_compile_commands() ret, stdout, stderr = cppcheck(['--project=' + os.path.realpath('proj2/' + COMPILE_COMMANDS_JSON)]) file1 = os.path.realpath('proj2/a/a.c') file2 = os.path.realpath('proj2/b/b.c') assert ret == 0, stdout assert stdout.find('Checking %s ...' % file1) >= 0 assert stdout.find('Checking %s ...' % file2) >= 0 def test_gui_project_loads_compile_commands_1(): create_compile_commands() ret, stdout, stderr = cppcheck(['--project=proj2/proj2.cppcheck']) file1 = os.path.join('proj2', 'a', 'a.c') file2 = os.path.join('proj2', 'b', 'b.c') assert ret == 0, stdout assert stdout.find('Checking %s ...' % file1) >= 0 assert stdout.find('Checking %s ...' % file2) >= 0 def test_gui_project_loads_compile_commands_2(): create_compile_commands() exclude_path_1 = 'proj2/b' create_gui_project_file('proj2/test.cppcheck', import_project='compile_commands.json', exclude_paths=[exclude_path_1]) ret, stdout, stderr = cppcheck(['--project=proj2/test.cppcheck']) file1 = os.path.join('proj2', 'a', 'a.c') file2 = os.path.join('proj2', 'b', 'b.c') # Excluded by test.cppcheck assert ret == 0, stdout assert stdout.find('Checking %s ...' % file1) >= 0 assert stdout.find('Checking %s ...' % file2) < 0 def test_gui_project_loads_relative_vs_solution(): create_gui_project_file('test.cppcheck', import_project='proj2/proj2.sln') ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) file1 = os.path.join('proj2', 'a', 'a.c') file2 = os.path.join('proj2', 'b', 'b.c') assert ret == 0, stdout assert stdout.find('Checking %s Debug|Win32...' % file1) >= 0 assert stdout.find('Checking %s Debug|x64...' % file1) >= 0 assert stdout.find('Checking %s Release|Win32...' % file1) >= 0 assert stdout.find('Checking %s Release|x64...' % file1) >= 0 assert stdout.find('Checking %s Debug|Win32...' % file2) >= 0 assert stdout.find('Checking %s Debug|x64...' % file2) >= 0 assert stdout.find('Checking %s Release|Win32...' % file2) >= 0 assert stdout.find('Checking %s Release|x64...' % file2) >= 0 def test_gui_project_loads_absolute_vs_solution(): create_gui_project_file('test.cppcheck', import_project=realpath('proj2/proj2.sln')) ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) file1 = os.path.realpath('proj2/a/a.c') file2 = os.path.realpath('proj2/b/b.c') print(stdout) assert ret == 0, stdout assert stdout.find('Checking %s Debug|Win32...' % file1) >= 0 assert stdout.find('Checking %s Debug|x64...' % file1) >= 0 assert stdout.find('Checking %s Release|Win32...' % file1) >= 0 assert stdout.find('Checking %s Release|x64...' % file1) >= 0 assert stdout.find('Checking %s Debug|Win32...' % file2) >= 0 assert stdout.find('Checking %s Debug|x64...' % file2) >= 0 assert stdout.find('Checking %s Release|Win32...' % file2) >= 0 assert stdout.find('Checking %s Release|x64...' % file2) >= 0 def test_gui_project_loads_relative_vs_solution_2(): create_gui_project_file('test.cppcheck', root_path='proj2', import_project='proj2/proj2.sln') ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) assert ret == 0, stdout assert stderr == ERR_A + ERR_B def test_gui_project_loads_relative_vs_solution_with_exclude(): create_gui_project_file('test.cppcheck', root_path='proj2', import_project='proj2/proj2.sln', exclude_paths=['b']) ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) assert ret == 0, stdout assert stderr == ERR_A def test_gui_project_loads_absolute_vs_solution_2(): create_gui_project_file('test.cppcheck', root_path=realpath('proj2'), import_project=realpath('proj2/proj2.sln')) ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) assert ret == 0, stdout assert stderr == ERR_A + ERR_B cppcheck-2.7/test/cli/test-project.py000066400000000000000000000116121417746362400177040ustar00rootroot00000000000000# python -m pytest test-projects.py import pytest import os import json from testutils import cppcheck @pytest.mark.parametrize("project_ext", ["json", "sln", "vcxproj", "bpr", "cppcheck"]) def test_missing_project(project_ext): project_file = "file.{}".format(project_ext) ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) assert 1 == ret assert "cppcheck: error: failed to open project '{}'. The file does not exist.\n".format(project_file) == stdout assert "" == stderr def _test_project_error(tmpdir, ext, content, expected): project_file = os.path.join(tmpdir, "file.{}".format(ext)) with open(project_file, 'w') as f: if content is not None: f.write(content) ret, stdout, stderr = cppcheck(['--project=' + str(project_file)]) assert 1 == ret assert "cppcheck: error: " + expected + "\ncppcheck: error: failed to load project '{}'. An error occurred.\n".format(project_file) == stdout assert "" == stderr @pytest.mark.parametrize("project_ext, expected", [ ("json", "compilation database is not a JSON array"), ("sln", "Visual Studio solution file is empty"), ("vcxproj", "Visual Studio project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT"), ("bpr", "Borland project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT"), ("cppcheck", "Cppcheck GUI project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT") ]) def test_empty_project(tmpdir, project_ext, expected): _test_project_error(tmpdir, project_ext, None, expected) def test_json_entry_file_not_found(tmpdir): compilation_db = [ {"directory": str(tmpdir), "command": "c++ -o bug1.o -c bug1.cpp", "file": "bug1.cpp", "output": "bug1.o"} ] expected = "'{}' from compilation database does not exist".format(os.path.join(tmpdir, "bug1.cpp")) _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) def test_json_no_arguments(tmpdir): compilation_db = [ {"directory": str(tmpdir), "file": "bug1.cpp", "output": "bug1.o"} ] expected = "no 'arguments' or 'command' field found in compilation database entry" _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) def test_json_invalid_arguments(tmpdir): compilation_db = [ {"directory": str(tmpdir), "arguments": "", "file": "bug1.cpp", "output": "bug1.o"} ] expected = "'arguments' field in compilation database entry is not a JSON array" _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) def test_sln_invalid_file(tmpdir): content = "some file" expected = "Visual Studio solution file header not found" _test_project_error(tmpdir, "sln", content, expected) def test_sln_no_header(tmpdir): content = "\xEF\xBB\xBF\r\n" \ "some header" expected = "Visual Studio solution file header not found" _test_project_error(tmpdir, "sln", content, expected) def test_sln_no_projects(tmpdir): content = "\xEF\xBB\xBF\r\n" \ "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" expected = "no projects found in Visual Studio solution file" _test_project_error(tmpdir, "sln", content, expected) def test_sln_project_file_not_found(tmpdir): content = "\xEF\xBB\xBF\r\n" \ "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" \ "# Visual Studio Version 16\r\n" \ "VisualStudioVersion = 16.0.29020.237\r\n" \ "MinimumVisualStudioVersion = 10.0.40219.1\r\n" \ 'Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli\\cli.vcxproj", "{35CBDF51-2456-3EC3-99ED-113C30858883}"\r\n' \ "ProjectSection(ProjectDependencies) = postProject\r\n" \ "{C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A}\r\n" \ "EndProjectSection\r\n" \ "EndProject\r\n" expected = "Visual Studio project file is not a valid XML - XML_ERROR_FILE_NOT_FOUND\n" \ "cppcheck: error: failed to load '{}' from Visual Studio solution".format(os.path.join(tmpdir, "cli\\cli.vcxproj")) _test_project_error(tmpdir, "sln", content, expected) def test_vcxproj_no_xml_root(tmpdir): content = '' expected = "Visual Studio project file has no XML root node" _test_project_error(tmpdir, "vcxproj", content, expected) def test_bpr_no_xml_root(tmpdir): content = '' expected = "Borland project file has no XML root node" _test_project_error(tmpdir, "bpr", content, expected) def test_cppcheck_no_xml_root(tmpdir): content = '' expected = "Cppcheck GUI project file has no XML root node" _test_project_error(tmpdir, "cppcheck", content, expected) cppcheck-2.7/test/cli/test-suppress-syntaxError.py000066400000000000000000000007201417746362400224560ustar00rootroot00000000000000 # python -m pytest test-suppress-syntaxError.py from testutils import cppcheck def test_j2(): ret, stdout, stderr = cppcheck(['--error-exitcode=1', '-j2', '-q', 'proj-suppress-syntaxError']) assert ret == 1 assert len(stderr) > 0 def test_j2_suppress(): ret, stdout, stderr = cppcheck(['--error-exitcode=1', '--suppress=*:proj-suppress-syntaxError/*', '-j2', '-q', 'proj-suppress-syntaxError']) assert ret == 0 assert len(stderr) == 0 cppcheck-2.7/test/cli/testutils.py000066400000000000000000000047261417746362400173310ustar00rootroot00000000000000 import logging import os import subprocess # Create Cppcheck project file import sys def create_gui_project_file(project_file, root_path=None, import_project=None, paths=None, exclude_paths=None, suppressions=None, addon=None): cppcheck_xml = ('\n' '\n') if root_path: cppcheck_xml += ' \n' if import_project: cppcheck_xml += ' ' + import_project + '\n' if paths: cppcheck_xml += ' \n' for path in paths: cppcheck_xml += ' \n' cppcheck_xml += ' \n' if exclude_paths: cppcheck_xml += ' \n' for path in exclude_paths: cppcheck_xml += ' \n' cppcheck_xml += ' \n' if suppressions: cppcheck_xml += ' \n' for suppression in suppressions: cppcheck_xml += ' \n' cppcheck_xml += ' \n' if addon: cppcheck_xml += ' \n' cppcheck_xml += ' %s\n' % addon cppcheck_xml += ' \n' cppcheck_xml += '\n' f = open(project_file, 'wt') f.write(cppcheck_xml) f.close() def lookup_cppcheck_exe(): # path the script is located in script_path = os.path.dirname(os.path.realpath(__file__)) exe_name = "cppcheck" if sys.platform == "win32": exe_name += ".exe" for base in (script_path + '/../../', './'): for path in ('', 'bin/', 'bin/debug/'): exe_path = base + path + exe_name if os.path.isfile(exe_path): return exe_path return None # Run Cppcheck with args def cppcheck(args): exe = lookup_cppcheck_exe() assert exe is not None, 'no cppcheck binary found' logging.info(exe + ' ' + ' '.join(args)) p = subprocess.Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') return p.returncode, stdout, stderr cppcheck-2.7/test/options.cpp000066400000000000000000000031551417746362400163420ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2021 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "options.h" options::options(int argc, const char* const argv[]) : mWhichTests(argv + 1, argv + argc) ,mQuiet(mWhichTests.count("-q") != 0) ,mHelp(mWhichTests.count("-h") != 0 || mWhichTests.count("--help")) ,mExe(argv[0]) { for (std::set::const_iterator it = mWhichTests.begin(); it != mWhichTests.end();) { if (!(*it).empty() && (((*it)[0] == '-') || ((*it).find("::") != std::string::npos && mWhichTests.count((*it).substr(0, (*it).find("::")))))) it = mWhichTests.erase(it); else ++it; } if (mWhichTests.empty()) { mWhichTests.insert(""); } } bool options::quiet() const { return mQuiet; } bool options::help() const { return mHelp; } const std::set& options::which_test() const { return mWhichTests; } const std::string& options::exe() const { return mExe; } cppcheck-2.7/test/options.h000066400000000000000000000031601417746362400160030ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2021 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef OPTIONS_H #define OPTIONS_H #include #include /** * @brief Class to parse command-line parameters for ./testrunner . * Has getters for available switches and parameters. * See test/testoptions.cpp for sample usage. */ class options { public: /** Call from main() to populate object */ options(int argc, const char* const argv[]); /** Don't print the name of each method being tested. */ bool quiet() const; /** Print help. */ bool help() const; /** Which test should be run. Empty string means 'all tests' */ const std::set& which_test() const; const std::string& exe() const; private: options(); options(const options& non_copy); const options& operator =(const options& non_assign); private: std::set mWhichTests; const bool mQuiet; const bool mHelp; std::string mExe; }; #endif cppcheck-2.7/test/precompiled.h000066400000000000000000000015471417746362400166220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include "testsuite.h" #include "settings.h" #include "tokenize.h" #include "library.h" #include "check.h" cppcheck-2.7/test/redirect.h000066400000000000000000000054151417746362400161160ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2021 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef REDIRECT_H #define REDIRECT_H #include #include #include extern std::ostringstream errout; extern std::ostringstream output; /** * @brief Utility class for capturing cout and cerr to ostringstream buffers * for later use. Uses RAII to stop redirection when the object goes out of * scope. */ class RedirectOutputError { public: /** Set up redirection, flushing anything in the pipes. */ RedirectOutputError() { // flush all old output std::cout.flush(); std::cerr.flush(); _oldCout = std::cout.rdbuf(); // back up cout's streambuf _oldCerr = std::cerr.rdbuf(); // back up cerr's streambuf std::cout.rdbuf(_out.rdbuf()); // assign streambuf to cout std::cerr.rdbuf(_err.rdbuf()); // assign streambuf to cerr } /** Revert cout and cerr behaviour */ ~RedirectOutputError() { std::cout.rdbuf(_oldCout); // restore cout's original streambuf std::cerr.rdbuf(_oldCerr); // restore cerrs's original streambuf errout << _err.str(); output << _out.str(); } /** Return what would be printed to cout. See also clearOutput() */ std::string getOutput() const { return _out.str(); } /** Normally called after getOutput() to prevent same text to be returned twice. */ void clearOutput() { _out.str(""); } /** Return what would be printed to cerr. See also clearErrout() */ std::string getErrout() const { return _err.str(); } /** Normally called after getErrout() to prevent same text to be returned twice. */ void clearErrout() { _err.str(""); } private: std::ostringstream _out; std::ostringstream _err; std::streambuf *_oldCout; std::streambuf *_oldCerr; }; #define REDIRECT RedirectOutputError redir; do {} while (false) #define GET_REDIRECT_OUTPUT redir.getOutput() #define CLEAR_REDIRECT_OUTPUT redir.clearOutput() #define GET_REDIRECT_ERROUT redir.getErrout() #define CLEAR_REDIRECT_ERROUT redir.clearErrout() #endif cppcheck-2.7/test/synthetic/000077500000000000000000000000001417746362400161515ustar00rootroot00000000000000cppcheck-2.7/test/synthetic/Makefile000066400000000000000000000004631417746362400176140ustar00rootroot00000000000000ifndef CC CC=gcc endif all: controlflow.o data.o functions.o ub.o controlflow.o: controlflow.c $(CC) -c controlflow.c data.o: data.c $(CC) -c data.c functions.o: functions.c $(CC) -c functions.c ub.o: ub.c $(CC) -c ub.c clean: rm -rf controlflow.o data.o functions.o ub.o cppcheck-2.7/test/synthetic/controlflow.c000066400000000000000000000021151417746362400206640ustar00rootroot00000000000000 ////////////////////////////// // control flow analysis ////////////////////////////// int buf[2]; void in_if(int a) { if (a==100) buf[a] = 0; // BUG } void before_if(int a) { buf[a] = 0; // WARNING if (a==100) {} } void after_if(int a) { if (a==100) {} buf[a] = 0; // WARNING } void in_for(void) { int x; for (x = 0; x<100; x++) { buf[x] = 0; // BUG } } void after_for(void) { int x; for (x = 0; x<100; x++) {} buf[x] = 0; // BUG } void in_switch(int x) { switch (x) { case 100: buf[x] = 0; // BUG break; } } void before_switch(int x) { buf[x] = 0; // WARNING switch (x) { case 100: break; } } void after_switch(int x) { switch (x) { case 100: break; } buf[x] = 0; // WARNING } void in_while(void) { int x = 0; while (x<100) { buf[x] = 0; // BUG x++; } } void after_while(void) { int x = 0; while (x<100) x++; buf[x] = 0; // BUG } cppcheck-2.7/test/synthetic/data.c000066400000000000000000000020261417746362400172260ustar00rootroot00000000000000 int TestData[10]; int g; void global() { g = 1000; TestData[g] = 0; // BUG } int garr[10]; void global_array() { garr[3] = 1000; TestData[garr[3]] = 0; // BUG } int *gp; void global_pointer() { *gp = 1000; TestData[*gp] = 0; // BUG } void local() { int x; x = 1000; TestData[x] = 0; // BUG } void local_array() { int arr[10]; arr[3] = 1000; TestData[arr[3]] = 0; // BUG } void local_alias_1() { int x; int *p = &x; *p = 1000; TestData[*p] = 0; // BUG } void local_alias_2() { int x; int *p = &x; x = 1000; TestData[*p] = 0; // BUG } struct ABC { int a; int b[10]; int c; }; void struct_member_init() { struct ABC abc = {1000,{0},3}; TestData[abc.a] = 0; // BUG } void struct_member_assign(struct ABC *abc) { abc->a = 1000; TestData[abc->a] = 0; // BUG } void struct_arraymember(struct ABC *abc) { abc->b[3] = 1000; TestData[abc->b[3]] = 0; // BUG } cppcheck-2.7/test/synthetic/functions.c000066400000000000000000000006011417746362400203220ustar00rootroot00000000000000 int TestData[100]; void par_not_dependant(int par) { TestData[par] = 0; // BUG } void par_dependant(int x, int y) { if (x < 10) TestData[y] = 0; // BUG } void call(int x) { par_not_dependant(1000); par_dependant(0, 1000); } int getLargeIndex() { return 1000; } void return_value() { TestData[getLargeIndex()] = 0; // BUG } cppcheck-2.7/test/synthetic/report.py000077500000000000000000000033021417746362400200370ustar00rootroot00000000000000#!/usr/bin/env python import os import re def hasresult(filename, result): if not os.path.isfile(filename): return False for line in open(filename, 'rt'): if result in line: return True return False def parsefile(filename): ret = [] linenr = 0 functionName = None for line in open(filename, 'rt'): linenr = linenr + 1 res = re.match('^[a-z]+[ *]+([a-z0-9_]+)[(]', line) if res: functionName = res.group(1) if line.startswith('}'): functionName = '' elif 'BUG' in line or 'WARN' in line or filename == 'ub.c': spaces = ' ' * 100 s = filename + spaces s = s[:15] + str(linenr) + spaces s = s[:20] + functionName + spaces s = s[:50] if hasresult('cppcheck.txt', '[' + filename + ':' + str(linenr) + ']'): s = s + ' X' else: s = s + ' ' if hasresult('clang.txt', filename + ':' + str(linenr)): s = s + ' X' else: s = s + ' ' if hasresult('lint.txt', filename + ' ' + str(linenr)): s = s + ' X' else: s = s + ' ' if hasresult('cov.txt', filename + ':' + str(linenr)): s = s + ' X' else: s = s + ' ' ret.append(s) return ret bugs = [] bugs.extend(parsefile('controlflow.c')) bugs.extend(parsefile('data.c')) bugs.extend(parsefile('functions.c')) bugs.extend(parsefile('ub.c')) for bug in bugs: print(bug) cppcheck-2.7/test/synthetic/run-clang.sh000077500000000000000000000004001417746362400203700ustar00rootroot00000000000000#!/bin/bash ~/llvm/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.security controlflow.c data.c functions.c 2>&1 /dev/null | grep warning ~/llvm/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.security,core ub.c 2>&1 /dev/null | grep warning cppcheck-2.7/test/synthetic/run-lint.bat000077500000000000000000000005621417746362400204170ustar00rootroot00000000000000\lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 controlflow.c \lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 data.c \lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 functions.c \lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 ub.c cppcheck-2.7/test/synthetic/ub.c000066400000000000000000000020231417746362400167200ustar00rootroot00000000000000void alias() { int x; int *ip=&x; float *fp = (float *)ip; } int buffer_overflow() { int x[10]={0}; return x[100]; } int dead_pointer(int a) { int *p=&a; if (a) { int x=0; p = &x; } return *p; } int division_by_zero() { return 100 / 0; } int float_to_int() { double d=1E100; return (int)d; } void negative_size(int sz) { if (sz < 0) { int buf[sz]; } } int no_return() {} int null_pointer() { int *p = 0; return *p; } int *pointer_arithmetic() { static int buf[10]; return buf + 100; } unsigned char pointer_to_u8() { static int buf[10]; return (int*)buf; } int pointer_subtraction() { char a[10]; char b[10]; return b-a; } int pointer_comparison() { char a[10]; char b[10]; return b> 1; return intmax * 2; } void string_literal() { *((char *)"hello") = 0; } int uninit() { int x; return x + 2; } cppcheck-2.7/test/test.cxx000066400000000000000000000007271417746362400156500ustar00rootroot00000000000000/* This is testing data for the GUI. Used for testing GUI with various error styles reported by cppcheck. Not meant to be compiled. */ #include void unused() { int a = 15; } void f(char k) { delete k; } void possible_style() { std::list::iterator it; for (it = ab.begin(); it != ab.end(); it++) ; } int main() { char *b = new char[1]; char *a = new char[8]; if (a); b = gets(); f(a); possible_style(); } cppcheck-2.7/test/test64bit.cpp000066400000000000000000000216061417746362400165000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check64bit.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class Test64BitPortability : public TestFixture { public: Test64BitPortability() : TestFixture("Test64BitPortability") {} private: Settings settings; void run() OVERRIDE { settings.severity.enable(Severity::portability); TEST_CASE(novardecl); TEST_CASE(functionpar); TEST_CASE(structmember); TEST_CASE(ptrcompare); TEST_CASE(ptrarithmetic); TEST_CASE(returnIssues); TEST_CASE(assignment); } #define check(code) check_(code, __FILE__, __LINE__) void check_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); LOAD_LIB_2(settings.library, "std.cfg"); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check char variable usage.. Check64BitPortability check64BitPortability(&tokenizer, &settings, this); check64BitPortability.pointerassignment(); } void assignment() { // #8631 check("using CharArray = char[16];\n" "void f() {\n" " CharArray foo = \"\";\n" "}"); ASSERT_EQUALS("", errout.str()); } void novardecl() { // if the variable declarations can't be seen then skip the warning check("void foo()\n" "{\n" " a = p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void functionpar() { check("int foo(int *p)\n" "{\n" " int a = p;\n" " return a + 4;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout.str()); check("int foo(int p[])\n" "{\n" " int a = p;\n" " return a + 4;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout.str()); check("int foo(int p[])\n" "{\n" " int *a = p;\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout.str()); check("void foo(int x)\n" "{\n" " int *p = x;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning an integer to a pointer is not portable.\n", errout.str()); check("int f(const char *p) {\n" // #4659 " return 6 + p[2] * 256;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(int *p) {\n" // #6096 " bool a = p;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::array f();\n" "void g() {\n" " std::array a = f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::array f(int x);\n" "void g(int i) {\n" " std::array a = f(i);\n" "}"); ASSERT_EQUALS("", errout.str()); check("typedef std::array Array;\n" "Array f(int x);\n" "void g(int i) {\n" " Array a = f(i);\n" "}"); ASSERT_EQUALS("", errout.str()); check("typedef std::array Array;\n" "Array f();\n" "void g(int i) {\n" " Array a = f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" // #9951 " enum E { E0 };\n" " std::array g(S::E);\n" "};\n" "void f() {\n" " std::array a = S::g(S::E::E0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void structmember() { check("struct Foo { int *p; };\n" "void f(struct Foo *foo) {\n" " int i = foo->p;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout.str()); } void ptrcompare() { // Ticket #2892 check("void foo(int *p) {\n" " int a = (p != NULL);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ptrarithmetic() { // #3073 check("void foo(int *p) {\n" " int x = 10;\n" " int *a = p + x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p) {\n" " int x = 10;\n" " int *a = x + p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p) {\n" " int x = 10;\n" " int *a = x * x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning an integer to a pointer is not portable.\n", errout.str()); check("void foo(int *start, int *end) {\n" " int len;\n" " int len = end + 10 - start;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnIssues() { check("void* foo(int i) {\n" " return i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an integer in a function with pointer return type is not portable.\n", errout.str()); check("void* foo(int* i) {\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* foo() {\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(int i) {\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo {};\n" "\n" "int* dostuff(Foo foo) {\n" " return foo;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(char* c) {\n" " return c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout.str()); check("int foo(char* c) {\n" " return 1+c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout.str()); check("std::string foo(char* c) {\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(char *a, char *b) {\n" // #4486 " return a + 1 - b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s {\n" // 4642 " int i;\n" "};\n" "int func(struct s *p) {\n" " return 1 + p->i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void __iomem *f(unsigned int port_no) {\n" " void __iomem *mmio = hpriv->mmio;\n" " return mmio + (port_no * 0x80);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7247: don't check return statements in nested functions.. check("int foo() {\n" " struct {\n" " const char * name() { return \"abc\"; }\n" " } table;\n" "}"); ASSERT_EQUALS("", errout.str()); // #7451: Lambdas check("const int* test(std::vector outputs, const std::string& text) {\n" " auto it = std::find_if(outputs.begin(), outputs.end(),\n" " [&](int ele) { return \"test\" == text; });\n" " return nullptr;\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(Test64BitPortability) cppcheck-2.7/test/testassert.cpp000066400000000000000000000204011417746362400170410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkassert.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestAssert : public TestFixture { public: TestAssert() : TestFixture("TestAssert") {} private: Settings settings; #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], const char *filename = "test.cpp") { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); // Check.. CheckAssert checkAssert; checkAssert.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { settings.severity.enable(Severity::warning); TEST_CASE(assignmentInAssert); TEST_CASE(functionCallInAssert); TEST_CASE(memberFunctionCallInAssert); TEST_CASE(safeFunctionCallInAssert); TEST_CASE(crash); } void safeFunctionCallInAssert() { check( "int a;\n" "bool b = false;\n" "int foo() {\n" " if (b) { a = 1+2 };\n" " return a;\n" "}\n" "assert(foo() == 3);"); ASSERT_EQUALS("", errout.str()); check( "int foo(int a) {\n" " int b=a+1;\n" " return b;\n" "}\n" "assert(foo(1) == 2);"); ASSERT_EQUALS("", errout.str()); } void functionCallInAssert() { check( "int a;\n" "int foo() {\n" " a = 1+2;\n" " return a;\n" "}\n" "assert(foo() == 3);"); ASSERT_EQUALS("[test.cpp:6]: (warning) Assert statement calls a function which may have desired side effects: 'foo'.\n", errout.str()); // Ticket #4937 "false positive: Assert calls a function which may have desired side effects" check("struct SquarePack {\n" " static bool isRank1Or8( Square sq ) {\n" " sq &= 0x38;\n" " return sq == 0 || sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct SquarePack {\n" " static bool isRank1Or8( Square &sq ) {\n" " sq &= 0x38;\n" " return sq == 0 || sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (warning) Assert statement calls a function which may have desired side effects: 'isRank1Or8'.\n", errout.str()); check("struct SquarePack {\n" " static bool isRank1Or8( Square *sq ) {\n" " *sq &= 0x38;\n" " return *sq == 0 || *sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (warning) Assert statement calls a function which may have desired side effects: 'isRank1Or8'.\n", errout.str()); check("struct SquarePack {\n" " static bool isRank1Or8( Square *sq ) {\n" " sq &= 0x38;\n" " return sq == 0 || sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}"); ASSERT_EQUALS("", errout.str()); } void memberFunctionCallInAssert() { check("struct SquarePack {\n" " void Foo();\n" "};\n" "void foo(SquarePack s) {\n" " assert( s.Foo() );\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Assert statement calls a function which may have desired side effects: 'Foo'.\n", errout.str()); check("struct SquarePack {\n" " void Foo() const;\n" "};\n" "void foo(SquarePack* s) {\n" " assert( s->Foo() );\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct SquarePack {\n" " static void Foo();\n" "};\n" "void foo(SquarePack* s) {\n" " assert( s->Foo() );\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct SquarePack {\n" "};\n" "void foo(SquarePack* s) {\n" " assert( s->Foo() );\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignmentInAssert() { check("void f() {\n" " int a; a = 0;\n" " assert(a = 2);\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f(int a) {\n" " assert(a == 2);\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int b) {\n" " assert(a == 2 && (b = 1));\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assert statement modifies 'b'.\n", errout.str()); check("void f() {\n" " int a; a = 0;\n" " assert(a += 2);\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " int a; a = 0;\n" " assert(a *= 2);\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " int a; a = 0;\n" " assert(a -= 2);\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " int a = 0;\n" " assert(a--);\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " int a = 0;\n" " assert(--a);\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " assert(std::all_of(first, last, []() {\n" " auto tmp = x.someValue();\n" " auto const expected = someOtherValue;\n" " return tmp == expected;\n" " }));\n" "}"); ASSERT_EQUALS("", errout.str()); } void crash() { check("void foo() {\n" " assert(sizeof(struct { int a[x++]; })==sizeof(int));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // #9790 " assert(kad_bucket_hash(&(kad_guid) { .bytes = { 0 } }, & (kad_guid){.bytes = { 0 }}) == -1);\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestAssert) cppcheck-2.7/test/testastutils.cpp000066400000000000000000000463401417746362400174220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "astutils.h" #include "config.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include class TestAstUtils : public TestFixture { public: TestAstUtils() : TestFixture("TestAstUtils") {} private: void run() OVERRIDE { TEST_CASE(findLambdaEndTokenTest); TEST_CASE(findLambdaStartTokenTest); TEST_CASE(isNullOperandTest); TEST_CASE(isReturnScopeTest); TEST_CASE(isSameExpressionTest); TEST_CASE(isVariableChangedTest); TEST_CASE(isVariableChangedByFunctionCallTest); TEST_CASE(nextAfterAstRightmostLeafTest); TEST_CASE(isUsedAsBool); } #define findLambdaEndToken(code) findLambdaEndToken_(code, __FILE__, __LINE__) bool findLambdaEndToken_(const char code[], const char* file, int line) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); const Token * const tokEnd = (::findLambdaEndToken)(tokenizer.tokens()); return tokEnd && tokEnd->next() == nullptr; } void findLambdaEndTokenTest() { const Token* nullTok = nullptr; ASSERT(nullptr == (::findLambdaEndToken)(nullTok)); ASSERT_EQUALS(false, findLambdaEndToken("void f() { }")); ASSERT_EQUALS(true, findLambdaEndToken("[]{ }")); ASSERT_EQUALS(true, findLambdaEndToken("[]{ return 0; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](){ }")); ASSERT_EQUALS(true, findLambdaEndToken("[&](){ }")); ASSERT_EQUALS(true, findLambdaEndToken("[&, i](){ }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) { return a + b; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) mutable { return a + b; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) constexpr { return a + b; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> int { return -1; }")); ASSERT_EQUALS(false, findLambdaEndToken("[](void) foo -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int* { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const ** int { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * const* int { return x; }")); } #define findLambdaStartToken(code) findLambdaStartToken_(code, __FILE__, __LINE__) bool findLambdaStartToken_(const char code[], const char* file, int line) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); const Token * const tokStart = (::findLambdaStartToken)(tokenizer.list.back()); return tokStart && tokStart == tokenizer.list.front(); } void findLambdaStartTokenTest() { ASSERT(nullptr == (::findLambdaStartToken)(nullptr)); ASSERT_EQUALS(false, findLambdaStartToken("void f() { }")); ASSERT_EQUALS(true, findLambdaStartToken("[]{ }")); ASSERT_EQUALS(true, findLambdaStartToken("[]{ return 0; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](){ }")); ASSERT_EQUALS(true, findLambdaStartToken("[&](){ }")); ASSERT_EQUALS(true, findLambdaStartToken("[&, i](){ }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) { return a + b; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) mutable { return a + b; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) constexpr { return a + b; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) mutable -> int { return -1; }")); ASSERT_EQUALS(false, findLambdaStartToken("[](void) foo -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> int* { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) mutable -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const ** int { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const * const* int { return x; }")); } #define isNullOperand(code) isNullOperand_(code, __FILE__, __LINE__) bool isNullOperand_(const char code[], const char* file, int line) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); return (::isNullOperand)(tokenizer.tokens()); } void isNullOperandTest() { ASSERT_EQUALS(true, isNullOperand("(void*)0;")); ASSERT_EQUALS(true, isNullOperand("(void*)0U;")); ASSERT_EQUALS(true, isNullOperand("(void*)0x0LL;")); ASSERT_EQUALS(true, isNullOperand("NULL;")); ASSERT_EQUALS(true, isNullOperand("nullptr;")); ASSERT_EQUALS(true, isNullOperand("(void*)NULL;")); ASSERT_EQUALS(true, isNullOperand("static_cast(0);")); ASSERT_EQUALS(false, isNullOperand("0;")); ASSERT_EQUALS(false, isNullOperand("(void*)0.0;")); ASSERT_EQUALS(false, isNullOperand("(void*)1;")); } #define isReturnScope(code, offset) isReturnScope_(code, offset, __FILE__, __LINE__) bool isReturnScope_(const char code[], int offset, const char* file, int line) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); const Token * const tok = (offset < 0) ? tokenizer.list.back()->tokAt(1+offset) : tokenizer.tokens()->tokAt(offset); return (::isReturnScope)(tok); } void isReturnScopeTest() { ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { return; } }", -2)); ASSERT_EQUALS(true, isReturnScope("int f() { if (a) { return {}; } }", -2)); // #8891 ASSERT_EQUALS(true, isReturnScope("std::string f() { if (a) { return std::string{}; } }", -2)); // #8891 ASSERT_EQUALS(true, isReturnScope("std::string f() { if (a) { return std::string{\"\"}; } }", -2)); // #8891 ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { return (ab){0}; } }", -2)); // #7103 ASSERT_EQUALS(false, isReturnScope("void f() { if (a) { return (ab){0}; } }", -4)); // #7103 ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { {throw new string(x);}; } }", -4)); // #7144 ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { {throw new string(x);}; } }", -2)); // #7144 ASSERT_EQUALS(false, isReturnScope("void f() { [=]() { return data; }; }", -1)); ASSERT_EQUALS(true, isReturnScope("auto f() { return [=]() { return data; }; }", -1)); ASSERT_EQUALS(true, isReturnScope("auto f() { return [=]() { return data; }(); }", -1)); ASSERT_EQUALS(false, isReturnScope("auto f() { [=]() { return data; }(); }", -1)); ASSERT_EQUALS(true, isReturnScope("void negativeTokenOffset() { return; }", -1)); ASSERT_EQUALS(false, isReturnScope("void zeroTokenOffset() { return; }", 0)); ASSERT_EQUALS(true, isReturnScope("void positiveTokenOffset() { return; }", 7)); } #define isSameExpression(code, tokStr1, tokStr2) isSameExpression_(code, tokStr1, tokStr2, __FILE__, __LINE__) bool isSameExpression_(const char code[], const char tokStr1[], const char tokStr2[], const char* file, int line) { Settings settings; Library library; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); tokenizer.simplifyTokens1(""); const Token * const tok1 = Token::findsimplematch(tokenizer.tokens(), tokStr1, strlen(tokStr1)); const Token * const tok2 = Token::findsimplematch(tok1->next(), tokStr2, strlen(tokStr2)); return (::isSameExpression)(false, false, tok1, tok2, library, false, true, nullptr); } void isSameExpressionTest() { ASSERT_EQUALS(true, isSameExpression("x = 1 + 1;", "1", "1")); ASSERT_EQUALS(false, isSameExpression("x = 1 + 1u;", "1", "1u")); ASSERT_EQUALS(true, isSameExpression("x = 1.0 + 1.0;", "1.0", "1.0")); ASSERT_EQUALS(false, isSameExpression("x = 1.0f + 1.0;", "1.0f", "1.0")); ASSERT_EQUALS(false, isSameExpression("x = 1L + 1;", "1L", "1")); ASSERT_EQUALS(true, isSameExpression("x = 0.0f + 0x0p+0f;", "0.0f", "0x0p+0f")); ASSERT_EQUALS(true, isSameExpression("x < x;", "x", "x")); ASSERT_EQUALS(false, isSameExpression("x < y;", "x", "y")); ASSERT_EQUALS(true, isSameExpression("(x + 1) < (x + 1);", "+", "+")); ASSERT_EQUALS(false, isSameExpression("(x + 1) < (x + 1L);", "+", "+")); ASSERT_EQUALS(true, isSameExpression("(1 + x) < (x + 1);", "+", "+")); ASSERT_EQUALS(false, isSameExpression("(1.0l + x) < (1.0 + x);", "+", "+")); ASSERT_EQUALS(true, isSameExpression("(0.0 + x) < (x + 0x0p+0);", "+", "+")); ASSERT_EQUALS(true, isSameExpression("void f() {double y = 1e1; (x + y) < (x + 10.0); } ", "+", "+")); ASSERT_EQUALS(true, isSameExpression("void f() {double y = 1e1; (x + 10.0) < (y + x); } ", "+", "+")); ASSERT_EQUALS(true, isSameExpression("void f() {double y = 1e1; double z = 10.0; (x + y) < (x + z); } ", "+", "+")); ASSERT_EQUALS(true, isSameExpression("A + A", "A", "A")); //https://trac.cppcheck.net/ticket/9700 ASSERT_EQUALS(true, isSameExpression("A::B + A::B;", "::", "::")); ASSERT_EQUALS(false, isSameExpression("A::B + A::C;", "::", "::")); ASSERT_EQUALS(true, isSameExpression("A::B* get() { if(x) return new A::B(true); else return new A::B(true); }", "new", "new")); ASSERT_EQUALS(false, isSameExpression("A::B* get() { if(x) return new A::B(true); else return new A::C(true); }", "new", "new")); ASSERT_EQUALS(true, true); } #define isVariableChanged(code, startPattern, endPattern) isVariableChanged_(code, startPattern, endPattern, __FILE__, __LINE__) bool isVariableChanged_(const char code[], const char startPattern[], const char endPattern[], const char* file, int line) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); const Token * const tok1 = Token::findsimplematch(tokenizer.tokens(), startPattern, strlen(startPattern)); const Token * const tok2 = Token::findsimplematch(tokenizer.tokens(), endPattern, strlen(endPattern)); return (::isVariableChanged)(tok1, tok2, 1, false, &settings, true); } void isVariableChangedTest() { // #8211 - no lhs for >> , do not crash isVariableChanged("void f() {\n" " int b;\n" " if (b) { (int)((INTOF(8))result >> b); }\n" "}", "if", "}"); // #9235 ASSERT_EQUALS(true, isVariableChanged("void f() {\n" " int &a = a;\n" "}\n", "= a", "}")); } #define isVariableChangedByFunctionCall(code, pattern, inconclusive) isVariableChangedByFunctionCall_(code, pattern, inconclusive, __FILE__, __LINE__) bool isVariableChangedByFunctionCall_(const char code[], const char pattern[], bool *inconclusive, const char* file, int line) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); const Token * const argtok = Token::findmatch(tokenizer.tokens(), pattern); return (::isVariableChangedByFunctionCall)(argtok, 0, &settings, inconclusive); } void isVariableChangedByFunctionCallTest() { const char *code; bool inconclusive; // #8271 - template method code = "void f(int x) {\n" " a(x);\n" "}"; inconclusive = false; ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); ASSERT_EQUALS(true, inconclusive); code = "int f(int x) {\n" "return int(x);\n" "}\n"; ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); TODO_ASSERT_EQUALS(false, true, inconclusive); } #define nextAfterAstRightmostLeaf(code, parentPattern, rightPattern) nextAfterAstRightmostLeaf_(code, parentPattern, rightPattern, __FILE__, __LINE__) bool nextAfterAstRightmostLeaf_(const char code[], const char parentPattern[], const char rightPattern[], const char* file, int line) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); const Token * tok = Token::findsimplematch(tokenizer.tokens(), parentPattern, strlen(parentPattern)); return Token::simpleMatch((::nextAfterAstRightmostLeaf)(tok), rightPattern, strlen(rightPattern)); } void nextAfterAstRightmostLeafTest() { ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f(int a, int b) { int x = a + b; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a); }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b]; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b]); }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b] + a); }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f() { int a; int b; int x = [](int a){}; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = a + b; }", "+", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "+", "] ; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a + 1)[b]; }", "+", ") [")); } enum class Result {False, True, Fail}; Result isUsedAsBool(const char code[], const char pattern[]) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); if (!tokenizer.tokenize(istr, "test.cpp")) return Result::Fail; const Token * const argtok = Token::findmatch(tokenizer.tokens(), pattern); if (!argtok) return Result::Fail; return ::isUsedAsBool(argtok) ? Result::True : Result::False; } void isUsedAsBool() { ASSERT(Result::True == isUsedAsBool("void f() { bool b = true; }", "b")); ASSERT(Result::False ==isUsedAsBool("void f() { int i = true; }", "i")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (i) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; while (i) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; for (;i;) {} }", "i ; )")); ASSERT(Result::False == isUsedAsBool("void f() { int i; for (;;i) {} }", "i )")); ASSERT(Result::False == isUsedAsBool("void f() { int i; for (i;;) {} }", "i ; ; )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; for (int j=0; i; ++j) {} }", "i ; ++")); ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i == 2) {} }", "i ==")); ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i == true) {} }", "i ==")); ASSERT(Result::False == isUsedAsBool("void f() { int i,j; if (i == (j&&f())) {} }", "i ==")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (!i == 0) {} }", "i ==")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (!i) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (!!i) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (i && f()) {} }", "i &&")); ASSERT(Result::True == isUsedAsBool("void f() { int i; int j = i && f(); }", "i &&")); ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i & f()) {} }", "i &")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (static_cast(i)) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if ((bool)i) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (1+static_cast(i)) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (1+(bool)i) {} }", "i )")); ASSERT(Result::False == isUsedAsBool("void f() { int i; if (1+static_cast(i)) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int i; if (1+!static_cast(i)) {} }", "i )")); ASSERT(Result::False == isUsedAsBool("void f() { int i; if (1+(int)i) {} }", "i )")); ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i + 2) {} }", "i +")); ASSERT(Result::True == isUsedAsBool("void f() { int i; bool b = i; }", "i ; }")); ASSERT(Result::True == isUsedAsBool("void f(bool b); void f() { int i; f(i); }","i )")); ASSERT(Result::True == isUsedAsBool("void f() { int *i; if (*i) {} }", "i )")); ASSERT(Result::True == isUsedAsBool("void f() { int *i; if (*i) {} }", "* i )")); } }; REGISTER_TEST(TestAstUtils) cppcheck-2.7/test/testautovariables.cpp000066400000000000000000004017021417746362400204100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkautovariables.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestAutoVariables : public TestFixture { public: TestAutoVariables() : TestFixture("TestAutoVariables") {} private: Settings settings; #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], bool inconclusive = false, const char* filename = "test.cpp") { // Clear the error buffer.. errout.str(""); settings.certainty.setEnabled(Certainty::inconclusive, inconclusive); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); CheckAutoVariables checkAutoVariables; checkAutoVariables.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { settings.severity.enable(Severity::warning); settings.severity.enable(Severity::style); LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "qt.cfg"); TEST_CASE(testautovar1); TEST_CASE(testautovar2); TEST_CASE(testautovar3); // ticket #2925 TEST_CASE(testautovar4); // ticket #2928 TEST_CASE(testautovar5); // ticket #2926 TEST_CASE(testautovar6); // ticket #2931 TEST_CASE(testautovar7); // ticket #3066 TEST_CASE(testautovar8); TEST_CASE(testautovar9); TEST_CASE(testautovar10); // ticket #2930 - void f(char *p) { p = '\0'; } TEST_CASE(testautovar11); // ticket #4641 - fp, assign local struct member address to function parameter TEST_CASE(testautovar12); // ticket #5024 - crash TEST_CASE(testautovar13); // ticket #5537 - crash TEST_CASE(testautovar14); // ticket #4776 - assignment of function parameter, goto TEST_CASE(testautovar15); // ticket #6538 TEST_CASE(testautovar16); // ticket #8114 TEST_CASE(testautovar_array1); TEST_CASE(testautovar_array2); TEST_CASE(testautovar_normal); // "normal" token list that does not remove casts etc TEST_CASE(testautovar_ptrptr); // ticket #6956 TEST_CASE(testautovar_return1); TEST_CASE(testautovar_return2); TEST_CASE(testautovar_return3); TEST_CASE(testautovar_return4); TEST_CASE(testautovar_extern); TEST_CASE(testautovar_reassigned); TEST_CASE(testinvaliddealloc); TEST_CASE(testinvaliddealloc_input); // Ticket #10600 TEST_CASE(testinvaliddealloc_string); TEST_CASE(testinvaliddealloc_C); TEST_CASE(testassign1); // Ticket #1819 TEST_CASE(testassign2); // Ticket #2765 TEST_CASE(assignAddressOfLocalArrayToGlobalPointer); TEST_CASE(assignAddressOfLocalVariableToGlobalPointer); TEST_CASE(assignAddressOfLocalVariableToMemberVariable); TEST_CASE(returnLocalVariable1); TEST_CASE(returnLocalVariable2); TEST_CASE(returnLocalVariable3); // &x[0] TEST_CASE(returnLocalVariable4); // x+y TEST_CASE(returnLocalVariable5); // cast TEST_CASE(returnLocalVariable6); // valueflow // return reference.. TEST_CASE(returnReference1); TEST_CASE(returnReference2); TEST_CASE(returnReference3); TEST_CASE(returnReference4); TEST_CASE(returnReference5); TEST_CASE(returnReference6); TEST_CASE(returnReference7); TEST_CASE(returnReference8); TEST_CASE(returnReference9); TEST_CASE(returnReference10); TEST_CASE(returnReference11); TEST_CASE(returnReference12); TEST_CASE(returnReference13); TEST_CASE(returnReference14); TEST_CASE(returnReference15); // #9432 TEST_CASE(returnReference16); // #9433 TEST_CASE(returnReference16); // #9433 TEST_CASE(returnReference17); // #9461 TEST_CASE(returnReference18); // #9482 TEST_CASE(returnReference19); // #9597 TEST_CASE(returnReference20); // #9536 TEST_CASE(returnReference21); // #9530 TEST_CASE(returnReference22); TEST_CASE(returnReference23); TEST_CASE(returnReference24); // #10098 TEST_CASE(returnReferenceFunction); TEST_CASE(returnReferenceContainer); TEST_CASE(returnReferenceLiteral); TEST_CASE(returnReferenceCalculation); TEST_CASE(returnReferenceLambda); TEST_CASE(returnReferenceInnerScope); TEST_CASE(returnReferenceRecursive); TEST_CASE(extendedLifetime); TEST_CASE(danglingReference); TEST_CASE(danglingTempReference); // global namespace TEST_CASE(testglobalnamespace); TEST_CASE(returnParameterAddress); TEST_CASE(testconstructor); // ticket #5478 - crash TEST_CASE(variableIsUsedInScope); // ticket #5599 crash in variableIsUsedInScope() TEST_CASE(danglingLifetimeLambda); TEST_CASE(danglingLifetimeContainer); TEST_CASE(danglingLifetimeContainerView); TEST_CASE(danglingLifetimeUniquePtr); TEST_CASE(danglingLifetime); TEST_CASE(danglingLifetimeFunction); TEST_CASE(danglingLifetimeAggegrateConstructor); TEST_CASE(danglingLifetimeInitList); TEST_CASE(danglingLifetimeImplicitConversion); TEST_CASE(danglingTemporaryLifetime); TEST_CASE(danglingLifetimeBorrowedMembers); TEST_CASE(danglingLifetimeClassMemberFunctions); TEST_CASE(invalidLifetime); TEST_CASE(deadPointer); TEST_CASE(splitNamespaceAuto); // crash #10473 } void testautovar1() { check("void func1(int **res)\n" "{\n" " int num = 2;\n" " *res = #\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("void func1(int **res)\n" "{\n" " int num = 2;\n" " res = #\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void func1(int **res)\n" "{\n" " int num = 2;\n" " foo.res = #\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar2() { check("class Fred {\n" " void func1(int **res);\n" "}\n" "void Fred::func1(int **res)\n" "{\n" " int num = 2;\n" " *res = #\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("class Fred {\n" " void func1(int **res);\n" "}\n" "void Fred::func1(int **res)\n" "{\n" " int num = 2;\n" " res = #\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("class Fred {\n" " void func1(int **res);\n" "}\n" "void Fred::func1(int **res)\n" "{\n" " int num = 2;\n" " foo.res = #\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar3() { // ticket #2925 check("void foo(int **p)\n" "{\n" " int x[100];\n" " *p = x;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar4() { // ticket #2928 check("void foo(int **p)\n" "{\n" " static int x[100];\n" " *p = x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar5() { // ticket #2926 check("void foo(struct AB *ab)\n" "{\n" " char a;\n" " ab->a = &a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar6() { // ticket #2931 check("void foo(struct X *x)\n" "{\n" " char a[10];\n" " x->str = a;\n" "}", false); ASSERT_EQUALS("", errout.str()); check("void foo(struct X *x)\n" "{\n" " char a[10];\n" " x->str = a;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar7() { // ticket #3066 check("struct txt_scrollpane_s * TXT_NewScrollPane(struct txt_widget_s * target)\n" "{\n" " struct txt_scrollpane_s * scrollpane;\n" " target->parent = &scrollpane->widget;\n" " return scrollpane;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void testautovar8() { check("void foo(int*& p) {\n" " int i = 0;\n" " p = &i;\n" "}", false); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("void foo(std::string& s) {\n" " s = foo;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void testautovar9() { check("struct FN {int i;};\n" "struct FP {FN* f};\n" "void foo(int*& p, FN* p_fp) {\n" " FN fn;\n" " FP fp;\n" " p = &fn.i;\n" "}", false); ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("struct FN {int i;};\n" "struct FP {FN* f};\n" "void foo(int*& p, FN* p_fp) {\n" " FN fn;\n" " FP fp;\n" " p = &p_fp->i;\n" "}", false); ASSERT_EQUALS("", errout.str()); check("struct FN {int i;};\n" "struct FP {FN* f};\n" "void foo(int*& p, FN* p_fp) {\n" " FN fn;\n" " FP fp;\n" " p = &fp.f->i;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void testautovar10() { // #2930 - assignment of function parameter check("void foo(char* p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void foo(int b) {\n" " b = foo(b);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(int b) {\n" " b += 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(std::string s) {\n" " s = foo(b);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(char* p) {\n" // don't warn for self assignment, there is another warning for this " p = p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " if (!p) p = buf;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " if (!p) p = buf;\n" " do_something(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " while (!p) p = buf;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " p = 0;\n" " asm(\"somecmd\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Foo* p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("class Foo {};\n" "void foo(Foo p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(Foo p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("double foo(double d) {\n" // #5005 " int i = d;\n" " d = i;\n" " return d;" "}",false); ASSERT_EQUALS("", errout.str()); check("void foo(int* ptr) {\n" // #4793 " ptr++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void foo(int* ptr) {\n" // #3177 " --ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void foo(struct S* const x) {\n" // #7839 " ++x->n;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar11() { // #4641 - fp, assign local struct member address to function parameter check("struct A {\n" " char (*data)[10];\n" "};\n" "void foo(char** p) {\n" " struct A a = bar();\n" " *p = &(*a.data)[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " char data[10];\n" "};\n" "void foo(char** p) {\n" " struct A a = bar();\n" " *p = &a.data[0];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("void f(char **out) {\n" " struct S *p = glob;\n" " *out = &p->data;\n" "}"); ASSERT_EQUALS("", errout.str()); // #4998 check("void f(s8**out) {\n" " s8 *p;\n" // <- p is pointer => no error " *out = &p[1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(s8**out) {\n" " s8 p[10];\n" // <- p is array => error " *out = &p[1];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar12() { // Ticket #5024, #5050 - Crash on invalid input ASSERT_THROW(check("void f(int* a) { a = }"), InternalError); check("struct custom_type { custom_type(int) {} };\n" "void func(int) {}\n" "int var;\n" "void init() { func(var); }\n" "UNKNOWN_MACRO_EXPANDING_TO_SIGNATURE { custom_type a(var); }"); } void testautovar13() { // Ticket #5537 check("class FileManager {\n" " FileManager() : UniqueRealDirs(*new UniqueDirContainer())\n" " {}\n" " ~FileManager() {\n" " delete &UniqueRealDirs;\n" " }\n" "};"); } void testautovar14() { // Ticket #4776 check("void f(int x) {\n" "label:" " if (x>0) {\n" " x = x >> 1;\n" " goto label;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar15() { // Ticket #6538 check("static const float4 darkOutline(0.05f, 0.05f, 0.05f, 0.95f);\n" "static const float darkLuminosity = 0.05 +\n" " 0.0722f * math::powf(darkOutline[2], 2.2);\n" "const float4* ChooseOutlineColor(const float4& textColor) {\n" " const float lumdiff = something;\n" " if (lumdiff > 5.0f)\n" " return &darkOutline;\n" " return 0;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void testautovar16() { // Ticket #8114 check("void f(const void* ptr, bool* result) {\n" " int dummy;\n" " *result = (&dummy < ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_array1() { check("void func1(int* arr[2])\n" "{\n" " int num=2;" " arr[0]=#\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_array2() { check("class Fred {\n" " void func1(int* arr[2]);\n" "}\n" "void Fred::func1(int* arr[2])\n" "{\n" " int num=2;" " arr[0]=#\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_normal() { check("void f(XmDestinationCallbackStruct *ds)\n" "{\n" " XPoint DropPoint;\n" " ds->location_data = (XtPointer *)&DropPoint;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_ptrptr() { // #6596 check("void remove_duplicate_matches (char **matches) {\n" " char dead_slot;\n" " matches[0] = (char *)&dead_slot;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_return1() { check("int* func1()\n" "{\n" " int num=2;" " return #" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:3]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout.str()); } void testautovar_return2() { check("class Fred {\n" " int* func1();\n" "}\n" "int* Fred::func1()\n" "{\n" " int num=2;" " return #" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout.str()); } void testautovar_return3() { // #2975 - FP check("void** f()\n" "{\n" " void *&value = tls[id];" " return &value;" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_return4() { // #8058 - FP ignore return in lambda check("void foo() {\n" " int cond2;\n" " dostuff([&cond2]() { return &cond2; });\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_extern() { check("struct foo *f()\n" "{\n" " extern struct foo f;\n" " return &f;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_reassigned() { check("void foo(cb* pcb) {\n" " int root0;\n" " pcb->root0 = &root0;\n" " dostuff(pcb);\n" " pcb->root0 = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(cb* pcb) {\n" " int root0;\n" " pcb->root0 = &root0;\n" " dostuff(pcb);\n" " if (condition) return;\n" // <- not reassigned => error " pcb->root0 = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("void foo(cb* pcb) {\n" " int root0;\n" " pcb->root0 = &root0;\n" " dostuff(pcb);\n" " if (condition)\n" " pcb->root0 = 0;\n" // <- conditional reassign => error "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testinvaliddealloc() { check("void func1() {\n" " char tmp1[256];\n" " free(tmp1);\n" " char tmp2[256];\n" " delete tmp2;\n" " char tmp3[256];\n" " delete tmp3;\n" " char tmp4[256];\n" " delete[] (tmp4);\n" " char tmp5[256];\n" " delete[] tmp5;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:5]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:7]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:9]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:11]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("void func1(char * ptr) {\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void func1() {\n" " char* tmp1[256];\n" " init(tmp1);\n" " delete tmp1[34];\n" "}"); ASSERT_EQUALS("", errout.str()); check("void func1() {\n" " static char tmp1[256];\n" " char *p = tmp1;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of a static variable (tmp1) results in undefined behaviour.\n", errout.str()); check("char tmp1[256];\n" "void func1() {\n" " char *p; if (x) p = tmp1;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of a global variable (tmp1) results in undefined behaviour.\n", errout.str()); check("void f()\n" "{\n" " char psz_title[10];\n" " {\n" " char *psz_title = 0;\n" " abc(0, psz_title);\n" " free(psz_title);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #2298 new check: passing stack-address to free() check("int main() {\n" " int *p = malloc(4);\n" " free(&p);\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("int main() {\n" " int i;\n" " free(&i);\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); // #5732 check("int main() {\n" " long (*pKoeff)[256] = new long[9][256];\n" " delete[] pKoeff;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main() {\n" " long *pKoeff[256];\n" " delete[] pKoeff;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("int main() {\n" " long *pKoeff[256];\n" " free (pKoeff);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("void foo() {\n" " const intPtr& intref = Getter();\n" " delete intref;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " MyObj& obj = *new MyObj;\n" " delete &obj;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6506 check("struct F {\n" " void free(void*) {}\n" "};\n" "void foo() {\n" " char c1[1];\n" " F().free(c1);\n" " char *c2 = 0;\n" " F().free(&c2);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class foo {\n" " void free(void* );\n" " void someMethod() {\n" " char **dst_copy = NULL;\n" " free(&dst_copy);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // #6551 check("bool foo( ) {\n" " SwTxtFld * pTxtFld = GetFldTxtAttrAt();\n" " delete static_cast(&pTxtFld->GetAttr());\n" "}"); ASSERT_EQUALS("", errout.str()); // #8910 check("void f() {\n" " char stack[512];\n" " RGNDATA *data;\n" " if (data_size > sizeof (stack)) data = malloc (data_size);\n" " else data = (RGNDATA *)stack;\n" " if ((char *)data != stack) free (data);\n" "}"); ASSERT_EQUALS("", errout.str()); // #8923 check("void f(char **args1, char *args2[]) {\n" " free((char **)args1);\n" " free((char **)args2);\n" "}"); ASSERT_EQUALS("", errout.str()); // #10097 check("struct Array {\n" " ~Array() { delete m_Arr; }\n" " std::array* m_Arr{};\n" "};\n" "Array arr;\n"); ASSERT_EQUALS("", errout.str()); } void testinvaliddealloc_input() { // #10600 check("void f(int* a[]) {\n" " free(a);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int a[]) {\n" " free(a);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int* a[]) {\n" " int * p = *a;\n" " free(p);\n" " int ** q = a;\n" " free(q);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int a[]) {\n" " int * p = a;\n" " free(p);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testinvaliddealloc_string() { // #7341 check("void f() {\n" " char *ptr = \"a\";\n" " free(\"a\");\n" " delete \"a\";\n" " free(ptr);\n" " delete ptr;\n" " char * p = malloc(1000);\n" " p = \"abc\";\n" " free(p);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of a string literal results in undefined behaviour.\n" "[test.cpp:4]: (error) Deallocation of a string literal results in undefined behaviour.\n" "[test.cpp:5]: (error) Deallocation of a pointer pointing to a string literal (\"a\") results in undefined behaviour.\n" "[test.cpp:6]: (error) Deallocation of a pointer pointing to a string literal (\"a\") results in undefined behaviour.\n" "[test.cpp:9]: (error) Deallocation of a pointer pointing to a string literal (\"abc\") results in undefined behaviour.\n", errout.str()); check("void f() {\n" " char *ptr = malloc(10);\n" " char *empty_str = \"\";\n" " if (ptr == NULL)\n" " ptr = empty_str;\n" " if (ptr != empty_str)\n" " free(ptr);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testinvaliddealloc_C() { // #5691 check("void svn_repos_dir_delta2() {\n" " struct context c;\n" " SVN_ERR(delete(&c, root_baton, src_entry, pool));\n" "}\n", false, "test.c"); ASSERT_EQUALS("", errout.str()); } void testassign1() { // Ticket #1819 check("void f(EventPtr *eventP, ActionPtr **actionsP) {\n" " EventPtr event = *eventP;\n" " *actionsP = &event->actions;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testassign2() { // Ticket #2765 check("static void function(unsigned long **datap) {\n" " struct my_s *mr = global_structure_pointer;\n" " *datap = &mr->value;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignAddressOfLocalArrayToGlobalPointer() { check("int *p;\n" "void f() {\n" " int x[10];\n" " p = x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'p' will use pointer to local variable 'x'.\n", errout.str()); check("int *p;\n" "void f() {\n" " int x[10];\n" " p = x;\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignAddressOfLocalVariableToGlobalPointer() { check("int *p;\n" "void f() {\n" " int x;\n" " p = &x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'p' will use pointer to local variable 'x'.\n", errout.str()); check("int *p;\n" "void f() {\n" " int x;\n" " p = &x;\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignAddressOfLocalVariableToMemberVariable() { check("struct A {\n" " void f() {\n" " int x;\n" " ptr = &x;\n" " }\n" " int *ptr;\n" "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'ptr' will use pointer to local variable 'x'.\n", errout.str()); check("struct A {\n" " void f() {\n" " int x;\n" " ptr = &x;\n" " ptr = 0;\n" " }\n" " int *ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); } void returnLocalVariable1() { check("char *foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str()); check("char *foo()\n" // use ValueFlow "{\n" " char str[100] = {0};\n" " char *p = str;\n" " return p;\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str()); check("class Fred {\n" " char *foo();\n" "};\n" "char *Fred::foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str()); check("char * format_reg(char *outbuffer_start) {\n" " return outbuffer_start;\n" "}\n" "void print_with_operands() {\n" " char temp[42];\n" " char *tp = temp;\n" " tp = format_reg(tp);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnLocalVariable2() { check("std::string foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " std::string foo();\n" "};\n" "std::string Fred::foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnLocalVariable3() { // &x[..] // #3030 check("char *foo() {\n" " char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'q' that will be invalid when returning.\n", errout.str()); check("char *foo()\n" "{\n" " static char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *foo()\n" "{\n" "char q[] = \"AAAAAAAAAAAA\";\n" "char *p;\n" "p = &q[1];\n" "return p;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3] -> [test.cpp:6]: (error) Returning pointer to local variable 'q' that will be invalid when returning.\n", errout.str()); } void returnLocalVariable4() { // x+y check("char *foo() {\n" " char x[10] = {0};\n" " return x+5;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("char *foo(int y) {\n" " char x[10] = {0};\n" " return (x+8)-y;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); } void returnLocalVariable5() { // cast check("char *foo() {\n" " int x[10] = {0};\n" " return (char *)x;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); } void returnLocalVariable6() { // valueflow check("int *foo() {\n" " int x = 123;\n" " int p = &x;\n" " return p;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); } void returnReference1() { check("int &foo()\n" "{\n" " int s = 0;\n" " int& x = s;\n" " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Reference to local variable returned.\n", errout.str()); check("std::string &foo()\n" "{\n" " std::string s;\n" " return s;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout.str()); check("std::vector &foo()\n" "{\n" " std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout.str()); check("std::vector &foo()\n" "{\n" " static std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector &foo()\n" "{\n" " thread_local std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string hello()\n" "{\n" " return \"hello\";\n" "}\n" "\n" "std::string &f()\n" "{\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Reference to temporary returned.\n", errout.str()); // make sure scope is used in function lookup check("class Fred {\n" " std::string hello() {\n" " return std::string();\n" " }\n" "};\n" "std::string &f() {\n" " return hello();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string hello() {\n" " return std::string();\n" "}\n" "\n" "std::string &f() {\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("std::string hello() {\n" " return \"foo\";\n" "}\n" "\n" "std::string &f() {\n" " return hello().substr(1);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("class Foo;\n" "Foo hello() {\n" " return Foo();\n" "}\n" "\n" "Foo& f() {\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Reference to temporary returned.\n", errout.str()); // make sure function overloads are handled properly check("class Foo;\n" "Foo & hello(bool) {\n" " static Foo foo;\n" " return foo;\n" "}\n" "Foo hello() {\n" " return Foo();\n" "}\n" "\n" "Foo& f() {\n" " return hello(true);\n" "}"); ASSERT_EQUALS("", errout.str()); check("Foo hello() {\n" " return Foo();\n" "}\n" "\n" "Foo& f() {\n" // Unknown type - might be a reference " return hello();\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference2() { check("class Fred {\n" " std::string &foo();\n" "}\n" "std::string &Fred::foo()\n" "{\n" " std::string s;\n" " return s;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Reference to local variable returned.\n", errout.str()); check("class Fred {\n" " std::vector &foo();\n" "};\n" "std::vector &Fred::foo()\n" "{\n" " std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Reference to local variable returned.\n", errout.str()); check("class Fred {\n" " std::vector &foo();\n" "};\n" "std::vector &Fred::foo()\n" "{\n" " static std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " std::string &f();\n" "};\n" "std::string hello()\n" "{\n" " return \"hello\";\n" "}\n" "std::string &Fred::f()\n" "{\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Reference to temporary returned.\n", errout.str()); check("class Fred {\n" " std::string hello();\n" " std::string &f();\n" "};\n" "std::string Fred::hello()\n" "{\n" " return \"hello\";\n" "}\n" "std::string &Fred::f()\n" "{\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Reference to temporary returned.\n", errout.str()); check("class Bar;\n" "Bar foo() {\n" " return something;\n" "}\n" "Bar& bar() {\n" " return foo();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("std::map foo() {\n" " return something;\n" "}\n" "std::map& bar() {\n" " return foo();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Reference to temporary returned.\n", errout.str()); check("Bar foo() {\n" " return something;\n" "}\n" "Bar& bar() {\n" // Unknown type - might be a typedef to a reference type " return foo();\n" "}"); ASSERT_EQUALS("", errout.str()); // Don't crash with function in unknown scope (#4076) check("X& a::Bar() {}" "X& foo() {" " return Bar();" "}"); } void returnReference3() { check("double & f(double & rd) {\n" " double ret = getValue();\n" " rd = ret;\n" " return rd;\n" "}", false); ASSERT_EQUALS("", errout.str()); } // Returning reference to global variable void returnReference4() { check("double a;\n" "double & f() {\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference5() { check("struct A {\n" " int i;\n" "};\n" "struct B {\n" " A a;\n" "};\n" "struct C {\n" " B *b;\n" " const A& a() const {\n" " const B *pb = b;\n" " const A &ra = pb->a;\n" " return ra;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void returnReference6() { check("Fred & create() {\n" " Fred &fred(*new Fred);\n" " return fred;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference7() { // 3791 - false positive for overloaded function check("std::string a();\n" "std::string &a(int);\n" "std::string &b() {\n" " return a(12);\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string &a(int);\n" "std::string a();\n" "std::string &b() {\n" " return a(12);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference8() { check("int& f(std::vector &v) {\n" " std::vector::iterator it = v.begin();\n" " int& value = *it;\n" " return value;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference9() { check("int& f(bool b, int& x, int& y) {\n" " return b ? x : y;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference10() { check("class A { int f() const; };\n" "int& g() {\n" " A a;\n" " return a.f();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to temporary returned.\n", errout.str()); check("class A { int& f() const; };\n" "int& g() {\n" " A a;\n" " return a.f();\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference11() { check("class A { static int f(); };\n" "int& g() {\n" " return A::f();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); check("class A { static int& f(); };\n" "int& g() {\n" " return A::f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace A { int& f(); }\n" "int& g() {\n" " return A::f();\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference12() { check("class A { static int& f(); };\n" "auto g() {\n" " return &A::f;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A { static int& f(); };\n" "auto g() {\n" " auto x = &A::f;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference13() { check("std::vector v;\n" "void* vp = &v;\n" "int& foo(size_t i) {\n" " return ((std::vector*)vp)->at(i);\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector v;\n" "void* vp = &v;\n" "int& foo(size_t i) {\n" " return static_cast*>(vp)->at(i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference14() { check("struct C { void* m; };\n" "struct A { void* &f(); };\n" "C* g() {\n" " static C c;\n" " return &c;\n" "}\n" "void* &A::f() {\n" " return g()->m;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference15() { check("template \n" "const int& f() {\n" " static int s;\n" " return s;\n" "}\n" "template \n" "const int& f(const T&) {\n" " return f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("template \n" "int g();\n" "template \n" "const int& f(const T&) {\n" " return g();\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void returnReference16() { check("int& f(std::tuple& x) {\n" " return std::get<0>(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& f(int x) {\n" " return std::get<0>(std::make_tuple(x));\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void returnReference17() { check("auto g() -> int&;\n" "int& f() {\n" " return g();\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference18() { check("template\n" "auto f(T& x) -> decltype(x);\n" "int& g(int* x) {\n" " return f(*x);\n" "}"); ASSERT_EQUALS("", errout.str()); } // #9597 void returnReference19() { check("struct C : B {\n" " const B &f() const { return (const B &)*this; }\n" "}"); ASSERT_EQUALS("", errout.str()); } // #9536 void returnReference20() { check("struct a {\n" " int& operator()() const;\n" "};\n" "int& b() {\n" " return a()();\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto a() {\n" " return []() -> int& {\n" " static int b;\n" " return b;\n" " };\n" "}\n" "const int& c() {\n" " return a()();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::function a();\n" "int& b() {\n" " return a()();\n" "}"); ASSERT_EQUALS("", errout.str()); // #9889 check("int f(std::vector>& v, int i) {\n" " auto& j = v[i]();\n" " return j;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } // #9530 void returnReference21() { check("int& f(int& x) {\n" " return {x};\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference22() { check("int& f() {\n" " std::unique_ptr p = std::make_unique(1);\n" " return *p;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to local variable returned.\n", errout.str()); check("void g(const std::unique_ptr&);\n" "int& f() {\n" " std::unique_ptr p = std::make_unique(1);\n" " g(p);\n" " return *p;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Reference to local variable returned.\n", errout.str()); check("void g(std::shared_ptr);\n" "int& f() {\n" " std::shared_ptr p = std::make_shared(1);\n" " g(p);\n" " return *p;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::shared_ptr g();\n" "int& f() {\n" " return *g();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::unique_ptr g();\n" "int& f() {\n" " return *g();\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); check("struct A { int x; };\n" "int& f() {\n" " std::unique_ptr p = std::make_unique();\n" " return p->x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout.str()); } void returnReference23() { check("const std::vector * g();\n" "const std::vector& f() {\n" " return *g();\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference24() { check("struct A {\n" " A() {}\n" "};\n" "const A& a() {\n" " return A();\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Reference to temporary returned.\n", errout.str()); } void returnReferenceFunction() { check("int& f(int& a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:6]: (error) Reference to local variable returned.\n", errout.str()); check("int& f(int& a) {\n" " return a;\n" "}\n" "int* hello() {\n" " int x = 0;\n" " return &f(x);\n" "}"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("int* f(int * x) {\n" " return x;\n" "}\n" "int * g(int x) {\n" " return f(&x);\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("int* f(int * x) {\n" " x = nullptr;\n" " return x;\n" "}\n" "int * g(int x) {\n" " return f(&x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int& a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("int& f(int a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to local variable returned.\n", errout.str()); check("int f(int a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("template\n" "int& f(int& x, T y) {\n" " x += y;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceContainer() { check("auto& f() {\n" " std::vector x;\n" " return x[0];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to local variable returned.\n", errout.str()); check("auto& f() {\n" " std::vector x;\n" " return x.front();\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to local variable returned.\n", errout.str()); check("std::vector g();\n" "auto& f() {\n" " return g().front();\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); check("auto& f() {\n" " return std::vector{1}.front();\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("struct A { int foo; };\n" "int& f(std::vector v) {\n" " auto it = v.begin();\n" " return it->foo;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Reference to local variable returned.\n", errout.str()); check("template \n" "const V& get_default(const T& t, const K& k, const V& v) {\n" " auto it = t.find(k);\n" " if (it == t.end()) return v;\n" " return it->second;\n" "}\n" "const int& bar(const std::unordered_map& m, int k) {\n" " auto x = 0;\n" " return get_default(m, k, x);\n" "}\n", true); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:9] -> [test.cpp:9]: (error, inconclusive) Reference to local variable returned.\n", errout.str()); check("template \n" "const V& get_default(const T& t, const K& k, const V& v) {\n" " auto it = t.find(k);\n" " if (it == t.end()) return v;\n" " return it->second;\n" "}\n" "const int& bar(const std::unordered_map& m, int k) {\n" " return get_default(m, k, 0);\n" "}\n", true); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:8] -> [test.cpp:8]: (error, inconclusive) Reference to temporary returned.\n", errout.str()); check("struct A { int foo; };\n" "int& f(std::vector& v) {\n" " auto it = v.begin();\n" " return it->foo;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceLiteral() { check("const std::string &a() {\n" " return \"foo\";\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("const std::string a() {\n" " return \"foo\";\n" "}"); ASSERT_EQUALS("", errout.str()); check("const std::string& f(const std::string& x) { return x; }\n" "const std::string &a() {\n" " return f(\"foo\");\n" "}"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:1] -> [test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); check("const char * f(const char * x) { return x; }\n" "const std::string &a() {\n" " return f(\"foo\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); } void returnReferenceCalculation() { check("const std::string &a(const std::string& str) {\n" " return \"foo\" + str;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("int& operator<<(int out, int path) {\n" " return out << path;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("std::ostream& operator<<(std::ostream& out, const std::string& path) {\n" " return out << path;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::ostream& operator<<(std::ostream* out, const std::string& path) {\n" " return *out << path;\n" "}"); ASSERT_EQUALS("", errout.str()); check("Unknown1& operator<<(Unknown1 out, Unknown2 path) {\n" " return out << path;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& a(int b) {\n" " return 2*(b+1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("const std::string &a(const std::string& str) {\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("const std::string &a(int bar) {\n" " return foo(bar + 1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("const std::string a(const std::string& str) {\n" " return \"foo\" + str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& incValue(int& value) {\n" " return ++value;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceLambda() { // #6787 check("const Item& foo(const Container& items) const {\n" " return bar(items.begin(), items.end(),\n" " [](const Item& lhs, const Item& rhs) {\n" " return false;\n" " });\n" "}"); ASSERT_EQUALS("", errout.str()); // #5844 check("map const &getVariableTable() {\n" "static map const s_var = []{\n" " map var;\n" " return var;\n" " }();\n" "return s_var;\n" "}"); ASSERT_EQUALS("", errout.str()); // #7583 check("Command& foo() {\n" " return f([]() -> int { return 1; });\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceInnerScope() { // #6951 check("const Callback& make() {\n" " struct _Wrapper {\n" " static ulong call(void* o, const void* f, const void*[]) {\n" " return 1;\n" " }\n" " };\n" " return _make(_Wrapper::call, pmf);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceRecursive() { check("int& f() { return f(); }"); ASSERT_EQUALS("", errout.str()); check("int& g(int& i) { return i; }\n" "int& f() { return g(f()); }"); ASSERT_EQUALS("", errout.str()); } void extendedLifetime() { check("void g(int*);\n" "int h();\n" "auto f() {\n" " const int& x = h();\n" " return [&] { return x; };\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning lambda that captures local variable 'x' that will be invalid when returning.\n", errout.str()); check("void g(int*);\n" "int h();\n" "int* f() {\n" " const int& x = h();\n" " return &x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("void g(int*);\n" "int h();\n" "void f() {\n" " int& x = h();\n" " g(&x);\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Using pointer that is a temporary.\n" "[test.cpp:4] -> [test.cpp:5]: (error) Using reference to dangling temporary.\n", errout.str()); check("void g(int*);\n" "int h();\n" "void f() {\n" " const int& x = h();\n" " g(&x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Data {\n" " std::string path;\n" "};\n" "const char* foo() {\n" " const Data& data = getData();\n" " return data.path.c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); } void danglingReference() { check("int f( int k )\n" "{\n" " static int &r = k;\n" " return r;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Non-local reference variable 'r' to local variable 'k'\n", errout.str()); check("int &f( int & k )\n" "{\n" " static int &r = k;\n" " return r;\n" "}"); ASSERT_EQUALS("", errout.str()); } void danglingTempReference() { check("const std::string& g(const std::string& str_cref) {\n" " return str_cref;\n" "}\n" "void f() {\n" " const auto& str_cref2 = g(std::string(\"hello\"));\n" " std::cout << str_cref2 << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:5] -> [test.cpp:6]: (error) Using reference to dangling temporary.\n", errout.str()); // Lifetime extended check("std::string g(const std::string& str_cref) {\n" " return str_cref;\n" "}\n" "void f() {\n" " const auto& str_cref2 = g(std::string(\"hello\"));\n" " std::cout << str_cref2 << std::endl;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("char f() {\n" " char c = 0;\n" " char&& cr = std::move(c);\n" " return cr;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9987 check("void g(std::vector);\n" "void f() {\n" " std::vector&& v = {};\n" " g(std::move(v));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(std::vector);\n" "std::vector h();\n" "void f() {\n" " std::vector&& v = h();\n" " g(std::move(v));\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testglobalnamespace() { check("class SharedPtrHolder\n" "{\n" " ::std::tr1::shared_ptr pNum;\n" "public:\n" " void SetNum(const ::std::tr1::shared_ptr & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnParameterAddress() { check("int* foo(int y)\n" "{\n" " return &y;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout.str()); check("int ** foo(int * y)\n" "{\n" " return &y;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout.str()); check("const int * foo(const int & y)\n" "{\n" " return &y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int * foo(int * y)\n" "{\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s { void *p; };\n" "extern struct s* f(void);\n" "void g(void **q)\n" "{\n" " struct s *r = f();\n" " *q = &r->p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testconstructor() { // Ticket #5478 - crash while checking a constructor check("class const_tree_iterator {\n" " const_tree_iterator(bool (*_incream)(node_type*&)) {}\n" " const_tree_iterator& parent() {\n" " return const_tree_iterator(foo);\n" " }\n" "};"); } void variableIsUsedInScope() { check("void removed_cb (GList *uids) {\n" "for (; uids; uids = uids->next) {\n" "}\n" "}\n" "void opened_cb () {\n" " g_signal_connect (G_CALLBACK (removed_cb));\n" "}"); } void danglingLifetimeLambda() { check("auto f() {\n" " int a = 1;\n" " auto l = [&](){ return a; };\n" " return l;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " int a = 1;\n" " return [&](){ return a; };\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(int a) {\n" " return [&](){ return a; };\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(int a) {\n" " auto p = &a;\n" " return [=](){ return p; };\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto g(int& a) {\n" " int p = a;\n" " return [&](){ return p; };\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'p' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " return [=](){\n" " int a = 1;\n" " return [&](){ return a; };\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(int b) {\n" " return [=](int a){\n" " a += b;\n" " return [&](){ return a; };\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto g(int& a) {\n" " return [&](){ return a; };\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto g(int a) {\n" " auto p = a;\n" " return [=](){ return p; };\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto g(int& a) {\n" " auto p = a;\n" " return [=](){ return p; };\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto g(int& a) {\n" " int& p = a;\n" " return [&](){ return p; };\n" "}"); ASSERT_EQUALS("", errout.str()); check("template\n" "void g(F);\n" "auto f() {\n" " int x;\n" " return g([&]() { return x; });\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto f() {\n" " int i = 0;\n" " return [&i] {};\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'i' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " int i = 0;\n" " return [i] {};\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto f() {\n" " int i = 0;\n" " return [=, &i] {};\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'i' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " int i = 0;\n" " int j = 0;\n" " return [=, &i] { return j; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'i' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " int i = 0;\n" " return [&, i] {};\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto f() {\n" " int i = 0;\n" " int j = 0;\n" " return [&, i] { return j; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'j' that will be invalid when returning.\n", errout.str()); check("auto f(int& i) {\n" " int j = 0;\n" " return [=, &i] { return j; };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int*);\n" "auto g(int y) {\n" " int x = y;\n" " return [=] {\n" " g(&x);\n" " };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int x;\n" "};\n" "auto f() {\n" " A a;\n" " return [=] {\n" " const A* ap = &a;\n" " ap->x;\n" " };\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeContainer() { check("auto f(const std::vector& x) {\n" " auto it = x.begin();\n" " return it;\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return it;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto p = x.data();\n" " return p;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto p = &x[0];\n" " return p;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("struct A { int foo; };\n" "int* f(std::vector v) {\n" " auto it = v.begin();\n" " return &it->foo;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", errout.str()); check("auto f(std::vector x) {\n" " auto it = x.begin();\n" " return it;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return std::next(it);\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return it + 1;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return std::next(it + 1);\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); check("std::vector f() {\n" " int i = 0;\n" " std::vector v;\n" " v.push_back(&i);\n" " return v;\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:5]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("std::vector f() {\n" " std::vector r;\n" " int i = 0;\n" " std::vector v;\n" " v.push_back(&i);\n" " r.assign(v.begin(), v.end());\n" " return r;\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:3] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " std::vector v;\n" " void f() {\n" " int i;\n" " v.push_back(&i);\n" " }\n" "};"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", errout.str()); check("struct A {\n" " std::vector v;\n" " void f() {\n" " int i;\n" " int * p = &i;\n" " v.push_back(p);\n" " }\n" "};"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:6]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", errout.str()); check("struct A {\n" " std::vector m;\n" " void f() {\n" " int x;\n" " std::vector v;\n" " v.push_back(&x);\n" " m.insert(m.end(), v.begin(), v.end());\n" " }\n" "};"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:7]: (error) Non-local variable 'm' will use object that points to local variable 'x'.\n", errout.str()); check("std::vector::iterator f(std::vector v) {\n" " for(auto it = v.begin();it != v.end();it++) {\n" " return it;\n" " }\n" " return {};\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 'v' that will be invalid when returning.\n", errout.str()); check("const char * f() {\n" " std::string ba(\"hello\");\n" " return ba.c_str();\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'ba' that will be invalid when returning.\n", errout.str()); check("template \n" "const V* get_default(const T& t, const K& k, const V* v) {\n" " auto it = t.find(k);\n" " if (it == t.end()) return v;\n" " return &it->second;\n" "}\n" "const int* bar(const std::unordered_map& m, int k) {\n" " auto x = 0;\n" " return get_default(m, k, &x);\n" "}\n", true); ASSERT_EQUALS( "[test.cpp:9] -> [test.cpp:9] -> [test.cpp:8] -> [test.cpp:9]: (error, inconclusive) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("std::vector g();\n" "auto f() {\n" " return g().begin();\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Returning iterator that will be invalid when returning.\n", errout.str()); check("std::vector f();\n" "auto f() {\n" " auto it = g().begin();\n" " return it;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("std::vector f();\n" "int& f() {\n" " return *g().begin();\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct A {\n" " std::vector v;\n" " void f() {\n" " char s[3];\n" " v.push_back(s);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("std::vector f() {\n" " const char * s = \"hello\";\n" " std::vector v;\n" " v.push_back(s);\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto f() {\n" " static std::vector x;\n" " return x.begin();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string g() {\n" " std::vector v;\n" " return v.data();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector::iterator f(std::vector* v) {\n" " return v->begin();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector::iterator f(std::vector* v) {\n" " std::vector* v = new std::vector();\n" " return v->begin();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector v) {\n" " return *v.begin();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector v) {\n" " return v.end() - v.begin();\n" "}"); ASSERT_EQUALS("", errout.str()); check("auto g() {\n" " std::vector v;\n" " return {v, [v]() { return v.data(); }};\n" "}"); ASSERT_EQUALS("", errout.str()); check("template\n" "void g(F);\n" "auto f() {\n" " std::vector v;\n" " return g([&]() { return v.data(); });\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector g();\n" "struct A {\n" " std::vector m;\n" " void f() {\n" " std::vector v = g();\n" " m.insert(m.end(), v.begin(), v.end());\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " std::vector v = {1};\n" " if (b) {\n" " int a[] = {0};\n" " v.insert(a, a+1);\n" " }\n" " return v.back() == 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int f( P p ) {\n" " std::vector< S > maps;\n" " m2.insert( m1.begin(), m1.end() );\n" " }\n" " struct B {};\n" " std::map< S, B > m1;\n" " std::map< S, B > m2;\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " int x;\n" " void f() {\n" " v.push_back(&x);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("size_t f(const std::string& x) {\n" " std::string y = \"x\";\n" " return y.find(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string* f();\n" "const char* g() {\n" " std::string* var = f();\n" " return var->c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string f() {\n" " std::vector data{};\n" " data.push_back('a');\n" " return std::string{ data.data(), data.size() };\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector f() {\n" " char a = 0;\n" " return std::vector{&a};\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout.str()); check("std::vector* g();\n" "int& f() {\n" " auto* p = g();\n" " return p->front();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector> g();\n" "void f() {\n" " for(auto& x:g())\n" " std::sort(x.begin(), x.end());\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " void add(int* i) {\n" " v.push_back(i);\n" " }\n" " void f() {\n" " int i = 0;\n" " add(&i);\n" " }\n" "};\n"); ASSERT_EQUALS( "[test.cpp:8] -> [test.cpp:8] -> [test.cpp:4] -> [test.cpp:7] -> [test.cpp:4]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", errout.str()); check("struct A {\n" " std::vector v;\n" " void add(int* i) {\n" " v.push_back(i);\n" " }\n" "};\n" "void f() {\n" " A a;\n" " int i = 0;\n" " a.add(&i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " void add(int* i) {\n" " v.push_back(i);\n" " }\n" " void f() {\n" " A a;\n" " int i = 0;\n" " a.add(&i);\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int i;\n" " {\n" " std::vector vec;\n" " const auto iter = vec.begin();\n" " i = (int)(iter - vec.begin());\n" " }\n" " return i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int* get(std::vector& container) {\n" " Sequence seq(container);\n" " for (auto& r : seq) {\n" " return &r;\n" " }\n" " return &*seq.begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::string f(std::string Str, int first, int last) {\n" " return { Str.begin() + first, Str.begin() + last + 1 };\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("std::string f(std::string s) {\n" " std::string r = { s.begin(), s.end() };\n" " return r;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector> mA;\n" " void f(std::unique_ptr a) {\n" " auto x = a.get();\n" " mA.push_back(std::move(a));\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::map m;\n" " int* f(std::string s) {\n" " auto r = m.emplace(name, name);\n" " return &(r.first->second);\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeContainerView() { check("std::string_view f() {\n" " std::string s = \"\";\n" " return s;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", errout.str()); check("std::string_view f() {\n" " std::string s = \"\";\n" " std::string_view sv = s;\n" " return sv;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", errout.str()); check("std::string_view f() {\n" " std::string s = \"\";\n" " return std::string_view{s};\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", errout.str()); check("std::string_view f(std::string_view s) {\n" " return s;\n" "}\n" "std::string_view g() {\n" " std::string s = \"\";\n" " return f(s);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", errout.str()); check("const char * f() {\n" " std::string s;\n" " std::string_view sv = s;\n" " return sv.begin();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", errout.str()); check("const char * f() {\n" " std::string s;\n" " return std::string_view{s}.begin();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", errout.str()); check("const char * f() {\n" " std::string s;\n" " return std::string_view(s).begin();\n" "}\n"); TODO_ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", "", errout.str()); check("const char * f(std::string_view sv) {\n" " return sv.begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("const char * f(std::string s) {\n" " std::string_view sv = s;\n" " return sv.begin();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", errout.str()); check("std::string_view f(std::string s) {\n" " return s;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", errout.str()); check("const char * f(const std::string& s) {\n" " std::string_view sv = s;\n" " return sv.begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::string_view f(const std::string_view& sv) {\n" " return sv;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeUniquePtr() { check("int* f(std::unique_ptr p) {\n" " int * rp = p.get();\n" " return rp;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'p' that will be invalid when returning.\n", errout.str()); } void danglingLifetime() { check("auto f() {\n" " std::vector a;\n" " auto it = a.begin();\n" " return [=](){ return it; };\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(std::vector a) {\n" " auto it = a.begin();\n" " return [=](){ return it; };\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("struct e {};\n" "e * j() {\n" " e c[20];\n" " return c;\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'c' that will be invalid when returning.\n", errout.str()); check("auto f(std::vector& a) {\n" " auto it = a.begin();\n" " return [=](){ return it; };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int * f(int a[]) {\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct b {\n" " uint32_t f[6];\n" " } d;\n" " uint32_t *a = d.f;\n" "}"); ASSERT_EQUALS("", errout.str()); // Don't decay std::array check("std::array f() {\n" " std::array x;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1];\n" "}"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1]();\n" "}"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1];\n" "}"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1]();\n" "}"); ASSERT_EQUALS("", errout.str()); // Crash #8872 check("struct a {\n" " void operator()(b c) override {\n" " d(c, [&] { c->e });\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " void operator()(b c) override {\n" " d(c, [=] { c->e });\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " a(char* b) {}\n" "};\n" "a f() {\n" " char c[20];\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " a(char* b) {}\n" "};\n" "a g() {\n" " char c[20];\n" " a d = c;\n" " return d;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct a {\n" " std::vector v;\n" " auto g() { return v.end(); }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int * f(std::vector& v) {\n" " for(int & x : v)\n" " return &x;\n" " return nullptr;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9275 check("struct S {\n" " void f();\n" " std::string m;\n" "}\n" "void S::f() {\n" " char buf[1024];\n" " const char* msg = buf;\n" " m = msg;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9201 check("int* f() {\n" " struct a { int m; };\n" " static a b{0};\n" " return &b.m;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9453 check("int *ptr;\n" "void foo(int arr[]) {\n" " ptr = &arr[2];\n" "}"); ASSERT_EQUALS("", errout.str()); // #9639 check("struct Fred {\n" " std::string s;\n" "};\n" "const Fred &getFred();\n" "const char * f() {\n" " const Fred &fred = getFred();\n" " return fred.s.c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); // #9534 check("struct A {\n" " int* x;\n" "};\n" "int* f(int i, std::vector& v) {\n" " A& y = v[i];\n" " return &y.x[i];\n" "}"); ASSERT_EQUALS("", errout.str()); // #9712 check("std::string f(const char *str) {\n" " char value[256];\n" " return value;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9770 check("class C {\n" " std::string f(const char*);\n" "};\n" "std::string C::f(const char*) {\n" " const char data[] = \"x\";\n" " return data;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9899 check("struct A {\n" " std::vector v;\n" " void f(std::vector w) {\n" " v = std::move(w);\n" " }\n" " void g(std::vector w) {\n" " f(std::move(w));\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); //Make sure we can still take the address of a reference without warning check("int* foo() {\n" " int& x = getX();\n" " return &x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int* m_x;\n" " void foo() {\n" " const int& x = getX();\n" " m_x = &x;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10090 check("struct a {\n" " int b{};\n" "};\n" "struct c {\n" " int* c{};\n" " a* d{};\n" "};\n" "a* f();\n" "c g() {\n" " c e;\n" " e.d = f();\n" " if (e.d)\n" " e.c = &e.d->b;\n" " return e;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10214 check("struct A {\n" " std::string key;\n" " const char *value;\n" "};\n" "const char *f(const std::string &key, const std::vector &lookup) {\n" " const auto &entry =\n" " std::find_if(lookup.begin(), lookup.end(),\n" " [key](const auto &v) { return v.key == key; });\n" " return (entry == lookup.end()) ? \"\" : entry->value;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9811 check("struct Base {\n" " virtual auto get() -> int & = 0;\n" "};\n" "struct A : public Base {\n" " int z = 42;\n" " auto get() -> int & override { return z; }\n" " auto getMore() -> int & { return get(); }\n" "};\n"); ASSERT_EQUALS("", errout.str()); // #10575 check("struct Data {\n" " int x=0;\n" " int y=0;\n" "};\n" "struct MoreData {\n" " Data *data1;\n" "};\n" "struct Fred {\n" " Fred() {\n" " Data data;\n" " mMoreData.data1 = &data;\n" " }\n" " MoreData mMoreData;\n" "};\n"); ASSERT_EQUALS( "[test.cpp:11] -> [test.cpp:10] -> [test.cpp:11]: (error) Non-local variable 'mMoreData.data1' will use pointer to local variable 'data'.\n", errout.str()); } void danglingLifetimeFunction() { check("auto f() {\n" " int a;\n" " return std::ref(a);\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " int a;\n" " return std::make_tuple(std::ref(a));\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout.str()); check("template\n" "auto by_value(T x) {\n" " return [=] { return x; };\n" "}\n" "auto g() {\n" " std::vector v;\n" " return by_value(v.begin());\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:7] -> [test.cpp:3] -> [test.cpp:3] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", errout.str()); check("template\n" "auto by_value(const T& x) {\n" " return [=] { return x; };\n" "}\n" "auto g() {\n" " std::vector v;\n" " return by_value(v.begin());\n" "}\n"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:7] -> [test.cpp:3] -> [test.cpp:3] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", errout.str()); check("auto by_ref(int& x) {\n" " return [&] { return x; };\n" "}\n" "auto f() {\n" " int i = 0;\n" " return by_ref(i);\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("auto by_ref(const int& x) {\n" " return [=] { return x; };\n" "}\n" "auto f() {\n" " int i = 0;\n" " return by_ref(i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto f(int x) {\n" " int a;\n" " std::tie(a) = x;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::pair\n" "str_pair(std::string const & a, std::string const & b) {\n" " return std::make_pair(a, b);\n" "}\n" "std::vector > create_parameters() {\n" " std::vector > par;\n" " par.push_back(str_pair(\"param1\", \"prop_a\"));\n" " par.push_back(str_pair(\"param2\", \"prop_b\"));\n" " par.push_back(str_pair(\"param3\", \"prop_c\"));\n" " return par;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeAggegrateConstructor() { check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " return A{i, i};\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " return {i, i};\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " A r{i, i};\n" " return r;\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:8]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " A r = {i, i};\n" " return r;\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:8]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f(int& x) {\n" " int i = 0;\n" " return A{i, x};\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f(int& x) {\n" " int i = 0;\n" " return A{x, i};\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f(int& x) {\n" " return A{x, x};\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int i; const int& j; };\n" "A f(int& x) {\n" " int y = 0;\n" " return A{y, x};\n" "}"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeInitList() { check("std::vector f() {\n" " int i = 0;\n" " std::vector v = {&i, &i};\n" " return v;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); // TODO: Ast is missing for this case check("std::vector f() {\n" " int i = 0;\n" " std::vector v{&i, &i};\n" " return v;\n" "}"); TODO_ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", "", errout.str()); check("std::vector f() {\n" " int i = 0;\n" " return {&i, &i};\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("std::vector f(int& x) {\n" " return {&x, &x};\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector f() {\n" " std::set x;\n" " x.insert(\"1\");\n" " x.insert(\"2\");\n" " return { x.begin(), x.end() };\n" "}"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeImplicitConversion() { check("struct A { A(const char *a); };\n" "A f() {\n" " std::string ba(\"hello\");\n" " return ba.c_str();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { A(const char *a); };\n" "A f() {\n" " std::string ba(\"hello\");\n" " A bp = ba.c_str();\n" " return bp;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { A(const char *a); };\n" "std::vector f() {\n" " std::string ba(\"hello\");\n" " std::vector v;\n" " v.push_back(ba.c_str());\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string f(const std::string& x) {\n" " const char c[] = \"\";\n" " if (!x.empty())\n" " return x + c;\n" " return \"\";\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string f(const std::string& x) {\n" " const char c[] = \"123\";\n" " if (!x.empty())\n" " return c + 1;\n" " return \"\";\n" "}"); ASSERT_EQUALS("", errout.str()); } void danglingTemporaryLifetime() { check("const int& g(const int& x) {\n" " return x;\n" "}\n" "void f(int& i) {\n" " int* x = &g(0);\n" " i += *x;\n" "}"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer that is a temporary.\n", errout.str()); check("QString f() {\n" " QString a(\"dummyValue\");\n" " const char* b = a.toStdString().c_str();\n" " QString c = b;\n" " return c;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:4]: (error) Using pointer that is a temporary.\n", errout.str()); check("auto f(std::string s) {\n" " const char *x = s.substr(1,2).c_str();\n" " auto i = s.substr(4,5).begin();\n" " return *i;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:4]: (error) Using iterator that is a temporary.\n", errout.str()); check("std::string f() {\n" " std::stringstream tmp;\n" " const std::string &str = tmp.str();\n" " return std::string(str.c_str(), 1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int get_value();\n" "const int &get_reference1() {\n" " const int &x = get_value();\n" " return x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Reference to temporary returned.\n", errout.str()); check("int get_value();\n" "const int &get_reference2() {\n" " const int &x1 = get_value();\n" " const int &x2 = x1;\n" " return x2;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Reference to temporary returned.\n", errout.str()); check("const std::string& getState() {\n" " static const std::string& state = \"\";\n" " return state;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct var {\n" " void fun();\n" "}x;\n" "var* T(const char*) {\n" " return &x;\n" "}\n" "std::string GetTemp();\n" "void f() {\n" " auto a = T(GetTemp().c_str());\n" " a->fun();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::map m_;\n" "};\n" "struct B {\n" " A a_;\n" "};\n" "B func();\n" "void f() {\n" " const std::map::iterator& m = func().a_.m_.begin();\n" " (void)m->first;\n" "}\n"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:9] -> [test.cpp:10]: (error) Using iterator that is a temporary.\n", errout.str()); check("void f(bool b) {\n" " std::vector ints = g();\n" " auto *ptr = &ints;\n" " if (b)\n" " ptr = &ints;\n" " for (auto it = ptr->begin(); it != ptr->end(); ++it)\n" " {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct String {\n" // #10469 " void Append(uint8_t Val);\n" " String& operator+=(const char s[]);\n" " String& operator+=(const std::string& Str) {\n" " return operator+=(Str.c_str());\n" " }\n" " void operator+=(uint8_t Val) {\n" " Append(Val);\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeBorrowedMembers() { // #10585 check("struct Info { int k; };\n" "struct MoreInfo {\n" " int* k;\n" " char dat;\n" "};\n" "struct Fields {\n" " Info info;\n" "};\n" "template void func1(T val){}\n" "template void func2(T val){}\n" "Fields* get();\n" "void doit() {\n" " MoreInfo rech;\n" " rech.k = &get()->info.k;\n" " func1(&rech.dat);\n" " func2(rech.k);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int x; };\n" "A* g();\n" "void f() {\n" " A** ap = &g();\n" " (*ap)->x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4] -> [test.cpp:5]: (error) Using pointer that is a temporary.\n", errout.str()); check("struct A { int* x; };\n" "A g();\n" "void f() {\n" " int* x = g().x;\n" " (void)*x + 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int x; };\n" "struct B { A* a; }\n" "B g();\n" "void f() {\n" " int* x = &g()->a.x;\n" " (void)*x + 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int x; };\n" "struct B { A* g(); };\n" "A* g();\n" "void f(B b) {\n" " A** ap = &b.g();\n" " (*ap)->x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer that is a temporary.\n", errout.str()); } void danglingLifetimeClassMemberFunctions() { check("struct S {\n" " S(int i) : i(i) {}\n" " int i;\n" " int* ptr() { return &i; }\n" "};\n" "int* fun(int i) { \n" " return S(i).ptr();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:7] -> [test.cpp:7]: (error) Returning pointer that will be invalid when returning.\n", errout.str()); check("struct Fred\n" "{\n" " int x[2];\n" " Fred() {\n" " x[0] = 0x41;\n" " x[1] = 0x42;\n" " }\n" " const int *get_x() {\n" " return x;\n" " }\n" "};\n" "static const int *foo() {\n" " Fred fred;\n" " return fred.get_x();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:9] -> [test.cpp:9] -> [test.cpp:14] -> [test.cpp:13] -> [test.cpp:14]: (error) Returning pointer to local variable 'fred' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " int i;\n" " auto f() const {\n" " return [=]{ return i; };\n" " }\n" "};\n" "auto g() {\n" " return A().f();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:8] -> [test.cpp:8]: (error) Returning object that will be invalid when returning.\n", errout.str()); check("struct A {\n" " int i;\n" " auto f() const {\n" " return [*this]{ return i; };\n" " }\n" "};\n" "auto g() {\n" " return A().f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int* i;\n" " auto f() const {\n" " return [*this]{ return i; };\n" " }\n" "};\n" "auto g() {\n" " int i = 0;\n" " return A{&i}.f();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:9] -> [test.cpp:9] -> [test.cpp:9] -> [test.cpp:4] -> [test.cpp:4] -> [test.cpp:8] -> [test.cpp:9]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct S {\n" " int i{};\n" "};\n" "struct T {\n" " S getS() const { return S{ j }; }\n" " int j{};\n" "};\n" "void f(S* p) {\n" " S ret;\n" " {\n" " T t;\n" " ret = t.getS();\n" " }\n" " *p = ret;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void invalidLifetime() { check("void foo(int a) {\n" " std::function f;\n" " if (a > 0) {\n" " int b = a + 1;\n" " f = [&]{ return b; };\n" " }\n" " f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using lambda that captures local variable 'b' that is out of scope.\n", errout.str()); check("void f(bool b) {\n" " int* x;\n" " if(b) {\n" " int y[6] = {0,1,2,3,4,5};\n" " x = y;\n" " }\n" " x[3];\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'y' that is out of scope.\n", errout.str()); check("void foo(int a) {\n" " std::function f;\n" " if (a > 0) {\n" " int b = a + 1;\n" " f = [&]{ return b; };\n" " f();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " b();\n" " std::list c;\n" "};\n" "void a::b() {\n" " c.end()\n" "}"); ASSERT_EQUALS("", errout.str()); check("void b(char f[], char c[]) {\n" " std::string d(c); {\n" " std::string e;\n" " b(f, e.c_str())\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " std::string s;\n" " if(b) {\n" " char buf[3];\n" " s = buf;\n" " }\n" " std::cout << s;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int &a[];\n" "void b(){int *c = a};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int x;\n" "};\n" "struct B {\n" " std::function x;\n" " void f() {\n" " this->x = [&] {\n" " B y;\n" " return y.x;\n" " };\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("namespace test {\n" "class Foo {};\n" "struct Bar {\n" " Foo *_foo;\n" "};\n" "\n" "int f(Bar *bar);\n" "\n" "void g(Bar *bar) {\n" " {\n" " Foo foo;\n" " bar->_foo = &foo;\n" " bar->_foo = nullptr;\n" " }\n" " f(bar);\n" "}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("class Foo {};\n" "struct Bar {\n" " Foo *_foo;\n" "};\n" "\n" "int f(Bar *bar);\n" "\n" "void g(Bar *bar) {\n" " {\n" " Foo foo;\n" " bar->_foo = &foo;\n" " bar->_foo = nullptr;\n" " }\n" " f(bar);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("namespace test {\n" "class Foo {};\n" "struct Bar {\n" " Foo *_foo;\n" "};\n" "\n" "int f(Bar *bar);\n" "\n" "void g(Bar *bar) {\n" " {\n" " Foo foo;\n" " bar->_foo = &foo;\n" " }\n" " f(bar);\n" "}\n" "}\n"); ASSERT_EQUALS("[test.cpp:12]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("class Foo {};\n" "struct Bar {\n" " Foo *_foo;\n" "};\n" "\n" "int f(Bar *bar);\n" "\n" "void g(Bar *bar) {\n" " {\n" " Foo foo;\n" " bar->_foo = &foo;\n" " }\n" " f(bar);\n" "}\n"); ASSERT_EQUALS("[test.cpp:11]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void deadPointer() { check("void f() {\n" " int *p = p1;\n" " if (cond) {\n" " int x;\n" " p = &x;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'x' that is out of scope.\n", errout.str()); // FP: don't warn in subfunction check("void f(struct KEY *key) {\n" " key->x = 0;\n" "}\n" "\n" "int main() {\n" " struct KEY *tmp = 0;\n" " struct KEY k;\n" "\n" " if (condition) {\n" " tmp = &k;\n" " } else {\n" " }\n" " f(tmp);\n" "}"); ASSERT_EQUALS("", errout.str()); // Don't warn about references (#6399) check("void f() {\n" " wxAuiToolBarItem* former_hover = NULL;\n" " for (i = 0, count = m_items.GetCount(); i < count; ++i) {\n" " wxAuiToolBarItem& item = m_items.Item(i);\n" " former_hover = &item;\n" " }\n" " if (former_hover != pitem)\n" " dosth();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " wxAuiToolBarItem* former_hover = NULL;\n" " for (i = 0, count = m_items.GetCount(); i < count; ++i) {\n" " wxAuiToolBarItem item = m_items.Item(i);\n" " former_hover = &item;\n" " }\n" " if (former_hover != pitem)\n" " dosth();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'item' that is out of scope.\n", errout.str()); // #6575 check("void trp_deliver_signal() {\n" " union {\n" " Uint32 theData[25];\n" " EventReport repData;\n" " };\n" " EventReport * rep = &repData;\n" " rep->setEventType(NDB_LE_Connected);\n" "}"); ASSERT_EQUALS("", errout.str()); // #8785 check("int f(bool a, bool b) {\n" " int *iPtr = 0;\n" " if(b) {\n" " int x = 42;\n" " iPtr = &x;\n" " }\n" " if(b && a)\n" " return *iPtr;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:8]: (error) Using pointer to local variable 'x' that is out of scope.\n", errout.str()); } void splitNamespaceAuto() { // #10473 check("namespace ns\n" "{\n" " auto var{ 0 };\n" "}\n" "namespace ns\n" "{\n" " int i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestAutoVariables) cppcheck-2.7/test/testbool.cpp000066400000000000000000001346341417746362400165110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkbool.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestBool : public TestFixture { public: TestBool() : TestFixture("TestBool") {} private: Settings settings; void run() OVERRIDE { settings.severity.enable(Severity::style); settings.severity.enable(Severity::warning); settings.certainty.enable(Certainty::inconclusive); TEST_CASE(bitwiseOnBoolean); // if (bool & bool) TEST_CASE(incrementBoolean); TEST_CASE(assignBoolToPointer); TEST_CASE(assignBoolToFloat); TEST_CASE(comparisonOfBoolExpressionWithInt1); TEST_CASE(comparisonOfBoolExpressionWithInt2); TEST_CASE(comparisonOfBoolExpressionWithInt3); TEST_CASE(comparisonOfBoolExpressionWithInt4); TEST_CASE(comparisonOfBoolWithInt1); TEST_CASE(comparisonOfBoolWithInt2); TEST_CASE(comparisonOfBoolWithInt3); TEST_CASE(comparisonOfBoolWithInt4); TEST_CASE(comparisonOfBoolWithInt5); TEST_CASE(comparisonOfBoolWithInt6); // #4224 - integer is casted to bool TEST_CASE(comparisonOfBoolWithInt7); // #4846 - (!x == true) TEST_CASE(comparisonOfBoolWithInt8); // #9165 TEST_CASE(comparisonOfBoolWithInt9); // #9304 TEST_CASE(checkComparisonOfFuncReturningBool1); TEST_CASE(checkComparisonOfFuncReturningBool2); TEST_CASE(checkComparisonOfFuncReturningBool3); TEST_CASE(checkComparisonOfFuncReturningBool4); TEST_CASE(checkComparisonOfFuncReturningBool5); TEST_CASE(checkComparisonOfFuncReturningBool6); // Integration tests.. TEST_CASE(checkComparisonOfFuncReturningBoolIntegrationTest1); // #7798 overloaded functions TEST_CASE(checkComparisonOfBoolWithBool); // Converting pointer addition result to bool TEST_CASE(pointerArithBool1); TEST_CASE(returnNonBool); TEST_CASE(returnNonBoolLambda); TEST_CASE(returnNonBoolLogicalOp); TEST_CASE(returnNonBoolClass); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], bool experimental = false, const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); settings.certainty.setEnabled(Certainty::experimental, experimental); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); // Check... CheckBool checkBool(&tokenizer, &settings, this); checkBool.runChecks(&tokenizer, &settings, this); } void assignBoolToPointer() { check("void foo(bool *p) {\n" " p = false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Boolean value assigned to pointer.\n", errout.str()); check("void foo(bool *p) {\n" " p = (x[rSize];\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #6588 (c mode) check("struct MpegEncContext { int *q_intra_matrix, *q_chroma_intra_matrix; };\n" "void dnxhd_10bit_dct_quantize(MpegEncContext *ctx, int n, int qscale) {\n" " const int *qmat = n < 4;\n" /* KO */ " const int *rmat = n < 4 ? " /* OK */ " ctx->q_intra_matrix :" " ctx->q_chroma_intra_matrix;\n" "}", /*experimental=*/ false, "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Boolean value assigned to pointer.\n", errout.str()); // ticket #6588 (c++ mode) check("struct MpegEncContext { int *q_intra_matrix, *q_chroma_intra_matrix; };\n" "void dnxhd_10bit_dct_quantize(MpegEncContext *ctx, int n, int qscale) {\n" " const int *qmat = n < 4;\n" /* KO */ " const int *rmat = n < 4 ? " /* OK */ " ctx->q_intra_matrix :" " ctx->q_chroma_intra_matrix;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Boolean value assigned to pointer.\n", errout.str()); // ticket #6665 check("void pivot_big(char *first, int compare(const void *, const void *)) {\n" " char *a = first, *b = first + 1, *c = first + 2;\n" " char* m1 = compare(a, b) < 0\n" " ? (compare(b, c) < 0 ? b : (compare(a, c) < 0 ? c : a))\n" " : (compare(a, c) < 0 ? a : (compare(b, c) < 0 ? c : b));\n" "}", /*experimental=*/ false, "test.c"); ASSERT_EQUALS("", errout.str()); // #7381 check("void foo(bool *p, bool b) {\n" " p = b;\n" " p = &b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Boolean value assigned to pointer.\n", errout.str()); } void assignBoolToFloat() { check("void foo1() {\n" " double d = false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout.str()); check("void foo2() {\n" " float d = true;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout.str()); check("void foo3() {\n" " long double d = (2>1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout.str()); // stability - don't crash: check("void foo4() {\n" " unknown = false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " float p;\n" "};\n" "void f() {\n" " S s = {0};\n" " s.p = true;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Boolean value assigned to floating point variable.\n", errout.str()); check("struct S {\n" " float* p[1];\n" "};\n" "void f() {\n" " S s = {0};\n" " *s.p[0] = true;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Boolean value assigned to floating point variable.\n", errout.str()); } void comparisonOfBoolExpressionWithInt1() { check("void f(int x) {\n" " if ((x && 0x0f)==6)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((x && 0x0f)==0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x || 0x0f)==6)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((x || 0x0f)==0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x & 0x0f)==6)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x | 0x0f)==6)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((5 && x)==3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x)==3 || (8 && x)==9)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x)!=3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x) > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x) > 0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((5 && x) < 0)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x) < 1)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((5 && x) > 1)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(int x) {\n" " if (0 < (5 && x))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (0 > (5 && x))\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(int x) {\n" " if (1 > (5 && x))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (1 < (5 && x))\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(bool x ) {\n" " if ( x > false )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false < x )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( x < false )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false > x )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( x >= false )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false >= x )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( x <= false )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false <= x )\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("typedef int (*func)(bool invert);\n" "void x(int, func f);\n" "void foo(int error) {\n" " if (error == ABC) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() { return !a+b1){} }"); ASSERT_EQUALS("",errout.str()); check("void f(int a, int b, int c) { if (a != !b || c) {} }"); ASSERT_EQUALS("",errout.str()); check("void f(int a, int b, int c) { if (1 < !!a + !!b + !!c) {} }"); ASSERT_EQUALS("",errout.str()); check("void f(int a, int b, int c) { if (1 < !(a+b)) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Comparison of a boolean expression with an integer.\n",errout.str()); } void comparisonOfBoolExpressionWithInt3() { check("int f(int x) {\n" " return t<0>() && x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolExpressionWithInt4() { // #5016 check("void f() {\n" " for(int i = 4; i > -1 < 5 ; --i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int a, int b, int c) {\n" " return (a > b) < c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int b, int c) {\n" " return x(a > b) < c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int b, int c) {\n" " return a > b == c;\n" "}"); ASSERT_EQUALS("", errout.str()); // templates check("struct Tokenizer { TokenList list; };\n" "void Tokenizer::f() {\n" " std::list locationList;\n" "}"); ASSERT_EQUALS("", errout.str()); // #5063 - or check("void f() {\n" " return a > b or c < d;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return (a < b) != 0U;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return (a < b) != 0x0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return (a < b) != 42U;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); } void checkComparisonOfFuncReturningBool1() { check("void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp==4){\n" " return false;\n" " }\n" " else\n" " return true;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool2() { check("void leftOfComparison(){\n" " int temp = 4;\n" " bool a = true;\n" " if(compare(temp) > a){\n" " printf(\"foo\");\n" " }\n" "}\n" "void rightOfComparison(){\n" " int temp = 4;\n" " bool a = true;\n" " if(a < compare(temp)){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" "[test.cpp:11]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool3() { check("void f(){\n" " int temp = 4;\n" " if(compare(temp) > temp){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare(int temp);"); ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n" "[test.cpp:3]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool4() { check("void f(){\n" " int temp = 4;\n" " bool b = compare2(6);\n" " if(compare1(temp)> b){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp == 5){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool5() { check("void f(){\n" " int temp = 4;\n" " if(compare1(temp) > !compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp==4){\n" " return false;\n" " }\n" " else\n" " return true;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool6() { check("int compare1(int temp);\n" "namespace Foo {\n" " bool compare1(int temp);\n" "}\n" "void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace Foo {\n" " bool compare1(int temp);\n" "}\n" "int compare1(int temp);\n" "void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int compare1(int temp);\n" "namespace Foo {\n" " bool compare1(int temp);\n" " void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); check("int compare1(int temp);\n" "namespace Foo {\n" " bool compare1(int temp);\n" " void f(){\n" " int temp = 4;\n" " if(::compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool compare1(int temp);\n" "void f(){\n" " int temp = 4;\n" " if(foo.compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkComparisonOfFuncReturningBoolIntegrationTest1() { // #7798 check("bool eval(double *) { return false; }\n" "double eval(char *) { return 1.0; }\n" "int main(int argc, char *argv[])\n" "{\n" " if ( eval(argv[1]) > eval(argv[2]) )\n" " return 1;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkComparisonOfBoolWithBool() { const char code[] = "void f(){\n" " int temp = 4;\n" " bool b = compare2(6);\n" " bool a = compare1(4);\n" " if(b > a){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp == 5){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n"; check(code, true); ASSERT_EQUALS("[test.cpp:5]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); check(code, false); ASSERT_EQUALS("", errout.str()); } void bitwiseOnBoolean() { // 3062 check("void f(_Bool a, _Bool b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(_Bool a, _Bool b) {\n" " if(a | b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a & !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a | !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a & !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a | b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a | !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("void f(bool a, int b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(int a, bool b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'b' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(int a, int b) {\n" " if((a > 0) & (b < 0)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a>0' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(bool a, int b) {\n" " if(a | b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("void f(int a, bool b) {\n" " if(a | b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'b' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("int f(bool a, int b) {\n" " return a | b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool a, int b) {\n" " return a | b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("void f(int a, int b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " foo(bar, &b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" // #9405 " class C { void foo(bool &b) {} };\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f();\n" "bool g() {\n" " return f() | f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("uint8 outcode(float p) {\n" " float d = 0.;\n" " return ((p - xm >= d) << 1) | (x - p > d);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void incrementBoolean() { check("bool bValue = true;\n" "void f() { bValue++; }"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(bool test){\n" " test++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(bool* test){\n" " (*test)++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(bool* test){\n" " test[0]++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(int test){\n" " test++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt1() { check("void f(bool x) {\n" " if (x < 10) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (10 >= x) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (x != 0) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x) {\n" // #3356 " if (x == 1) {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x) {\n" " if (x != 10) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (x == 10) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (x == 0) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("DensePropertyMap visited;"); // #4075 ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt2() { check("void f(bool x, int y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, bool y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x, bool y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x, fooClass y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt3() { check("void f(int y) {\n" " if (y > false) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(int y) {\n" " if (true == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool y) {\n" " if (y == true) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool y) {\n" " if (false < 5) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); } void comparisonOfBoolWithInt4() { check("void f(int x) {\n" " if (!x == 1) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt5() { check("void SetVisible(int index, bool visible) {\n" " bool (SciTEBase::*ischarforsel)(char ch);\n" " if (visible != GetVisible(index)) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt6() { // #4224 - integer is casted to bool check("void SetVisible(bool b, int i) {\n" " if (b == (bool)i) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt7() { // #4846 - (!x==true) check("void f(int x) {\n" " if (!x == true) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt8() { // #9165 check("bool Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (5 + expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (5 + expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (expectedResult == res + 5)\n" " throw 2;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void comparisonOfBoolWithInt9() { // #9304 check("bool f(int a, bool b)\n" "{\n" " if ((a == 0 ? false : true) != b) {\n" " b = !b;\n" " }\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void pointerArithBool1() { // #5126 check("void f(char *p) {\n" " if (p+1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " do {} while (p+1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " while (p-1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " for (int i = 0; p+1; i++) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " if (p && p+1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " if (p+2 || p) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); } void returnNonBool() { check("bool f(void) {\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " return 2;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return -1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return 1 + 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " int x = 0;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " int x = 10;\n" " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return 2 < 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " int ret = 0;\n" " if (a)\n" " ret = 1;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " int ret = 0;\n" " if (a)\n" " ret = 3;\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " if (a)\n" " return 3;\n" " return 4;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n" "[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnNonBoolLambda() { check("bool f(void) {\n" " auto x = [](void) { return -1; };\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " auto x = [](void) { return -1; };\n" " return 2;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " auto x = [](void) -> int { return -1; };\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " auto x = [](void) -> int { return -1; };\n" " return 2;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); } void returnNonBoolLogicalOp() { check("bool f(int x) {\n" " return x & 0x4;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x, int y) {\n" " return x | y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " return (x & 0x2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnNonBoolClass() { check("class X {\n" " public:\n" " bool f() { return -1;}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f() {\n" " struct X {\n" " public:\n" " int f() { return -1;}\n" " };\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " class X {\n" " public:\n" " int f() { return -1;}\n" " };\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " class X {\n" " public:\n" " bool f() { return -1;}\n" " };\n" " return -1;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Non-boolean value returned from function returning bool\n" "[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout.str()); } }; REGISTER_TEST(TestBool) cppcheck-2.7/test/testboost.cpp000066400000000000000000000074531417746362400167020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkboost.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestBoost : public TestFixture { public: TestBoost() : TestFixture("TestBoost") {} private: Settings settings; void run() OVERRIDE { settings.severity.enable(Severity::style); settings.severity.enable(Severity::performance); TEST_CASE(BoostForeachContainerModification); } #define check(code) check_(code, __FILE__, __LINE__) void check_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckBoost checkBoost; checkBoost.runChecks(&tokenizer, &settings, this); } void BoostForeachContainerModification() { check("void f() {\n" " vector data;\n" " BOOST_FOREACH(int i, data) {\n" " data.push_back(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); check("void f() {\n" " set data;\n" " BOOST_FOREACH(int i, data) {\n" " data.insert(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); check("void f() {\n" " set data;\n" " BOOST_FOREACH(const int &i, data) {\n" " data.erase(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); // Check single line usage check("void f() {\n" " set data;\n" " BOOST_FOREACH(const int &i, data)\n" " data.clear();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); // Container returned as result of a function -> Be quiet check("void f() {\n" " BOOST_FOREACH(const int &i, get_data())\n" " data.insert(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // Break after modification (#4788) check("void f() {\n" " vector data;\n" " BOOST_FOREACH(int i, data) {\n" " data.push_back(123);\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestBoost) cppcheck-2.7/test/testbufferoverrun.cpp000066400000000000000000005462201417746362400204460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check.h" #include "checkbufferoverrun.h" #include "config.h" #include "ctu.h" #include "errortypes.h" #include "standards.h" #include "library.h" #include "preprocessor.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include #include #include #include class TestBufferOverrun : public TestFixture { public: TestBufferOverrun() : TestFixture("TestBufferOverrun") {} private: Settings settings0; #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); settings0.certainty.enable(Certainty::inconclusive); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); // Check for buffer overruns.. CheckBufferOverrun checkBufferOverrun; checkBufferOverrun.runChecks(&tokenizer, &settings0, this); } void check_(const char* file, int line, const char code[], const Settings &settings, const char filename[] = "test.cpp") { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); // Clear the error buffer.. errout.str(""); // Check for buffer overruns.. CheckBufferOverrun checkBufferOverrun(&tokenizer, &settings, this); checkBufferOverrun.runChecks(&tokenizer, &settings, this); } void checkP(const char code[], const char* filename = "test.cpp") { // Clear the error buffer.. errout.str(""); Settings* settings = &settings0; settings->severity.enable(Severity::style); settings->severity.enable(Severity::warning); settings->severity.enable(Severity::portability); settings->severity.enable(Severity::performance); settings->standards.c = Standards::CLatest; settings->standards.cpp = Standards::CPPLatest; settings->certainty.enable(Certainty::inconclusive); settings->certainty.disable(Certainty::experimental); // Raw tokens.. std::vector files(1, filename); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); Preprocessor preprocessor(*settings, nullptr); preprocessor.setDirectives(tokens1); // Tokenizer.. Tokenizer tokenizer(settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); tokenizer.setPreprocessor(&preprocessor); // Check for buffer overruns.. CheckBufferOverrun checkBufferOverrun(&tokenizer, settings, this); checkBufferOverrun.runChecks(&tokenizer, settings, this); } void run() OVERRIDE { LOAD_LIB_2(settings0.library, "std.cfg"); settings0.severity.enable(Severity::warning); settings0.severity.enable(Severity::style); settings0.severity.enable(Severity::portability); TEST_CASE(noerr1); TEST_CASE(noerr2); TEST_CASE(noerr3); TEST_CASE(noerr4); TEST_CASE(sizeof3); TEST_CASE(array_index_1); TEST_CASE(array_index_2); TEST_CASE(array_index_3); TEST_CASE(array_index_4); TEST_CASE(array_index_6); TEST_CASE(array_index_7); TEST_CASE(array_index_11); TEST_CASE(array_index_12); TEST_CASE(array_index_13); TEST_CASE(array_index_14); TEST_CASE(array_index_15); TEST_CASE(array_index_16); TEST_CASE(array_index_17); TEST_CASE(array_index_18); TEST_CASE(array_index_19); TEST_CASE(array_index_20); TEST_CASE(array_index_21); TEST_CASE(array_index_22); TEST_CASE(array_index_23); TEST_CASE(array_index_24); // ticket #1492 and #1539 TEST_CASE(array_index_25); // ticket #1536 TEST_CASE(array_index_26); TEST_CASE(array_index_27); TEST_CASE(array_index_28); // ticket #1418 TEST_CASE(array_index_29); // ticket #1734 TEST_CASE(array_index_30); // ticket #2086 - out of bounds when type is unknown TEST_CASE(array_index_31); // ticket #2120 - out of bounds in subfunction when type is unknown TEST_CASE(array_index_32); TEST_CASE(array_index_33); // ticket #3044 TEST_CASE(array_index_34); // ticket #3063 TEST_CASE(array_index_35); // ticket #2889 TEST_CASE(array_index_36); // ticket #2960 TEST_CASE(array_index_37); TEST_CASE(array_index_38); // ticket #3273 TEST_CASE(array_index_39); TEST_CASE(array_index_40); // loop variable calculation, taking address TEST_CASE(array_index_41); // structs with the same name TEST_CASE(array_index_42); TEST_CASE(array_index_43); // struct with array TEST_CASE(array_index_44); // #3979 TEST_CASE(array_index_45); // #4207 - calling function with variable number of parameters (...) TEST_CASE(array_index_46); // #4840 - two-statement for loop TEST_CASE(array_index_47); // #5849 TEST_CASE(array_index_48); // #9478 TEST_CASE(array_index_49); // #8653 TEST_CASE(array_index_50); TEST_CASE(array_index_51); // #3763 TEST_CASE(array_index_52); // #7682 TEST_CASE(array_index_53); // #4750 TEST_CASE(array_index_54); // #10268 TEST_CASE(array_index_55); // #10254 TEST_CASE(array_index_56); // #10284 TEST_CASE(array_index_57); // #10023 TEST_CASE(array_index_58); // #7524 TEST_CASE(array_index_59); // #10413 TEST_CASE(array_index_60); // #10617, #9824 TEST_CASE(array_index_61); // #10621 TEST_CASE(array_index_multidim); TEST_CASE(array_index_switch_in_for); TEST_CASE(array_index_for_in_for); // FP: #2634 TEST_CASE(array_index_bounds); TEST_CASE(array_index_calculation); TEST_CASE(array_index_negative1); TEST_CASE(array_index_negative2); // ticket #3063 TEST_CASE(array_index_negative3); TEST_CASE(array_index_negative4); TEST_CASE(array_index_for_decr); TEST_CASE(array_index_varnames); // FP: struct member #1576, FN: #1586 TEST_CASE(array_index_for_continue); // for,continue TEST_CASE(array_index_for); // FN: for,if TEST_CASE(array_index_for_neq); // #2211: Using != in condition TEST_CASE(array_index_for_question); // #2561: for, ?: TEST_CASE(array_index_for_andand_oror); // FN: using && or || in the for loop condition TEST_CASE(array_index_for_varid0); // #4228: No varid for counter variable TEST_CASE(array_index_vla_for); // #3221: access VLA inside for TEST_CASE(array_index_extern); // FP when using 'extern'. #1684 TEST_CASE(array_index_cast); // FP after cast. #2841 TEST_CASE(array_index_string_literal); TEST_CASE(array_index_same_struct_and_var_name); // #4751 - not handled well when struct name and var name is same TEST_CASE(array_index_valueflow); TEST_CASE(array_index_valueflow_pointer); TEST_CASE(array_index_function_parameter); TEST_CASE(array_index_enum_array); // #8439 TEST_CASE(array_index_container); // #9386 TEST_CASE(array_index_two_for_loops); TEST_CASE(buffer_overrun_2_struct); TEST_CASE(buffer_overrun_3); TEST_CASE(buffer_overrun_4); TEST_CASE(buffer_overrun_5); TEST_CASE(buffer_overrun_6); TEST_CASE(buffer_overrun_7); TEST_CASE(buffer_overrun_8); TEST_CASE(buffer_overrun_9); TEST_CASE(buffer_overrun_10); TEST_CASE(buffer_overrun_11); TEST_CASE(buffer_overrun_15); // ticket #1787 TEST_CASE(buffer_overrun_16); TEST_CASE(buffer_overrun_18); // ticket #2576 - for, calculation with loop variable TEST_CASE(buffer_overrun_19); // #2597 - class member with unknown type TEST_CASE(buffer_overrun_21); TEST_CASE(buffer_overrun_24); // index variable is changed in for-loop TEST_CASE(buffer_overrun_26); // #4432 (segmentation fault) TEST_CASE(buffer_overrun_27); // #4444 (segmentation fault) TEST_CASE(buffer_overrun_29); // #7083: false positive: typedef and initialization with strings TEST_CASE(buffer_overrun_30); // #6367 TEST_CASE(buffer_overrun_31); TEST_CASE(buffer_overrun_32); //#10244 TEST_CASE(buffer_overrun_33); //#2019 TEST_CASE(buffer_overrun_errorpath); TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch TEST_CASE(buffer_overrun_function_array_argument); TEST_CASE(possible_buffer_overrun_1); // #3035 TEST_CASE(buffer_overrun_readSizeFromCfg); TEST_CASE(valueflow_string); // using ValueFlow string values in checking // It is undefined behaviour to point out of bounds of an array // the address beyond the last element is in bounds // char a[10]; // char *p1 = a + 10; // OK // char *p2 = a + 11 // UB TEST_CASE(pointer_out_of_bounds_1); TEST_CASE(pointer_out_of_bounds_2); TEST_CASE(pointer_out_of_bounds_3); TEST_CASE(pointer_out_of_bounds_4); TEST_CASE(pointer_out_of_bounds_sub); TEST_CASE(strcat1); TEST_CASE(varid1); TEST_CASE(varid2); // ticket #4764 TEST_CASE(assign1); TEST_CASE(alloc_new); // Buffer allocated with new TEST_CASE(alloc_malloc); // Buffer allocated with malloc TEST_CASE(alloc_string); // statically allocated buffer TEST_CASE(alloc_alloca); // Buffer allocated with alloca // TODO TEST_CASE(countSprintfLength); TEST_CASE(minsize_argvalue); TEST_CASE(minsize_sizeof); TEST_CASE(minsize_strlen); TEST_CASE(minsize_mul); TEST_CASE(unknownType); TEST_CASE(terminateStrncpy1); TEST_CASE(terminateStrncpy2); TEST_CASE(terminateStrncpy3); TEST_CASE(terminateStrncpy4); TEST_CASE(recursive_long_time); TEST_CASE(crash1); // Ticket #1587 - crash TEST_CASE(crash2); // Ticket #3034 - crash TEST_CASE(crash3); // Ticket #5426 - crash TEST_CASE(crash4); // Ticket #8679 - crash TEST_CASE(crash5); // Ticket #8644 - crash TEST_CASE(crash6); // Ticket #9024 - crash TEST_CASE(crash7); // Ticket #9073 - crash TEST_CASE(insecureCmdLineArgs); TEST_CASE(checkBufferAllocatedWithStrlen); TEST_CASE(scope); // handling different scopes TEST_CASE(getErrorMessages); // Access array and then check if the used index is within bounds TEST_CASE(arrayIndexThenCheck); TEST_CASE(arrayIndexEarlyReturn); // #6884 TEST_CASE(bufferNotZeroTerminated); TEST_CASE(negativeMemoryAllocationSizeError); // #389 TEST_CASE(negativeArraySize); TEST_CASE(pointerAddition1); TEST_CASE(ctu_malloc); TEST_CASE(ctu_array); TEST_CASE(ctu_variable); TEST_CASE(ctu_arithmetic); TEST_CASE(objectIndex); } void noerr1() { check("extern int ab;\n" "void f()\n" "{\n" " if (ab)\n" " {\n" " char str[50];\n" " }\n" " if (ab)\n" " {\n" " char str[50];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void noerr2() { check("static char buf[2];\n" "void f1(char *str)\n" "{\n" " strcpy(buf,str);\n" "}\n" "void f2(char *str)\n" "{\n" " strcat(buf,str);\n" "}\n" "void f3(char *str)\n" "{\n" " sprintf(buf,\"%s\",str);\n" "}\n" "void f4(const char str[])\n" "{\n" " strcpy(buf, str);\n" "}"); ASSERT_EQUALS("", errout.str()); } void noerr3() { check("struct { char data[10]; } abc;\n" "static char f()\n" "{\n" " char data[1];\n" " return abc.data[1];\n" "}"); ASSERT_EQUALS("", errout.str()); } void noerr4() { // The memory isn't read or written and therefore there is no error. check("static void f() {\n" " char data[100];\n" " const char *p = data + 100;\n" "}"); ASSERT_EQUALS("", errout.str()); } void sizeof3() { check("struct group { int gr_gid; };\n" "void f()\n" "{\n" " char group[32];\n" " snprintf(group, 32, \"%u\", 0);\n" " struct group *gr;\n" " snprintf(group, 32, \"%u\", gr->gr_gid);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_1() { check("void f()\n" "{\n" " char str[0x10] = {0};\n" " str[15] = 0;\n" " str[16] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", errout.str()); check("char f()\n" "{\n" " char str[16] = {0};\n" " return str[16];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", errout.str()); // test stack array check("int f()\n" "{\n" " int x[ 3 ] = { 0, 1, 2 };\n" " int y;\n" " y = x[ 4 ];\n" " return y;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'x[3]' accessed at index 4, which is out of bounds.\n", errout.str()); check("int f()\n" "{\n" " int x[ 3 ] = { 0, 1, 2 };\n" " int y;\n" " y = x[ 2 ];\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int x[5] = {0};\n" "int a = x[10];"); ASSERT_EQUALS("[test.cpp:2]: (error) Array 'x[5]' accessed at index 10, which is out of bounds.\n", errout.str()); check("int x[5] = {0};\n" "int a = (x)[10];"); ASSERT_EQUALS("[test.cpp:2]: (error) Array 'x[5]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_2() { check("void a(int i)\n" // valueflow "{\n" " char *str = new char[0x10];\n" " str[i] = 0;\n" "}\n" "void b() { a(16); }"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", "", errout.str()); } void array_index_4() { check("char c = \"abc\"[4];"); ASSERT_EQUALS("[test.cpp:1]: (error) Array '\"abc\"[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("p = &\"abc\"[4];"); ASSERT_EQUALS("", errout.str()); check("char c = \"\\0abc\"[2];"); ASSERT_EQUALS("", errout.str()); check("char c = L\"abc\"[4];"); ASSERT_EQUALS("[test.cpp:1]: (error) Array 'L\"abc\"[4]' accessed at index 4, which is out of bounds.\n", errout.str()); } void array_index_3() { check("void f()\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = 0; i < 100; i++)\n" " sum += val[i];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = 1; i < 100; i++)\n" " sum += val[i];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str()); check("void f(int a)\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = a; i < 100; i++)\n" " sum += val[i];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str()); check("typedef struct g g2[3];\n" "void foo(char *a)\n" "{\n" " for (int i = 0; i < 4; i++)\n" " {\n" " a[i]=0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int argc)\n" "{\n" " char a[2];\n" " for (int i = 4; i < argc; i++){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a[10]) {\n" " for (int i=0;i<50;++i) {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10]' accessed at index 49, which is out of bounds.\n", errout.str()); } void array_index_6() { check("struct ABC\n" "{\n" " char str[10];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC abc;\n" " abc.str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("struct ABC\n" "{\n" " char str[10];\n" "};\n" "\n" "static char f()\n" "{\n" " struct ABC abc;\n" " return abc.str[10];\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // This is not out of bounds because it is a variable length array check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[1] = 0;" "}"); ASSERT_EQUALS("", errout.str()); // This is not out of bounds because it is not a variable length array check("struct ABC\n" "{\n" " char str[1];\n" " int x;\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[1] = 0;" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // This is not out of bounds because it is a variable length array // and the index is within the memory allocated. /** @todo this works by accident because we ignore any access to this array */ check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[10] = 0;" "}"); ASSERT_EQUALS("", errout.str()); // This is out of bounds because it is outside the memory allocated. /** @todo this doesn't work because of a bug in sizeof(struct) */ check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[11] = 0;" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Array 'str[1]' accessed at index 11, which is out of bounds.\n", "", errout.str()); // This is out of bounds if 'sizeof(ABC)' is 1 (No padding) check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(ABC) + 10);\n" " x->str[11] = 0;" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // This is out of bounds because it is outside the memory allocated /** @todo this doesn't work because of a bug in sizeof(struct) */ check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC));\n" " x->str[1] = 0;" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Array 'str[1]' accessed at index 1, which is out of bounds.\n", "", errout.str()); // This is out of bounds because it is outside the memory allocated // But only if 'sizeof(ABC)' is 1 (No padding) check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(ABC));\n" " x->str[1] = 0;" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // This is out of bounds because it is not a variable array check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC x;\n" " x.str[1] = 0;" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'x.str[1]' accessed at index 1, which is out of bounds.\n", errout.str()); check("struct foo\n" "{\n" " char str[10];\n" "};\n" "\n" "void x()\n" "{\n" " foo f;\n" " for ( unsigned int i = 0; i < 64; ++i )\n" " f.str[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'f.str[10]' accessed at index 63, which is out of bounds.\n", errout.str()); check("struct AB { char a[NUM]; char b[NUM]; }\n" "void f(struct AB *ab) {\n" " ab->a[0] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("union { char a[1]; int b; } ab;\n" "void f() {\n" " ab.a[2] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'ab.a[1]' accessed at index 2, which is out of bounds.\n", errout.str()); } void array_index_7() { check("struct ABC\n" "{\n" " char str[10];\n" "};\n" "\n" "static void f(struct ABC *abc)\n" "{\n" " abc->str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'abc->str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_11() { check("class ABC\n" "{\n" "public:\n" " ABC();\n" " char *str[10];\n" " struct ABC *next();\n" "};\n" "\n" "static void f(ABC *abc1)\n" "{\n" " for ( ABC *abc = abc1; abc; abc = abc->next() )\n" " {\n" " abc->str[10] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (error) Array 'abc->str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_12() { check("class Fred\n" "{\n" "private:\n" " char str[10];\n" "public:\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{\n" " str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " char str[10];\n" "public:\n" " char c();\n" "};\n" "char Fred::c()\n" "{\n" " return str[10];\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_13() { check("void f()\n" "{\n" " char buf[10];\n" " for (int i = 0; i < 100; i++)\n" " {\n" " if (i < 10)\n" " int x = buf[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_14() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i+10] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); } void array_index_15() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[10+i] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); } void array_index_16() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i+1] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_17() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i*2] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 18, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " int a[12];\n" " for (int i = 0; i < 12; i+=6)\n" " a[i+5] = i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[12];\n" " for (int i = 0; i < 12; i+=6)\n" " a[i+6] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[12]' accessed at index 12, which is out of bounds.\n", errout.str()); check("void f() {\n" // #4398 " int a[2];\n" " for (int i = 0; i < 4; i+=2)\n" " a[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", errout.str()); check("void f() {\n" // #4398 " int a[2];\n" " for (int i = 0; i < 4; i+=2)\n" " do_stuff(a+i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_18() { check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i+=1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i=4;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[6];\n" " for (int i = 0; i < 7; i++)\n" " {\n" " a[i] = i;\n" " i+=1;\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Buffer overrun\n", "", errout.str()); } void array_index_19() { // "One Past the End" is legal, as long as pointer is not dereferenced. check("void f()\n" "{\n" " char a[2];\n" " char *end = &(a[2]);\n" "}"); ASSERT_EQUALS("", errout.str()); // Getting more than one past the end is not legal check("void f()\n" "{\n" " char a[2];\n" " char *end = &(a[3]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 3, which is out of bounds.\n", errout.str()); } void array_index_20() { check("void f()\n" "{\n" " char a[8];\n" " int b[10];\n" " for ( int i = 0; i < 9; i++ )\n" " b[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_21() { check("class A {\n" " int indices[2];\n" " void foo(int indices[3]);\n" "};\n" "\n" "void A::foo(int indices[3]) {\n" " for(int j=0; j<3; ++j) {\n" " int b = indices[j];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_22() { check("int main() {\n" " size_t indices[2];\n" " int b = indices[2];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'indices[2]' accessed at index 2, which is out of bounds.\n", errout.str()); } void array_index_23() { check("void foo()\n" "{\n" " char c[10];\n" " c[1<<23]='a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'c[10]' accessed at index 8388608, which is out of bounds.\n", errout.str()); } void array_index_24() { // ticket #1492 and #1539 const std::string charMaxPlusOne(settings0.defaultSign == 'u' ? "256" : "128"); check(("void f(char n) {\n" " int a[n];\n" // n <= CHAR_MAX " a[-1] = 0;\n" // negative index " a[" + charMaxPlusOne + "] = 0;\n" // 128/256 > CHAR_MAX "}\n").c_str()); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[" + charMaxPlusOne + "]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[" + charMaxPlusOne + "]' accessed at index " + charMaxPlusOne + ", which is out of bounds.\n", errout.str()); check("void f(signed char n) {\n" " int a[n];\n" // n <= SCHAR_MAX " a[-1] = 0;\n" // negative index " a[128] = 0;\n" // 128 > SCHAR_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[128]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[128]' accessed at index 128, which is out of bounds.\n", errout.str()); check("void f(unsigned char n) {\n" " int a[n];\n" // n <= UCHAR_MAX " a[-1] = 0;\n" // negative index " a[256] = 0;\n" // 256 > UCHAR_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[256]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[256]' accessed at index 256, which is out of bounds.\n", errout.str()); check("void f(short n) {\n" " int a[n];\n" // n <= SHRT_MAX " a[-1] = 0;\n" // negative index " a[32768] = 0;\n" // 32768 > SHRT_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[32768]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[32768]' accessed at index 32768, which is out of bounds.\n", errout.str()); check("void f(unsigned short n) {\n" " int a[n];\n" // n <= USHRT_MAX " a[-1] = 0;\n" // negative index " a[65536] = 0;\n" // 65536 > USHRT_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[65536]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[65536]' accessed at index 65536, which is out of bounds.\n", errout.str()); check("void f(signed short n) {\n" " int a[n];\n" // n <= SHRT_MAX " a[-1] = 0;\n" // negative index " a[32768] = 0;\n" // 32768 > SHRT_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[32768]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[32768]' accessed at index 32768, which is out of bounds.\n", errout.str()); check("void f(int n) {\n" " int a[n];\n" // n <= INT_MAX " a[-1] = 0;\n" // negative index "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[2147483648]' accessed at index -1, which is out of bounds.\n", errout.str()); check("void f(unsigned int n) {\n" " int a[n];\n" // n <= UINT_MAX " a[-1] = 0;\n" // negative index "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[4294967296]' accessed at index -1, which is out of bounds.\n", errout.str()); check("void f(signed int n) {\n" " int a[n];\n" // n <= INT_MAX " a[-1] = 0;\n" // negative index "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[2147483648]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_25() { // #1536 check("void foo()\n" "{\n" " long l[SOME_SIZE];\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_26() { check("void f()\n" "{\n" " int a[3];\n" " for (int i = 3; 0 <= i; i--)\n" " a[i] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " int a[4];\n" " for (int i = 3; 0 <= i; i--)\n" " a[i] = i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_27() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i-1] = a[i];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_28() { // ticket #1418 check("void f()\n" "{\n" " int i[2];\n" " int *ip = i + 1;\n" " ip[-10] = 1;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Array ip[-10] out of bounds.\n", "", errout.str()); } void array_index_29() { // ticket #1724 check("void f()\n" "{\n" " int iBuf[10];" " int *i = iBuf + 9;" " int *ii = i + -5;" " ii[10] = 0;" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Array ii[10] out of bounds.\n", "", errout.str()); } void array_index_30() { // ticket #2086 - unknown type // extracttests.start: typedef unsigned char UINT8; check("void f() {\n" " UINT8 x[2];\n" " x[5] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'x[2]' accessed at index 5, which is out of bounds.\n", errout.str()); } void array_index_31() { // ticket #2120 - sub function, unknown type check("struct s1 {\n" " unknown_type_t delay[3];\n" "};\n" "\n" "void x(unknown_type_t *delay, const int *net) {\n" " delay[0] = 0;\n" "}\n" "\n" "void y() {\n" " struct s1 obj;\n" " x(obj.delay, 123);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s1 {\n" " unknown_type_t delay[3];\n" "};\n" "\n" "void x(unknown_type_t *delay, const int *net) {\n" " delay[4] = 0;\n" "}\n" "\n" "void y() {\n" " struct s1 obj;\n" " x(obj.delay, 123);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:6]: (error) Array 'obj.delay[3]' accessed at index 4, which is out of bounds.\n", "", errout.str()); check("struct s1 {\n" " float a[0];\n" "};\n" "\n" "void f() {\n" " struct s1 *obj;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_32() { check("class X\n" "{\n" " public:\n" " X()\n" " {\n" " m_x[0] = 0;\n" " m_x[1] = 0;\n" " }\n" " int m_x[1];\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'm_x[1]' accessed at index 1, which is out of bounds.\n", errout.str()); } void array_index_33() { check("void foo(char bar[][4]) {\n" " baz(bar[5]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_34() { // ticket #3063 check("void foo() {\n" " int y[2][2][2];\n" " y[0][2][0] = 0;\n" " y[0][0][2] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'y[2][2][2]' accessed at index y[0][2][0], which is out of bounds.\n" "[test.cpp:4]: (error) Array 'y[2][2][2]' accessed at index y[0][0][2], which is out of bounds.\n", errout.str()); check("struct TEST\n" "{\n" " char a[10];\n" " char b[10][5];\n" "};\n" "void foo()\n" "{\n" " TEST test;\n" " test.a[10] = 3;\n" " test.b[10][2] = 4;\n" " test.b[0][19] = 4;\n" " TEST *ptest;\n" " ptest = &test;\n" " ptest->a[10] = 3;\n" " ptest->b[10][2] = 4;\n" " ptest->b[0][19] = 4;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'test.a[10]' accessed at index 10, which is out of bounds.\n" "[test.cpp:10]: (error) Array 'test.b[10][5]' accessed at index test.b[10][2], which is out of bounds.\n" "[test.cpp:11]: (error) Array 'test.b[10][5]' accessed at index test.b[0][19], which is out of bounds.\n" "[test.cpp:14]: (error) Array 'ptest->a[10]' accessed at index 10, which is out of bounds.\n" "[test.cpp:15]: (error) Array 'ptest->b[10][5]' accessed at index ptest->b[10][2], which is out of bounds.\n" "[test.cpp:16]: (error) Array 'ptest->b[10][5]' accessed at index ptest->b[0][19], which is out of bounds.\n", errout.str()); check("struct TEST\n" "{\n" " char a[10][5];\n" "};\n" "void foo()\n" "{\n" " TEST test;\n" " test.a[9][5] = 4;\n" " test.a[0][50] = 4;\n" " TEST *ptest;\n" " ptest = &test;\n" " ptest->a[9][5] = 4;\n" " ptest->a[0][50] = 4;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'test.a[10][5]' accessed at index test.a[9][5], which is out of bounds.\n" "[test.cpp:9]: (error) Array 'test.a[10][5]' accessed at index test.a[0][50], which is out of bounds.\n" "[test.cpp:12]: (error) Array 'ptest->a[10][5]' accessed at index ptest->a[9][5], which is out of bounds.\n" "[test.cpp:13]: (error) Array 'ptest->a[10][5]' accessed at index ptest->a[0][50], which is out of bounds.\n", errout.str()); } void array_index_35() { // ticket #2889 check("void f() {\n" " struct Struct { unsigned m_Var[1]; } s;\n" " s.m_Var[1] = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 's.m_Var[1]' accessed at index 1, which is out of bounds.\n", errout.str()); check("struct Struct { unsigned m_Var[1]; };\n" "void f() {\n" " struct Struct s;\n" " s.m_Var[1] = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's.m_Var[1]' accessed at index 1, which is out of bounds.\n", errout.str()); check("struct Struct { unsigned m_Var[1]; };\n" "void f() {\n" " struct Struct * s = calloc(40);\n" " s->m_Var[1] = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_36() { // ticket #2960 check("class Fred {\n" " Fred(const Fred &);\n" "private:\n" " bool m_b[2];\n" "};\n" "Fred::Fred(const Fred & rhs) {\n" " m_b[2] = rhs.m_b[2];\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'm_b[2]' accessed at index 2, which is out of bounds.\n" "[test.cpp:7]: (error) Array 'rhs.m_b[2]' accessed at index 2, which is out of bounds.\n", errout.str()); } void array_index_37() { check("class Fred {\n" " char x[X];\n" " Fred() {\n" " for (unsigned int i = 0; i < 15; i++)\n" " i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_38() { //ticket #3273 check("void aFunction() {\n" " double aDoubleArray[ 10 ];\n" " unsigned int i; i = 0;\n" " for( i = 0; i < 6; i++ )\n" " {\n" " unsigned int j; j = 0;\n" " for( j = 0; j < 5; j++ )\n" " {\n" " unsigned int x; x = 0;\n" " for( x = 0; x < 4; x++ )\n" " {\n" " }\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_39() { // ticket 3387 check("void aFunction()\n" "{\n" " char a[10];\n" " a[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_40() { check("void f() {\n" " char a[10];\n" " for (int i = 0; i < 10; ++i)\n" " f2(&a[i + 1]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_41() { // Don't generate false positives when structs have the same name check("void a() {\n" " struct Fred { char data[6]; } fred;\n" " fred.data[4] = 0;\n" // <- no error "}\n" "\n" "void b() {\n" " struct Fred { char data[3]; } fred;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void a() {\n" " struct Fred { char data[6]; } fred;\n" " fred.data[4] = 0;\n" // <- no error "}\n" "\n" "void b() {\n" " struct Fred { char data[3]; } fred;\n" " fred.data[4] = 0;\n" // <- error "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'fred.data[3]' accessed at index 4, which is out of bounds.\n", errout.str()); } void array_index_42() { // ticket #3569 check("void f()\n" "{\n" " char *p; p = (char *)malloc(10);\n" " p[10] = 7;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'p[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " float *p; p = (float *)malloc(10 * sizeof(float));\n" " p[10] = 7;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'p[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char *p; p = (char *)malloc(10);\n" " p[0] = 0;\n" " p[9] = 9;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *p; p = new char[10];\n" " p[0] = 0;\n" " p[9] = 9;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *p(new char[10]);\n" " p[0] = 0;\n" " p[9] = 9;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *p = NULL;" " try{\n" " p = new char[10];\n" " }\n" " catch(...){\n" " return;\n" " }" " p[0] = 0;\n" " p[9] = 9;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_43() { // #3838 check("int f( )\n" "{\n" " struct {\n" " int arr[ 3 ];\n" " } var[ 1 ];\n" " int y;\n" " var[ 0 ].arr[ 0 ] = 0;\n" " var[ 0 ].arr[ 1 ] = 1;\n" " var[ 0 ].arr[ 2 ] = 2;\n" " y = var[ 0 ].arr[ 3 ];\n" // <-- array access out of bounds " return y;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'var[0].arr[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("int f( )\n" "{\n" " struct {\n" " int arr[ 3 ];\n" " } var[ 1 ];\n" " int y=1;\n" " var[ 0 ].arr[ 0 ] = 0;\n" " var[ 0 ].arr[ 1 ] = 1;\n" " var[ 0 ].arr[ 2 ] = 2;\n" " y = var[ 0 ].arr[ 2 ];\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f( ){\n" "struct Struct{\n" " int arr[ 3 ];\n" "};\n" "int y;\n" "Struct var;\n" "var.arr[ 0 ] = 0;\n" "var.arr[ 1 ] = 1;\n" "var.arr[ 2 ] = 2;\n" "var.arr[ 3 ] = 3;\n" // <-- array access out of bounds "y=var.arr[ 3 ];\n" // <-- array access out of bounds "return y;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n" "[test.cpp:11]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("void f( ) {\n" "struct S{\n" " int var[ 3 ];\n" "} ;\n" "S var[2];\n" "var[0].var[ 0 ] = 0;\n" "var[0].var[ 1 ] = 1;\n" "var[0].var[ 2 ] = 2;\n" "var[0].var[ 4 ] = 4;\n" // <-- array access out of bounds "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'var[0].var[3]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void f( ) {\n" "struct S{\n" " int var[ 3 ];\n" "} ;\n" "S var[2];\n" "var[0].var[ 0 ] = 0;\n" "var[0].var[ 1 ] = 1;\n" "var[0].var[ 2 ] = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid FPs (modified examples taken from #3838) check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab;\n" " int * p = &ab.a[10];\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab[1];\n" " int * p = &ab[0].a[10];\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab[1];\n" " int * p = &ab[10].a[0];\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'ab[1]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_44() { // #3979 (false positive) check("void f()\n" "{\n" " char buf[2];\n" " int i;\n" " for (i = 2; --i >= 0; )\n" " {\n" " buf[i] = 1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " double buf[2];\n" " for (int i = 2; i--; )\n" " {\n" " buf[i] = 2.;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_45() { // #4207 - handling of function with variable number of parameters / unnamed arguments // Variable number of arguments check("void f(const char *format, ...) {\n" " va_args args;\n" " va_start(args, format);\n" "}\n" "void test() {\n" " CHAR buffer[1024];\n" " f(\"%s\", buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); // Unnamed argument check("void f(char *) {\n" " dostuff();\n" "}\n" "void test() {\n" " char buffer[1024];\n" " f(buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Two statement for-loop void array_index_46() { // #4840 check("void bufferAccessOutOfBounds2() {\n" " char *buffer[]={\"a\",\"b\",\"c\"};\n" " for(int i=3; i--;) {\n" " printf(\"files(%i): %s\", 3-i, buffer[3-i]);\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 'buffer[3]' accessed at index 3, which is out of bounds.\n", "", errout.str()); check("void f() {\n" " int buffer[9];\n" " long int i;\n" " for(i=10; i--;) {\n" " buffer[i] = i;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buffer[9]' accessed at index 9, which is out of bounds.\n", errout.str()); // Correct access limits -> i from 9 to 0 check("void f() {\n" " int buffer[10];\n" " for(unsigned long int i=10; i--;) {\n" " buffer[i] = i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_47() { // #5849 check("int s[4];\n" "void f() {\n" " for (int i = 2; i < 0; i++)\n" " s[i] = 5;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_48() { // #9478 check("void test(void)\n" "{\n" " int array[4] = { 1,2,3,4 };\n" " for (int i = 1; i <= 4; i++) {\n" " printf(\" %i\", i);\n" " array[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'array[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void test(void)\n" "{\n" " int array[4] = { 1,2,3,4 };\n" " for (int i = 1; i <= 4; i++) {\n" " scanf(\"%i\", &i);\n" " array[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_49() { // #8653 check("void f() {\n" " int i, k;\n" " int arr[34] = {};\n" " i = 1;\n" " for (k = 0; k < 34 && i < 34; k++) {\n" " i++;\n" " }\n" " arr[k];\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_50() { check("void f(const char * str) {\n" " int len = strlen(str);\n" " (void)str[len - 1];\n" "}\n" "void g() {\n" " f(\"12345678\");\n" " f(\"12345\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_51() { check("void f(void){\n" " int k=0, dd, d[1U] = {1};\n" " for (dd=d[k]; k<10; dd=d[++k]){;}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'd[1]' accessed at index 1, which is out of bounds.\n", errout.str()); } void array_index_52() { check("char f(void)\n" "{\n" " char buf[10];\n" " for(int i = 0, j= 11; i < j; ++i)\n" " buf[i] = 0;\n" " return buf[0];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buf[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_53() { check("double M[3][1];\n" " \n" "void matrix()\n" "{\n" " for (int i=0; i < 3; i++)\n" " for (int j = 0; j < 3; j++)\n" " M[i][j]=0.0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'M[3][1]' accessed at index M[*][2], which is out of bounds.\n", errout.str()); } void array_index_54() { check("void f() {\n" " g(0);\n" "}\n" "void g(unsigned int x) {\n" " int b[4];\n" " for (unsigned int i = 0; i < 4; i += 2) {\n" " b[i] = 0;\n" " b[i+1] = 0;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void array_index_55() { check("void make(const char* s, size_t len) {\n" " for (size_t i = 0; i < len; ++i)\n" " s[i];\n" "}\n" "void make(const char* s) {\n" " make(s, strlen(s));\n" "}\n" "void f() {\n" " make(\"my-utf8-payload\");\n" "}\n" "void f2() {\n" " make(\"false\");\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void array_index_56() { check("struct s {\n" " int array[1];\n" " int index;\n" "};\n" "void f(struct s foo) {\n" " foo.array[foo.index++] = 1;\n" " if (foo.index == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void array_index_57() { check("void f(std::vector& v) {\n" " int a[3] = { 1, 2, 3 };\n" " int i = 0;\n" " for (auto& x : v) {\n" " int c = a[i++];\n" " if (i == 3)\n" " i = 0;\n" " x = c;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" " int a[3] = { 1, 2, 3 };\n" " int i = 0;\n" " for (auto& x : v) {\n" " int c = a[i++];\n" " if (i == 4)\n" " i = 0;\n" " x = c;\n" " }\n" "}\n"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:5]: (warning) Either the condition 'i==4' is redundant or the array 'a[3]' is accessed at index 3, which is out of bounds.\n", errout.str()); } void array_index_58() { check("int f(int x, int y) {\n" " int a[3]= {0,1,2};\n" " if(x<2)\n" " y = a[x] + 1;\n" " else\n" " y = a[x];\n" " return y;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:6]: (warning) Either the condition 'x<2' is redundant or the array 'a[3]' is accessed at index 3, which is out of bounds.\n", errout.str()); } void array_index_59() { check("long f(long b) {\n" " const long a[] = { 0, 1, };\n" " const long c = std::size(a);\n" " if (b < 0 || b >= c)\n" " return 0;\n" " return a[b];\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void array_index_60() { checkP("#define CKR(B) if (!(B)) { return -1; }\n" "int f(int i) {\n" " const int A[3] = {};\n" " CKR(i < 3);\n" " if (i > 0)\n" " i = A[i];\n" " return i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkP("#define ASSERT(expression, action) if (expression) {action;}\n" "int array[5];\n" "void func (int index) {\n" " ASSERT(index > 5, return);\n" " array[index]++;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:5]: (warning) Either the condition 'index>5' is redundant or the array 'array[5]' is accessed at index 5, which is out of bounds.\n", errout.str()); } void array_index_61() { check("int f(int i) {\n" " const int M[] = { 0, 1, 2, 3 };\n" " if (i > 4)\n" " return -1;\n" " if (i < 0 || i == std::size(M))\n" " return 0; \n" " return M[i];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct S { enum E { e0 }; };\n" "const S::E M[4] = { S::E:e0, S::E:e0, S::E:e0, S::E:e0 };\n" "int f(int i) {\n" " if (i > std::size(M) + 1)\n" " return -1;\n" " if (i < 0 || i >= std::size(M))\n" " return 0;\n" " return M[i]; \n" "}\n"); ASSERT_EQUALS("", errout.str()); } void array_index_multidim() { check("void f()\n" "{\n" " char a[2][2];\n" " a[1][1] = 'a';\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[1][1][1] = 'a';\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[2][2];\n" " a[2][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2]' accessed at index a[2][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2];\n" " a[1][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2]' accessed at index a[1][2], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[2][1][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[2][1][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[1][2][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[1][2][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2][2];\n" " a[1][2][1][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2][2]' accessed at index a[1][2][1][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[1][1][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[1][1][2], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[10][10][10];\n" " a[2*3][4*3][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10][10][10]' accessed at index a[6][12][2], which is out of bounds.\n", errout.str()); check("void f() {\n" " char a[10][10][10];\n" " a[6][40][10] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10][10][10]' accessed at index a[6][40][10], which is out of bounds.\n", errout.str()); check("void f() {\n" " char a[1][1][1];\n" " a[2][2][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[1][1][1]' accessed at index a[2][2][2], which is out of bounds.\n", errout.str()); check("void f() {\n" " char a[6][6][6];\n" " a[6][6][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[6][6][6]' accessed at index a[6][6][2], which is out of bounds.\n", errout.str()); check("void f() {\n" " int a[2][2];\n" " p = &a[2][0];\n" "}"); ASSERT_EQUALS("", errout.str()); // unknown dim.. check("void f()\n" "{\n" " int a[2][countof(x)] = {{1,2},{3,4}};\n" " a[0][0] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void draw_quad(float z) {\n" " int i;\n" " float (*vertices)[2][4];\n" " vertices[0][0][0] = z;\n" " vertices[0][0][1] = z;\n" " vertices[1][0][0] = z;\n" " vertices[1][0][1] = z;\n" " vertices[2][0][0] = z;\n" " vertices[2][0][1] = z;\n" " vertices[3][0][0] = z;\n" " vertices[3][0][1] = z;\n" " for (i = 0; i < 4; i++) {\n" " vertices[i][0][2] = z;\n" " vertices[i][0][3] = 1.0;\n" " vertices[i][1][0] = 2.0;\n" " vertices[i][1][1] = 3.0;\n" " vertices[i][1][2] = 4.0;\n" " vertices[i][1][3] = 5.0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); { check("int foo() {\n" " const size_t A = 4;\n" " const size_t B = 2;\n" " extern int stuff[A][B];\n" " return stuff[0][1];\n" "}"); ASSERT_EQUALS("", errout.str()); // TODO: better handling of VLAs in symboldatabase. should be // possible to use ValueFlow values. check("int foo() {\n" " const size_t A = 4;\n" " const size_t B = 2;\n" " extern int stuff[A][B];\n" " return stuff[0][1];\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } } void array_index_switch_in_for() { check("void f()\n" "{\n" " int ar[10];\n" " for (int i = 0; i < 10; ++i)\n" " {\n" " switch(i)\n" " {\n" " case 9:\n" " ar[i] = 0;\n" " break;\n" " default:\n" " ar[i] = ar[i+1];\n" " break;\n" " };\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int ar[10];\n" " for (int i = 0; i < 10; ++i)\n" " {\n" " switch(i)\n" " {\n" " case 8:\n" " ar[i] = 0;\n" " break;\n" " default:\n" " ar[i] = ar[i+1];\n" " break;\n" " };\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:12]: (error) Array index out of bounds.\n", "", errout.str()); } void array_index_for_in_for() { check("void f() {\n" " int a[5];\n" " for (int i = 0; i < 10; ++i) {\n" " for (int j = i; j < 5; ++j) {\n" " a[i] = 0;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_bounds() { // #10275 check("int a[10];\n" "void f(int i) {\n" " if (i >= 0 && i < 10) {}\n" " a[i] = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'i<10' is redundant or the array 'a[10]' is accessed at index 10, which is out of bounds.\n" "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'i>=0' is redundant or the array 'a[10]' is accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_calculation() { // #1193 - false negative: array out of bounds in loop when there is calculation check("void f()\n" "{\n" " char data[8];\n" " for (int i = 19; i < 36; ++i) {\n" " data[i/2] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[8]' accessed at index 17, which is out of bounds.\n", errout.str()); // #2199 - false negative: array out of bounds in loop when there is calculation check("void f()\n" "{\n" " char arr[5];\n" " for (int i = 0; i < 5; ++i) {\n" " arr[i + 7] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'arr[5]' accessed at index 11, which is out of bounds.\n", errout.str()); } void array_index_negative1() { // #948 - array index out of bound not detected 'a[-1] = 0' check("void f()\n" "{\n" " char data[8];\n" " data[-1] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'data[8]' accessed at index -1, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char data[8][4];\n" " data[5][-1] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'data[8][4]' accessed at index data[*][-1], which is out of bounds.\n", errout.str()); // #1614 - negative index is ok for pointers check("void foo(char *p)\n" "{\n" " p[-1] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char s[] = \"abc\";\n" " char *p = s + strlen(s);\n" " if (p[-1]);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #1850 check("int f(const std::map > &m)\n" "{\n" " return m[0][-1];\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_negative2() { // ticket #3063 check("struct TEST { char a[10]; };\n" "void foo() {\n" " TEST test;\n" " test.a[-1] = 3;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'test.a[10]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_negative3() { check("int f(int i) {\n" " int p[2] = {0, 0};\n" " if(i >= 2)\n" " return 0;\n" " else if(i == 0)\n" " return 0;\n" " return p[i - 1];\n" "}\n" "void g(int i) {\n" " if( i == 0 )\n" " return f(i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_negative4() { check("void f(void) {\n" " int buf[64]={};\n" " int i;\n" " for(i=0; i <16; ++i){}\n" " for(; i < 24; ++i){ buf[i] = buf[i-16];}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void array_index_for_decr() { check("void f()\n" "{\n" " char data[8];\n" " for (int i = 10; i > 0; --i) {\n" " data[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[8]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char val[5];\n" " for (unsigned int i = 3; i < 5; --i) {\n" " val[i+1] = val[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char val[5];\n" " for (int i = 3; i < 5; --i) {\n" " val[i+1] = val[i];\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'val[5]' accessed at index -9994, which is out of bounds.\n" "[test.cpp:5]: (error) Array 'val[5]' accessed at index -9995, which is out of bounds.\n", errout.str()); } void array_index_varnames() { check("struct A {\n" " char data[4];\n" " struct B { char data[3]; };\n" " B b;\n" "};\n" "\n" "void f()\n" "{\n" " A a;\n" " a.data[3] = 0;\n" " a.b.data[2] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #1586 check("struct A {\n" " char data[4];\n" " struct B { char data[3]; };\n" " B b;\n" "};\n" "\n" "void f()\n" "{\n" " A a;\n" " a.data[4] = 0;\n" " a.b.data[3] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'a.data[4]' accessed at index 4, which is out of bounds.\n" "[test.cpp:11]: (error) Array 'a.b.data[3]' accessed at index 3, which is out of bounds.\n", errout.str()); } void array_index_for_andand_oror() { // #3907 - using && or || // extracttests.start: volatile int y; check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; x < 10 && y; x++) {\n" " data[x] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str()); check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; x < 10 || y; x++) {\n" " data[x] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str()); check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; x <= 10 && y; x++) {\n" " data[x] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; y && x <= 10; x++) {\n" " data[x] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str()); check("int f() {\n" // #9126 " int i, c;\n" " char* words[100] = {0};\n" " g(words);\n" " for (i = c = 0; (i < N) && (c < 1); i++) {\n" " if (words[i][0] == '|')\n" " c++;\n" " }\n" " return c;\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); } void array_index_for_continue() { // #3913 check("void f() {\n" " int a[2];\n" " for (int i = 0; i < 2; ++i) {\n" " if (i == 0) {\n" " continue;\n" " }\n" " a[i - 1] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // extracttests.start: int maybe(); check("void f() {\n" " int a[2];\n" " for (int i = 0; i < 2; ++i) {\n" " if (maybe()) {\n" " continue;\n" " }\n" " a[i - 1] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'a[2]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_for() { // Ticket #2370 - No false negative when there is no "break" check("void f() {\n" " int a[10];\n" " for (int i = 0; i < 20; ++i) {\n" " if (i==1) {\n" " }\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); // Ticket #2385 - No false positive check("void f() {\n" " int a[10];\n" " for (int i = 0; i < 20; ++i) {\n" " if (i<10) {\n" " } else {\n" " a[i-10] = 0;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #3893 - start value out of bounds // extracttests.start: int maybe(); int dostuff(); check("void f() {\n" " int a[10];\n" " for (int i = 10; maybe(); dostuff()) {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_for_neq() { // Ticket #2211 - for loop using != in the condition check("void f() {\n" " int a[5];\n" " for (int i = 0; i != 10; ++i) {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[5]' accessed at index 9, which is out of bounds.\n", errout.str()); } void array_index_for_question() { // Ticket #2561 - using ?: inside for loop check("void f() {\n" " int a[10];\n" " for (int i = 0; i != 10; ++i) {\n" " i == 0 ? 0 : a[i-1];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[10];\n" " for (int i = 0; i != 10; ++i) {\n" " some_condition ? 0 : a[i-1];\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array index -1 is out of bounds.\n", "", errout.str()); check("void f() {\n" " int a[10];\n" " for (int i = 0; i != 10; ++i) {\n" " i==0 ? 0 : a[i-1];\n" " a[i-1] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_for_varid0() { // #4228: No varid for counter variable check("void f() {\n" " char a[10];\n" " for (i=0; i<10; i++);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_vla_for() { // #3221 - access VLA inside for check("void f(int len) {\n" " char a[len];\n" " for (int i=0; i<7; ++i) {\n" " a[0] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_extern() { // Ticket #1684. FP when using 'extern'. check("extern char arr[15];\n" "char arr[15] = \"abc\";"); ASSERT_EQUALS("", errout.str()); } void array_index_cast() { // Ticket #2841. FP when using cast. // Different types => no error check("void f1(char *buf) {\n" " buf[4] = 0;\n" "}\n" "void f2() {\n" " int x[2];\n" " f1(x);\n" "}"); ASSERT_EQUALS("", errout.str()); // Same type => error check("void f1(const char buf[]) {\n" " char c = buf[4];\n" "}\n" "void f2() {\n" " char x[2];\n" " f1(x);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array 'x[2]' accessed at index 4, which is out of bounds.\n", "", errout.str()); } void array_index_string_literal() { check("void f() {\n" " const char *str = \"abc\";\n" " bar(str[10]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'str[4]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " const char *str = \"abc\";\n" " bar(str[4]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " const char *str = \"abc\";\n" " bar(str[3]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " const char *str = \"a\tc\";\n" " bar(str[4]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void f() {\n" // #6973 " const char *name = \"\";\n" " if ( name[0] == 'U' ? name[1] : 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char **argv) {\n" " char str[6] = \"\\0\";\n" " unsigned short port = 65535;\n" " snprintf(str, sizeof(str), \"%hu\", port);\n" "}", settings0, "test.c"); ASSERT_EQUALS("", errout.str()); } void array_index_same_struct_and_var_name() { // don't throw internal error check("struct tt {\n" " char name[21];\n" "} ;\n" "void doswitch(struct tt *x)\n" "{\n" " struct tt *tt=x;\n" " tt->name;\n" "}"); ASSERT_EQUALS("", errout.str()); // detect error check("struct tt {\n" " char name[21];\n" "} ;\n" "void doswitch(struct tt *x)\n" "{\n" " struct tt *tt=x;\n" " tt->name[22] = 123;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'tt->name[21]' accessed at index 22, which is out of bounds.\n", errout.str()); } void array_index_valueflow() { check("void f(int i) {\n" " char str[3];\n" " str[i] = 0;\n" " if (i==10) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'i==10' is redundant or the array 'str[3]' is accessed at index 10, which is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char str[3];\n" " str[i] = 0;\n" " switch (i) {\n" " case 10: break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning) Either the switch case 'case 10' is redundant or the array 'str[3]' is accessed at index 10, which is out of bounds.\n", errout.str()); check("void f() {\n" " char str[3];\n" " str[((unsigned char)3) - 1] = 0;\n" "}", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #5416 FP " char *str[3];\n" " do_something(&str[0][5]);\n" "}", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("class X { static const int x[100]; };\n" // #6070 "const int X::x[100] = {0};"); ASSERT_EQUALS("", errout.str()); check("namespace { class X { static const int x[100]; };\n" // #6232 "const int X::x[100] = {0}; }"); ASSERT_EQUALS("", errout.str()); check("class ActorSprite { static ImageSet * targetCursorImages[2][10]; };\n" "ImageSet *ActorSprite::targetCursorImages[2][10];"); ASSERT_EQUALS("", errout.str()); } void array_index_valueflow_pointer() { check("void f() {\n" " int a[10];\n" " int *p = a;\n" " p[20] = 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[10]' accessed at index 20, which is out of bounds.\n", "", errout.str()); { // address of check("void f() {\n" " int a[10];\n" " int *p = a;\n" " p[10] = 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", "", errout.str()); check("void f() {\n" " int a[10];\n" " int *p = a;\n" " dostuff(&p[10]);\n" "}"); ASSERT_EQUALS("", errout.str()); } check("void f() {\n" " int a[X];\n" // unknown size " int *p = a;\n" " p[20] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[2];\n" " char *p = (char *)a;\n" // cast " p[4] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_function_parameter() { check("void f(char a[10]) {\n" " a[20] = 0;\n" // <- cppcheck warn here even though it's not a definite access out of bounds "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Array 'a[10]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f(char a[10]) {\n" // #6353 - reassign 'a' " a += 4;\n" " a[-1] = 0;\n" "}"); TODO_ASSERT_EQUALS("", "[test.cpp:3]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_enum_array() { // #8439 check("enum E : unsigned int { e1, e2 };\n" "void f() {\n" " E arrE[] = { e1, e2 };\n" " arrE[sizeof(arrE)] = e1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'arrE[2]' accessed at index 8, which is out of bounds.\n", errout.str()); } void array_index_container() { // #9386 check("constexpr int blockLen = 10;\n" "void foo(std::array& a) {\n" " a[2] = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_two_for_loops() { check("bool b();\n" "void f()\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = 1; b() && i < 50; i++)\n" " sum += val[i];\n" " if (i < 50)\n" " sum -= val[i];\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool b();\n" "void f()\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = 1; b() && i < 50; i++)\n" " sum += val[i];\n" " for (; i < 50;) {\n" " sum -= val[i];\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool b();\n" "void f()\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = 1; b() && i < 50; i++)\n" " sum += val[i];\n" " for (; i < 50; i++)\n" " sum -= val[i];\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_2_struct() { check("struct ABC\n" "{\n" " char str[5];\n" "};\n" "\n" "static void f(struct ABC *abc)\n" "{\n" " strcpy( abc->str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Buffer is accessed out of bounds: abc->str\n", errout.str()); check("struct ABC\n" "{\n" " char str[5];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC abc;\n" " strcpy( abc.str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Buffer is accessed out of bounds: abc.str\n", errout.str()); check("struct ABC\n" "{\n" " char str[5];\n" "};\n" "\n" "static void f(struct ABC &abc)\n" "{\n" " strcpy( abc.str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Buffer is accessed out of bounds: abc.str\n", errout.str()); check("static void f()\n" "{\n" " struct ABC\n" " {\n" " char str[5];\n" " } abc;\n" " strcpy( abc.str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: abc.str\n", errout.str()); check("static void f()\n" "{\n" " struct ABC\n" " {\n" " char str[5];\n" " };\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " strcpy( abc->str, \"abcdef\" );\n" " free(abc);\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Buffer is accessed out of bounds: abc->str\n", errout.str()); } void buffer_overrun_3() { check("int a[10];\n" "\n" "void foo()\n" "{\n" " int i;\n" " for (i = 0; i <= 10; ++i)\n" " a[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void buffer_overrun_4() { check("void foo()\n" "{\n" " const char *p[2];\n" " for (int i = 0; i < 8; ++i)\n" " p[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'p[2]' accessed at index 7, which is out of bounds.\n", errout.str()); // No false positive check("void foo(int x, int y)\n" "{\n" " const char *p[2];\n" " const char *s = y + p[1];\n" " p[1] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // There is no error here check("void f1(char *s,int size)\n" "{\n" " if( size > 10 ) strcpy(s,\"abc\");\n" "}\n" "void f2()\n" "{\n" " char s[3];\n" " f1(s,20);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f1(char *s,int size)\n" "{\n" " if( size > 10 ) strcpy(s,\"abc\");\n" "}\n" "void f2()\n" "{\n" " char s[3];\n" " f1(s,3);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_5() { check("void f()\n" "{\n" " char n[5];\n" " sprintf(n, \"d\");\n" " printf(\"hello!\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_6() { check("void f()\n" "{\n" " char n[5];\n" " strcat(n, \"abc\");\n" " strcat(n, \"def\");\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: n\n", "", errout.str()); } void buffer_overrun_7() { // ticket #731 check("void f()\n" "{\n" " char a[2];\n" " strcpy(a, \"a\\0\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_8() { // ticket #714 check("void f()\n" "{\n" " char a[5];\n" " for (int i = 0; i < 20; i = i + 100)\n" " {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[5];\n" " for (int i = 0; i < 20; i = 100 + i)\n" " {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_9() { // ticket #738 check("void f()\n" "{\n" " char a[5];\n" " for (int i = 0; i < 20; )\n" " {\n" " a[i] = 0;\n" " i += 100;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_10() { // ticket #740 check("void f()\n" "{\n" " char a[4];\n" " for (int i = 0; i < 4; i++)\n" " {\n" " char b = a[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_11() { check("void f()\n" "{\n" " char a[4];\n" " for (float i=0; i<10.0;i=i+0.1)\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[4];\n" " for (float i=0; i<10.0;i=0.1+i)\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_15() { // ticket #1787 check("class A : public B {\n" " char val[2];\n" " void f(int i, int ii);\n" "};\n" "void A::f(int i, int ii)\n" "{\n" " strcpy(val, \"ab\") ;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: val\n", errout.str()); } void buffer_overrun_16() { // unknown types check("void f() {\n" " struct Foo foo[5];\n" " memset(foo, 0, sizeof(foo));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // ticket #2093 " gchar x[3];\n" " strcpy(x, \"12\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("extern char a[10];\n" "void f() {\n" " char b[25] = {0};\n" " std::memcpy(b, a, 10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_18() { // ticket #2576 check("class A {\n" " void foo();\n" " bool b[7];\n" "};\n" "\n" "void A::foo() {\n" " for (int i=0; i<6; i++) {\n" " b[i] = b[i+1];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " void foo();\n" " bool b[7];\n" "};\n" "\n" "void A::foo() {\n" " for (int i=0; i<7; i++) {\n" " b[i] = b[i+1];\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'b[7]' accessed at index 7, which is out of bounds.\n", errout.str()); } void buffer_overrun_19() { // #2597 - class member with unknown type check("class A {\n" "public:\n" " u8 buf[10];\n" " A();" "};\n" "\n" "A::A() {\n" " memset(buf, 0, 10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_21() { check("void foo()\n" "{ { {\n" " char dst[4];\n" " const char *src = \"AAAAAAAAAAAAAAAAAAAAA\";\n" " for (size_t i = 0; i <= 4; i++)\n" " dst[i] = src[i];\n" "} } }"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'dst[4]' accessed at index 4, which is out of bounds.\n", errout.str()); } void buffer_overrun_24() { // index variable is changed in for-loop // ticket #4106 check("void main() {\n" " int array[] = {1,2};\n" " int x = 0;\n" " for( int i = 0; i<6; ) {\n" " x += array[i];\n" " i++; }\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // ticket #4096 check("void main() {\n" " int array[] = {1,2};\n" " int x = 0;\n" " for( int i = 0; i<6; ) {\n" " x += array[i++];\n" " }\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void buffer_overrun_26() { // ticket #4432 (segmentation fault) check("extern int split();\n" "void regress() {\n" " char inbuf[1000];\n" " char *f[10];\n" " split(inbuf, f, 10, \"\t\t\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_27() { // ticket #4444 (segmentation fault) check("void abc(struct foobar[5]);\n" "void main() {\n" "struct foobar x[5];\n" "abc(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } // #7083: false positive: typedef and initialization with strings void buffer_overrun_29() { check("typedef char testChar[10];\n" "int main(){\n" " testChar tc1 = \"\";\n" " tc1[5]='a';\n" "}"); ASSERT_EQUALS("", errout.str()); } // #6367 void buffer_overrun_30() { check("struct S { int m[9]; };\n" "int f(S * s) {\n" " return s->m[sizeof(s->m)];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 's->m[9]' accessed at index 36, which is out of bounds.\n", errout.str()); } void buffer_overrun_31() { check("void f(WhereInfo *pWInfo, int *aiCur) {\n" " memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_32() { // destination size is too small check("void f(void) {\n" " const char src[3] = \"abc\";\n" " char dest[1] = \"a\";\n" " (void)strxfrm(dest,src,1);\n" " (void)strxfrm(dest,src,2);\n"// << "}"); ASSERT_EQUALS("[test.cpp:5]: (error, inconclusive) Buffer is accessed out of bounds: dest\n", errout.str()); // destination size is too small check("void f(void) {\n" " const char src[3] = \"abc\";\n" " char dest[2] = \"ab\";\n" " (void)strxfrm(dest,src,1);\n" " (void)strxfrm(dest,src,2);\n" " (void)strxfrm(dest,src,3);\n" // << "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: dest\n", errout.str()); // source size is too small check("void f(void) {\n" " const char src[2] = \"ab\";\n" " char dest[3] = \"abc\";\n" " (void)strxfrm(dest,src,1);\n" " (void)strxfrm(dest,src,2);\n" " (void)strxfrm(dest,src,3);\n" // << "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: src\n", errout.str()); // source size is too small check("void f(void) {\n" " const char src[1] = \"a\";\n" " char dest[3] = \"abc\";\n" " (void)strxfrm(dest,src,1);\n" " (void)strxfrm(dest,src,2);\n" // << "}"); ASSERT_EQUALS("[test.cpp:5]: (error, inconclusive) Buffer is accessed out of bounds: src\n", errout.str()); } void buffer_overrun_33() { // #2019 check("int f() {\n" " int z[16];\n" " for (int i=0; i<20; i++)\n" " for (int j=0; j<20; j++)\n" " z[i] = 0;\n" " return z[0];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'z[16]' accessed at index 19, which is out of bounds.\n", errout.str()); } void buffer_overrun_errorpath() { setMultiline(); settings0.templateLocation = "{file}:{line}:note:{info}"; check("void f() {\n" " char *p = malloc(10);\n" " memset(p, 0, 20);\n" "}"); ASSERT_EQUALS("test.cpp:3:error:Buffer is accessed out of bounds: p\n" "test.cpp:2:note:Assign p, buffer with size 10\n" "test.cpp:3:note:Buffer overrun\n", errout.str()); } void buffer_overrun_bailoutIfSwitch() { // No false positive check("void f1(char *s) {\n" " if (x) s[100] = 0;\n" "}\n" "\n" "void f2() {\n" " char a[10];\n" " f1(a);" "}"); ASSERT_EQUALS("", errout.str()); // No false positive check("void f1(char *s) {\n" " if (x) return;\n" " s[100] = 0;\n" "}\n" "\n" "void f2() {\n" " char a[10];\n" " f1(a);" "}"); ASSERT_EQUALS("", errout.str()); // No false negative check("void f1(char *s) {\n" " if (x) { }\n" " s[100] = 0;\n" "}\n" "\n" "void f2() {\n" " char a[10];\n" " f1(a);" "}"); TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (error) Array 'a[10]' accessed at index 100, which is out of bounds.\n", "", errout.str()); } void buffer_overrun_function_array_argument() { setMultiline(); check("void f(char a[10]);\n" "void g() {\n" " char a[2];\n" " f(a);\n" "}"); ASSERT_EQUALS("test.cpp:4:warning:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n" "test.cpp:4:note:Function 'f' is called\n" "test.cpp:1:note:Declaration of 1st function argument.\n" "test.cpp:3:note:Passing buffer 'a' to function that is declared here\n" "test.cpp:4:note:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n", errout.str()); check("void f(float a[10][3]);\n" "void g() {\n" " float a[2][3];\n" " f(a);\n" "}"); ASSERT_EQUALS("test.cpp:4:warning:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n" "test.cpp:4:note:Function 'f' is called\n" "test.cpp:1:note:Declaration of 1st function argument.\n" "test.cpp:3:note:Passing buffer 'a' to function that is declared here\n" "test.cpp:4:note:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n", errout.str()); check("void f(int a[20]);\n" "void g() {\n" " int a[2];\n" " f(a);\n" "}"); ASSERT_EQUALS("test.cpp:4:warning:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n" "test.cpp:4:note:Function 'f' is called\n" "test.cpp:1:note:Declaration of 1st function argument.\n" "test.cpp:3:note:Passing buffer 'a' to function that is declared here\n" "test.cpp:4:note:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n", errout.str()); check("void f(int a[]) {\n" " switch (2) {\n" " case 1:\n" " a[1] = 1;\n" " }\n" "}\n" "int a[1];\n" "f(a);\n" ""); ASSERT_EQUALS("", errout.str()); check("void CreateLeafTex(unsigned char buf[256][2048][4]);\n" "void foo() {\n" " unsigned char(* tree)[2048][4] = new unsigned char[256][2048][4];\n" " CreateLeafTex(tree);\n" "}"); ASSERT_EQUALS("", errout.str()); } void possible_buffer_overrun_1() { // #3035 check("void foo() {\n" " char * data = (char *)alloca(50);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcat(data, src);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Possible buffer overflow if strlen(src) is larger than sizeof(data)-strlen(data).\n", "", errout.str()); check("void foo() {\n" " char * data = (char *)alloca(100);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcat(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char src[100]) {\n" " char * data = (char *)alloca(50);\n" " strcat(data, src);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) Possible buffer overflow if strlen(src) is larger than sizeof(data)-strlen(data).\n", "", errout.str()); check("void foo(char src[100]) {\n" " char * data = (char *)alloca(100);\n" " strcat(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char * data = (char *)alloca(50);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcpy(data, src);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Possible buffer overflow if strlen(src) is larger than or equal to sizeof(data).\n", "", errout.str()); check("void foo() {\n" " char * data = (char *)alloca(100);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcpy(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char src[100]) {\n" " char * data = (char *)alloca(50);\n" " strcpy(data, src);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) Possible buffer overflow if strlen(src) is larger than or equal to sizeof(data).\n", "", errout.str()); check("void foo(char src[100]) {\n" " char * data = (char *)alloca(100);\n" " strcpy(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_readSizeFromCfg() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); // Attempt to get size from Cfg files, no false positives if size is not specified check("void f() {\n" " u8 str[256];\n" " mystrcpy(str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " u8 str[2];\n" " mystrcpy(str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); // The same for structs, where the message comes from a different check check("void f() {\n" " struct { u8 str[256]; } ms;\n" " mystrcpy(ms.str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct { u8 str[2]; } ms;\n" " mystrcpy(ms.str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: ms.str\n", errout.str()); } void valueflow_string() { // using ValueFlow string values in checking check("char f() {\n" " const char *x = s;\n" " if (cond) x = \"abcde\";\n" " return x[20];\n" // <- array index out of bounds when x is "abcde" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'x[6]' accessed at index 20, which is out of bounds.\n", errout.str()); } void pointer_out_of_bounds_1() { // extracttests.start: void dostuff(char *); check("void f() {\n" " char a[10];\n" " char *p = a + 100;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'a+100' is out of bounds.\n", errout.str()); check("char *f() {\n" " char a[10];\n" " return a + 100;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'a+100' is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char x[10];\n" " if (i == 123) {}\n" " dostuff(x+i);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (portability) Undefined behaviour, when 'i' is 123 the pointer arithmetic 'x+i' is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char x[10];\n" " if (i == -1) {}\n" " dostuff(x+i);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (portability) Undefined behaviour, when 'i' is -1 the pointer arithmetic 'x+i' is out of bounds.\n", errout.str()); check("void f() {\n" // #6350 - fp when there is cast of buffer " wchar_t buf[64];\n" " p = (unsigned char *) buf + sizeof (buf);\n" "}", "6350.c"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " const char d[] = \"0123456789\";\n" " char *cp = d + 3;\n" " return cp - d;\n" "}"); ASSERT_EQUALS("", errout.str()); } void pointer_out_of_bounds_2() { check("void f() {\n" " char *p = malloc(10);\n" " p += 100;\n" " free(p);" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'p+100' is out of bounds.\n", "", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " p += 10;\n" " *p = 0;\n" " free(p);" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) p is out of bounds.\n", "", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " p += 10;\n" " p -= 10;\n" " *p = 0;\n" " free(p);" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " p += 10;\n" " p = p - 1;\n" " *p = 0;\n" " free(p);" "}"); ASSERT_EQUALS("", errout.str()); } void pointer_out_of_bounds_3() { check("struct S { int a[10]; };\n" "void f(struct S *s) {\n" " int *p = s->a + 100;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 's->a+100' is out of bounds.\n", errout.str()); check("template class Vector\n" "{\n" "public:\n" " void test() const;\n" " T* data();\n" "};\n" "template \n" "void Vector::test() const\n" "{\n" " const T* PDat = data();\n" " const T* P2 = PDat + 1;\n" " const T* P1 = P2 - 1;\n" "}\n" "Vector> Foo;\n"); ASSERT_EQUALS("", errout.str()); } void pointer_out_of_bounds_4() { check("const char* f() {\n" " g(\"Hello\" + 6);\n" "}"); ASSERT_EQUALS("", errout.str()); check("const char* f() {\n" " g(\"Hello\" + 7);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Undefined behaviour, pointer arithmetic '\"Hello\"+7' is out of bounds.\n", errout.str()); check("const char16_t* f() {\n" " g(u\"Hello\" + 6);\n" "}"); ASSERT_EQUALS("", errout.str()); check("const char16_t* f() {\n" " g(u\"Hello\" + 7);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Undefined behaviour, pointer arithmetic 'u\"Hello\"+7' is out of bounds.\n", errout.str()); check("void f() {\n" // #4647 " int val = 5;\n" " std::string hi = \"hi\" + val;\n" " std::cout << hi << std::endl;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic '\"hi\"+val' is out of bounds.\n", errout.str()); } void pointer_out_of_bounds_sub() { // extracttests.start: void dostuff(char *); check("char *f() {\n" " char x[10];\n" " return x-1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'x-1' is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char x[10];\n" " if (i == 123) {}\n" " dostuff(x-i);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (portability) Undefined behaviour, when 'i' is 123 the pointer arithmetic 'x-i' is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char x[10];\n" " if (i == -20) {}\n" " dostuff(x-i);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (portability) Undefined behaviour, when 'i' is -20 the pointer arithmetic 'x-i' is out of bounds.\n", "", errout.str()); check("void f(const char *x[10]) {\n" " return x-4;\n" "}"); ASSERT_EQUALS("", errout.str()); } void strcat1() { check("struct Foo { char a[4]; };\n" "void f() {\n" " struct Foo x = {0};\n" " strcat(x.a, \"aa\");\n" " strcat(x.a, \"aa\");\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); } void varid1() { check("void foo()\n" "{\n" " char str[10];\n" " if (str[0])\n" " {\n" " char str[50];\n" " str[30] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varid2() { // #4764 check("struct foo {\n" " void bar() { return; }\n" " type<> member[1];\n" "};"); ASSERT_EQUALS("", errout.str()); } void assign1() { check("char str[3] = {'a', 'b', 'c'};\n" "\n" "void foo()\n" "{\n" " str[3] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'str[3]' accessed at index 3, which is out of bounds.\n", errout.str()); } void alloc_new() { check("void foo()\n" "{\n" " char *s; s = new char[10];\n" " s[10] = 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", "", errout.str()); // ticket #1670 - false negative when using return check("char f()\n" "{\n" " int *s; s = new int[10];\n" " return s[10];\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", "", errout.str()); check("struct Fred { char c[10]; };\n" "char f()\n" "{\n" " Fred *f; f = new Fred;\n" " return f->c[10];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'f->c[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("static const size_t MAX_SIZE = UNAVAILABLE_TO_CPPCHECK;\n" "struct Thing { char data[MAX_SIZE]; };\n" "char f4(const Thing& t) { return !t.data[0]; }"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char * buf; buf = new char[8];\n" " buf[7] = 0;\n" " delete [] buf;\n" " buf = new char[9];\n" " buf[8] = 0;\n" " delete [] buf;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char * buf; buf = new char[8];\n" " buf[7] = 0;\n" " delete [] buf;\n" " buf = new char[9];\n" " buf[9] = 0;\n" " delete [] buf;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Array 'buf[9]' accessed at index 9, which is out of bounds.\n", "", errout.str()); check("void foo()\n" "{\n" " enum E { Size = 10 };\n" " char *s; s = new char[Size];\n" " s[Size] = 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", "", errout.str()); check("void foo()\n" "{\n" " enum E { ZERO };\n" " E *e; e = new E[10];\n" " e[10] = ZERO;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Array 'e[10]' accessed at index 10, which is out of bounds.\n", "", errout.str()); } // data is allocated with malloc void alloc_malloc() { check("void foo()\n" "{\n" " char *s; s = (char *)malloc(10);\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // ticket #842 check("void f() {\n" " int *tab4 = (int *)malloc(20 * sizeof(int));\n" " tab4[20] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); // ticket #1478 check("void foo() {\n" " char *p = (char *)malloc(10);\n" " free(p);\n" " p = (char *)malloc(10);\n" " p[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'p[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // ticket #1134 check("void f() {\n" " int *x, i;\n" " x = (int *)malloc(10 * sizeof(int));\n" " x[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'x[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f() {\n" " int *tab4; tab4 = malloc(20 * sizeof(int));\n" " tab4[19] = 0;\n" " free(tab4);\n" " tab4 = malloc(21 * sizeof(int));\n" " tab4[20] = 0;\n" " free(tab4);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *tab4 = malloc(20 * sizeof(int));\n" " tab4[19] = 0;\n" " tab4 = realloc(tab4,21 * sizeof(int));\n" " tab4[20] = 0;\n" " free(tab4);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum E { Size = 20 };\n" " E *tab4 = (E *)malloc(Size * 4);\n" " tab4[Size] = Size;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f() {\n" " enum E { Size = 20 };\n" " E *tab4 = (E *)malloc(4 * Size);\n" " tab4[Size] = Size;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f() {\n" " enum E { ZERO };\n" " E *tab4 = (E *)malloc(20 * sizeof(E));\n" " tab4[20] = ZERO;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f() {\n" // #8721 " unsigned char **cache = malloc(32);\n" " cache[i] = malloc(65536);\n" " cache[i][0xFFFF] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int **a = malloc(2 * sizeof(int*));\n" " for (int i = 0; i < 3; i++)\n" " a[i] = NULL;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", errout.str()); check("void f() {\n" " int **a = new int*[2];\n" " for (int i = 0; i < 3; i++)\n" " a[i] = NULL;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", "", errout.str()); } // statically allocated buffer void alloc_string() { check("void foo()\n" "{\n" " const char *s = \"123\";\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[4]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void foo()\n" "{\n" " char *s; s = \"\";\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[1]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void foo() {\n" " const char *s = \"\";\n" " s = y();\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" // #7718 "{\n" " std::string s = \"123\";\n" " s.resize(100);\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } // data is allocated with alloca void alloc_alloca() { check("void foo()\n" "{\n" " char *s = (char *)alloca(10);\n" " s[10] = 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", "", errout.str()); } /* void countSprintfLength() const { std::list unknownParameter(1, nullptr); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("Hello", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("s", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("i", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%d", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%1d", unknownParameter)); ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("%2.2d", unknownParameter)); ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("%s", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("f%s", unknownParameter)); ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("%-s", unknownParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-5s", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("\\\"", unknownParameter)); ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("Hello \\0Text", unknownParameter)); ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("\\0", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%%", unknownParameter)); ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("%d%d", unknownParameter)); ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("\\\\a%s\\0a", unknownParameter)); ASSERT_EQUALS(10, CheckBufferOverrun::countSprintfLength("\\\\\\\\Hello%d \\0Text\\\\\\\\", unknownParameter)); ASSERT_EQUALS(4, CheckBufferOverrun::countSprintfLength("%%%%%d", unknownParameter)); Token strTok; std::list stringAsParameter(1, &strTok); strTok.str("\"\""); ASSERT_EQUALS(4, CheckBufferOverrun::countSprintfLength("str%s", stringAsParameter)); strTok.str("\"12345\""); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("str%s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-4s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-5s", stringAsParameter)); ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("%-6s", stringAsParameter)); ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%.4s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.5s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.6s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5.6s", stringAsParameter)); ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("%6.6s", stringAsParameter)); Token numTok; numTok.str("12345"); std::list intAsParameter(1, &numTok); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%02ld", intAsParameter)); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%08ld", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.2d", intAsParameter)); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%08.2d", intAsParameter)); TODO_ASSERT_EQUALS(5, 2, CheckBufferOverrun::countSprintfLength("%x", intAsParameter)); ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%4x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5x", intAsParameter)); ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%.4x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.5x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%1.5x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5.1x", intAsParameter)); Token floatTok; floatTok.str("1.12345f"); std::list floatAsParameter(1, &floatTok); TODO_ASSERT_EQUALS(5, 3, CheckBufferOverrun::countSprintfLength("%.2f", floatAsParameter)); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%8.2f", floatAsParameter)); TODO_ASSERT_EQUALS(5, 3, CheckBufferOverrun::countSprintfLength("%2.2f", floatAsParameter)); Token floatTok2; floatTok2.str("100.12345f"); std::list floatAsParameter2(1, &floatTok2); TODO_ASSERT_EQUALS(7, 3, CheckBufferOverrun::countSprintfLength("%2.2f", floatAsParameter2)); TODO_ASSERT_EQUALS(7, 3, CheckBufferOverrun::countSprintfLength("%.2f", floatAsParameter)); TODO_ASSERT_EQUALS(7, 5, CheckBufferOverrun::countSprintfLength("%4.2f", floatAsParameter)); std::list multipleParams = { &strTok, nullptr, &numTok }; ASSERT_EQUALS(15, CheckBufferOverrun::countSprintfLength("str%s%d%d", multipleParams)); ASSERT_EQUALS(26, CheckBufferOverrun::countSprintfLength("str%-6s%08ld%08ld", multipleParams)); } */ // extracttests.disable void minsize_argvalue() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); settings.severity.enable(Severity::warning); settings.sizeof_wchar_t = 4; check("void f() {\n" " char c[10];\n" " mymemset(c, 0, 10);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[10];\n" " mymemset(c, 0, 11);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); check("struct S {\n" " char a[5];\n" "};\n" "void f() {\n" " S s;\n" " mymemset(s.a, 0, 10);\n" "}", settings); ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: s.a\n", errout.str()); check("void foo() {\n" " char s[10];\n" " mymemset(s, 0, '*');\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) The size argument is given as a char constant.\n" "[test.cpp:3]: (error) Buffer is accessed out of bounds: s\n", "[test.cpp:3]: (error) Buffer is accessed out of bounds: s\n", errout.str()); // ticket #836 check("void f(void) {\n" " char a[10];\n" " mymemset(a+5, 0, 10);\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", "", errout.str()); // Ticket #909 check("void f(void) {\n" " char str[] = \"abcd\";\n" " mymemset(str, 0, 6);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f(void) {\n" " char str[] = \"abcd\";\n" " mymemset(str, 0, 5);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " wchar_t str[] = L\"abcd\";\n" " mymemset(str, 0, 21);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f(void) {\n" " wchar_t str[] = L\"abcd\";\n" " mymemset(str, 0, 20);\n" "}", settings); ASSERT_EQUALS("", errout.str()); // ticket #1659 - overflowing variable when using memcpy check("void f(void) {\n" " char c;\n" " mymemset(&c, 0, 4);\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", "", errout.str()); // ticket #2121 - buffer access out of bounds when using uint32_t check("void f(void) {\n" " unknown_type_t buf[4];\n" " mymemset(buf, 0, 100);\n" "}", settings); ASSERT_EQUALS("", errout.str()); // #3124 - multidimensional array check("int main() {\n" " char b[5][6];\n" " mymemset(b, 0, 5 * 6);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("int main() {\n" " char b[5][6];\n" " mymemset(b, 0, 6 * 6);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: b\n", errout.str()); check("int main() {\n" " char b[5][6];\n" " mymemset(b, 0, 31);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: b\n", errout.str()); // #4968 - not standard function check("void f() {\n" " char str[3];\n" " foo.mymemset(str, 0, 100);\n" " foo::mymemset(str, 0, 100);\n" " std::mymemset(str, 0, 100);\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", "", errout.str()); // #5257 - check strings check("void f() {\n" " mymemset(\"abc\", 0, 20);\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f() {\n" " mymemset(temp, \"abc\", 4);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #6816 - fp when array has known string value " char c[10] = \"c\";\n" " mymemset(c, 0, 10);\n" "}", settings); ASSERT_EQUALS("", errout.str()); } void minsize_sizeof() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); check("void f() {\n" " char c[7];\n" " mystrncpy(c, \"hello\", 7);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[6];\n" " mystrncpy(c,\"hello\",6);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[5];\n" " mystrncpy(c,\"hello\",6);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); check("void f() {\n" " char c[6];\n" " mystrncpy(c,\"hello!\",7);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); check("void f(unsigned int addr) {\n" " memset((void *)addr, 0, 1000);\n" "}", settings0); ASSERT_EQUALS("", errout.str()); check("struct AB { char a[10]; };\n" "void foo(AB *ab) {\n" " mystrncpy(x, ab->a, 100);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void a(char *p) { mystrncpy(p,\"hello world!\",10); }\n" // #3168 "void b() {\n" " char buf[5];\n" " a(buf);" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Buffer is accessed out of bounds: buf\n", "", errout.str()); } void minsize_strlen() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); // formatstr.. check("void f() {\n" " char str[3];\n" " mysprintf(str, \"test\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f() {\n" " char str[5];\n" " mysprintf(str, \"%s\", \"abcde\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("int getnumber();\n" "void f()\n" "{\n" " char str[5];\n" " mysprintf(str, \"%d: %s\", getnumber(), \"abcde\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f() {\n" " char str[5];\n" " mysprintf(str, \"test%s\", \"\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *str = new char[5];\n" " mysprintf(str, \"abcde\");\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f(int condition) {\n" " char str[5];\n" " mysprintf(str, \"test%s\", condition ? \"12\" : \"34\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f(int condition) {\n" " char str[5];\n" " mysprintf(str, \"test%s\", condition ? \"12\" : \"345\");\n" "}", settings); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo x;\n" " mysprintf(x.a, \"aa\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Buffer is accessed out of bounds: x.a\n", errout.str()); // ticket #900 check("void f() {\n" " char *a = new char(30);\n" " mysprintf(a, \"a\");\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f(char value) {\n" " char *a = new char(value);\n" " mysprintf(a, \"a\");\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); // This is out of bounds if 'sizeof(ABC)' is 1 (No padding) check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo *x = malloc(sizeof(Foo));\n" " mysprintf(x->a, \"aa\");\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Buffer is accessed out of bounds: x.a\n", "", errout.str()); check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo *x = malloc(sizeof(Foo) + 10);\n" " mysprintf(x->a, \"aa\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo x;\n" " mysprintf(x.a, \"aa\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Buffer is accessed out of bounds: x.a\n", errout.str()); check("struct Foo {\n" // #6668 - unknown size " char a[LEN];\n" " void f();\n" "};" "void Foo::f() {\n" " mysprintf(a, \"abcd\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); } void minsize_mul() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); check("void f() {\n" " char c[5];\n" " myfread(c, 1, 5, stdin);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[5];\n" " myfread(c, 1, 6, stdin);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); } // extracttests.enable void unknownType() { check("void f()\n" "{\n" " UnknownType *a = malloc(4);\n" "}"); ASSERT_EQUALS("", errout.str()); } // extracttests.disable void terminateStrncpy1() { check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " strncpy(baz, bar, 100);\n" " baz[99] = 0;\n" " strncpy(baz, bar, 100);\n" " baz[99] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " baz[99] = '\\0';\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " baz[x+1] = '\\0';\n" "}"); ASSERT_EQUALS("", errout.str()); // Test with invalid code that there is no segfault check("char baz[100];\n" "strncpy(baz, \"var\", 100)"); ASSERT_EQUALS("", errout.str()); // Test that there are no duplicate error messages check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " foo(baz);\n" " foo(baz);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'baz' may not be null-terminated after the call to strncpy().\n", errout.str()); } void terminateStrncpy2() { check("char *foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " bar[99] = 0;\n" " return strdup(baz);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'baz' may not be null-terminated after the call to strncpy().\n", errout.str()); } void terminateStrncpy3() { // Ticket #2170 - false positive // The function bar is risky. But it might work that way intentionally. check("char str[100];\n" "\n" "void foo(char *a) {\n" " strncpy(str, a, 100);\n" "}\n" "\n" "void bar(char *p) {\n" " strncpy(p, str, 100);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) The buffer 'str' may not be null-terminated after the call to strncpy().\n", errout.str()); } void terminateStrncpy4() { check("void bar() {\n" " char buf[4];\n" " strncpy(buf, \"ab\", 4);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void bar() {\n" " char buf[4];\n" " strncpy(buf, \"abcde\", 4);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'buf' may not be null-terminated after the call to strncpy().\n", errout.str()); } // extracttests.enable void recursive_long_time() { // Just test that recursive check doesn't take long time check("char *f2 ( char *b )\n" "{\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" "}\n" "void f()\n" "{\n" " char a[10];\n" " f2(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Ticket #1587 - crash void crash1() { check("struct struct A\n" "{\n" " int alloclen;\n" "};\n" "\n" "void foo()\n" "{\n" " struct A *str;\n" " str = malloc(4);\n" "}"); ASSERT_EQUALS("", errout.str()); } void crash2() { check("void a(char *p) {\n" " f( { if(finally_arg); } );\n" "}\n" "\n" "void b() {\n" " char arr[64];\n" " a(arr);\n" "}"); } void crash3() { check("struct b { unknown v[0]; };\n" "void d() { struct b *f; f = malloc(108); }"); } void crash4() { // #8679 check("__thread void *thread_local_var; " "int main() { " " thread_local_var = malloc(1337); " " return 0; " "}"); check("thread_local void *thread_local_var; " "int main() { " " thread_local_var = malloc(1337); " " return 0; " "}"); } void crash5() { // 8644 - token has varId() but variable() is null check("int a() {\n" " void b(char **dst) {\n" " *dst = malloc(50);\n" " }\n" "}"); } void crash6() { check("void start(char* name) {\n" "char snapname[64] = { 0 };\n" "strncpy(snapname, \"snapshot\", arrayLength(snapname));\n" "}"); } void crash7() { // 9073 - [ has no astParent check("char x[10];\n" "void f() { x[10]; }"); } void insecureCmdLineArgs() { check("int main(int argc, char *argv[])\n" "{\n" " if(argc>1)\n" " {\n" " char buf[2];\n" " char *p = strdup(argv[1]);\n" " strcpy(buf,p);\n" " free(p);\n" " }\n" " return 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char *argv[])\n" "{\n" " if(argc>1)\n" " {\n" " char buf[2] = {'\\0','\\0'};\n" " char *p = strdup(argv[1]);\n" " strcat(buf,p);\n" " free(p);\n" " }\n" " return 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(const int argc, char* argv[])\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, const char* argv[])\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(const int argc, const char* argv[])\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char* argv[])\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, const char *const *const argv, char **envp)\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(const int argc, const char *const *const argv, const char *const *const envp)\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(const int argc, const char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, const char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(const int argc, char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10];\n" " strcpy(prog, options[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, options[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10];\n" " strcpy(prog, *options);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10];\n" " strcpy(prog+3, *options);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10];\n" " if (strlen(argv[0]) < 10)\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " if (10 > strlen(argv[0]))\n" " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10];\n" " argv[0][0] = '\\0';\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); // #5835 check("int main(int argc, char* argv[]) {\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" " strcpy(prog, argv[0]);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer overrun possible for long command line arguments.\n" "[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout.str()); // #7964 check("int main(int argc, char *argv[]) {\n" " char *strcpy();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char *argv[]) {\n" " char *strcat();\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkBufferAllocatedWithStrlen() { check("void f(char *a) {\n" " char *b = new char[strlen(a)];\n" " strcpy(b, a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f(char *a) {\n" " char *b = new char[strlen(a) + 1];\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = new char[strlen(a)];\n" " a[0] = '\\0';\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = (char *)malloc(strlen(a));\n" " b = realloc(b, 10000);\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = (char *)malloc(strlen(a));\n" " strcpy(b, a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f(char *a) {\n" " char *b = (char *)malloc(strlen(a));\n" " {\n" " strcpy(b, a);\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f(char *a) {\n" " char *b = (char *)malloc(strlen(a) + 1);\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a, char *c) {\n" " char *b = (char *)realloc(c, strlen(a));\n" " strcpy(b, a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f(char *a, char *c) {\n" " char *b = (char *)realloc(c, strlen(a) + 1);\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = (char *)malloc(strlen(a));\n" " strcpy(b, a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); } void scope() { check("class A {\n" "private:\n" " struct X { char buf[10]; };\n" "};\n" "\n" "void f()\n" "{\n" " X x;\n" " x.buf[10] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A {\n" "public:\n" " struct X { char buf[10]; };\n" "};\n" "\n" "void f()\n" "{\n" " A::X x;\n" " x.buf[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'x.buf[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void getErrorMessages() { // Ticket #2292: segmentation fault when using --errorlist CheckBufferOverrun c; c.getErrorMessages(this, nullptr); } void arrayIndexThenCheck() { // extracttests.start: volatile int y; check("void f(const char s[]) {\n" " if (s[i] == 'x' && i < y) {\n" " }" "}"); ASSERT_EQUALS("", errout.str()); // No message because i is unknown and thus gets no varid. Avoid an internalError here. check("void f(const char s[], int i) {\n" " if (s[i] == 'x' && i < y) {\n" " }" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const char s[]) {\n" " for (int i = 0; s[i] == 'x' && i < y; ++i) {\n" " }" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const int a[], unsigned i) {\n" " if((a[i] < 2) && (i <= 42)) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const int a[], unsigned i) {\n" " if((a[i] < 2) && (42 >= i)) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); // extracttests.start: int elen; check("void f(char* e, int y) {\n" " if (e[y] == '/' && elen > y + 1 && e[y + 1] == '?') {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // extracttests.start: int foo(int); int func(int); check("void f(const int a[], unsigned i) {\n" " if(a[i] < func(i) && i <= 42) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const int a[], unsigned i) {\n" " if (i <= 42 && a[i] < func(i)) {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int a[], unsigned i) {\n" " if (foo(a[i] + 3) < func(i) && i <= 42) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(int i) {\n" // sizeof " sizeof(a)/sizeof(a[i]) && i < 10;\n" "}"); ASSERT_EQUALS("", errout.str()); // extracttests.start: extern int buf[]; check("void f(int i) {\n" // ?: " if ((i < 10 ? buf[i] : 1) && (i < 5 ? buf[i] : 5)){}\n" "}"); ASSERT_EQUALS("", errout.str()); } void arrayIndexEarlyReturn() { // #6884 check("extern const char *Names[2];\n" "const char* getName(int value) {\n" " if ((value < 0) || (value > 1))\n" " return \"???\";\n" " const char* name = Names[value]; \n" " switch (value) {\n" " case 2:\n" " break; \n" " }\n" " return name;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void bufferNotZeroTerminated() { check("void f() {\n" " char c[6];\n" " strncpy(c,\"hello!\",6);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' may not be null-terminated after the call to strncpy().\n", errout.str()); check("void f() {\n" " char c[6];\n" " memcpy(c,\"hello!\",6);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' may not be null-terminated after the call to memcpy().\n", "", errout.str()); check("void f() {\n" " char c[6];\n" " memmove(c,\"hello!\",6);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' may not be null-terminated after the call to memmove().\n", "", errout.str()); } void negativeMemoryAllocationSizeError() { // #389 check("void f()\n" "{\n" " int *a;\n" " a = (int *)malloc( -10 );\n" " free(a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", "", errout.str()); check("void f()\n" "{\n" " int *a;\n" " a = (int *)malloc( -10);\n" " free(a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", "", errout.str()); check("void f()\n" "{\n" " int *a;\n" " a = (int *)alloca( -10 );\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", "", errout.str()); } void negativeArraySize() { check("void f(int sz) {\n" // #1760 - VLA " int a[sz];\n" "}\n" "void x() { f(-100); }"); TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Declaration of array 'a' with negative size is undefined behaviour\n", "", errout.str()); // don't warn for constant sizes -> this is a compiler error so this is used for static assertions for instance check("int x, y;\n" "int a[-1];\n" "int b[x?1:-1];\n" "int c[x?y:-1];"); ASSERT_EQUALS("", errout.str()); } void pointerAddition1() { check("void f() {\n" " char arr[10];\n" " char *p = arr + 20;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'arr+20' is out of bounds.\n", errout.str()); } #define ctu(code) ctu_(code, __FILE__, __LINE__) void ctu_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CTU::FileInfo *ctu = CTU::getFileInfo(&tokenizer); // Check code.. std::list fileInfo; CheckBufferOverrun checkBO(&tokenizer, &settings0, this); fileInfo.push_back(checkBO.getFileInfo(&tokenizer, &settings0)); checkBO.analyseWholeProgram(ctu, fileInfo, settings0, *this); while (!fileInfo.empty()) { delete fileInfo.back(); fileInfo.pop_back(); } delete ctu; } void ctu_malloc() { ctu("void dostuff(char *p) {\n" " p[-3] = 0;\n" "}\n" "\n" "int main() {\n" " char *s = malloc(4);\n" " dostuff(s);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7] -> [test.cpp:2]: (error) Array index out of bounds; buffer 'p' is accessed at offset -3.\n", errout.str()); ctu("void dostuff(char *p) {\n" " p[4] = 0;\n" "}\n" "\n" "int main() {\n" " char *s = malloc(4);\n" " dostuff(s);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 4.\n", errout.str()); } void ctu_array() { ctu("void dostuff(char *p) {\n" " p[10] = 0;\n" "}\n" "int main() {\n" " char str[4];\n" " dostuff(str);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 10.\n", errout.str()); ctu("static void memclr( char *data )\n" "{\n" " data[10] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( str );\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (error) Array index out of bounds; 'data' buffer size is 5 and it is accessed at offset 10.\n", errout.str()); ctu("static void memclr( int i, char *data )\n" "{\n" " data[10] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( 0, str );\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (error) Array index out of bounds; 'data' buffer size is 5 and it is accessed at offset 10.\n", errout.str()); ctu("static void memclr( int i, char *data )\n" "{\n" " data[i] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( 10, str );\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (possible error) Array index out of bounds.\n", "", errout.str()); // This is not an error ctu("static void memclr( char *data, int size )\n" "{\n" " if( size > 10 )" " data[10] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( str, 5 );\n" "}"); ASSERT_EQUALS("", errout.str()); // #2097 ctu("void foo(int *p)\n" "{\n" " --p;\n" " p[2] = 0;\n" "}\n" "\n" "void bar()\n" "{\n" " int p[3];\n" " foo(p+1);\n" "}"); ASSERT_EQUALS("", errout.str()); // #9112 ctu("static void get_mac_address(const u8 *strbuf)\n" "{\n" " (strbuf[2]);\n" "}\n" "\n" "static void program_mac_address(u32 mem_base)\n" "{\n" " u8 macstrbuf[17] = { 0 };\n" " get_mac_address(macstrbuf);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ctu_variable() { ctu("void dostuff(int *p) {\n" " p[10] = 0;\n" "}\n" "int main() {\n" " int x = 4;\n" " dostuff(&x);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 40.\n", errout.str()); } void ctu_arithmetic() { ctu("void dostuff(int *p) { x = p + 10; }\n" "int main() {\n" " int x[3];\n" " dostuff(x);\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Pointer arithmetic overflow; 'p' buffer size is 12\n", errout.str()); } void objectIndex() { check("int f() {\n" " int i;\n" " return (&i)[1];\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3]: (error) The address of local variable 'i' is accessed at non-zero index.\n", errout.str()); check("int f(int j) {\n" " int i;\n" " return (&i)[j];\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3]: (warning) The address of local variable 'i' might be accessed at non-zero index.\n", errout.str()); check("int f() {\n" " int i;\n" " return (&i)[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int * i) {\n" " return i[1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector i) {\n" " return i[1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector i) {\n" " return i.data()[1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(std::vector& i) {\n" " return &(i[1]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int i; int j; };\n" "int f() {\n" " A x;\n" " return (&x.i)[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int i; int j; };\n" "int f() {\n" " A x;\n" " int * i = &x.i;\n" " return i[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 0;\n" " std::map m;\n" " m[0] = &x;\n" " m[1] = &x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int x = 0;\n" " std::map m;\n" " m[0] = &x;\n" " return m[0][1];\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:5]: (error) The address of local variable 'x' is accessed at non-zero index.\n", errout.str()); check("int f(int * y) {\n" " int x = 0;\n" " std::map m;\n" " m[0] = &x;\n" " m[1] = y;\n" " return m[1][1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("void print(char** test);\n" "int main(){\n" " char* test = \"abcdef\";\n" " print(&test);\n" " return 0;\n" "}\n" "void print(char** test){\n" " for(int i=0;i [test.cpp:4] -> [test.cpp:9]: (warning) The address of local variable 'test' might be accessed at non-zero index.\n", "", errout.str()); check("void Bar(uint8_t data);\n" "void Foo(const uint8_t * const data, const uint8_t length) {\n" " for(uint8_t index = 0U; index < length ; ++index)\n" " Bar(data[index]);\n" "}\n" "void test() {\n" " const uint8_t data = 0U;\n" " Foo(&data,1U);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int foo(int n, int* p) {\n" " int res = 0;\n" " for(int i = 0; i < n; i++ )\n" " res += p[i];\n" " return res;\n" "}\n" "int bar() {\n" " int single_value = 0;\n" " return foo(1, &single_value);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(const char* app, size_t applen) {\n" // #10137 " char* tmp_de = NULL;\n" " char** str = &tmp_de;\n" " char* tmp = (char*)realloc(*str, applen + 1);\n" " if (tmp) {\n" " *str = tmp;\n" " memcpy(*str, app, applen);\n" " (*str)[applen] = '\\0';\n" " }\n" " free(*str);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("template \n" "using vector = Eigen::Matrix;\n" "template \n" "void scharr(image2d>& out) {\n" " vector* out_row = &out(r, 0);\n" " out_row[c] = vector(1,2);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(const uint8_t* d, const uint8_t L) {\n" // #10092 " for (uint8_t i = 0U; i < L; ++i)\n" " g(d[i]);\n" "}\n" "void h() {\n" " const uint8_t u = 4;\n" " f(&u, N);\n" "}"); ASSERT_EQUALS("", errout.str()); check("uint32_t f(uint32_t u) {\n" // #10154 " return ((uint8_t*)&u)[3];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("uint32_t f(uint32_t u) {\n" " return ((uint8_t*)&u)[4];\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) The address of local variable 'u' is accessed at non-zero index.\n", errout.str()); check("uint32_t f(uint32_t u) {\n" " return reinterpret_cast(&u)[3];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("uint32_t f(uint32_t u) {\n" " return reinterpret_cast(&u)[4];\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) The address of local variable 'u' is accessed at non-zero index.\n", errout.str()); check("uint32_t f(uint32_t u) {\n" " uint8_t* p = (uint8_t*)&u;\n" " return p[3];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("uint32_t f(uint32_t u) {\n" " uint8_t* p = (uint8_t*)&u;\n" " return p[4];\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) The address of local variable 'u' is accessed at non-zero index.\n", errout.str()); check("uint32_t f(uint32_t* pu) {\n" " uint8_t* p = (uint8_t*)pu;\n" " return p[4];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct S { uint8_t padding[500]; };\n" // #10133 "S s = { 0 };\n" "uint8_t f() {\n" " uint8_t* p = (uint8_t*)&s;\n" " return p[10];\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestBufferOverrun) cppcheck-2.7/test/testbughuntingchecks.cpp000066400000000000000000000345111417746362400211020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "errortypes.h" #include "exprengine.h" #include "library.h" #include "platform.h" #include "settings.h" #include "tokenize.h" #include "testsuite.h" #include class TestBughuntingChecks : public TestFixture { public: TestBughuntingChecks() : TestFixture("TestBughuntingChecks") { settings.platform(cppcheck::Platform::Unix64); } private: Settings settings; void run() OVERRIDE { #ifdef USE_Z3 settings.certainty.setEnabled(Certainty::inconclusive, true); LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(checkAssignment); TEST_CASE(arrayIndexOutOfBounds1); TEST_CASE(arrayIndexOutOfBounds2); TEST_CASE(arrayIndexOutOfBounds3); TEST_CASE(arrayIndexOutOfBounds4); TEST_CASE(arrayIndexOutOfBounds5); TEST_CASE(arrayIndexOutOfBounds6); TEST_CASE(arrayIndexOutOfBoundsDim1); TEST_CASE(bufferOverflowMemCmp1); TEST_CASE(bufferOverflowMemCmp2); TEST_CASE(bufferOverflowStrcpy1); TEST_CASE(bufferOverflowStrcpy2); TEST_CASE(divisionByZeroNoReturn); TEST_CASE(uninit); TEST_CASE(uninit_array); TEST_CASE(uninit_function_par); TEST_CASE(uninit_malloc); TEST_CASE(uninit_struct); TEST_CASE(uninit_bailout); TEST_CASE(uninit_fp_smartptr); TEST_CASE(uninit_fp_struct); TEST_CASE(uninit_fp_struct_member_init_2); TEST_CASE(uninit_fp_template_var); TEST_CASE(ctu); #endif } void check(const char code[]) { settings.bugHunting = settings.library.bugHunting = true; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); errout.str(""); ExprEngine::runChecks(this, &tokenizer, &settings); } void checkAssignment() { check("void foo(int any) { __cppcheck_low__(0) int x; x = any; }"); ASSERT_EQUALS("[test.cpp:1]: (error) There is assignment, cannot determine that value is greater or equal with 0\n", errout.str()); check("struct S { __cppcheck_low__(0) int x; };\n" "void foo(S *s, int any) { s->x = any; }"); ASSERT_EQUALS("[test.cpp:2]: (error) There is assignment, cannot determine that value is greater or equal with 0\n", errout.str()); } void arrayIndexOutOfBounds1() { check("void foo(int x) {\n" " int p[8];" " p[x] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Array index out of bounds, cannot determine that x is less than 8\n" "[test.cpp:2]: (error) Array index out of bounds, cannot determine that x is not negative\n", errout.str()); } void arrayIndexOutOfBounds2() { // loop check("void foo(int n) {\n" " int p[8];\n" " for (int i = 0; i < n; i++)\n" " p[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array index out of bounds, cannot determine that i is less than 8\n" "[test.cpp:4]: (error) Array index out of bounds, cannot determine that i is not negative\n", errout.str()); // .. with unknown expression check("void foo(int n) {\n" " int p[8];\n" " crx_data_header_t *d =\n" " &libraw_internal_data.unpacker_data.crx_header[framei];\n" " for (int i = 0; i < n; i++)\n" " p[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array index out of bounds, cannot determine that i is less than 8\n" "[test.cpp:6]: (error) Array index out of bounds, cannot determine that i is not negative\n", errout.str()); } void arrayIndexOutOfBounds3() { // struct check("struct S { int x; };\n" "void foo(short i) {\n" " S s[8];\n" " if (s[i].x == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array index out of bounds, cannot determine that i is less than 8\n" "[test.cpp:4]: (error) Array index out of bounds, cannot determine that i is not negative\n" "[test.cpp:4]: (error) Cannot determine that 's[i]' is initialized\n", errout.str()); } void arrayIndexOutOfBounds4() { // ensure there are warnings for bailout value check("void foo(short i) {\n" " int buf[8];\n" "\n" " data *d = x;\n" " switch (d->layout) { case 0: break; }\n" "\n" " if (buf[i] > 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array index out of bounds, cannot determine that i is less than 8\n" "[test.cpp:7]: (error) Array index out of bounds, cannot determine that i is not negative\n" "[test.cpp:7]: (error) Cannot determine that 'buf[i]' is initialized\n", errout.str()); } void arrayIndexOutOfBounds5() { check("struct {\n" " struct { int z; } y;\n" "} x;\n" "\n" "void foo(int i) {\n" " for (int c = 0; c <= i; c++)\n" " x.y.z = 13;\n" " int buf[10];\n" " if (buf[i] > 0) { }\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array index out of bounds, cannot determine that i is less than 10\n" "[test.cpp:9]: (error) Array index out of bounds, cannot determine that i is not negative\n" "[test.cpp:9]: (error) Cannot determine that 'buf[i]' is initialized\n", errout.str()); } void arrayIndexOutOfBounds6() { check("int buf[5];\n" "uint16_t foo(size_t offset) {\n" " uint8_t c = (offset & 0xc0) >> 6;\n" " return 2 * buf[c];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array index out of bounds, cannot determine that c is less than 5\n", errout.str()); } void arrayIndexOutOfBoundsDim1() { // itc test case check("void overrun_st_008 () {\n" " int buf[5][6];\n" " buf[5][5] = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array index out of bounds, cannot determine that 5 is less than 5\n", errout.str()); } void bufferOverflowMemCmp1() { // CVE-2020-24265 check("void foo(const char *pktdata, int datalen) {\n" " if (memcmp(pktdata, \"MGC\", 3)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Buffer read/write, when calling 'memcmp' it cannot be determined that 1st argument is not overflowed\n", errout.str()); } void bufferOverflowMemCmp2() { check("void foo(const char *pktdata, unsigned int datalen) {\n" " if (memcmp(pktdata, \"MGC\", datalen)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Buffer read/write, when calling 'memcmp' it cannot be determined that 1st argument is not overflowed\n", errout.str()); } void bufferOverflowStrcpy1() { check("void foo(char *p) {\n" " strcpy(p, \"hello\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Buffer read/write, when calling 'strcpy' it cannot be determined that 1st argument is not overflowed\n", errout.str()); } void bufferOverflowStrcpy2() { check("void foo(char *p, const char *q) {\n" " strcpy(p, q);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Buffer read/write, when calling 'strcpy' it cannot be determined that 1st argument is not overflowed\n", errout.str()); } void divisionByZeroNoReturn() { // Don't know if function is noreturn or not.. check("int f(int leftarg, int rightarg) {\n" " if (rightarg == 0)\n" " raise (SIGFPE);\n" // <- maybe noreturn " return leftarg / rightarg;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) There is division, cannot determine that there can't be a division by zero.\n", errout.str()); } void uninit() { check("void foo() { int x; x = x + 1; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str()); check("void foo() { int x; int y = x + 1; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str()); check("void foo() { int x; x++; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str()); } void uninit_array() { check("void foo(int x) {\n" " int a[10];\n" " if (x > 0) a[0] = 32;\n" " return a[0];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Cannot determine that 'a[0]' is initialized\n", errout.str()); } void uninit_function_par() { // non constant parameters may point at uninitialized data // constant parameters should point at initialized data check("char foo(char id[]) { return id[0]; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'id[0]' is initialized (you can use 'const' to say data must be initialized)\n", errout.str()); check("char foo(const char id[]) { return id[0]; }"); ASSERT_EQUALS("", errout.str()); check("char foo(const char id[]);\n" "void bar() { char data[10]; foo(data); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Cannot determine that 'data[0]' is initialized\n", errout.str()); check("char foo(char id[]);\n" "void bar() { char data[10]; foo(data); }"); ASSERT_EQUALS("[test.cpp:2]: (error, inconclusive) Cannot determine that 'data[0]' is initialized. It is inconclusive if there would be a problem in the function call.\n", errout.str()); check("void foo(int *p) { if (p) *p=0; }"); ASSERT_EQUALS("", errout.str()); check("class C {\n" "public:\n" " C();\n" " int x;\n" "};\n" "\n" "void foo(const C &c) { int x = c.x; }"); ASSERT_EQUALS("", errout.str()); } void uninit_malloc() { check("void foo() { char *p = malloc(10); return *p; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that '*p' is initialized\n", errout.str()); } void uninit_struct() { // Assume that constructors initialize all members // TODO whole program analysis check("struct Data { Data(); int x; }\n" "void foo() {\n" " Data data;\n" " x = data.x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninit_bailout() { check("void foo() {\n" " __CPPCHECK_BAILOUT__;\n" " int values[5];\n" " values[i] = 123;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " __CPPCHECK_BAILOUT__;\n" " std::ostringstream comm;\n" " comm << 123;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ctu() { check("void init(int &x) {\n" " x = 1;\n" "}\n" "\n" "void foo() {\n" " int x;\n" " init(x);\n" " x++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void init(int a, int &x) {\n" " if (a < 10)\n" " x = 1;\n" "}\n" "\n" "void foo(int a) {\n" " int x;\n" " init(a, x);\n" " x++;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Cannot determine that 'x' is initialized\n", errout.str()); check("void init(int a, int &x) {\n" " if (a < 10)\n" " x = 1;\n" " else\n" " x = 3;\n" "}\n" "\n" "void foo(int a) {\n" " int x;\n" " init(a, x);\n" " x++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninit_fp_smartptr() { check("void foo() {\n" " std::unique_ptr buffer;\n" " try { } catch (std::exception& e) { }\n" " doneCallback(std::move(buffer));\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninit_fp_struct() { check("struct Pos {\n" " int x {0};\n" " int y {0};\n" "};\n" "\n" "void dostuff() {\n" " auto obj = C {};\n" " Pos xy;\n" " foo(xy);\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninit_fp_struct_member_init_2() { check("struct A {\n" " int x {0}; int y {0};\n" "};\n" "void foo(const A& a) {\n" " bar(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninit_fp_template_var() { check("void foo() {\n" " X*x = DYNAMIC_CAST(X, p);\n" " C c;\n" " f(c);\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestBughuntingChecks) cppcheck-2.7/test/testcharvar.cpp000066400000000000000000000143251417746362400171760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkother.h" #include "config.h" #include "errortypes.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestCharVar : public TestFixture { public: TestCharVar() : TestFixture("TestCharVar") {} private: Settings settings; void run() OVERRIDE { settings.platform(Settings::Unspecified); settings.severity.enable(Severity::warning); settings.severity.enable(Severity::portability); TEST_CASE(array_index_1); TEST_CASE(array_index_2); TEST_CASE(bitop); } #define check(code) check_(code, __FILE__, __LINE__) void check_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check char variable usage.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkCharVariable(); } void array_index_1() { check("int buf[256];\n" "void foo()\n" "{\n" " unsigned char ch = 0x80;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " char ch = 0x80;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (portability) 'char' type used as array index.\n", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " char ch = 0;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " signed char ch = 0;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " char ch = 0x80;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (portability) 'char' type used as array index.\n", errout.str()); check("int buf[256];\n" "void foo(signed char ch)\n" "{\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo(char ch)\n" "{\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* buf)\n" "{\n" " char ch = 0x80;" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) 'char' type used as array index.\n", errout.str()); check("void foo(char* buf)\n" "{\n" " char ch = 0;" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* buf)\n" "{\n" " buf['A'] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* buf, char ch)\n" "{\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int flags[256];\n" "void foo(const char* str)\n" "{\n" " flags[*str] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int flags[256];\n" "void foo(const char* str)\n" "{\n" " flags[(unsigned char)*str] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const char str[])\n" "{\n" " map[str] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_2() { // #3282 - False positive check("void foo(char i);\n" "void bar(int i) {\n" " const char *s = \"abcde\";\n" " foo(s[i]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void bitop() { check("void foo(int *result) {\n" " signed char ch = -1;\n" " *result = a | ch;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout.str()); check("void foo(int *result) {\n" " unsigned char ch = -1;\n" " *result = a | ch;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *result) {\n" " signed char ch = -1;\n" " *result = a | ch;\n" "}"); ASSERT_EQUALS("", errout.str()); // 0x03 & .. check("void foo(int *result) {\n" " signed char ch = -1;\n" " *result = 0x03 | ch;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout.str()); check("void foo(int *result) {\n" " signed char ch = -1;\n" " *result = 0x03 & ch;\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestCharVar) cppcheck-2.7/test/testclangimport.cpp000066400000000000000000002514341417746362400200730ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2022 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "clangimport.h" #include "config.h" #include "platform.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "testsuite.h" #include #include #include #include #include #include class TestClangImport : public TestFixture { public: TestClangImport() : TestFixture("TestClangImport") {} private: void run() OVERRIDE { TEST_CASE(breakStmt); TEST_CASE(callExpr); TEST_CASE(caseStmt1); TEST_CASE(characterLiteral); TEST_CASE(class1); TEST_CASE(classTemplateDecl1); TEST_CASE(classTemplateDecl2); TEST_CASE(conditionalExpr); TEST_CASE(compoundAssignOperator); TEST_CASE(continueStmt); TEST_CASE(cstyleCastExpr); TEST_CASE(cxxBoolLiteralExpr); TEST_CASE(cxxConstructorDecl1); TEST_CASE(cxxConstructorDecl2); TEST_CASE(cxxConstructExpr1); TEST_CASE(cxxConstructExpr2); TEST_CASE(cxxConstructExpr3); TEST_CASE(cxxDeleteExpr); TEST_CASE(cxxDestructorDecl); TEST_CASE(cxxForRangeStmt1); TEST_CASE(cxxForRangeStmt2); TEST_CASE(cxxFunctionalCastExpr); TEST_CASE(cxxMemberCall); TEST_CASE(cxxMethodDecl1); TEST_CASE(cxxMethodDecl2); TEST_CASE(cxxMethodDecl3); TEST_CASE(cxxMethodDecl4); TEST_CASE(cxxNewExpr1); TEST_CASE(cxxNewExpr2); TEST_CASE(cxxNullPtrLiteralExpr); TEST_CASE(cxxOperatorCallExpr); TEST_CASE(cxxRecordDecl1); TEST_CASE(cxxRecordDecl2); TEST_CASE(cxxRecordDeclDerived); TEST_CASE(cxxStaticCastExpr1); TEST_CASE(cxxStaticCastExpr2); TEST_CASE(cxxStaticCastExpr3); TEST_CASE(cxxStdInitializerListExpr); TEST_CASE(cxxThrowExpr); TEST_CASE(defaultStmt); TEST_CASE(doStmt); TEST_CASE(enumDecl1); TEST_CASE(enumDecl2); TEST_CASE(enumDecl3); TEST_CASE(enumDecl4); TEST_CASE(forStmt); TEST_CASE(funcdecl1); TEST_CASE(funcdecl2); TEST_CASE(funcdecl3); TEST_CASE(funcdecl4); TEST_CASE(funcdecl5); TEST_CASE(funcdecl6); TEST_CASE(functionTemplateDecl1); TEST_CASE(functionTemplateDecl2); TEST_CASE(initListExpr); TEST_CASE(ifelse); TEST_CASE(ifStmt); TEST_CASE(labelStmt); TEST_CASE(memberExpr); TEST_CASE(namespaceDecl1); TEST_CASE(namespaceDecl2); TEST_CASE(recordDecl1); TEST_CASE(recordDecl2); TEST_CASE(switchStmt); TEST_CASE(typedefDecl1); TEST_CASE(typedefDecl2); TEST_CASE(typedefDecl3); TEST_CASE(unaryExprOrTypeTraitExpr1); TEST_CASE(unaryExprOrTypeTraitExpr2); TEST_CASE(unaryOperator); TEST_CASE(vardecl1); TEST_CASE(vardecl2); TEST_CASE(vardecl3); TEST_CASE(vardecl4); TEST_CASE(vardecl5); TEST_CASE(vardecl6); TEST_CASE(vardecl7); TEST_CASE(whileStmt1); TEST_CASE(whileStmt2); TEST_CASE(tokenIndex); TEST_CASE(symbolDatabaseEnum1); TEST_CASE(symbolDatabaseFunction1); TEST_CASE(symbolDatabaseFunction2); TEST_CASE(symbolDatabaseFunction3); TEST_CASE(symbolDatabaseFunctionConst); TEST_CASE(symbolDatabaseVariableRef); TEST_CASE(symbolDatabaseVariableRRef); TEST_CASE(symbolDatabaseVariablePointerRef); TEST_CASE(symbolDatabaseNodeType1); TEST_CASE(symbolDatabaseForVariable); TEST_CASE(valueFlow1); TEST_CASE(valueFlow2); TEST_CASE(valueType1); TEST_CASE(valueType2); } std::string parse(const char clang[]) { Settings settings; settings.clang = true; Tokenizer tokenizer(&settings, this); std::istringstream istr(clang); clangimport::parseClangAstDump(&tokenizer, istr); if (!tokenizer.tokens()) { return std::string(); } return tokenizer.tokens()->stringifyList(true, false, false, false, false); } void breakStmt() { const char clang[] = "`-FunctionDecl 0x2c31b18 <1.c:1:1, col:34> col:6 foo 'void ()'\n" " `-CompoundStmt 0x2c31c40 \n" " `-WhileStmt 0x2c31c20 \n" " |-<<>>\n" " |-IntegerLiteral 0x2c31bf8 'int' 0\n" " `-BreakStmt 0x3687c18 "; ASSERT_EQUALS("void foo ( ) { while ( 0 ) { break ; } }", parse(clang)); } void callExpr() { const char clang[] = "`-FunctionDecl 0x2444b60 <1.c:1:1, line:8:1> line:1:6 foo 'void (int)'\n" " |-ParmVarDecl 0x2444aa0 col:14 used x 'int'\n" " `-CompoundStmt 0x2444e00 \n" " `-CallExpr 0x7f5a6c04b158 'bool'\n" " |-ImplicitCastExpr 0x7f5a6c04b140 'bool (*)(const Token *, const char *, int)' \n" " | `-DeclRefExpr 0x7f5a6c04b0a8 'bool (const Token *, const char *, int)' lvalue CXXMethod 0x43e5600 'Match' 'bool (const Token *, const char *, int)'\n" " |-ImplicitCastExpr 0x7f5a6c04b1c8 'const Token *' \n" " | `-ImplicitCastExpr 0x7f5a6c04b1b0 'Token *' \n" " | `-DeclRefExpr 0x7f5a6c04b0e0 'Token *' lvalue Var 0x7f5a6c045968 'tokAfterCondition' 'Token *'\n" " |-ImplicitCastExpr 0x7f5a6c04b1e0 'const char *' \n" " | `-StringLiteral 0x7f5a6c04b108 'const char [11]' lvalue \"%name% : {\"\n" " `-CXXDefaultArgExpr 0x7f5a6c04b1f8 <> 'int'\n"; ASSERT_EQUALS("void foo ( int x@1 ) { Match ( tokAfterCondition , \"%name% : {\" ) ; }", parse(clang)); } void caseStmt1() { const char clang[] = "`-FunctionDecl 0x2444b60 <1.c:1:1, line:8:1> line:1:6 foo 'void (int)'\n" " |-ParmVarDecl 0x2444aa0 col:14 used x 'int'\n" " `-CompoundStmt 0x2444e00 \n" " `-SwitchStmt 0x2444c88 \n" " |-<<>>\n" " |-<<>>\n" " |-ImplicitCastExpr 0x2444c70 'int' \n" " | `-DeclRefExpr 0x2444c48 'int' lvalue ParmVar 0x2444aa0 'x' 'int'\n" " `-CompoundStmt 0x2444de0 \n" " |-CaseStmt 0x2444cd8 \n" " | |-IntegerLiteral 0x2444cb8 'int' 16\n" " | |-<<>>\n" " | `-CaseStmt 0x2444d30 \n" " | |-IntegerLiteral 0x2444d10 'int' 32\n" " | |-<<>>\n" " | `-BinaryOperator 0x2444db0 'int' '='\n" " | |-DeclRefExpr 0x2444d68 'int' lvalue ParmVar 0x2444aa0 'x' 'int'\n" " | `-IntegerLiteral 0x2444d90 'int' 123\n" " `-BreakStmt 0x2444dd8 "; ASSERT_EQUALS("void foo ( int x@1 ) { switch ( x@1 ) { case 16 : case 32 : x@1 = 123 ; break ; } }", parse(clang)); } void characterLiteral() { const char clang[] = "`-VarDecl 0x3df8608 col:6 c 'char' cinit\n" " `-CharacterLiteral 0x3df86a8 'char' 120"; ASSERT_EQUALS("char c@1 = 'x' ;", parse(clang)); } void class1() { const char clang[] = "`-CXXRecordDecl 0x274c638 col:7 class C definition\n" " |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" " | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" " | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" " | |-MoveConstructor exists simple trivial needs_implicit\n" " | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" " | |-MoveAssignment exists simple trivial needs_implicit\n" " | `-Destructor simple irrelevant trivial needs_implicit\n" " |-CXXRecordDecl 0x274c758 col:7 implicit class C\n" " `-CXXMethodDecl 0x274c870 col:16 foo 'void ()'\n" " `-CompoundStmt 0x274c930 "; ASSERT_EQUALS("class C { void foo ( ) { } } ;", parse(clang)); } void classTemplateDecl1() { const char clang[] = "`-ClassTemplateDecl 0x29d1748 col:25 C\n" " |-TemplateTypeParmDecl 0x29d15f8 col:16 referenced class depth 0 index 0 T\n" " `-CXXRecordDecl 0x29d16b0 col:25 class C definition\n" " |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" " | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" " | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" " | |-MoveConstructor exists simple trivial needs_implicit\n" " | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" " | |-MoveAssignment exists simple trivial needs_implicit\n" " | `-Destructor simple irrelevant trivial needs_implicit\n" " |-CXXRecordDecl 0x29d19b0 col:25 implicit class C\n" " |-AccessSpecDecl 0x29d1a48 col:29 public\n" " `-CXXMethodDecl 0x29d1b20 col:39 foo 'T ()'\n" " `-CompoundStmt 0x29d1c18 \n" " `-ReturnStmt 0x29d1c00 \n" " `-IntegerLiteral 0x29d1be0 'int' 0"; ASSERT_EQUALS("", parse(clang)); } void classTemplateDecl2() { const char clang[] = "|-ClassTemplateDecl 0x244e748 col:25 C\n" "| |-TemplateTypeParmDecl 0x244e5f8 col:16 referenced class depth 0 index 0 T\n" "| |-CXXRecordDecl 0x244e6b0 col:25 class C definition\n" "| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" "| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" "| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" "| | | |-MoveConstructor exists simple trivial needs_implicit\n" "| | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" "| | | |-MoveAssignment exists simple trivial needs_implicit\n" "| | | `-Destructor simple irrelevant trivial needs_implicit\n" "| | |-CXXRecordDecl 0x244e9b0 col:25 implicit class C\n" "| | |-AccessSpecDecl 0x244ea48 col:29 public\n" "| | `-CXXMethodDecl 0x244eb20 col:39 foo 'T ()'\n" "| | `-CompoundStmt 0x244ec18 \n" "| | `-ReturnStmt 0x244ec00 \n" "| | `-IntegerLiteral 0x244ebe0 'int' 0\n" "| `-ClassTemplateSpecializationDecl 0x244ed78 col:25 class C definition\n" "| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" "| | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr\n" "| | |-CopyConstructor simple trivial has_const_param implicit_has_const_param\n" "| | |-MoveConstructor exists simple trivial\n" "| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" "| | |-MoveAssignment exists simple trivial needs_implicit\n" "| | `-Destructor simple irrelevant trivial needs_implicit\n" "| |-TemplateArgument type 'int'\n" "| |-CXXRecordDecl 0x244eff0 prev 0x244ed78 col:25 implicit class C\n" "| |-AccessSpecDecl 0x244f088 col:29 public\n" "| |-CXXMethodDecl 0x244f160 col:39 used foo 'int ()'\n" "| | `-CompoundStmt 0x247cb40 \n" "| | `-ReturnStmt 0x247cb28 \n" "| | `-IntegerLiteral 0x244ebe0 'int' 0\n" "| |-CXXConstructorDecl 0x247c540 col:25 implicit used constexpr C 'void () noexcept' inline default trivial\n" "| | `-CompoundStmt 0x247ca00 \n" "| |-CXXConstructorDecl 0x247c658 col:25 implicit constexpr C 'void (const C &)' inline default trivial noexcept-unevaluated 0x247c658\n" "| | `-ParmVarDecl 0x247c790 col:25 'const C &'\n" "| `-CXXConstructorDecl 0x247c828 col:25 implicit constexpr C 'void (C &&)' inline default trivial noexcept-unevaluated 0x247c828\n" "| `-ParmVarDecl 0x247c960 col:25 'C &&'\n"; ASSERT_EQUALS("class C { int foo ( ) { return 0 ; } C ( ) { } C ( const C & ) = default ; C ( C && ) = default ; } ;", parse(clang)); } void conditionalExpr() { const char clang[] = "`-VarDecl 0x257cc88 col:5 x 'int' cinit\n" " `-ConditionalOperator 0x257cda8 'int'\n" " |-ImplicitCastExpr 0x257cd60 'int' \n" " | `-DeclRefExpr 0x257cce8 'int' lvalue Var 0x257cae0 'a' 'int'\n" " |-ImplicitCastExpr 0x257cd78 'int' \n" " | `-DeclRefExpr 0x257cd10 'int' lvalue Var 0x257cb98 'b' 'int'\n" " `-ImplicitCastExpr 0x257cd90 'int' \n" " `-DeclRefExpr 0x257cd38 'int' lvalue Var 0x257cc10 'c' 'int'"; ASSERT_EQUALS("int x@1 = a ? b : c ;", parse(clang)); } void compoundAssignOperator() { const char clang[] = "`-FunctionDecl 0x3570690 <1.cpp:2:1, col:25> col:6 f 'void ()'\n" " `-CompoundStmt 0x3570880 \n" " `-CompoundAssignOperator 0x3570848 'int' lvalue '+=' ComputeLHSTy='int' ComputeResultTy='int'\n" " |-DeclRefExpr 0x3570800 'int' lvalue Var 0x3570788 'x' 'int'\n" " `-IntegerLiteral 0x3570828 'int' 1"; ASSERT_EQUALS("void f ( ) { x += 1 ; }", parse(clang)); } void continueStmt() { const char clang[] = "`-FunctionDecl 0x2c31b18 <1.c:1:1, col:34> col:6 foo 'void ()'\n" " `-CompoundStmt 0x2c31c40 \n" " `-WhileStmt 0x2c31c20 \n" " |-<<>>\n" " |-IntegerLiteral 0x2c31bf8 'int' 0\n" " `-ContinueStmt 0x2c31c18 "; ASSERT_EQUALS("void foo ( ) { while ( 0 ) { continue ; } }", parse(clang)); } void cstyleCastExpr() { const char clang[] = "`-VarDecl 0x2336aa0 <1.c:1:1, col:14> col:5 x 'int' cinit\n" " `-CStyleCastExpr 0x2336b70 'int' \n" " `-CharacterLiteral 0x2336b40 'int' 97"; ASSERT_EQUALS("int x@1 = ( int ) 'a' ;", parse(clang)); } void cxxBoolLiteralExpr() { const char clang[] = "`-VarDecl 0x3940608 col:6 x 'bool' cinit\n" " `-CXXBoolLiteralExpr 0x39406a8 'bool' true"; ASSERT_EQUALS("bool x@1 = true ;", parse(clang)); } void cxxConstructorDecl1() { const char clang[] = "|-CXXConstructorDecl 0x428e890 col:11 C 'void ()'\n" "| `-CompoundStmt 0x428ea58 \n" "| `-BinaryOperator 0x428ea30 'int' lvalue '='\n" "| |-MemberExpr 0x428e9d8 'int' lvalue ->x 0x428e958\n" "| | `-CXXThisExpr 0x428e9c0 'C *' this\n" "| `-IntegerLiteral 0x428ea10 'int' 0\n" "`-FieldDecl 0x428e958 col:30 referenced x 'int'"; ASSERT_EQUALS("C ( ) { this . x@1 = 0 ; } int x@1", parse(clang)); } void cxxConstructorDecl2() { const char clang[] = "`-CXXConstructorDecl 0x1c208c0 col:11 implicit constexpr basic_string 'void (std::basic_string &&)' inline default trivial noexcept-unevaluated 0x1c208c0\n" " `-ParmVarDecl 0x1c209f0 col:11 'std::basic_string &&'"; ASSERT_EQUALS("basic_string ( std::basic_string && ) = default ;", parse(clang)); } void cxxConstructExpr1() { const char clang[] = "`-FunctionDecl 0x2dd7940 col:5 f 'Foo (Foo)'\n" " |-ParmVarDecl 0x2dd7880 col:11 used foo 'Foo'\n" " `-CompoundStmt 0x2dd80c0 \n" " `-ReturnStmt 0x2dd80a8 \n" " `-CXXConstructExpr 0x2dd8070 'Foo' 'void (Foo &&) noexcept'\n" " `-ImplicitCastExpr 0x2dd7f28 'Foo' xvalue \n" " `-DeclRefExpr 0x2dd7a28 'Foo' lvalue ParmVar 0x2dd7880 'foo' 'Foo'"; ASSERT_EQUALS("Foo f ( Foo foo@1 ) { return foo@1 ; }", parse(clang)); } void cxxConstructExpr2() { const char clang[] = "`-FunctionDecl 0x3e44180 <1.cpp:2:1, col:30> col:13 f 'std::string ()'\n" " `-CompoundStmt 0x3e4cb80 \n" " `-ReturnStmt 0x3e4cb68 \n" " `-CXXConstructExpr 0x3e4cb38 'std::string':'std::__cxx11::basic_string' '....' list"; ASSERT_EQUALS("std :: string f ( ) { return std :: string ( ) ; }", parse(clang)); } void cxxConstructExpr3() { const char clang[] = "`-FunctionDecl 0x2c585b8 <1.cpp:4:1, col:39> col:6 f 'void ()'\n" " `-CompoundStmt 0x2c589d0 \n" " |-DeclStmt 0x2c586d0 \n" " | `-VarDecl 0x2c58670 col:18 used p 'char *'\n" " `-DeclStmt 0x2c589b8 \n" " `-VarDecl 0x2c58798 col:33 s 'std::string':'std::__cxx11::basic_string' callinit\n" " `-ExprWithCleanups 0x2c589a0 'std::string':'std::__cxx11::basic_string'\n" " `-CXXConstructExpr 0x2c58960 'std::string':'std::__cxx11::basic_string' 'void (const char *, const std::allocator &)'\n" " |-ImplicitCastExpr 0x2c58870 'const char *' \n" " | `-ImplicitCastExpr 0x2c58858 'char *' \n" " | `-DeclRefExpr 0x2c58750 'char *' lvalue Var 0x2c58670 'p' 'char *'\n" " `-CXXDefaultArgExpr 0x2c58940 <> 'const std::allocator':'const std::allocator' lvalue\n"; ASSERT_EQUALS("void f ( ) { char * p@1 ; std :: string s@2 ( p@1 ) ; }", parse(clang)); } void cxxDeleteExpr() { const char clang[] = "|-FunctionDecl 0x2e0e740 <1.cpp:1:1, col:28> col:6 f 'void (int *)'\n" "| |-ParmVarDecl 0x2e0e680 col:13 used p 'int *'\n" "| `-CompoundStmt 0x2e0ee70 \n" "| `-CXXDeleteExpr 0x2e0ee48 'void' Function 0x2e0ebb8 'operator delete' 'void (void *) noexcept'\n" "| `-ImplicitCastExpr 0x2e0e850 'int *' \n" "| `-DeclRefExpr 0x2e0e828 'int *' lvalue ParmVar 0x2e0e680 'p' 'int *'"; ASSERT_EQUALS("void f ( int * p@1 ) { delete p@1 ; }", parse(clang)); } void cxxDestructorDecl() { const char clang[] = "`-CXXRecordDecl 0x8ecd60 <1.cpp:1:1, line:4:1> line:1:8 struct S definition\n" " `-CXXDestructorDecl 0x8ed088 col:3 ~S 'void () noexcept'\n" " `-CompoundStmt 0x8ed1a8 "; ASSERT_EQUALS("struct S { ~S ( ) { } } ;", parse(clang)); } void cxxForRangeStmt1() { const char clang[] = "`-FunctionDecl 0x4280820 line:4:6 foo 'void ()'\n" " `-CompoundStmt 0x42810f0 \n" " `-CXXForRangeStmt 0x4281090 \n" " |-DeclStmt 0x4280c30 \n" " | `-VarDecl 0x42809c8 col:17 implicit referenced __range1 'char const (&)[6]' cinit\n" " | `-DeclRefExpr 0x42808c0 'const char [6]' lvalue Var 0x4280678 'hello' 'const char [6]'\n" " |-DeclStmt 0x4280ef8 \n" " | `-VarDecl 0x4280ca8 col:15 implicit used __begin1 'const char *':'const char *' cinit\n" " | `-ImplicitCastExpr 0x4280e10 'const char *' \n" " | `-DeclRefExpr 0x4280c48 'char const[6]' lvalue Var 0x42809c8 '__range1' 'char const (&)[6]'\n" " |-DeclStmt 0x4280f10 \n" " | `-VarDecl 0x4280d18 col:15 implicit used __end1 'const char *':'const char *' cinit\n" " | `-BinaryOperator 0x4280e60 'const char *' '+'\n" " | |-ImplicitCastExpr 0x4280e48 'const char *' \n" " | | `-DeclRefExpr 0x4280c70 'char const[6]' lvalue Var 0x42809c8 '__range1' 'char const (&)[6]'\n" " | `-IntegerLiteral 0x4280e28 'long' 6\n" " |-BinaryOperator 0x4280fa8 'bool' '!='\n" " | |-ImplicitCastExpr 0x4280f78 'const char *':'const char *' \n" " | | `-DeclRefExpr 0x4280f28 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" " | `-ImplicitCastExpr 0x4280f90 'const char *':'const char *' \n" " | `-DeclRefExpr 0x4280f50 'const char *':'const char *' lvalue Var 0x4280d18 '__end1' 'const char *':'const char *'\n" " |-UnaryOperator 0x4280ff8 'const char *':'const char *' lvalue prefix '++'\n" " | `-DeclRefExpr 0x4280fd0 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" " |-DeclStmt 0x4280958 \n" " | `-VarDecl 0x42808f8 col:13 c1 'char' cinit\n" " | `-ImplicitCastExpr 0x4281078 'char' \n" " | `-UnaryOperator 0x4281058 'const char' lvalue prefix '*' cannot overflow\n" " | `-ImplicitCastExpr 0x4281040 'const char *':'const char *' \n" " | `-DeclRefExpr 0x4281018 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" " `-CompoundStmt 0x42810e0 "; ASSERT_EQUALS("void foo ( ) { for ( char c1@1 : hello ) { } }", parse(clang)); } void cxxForRangeStmt2() { // clang 9 const char clang[] = "`-FunctionDecl 0xc15d98 col:6 foo 'void ()'\n" " `-CompoundStmt 0xc16668 \n" " `-CXXForRangeStmt 0xc165f8 \n" " |-<<>>\n" " |-DeclStmt 0xc161c0 \n" " | `-VarDecl 0xc15f48 col:25 implicit referenced __range1 'int const (&)[4]' cinit\n" " | `-DeclRefExpr 0xc15e38 'const int [4]' lvalue Var 0xc15ac0 'values' 'const int [4]'\n" " |-DeclStmt 0xc16498 \n" " | `-VarDecl 0xc16228 col:24 implicit used __begin1 'const int *':'const int *' cinit\n" " | `-ImplicitCastExpr 0xc163b0 'const int *' \n" " | `-DeclRefExpr 0xc161d8 'int const[4]' lvalue Var 0xc15f48 '__range1' 'int const (&)[4]' non_odr_use_constant\n" " |-DeclStmt 0xc164b0 \n" " | `-VarDecl 0xc162a0 col:24 implicit used __end1 'const int *':'const int *' cinit\n" " | `-BinaryOperator 0xc16400 'const int *' '+'\n" " | |-ImplicitCastExpr 0xc163e8 'const int *' \n" " | | `-DeclRefExpr 0xc161f8 'int const[4]' lvalue Var 0xc15f48 '__range1' 'int const (&)[4]' non_odr_use_constant\n" " | `-IntegerLiteral 0xc163c8 'long' 4\n" " |-BinaryOperator 0xc16538 'bool' '!='\n" " | |-ImplicitCastExpr 0xc16508 'const int *':'const int *' \n" " | | `-DeclRefExpr 0xc164c8 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" " | `-ImplicitCastExpr 0xc16520 'const int *':'const int *' \n" " | `-DeclRefExpr 0xc164e8 'const int *':'const int *' lvalue Var 0xc162a0 '__end1' 'const int *':'const int *'\n" " |-UnaryOperator 0xc16578 'const int *':'const int *' lvalue prefix '++'\n" " | `-DeclRefExpr 0xc16558 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" " |-DeclStmt 0xc15ed8 \n" " | `-VarDecl 0xc15e70 col:23 v 'int' cinit\n" " | `-ImplicitCastExpr 0xc165e0 'int' \n" " | `-UnaryOperator 0xc165c8 'const int' lvalue prefix '*' cannot overflow\n" " | `-ImplicitCastExpr 0xc165b0 'const int *':'const int *' \n" " | `-DeclRefExpr 0xc16590 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" " `-CompoundStmt 0xc16658 "; ASSERT_EQUALS("void foo ( ) { for ( int v@1 : values ) { } }", parse(clang)); } void cxxFunctionalCastExpr() { const char clang[] = "`-FunctionDecl 0x156fe98 line:1:5 main 'int (int, char **)'\n" " |-ParmVarDecl 0x156fd00 col:14 argc 'int'\n" " |-ParmVarDecl 0x156fdb8 col:27 argv 'char **'\n" " `-CompoundStmt 0x1596410 \n" " |-DeclStmt 0x15946a8 \n" " | `-VarDecl 0x1570118 col:11 used setCode 'MyVar':'MyVar' cinit\n" " | `-ExprWithCleanups 0x1594690 'MyVar':'MyVar'\n" " | `-CXXConstructExpr 0x1594660 'MyVar':'MyVar' 'void (MyVar &&) noexcept' elidable\n" " | `-MaterializeTemporaryExpr 0x1592b68 'MyVar':'MyVar' xvalue\n" " | `-CXXFunctionalCastExpr 0x1592b40 'MyVar':'MyVar' functional cast to MyVar \n" " | `-CXXConstructExpr 0x15929f0 'MyVar':'MyVar' 'void (int)'\n" " | `-IntegerLiteral 0x1570248 'int' 5\n"; ASSERT_EQUALS("int main ( int argc@1 , char * * argv@2 ) { MyVar setCode@3 = MyVar ( 5 ) ; }", parse(clang)); } void cxxMemberCall() { const char clang[] = "`-FunctionDecl 0x320dc80 col:6 bar 'void ()'\n" " `-CompoundStmt 0x323bb08 \n" " |-DeclStmt 0x323ba40 \n" " | `-VarDecl 0x320df28 col:21 used c 'C':'C' callinit\n" " | `-CXXConstructExpr 0x323ba10 'C':'C' 'void () noexcept'\n" " `-CXXMemberCallExpr 0x323bab8 'int':'int'\n" " `-MemberExpr 0x323ba80 '' .foo 0x320e160\n" " `-DeclRefExpr 0x323ba58 'C':'C' lvalue Var 0x320df28 'c' 'C':'C'"; ASSERT_EQUALS("void bar ( ) { C c@1 ( C ( ) ) ; c@1 . foo ( ) ; }", parse(clang)); } void cxxMethodDecl1() { const char clang[] = "|-CXXMethodDecl 0x55c786f5ad60 col:10 analyzeFile '_Bool (const std::string &, const std::string &, const std::string &, unsigned long long, std::list *)'\n" "| |-ParmVarDecl 0x55c786f5a4c8 col:41 buildDir 'const std::string &'\n" "| |-ParmVarDecl 0x55c786f5a580 col:70 sourcefile 'const std::string &'\n" "| |-ParmVarDecl 0x55c786f5a638 col:101 cfg 'const std::string &'\n" "| |-ParmVarDecl 0x55c786f5a6a8 col:125 checksum 'unsigned long long'\n" "| |-ParmVarDecl 0x55c786f5ac00 col:173 errors 'std::list *'\n" " `-CompoundStmt 0x0 <>"; ASSERT_EQUALS("_Bool analyzeFile ( const std :: string & buildDir@1 , const std :: string & sourcefile@2 , const std :: string & cfg@3 , unsigned long long checksum@4 , std::list * errors@5 ) { }", parse(clang)); } void cxxMethodDecl2() { // "unexpanded" template method const char clang[] = "`-CXXMethodDecl 0x220ecb0 parent 0x21e4c28 prev 0x21e5338 line:14:1 find 'const typename char_traits<_CharT>::char_type *(const char_traits::char_type *, int, const char_traits::char_type &)'\n" " `-CompoundStmt 0x220ede0 \n" " `-ReturnStmt 0x220edd0 \n" " `-IntegerLiteral 0x220edb0 'int' 0"; ASSERT_EQUALS("", parse(clang)); } void cxxMethodDecl3() { const char clang[] = "|-CXXRecordDecl 0x21cca40 <2.cpp:2:1, line:4:1> line:2:7 class Fred definition\n" "| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" "| | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" "| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" "| | |-MoveConstructor exists simple trivial needs_implicit\n" "| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" "| | |-MoveAssignment exists simple trivial needs_implicit\n" "| | `-Destructor simple irrelevant trivial needs_implicit\n" "| |-CXXRecordDecl 0x21ccb58 col:7 implicit class Fred\n" "| `-CXXMethodDecl 0x21ccc68 col:6 foo 'void ()'\n" "`-CXXMethodDecl 0x21ccd60 parent 0x21cca40 prev 0x21ccc68 col:12 foo 'void ()'\n" " `-CompoundStmt 0x21cce50 "; ASSERT_EQUALS("class Fred { void foo ( ) ; } ; void Fred :: foo ( ) { }", parse(clang)); } void cxxMethodDecl4() { const char clang[] = "|-ClassTemplateSpecializationDecl 0x15d82f8 line:8:12 struct char_traits definition\n" "| |-TemplateArgument type 'char'\n" "| | `-BuiltinType 0x15984c0 'char'\n" "| |-CXXRecordDecl 0x15d8520 col:12 implicit struct char_traits\n" "| |-CXXMethodDecl 0x15d8738 line:13:7 move 'char *(char *)' static\n" "| | |-ParmVarDecl 0x15d8630 col:18 used __s1 'char *'\n" "| | `-CompoundStmt 0x15d88e8 \n"; ASSERT_EQUALS("struct char_traits { static char * move ( char * __s1@1 ) { } } ;", parse(clang)); } void cxxNewExpr1() { const char clang[] = "|-VarDecl 0x3a97680 <1.cpp:2:1, col:14> col:6 i 'int *' cinit\n" "| `-CXXNewExpr 0x3a97d18 'int *' Function 0x3a97778 'operator new' 'void *(unsigned long)'\n" "`-VarDecl 0x3a97d80 col:6 j 'int *' cinit\n" " `-CXXNewExpr 0x3a97e68 'int *' array Function 0x3a978c0 'operator new[]' 'void *(unsigned long)'\n" " `-ImplicitCastExpr 0x3a97e18 'unsigned long' \n" " `-IntegerLiteral 0x3a97de0 'int' 100"; ASSERT_EQUALS("int * i@1 = new int ; int * j@2 = new int [ 100 ] ;", parse(clang)); } void cxxNewExpr2() { const char clang[] = "|-FunctionDecl 0x59a188 line:7:11 f 'struct S *()'\n" "| `-CompoundStmt 0x5c4318 \n" "| `-ReturnStmt 0x5c4308 \n" "| `-CXXNewExpr 0x5c42c8 'S *' Function 0x59a378 'operator new' 'void *(unsigned long)'\n" "| `-CXXConstructExpr 0x5c42a0 'S' 'void () noexcept'"; ASSERT_EQUALS("struct S * f ( ) { return new S ( ) ; }", parse(clang)); } void cxxNullPtrLiteralExpr() { const char clang[] = "`-VarDecl 0x2a7d650 <1.cpp:1:1, col:17> col:13 p 'const char *' cinit\n" " `-ImplicitCastExpr 0x2a7d708 'const char *' \n" " `-CXXNullPtrLiteralExpr 0x2a7d6f0 'nullptr_t'"; ASSERT_EQUALS("const char * p@1 = nullptr ;", parse(clang)); } void cxxOperatorCallExpr() { const char clang[] = "`-FunctionDecl 0x3c099f0 col:6 foo 'void ()'\n" " `-CompoundStmt 0x3c37308 \n" " |-DeclStmt 0x3c0a060 \n" " | `-VarDecl 0x3c09ae0 col:16 used c 'C' callinit\n" " | `-CXXConstructExpr 0x3c0a030 'C' 'void () noexcept'\n" " `-CXXOperatorCallExpr 0x3c372c0 'void'\n" " |-ImplicitCastExpr 0x3c372a8 'void (*)(int)' \n" " | `-DeclRefExpr 0x3c37250 'void (int)' lvalue CXXMethod 0x3c098c0 'operator=' 'void (int)'\n" " |-DeclRefExpr 0x3c0a078 'C' lvalue Var 0x3c09ae0 'c' 'C'\n" " `-IntegerLiteral 0x3c0a0a0 'int' 4"; ASSERT_EQUALS("void foo ( ) { C c@1 ( C ( ) ) ; c@1 . operator= ( 4 ) ; }", parse(clang)); } void cxxRecordDecl1() { const char clang[] = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 class Foo"; ASSERT_EQUALS("class Foo ;", parse(clang)); } void cxxRecordDecl2() { const char clang[] = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 struct Foo definition"; ASSERT_EQUALS("struct Foo { } ;", parse(clang)); } void cxxRecordDeclDerived() { const char clang[] = "|-CXXRecordDecl 0x19ccd38 line:4:8 referenced struct base definition\n" "| `-VarDecl 0x19ccf00 col:27 value 'const bool' static constexpr cinit\n" "| |-value: Int 0\n" "| `-CXXBoolLiteralExpr 0x19ccf68 'bool' false\n" "`-CXXRecordDecl 0x19ccfe8 col:8 struct derived definition\n" " |-public 'base'\n" " `-CXXRecordDecl 0x19cd150 col:8 implicit struct derived"; ASSERT_EQUALS("struct base { static const bool value@1 = false ; } ; struct derived : public base { } ;", parse(clang)); } void cxxStaticCastExpr1() { const char clang[] = "`-VarDecl 0x2e0e650 col:5 a 'int' cinit\n" " `-CXXStaticCastExpr 0x2e0e728 'int' static_cast \n" " `-IntegerLiteral 0x2e0e6f0 'int' 0"; ASSERT_EQUALS("int a@1 = static_cast ( 0 ) ;", parse(clang)); } void cxxStaticCastExpr2() { const char clang[] = "`-VarDecl 0x2e0e650 col:5 a 'int' cinit\n" " `-CXXStaticCastExpr 0x3e453e8 'std::_Rb_tree_iterator, Library::AllocFunc> >' xvalue static_cast, struct Library::AllocFunc> > &&> \n" " `-DeclRefExpr 0x3e453b0 'std::_Rb_tree_iterator, Library::AllocFunc> >' lvalue ParmVar 0x3e45250 '' 'std::_Rb_tree_iterator, Library::AllocFunc> > &&'"; ASSERT_EQUALS("int a@1 = static_cast,structLibrary::AllocFunc>>&&> ( ) ;", parse(clang)); } void cxxStaticCastExpr3() { const char clang[] = "`-ClassTemplateSpecializationDecl 0xd842d8 line:4:21 struct char_traits definition\n" " |-TemplateArgument type 'char'\n" " | `-BuiltinType 0xd444c0 'char'\n" " |-CXXRecordDecl 0xd84500 col:21 implicit struct char_traits\n" " |-TypedefDecl 0xd845a0 col:20 referenced char_type 'char'\n" " | `-BuiltinType 0xd444c0 'char'\n" " `-CXXMethodDecl 0xd847b0 col:18 assign 'char_traits::char_type *(char_traits::char_type *)'\n" " |-ParmVarDecl 0xd84670 col:36 used __s 'char_traits::char_type *'\n" " `-CompoundStmt 0xd848f8 \n" " `-ReturnStmt 0xd848e8 \n" " `-CXXStaticCastExpr 0xd848b8 'char_traits::char_type *' static_cast::char_type *> \n" " `-ImplicitCastExpr 0xd848a0 'char_traits::char_type *' part_of_explicit_cast\n" " `-DeclRefExpr 0xd84870 'char_traits::char_type *' lvalue ParmVar 0xd84670 '__s' 'char_traits::char_type *'\n"; ASSERT_EQUALS("struct char_traits { typedef char char_type ; char_traits::char_type * assign ( char_traits::char_type * __s@1 ) { return static_cast::char_type*> ( __s@1 ) ; } } ;", parse(clang)); } void cxxStdInitializerListExpr() { const char clang[] = "`-VarDecl 0x2f92060 <1.cpp:3:1, col:25> col:18 x 'std::vector':'std::vector >' listinit\n" " `-ExprWithCleanups 0x2fb0b40 'std::vector':'std::vector >'\n" " `-CXXConstructExpr 0x2fb0b00 'std::vector':'std::vector >' 'void (initializer_list >::value_type>, const std::vector >::allocator_type &)' list std::initializer_list\n" " |-CXXStdInitializerListExpr 0x2fb0928 'initializer_list >::value_type>':'std::initializer_list'\n" " | `-MaterializeTemporaryExpr 0x2fb0910 'const int [3]' xvalue\n" " | `-InitListExpr 0x2fb08b8 'const int [3]'\n" " | |-IntegerLiteral 0x2f920c0 'int' 1\n" " | |-IntegerLiteral 0x2f920e0 'int' 2\n" " | `-IntegerLiteral 0x2f92100 'int' 3\n" " `-CXXDefaultArgExpr 0x2fb0ae0 <> 'const std::vector >::allocator_type':'const std::allocator' lvalue"; ASSERT_EQUALS("std :: vector x@1 { 1 , 2 , 3 } ;", parse(clang)); } void cxxThrowExpr() { const char clang[] = "`-FunctionDecl 0x3701690 <1.cpp:2:1, col:23> col:6 foo 'void ()'\n" " `-CompoundStmt 0x37017b0 \n" " `-CXXThrowExpr 0x3701790 'void'\n" " `-IntegerLiteral 0x3701770 'int' 1"; ASSERT_EQUALS("void foo ( ) { throw 1 ; }", parse(clang)); } void defaultStmt() { const char clang[] = "`-FunctionDecl 0x18476b8 <1.c:3:1, line:9:1> line:3:5 foo 'int (int)'\n" " |-ParmVarDecl 0x18475e0 col:13 used rc 'int'\n" " `-CompoundStmt 0x1847868 \n" " `-SwitchStmt 0x18477e0 \n" " |-ImplicitCastExpr 0x18477c8 'int' \n" " | `-DeclRefExpr 0x18477a8 'int' lvalue ParmVar 0x18475e0 'rc' 'int'\n" " `-CompoundStmt 0x1847850 \n" " `-DefaultStmt 0x1847830 \n" " `-ReturnStmt 0x1847820 \n" " `-IntegerLiteral 0x1847800 'int' 1"; ASSERT_EQUALS("int foo ( int rc@1 ) { switch ( rc@1 ) { default : return 1 ; } }", parse(clang)); } void doStmt() { const char clang[] = "`-FunctionDecl 0x27fbbc8 col:6 foo 'void ()'\n" " `-CompoundStmt 0x27fbd08 \n" " `-DoStmt 0x27fbce8 \n" " |-CompoundStmt 0x27fbcb0 \n" " | `-UnaryOperator 0x27fbc90 'int' postfix '++'\n" " | `-DeclRefExpr 0x27fbc68 'int' lvalue Var 0x27fbae0 'x' 'int'\n" " `-IntegerLiteral 0x27fbcc8 'int' 1"; ASSERT_EQUALS("void foo ( ) { do { ++ x ; } while ( 1 ) ; }", parse(clang)); } void enumDecl1() { const char clang[] = "`-EnumDecl 0x2660660 col:6 referenced abc\n" " |-EnumConstantDecl 0x2660720 col:11 referenced a 'abc'\n" " |-EnumConstantDecl 0x2660768 col:13 b 'abc'\n" " `-EnumConstantDecl 0x26607b0 col:15 c 'abc'"; ASSERT_EQUALS("enum abc { a , b , c }", parse(clang)); } void enumDecl2() { const char clang[] = "`-EnumDecl 0xb55d50 <2.cpp:4:3, col:44> col:8 syntax_option_type 'unsigned int'"; ASSERT_EQUALS("enum syntax_option_type : unsigned int { }", parse(clang)); } void enumDecl3() { const char clang[] = "|-EnumDecl 0x1586e48 <2.cpp:1:3, line:5:3> line:1:8 __syntax_option\n" "| |-EnumConstantDecl 0x1586f18 col:5 referenced _S_polynomial '__syntax_option'\n" "| `-EnumConstantDecl 0x1586f68 col:5 _S_syntax_last '__syntax_option'"; ASSERT_EQUALS("enum __syntax_option { _S_polynomial , _S_syntax_last }", parse(clang)); } void enumDecl4() { const char clang[] = "|-EnumDecl 0xace1f8 col:1\n" "| |-EnumConstantDecl 0xace2c8 col:7 A '(anonymous enum at e1.cpp:3:1)'\n" "| |-EnumConstantDecl 0xace318 col:16 B '(anonymous enum at e1.cpp:3:1)'\n" "| `-EnumConstantDecl 0xace3b8 col:46 referenced C '(anonymous enum at e1.cpp:3:1)'\n" "`-VarDecl 0xace470 col:53 x 'enum (anonymous enum at e1.cpp:3:1)':'(anonymous enum at e1.cpp:3:1)' cinit\n" " `-DeclRefExpr 0xace520 '(anonymous enum at e1.cpp:3:1)' EnumConstant 0xace3b8 'C' '(anonymous enum at e1.cpp:3:1)'"; ASSERT_EQUALS("enum { A , B , C } x@1 = C ;", parse(clang)); } void forStmt() { const char clang[] = "`-FunctionDecl 0x2f93ae0 <1.c:1:1, col:56> col:5 main 'int ()'\n" " `-CompoundStmt 0x2f93dc0 \n" " |-ForStmt 0x2f93d50 \n" " | |-DeclStmt 0x2f93c58 \n" " | | `-VarDecl 0x2f93bd8 col:23 used i 'int' cinit\n" " | | `-IntegerLiteral 0x2f93c38 'int' 0\n" " | |-<<>>\n" " | |-BinaryOperator 0x2f93cd0 'int' '<'\n" " | | |-ImplicitCastExpr 0x2f93cb8 'int' \n" " | | | `-DeclRefExpr 0x2f93c70 'int' lvalue Var 0x2f93bd8 'i' 'int'\n" " | | `-IntegerLiteral 0x2f93c98 'int' 10\n" " | |-UnaryOperator 0x2f93d20 'int' postfix '++'\n" " | | `-DeclRefExpr 0x2f93cf8 'int' lvalue Var 0x2f93bd8 'i' 'int'\n" " | `-CompoundStmt 0x2f93d40 \n" " `-ReturnStmt 0x2f93da8 \n" " `-IntegerLiteral 0x2f93d88 'int' 0"; ASSERT_EQUALS("int main ( ) { for ( int i@1 = 0 ; i@1 < 10 ; ++ i@1 ) { } return 0 ; }", parse(clang)); } void funcdecl1() { const char clang[] = "`-FunctionDecl 0x3122c30 <1.c:1:1, col:22> col:6 foo 'void (int, int)'\n" " |-ParmVarDecl 0x3122ae0 col:14 x 'int'\n" " `-ParmVarDecl 0x3122b58 col:21 y 'int'"; ASSERT_EQUALS("void foo ( int x@1 , int y@2 ) ;", parse(clang)); } void funcdecl2() { const char clang[] = "`-FunctionDecl 0x24b2c38 <1.c:1:1, line:4:1> line:1:5 foo 'int (int, int)'\n" " |-ParmVarDecl 0x24b2ae0 col:13 used x 'int'\n" " |-ParmVarDecl 0x24b2b58 col:20 used y 'int'\n" " `-CompoundStmt 0x24b2de8 \n" " `-ReturnStmt 0x24b2dd0 \n" " `-BinaryOperator 0x24b2da8 'int' '/'\n" " |-ImplicitCastExpr 0x24b2d78 'int' \n" " | `-DeclRefExpr 0x24b2d28 'int' lvalue ParmVar 0x24b2ae0 'x' 'int'\n" " `-ImplicitCastExpr 0x24b2d90 'int' \n" " `-DeclRefExpr 0x24b2d50 'int' lvalue ParmVar 0x24b2b58 'y' 'int'"; ASSERT_EQUALS("int foo ( int x@1 , int y@2 ) { return x@1 / y@2 ; }", parse(clang)); } void funcdecl3() { const char clang[] = "|-FunctionDecl 0x27cb6b8 col:12 __overflow 'int (FILE *, int)' extern\n" "| |-ParmVarDecl 0x27cb528 col:30 'FILE *'\n" "| `-ParmVarDecl 0x27cb5a0 col:35 'int'"; ASSERT_EQUALS("int __overflow ( FILE * , int ) ;", parse(clang)); } void funcdecl4() { const char clang[] = "|-FunctionDecl 0x272bb60 col:15 implicit fwrite 'unsigned long (const void *, unsigned long, unsigned long, FILE *)' extern\n" "| |-ParmVarDecl 0x272cc40 <> 'const void *'\n" "| |-ParmVarDecl 0x272cca0 <> 'unsigned long'\n" "| |-ParmVarDecl 0x272cd00 <> 'unsigned long'\n" "| `-ParmVarDecl 0x272cd60 <> 'FILE *'"; ASSERT_EQUALS("unsigned long fwrite ( const void * , unsigned long , unsigned long , FILE * ) ;", parse(clang)); } void funcdecl5() { const char clang[] = "`-FunctionDecl 0x59d670 <1.c:1:1, col:28> col:20 foo 'void (void)' static inline"; ASSERT_EQUALS("static inline void foo ( ) ;", parse(clang)); } void funcdecl6() { const char clang[] = "`-FunctionDecl 0x196eea8 <1.cpp:3:5, col:27> col:12 foo 'void **(int)'\n" " `-ParmVarDecl 0x196eda0 col:21 count 'int'"; ASSERT_EQUALS("void * * foo ( int count@1 ) ;", parse(clang)); } void functionTemplateDecl1() { const char clang[] = "`-FunctionTemplateDecl 0x3242860 col:21 foo"; ASSERT_EQUALS("", parse(clang)); } void functionTemplateDecl2() { const char clang[] = "|-FunctionTemplateDecl 0x333a860 col:21 foo\n" "| |-TemplateTypeParmDecl 0x333a5f8 col:16 referenced class depth 0 index 0 T\n" "| |-FunctionDecl 0x333a7c0 col:21 foo 'T (T)'\n" "| | |-ParmVarDecl 0x333a6c0 col:27 referenced t 'T'\n" "| | `-CompoundStmt 0x333a980 \n" "| | `-ReturnStmt 0x333a968 \n" "| | `-BinaryOperator 0x333a940 '' '+'\n" "| | |-DeclRefExpr 0x333a8f8 'T' lvalue ParmVar 0x333a6c0 't' 'T'\n" "| | `-IntegerLiteral 0x333a920 'int' 1\n" "| `-FunctionDecl 0x333ae00 col:21 used foo 'int (int)'\n" "| |-TemplateArgument type 'int'\n" "| |-ParmVarDecl 0x333ad00 col:27 used t 'int':'int'\n" "| `-CompoundStmt 0x333b0a8 \n" "| `-ReturnStmt 0x333b090 \n" "| `-BinaryOperator 0x333b068 'int' '+'\n" "| |-ImplicitCastExpr 0x333b050 'int':'int' \n" "| | `-DeclRefExpr 0x333b028 'int':'int' lvalue ParmVar 0x333ad00 't' 'int':'int'\n" "| `-IntegerLiteral 0x333a920 'int' 1\n" "`-FunctionDecl 0x333a9f8 col:1 invalid bar 'int ()'\n" " `-CompoundStmt 0x333b010 \n" " `-CallExpr 0x333afe0 'int':'int'\n" " |-ImplicitCastExpr 0x333afc8 'int (*)(int)' \n" " | `-DeclRefExpr 0x333af00 'int (int)' lvalue Function 0x333ae00 'foo' 'int (int)' (FunctionTemplate 0x333a860 'foo')\n" " `-IntegerLiteral 0x333ab48 'int' 1"; ASSERT_EQUALS("int foo ( int t@1 ) { return t@1 + 1 ; } int bar ( ) { foo ( 1 ) ; }", parse(clang)); } void ifelse() { const char clang[] = "`-FunctionDecl 0x2637ba8 <1.c:1:1, line:4:1> line:1:5 foo 'int (int)'\n" " |-ParmVarDecl 0x2637ae0 col:13 used x 'int'\n" " `-CompoundStmt 0x2637d70 \n" " `-IfStmt 0x2637d38 \n" " |-<<>>\n" " |-<<>>\n" " |-BinaryOperator 0x2637cf0 'int' '>'\n" " | |-ImplicitCastExpr 0x2637cd8 'int' \n" " | | `-DeclRefExpr 0x2637c90 'int' lvalue ParmVar 0x2637ae0 'x' 'int'\n" " | `-IntegerLiteral 0x2637cb8 'int' 10\n" " |-CompoundStmt 0x2637d18 \n" " `-CompoundStmt 0x2637d28 "; ASSERT_EQUALS("int foo ( int x@1 ) { if ( x@1 > 10 ) { } else { } }", parse(clang)); } void ifStmt() { // Clang 8 in cygwin const char clang[] = "`-FunctionDecl 0x41d0690 <2.cpp:1:1, col:24> col:6 foo 'void ()'\n" " `-CompoundStmt 0x41d07f0 \n" " `-IfStmt 0x41d07b8 \n" " |-ImplicitCastExpr 0x41d0790 'bool' \n" " | `-IntegerLiteral 0x41d0770 'int' 1\n" " |-CompoundStmt 0x41d07a8 \n"; ASSERT_EQUALS("void foo ( ) { if ( 1 ) { } }", parse(clang)); } void initListExpr() { const char clang[] = "|-VarDecl 0x397c680 <1.cpp:2:1, col:26> col:11 used ints 'const int [3]' cinit\n" "| `-InitListExpr 0x397c7d8 'const int [3]'\n" "| |-IntegerLiteral 0x397c720 'int' 1\n" "| |-IntegerLiteral 0x397c740 'int' 2\n" "| `-IntegerLiteral 0x397c760 'int' 3"; ASSERT_EQUALS("const int [3] ints@1 = { 1 , 2 , 3 } ;", parse(clang)); } void labelStmt() { const char clang[] = "`-FunctionDecl 0x2ed1ba0 <1.c:1:1, col:36> col:6 foo 'void (int)'\n" " `-CompoundStmt 0x2ed1d00 \n" " `-LabelStmt 0x2ed1ce8 'loop'\n" " `-GotoStmt 0x2ed1cd0 'loop' 0x2ed1c88"; ASSERT_EQUALS("void foo ( ) { loop : goto loop ; }", parse(clang)); } void memberExpr() { // C code: // struct S { int x }; // int foo(struct S s) { return s.x; } const char clang[] = "|-RecordDecl 0x2441a88 <1.c:1:1, col:18> col:8 struct S definition\n" "| `-FieldDecl 0x2441b48 col:16 referenced x 'int'\n" "`-FunctionDecl 0x2441cf8 col:5 foo 'int (struct S)'\n" " |-ParmVarDecl 0x2441be8 col:18 used s 'struct S':'struct S'\n" " `-CompoundStmt 0x2441e70 \n" " `-ReturnStmt 0x2441e58 \n" " `-ImplicitCastExpr 0x2441e40 'int' \n" " `-MemberExpr 0x2441e08 'int' lvalue .x 0x2441b48\n" " `-DeclRefExpr 0x2441de0 'struct S':'struct S' lvalue ParmVar 0x2441be8 's' 'struct S':'struct S'"; ASSERT_EQUALS("struct S { int x@1 ; } ; int foo ( struct S s@2 ) { return s@2 . x@1 ; }", parse(clang)); } void namespaceDecl1() { const char clang[] = "`-NamespaceDecl 0x2e5f658 col:11 x\n" " `-VarDecl 0x2e5f6d8 col:19 var 'int'"; ASSERT_EQUALS("namespace x { int var@1 ; }", parse(clang)); } void namespaceDecl2() { const char clang[] = "`-NamespaceDecl 0x1753e60 <1.cpp:1:1, line:4:1> line:1:11 std\n" " |-VisibilityAttr 0x1753ed0 Default\n" " `-VarDecl 0x1753f40 col:9 x 'int'"; ASSERT_EQUALS("namespace std { int x@1 ; }", parse(clang)); } void recordDecl1() { const char clang[] = "`-RecordDecl 0x354eac8 <1.c:1:1, line:4:1> line:1:8 struct S definition\n" " |-FieldDecl 0x354eb88 col:7 x 'int'\n" " `-FieldDecl 0x354ebe8 col:7 y 'int'"; ASSERT_EQUALS("struct S { int x@1 ; int y@2 ; } ;", parse(clang)); } void recordDecl2() { const char clang[] = "`-RecordDecl 0x3befac8 <2.c:2:1, col:22> col:1 struct definition\n" " `-FieldDecl 0x3befbf0 col:14 val 'int'"; ASSERT_EQUALS("struct { int val@1 ; } ;", parse(clang)); } void switchStmt() { const char clang[] = "`-FunctionDecl 0x2796ba0 <1.c:1:1, col:35> col:6 foo 'void (int)'\n" " |-ParmVarDecl 0x2796ae0 col:14 used x 'int'\n" " `-CompoundStmt 0x2796d18 \n" " |-SwitchStmt 0x2796cc8 \n" " | |-<<>>\n" " | |-<<>>\n" " | |-ImplicitCastExpr 0x2796cb0 'int' \n" " | | `-DeclRefExpr 0x2796c88 'int' lvalue ParmVar 0x2796ae0 'x' 'int'\n" " | `-CompoundStmt 0x2796cf8 \n" " `-NullStmt 0x2796d08 "; ASSERT_EQUALS("void foo ( int x@1 ) { switch ( x@1 ) { } ; }", parse(clang)); } void typedefDecl1() { const char clang[] = "|-TypedefDecl 0x2d60180 <> implicit __int128_t '__int128'\n" "| `-BuiltinType 0x2d5fe80 '__int128'"; ASSERT_EQUALS("typedef __int128 __int128_t ;", parse(clang)); } void typedefDecl2() { const char clang[] = "|-TypedefDecl 0x2d604a8 <> implicit __NSConstantString 'struct __NSConstantString_tag'\n" "| `-RecordType 0x2d602c0 'struct __NSConstantString_tag'\n" "| `-Record 0x2d60238 '__NSConstantString_tag'"; ASSERT_EQUALS("typedef struct __NSConstantString_tag __NSConstantString ;", parse(clang)); } void typedefDecl3() { const char clang[] = "|-TypedefDecl 0x2d60540 <> implicit __builtin_ms_va_list 'char *'\n" "| `-PointerType 0x2d60500 'char *'\n" "| `-BuiltinType 0x2d5f980 'char'"; ASSERT_EQUALS("typedef char * __builtin_ms_va_list ;", parse(clang)); } void unaryExprOrTypeTraitExpr1() { const char clang[] = "`-VarDecl 0x24cc610 col:5 x 'int' cinit\n" " `-ImplicitCastExpr 0x24cc6e8 'int' \n" " `-UnaryExprOrTypeTraitExpr 0x24cc6c8 'unsigned long' sizeof 'int'\n"; ASSERT_EQUALS("int x@1 = sizeof ( int ) ;", parse(clang)); } void unaryExprOrTypeTraitExpr2() { const char clang[] = "`-VarDecl 0x27c6c00 col:9 x 'int' cinit\n" " `-ImplicitCastExpr 0x27c6cc8 'int' \n" " `-UnaryExprOrTypeTraitExpr 0x27c6ca8 'unsigned long' sizeof\n" " `-ParenExpr 0x27c6c88 'char [10]' lvalue\n" " `-DeclRefExpr 0x27c6c60 'char [10]' lvalue Var 0x27c6b48 'buf' 'char [10]'"; ASSERT_EQUALS("int x@1 = sizeof ( buf ) ;", parse(clang)); } void unaryOperator() { const char clang[] = "`-FunctionDecl 0x2dd9748 <1.cpp:2:1, col:44> col:5 foo 'int (int *)'\n" " |-ParmVarDecl 0x2dd9680 col:19 used p 'int *'\n" " `-CompoundStmt 0x2dd9908 \n" " `-ReturnStmt 0x2dd98f0 \n" " `-BinaryOperator 0x2dd98c8 'int' '/'\n" " |-IntegerLiteral 0x2dd9830 'int' 100000\n" " `-ImplicitCastExpr 0x2dd98b0 'int' \n" " `-UnaryOperator 0x2dd9890 'int' lvalue prefix '*' cannot overflow\n" " `-ImplicitCastExpr 0x2dd9878 'int *' \n" " `-DeclRefExpr 0x2dd9850 'int *' lvalue ParmVar 0x2dd9680 'p' 'int *'"; ASSERT_EQUALS("int foo ( int * p@1 ) { return 100000 / * p@1 ; }", parse(clang)); } void vardecl1() { const char clang[] = "|-VarDecl 0x32b8aa0 <1.c:1:1, col:9> col:5 used a 'int' cinit\n" "| `-IntegerLiteral 0x32b8b40 'int' 1\n" "`-VarDecl 0x32b8b78 col:5 b 'int' cinit\n" " `-ImplicitCastExpr 0x32b8c00 'int' \n" " `-DeclRefExpr 0x32b8bd8 'int' lvalue Var 0x32b8aa0 'a' 'int'"; ASSERT_EQUALS("int a@1 = 1 ; int b@2 = a@1 ;", parse(clang)); } void vardecl2() { const char clang[] = "|-VarDecl 0x3873b50 <1.c:1:1, col:9> col:5 used a 'int [10]'\n" "`-FunctionDecl 0x3873c38 line:3:6 foo 'void ()'\n" " `-CompoundStmt 0x3873dd0 \n" " `-BinaryOperator 0x3873da8 'int' '='\n" " |-ArraySubscriptExpr 0x3873d60 'int' lvalue\n" " | |-ImplicitCastExpr 0x3873d48 'int *' \n" " | | `-DeclRefExpr 0x3873cd8 'int [10]' lvalue Var 0x3873b50 'a' 'int [10]'\n" " | `-IntegerLiteral 0x3873d00 'int' 0\n" " `-IntegerLiteral 0x3873d88 'int' 0\n"; ASSERT_EQUALS("int [10] a@1 ; void foo ( ) { a@1 [ 0 ] = 0 ; }", parse(clang)); } void vardecl3() { const char clang[] = "`-VarDecl 0x25a8aa0 <1.c:1:1, col:12> col:12 p 'const int *'"; ASSERT_EQUALS("const int * p@1 ;", parse(clang)); } void vardecl4() { const char clang[] = "|-VarDecl 0x23d6c78 col:14 stdin 'FILE *' extern"; ASSERT_EQUALS("FILE * stdin@1 ;", parse(clang)); } void vardecl5() { const char clang[] = "|-VarDecl 0x2e31fc0 col:26 sys_errlist 'const char *const []' extern"; ASSERT_EQUALS("const char * const [] sys_errlist@1 ;", parse(clang)); } void vardecl6() { const char clang[] = "`-VarDecl 0x278e170 <1.c:1:1, col:16> col:12 x 'int' static cinit\n" " `-IntegerLiteral 0x278e220 'int' 3"; ASSERT_EQUALS("static int x@1 = 3 ;", parse(clang)); } void vardecl7() { const char clang[] = "`-VarDecl 0x2071f20 <1.cpp:2:1, col:23> col:9 start 'void *(*)(void *)'"; ASSERT_EQUALS("void * * start@1 ;", parse(clang)); } void whileStmt1() { const char clang[] = "`-FunctionDecl 0x3d45b18 <1.c:1:1, line:3:1> line:1:6 foo 'void ()'\n" " `-CompoundStmt 0x3d45c48 \n" " `-WhileStmt 0x3d45c28 \n" " |-<<>>\n" " |-IntegerLiteral 0x3d45bf8 'int' 0\n" " `-NullStmt 0x3d45c18 "; ASSERT_EQUALS("void foo ( ) { while ( 0 ) { ; } }", parse(clang)); } void whileStmt2() { // clang 9 const char clang[] = "`-FunctionDecl 0x1c99ac8 <1.cpp:1:1, col:27> col:6 foo 'void ()'\n" " `-CompoundStmt 0x1c99c10 \n" " `-WhileStmt 0x1c99bf8 \n" " |-ImplicitCastExpr 0x1c99bd0 'bool' \n" " | `-IntegerLiteral 0x1c99bb0 'int' 1\n" " `-CompoundStmt 0x1c99be8 "; ASSERT_EQUALS("void foo ( ) { while ( 1 ) { } }", parse(clang)); } #define GET_SYMBOL_DB(AST) \ Settings settings; \ settings.clang = true; \ settings.platform(cppcheck::Platform::PlatformType::Unix64); \ Tokenizer tokenizer(&settings, this); \ std::istringstream istr(AST); \ clangimport::parseClangAstDump(&tokenizer, istr); \ const SymbolDatabase *db = tokenizer.getSymbolDatabase(); \ ASSERT(db) void tokenIndex() { const char clang[] = "`-FunctionDecl 0x1e07dd0 <67.cpp:1:1, col:13> col:6 foo 'void ()'\n" " `-CompoundStmt 0x1e07eb8 "; ASSERT_EQUALS("void foo ( ) { }", parse(clang)); GET_SYMBOL_DB(clang); const Token *tok = tokenizer.tokens(); ASSERT_EQUALS(tok->index() + 1, tok->next()->index()); } void symbolDatabaseEnum1() { const char clang[] = "|-NamespaceDecl 0x29ad5f8 <1.cpp:1:1, line:3:1> line:1:11 ns\n" "| `-EnumDecl 0x29ad660 col:6 referenced abc\n" "| |-EnumConstantDecl 0x29ad720 col:11 a 'ns::abc'\n" "| |-EnumConstantDecl 0x29ad768 col:13 b 'ns::abc'\n" "| `-EnumConstantDecl 0x29ad7b0 col:15 referenced c 'ns::abc'\n" "`-VarDecl 0x29ad898 col:9 x 'ns::abc':'ns::abc' cinit\n" " `-DeclRefExpr 0x29ad998 'ns::abc' EnumConstant 0x29ad7b0 'c' 'ns::abc'\n"; ASSERT_EQUALS("namespace ns { enum abc { a , b , c } } ns :: abc x@1 = c ;", parse(clang)); GET_SYMBOL_DB(clang); // Enum scope and type ASSERT_EQUALS(3, db->scopeList.size()); const Scope &enumScope = db->scopeList.back(); ASSERT_EQUALS(Scope::ScopeType::eEnum, enumScope.type); ASSERT_EQUALS("abc", enumScope.className); const Type *enumType = enumScope.definedType; ASSERT_EQUALS("abc", enumType->name()); // Variable const Token *vartok = Token::findsimplematch(tokenizer.tokens(), "x"); ASSERT(vartok); ASSERT(vartok->variable()); ASSERT(vartok->variable()->valueType()); ASSERT_EQUALS(uintptr_t(&enumScope), uintptr_t(vartok->variable()->valueType()->typeScope)); } void symbolDatabaseFunction1() { const char clang[] = "|-FunctionDecl 0x3aea7a0 <1.cpp:2:1, col:22> col:6 used foo 'void (int, int)'\n" " |-ParmVarDecl 0x3aea650 col:14 x 'int'\n" " |-ParmVarDecl 0x3aea6c8 col:21 y 'int'\n" " `-CompoundStmt 0x3d45c48 \n"; GET_SYMBOL_DB(clang); // There is a function foo that has 2 arguments ASSERT_EQUALS(1, db->functionScopes.size()); const Scope *scope = db->functionScopes[0]; const Function *func = scope->function; ASSERT_EQUALS(2, func->argCount()); ASSERT_EQUALS("x", func->getArgumentVar(0)->name()); ASSERT_EQUALS("y", func->getArgumentVar(1)->name()); ASSERT_EQUALS(ValueType::Type::INT, func->getArgumentVar(0)->valueType()->type); ASSERT_EQUALS(ValueType::Type::INT, func->getArgumentVar(1)->valueType()->type); } void symbolDatabaseFunction2() { const char clang[] = "|-FunctionDecl 0x3aea7a0 <1.cpp:2:1, col:22> col:6 used foo 'void (int, int)'\n" "| |-ParmVarDecl 0x3aea650 col:14 'int'\n" "| |-ParmVarDecl 0x3aea6c8 col:21 'int'\n" " `-CompoundStmt 0x3d45c48 \n"; GET_SYMBOL_DB(clang); // There is a function foo that has 2 arguments ASSERT_EQUALS(1, db->functionScopes.size()); const Scope *scope = db->functionScopes[0]; const Function *func = scope->function; ASSERT_EQUALS(2, func->argCount()); ASSERT_EQUALS(0, (long long)func->getArgumentVar(0)->nameToken()); ASSERT_EQUALS(0, (long long)func->getArgumentVar(1)->nameToken()); } void symbolDatabaseFunction3() { // #9640 const char clang[] = "`-FunctionDecl 0x238fcd8 <9640.cpp:1:1, col:26> col:6 used bar 'bool (const char, int &)'\n" " |-ParmVarDecl 0x238fb10 col:20 'const char'\n" " |-ParmVarDecl 0x238fbc0 col:26 'int &'\n" " `-CompoundStmt 0x3d45c48 \n"; GET_SYMBOL_DB(clang); // There is a function foo that has 2 arguments ASSERT_EQUALS(1, db->functionScopes.size()); const Scope *scope = db->functionScopes[0]; const Function *func = scope->function; ASSERT_EQUALS(2, func->argCount()); ASSERT_EQUALS(false, func->getArgumentVar(0)->isReference()); ASSERT_EQUALS(true, func->getArgumentVar(1)->isReference()); } void symbolDatabaseFunctionConst() { const char clang[] = "`-CXXRecordDecl 0x7e2d98 <1.cpp:2:1, line:5:1> line:2:7 class foo definition\n" " `-CXXMethodDecl 0x7e3000 col:8 f 'void () const'"; GET_SYMBOL_DB(clang); // There is a function f that is const ASSERT_EQUALS(2, db->scopeList.size()); ASSERT_EQUALS(1, db->scopeList.back().functionList.size()); const Function &func = db->scopeList.back().functionList.back(); ASSERT(func.isConst()); } void symbolDatabaseVariableRef() { const char clang[] = "`-FunctionDecl 0x1593df0 <3.cpp:1:1, line:4:1> line:1:6 foo 'void ()'\n" " `-CompoundStmt 0x15940b0 \n" " |-DeclStmt 0x1593f58 \n" " | `-VarDecl 0x1593ef0 col:7 used x 'int'\n" " `-DeclStmt 0x1594098 \n" " `-VarDecl 0x1593fb8 col:8 ref 'int &' cinit\n" " `-DeclRefExpr 0x1594020 'int' lvalue Var 0x1593ef0 'x' 'int'"; GET_SYMBOL_DB(clang); const Variable *refVar = db->variableList().back(); ASSERT(refVar->isReference()); } void symbolDatabaseVariableRRef() { const char clang[] = "`-FunctionDecl 0x1a40df0 <3.cpp:1:1, line:4:1> line:1:6 foo 'void ()'\n" " `-CompoundStmt 0x1a41180 \n" " |-DeclStmt 0x1a40f58 \n" " | `-VarDecl 0x1a40ef0 col:7 used x 'int'\n" " `-DeclStmt 0x1a41168 \n" " `-VarDecl 0x1a40fb8 col:9 ref 'int &&' cinit\n" " `-ExprWithCleanups 0x1a410f8 'int' xvalue\n" " `-MaterializeTemporaryExpr 0x1a41098 'int' xvalue extended by Var 0x1a40fb8 'ref' 'int &&'\n" " `-BinaryOperator 0x1a41078 'int' '+'\n" " |-ImplicitCastExpr 0x1a41060 'int' \n" " | `-DeclRefExpr 0x1a41020 'int' lvalue Var 0x1a40ef0 'x' 'int'\n" " `-IntegerLiteral 0x1a41040 'int' 1\n"; ASSERT_EQUALS("void foo ( ) { int x@1 ; int && ref@2 = x@1 + 1 ; }", parse(clang)); GET_SYMBOL_DB(clang); const Variable *refVar = db->variableList().back(); ASSERT(refVar->isReference()); ASSERT(refVar->isRValueReference()); } void symbolDatabaseVariablePointerRef() { const char clang[] = "`-FunctionDecl 0x9b4f10 <3.cpp:1:1, col:17> col:6 used foo 'void (int *&)'\n" " `-ParmVarDecl 0x9b4e40 col:16 p 'int *&'\n"; ASSERT_EQUALS("void foo ( int * & p@1 ) ;", parse(clang)); GET_SYMBOL_DB(clang); const Variable *p = db->variableList().back(); ASSERT(p->isPointer()); ASSERT(p->isReference()); } void symbolDatabaseNodeType1() { const char clang[] = "`-FunctionDecl 0x32438c0 line:5:6 foo 'a::b (a::b)'\n" " |-ParmVarDecl 0x32437b0 col:15 used i 'a::b':'long'\n" " `-CompoundStmt 0x3243a60 \n" " `-ReturnStmt 0x3243a48 \n" " `-BinaryOperator 0x3243a20 'long' '+'\n" " |-ImplicitCastExpr 0x32439f0 'a::b':'long' \n" " | `-DeclRefExpr 0x32439a8 'a::b':'long' lvalue ParmVar 0x32437b0 'i' 'a::b':'long'\n" " `-ImplicitCastExpr 0x3243a08 'long' \n" " `-IntegerLiteral 0x32439d0 'int' 1\n"; GET_SYMBOL_DB(clang); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "i + 1"); ASSERT(!!tok); ASSERT(!!tok->valueType()); ASSERT_EQUALS("signed long", tok->valueType()->str()); } void symbolDatabaseForVariable() { const char clang[] = "`-FunctionDecl 0x38f8bb0 <10100.c:2:1, line:4:1> line:2:6 f 'void (void)'\n" " `-CompoundStmt 0x38f8d88 \n" " `-ForStmt 0x38f8d50 \n" " |-DeclStmt 0x38f8d28 \n" " | `-VarDecl 0x38f8ca8 col:14 i 'int' cinit\n" " | `-IntegerLiteral 0x38f8d08 'int' 0\n" " |-<<>>\n" " |-<<>>\n" " |-<<>>\n" " `-CompoundStmt 0x38f8d40 "; ASSERT_EQUALS("void f ( ) { for ( int i@1 = 0 ; ; ) { } }", parse(clang)); GET_SYMBOL_DB(clang); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "i"); ASSERT(!!tok); ASSERT(!!tok->variable()); ASSERT_EQUALS(Scope::ScopeType::eFor, tok->variable()->scope()->type); } void valueFlow1() { // struct S { int x; int buf[10]; } ; int sz = sizeof(struct S); const char clang[] = "|-RecordDecl 0x2fc5a88 <1.c:1:1, line:4:1> line:1:8 struct S definition\n" "| |-FieldDecl 0x2fc5b48 col:7 x 'int'\n" "| `-FieldDecl 0x2fc5c10 col:7 buf 'int [10]'\n" "`-VarDecl 0x2fc5c70 col:5 sz 'int' cinit\n" " `-ImplicitCastExpr 0x2fc5d88 'int' \n" " `-UnaryExprOrTypeTraitExpr 0x2fc5d68 'unsigned long' sizeof 'struct S':'struct S'"; GET_SYMBOL_DB(clang); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "sizeof ("); ASSERT(!!tok); tok = tok->next(); ASSERT(tok->hasKnownIntValue()); ASSERT_EQUALS(44, tok->getKnownIntValue()); } void valueFlow2() { // int buf[42]; // int x = sizeof(buf); const char clang[] = "|-VarDecl 0x10f6de8 <66.cpp:3:1, col:11> col:5 referenced buf 'int [42]'\n" "`-VarDecl 0x10f6eb0 col:5 x 'int' cinit\n" " `-ImplicitCastExpr 0x10f6f78 'int' \n" " `-UnaryExprOrTypeTraitExpr 0x10f6f58 'unsigned long' sizeof\n" " `-ParenExpr 0x10f6f38 'int [42]' lvalue\n" " `-DeclRefExpr 0x10f6f18 'int [42]' lvalue Var 0x10f6de8 'buf' 'int [42]' non_odr_use_unevaluated"; GET_SYMBOL_DB(clang); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "sizeof ("); ASSERT(!!tok); tok = tok->next(); // TODO ASSERT(tok->hasKnownIntValue()); // TODO ASSERT_EQUALS(10, tok->getKnownIntValue()); } void valueType1() { const char clang[] = "`-FunctionDecl 0x32438c0 line:5:6 foo 'a::b (a::b)'\n" " |-ParmVarDecl 0x32437b0 col:15 used i 'a::b':'long'\n" " `-CompoundStmt 0x3243a60 \n" " `-ReturnStmt 0x3243a48 \n" " `-ImplicitCastExpr 0x2176ca8 'int' \n" " `-ImplicitCastExpr 0x2176c90 'bool' \n" " `-DeclRefExpr 0x2176c60 'bool' lvalue Var 0x2176bd0 'e' 'bool'\n"; GET_SYMBOL_DB(clang); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "e"); ASSERT(!!tok); ASSERT(!!tok->valueType()); ASSERT_EQUALS("bool", tok->valueType()->str()); } void valueType2() { const char clang[] = "`-VarDecl 0xc9eda0 <1.cpp:2:1, col:17> col:13 s 'const char *' cinit\n" " `-ImplicitCastExpr 0xc9eef0 'const char *' \n" " `-StringLiteral 0xc9eed0 'const char [6]' lvalue \"hello\"\n"; GET_SYMBOL_DB(clang); const Token *tok = Token::findsimplematch(tokenizer.tokens(), "\"hello\""); ASSERT(!!tok); ASSERT(!!tok->valueType()); ASSERT_EQUALS("const signed char *", tok->valueType()->str()); } }; REGISTER_TEST(TestClangImport) cppcheck-2.7/test/testclass.cpp000066400000000000000000011614541417746362400166640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check.h" #include "checkclass.h" #include "config.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include class TestClass : public TestFixture { public: TestClass() : TestFixture("TestClass") {} private: Settings settings0; Settings settings1; void run() OVERRIDE { settings0.severity.enable(Severity::style); settings1.severity.enable(Severity::warning); // Load std.cfg configuration { const char xmldata[] = "\n" "\n" " \n" " malloc\n" " free\n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings0.library.load(doc); settings1.library.load(doc); } TEST_CASE(virtualDestructor1); // Base class not found => no error TEST_CASE(virtualDestructor2); // Base class doesn't have a destructor TEST_CASE(virtualDestructor3); // Base class has a destructor, but it's not virtual TEST_CASE(virtualDestructor4); // Derived class doesn't have a destructor => no error TEST_CASE(virtualDestructor5); // Derived class has empty destructor => no error TEST_CASE(virtualDestructor6); // only report error if base class pointer that points at derived class is deleted TEST_CASE(virtualDestructorProtected); TEST_CASE(virtualDestructorInherited); TEST_CASE(virtualDestructorTemplate); TEST_CASE(virtualDestructorInconclusive); // ticket # 5807 TEST_CASE(copyConstructor1); TEST_CASE(copyConstructor2); // ticket #4458 TEST_CASE(copyConstructor3); // defaulted/deleted TEST_CASE(copyConstructor4); // base class with private constructor TEST_CASE(copyConstructor5); // multiple inheritance TEST_CASE(noOperatorEq); // class with memory management should have operator eq TEST_CASE(noDestructor); // class with memory management should have destructor TEST_CASE(operatorEqRetRefThis1); TEST_CASE(operatorEqRetRefThis2); // ticket #1323 TEST_CASE(operatorEqRetRefThis3); // ticket #1405 TEST_CASE(operatorEqRetRefThis4); // ticket #1451 TEST_CASE(operatorEqRetRefThis5); // ticket #1550 TEST_CASE(operatorEqRetRefThis6); // ticket #2479 TEST_CASE(operatorEqRetRefThis7); // ticket #5782 endless recursion TEST_CASE(operatorEqToSelf1); // single class TEST_CASE(operatorEqToSelf2); // nested class TEST_CASE(operatorEqToSelf3); // multiple inheritance TEST_CASE(operatorEqToSelf4); // nested class with multiple inheritance TEST_CASE(operatorEqToSelf5); // ticket # 1233 TEST_CASE(operatorEqToSelf6); // ticket # 1550 TEST_CASE(operatorEqToSelf7); TEST_CASE(operatorEqToSelf8); // ticket #2179 TEST_CASE(operatorEqToSelf9); // ticket #2592 TEST_CASE(memsetOnStruct); TEST_CASE(memsetVector); TEST_CASE(memsetOnClass); TEST_CASE(memsetOnInvalid); // Ticket #5425: Crash upon invalid TEST_CASE(memsetOnStdPodType); // Ticket #5901 - std::uint8_t TEST_CASE(memsetOnFloat); // Ticket #5421 TEST_CASE(memsetOnUnknown); // Ticket #7183 TEST_CASE(mallocOnClass); TEST_CASE(this_subtraction); // warn about "this-x" // can member function be made const TEST_CASE(const1); TEST_CASE(const2); TEST_CASE(const3); TEST_CASE(const4); TEST_CASE(const5); // ticket #1482 TEST_CASE(const6); // ticket #1491 TEST_CASE(const7); TEST_CASE(const8); // ticket #1517 TEST_CASE(const9); // ticket #1515 TEST_CASE(const10); // ticket #1522 TEST_CASE(const11); // ticket #1529 TEST_CASE(const12); // ticket #1552 TEST_CASE(const13); // ticket #1519 TEST_CASE(const14); TEST_CASE(const15); TEST_CASE(const16); // ticket #1551 TEST_CASE(const17); // ticket #1552 TEST_CASE(const18); TEST_CASE(const19); // ticket #1612 TEST_CASE(const20); // ticket #1602 TEST_CASE(const21); // ticket #1683 TEST_CASE(const22); TEST_CASE(const23); // ticket #1699 TEST_CASE(const24); // ticket #1708 TEST_CASE(const25); // ticket #1724 TEST_CASE(const26); // ticket #1847 TEST_CASE(const27); // ticket #1882 TEST_CASE(const28); // ticket #1883 TEST_CASE(const29); // ticket #1922 TEST_CASE(const30); TEST_CASE(const31); TEST_CASE(const32); // ticket #1905 - member array is assigned TEST_CASE(const33); TEST_CASE(const34); // ticket #1964 TEST_CASE(const35); // ticket #2001 TEST_CASE(const36); // ticket #2003 TEST_CASE(const37); // ticket #2081 and #2085 TEST_CASE(const38); // ticket #2135 TEST_CASE(const39); TEST_CASE(const40); // ticket #2228 TEST_CASE(const41); // ticket #2255 TEST_CASE(const42); // ticket #2282 TEST_CASE(const43); // ticket #2377 TEST_CASE(const44); // ticket #2595 TEST_CASE(const45); // ticket #2664 TEST_CASE(const46); // ticket #2636 TEST_CASE(const47); // ticket #2670 TEST_CASE(const48); // ticket #2672 TEST_CASE(const49); // ticket #2795 TEST_CASE(const50); // ticket #2943 TEST_CASE(const51); // ticket #3040 TEST_CASE(const52); // ticket #3048 TEST_CASE(const53); // ticket #3049 TEST_CASE(const54); // ticket #3052 TEST_CASE(const55); TEST_CASE(const56); // ticket #3149 TEST_CASE(const57); // tickets #2669 and #2477 TEST_CASE(const58); // ticket #2698 TEST_CASE(const59); // ticket #4646 TEST_CASE(const60); // ticket #3322 TEST_CASE(const61); // ticket #5606 TEST_CASE(const62); // ticket #5701 TEST_CASE(const63); // ticket #5983 TEST_CASE(const64); // ticket #6268 TEST_CASE(const65); // ticket #8693 TEST_CASE(const66); // ticket #7714 TEST_CASE(const67); // ticket #9193 TEST_CASE(const68); // ticket #6471 TEST_CASE(const69); // ticket #9806 TEST_CASE(const70); // variadic template can receive more arguments than in its definition TEST_CASE(const71); // ticket #10146 TEST_CASE(const72); // ticket #10520 TEST_CASE(const73); // ticket #10735 TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(assigningPointerToPointerIsNotAConstOperation); TEST_CASE(assigningArrayElementIsNotAConstOperation); TEST_CASE(constoperator1); // operator< can often be const TEST_CASE(constoperator2); // operator<< TEST_CASE(constoperator3); TEST_CASE(constoperator4); TEST_CASE(constoperator5); // ticket #3252 TEST_CASE(constoperator6); // ticket #8669 TEST_CASE(constincdec); // increment/decrement => non-const TEST_CASE(constassign1); TEST_CASE(constassign2); TEST_CASE(constincdecarray); // increment/decrement array element => non-const TEST_CASE(constassignarray); TEST_CASE(constReturnReference); TEST_CASE(constDelete); // delete member variable => not const TEST_CASE(constLPVOID); // a function that returns LPVOID can't be const TEST_CASE(constFunc); // a function that calls const functions can be const TEST_CASE(constVirtualFunc); TEST_CASE(constIfCfg); // ticket #1881 - fp when there are #if TEST_CASE(constFriend); // ticket #1921 - fp for friend function TEST_CASE(constUnion); // ticket #2111 - fp when there is a union TEST_CASE(constArrayOperator); // #4406 TEST_CASE(constRangeBasedFor); // #5514 TEST_CASE(const_shared_ptr); TEST_CASE(constPtrToConstPtr); TEST_CASE(constTrailingReturnType); TEST_CASE(staticArrayPtrOverload); TEST_CASE(initializerListOrder); TEST_CASE(initializerListUsage); TEST_CASE(selfInitialization); TEST_CASE(virtualFunctionCallInConstructor); TEST_CASE(pureVirtualFunctionCall); TEST_CASE(pureVirtualFunctionCallOtherClass); TEST_CASE(pureVirtualFunctionCallWithBody); TEST_CASE(pureVirtualFunctionCallPrevented); TEST_CASE(duplInheritedMembers); TEST_CASE(explicitConstructors); TEST_CASE(copyCtorAndEqOperator); TEST_CASE(override1); TEST_CASE(overrideCVRefQualifiers); TEST_CASE(thisUseAfterFree); TEST_CASE(unsafeClassRefMember); TEST_CASE(ctuOneDefinitionRule); TEST_CASE(testGetFileInfo); } #define checkCopyCtorAndEqOperator(code) checkCopyCtorAndEqOperator_(code, __FILE__, __LINE__) void checkCopyCtorAndEqOperator_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); Settings settings; settings.severity.enable(Severity::warning); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings, this); (checkClass.checkCopyCtorAndEqOperator)(); } void copyCtorAndEqOperator() { checkCopyCtorAndEqOperator("class A\n" "{\n" " A(const A& other) { }\n" " A& operator=(const A& other) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A\n" "{\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A\n" "{\n" " A(const A& other) { }\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A\n" "{\n" " A& operator=(const A& other) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A\n" "{\n" " A(const A& other) { }\n" " int x;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'copy constructor' but lack of 'operator='.\n", "", errout.str()); // TODO the error message should be clarified. It should say something like 'copy constructor is empty and will not assign i and therefore the behaviour is different to the default assignment operator' checkCopyCtorAndEqOperator("class A\n" "{\n" " A& operator=(const A& other) { return *this; }\n" " int x;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'operator=' but lack of 'copy constructor'.\n", "", errout.str()); // TODO the error message should be clarified. It should say something like 'assignment operator does not assign i and therefore the behaviour is different to the default copy constructor' checkCopyCtorAndEqOperator("class A\n" "{\n" " A& operator=(const int &x) { this->x = x; return *this; }\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A {\n" "public:\n" " A() : x(0) { }\n" " A(const A & a) { x = a.x; }\n" " A & operator = (const A & a) {\n" " x = a.x;\n" " return *this;\n" " }\n" "private:\n" " int x;\n" "};\n" "class B : public A {\n" "public:\n" " B() { }\n" " B(const B & b) :A(b) { }\n" "private:\n" " static int i;\n" "};"); ASSERT_EQUALS("", errout.str()); // #7987 - Don't show warning when there is a move constructor checkCopyCtorAndEqOperator("struct S {\n" " std::string test;\n" " S(S&& s) : test(std::move(s.test)) { }\n" " S& operator = (S &&s) {\n" " test = std::move(s.test);\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // #8337 - False positive in copy constructor detection checkCopyCtorAndEqOperator("struct StaticListNode {\n" " StaticListNode(StaticListNode*& prev) : m_next(0) {}\n" " StaticListNode* m_next;\n" "};"); ASSERT_EQUALS("", errout.str()); } #define checkExplicitConstructors(code) checkExplicitConstructors_(code, __FILE__, __LINE__) void checkExplicitConstructors_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); (checkClass.checkExplicitConstructors)(); } void explicitConstructors() { checkExplicitConstructors("class Class {\n" " Class() = delete;\n" " Class(const Class& other) { }\n" " Class(Class&& other) { }\n" " explicit Class(int i) { }\n" " explicit Class(const std::string&) { }\n" " Class(int a, int b) { }\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class() = delete;\n" " explicit Class(const Class& other) { }\n" " explicit Class(Class&& other) { }\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class() = delete;\n" " Class(const Class& other) = delete;\n" " Class(Class&& other) = delete;\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class(int i) { }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (style) Class 'Class' has a constructor with 1 argument that is not explicit.\n", errout.str()); checkExplicitConstructors("class Class {\n" " Class(const Class& other) { }\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class(Class&& other) { }\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); // #6585 checkExplicitConstructors("class Class {\n" " private: Class(const Class&);\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " public: Class(const Class&);\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); // #7465: Error properly reported in templates checkExplicitConstructors("template struct Test {\n" " Test(int) : fData(0) {}\n" " T fData;\n" "};\n" "int main() {\n" " Test test;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Struct 'Test < int >' has a constructor with 1 argument that is not explicit.\n", errout.str()); // #7465: No error for copy or move constructors checkExplicitConstructors("template struct Test {\n" " Test() : fData(0) {}\n" " Test (const Test& aOther) : fData(aOther.fData) {}\n" " Test (Test&& aOther) : fData(std::move(aOther.fData)) {}\n" " T fData;\n" "};\n" "int main() {\n" " Test test;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #8600 checkExplicitConstructors("struct A { struct B; };\n" "struct A::B {\n" " B() = default;\n" " B(const B&) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("struct A{" " A(int, int y=2) {}" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) Struct 'A' has a constructor with 1 argument that is not explicit.\n", errout.str()); checkExplicitConstructors("struct Foo {\n" // #10515 " template \n" " explicit constexpr Foo(T) {}\n" "};\n" "struct Bar {\n" " template \n" " constexpr explicit Bar(T) {}\n" "};\n" "struct Baz {\n" " explicit constexpr Baz(int) {}\n" "};\n"); ASSERT_EQUALS("", errout.str()); } #define checkDuplInheritedMembers(code) checkDuplInheritedMembers_(code, __FILE__, __LINE__) void checkDuplInheritedMembers_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); (checkClass.checkDuplInheritedMembers)(); } void duplInheritedMembers() { checkDuplInheritedMembers("class Base {\n" " int x;\n" "};\n" "struct Derived : Base {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class Base {\n" " protected:\n" " int x;\n" "};\n" "struct Derived : Base {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base'.\n", errout.str()); checkDuplInheritedMembers("class Base {\n" " protected:\n" " int x;\n" "};\n" "struct Derived : public Base {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base'.\n", errout.str()); checkDuplInheritedMembers("class Base0 {\n" " int x;\n" "};\n" "class Base1 {\n" " int x;\n" "};\n" "struct Derived : Base0, Base1 {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class Base0 {\n" " protected:\n" " int x;\n" "};\n" "class Base1 {\n" " int x;\n" "};\n" "struct Derived : Base0, Base1 {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:9]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base0'.\n", errout.str()); checkDuplInheritedMembers("class Base0 {\n" " protected:\n" " int x;\n" "};\n" "class Base1 {\n" " public:\n" " int x;\n" "};\n" "struct Derived : Base0, Base1 {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:10]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base0'.\n" "[test.cpp:7] -> [test.cpp:10]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base1'.\n", errout.str()); checkDuplInheritedMembers("class Base {\n" " int x;\n" "};\n" "struct Derived : Base {\n" " int y;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class A {\n" " int x;\n" "};\n" "struct B {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown 'Base' class checkDuplInheritedMembers("class Derived : public UnknownBase {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class Base {\n" " int x;\n" "};\n" "class Derived : public Base {\n" "};"); ASSERT_EQUALS("", errout.str()); // #6692 checkDuplInheritedMembers("namespace test1 {\n" " struct SWibble{};\n" " typedef SWibble wibble;\n" "}\n" "namespace test2 {\n" " struct SWibble : public test1::wibble {\n" " int Value;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); // #9957 checkDuplInheritedMembers("class Base {\n" " public:\n" " int i;\n" "};\n" "class Derived1: public Base {\n" " public:\n" " int j;\n" "};\n" "class Derived2 : public Derived1 {\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:10]: (warning) The class 'Derived2' defines member variable with name 'i' also defined in its parent class 'Base'.\n", errout.str()); // don't crash on recursive template checkDuplInheritedMembers("template\n" "struct BitInt : public BitInt { };"); ASSERT_EQUALS("", errout.str()); // don't crash on recursive template checkDuplInheritedMembers("namespace _impl {\n" " template \n" " struct fn_traits;\n" "}\n" "template \n" "struct function_traits\n" " : public _impl::fn_traits> {};\n" "namespace _impl {\n" " template \n" " struct fn_traits\n" " : public fn_traits {};\n" "}"); ASSERT_EQUALS("", errout.str()); } #define checkCopyConstructor(code) checkCopyConstructor_(code, __FILE__, __LINE__) void checkCopyConstructor_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.copyconstructors(); } void copyConstructor1() { checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F(const F &f) : p(f.p), c(f.c)\n" " {\n" " p=(char *)malloc(strlen(f.p)+1);\n" " strcpy(p,f.p);\n" " }\n" " F(char *str)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,str);\n" " }\n" " F&operator=(const F&);\n" " ~F();\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:5]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n", "", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F(const F &f) {\n" " p = f.p;\n" " }\n" " F(char *str) {\n" " p = malloc(strlen(str)+1);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" "[test.cpp:3] -> [test.cpp:7]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", "[test.cpp:4]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" , errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F(const F &f) :p(f.p)\n" " {\n" " }\n" " F(char *str)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,str);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:5]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" "[test.cpp:5] -> [test.cpp:10]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", "" , errout.str()); checkCopyConstructor("class kalci\n" "{\n" " public:\n" " char *c,*p,*d;\n" " kalci()\n" " {\n" " p=(char *)malloc(100);\n" " strcpy(p,\"hello\");\n" " c=(char *)malloc(100);\n" " strcpy(p,\"hello\");\n" " d=(char *)malloc(100);\n" " strcpy(p,\"hello\");\n" " }\n" " kalci(const kalci &f)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " c=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " d=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " }\n" " ~kalci();\n" " kalci& operator=(const kalci&kalci);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F(char *str,char *st,char *string)\n" " {\n" " p=(char *)malloc(100);\n" " strcpy(p,str);\n" " c=(char *)malloc(100);\n" " strcpy(p,st);\n" " d=(char *)malloc(100);\n" " strcpy(p,string);\n" " }\n" " F(const F &f)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " c=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:14] -> [test.cpp:11]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", "", errout.str()); checkCopyConstructor("class F {\n" " char *c;\n" " F(char *str,char *st,char *string) {\n" " p=(char *)malloc(100);\n" " }\n" " F(const F &f)\n" " : p(malloc(size))\n" " {\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F {\n" " char *c;\n" " F(char *str,char *st,char *string)\n" " : p(malloc(size))\n" " {\n" " }\n" " F(const F &f)\n" " {\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", "", errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F()\n" " {\n" " p=(char *)malloc(100);\n" " c=(char *)malloc(100);\n" " d=(char*)malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:8]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", "", errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c;\n" " const char *p,*d;\n" " F(char *str,char *st,char *string)\n" " {\n" " p=str;\n" " d=st;\n" " c=(char *)malloc(strlen(string)+1);\n" " strcpy(d,string);\n" " }\n" " F(const F &f)\n" " {\n" " p=f.p;\n" " d=f.d;\n" " c=(char *)malloc(strlen(str)+1);\n" " strcpy(d,f.p);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F : E\n" "{\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class E { E(E&); };\n" // non-copyable "class F : E\n" "{\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class E {};\n" "class F : E {\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " F(F& f);\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F() : p(malloc(100)) {}\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); // #7198 checkCopyConstructor("struct F {\n" " static char* c;\n" " F() {\n" " p = malloc(100);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void copyConstructor2() { // ticket #4458 checkCopyConstructor("template \n" "class Vector\n" "{\n" "public:\n" " Vector() {\n" " _M_finish = new _Tp[ 42 ];\n" " }\n" " Vector( const Vector<_Tp>& v ) {\n" " }\n" " ~Vector();\n" " Vector& operator=(const Vector&v);\n" " _Tp* _M_finish;\n" "};"); ASSERT_EQUALS("", errout.str()); } void copyConstructor3() { checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f) = delete;\n" " F&operator=(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f) = default;\n" " F&operator=(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The copy constructor is explicitly defaulted but the default copy constructor does not work well. It is recommended to define or delete the copy constructor.\n", errout.str()); } void copyConstructor4() { checkCopyConstructor("class noncopyable {\n" "protected:\n" " noncopyable() {}\n" " ~noncopyable() {}\n" "\n" "private:\n" " noncopyable( const noncopyable& );\n" " const noncopyable& operator=( const noncopyable& );\n" "};\n" "\n" "class Base : private noncopyable {};\n" "\n" "class Foo : public Base {\n" "public:\n" " Foo() : m_ptr(new int) {}\n" " ~Foo() { delete m_ptr; }\n" "private:\n" " int* m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); } void copyConstructor5() { checkCopyConstructor("class Copyable {};\n" "\n" "class Foo : public Copyable, public UnknownType {\n" "public:\n" " Foo() : m_ptr(new int) {}\n" " ~Foo() { delete m_ptr; }\n" "private:\n" " int* m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class Copyable {};\n" "\n" "class Foo : public UnknownType, public Copyable {\n" "public:\n" " Foo() : m_ptr(new int) {}\n" " ~Foo() { delete m_ptr; }\n" "private:\n" " int* m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noOperatorEq() { checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a operator= which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); // defaulted operator= checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f) = default;\n" " ~F();\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The operator= is explicitly defaulted but the default operator= does not work well. It is recommended to define or delete the operator=.\n", errout.str()); // deleted operator= checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f) = delete;\n" " ~F();\n" "};"); ASSERT_EQUALS("", errout.str()); // base class deletes operator= checkCopyConstructor("struct F : NonCopyable {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("", errout.str()); } void noDestructor() { checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F&operator=(const F&);" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); checkCopyConstructor("struct F {\n" " C* c;\n" " F() { c = new C; }\n" " F(const F &f);\n" " F&operator=(const F&);" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("struct F {\n" " int* i;\n" " F() { i = new int(); }\n" " F(const F &f);\n" " F& operator=(const F&);" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); checkCopyConstructor("struct Data { int x; int y; };\n" "struct F {\n" " Data* c;\n" " F() { c = new Data; }\n" " F(const F &f);\n" " F&operator=(const F&);" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); // defaulted destructor checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f);\n" " ~F() = default;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The destructor is explicitly defaulted but the default destructor does not work well. It is recommended to define the destructor.\n", errout.str()); // deleted destructor checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f);\n" " ~F() = delete;\n" "};"); ASSERT_EQUALS("", errout.str()); } // Check that operator Equal returns reference to this #define checkOpertorEqRetRefThis(code) checkOpertorEqRetRefThis_(code, __FILE__, __LINE__) void checkOpertorEqRetRefThis_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.operatorEqRetRefThis(); } void operatorEqRetRefThis1() { checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return a; }"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a) { return a; }"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { return b; }\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return *this; }"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return b; }"); ASSERT_EQUALS("[test.cpp:10]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " B & operator=(const B & b) { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " B & operator=(const B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " A::B & operator=(const A::B & b) { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " A::B & operator=(const A::B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " B & operator=(const B & b) { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " B & operator=(const B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " A::B & operator=(const A::B & b) { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " A::B & operator=(const A::B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); } void operatorEqRetRefThis2() { // ticket # 1323 checkOpertorEqRetRefThis( "class szp\n" "{\n" " szp &operator =(int *other) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class szp\n" "{\n" " szp &operator =(int *other);\n" "};\n" "szp &szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " szp &operator =(int *other) {}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " szp &operator =(int *other);\n" "};\n" "NS::szp &NS::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " NS::szp &operator =(int *other) {}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " NS::szp &operator =(int *other);\n" "};\n" "NS::szp &NS::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " szp &operator =(int *other) {}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " szp &operator =(int *other);\n" "};\n" "A::szp &A::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " A::szp &operator =(int *other) {}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " A::szp &operator =(int *other);\n" "};\n" "A::szp &A::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); } void operatorEqRetRefThis3() { // ticket # 1405 checkOpertorEqRetRefThis( "class A {\n" "public:\n" " inline A &operator =(int *other) { return (*this); };\n" " inline A &operator =(long *other) { return (*this = 0); };\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A &operator =(int *other);\n" " A &operator =(long *other);\n" "};\n" "A &A::operator =(int *other) { return (*this); };\n" "A &A::operator =(long *other) { return (*this = 0); };"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " inline A &operator =(int *other) { return (*this); };\n" " inline A &operator =(long *other) { return operator = (*(int *)other); };\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A &operator =(int *other);\n" " A &operator =(long *other);\n" "};\n" "A &A::operator =(int *other) { return (*this); };\n" "A &A::operator =(long *other) { return operator = (*(int *)other); };"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A &operator =(int *other);\n" " A &operator =(long *other);\n" "};\n" "A &A::operator =(int *other) { return (*this); };\n" "A &A::operator =(long *other) { return this->operator = (*(int *)other); };"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( // #9045 "class V {\n" "public:\n" " V& operator=(const V& r) {\n" " if (this == &r) {\n" " return ( *this );\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEqRetRefThis4() { // ticket # 1451 checkOpertorEqRetRefThis( "P& P::operator = (const P& pc)\n" "{\n" " return (P&)(*this += pc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqRetRefThis5() { // ticket # 1550 checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a) { }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "protected:\n" " A & operator=(const A &a) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "private:\n" " A & operator=(const A &a) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a) {\n" " rand();\n" " throw std::exception();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a) {\n" " rand();\n" " abort();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a);\n" "};\n" "A & A :: operator=(const A &a) { }"); ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); } void operatorEqRetRefThis6() { // ticket #2478 (segmentation fault) checkOpertorEqRetRefThis( "class UString {\n" "public:\n" " UString& assign( const char* c_str );\n" " UString& operator=( const UString& s );\n" "};\n" "UString& UString::assign( const char* c_str ) {\n" " std::string tmp( c_str );\n" " return assign( tmp );\n" "}\n" "UString& UString::operator=( const UString& s ) {\n" " return assign( s );\n" "}"); } void operatorEqRetRefThis7() { // ticket #5782 Endless recursion in CheckClass::checkReturnPtrThis() checkOpertorEqRetRefThis( "class basic_fbstring {\n" " basic_fbstring& operator=(int il) {\n" " return assign();\n" " }\n" " basic_fbstring& assign() {\n" " return replace();\n" " }\n" " basic_fbstring& replaceImplDiscr() {\n" " return replace();\n" " }\n" " basic_fbstring& replace() {\n" " return replaceImplDiscr();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } // Check that operator Equal checks for assignment to self #define checkOpertorEqToSelf(code) checkOpertorEqToSelf_(code, __FILE__, __LINE__) void checkOpertorEqToSelf_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); checkClass.operatorEqToSelf(); } void operatorEqToSelf1() { // this test has an assignment test but it is not needed checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { if (&a != this) { } return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but it is not needed checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test and has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if (&a != this)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // this class needs an assignment test but doesn't have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test has an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { if (&a != this) { } return *this; }"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test and has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if (&a != this)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test and has the inverse test checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if (&a == this)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test needs an assignment test and has the inverse test checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if ((&a == this) == true)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test needs an assignment test and has the inverse test checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if ((&a == this) != false)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test needs an assignment test and has the inverse test checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if (!((&a == this) == false))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test needs an assignment test and has the inverse test checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if ((&a != this) == false)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test needs an assignment test and has the inverse test checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if (&a != this)\n" " {\n" " }\n" " else\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test needs an assignment test and has the inverse test checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if (&a != this)\n" " free(s);\n" " else\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test needs an assignment test but doesn’t have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // ticket #1224 checkOpertorEqToSelf( "const SubTree &SubTree::operator= (const SubTree &b)\n" "{\n" " CodeTree *oldtree = tree;\n" " tree = new CodeTree(*b.tree);\n" " delete oldtree;\n" " return *this;\n" "}\n" "const SubTree &SubTree::operator= (const CodeTree &b)\n" "{\n" " CodeTree *oldtree = tree;\n" " tree = new CodeTree(b);\n" " delete oldtree;\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf2() { // this test has an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { if (&b != this) { } return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test but has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char *s;\n" " B & operator=(const B &b)\n" " {\n" " if (&b != this)\n" " {\n" " }\n" " return *this;\n" " }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test but doesn't have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char *s;\n" " B & operator=(const B &b)\n" " {\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" " }\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test has an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { if (&b != this) { } return *this; }"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test and has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b)\n" "{\n" " if (&b != this)\n" " {\n" " free(s);\n" " s = strdup(b.s);\n" " }\n" " return *this;\n" " }"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test but doesn't have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b)\n" "{\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" " }"); ASSERT_EQUALS("[test.cpp:11]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); } void operatorEqToSelf3() { // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " A & operator=(const A &a) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf4() { // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " B & operator=(const B &b) { return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &b)\n" " {\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" " }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b)\n" "{\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf5() { // ticket # 1233 checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if((&a!=this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if((this!=&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(!(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(!(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(false==(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(false==(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(true!=(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(true!=(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if((&a!=this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if((this!=&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(!(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(!(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(false==(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(false==(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(true!=(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(true!=(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "struct A {\n" " char *s;\n" " A& operator=(const B &b);\n" "};\n" "A& A::operator=(const B &b) {\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf6() { // ticket # 1550 checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a)\n" " {\n" " delete [] data;\n" " data = new char[strlen(a.data) + 1];\n" " strcpy(data, a.data);\n" " return *this;\n" " }\n" "private:\n" " char * data;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "private:\n" " char * data;\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " delete [] data;\n" " data = new char[strlen(a.data) + 1];\n" " strcpy(data, a.data);\n" " return *this;\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a)\n" " {\n" " delete data;\n" " data = new char;\n" " *data = *a.data;\n" " return *this;\n" " }\n" "private:\n" " char * data;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "private:\n" " char * data;\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " delete data;\n" " data = new char;\n" " *data = *a.data;\n" " return *this;\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); } void operatorEqToSelf7() { checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & assign(const A & a)\n" " {\n" " return *this;\n" " }\n" " A & operator=(const A &a)\n" " {\n" " return assign(a);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf8() { checkOpertorEqToSelf( "class FMat\n" "{\n" "public:\n" " FMat& copy(const FMat& rhs);\n" " FMat& operator=(const FMat& in);\n" "};\n" "FMat& FMat::copy(const FMat& rhs)\n" "{\n" " return *this;\n" "}\n" "FMat& FMat::operator=(const FMat& in)\n" "{\n" " return copy(in);\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf9() { checkOpertorEqToSelf( "class Foo\n" "{\n" "public:\n" " Foo& operator=(Foo* pOther);\n" " Foo& operator=(Foo& other);\n" "};\n" "Foo& Foo::operator=(Foo* pOther)\n" "{\n" " return *this;\n" "}\n" "Foo& Foo::operator=(Foo& other)\n" "{\n" " return Foo::operator=(&other);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Check that base classes have virtual destructors #define checkVirtualDestructor(...) checkVirtualDestructor_(__FILE__, __LINE__, __VA_ARGS__) void checkVirtualDestructor_(const char* file, int line, const char code[], bool inconclusive = false) { // Clear the error log errout.str(""); settings0.certainty.setEnabled(Certainty::inconclusive, inconclusive); settings0.severity.enable(Severity::warning); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.virtualDestructor(); } void virtualDestructor1() { // Base class not found checkVirtualDestructor("class Derived : public Base { };\n" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); checkVirtualDestructor("class Derived : Base { };\n" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); } void virtualDestructor2() { // Base class doesn't have a destructor checkVirtualDestructor("class Base { };\n" "class Derived : public Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { };\n" "class Derived : protected Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { };\n" "class Derived : private Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); checkVirtualDestructor("class Base { };\n" "class Derived : Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); } void virtualDestructor3() { // Base class has a destructor, but it's not virtual checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : protected Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : private Fred, public Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); } void virtualDestructor4() { // Derived class doesn't have a destructor => undefined behaviour according to paragraph 3 in [expr.delete] checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : private Fred, public Base { };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); } void virtualDestructor5() { // Derived class has empty destructor => undefined behaviour according to paragraph 3 in [expr.delete] checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived() {} };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived(); }; Derived::~Derived() {}" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); } void virtualDestructor6() { // Only report error if base class pointer is deleted that // points at derived class checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived() { (void)11; } };"); ASSERT_EQUALS("", errout.str()); } void virtualDestructorProtected() { // Base class has protected destructor, it makes Base *p = new Derived(); fail // during compilation time, so error is not possible. => no error checkVirtualDestructor("class A\n" "{\n" "protected:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void virtualDestructorInherited() { // class A inherits virtual destructor from class Base -> no error checkVirtualDestructor("class Base\n" "{\n" "public:\n" "virtual ~Base() {}\n" "};\n" "class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // class A inherits virtual destructor from struct Base -> no error // also notice that public is not given, but destructor is public, because // we are using struct instead of class checkVirtualDestructor("struct Base\n" "{\n" "virtual ~Base() {}\n" "};\n" "class A : public Base\n" "{\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown Base class -> it could have virtual destructor, so ignore checkVirtualDestructor("class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // Virtual destructor is inherited -> no error checkVirtualDestructor("class Base2\n" "{\n" "virtual ~Base2() {}\n" "};\n" "class Base : public Base2\n" "{\n" "};\n" "class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // class A doesn't inherit virtual destructor from class Base -> error checkVirtualDestructor("class Base\n" "{\n" "public:\n" " ~Base() {}\n" "};\n" "class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Class 'Base' which is inherited by class 'B' does not have a virtual destructor.\n", "", errout.str()); } void virtualDestructorTemplate() { checkVirtualDestructor("template class A\n" "{\n" " public:\n" " virtual ~A(){}\n" "};\n" "template class AA\n" "{\n" " public:\n" " ~AA(){}\n" "};\n" "class B : public A, public AA\n" "{\n" " public:\n" " ~B(){int a;}\n" "};\n" "\n" "AA *p = new B; delete p;"); ASSERT_EQUALS("[test.cpp:9]: (error) Class 'AA < double >' which is inherited by class 'B' does not have a virtual destructor.\n", errout.str()); } void virtualDestructorInconclusive() { checkVirtualDestructor("class Base {\n" "public:\n" " ~Base(){}\n" " virtual void foo(){}\n" "};\n", true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Class 'Base' which has virtual members does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base {\n" "public:\n" " ~Base(){}\n" " virtual void foo(){}\n" "};\n" "class Derived : public Base {\n" "public:\n" " ~Derived() { bar(); }\n" "};\n" "void foo() {\n" " Base * base = new Derived();\n" " delete base;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); // class Base destructor is not virtual but protected -> no error checkVirtualDestructor("class Base {\n" "public:\n" " virtual void foo(){}\n" "protected:\n" " ~Base(){}\n" "};\n", true); ASSERT_EQUALS("", errout.str()); checkVirtualDestructor("class C {\n" "private:\n" " C();\n" " virtual ~C();\n" "};\n", true); ASSERT_EQUALS("", errout.str()); } #define checkNoMemset(...) checkNoMemset_(__FILE__, __LINE__, __VA_ARGS__) void checkNoMemset_(const char* file, int line, const char code[]) { Settings settings; settings.severity.enable(Severity::warning); settings.severity.enable(Severity::portability); checkNoMemset_(file, line, code, settings); } void checkNoMemset_(const char* file, int line, const char code[], const Settings &settings) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.checkMemset(); } void memsetOnClass() { checkNoMemset("class Fred\n" "{\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " static std::string b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string * b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " mutable std::string b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred {\n" " std::string b;\n" " void f();\n" "};\n" "void Fred::f() {\n" " memset(this, 0, sizeof(*this));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string s;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string s;\n" "};\n" "class Pebbles: public Fred {};\n" "void f()\n" "{\n" " Pebbles pebbles;\n" " memset(&pebbles, 0, sizeof(pebbles));\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " virtual ~Fred();\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a virtual function.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " virtual ~Fred();\n" "};\n" "void f()\n" "{\n" " static Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a virtual function.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" "};\n" "class Wilma\n" "{\n" " virtual ~Wilma();\n" "};\n" "class Pebbles: public Fred, Wilma {};\n" "void f()\n" "{\n" " Pebbles pebbles;\n" " memset(&pebbles, 0, sizeof(pebbles));\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (error) Using 'memset' on class that contains a virtual function.\n", errout.str()); // Fred not defined in scope checkNoMemset("namespace n1 {\n" " class Fred\n" " {\n" " std::string b;\n" " };\n" "}\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); // Fred with namespace qualifier checkNoMemset("namespace n1 {\n" " class Fred\n" " {\n" " std::string b;\n" " };\n" "}\n" "void f()\n" "{\n" " n1::Fred fred;\n" " memset(&fred, 0, sizeof(n1::Fred));\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); // Fred with namespace qualifier checkNoMemset("namespace n1 {\n" " class Fred\n" " {\n" " std::string b;\n" " };\n" "}\n" "void f()\n" "{\n" " n1::Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class A {\n" " virtual ~A() { }\n" " std::string s;\n" "};\n" "int f() {\n" " const int N = 10;\n" " A** arr = new A*[N];\n" " memset(arr, 0, N * sizeof(A*));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class A {\n" // #5116 - nested class data is mixed in the SymbolDatabase " std::string s;\n" " struct B { int x; };\n" "};\n" "void f(A::B *b) {\n" " memset(b,0,4);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4461 Warn about memset/memcpy on class with references as members checkNoMemset("class A {\n" " std::string &s;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(a));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a reference.\n", errout.str()); checkNoMemset("class A {\n" " const B&b;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(a));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a reference.\n", errout.str()); // #7456 checkNoMemset("struct A {\n" " A() {}\n" " virtual ~A() {}\n" "};\n" "struct B {\n" " A* arr[4];\n" "};\n" "void func() {\n" " B b[4];\n" " memset(b, 0, sizeof(b));\n" "}"); ASSERT_EQUALS("", errout.str()); } void memsetOnInvalid() { // Ticket #5425 checkNoMemset("union ASFStreamHeader {\n" " struct AVMPACKED {\n" " union {\n" " struct AVMPACKED {\n" " int width;\n" " } vid;\n" " };\n" " } hdr;\n" "};" "void parseHeader() {\n" " ASFStreamHeader strhdr;\n" " memset(&strhdr, 0, sizeof(strhdr));\n" "}"); } void memsetOnStruct() { checkNoMemset("struct A\n" "{\n" "};\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A\n" "{\n" "};\n" "void f()\n" "{\n" " struct A a;\n" " memset(&a, 0, sizeof(struct A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A\n" "{\n" "};\n" "void f()\n" "{\n" " struct A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("void f()\n" "{\n" " struct sockaddr_in6 fail;\n" " memset(&fail, 0, sizeof(struct sockaddr_in6));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A\n" "{\n" " void g( struct sockaddr_in6& a);\n" "private:\n" " std::string b;\n" "};\n" "void f()\n" "{\n" " struct A fail;\n" " memset(&fail, 0, sizeof(struct A));\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); checkNoMemset("struct Fred\n" "{\n" " std::string s;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); checkNoMemset("struct Stringy {\n" " std::string inner;\n" "};\n" "struct Foo {\n" " Stringy s;\n" "};\n" "int main() {\n" " Foo foo;\n" " memset(&foo, 0, sizeof(Foo));\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); } void memsetVector() { checkNoMemset("class A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on class that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(struct A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(a));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("class A\n" "{ std::vector< std::vector > ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on class that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector< std::vector > ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector< std::vector > ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(a));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A {\n" " std::vector buf;\n" " operator int*() {return &buf[0];}\n" "};\n" "void f() {\n" " A a;\n" " memset(a, 0, 100);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4460 checkNoMemset("struct C {\n" " std::string s;\n" "};\n" "int foo() {\n" " C* c1[10][10];\n" " C* c2[10];\n" " C c3[10][10];\n" " C** c4 = new C*[10];\n" " memset(**c1, 0, 10);\n" " memset(*c1, 0, 10);\n" " memset(*c2, 0, 10);\n" " memset(*c3, 0, 10);\n" " memset(*c4, 0, 10);\n" " memset(c2, 0, 10);\n" " memset(c3, 0, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on struct that contains a 'std::string'.\n" "[test.cpp:11]: (error) Using 'memset' on struct that contains a 'std::string'.\n" "[test.cpp:12]: (error) Using 'memset' on struct that contains a 'std::string'.\n" "[test.cpp:13]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); // Ticket #6953 checkNoMemset("typedef float realnum;\n" "struct multilevel_data {\n" " realnum *GammaInv;\n" " realnum data[1];\n" "};\n" "void *new_internal_data() const {\n" " multilevel_data *d = (multilevel_data *) malloc(sizeof(multilevel_data));\n" " memset(d, 0, sizeof(multilevel_data));\n" " return (void*) d;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (portability) Using memset() on struct which contains a floating point number.\n", errout.str()); } void memsetOnStdPodType() { // Ticket #5901 Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); checkNoMemset("class A {\n" " std::array ints;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); // std::array is POD (#5481) checkNoMemset("struct st {\n" " std::uint8_t a;\n" " std::atomic_bool b;\n" "};\n" "\n" "void f() {\n" " st s;\n" " std::memset(&s, 0, sizeof(st));\n" "}", settings); ASSERT_EQUALS("", errout.str()); } void memsetOnFloat() { checkNoMemset("struct A {\n" " float f;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (portability) Using memset() on struct which contains a floating point number.\n", errout.str()); checkNoMemset("struct A {\n" " float f[4];\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (portability) Using memset() on struct which contains a floating point number.\n", errout.str()); checkNoMemset("struct A {\n" " float f[4];\n" "};\n" "void f(const A& b) {\n" " A a;\n" " memcpy(&a, &b, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A {\n" " float* f;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); } void memsetOnUnknown() { checkNoMemset("void clang_tokenize(CXToken **Tokens) {\n" " *Tokens = (CXToken *)malloc(sizeof(CXToken) * CXTokens.size());\n" " memmove(*Tokens, CXTokens.data(), sizeof(CXToken) * CXTokens.size());\n" "}"); ASSERT_EQUALS("", errout.str()); } void mallocOnClass() { checkNoMemset("class C { C() {} };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with malloc(), but class provides constructors.\n", errout.str()); checkNoMemset("class C { C(int z, Foo bar) { bar(); } };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with malloc(), but class provides constructors.\n", errout.str()); checkNoMemset("struct C { C() {} };\n" "void foo(C*& p) {\n" " p = realloc(p, sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with realloc(), but class provides constructors.\n", errout.str()); checkNoMemset("struct C { virtual void bar(); };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (error) Memory for class instance allocated with malloc(), but class contains a virtual function.\n", errout.str()); checkNoMemset("struct C { std::string s; };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (error) Memory for class instance allocated with malloc(), but class contains a 'std::string'.\n", errout.str()); checkNoMemset("class C { };\n" // C-Style class/struct "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct C { C() {} };\n" "void foo(C*& p) {\n" " p = new C();\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class C { C() {} };\n" "void foo(D*& p) {\n" // Unknown type " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("", errout.str()); } #define checkThisSubtraction(code) checkThisSubtraction_(code, __FILE__, __LINE__) void checkThisSubtraction_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); checkClass.thisSubtraction(); } void this_subtraction() { checkThisSubtraction("; this-x ;"); ASSERT_EQUALS("[test.cpp:1]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout.str()); checkThisSubtraction("; *this = *this-x ;"); ASSERT_EQUALS("", errout.str()); checkThisSubtraction("; *this = *this-x ;\n" "this-x ;"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout.str()); checkThisSubtraction("; *this = *this-x ;\n" "this-x ;\n" "this-x ;"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n" "[test.cpp:3]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout.str()); } #define checkConst(...) checkConst_(__FILE__, __LINE__, __VA_ARGS__) void checkConst_(const char* file, int line, const char code[], Settings *s = nullptr, bool inconclusive = true) { // Clear the error log errout.str(""); // Check.. if (!s) s = &settings0; s->certainty.setEnabled(Certainty::inconclusive, inconclusive); // Tokenize.. Tokenizer tokenizer(s, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckClass checkClass(&tokenizer, s, this); (checkClass.checkConst)(); } void const1() { checkConst("class Fred {\n" " int a;\n" " int getA() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " const std::string foo() { return \"\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Fred {\n" " std::string s;\n" " const std::string & foo() { return \"\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // constructors can't be const.. checkConst("class Fred {\n" " int a;\n" "public:\n" " Fred() { }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment through |=.. checkConst("class Fred {\n" " int a;\n" " int setA() { a |= true; }\n" "};"); ASSERT_EQUALS("", errout.str()); // functions with a call to a member function can only be const, if that member function is const, too.. (#1305) checkConst("class foo {\n" "public:\n" " int x;\n" " void a() { x = 1; }\n" " void b() { a(); }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" "public:\n" " int x;\n" " int a() const { return x; }\n" " void b() { a(); }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::b' can be const.\n", errout.str()); checkConst("class Fred {\n" "public:\n" " int x;\n" " void b() { a(); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::b' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // static functions can't be const.. checkConst("class foo\n" "{\n" "public:\n" " static unsigned get()\n" " { return 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " const std::string foo() const throw() { return \"\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const2() { // ticket 1344 // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo() { s = \"\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to function argument reference can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a) { a = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a) { s = a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to function argument references can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { a = s; b = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { s = a; s = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { s = a; b = a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { a = s; s = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const3() { // assignment to function argument pointer can be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a) { *a = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a) { s = *a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to function argument pointers can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { *a = s; *b = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { s = *a; s = *b; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { s = *a; *b = s; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { *a = s; s = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const4() { checkConst("class Fred {\n" " int a;\n" " int getA();\n" "};\n" "int Fred::getA() { return a; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " std::string s;\n" " const std::string & foo();\n" "};\n" "const std::string & Fred::foo() { return \"\"; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // functions with a function call to a non-const member can't be const.. (#1305) checkConst("class Fred\n" "{\n" "public:\n" " int x;\n" " void a() { x = 1; }\n" " void b();\n" "};\n" "void Fred::b() { a(); }"); ASSERT_EQUALS("", errout.str()); // static functions can't be const.. checkConst("class Fred\n" "{\n" "public:\n" " static unsigned get();\n" "};\n" "static unsigned Fred::get() { return 0; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo();\n" "};\n" "void Fred::foo() { s = \"\"; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument reference can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a);\n" "};\n" "void Fred::foo(std::string & a) { a = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a);\n" "};\n" "void Fred::foo(std::string & a) { s = a; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument references can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { a = s; b = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { s = a; s = b; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { s = a; b = a; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { a = s; s = b; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument pointer can be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a);\n" "};\n" "void Fred::foo(int * a) { *a = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a);\n" "};\n" "void Fred::foo(int * a) { s = *a; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument pointers can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { *a = s; *b = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { s = *a; s = *b; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { s = *a; *b = s; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { *a = s; s = b; }"); ASSERT_EQUALS("", errout.str()); // check functions with same name checkConst("class Fred {\n" " std::string s;\n" " void foo();\n" " void foo(std::string & a);\n" " void foo(const std::string & a);\n" "};\n" "void Fred::foo() { }" "void Fred::foo(std::string & a) { a = s; }" "void Fred::foo(const std::string & a) { s = a; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:7] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // check functions with different or missing parameter names checkConst("class Fred {\n" " std::string s;\n" " void foo1(int, int);\n" " void foo2(int a, int b);\n" " void foo3(int, int b);\n" " void foo4(int a, int);\n" " void foo5(int a, int b);\n" "};\n" "void Fred::foo1(int a, int b) { }\n" "void Fred::foo2(int c, int d) { }\n" "void Fred::foo3(int a, int b) { }\n" "void Fred::foo4(int a, int b) { }\n" "void Fred::foo5(int, int) { }"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo1' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:10] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo2' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:11] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::foo3' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:12] -> [test.cpp:6]: (performance, inconclusive) Technically the member function 'Fred::foo4' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:13] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::foo5' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // check nested classes checkConst("class Fred {\n" " class A {\n" " int a;\n" " int getA() { return a; }\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " int A::getA() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" "};\n" "int Fred::A::getA() { return a; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout.str()); // check deeply nested classes checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB() { return b; }\n" " class A {\n" " int a;\n" " int getA() { return a; }\n" " };\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n" , errout.str()); checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB();\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " int A::getA() { return a; }\n" " };\n" " int B::getB() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:9] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB();\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " };\n" " int B::A::getA() { return a; }\n" " int B::getB() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:10] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB();\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " };\n" "};\n" "int Fred::B::A::getA() { return a; }\n" "int Fred::B::getB() { return b; }"); ASSERT_EQUALS("[test.cpp:12] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:11] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout.str()); } // operator< can often be const void constoperator1() { checkConst("struct Fred {\n" " int a;\n" " bool operator<(const Fred &f) { return a < f.a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::operator<' can be const.\n", errout.str()); } // operator<< void constoperator2() { checkConst("struct Foo {\n" " void operator<<(int);\n" "};\n" "struct Fred {\n" " Foo foo;\n" " void x()\n" " {\n" " foo << 123;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Foo {\n" " void operator<<(int);\n" "};\n" "struct Fred {\n" " Foo foo;\n" " void x()\n" " {\n" " std::cout << foo << 123;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'Fred::x' can be const.\n", errout.str()); } void constoperator3() { checkConst("struct Fred {\n" " int array[10];\n" " int const & operator [] (unsigned int index) const { return array[index]; }\n" " int & operator [] (unsigned int index) { return array[index]; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Fred {\n" " int array[10];\n" " int const & operator [] (unsigned int index) { return array[index]; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::operator[]' can be const.\n", errout.str()); } void constoperator4() { // #7953 checkConst("class A {\n" " int c;\n" "public:\n" " operator int*() { return &c; };\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" " int c;\n" "public:\n" " operator const int*() { return &c; };\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorconstint*' can be const.\n", errout.str()); // #2375 checkConst("struct Fred {\n" " int array[10];\n" " typedef int* (Fred::*UnspecifiedBoolType);\n" " operator UnspecifiedBoolType() { };\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::operatorint**' can be const.\n", "", errout.str()); checkConst("struct Fred {\n" " int array[10];\n" " typedef int* (Fred::*UnspecifiedBoolType);\n" " operator UnspecifiedBoolType() { array[0] = 0; };\n" "};"); ASSERT_EQUALS("", errout.str()); } void constoperator5() { // ticket #3252 checkConst("class A {\n" " int c;\n" "public:\n" " operator int& () {return c}\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" " int c;\n" "public:\n" " operator const int& () {return c}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorconstint&' can be const.\n", errout.str()); checkConst("class A {\n" " int c;\n" "public:\n" " operator int () {return c}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorint' can be const.\n", errout.str()); } void constoperator6() { // ticket #8669 checkConst("class A {\n" " int c;\n" " void f() { os >> *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const5() { // ticket #1482 checkConst("class A {\n" " int a;\n" " bool foo(int i)\n" " {\n" " bool same;\n" " same = (i == a);\n" " return same;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout.str()); } void const6() { // ticket #1491 checkConst("class foo {\n" "public:\n" "};\n" "void bar() {}"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred\n" "{\n" "public:\n" " void foo() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct fast_string\n" "{\n" " union\n" " {\n" " char buff[100];\n" " };\n" " void set_type(char t);\n" "};\n" "inline void fast_string::set_type(char t)\n" "{\n" " buff[10] = t;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const7() { checkConst("class foo {\n" " int a;\n" "public:\n" " void set(int i) { a = i; }\n" " void set(const foo & f) { *this = f; }\n" "};\n" "void bar() {}"); ASSERT_EQUALS("", errout.str()); } void const8() { // ticket #1517 checkConst("class A {\n" "public:\n" " A():m_strValue(\"\"){}\n" " std::string strGetString() { return m_strValue; }\n" "private:\n" " std::string m_strValue;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetString' can be const.\n", errout.str()); } void const9() { // ticket #1515 checkConst("class wxThreadInternal {\n" "public:\n" " void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; }\n" "private:\n" " wxThread::ExitCode m_exitcode;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const10() { // ticket #1522 checkConst("class A {\n" "public:\n" " int foo() { return x = 0; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " int foo() { return x ? x : x = 0; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " int foo() { return x ? x = 0 : x; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const11() { // ticket #1529 checkConst("class A {\n" "public:\n" " void set(struct tm time) { m_time = time; }\n" "private:\n" " struct tm m_time;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const12() { // ticket #1525 checkConst("class A {\n" "public:\n" " int foo() { x = 0; }\n" "private:\n" " mutable int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout.str()); } void const13() { // ticket #1519 checkConst("class A {\n" "public:\n" " A(){}\n" " std::vector GetVec() {return m_vec;}\n" " std::pair GetPair() {return m_pair;}\n" "private:\n" " std::vector m_vec;\n" " std::pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n" "[test.cpp:5]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::vector & GetVec() {return m_vec;}\n" " const std::pair & GetPair() {return m_pair;}\n" "private:\n" " std::vector m_vec;\n" " std::pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n" "[test.cpp:5]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); } void const14() { // extends ticket 1519 checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair,double> GetPair() {return m_pair;}\n" "private:\n" " std::pair,double> m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair,double>& GetPair() {return m_pair;}\n" "private:\n" " std::pair,double> m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair,double>& GetPair() {return m_pair;}\n" "private:\n" " std::pair,double> m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair GetPair() {return m_pair;}\n" "private:\n" " pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " const pair & GetPair() {return m_pair;}\n" "private:\n" " pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair & GetPair() {return m_pair;}\n" "private:\n" " pair m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int,std::vector > GetPair() {return m_pair;}\n" "private:\n" " std::pair< int,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< int,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair< vector, int > GetPair() {return m_pair;}\n" "private:\n" " pair< vector, int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " const pair< vector, int >& GetPair() {return m_pair;}\n" "private:\n" " pair< vector, int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair< vector, int >& GetPair() {return m_pair;}\n" "private:\n" " pair< vector, int > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::vector,std::vector > GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::vector,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< std::vector,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::vector,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::vector,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::vector,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::pair < int, char > , int > GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::pair < int, char > , int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< std::pair < int, char > , int > & GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::pair < int, char > , int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::pair < int, char > , int > & GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::pair < int, char > , int > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int , std::pair < int, char > > GetPair() {return m_pair;}\n" "private:\n" " std::pair< int , std::pair < int, char > > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< int , std::pair < int, char > >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int , std::pair < int, char > > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int , std::pair < int, char > >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int , std::pair < int, char > > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " vector GetVec() {return m_Vec;}\n" "private:\n" " vector m_Vec;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " const vector& GetVec() {return m_Vec;}\n" "private:\n" " vector m_Vec;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " vector& GetVec() {return m_Vec;}\n" "private:\n" " vector m_Vec;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " int * * foo() { return &x; }\n" "private:\n" " const int * x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " const int ** foo() { return &x; }\n" "private:\n" " const int * x;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout.str()); } void const15() { checkConst("class Fred {\n" " unsigned long long int a;\n" " unsigned long long int getA() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout.str()); // constructors can't be const.. checkConst("class Fred {\n" " unsigned long long int a;\n" "public:\n" " Fred() { }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment through |=.. checkConst("class Fred {\n" " unsigned long long int a;\n" " unsigned long long int setA() { a |= true; }\n" "};"); ASSERT_EQUALS("", errout.str()); // static functions can't be const.. checkConst("class foo\n" "{\n" "public:\n" " static unsigned long long int get()\n" " { return 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const16() { // ticket #1551 checkConst("class Fred {\n" " int a;\n" " void set(int i) { Fred::a = i; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const17() { // ticket #1552 checkConst("class Fred {\n" "public:\n" " void set(int i, int j) { a[i].k = i; }\n" "private:\n" " struct { int k; } a[4];\n" "};"); ASSERT_EQUALS("", errout.str()); } void const18() { checkConst("class Fred {\n" "static int x;\n" "public:\n" " void set(int i) { x = i; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::set' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const19() { // ticket #1612 checkConst("using namespace std;\n" "class Fred {\n" "private:\n" " std::string s;\n" "public:\n" " void set(std::string ss) { s = ss; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const20() { // ticket #1602 checkConst("class Fred {\n" " int x : 3;\n" "public:\n" " void set(int i) { x = i; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " list x;\n" "public:\n" " list get() { return x; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " list x;\n" "public:\n" " list get() { return x; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout.str()); checkConst("class Fred {\n" " std::list x;\n" "public:\n" " std::list get() { return x; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " std::list x;\n" "public:\n" " std::list get() { return x; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout.str()); } void const21() { // ticket #1683 checkConst("class A\n" "{\n" "private:\n" " const char * l1[10];\n" "public:\n" " A()\n" " {\n" " for (int i = 0 ; i < 10; l1[i] = NULL, i++);\n" " }\n" " void f1() { l1[0] = \"Hello\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const22() { checkConst("class A\n" "{\n" "private:\n" " B::C * v1;\n" "public:\n" " void f1() { v1 = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A\n" "{\n" "private:\n" " B::C * v1[0];\n" "public:\n" " void f1() { v1[0] = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const23() { checkConst("class Class {\n" "public:\n" " typedef Template Type;\n" " typedef Template2 Type2;\n" " void set_member(Type2 m) { _m = m; }\n" "private:\n" " Type2 _m;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const24() { checkConst("class Class {\n" "public:\n" "void Settings::SetSetting(QString strSetting, QString strNewVal)\n" "{\n" " (*m_pSettings)[strSetting] = strNewVal;\n" "}\n" "private:\n" " std::map *m_pSettings;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const25() { // ticket #1724 checkConst("class A{\n" "public:\n" "A(){m_strVal=\"\";}\n" "std::string strGetString() const\n" "{return m_strVal.c_str();}\n" "const std::string strGetString1() const\n" "{return m_strVal.c_str();}\n" "private:\n" "std::string m_strVal;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVal=\"\";}\n" "std::string strGetString()\n" "{return m_strVal.c_str();}\n" "private:\n" "std::string m_strVal;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetString' can be const.\n", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVal=\"\";}\n" "const std::string strGetString1()\n" "{return m_strVal.c_str();}\n" "private:\n" "std::string m_strVal;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetString1' can be const.\n", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVec.push_back(\"\");}\n" "size_t strGetSize()\n" "{return m_strVec.size();}\n" "private:\n" "std::vector m_strVec;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetSize' can be const.\n", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVec.push_back(\"\");}\n" "bool strGetEmpty()\n" "{return m_strVec.empty();}\n" "private:\n" "std::vector m_strVec;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetEmpty' can be const.\n", errout.str()); } void const26() { // ticket #1847 checkConst("class DelayBase {\n" "public:\n" "void swapSpecificDelays(int index1, int index2) {\n" " std::swap(delays_[index1], delays_[index2]);\n" "}\n" "float delays_[4];\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct DelayBase {\n" " float swapSpecificDelays(int index1) {\n" " return delays_[index1];\n" " }\n" " float delays_[4];\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Technically the member function 'DelayBase::swapSpecificDelays' can be const.\n", errout.str()); } void const27() { // ticket #1882 checkConst("class A {\n" "public:\n" " A(){m_d=1.0; m_iRealVal=2.0;}\n" " double dGetValue();\n" "private:\n" " double m_d;\n" " double m_iRealVal;\n" "};\n" "double A::dGetValue() {\n" " double dRet = m_iRealVal;\n" " if( m_d != 0 )\n" " return m_iRealVal / m_d;\n" " return dRet;\n" "};", nullptr, true); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'A::dGetValue' can be const.\n", errout.str()); } void const28() { // ticket #1883 checkConst("class P {\n" "public:\n" " P() { x=0.0; y=0.0; }\n" " double x,y;\n" "};\n" "class A : public P {\n" "public:\n" " A():P(){}\n" " void SetPos(double xPos, double yPos) {\n" " x=xPos;\n" " y=yPos;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class AA : public P {\n" "public:\n" " AA():P(){}\n" " inline void vSetXPos(int x_)\n" " {\n" " UnknownScope::x = x_;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class AA {\n" "public:\n" " AA():P(){}\n" " inline void vSetXPos(int x_)\n" " {\n" " UnknownScope::x = x_;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'AA::vSetXPos' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const29() { // ticket #1922 checkConst("class test {\n" " public:\n" " test();\n" " const char* get() const;\n" " char* get();\n" " private:\n" " char* value_;\n" "};\n" "test::test()\n" "{\n" " value_ = 0;\n" "}\n" "const char* test::get() const\n" "{\n" " return value_;\n" "}\n" "char* test::get()\n" "{\n" " return value_;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const30() { // check for false negatives checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived : public Base {\n" "public:\n" " int get() {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'Derived::get' can be const.\n", errout.str()); checkConst("class Base1 {\n" "public:\n" " int a;\n" "};\n" "class Base2 {\n" "public:\n" " int b;\n" "};\n" "class Derived : public Base1, public Base2 {\n" "public:\n" " int getA() {\n" " return a;\n" " }\n" " int getB() {\n" " return b;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (style, inconclusive) Technically the member function 'Derived::getA' can be const.\n" "[test.cpp:14]: (style, inconclusive) Technically the member function 'Derived::getB' can be const.\n", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 {\n" "public:\n" " int get() {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Derived2::get' can be const.\n", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 { };\n" "class Derived3 : public Derived2 { };\n" "class Derived4 : public Derived3 {\n" "public:\n" " int get() {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (style, inconclusive) Technically the member function 'Derived4::get' can be const.\n", errout.str()); // check for false positives checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived : public Base {\n" "public:\n" " int get() const {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Base1 {\n" "public:\n" " int a;\n" "};\n" "class Base2 {\n" "public:\n" " int b;\n" "};\n" "class Derived : public Base1, public Base2 {\n" "public:\n" " int getA() const {\n" " return a;\n" " }\n" " int getB() const {\n" " return b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 {\n" "public:\n" " int get() const {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 { };\n" "class Derived3 : public Derived2 { };\n" "class Derived4 : public Derived3 {\n" "public:\n" " int get() const {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const31() { checkConst("namespace std { }\n" "class Fred {\n" "public:\n" " int a;\n" " int get() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout.str()); } void const32() { checkConst("class Fred {\n" "public:\n" " std::string a[10];\n" " void seta() { a[0] = \"\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const33() { checkConst("class derived : public base {\n" "public:\n" " void f(){}\n" "};"); ASSERT_EQUALS("", errout.str()); } void const34() { // ticket #1964 checkConst("class Bar {\n" " void init(Foo * foo) {\n" " foo.bar = this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const35() { // ticket #2001 checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " };\n" "}\n" "namespace N\n" "{\n" " class Derived : public Base\n" " {\n" " public:\n" " int getResourceName() { return var; }\n" " int var;\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (style, inconclusive) Technically the member function 'N::Derived::getResourceName' can be const.\n", errout.str()); checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " public:\n" " int getResourceName();\n" " int var;\n" " };\n" "}\n" "int N::Base::getResourceName() { return var; }"); ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout.str()); checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " public:\n" " int getResourceName();\n" " int var;\n" " };\n" "}\n" "namespace N\n" "{\n" " int Base::getResourceName() { return var; }\n" "}"); ASSERT_EQUALS("[test.cpp:12] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout.str()); checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " public:\n" " int getResourceName();\n" " int var;\n" " };\n" "}\n" "using namespace N;\n" "int Base::getResourceName() { return var; }"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout.str()); } void const36() { // ticket #2003 checkConst("class Foo {\n" "public:\n" " Blue::Utility::Size m_MaxQueueSize;\n" " void SetMaxQueueSize(Blue::Utility::Size a_MaxQueueSize)\n" " {\n" " m_MaxQueueSize = a_MaxQueueSize;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const37() { // ticket #2081 and #2085 checkConst("class A\n" "{\n" "public:\n" " A(){};\n" " std::string operator+(const char *c)\n" " {\n" " return m_str+std::string(c);\n" " }\n" "private:\n" " std::string m_str;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::operator+' can be const.\n", errout.str()); checkConst("class Fred\n" "{\n" "private:\n" " long x;\n" "public:\n" " Fred() {\n" " x = 0;\n" " }\n" " bool isValid() {\n" " return (x == 0x11224488);\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'Fred::isValid' can be const.\n", errout.str()); } void const38() { // ticket #2135 checkConst("class Foo {\n" "public:\n" " ~Foo() { delete oArq; }\n" " Foo(): oArq(new std::ofstream(\"...\")) {}\n" " void MyMethod();\n" "private:\n" " std::ofstream *oArq;\n" "};\n" "void Foo::MyMethod()\n" "{\n" " (*oArq) << \"\";\n" "}"); ASSERT_EQUALS("", errout.str()); } void const39() { checkConst("class Foo\n" "{\n" " int * p;\n" "public:\n" " Foo () : p(0) { }\n" " int * f();\n" " const int * f() const;\n" "};\n" "const int * Foo::f() const\n" "{\n" " return p;\n" "}\n" "int * Foo::f()\n" "{\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const40() { // ticket #2228 checkConst("class SharedPtrHolder\n" "{\n" " private:\n" " std::tr1::shared_ptr pView;\n" " public:\n" " SharedPtrHolder()\n" " { }\n" " void SetView(const std::shared_ptr & aView)\n" " {\n" " pView = aView;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const41() { // ticket #2255 checkConst("class Fred\n" "{\n" " ::std::string m_name;\n" "public:\n" " void SetName(const ::std::string & name)\n" " {\n" " m_name = name;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class SharedPtrHolder\n" "{\n" " ::std::tr1::shared_ptr pNum;\n" " public :\n" " void SetNum(const ::std::tr1::shared_ptr & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class SharedPtrHolder2\n" "{\n" " public:\n" " typedef ::std::tr1::shared_ptr IntSharedPtr;\n" " private:\n" " IntSharedPtr pNum;\n" " public :\n" " void SetNum(const IntSharedPtr & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct IntPtrTypes\n" "{\n" " typedef ::std::tr1::shared_ptr Shared;\n" "};\n" "class SharedPtrHolder3\n" "{\n" " private:\n" " IntPtrTypes::Shared pNum;\n" " public :\n" " void SetNum(const IntPtrTypes::Shared & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("template \n" "struct PtrTypes\n" "{\n" " typedef ::std::tr1::shared_ptr Shared;\n" "};\n" "class SharedPtrHolder4\n" "{\n" " private:\n" " PtrTypes::Shared pNum;\n" " public :\n" " void SetNum(const PtrTypes::Shared & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const42() { // ticket #2282 checkConst("class Fred\n" "{\n" "public:\n" " struct AB { };\n" " bool f(AB * ab);\n" "};\n" "bool Fred::f(Fred::AB * ab)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Fred\n" "{\n" "public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" "};\n" "bool Fred::f(Fred::AB::CD * cd)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("namespace NS {\n" " class Fred\n" " {\n" " public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" " };\n" " bool Fred::f(Fred::AB::CD * cd)\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("namespace NS {\n" " class Fred\n" " {\n" " public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" " };\n" "}\n" "bool NS::Fred::f(NS::Fred::AB::CD * cd)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Foo {\n" " class Fred\n" " {\n" " public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" " };\n" "};\n" "bool Foo::Fred::f(Foo::Fred::AB::CD * cd)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'Foo::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const43() { // ticket 2377 checkConst("class A\n" "{\n" "public:\n" " void foo( AA::BB::CC::DD b );\n" " AA::BB::CC::DD a;\n" "};\n" "void A::foo( AA::BB::CC::DD b )\n" "{\n" " a = b;\n" "}"); ASSERT_EQUALS("", errout.str()); checkConst("namespace AA\n" "{\n" " namespace BB\n" " {\n" " namespace CC\n" " {\n" " struct DD\n" " {};\n" " }\n" " }\n" "}\n" "class A\n" "{\n" " public:\n" "\n" " AA::BB::CC::DD a;\n" " void foo(AA::BB::CC::DD b)\n" " {\n" " a = b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("namespace ZZ\n" "{\n" " namespace YY\n" " {\n" " struct XX\n" " {};\n" " }\n" "}\n" "class B\n" "{\n" " public:\n" " ZZ::YY::XX a;\n" " void foo(ZZ::YY::XX b)\n" " {\n" " a = b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const44() { // ticket 2595 checkConst("class A\n" "{\n" "public:\n" " bool bOn;\n" " bool foo()\n" " {\n" " return 0 != (bOn = bOn);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const45() { // ticket 2664 checkConst("namespace wraps {\n" " class BaseLayout {};\n" "}\n" "namespace tools {\n" " class WorkspaceControl :\n" " public wraps::BaseLayout\n" " {\n" " int toGrid(int _value)\n" " {\n" " }\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (performance, inconclusive) Technically the member function 'tools::WorkspaceControl::toGrid' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const46() { // ticket 2663 checkConst("class Altren {\n" "public:\n" " int fun1() {\n" " int a;\n" " a++;\n" " }\n" " int fun2() {\n" " b++;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Altren::fun1' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:7]: (performance, inconclusive) Technically the member function 'Altren::fun2' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const47() { // ticket 2670 checkConst("class Altren {\n" "public:\n" " void foo() { delete this; }\n" " void foo(int i) const { }\n" " void bar() { foo(); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Altren {\n" "public:\n" " void foo() { delete this; }\n" " void foo(int i) const { }\n" " void bar() { foo(1); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:5]: (style, inconclusive) Technically the member function 'Altren::bar' can be const.\n", errout.str()); } void const48() { // ticket 2672 checkConst("class S0 {\n" " class S1 {\n" " class S2 {\n" " class S3 {\n" " class S4 { };\n" " };\n" " };\n" " };\n" "};\n" "class TextIterator {\n" " S0::S1::S2::S3::S4 mCurrent, mSave;\n" "public:\n" " bool setTagColour();\n" "};\n" "bool TextIterator::setTagColour() {\n" " mSave = mCurrent;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const49() { // ticket 2795 checkConst("class A {\n" " private:\n" " std::map _hash;\n" " public:\n" " A() : _hash() {}\n" " unsigned int fetch(unsigned int key)\n" // cannot be 'const' " {\n" " return _hash[key];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const50() { // ticket 2943 checkConst("class Altren\n" "{\n" " class SubClass : public std::vector\n" " {\n" " };\n" "};\n" "void _setAlign()\n" "{\n" " if (mTileSize.height > 0) return;\n" " if (mEmptyView) return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const51() { // ticket 3040 checkConst("class PSIPTable {\n" "public:\n" " PSIPTable() : _pesdata(0) { }\n" " const unsigned char* pesdata() const { return _pesdata; }\n" " unsigned char* pesdata() { return _pesdata; }\n" " void SetSection(uint num) { pesdata()[6] = num; }\n" "private:\n" " unsigned char *_pesdata;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class PESPacket {\n" "public:\n" " PESPacket() : _pesdata(0) { }\n" " const unsigned char* pesdata() const { return _pesdata; }\n" " unsigned char* pesdata() { return _pesdata; }\n" "private:\n" " unsigned char *_pesdata;\n" "};\n" "class PSIPTable : public PESPacket\n" "{\n" "public:\n" " void SetSection(uint num) { pesdata()[6] = num; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const52() { // ticket 3048 checkConst("class foo {\n" " void DoSomething(int &a) const { a = 1; }\n" " void DoSomethingElse() { DoSomething(bar); }\n" "private:\n" " int bar;\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'foo::DoSomething' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const53() { // ticket 3049 checkConst("class A {\n" " public:\n" " A() : foo(false) {};\n" " virtual bool One(bool b = false) { foo = b; return false; }\n" " private:\n" " bool foo;\n" "};\n" "class B : public A {\n" " public:\n" " B() {};\n" " bool One(bool b = false) { return false; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const54() { // ticket 3052 checkConst("class Example {\n" " public:\n" " void Clear(void) { Example tmp; (*this) = tmp; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const55() { checkConst("class MyObject {\n" " int tmp;\n" " MyObject() : tmp(0) {}\n" "public:\n" " void set(std::stringstream &in) { in >> tmp; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const56() { // ticket #3149 checkConst("class MyObject {\n" "public:\n" " void foo(int x) {\n" " switch (x) { }\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class A\n" "{\n" " protected:\n" " unsigned short f (unsigned short X);\n" " public:\n" " A ();\n" "};\n" "\n" "unsigned short A::f (unsigned short X)\n" "{\n" " enum ERetValues {RET_NOK = 0, RET_OK = 1};\n" " enum ETypes {FLOAT_TYPE = 1, INT_TYPE = 2};\n" "\n" " try\n" " {\n" " switch (X)\n" " {\n" " case FLOAT_TYPE:\n" " {\n" " return RET_OK;\n" " }\n" " case INT_TYPE:\n" " {\n" " return RET_OK;\n" " }\n" " default:\n" " {\n" " return RET_NOK;\n" " }\n" " }\n" " }\n" " catch (...)\n" " {\n" " return RET_NOK;\n" " }\n" "\n" " return RET_NOK;\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'A::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class MyObject {\n" "public:\n" " void foo(int x) {\n" " for (int i = 0; i < 5; i++) { }\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const57() { // tickets #2669 and #2477 checkConst("namespace MyGUI\n" "{\n" " namespace types\n" " {\n" " struct TSize {};\n" " struct TCoord {\n" " TSize size() const { }\n" " };\n" " }\n" " typedef types::TSize IntSize;\n" " typedef types::TCoord IntCoord;\n" "}\n" "class SelectorControl\n" "{\n" " MyGUI::IntSize getSize()\n" " {\n" " return mCoordValue.size();\n" " }\n" "private:\n" " MyGUI::IntCoord mCoordValue;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:15]: (style, inconclusive) Technically the member function 'SelectorControl::getSize' can be const.\n", "[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct Foo {\n" " Bar b;\n" " void foo(Foo f) {\n" " b.run();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Bar {\n" " int i = 0;\n" " void run() { i++; }\n" "};\n" "struct Foo {\n" " Bar b;\n" " void foo(Foo f) {\n" " b.run();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Bar {\n" " void run() const { }\n" "};\n" "struct Foo {\n" " Bar b;\n" " void foo(Foo f) {\n" " b.run();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Bar::run' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:6]: (style, inconclusive) Technically the member function 'Foo::foo' can be const.\n", errout.str()); } void const58() { checkConst("struct MyObject {\n" " void foo(Foo f) {\n" " f.clear();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct MyObject {\n" " int foo(Foo f) {\n" " return f.length();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct MyObject {\n" " Foo f;\n" " int foo() {\n" " return f.length();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct MyObject {\n" " std::string f;\n" " int foo() {\n" " return f.length();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'MyObject::foo' can be const.\n", errout.str()); } void const59() { // ticket #4646 checkConst("class C {\n" "public:\n" " inline void operator += (const int &x ) { re += x; }\n" " friend inline void exp(C & c, const C & x) { }\n" "protected:\n" " int re;\n" " int im;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const60() { // ticket #3322 checkConst("class MyString {\n" "public:\n" " MyString() : m_ptr(0){}\n" " MyString& operator+=( const MyString& rhs ) {\n" " delete m_ptr;\n" " m_ptr = new char[42];\n" " }\n" " MyString append( const MyString& str )\n" " { return operator+=( str ); }\n" " char *m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class MyString {\n" "public:\n" " MyString() : m_ptr(0){}\n" " MyString& operator+=( const MyString& rhs );\n" " MyString append( const MyString& str )\n" " { return operator+=( str ); }\n" " char *m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const61() { // ticket #5606 - don't crash // this code is invalid so a false negative is OK checkConst("class MixerParticipant : public MixerParticipant {\n" " int GetAudioFrame();\n" "};\n" "int MixerParticipant::GetAudioFrame() {\n" " return 0;\n" "}"); // this code is invalid so a false negative is OK checkConst("class MixerParticipant : public MixerParticipant {\n" " bool InitializeFileReader() {\n" " printf(\"music\");\n" " }\n" "};"); // Based on an example from SVN source code causing an endless recursion within CheckClass::isConstMemberFunc() // A more complete example including a template declaration like // template class Hash{/* ... */}; // didn't . checkConst("template<>\n" "class Hash {\n" "protected:\n" " typedef Key::key_type key_type;\n" " void set(const Key& key);\n" "};\n" "template\n" "class Hash : private Hash {\n" " typedef Hash inherited;\n" " void set(const Key& key) {\n" " inherited::set(inherited::Key(key));\n" " }\n" "};\n", nullptr, false); ASSERT_EQUALS("", errout.str()); } void const62() { checkConst("class A {\n" " private:\n" " std::unordered_map _hash;\n" " public:\n" " A() : _hash() {}\n" " unsigned int fetch(unsigned int key)\n" // cannot be 'const' " {\n" " return _hash[key];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const63() { checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " std::string* p = &s;\n" " p->clear();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " std::string& r = s;\n" " r.clear();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " std::string& r = sth; r = s;\n" " r.clear();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " const std::string* p = &s;\n" " p->somefunction();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " const std::string& r = s;\n" " r.somefunction();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout.str()); } void const64() { checkConst("namespace B {\n" " namespace D {\n" " typedef int DKIPtr;\n" " }\n" " class ZClass {\n" " void set(const ::B::D::DKIPtr& p) {\n" " membervariable = p;\n" " }\n" " ::B::D::DKIPtr membervariable;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void const65() { checkConst("template \n" "class TemplateClass {\n" "public:\n" " TemplateClass() { }\n" "};\n" "template <>\n" "class TemplateClass {\n" "public:\n" " TemplateClass() { }\n" "};\n" "int main() {\n" " TemplateClass a;\n" " TemplateClass b;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const66() { checkConst("struct C {\n" " C() : n(0) {}\n" " void f(int v) { g((char *) &v); }\n" " void g(char *) { n++; }\n" " int n;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const67() { // #9193 checkConst("template >\n" "class TestList {\n" "public:\n" " LIST_T m_list;\n" "};\n" "class Test {\n" "public:\n" " const std::list>& get() { return m_test.m_list; }\n" " TestList> m_test;\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Test::get' can be const.\n", errout.str()); } void const68() { // #6471 checkConst("class MyClass {\n" " void clear() {\n" " SVecPtr v = (SVecPtr) m_data;\n" " v->clear();\n" " }\n" " void* m_data;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void const69() { // #9806 checkConst("struct A {\n" " int a = 0;\n" " template void call(const Args &... args) { a = 1; }\n" " template auto call(const Args &... args) -> T {\n" " a = 2;\n" " return T{};\n" " }\n" "};\n" "\n" "struct B : public A {\n" " void test() {\n" " call();\n" " call(1, 2, 3);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const70() { checkConst("struct A {\n" " template void call(Args ... args) {\n" " func(this);\n" " }\n" "\n" " void test() {\n" " call(1, 2);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const71() { // #10146 checkConst("struct Bar {\n" " int j = 5;\n" " void f(int& i) const { i += j; }\n" "};\n" "struct Foo {\n" " Bar bar;\n" " int k{};\n" " void g() { bar.f(k); }\n" "};\n"); ASSERT_EQUALS("", errout.str()); checkConst("struct S {\n" " A a;\n" " void f(int j, int*& p) {\n" " p = &(((a[j])));\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void const72() { // #10520 checkConst("struct S {\n" " explicit S(int* p) : mp(p) {}\n" " int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return S{ &i }; }\n" "};\n"); ASSERT_EQUALS("", errout.str()); checkConst("struct S {\n" " explicit S(int* p) : mp(p) {}\n" " int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return S(&i); }\n" "};\n"); ASSERT_EQUALS("", errout.str()); checkConst("struct S {\n" " int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return S{ &i }; }\n" "};\n"); ASSERT_EQUALS("", errout.str()); checkConst("struct S {\n" " int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return { &i }; }\n" "};\n"); ASSERT_EQUALS("", errout.str()); checkConst("struct S {\n" " explicit S(const int* p) : mp(p) {}\n" " const int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return S{ &i }; }\n" "};\n"); TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); checkConst("struct S {\n" " explicit S(const int* p) : mp(p) {}\n" " const int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return S(&i); }\n" "};\n"); TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); checkConst("struct S {\n" " const int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return S{ &i }; }\n" "};\n"); TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); checkConst("struct S {\n" " const int* mp{};\n" "};\n" "struct C {\n" " int i{};\n" " S f() { return { &i }; }\n" "};\n"); TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout.str()); } void const73() { checkConst("struct A {\n" " int* operator[](int i);\n" " const int* operator[](int i) const;\n" "};\n" "struct S {\n" " A a;\n" " void f(int j) {\n" " int* p = a[j];\n" " *p = 0;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); checkConst("struct S {\n" // #10758 " T* h;\n" " void f(); \n" "};\n" "void S::f() {\n" " char* c = h->x[y];\n" "};\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'S::f' can be const.\n", errout.str()); } void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n" " return func(this);\n" " }\n" " int bar1() {\n" " return foo1(1);\n" " }\n" " int bar2() {\n" " return foo1(1, 2);\n" " }\n" " int bar3() {\n" " return foo1(1, 2, 3);\n" " }\n" " int bar4() {\n" " return foo1();\n" " }\n" " void foo2(int i = 0) {\n" " return func(this);\n" " }\n" " int bar5() {\n" " return foo2();\n" " }\n" " void foo3() {\n" " return func(this);\n" " }\n" " int bar6() {\n" " return foo3();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (performance, inconclusive) Technically the member function 'Foo::bar3' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:14]: (performance, inconclusive) Technically the member function 'Foo::bar4' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const_passThisToMemberOfOtherClass() { checkConst("struct Foo {\n" " void foo() {\n" " Bar b;\n" " b.takeFoo(this);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Foo {\n" " void foo() {\n" " Foo f;\n" " f.foo();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Foo::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A;\n" // #5839 - operator() "struct B {\n" " void operator()(A *a);\n" "};\n" "struct A {\n" " void dostuff() {\n" " B()(this);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void assigningPointerToPointerIsNotAConstOperation() { checkConst("struct s\n" "{\n" " int** v;\n" " void f()\n" " {\n" " v = 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void assigningArrayElementIsNotAConstOperation() { checkConst("struct s\n" "{\n" " ::std::string v[3];\n" " void f()\n" " {\n" " v[0] = \"Happy new year!\";\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } // increment/decrement => not const void constincdec() { checkConst("class Fred {\n" " int a;\n" " void nextA() { return ++a; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return --a; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a++; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a--; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return ++a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return --a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a++; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a--; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassign1() { checkConst("class Fred {\n" " int a;\n" " void nextA() { return a=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a/=-2; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a-=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a+=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a*=-1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a/=-2; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassign2() { checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a-=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a+=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a*=-1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a/=-2; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a/=-2; }\n" "};"); ASSERT_EQUALS("", errout.str()); } // increment/decrement array element => not const void constincdecarray() { checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return ++a[0]; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return --a[0]; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]++; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]--; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return ++a[0]; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return --a[0]; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]++; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]--; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassignarray() { checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]/=-2; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]-=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]+=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]*=-1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]/=-2; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } // return pointer/reference => not const void constReturnReference() { checkConst("class Fred {\n" " int a;\n" " int &getR() { return a; }\n" " int *getP() { return &a; }" "};"); ASSERT_EQUALS("", errout.str()); } // delete member variable => not const (but technically it can, it compiles without errors) void constDelete() { checkConst("class Fred {\n" " int *a;\n" " void clean() { delete a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } // A function that returns unknown types can't be const (#1579) void constLPVOID() { checkConst("class Fred {\n" " UNKNOWN a() { return 0; };\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::a' can be static.\n", "", errout.str()); // #1579 - HDC checkConst("class Fred {\n" " foo bar;\n" " UNKNOWN a() { return b; };\n" "};"); ASSERT_EQUALS("", errout.str()); } // a function that calls const functions can be const void constFunc() { checkConst("class Fred {\n" " void f() const { };\n" " void a() { f(); };\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::a' can be const.\n", errout.str()); // ticket #1593 checkConst("class A\n" "{\n" " std::vector m_v;\n" "public:\n" " A(){}\n" " unsigned int GetVecSize() {return m_v.size();}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'A::GetVecSize' can be const.\n", errout.str()); checkConst("class A\n" "{\n" " std::vector m_v;\n" "public:\n" " A(){}\n" " bool GetVecEmpty() {return m_v.empty();}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'A::GetVecEmpty' can be const.\n", errout.str()); } void constVirtualFunc() { // base class has no virtual function checkConst("class A { };\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); checkConst("class A { };\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); // base class has no virtual function checkConst("class A {\n" "public:\n" " int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:9]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); // base class has virtual function checkConst("class A {\n" "public:\n" " virtual int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " virtual int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " virtual int func() = 0;\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("", errout.str()); // base class has no virtual function checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " int func() { return a; }\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func() { return c; }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::func' can be const.\n" "[test.cpp:11]: (style, inconclusive) Technically the member function 'B::func' can be const.\n" "[test.cpp:17]: (style, inconclusive) Technically the member function 'C::func' can be const.\n", errout.str()); checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " int func();\n" "};\n" "int A::func() { return a; }\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func();\n" "};\n" "int C::func() { return c; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Technically the member function 'A::func' can be const.\n" "[test.cpp:14] -> [test.cpp:12]: (style, inconclusive) Technically the member function 'B::func' can be const.\n" "[test.cpp:21] -> [test.cpp:19]: (style, inconclusive) Technically the member function 'C::func' can be const.\n", errout.str()); // base class has virtual function checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " virtual int func() { return a; }\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func() { return c; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " virtual int func();\n" "};\n" "int A::func() { return a; }\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func();\n" "};\n" "int C::func() { return c; }"); ASSERT_EQUALS("", errout.str()); // ticket #1311 checkConst("class X {\n" " int x;\n" "public:\n" " X(int x) : x(x) { }\n" " int getX() { return x; }\n" "};\n" "class Y : public X {\n" " int y;\n" "public:\n" " Y(int x, int y) : X(x), y(y) { }\n" " int getY() { return y; }\n" "};\n" "class Z : public Y {\n" " int z;\n" "public:\n" " Z(int x, int y, int z) : Y(x, y), z(z) { }\n" " int getZ() { return z; }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'X::getX' can be const.\n" "[test.cpp:11]: (style, inconclusive) Technically the member function 'Y::getY' can be const.\n" "[test.cpp:17]: (style, inconclusive) Technically the member function 'Z::getZ' can be const.\n", errout.str()); checkConst("class X {\n" " int x;\n" "public:\n" " X(int x) : x(x) { }\n" " int getX();\n" "};\n" "int X::getX() { return x; }\n" "class Y : public X {\n" " int y;\n" "public:\n" " Y(int x, int y) : X(x), y(y) { }\n" " int getY();\n" "};\n" "int Y::getY() { return y; }\n" "class Z : public Y {\n" " int z;\n" "public:\n" " Z(int x, int y, int z) : Y(x, y), z(z) { }\n" " int getZ();\n" "};\n" "int Z::getZ() { return z; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Technically the member function 'X::getX' can be const.\n" "[test.cpp:14] -> [test.cpp:12]: (style, inconclusive) Technically the member function 'Y::getY' can be const.\n" "[test.cpp:21] -> [test.cpp:19]: (style, inconclusive) Technically the member function 'Z::getZ' can be const.\n", errout.str()); } void constIfCfg() { const char code[] = "struct foo {\n" " int i;\n" " void f() {\n" //"#ifdef ABC\n" //" i = 4;\n" //"endif\n" " }\n" "};"; checkConst(code, &settings0, true); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'foo::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst(code, &settings0, false); // TODO: Set inconclusive to true (preprocess it) ASSERT_EQUALS("", errout.str()); } void constFriend() { // ticket #1921 const char code[] = "class foo {\n" " friend void f() { }\n" "};"; checkConst(code); ASSERT_EQUALS("", errout.str()); } void constUnion() { // ticket #2111 checkConst("class foo {\n" "public:\n" " union {\n" " int i;\n" " float f;\n" " } d;\n" " void setf(float x) {\n" " d.f = x;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void constArrayOperator() { checkConst("struct foo {\n" " int x;\n" " int y[5][724];\n" " T a() {\n" " return y[x++][6];\n" " }\n" " T b() {\n" " return y[1][++x];\n" " }\n" " T c() {\n" " return y[1][6];\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (style, inconclusive) Technically the member function 'foo::c' can be const.\n", errout.str()); } void constRangeBasedFor() { // #5514 checkConst("class Fred {\n" " int array[256];\n" "public:\n" " void f1() {\n" " for (auto & e : array)\n" " foo(e);\n" " }\n" " void f2() {\n" " for (const auto & e : array)\n" " foo(e);\n" " }\n" " void f3() {\n" " for (decltype(auto) e : array)\n" " foo(e);\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Fred::f2' can be const.\n", errout.str()); } void const_shared_ptr() { // #8674 checkConst("class Fred {\n" "public:\n" " std::shared_ptr getData();\n" "private:\n" " std::shared_ptr data;\n" "};\n" "\n" "std::shared_ptr Fred::getData() { return data; }"); ASSERT_EQUALS("", errout.str()); } void constPtrToConstPtr() { checkConst("class Fred {\n" "public:\n" " const char *const *data;\n" " const char *const *getData() { return data; }\n}"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::getData' can be const.\n", errout.str()); } void constTrailingReturnType() { // #9814 checkConst("struct A {\n" " int x = 1;\n" " auto get() -> int & { return x; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void staticArrayPtrOverload() { checkConst("struct S {\n" " template\n" " void f(const std::array& sv);\n" " template\n" " void f(const char* const (&StrArr)[N]);\n" "};\n" "template\n" "void S::f(const std::array& sv) {\n" " const char* ptrs[N]{};\n" " return f(ptrs);\n" "}\n" "template void S::f(const std::array& sv);\n"); ASSERT_EQUALS("", errout.str()); } #define checkInitializerListOrder(code) checkInitializerListOrder_(code, __FILE__, __LINE__) void checkInitializerListOrder_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Check.. settings0.certainty.setEnabled(Certainty::inconclusive, true); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckClass checkClass(&tokenizer, &settings0, this); checkClass.initializerListOrder(); } void initializerListOrder() { checkInitializerListOrder("class Fred {\n" " int a, b, c;\n" "public:\n" " Fred() : c(0), b(0), a(0) { }\n" "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout.str()); checkInitializerListOrder("class Fred {\n" " int a, b, c;\n" "public:\n" " Fred() : c{0}, b{0}, a{0} { }\n" "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout.str()); } #define checkInitializationListUsage(code) checkInitializationListUsage_(code, __FILE__, __LINE__) void checkInitializationListUsage_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Check.. Settings settings; settings.severity.enable(Severity::performance); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckClass checkClass(&tokenizer, &settings, this); checkClass.initializationListUsage(); } void initializerListUsage() { checkInitializationListUsage("enum Enum { C = 0 };\n" "class Fred {\n" " int a;\n" // No message for builtin types: No performance gain " int* b;\n" // No message for pointers: No performance gain " Enum c;\n" // No message for enums: No performance gain " Fred() { a = 0; b = 0; c = C; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string s;\n" " Fred() { a = 0; s = \"foo\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 's' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string& s;\n" // Message is invalid for references, since their initialization in initializer list is required anyway and behaves different from assignment (#5004) " Fred(const std::string& s_) : s(s_) { s = \"foo\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::vector v;\n" " Fred() { v = unknown; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 'v' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C { std::string s; };\n" "class Fred {\n" " C c;\n" " Fred() { c = unknown; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C c;\n" " Fred() { c = unknown; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C c;\n" " Fred(Fred const & other) { c = other.c; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C c;\n" " Fred(Fred && other) { c = other.c; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a;\n" " Fred() { initB(); a = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a;\n" " Fred() : a(0) { if(b) a = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a[5];\n" " Fred() { for(int i = 0; i < 5; i++) a[i] = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a; int b;\n" " Fred() : b(5) { a = b; }\n" // Don't issue a message here: You actually could move it to the initialization list, but it would cause problems if you change the order of the variable declarations. "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a;\n" " Fred() { try { a = new int; } catch(...) {} }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string s;\n" " Fred() { s = toString((size_t)this); }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string a;\n" " std::string foo();\n" " Fred() { a = foo(); }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string a;\n" " Fred() { a = foo(); }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 'a' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class Fred {\n" // #4332 " static std::string s;\n" " Fred() { s = \"foo\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" // #5640 " std::string s;\n" " Fred() {\n" " char str[2];\n" " str[0] = c;\n" " str[1] = 0;\n" " s = str;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class B {\n" // #5640 " std::shared_ptr _d;\n" " B(const B& other) : _d(std::make_shared()) {\n" " *_d = *other._d;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Bar {\n" // #8466 "public:\n" " explicit Bar(const Bar &bar) : Bar{bar.s} {}\n" " explicit Bar(const char s) : s{s} {}\n" "private:\n" " char s;\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("unsigned bar(std::string);\n" // #8291 "class Foo {\n" "public:\n" " int a_, b_;\n" " Foo(int a, int b) : a_(a), b_(b) {}\n" " Foo(int a, const std::string& b) : Foo(a, bar(b)) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" // #8111 " std::string a;\n" " Fred() {\n" " std::ostringstream ostr;\n" " ostr << x;\n" " a = ostr.str();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // bailout: multi line lambda in rhs => do not warn checkInitializationListUsage("class Fred {\n" " std::function f;\n" " Fred() {\n" " f = [](){\n" " return 1;\n" " };\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // don't warn if some other instance's members are assigned to checkInitializationListUsage("class C {\n" "public:\n" " C(C& c) : m_i(c.m_i) { c.m_i = (Foo)-1; }\n" "private:\n" " Foo m_i;\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class A {\n" // #9821 - delegate constructor "public:\n" " A() : st{} {}\n" "\n" " explicit A(const std::string &input): A() {\n" " st = input;\n" " }\n" "\n" "private:\n" " std::string st;\n" "};"); ASSERT_EQUALS("", errout.str()); } #define checkSelfInitialization(code) checkSelfInitialization_(code, __FILE__, __LINE__) void checkSelfInitialization_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckClass checkClass(&tokenizer, &settings0, this); (checkClass.checkSelfInitialization)(); } void selfInitialization() { checkSelfInitialization("class Fred {\n" " int i;\n" " Fred() : i(i) {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 'i' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " int i;\n" " Fred() : i{i} {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 'i' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " int i;\n" " Fred();\n" "};\n" "Fred::Fred() : i(i) {\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Member variable 'i' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " std::string s;\n" " Fred() : s(s) {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 's' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " int x;\n" " Fred(int x);\n" "};\n" "Fred::Fred(int x) : x(x) { }"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("class Fred {\n" " int x;\n" " Fred(int x);\n" "};\n" "Fred::Fred(int x) : x{x} { }"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("class Fred {\n" " std::string s;\n" " Fred(const std::string& s) : s(s) {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("class Fred {\n" " std::string s;\n" " Fred(const std::string& s) : s{s} {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("struct Foo : Bar {\n" " int i;\n" " Foo(int i)\n" " : Bar(\"\"), i(i) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("struct Foo : std::Bar {\n" // #6073 " int i;\n" " Foo(int i)\n" " : std::Bar(\"\"), i(i) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("struct Foo : std::Bar {\n" // #6073 " int i;\n" " Foo(int i)\n" " : std::Bar(\"\"), i{i} {}\n" "};"); ASSERT_EQUALS("", errout.str()); } #define checkVirtualFunctionCall(...) checkVirtualFunctionCall_(__FILE__, __LINE__, __VA_ARGS__) void checkVirtualFunctionCall_(const char* file, int line, const char code[], Settings *s = nullptr, bool inconclusive = true) { // Clear the error log errout.str(""); // Check.. if (!s) { static Settings settings_; s = &settings_; s->severity.enable(Severity::warning); } s->certainty.setEnabled(Certainty::inconclusive, inconclusive); // Tokenize.. Tokenizer tokenizer(s, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckClass checkClass(&tokenizer, s, this); checkClass.checkVirtualFunctionCallInConstructor(); } void virtualFunctionCallInConstructor() { checkVirtualFunctionCall("class A\n" "{\n" " virtual int f() { return 1; }\n" " A();\n" "};\n" "A::A()\n" "{f();}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (style) Virtual function 'f' is called from constructor 'A()' at line 7. Dynamic binding is not used.\n", errout.str()); checkVirtualFunctionCall("class A {\n" " virtual int f();\n" " A() {f();}\n" "};\n" "int A::f() { return 1; }"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout.str()); checkVirtualFunctionCall("class A : B {\n" " int f() override;\n" " A() {f();}\n" "};\n" "int A::f() { return 1; }"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout.str()); checkVirtualFunctionCall("class B {\n" " virtual int f() = 0;\n" "};\n" "class A : B {\n" " int f();\n" // <- not explicitly virtual " A() {f();}\n" "};\n" "int A::f() { return 1; }"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " A() { A::f(); }\n" " virtual void f() {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A : B {\n" " int f() final { return 1; }\n" " A() { f(); }\n" "};\n"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class B {\n" "public:" " virtual void f() {}\n" "};\n" "class A : B {\n" "public:" " void f() override final {}\n" " A() { f(); }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCall() { checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A();\n" "};\n" "A::A()\n" "{pure();}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual int pure()=0;\n" " A();\n" " int m;\n" "};\n" "A::A():m(A::pure())\n" "{}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " virtual ~A();\n" " int m;\n" "};\n" "A::~A()\n" "{pure();}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure()\n" " {pure();}\n" " A();\n" "};\n" "A::A()\n" "{nonpure();}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual int pure()=0;\n" " int nonpure()\n" " {return pure();}\n" " A();\n" " int m;\n" "};\n" "A::A():m(nonpure())\n" "{}"); TODO_ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", "", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure()\n" " {pure();}\n" " virtual ~A();\n" " int m;\n" "};\n" "A::~A()\n" "{nonpure();}"); ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A(bool b);\n" "};\n" "A::A(bool b)\n" "{if (b) pure();}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " virtual ~A();\n" " int m;\n" "};\n" "A::~A()\n" "{if (b) pure();}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); // #5831 checkVirtualFunctionCall("class abc {\n" "public:\n" " virtual ~abc() throw() {}\n" " virtual void def(void* g) throw () = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); // #4992 checkVirtualFunctionCall("class CMyClass {\n" " std::function< void(void) > m_callback;\n" "public:\n" " CMyClass() {\n" " m_callback = [this]() { return VirtualMethod(); };\n" " }\n" " virtual void VirtualMethod() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCallOtherClass() { checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A(const A & a);\n" "};\n" "A::A(const A & a)\n" "{a.pure();}"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A();\n" "};\n" "class B\n" "{\n" " virtual void pure()=0;\n" "};\n" "A::A()\n" "{B b; b.pure();}"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCallWithBody() { checkVirtualFunctionCall("class A\n" "{\n" " virtual void pureWithBody()=0;\n" " A();\n" "};\n" "A::A()\n" "{pureWithBody();}\n" "void A::pureWithBody()\n" "{}"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pureWithBody()=0;\n" " void nonpure()\n" " {pureWithBody();}\n" " A();\n" "};\n" "A::A()\n" "{nonpure();}\n" "void A::pureWithBody()\n" "{}"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCallPrevented() { checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure(bool bCallPure)\n" " { if (bCallPure) pure();}\n" " A();\n" "};\n" "A::A()\n" "{nonpure(false);}"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure(bool bCallPure)\n" " { if (!bCallPure) ; else pure();}\n" " A();\n" "};\n" "A::A()\n" "{nonpure(false);}"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure(bool bCallPure)\n" " {\n" " switch (bCallPure) {\n" " case true: pure(); break;\n" " }\n" " }\n" " A();\n" "};\n" "A::A()\n" "{nonpure(false);}"); ASSERT_EQUALS("", errout.str()); } #define checkOverride(code) checkOverride_(code, __FILE__, __LINE__) void checkOverride_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); Settings settings; settings.severity.enable(Severity::style); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings, this); (checkClass.checkOverride)(); } void override1() { checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f(); };"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f() override; };"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f() final; };"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base {\n" "public:\n" " virtual auto foo( ) const -> size_t { return 1; }\n" " virtual auto bar( ) const -> size_t { return 1; }\n" "};\n" "class Derived : public Base {\n" "public :\n" " auto foo( ) const -> size_t { return 0; }\n" " auto bar( ) const -> size_t override { return 0; }\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:8]: (style) The function 'foo' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout.str()); checkOverride("namespace Test {\n" " class C {\n" " public:\n" " virtual ~C();\n" " };\n" "}\n" "class C : Test::C {\n" "public:\n" " ~C();\n" "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:9]: (style) The destructor '~C' overrides a destructor in a base class but is not marked with a 'override' specifier.\n", errout.str()); checkOverride("struct Base {\n" " virtual void foo();\n" "};\n" "\n" "struct Derived: public Base {\n" " void foo() override;\n" " void foo(int);\n" "};"); ASSERT_EQUALS("", errout.str()); } void overrideCVRefQualifiers() { checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() const; }"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() volatile; }"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() &; }"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() &&; }"); ASSERT_EQUALS("", errout.str()); } #define checkUnsafeClassRefMember(code) checkUnsafeClassRefMember_(code, __FILE__, __LINE__) void checkUnsafeClassRefMember_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); Settings settings; settings.safeChecks.classes = true; settings.severity.enable(Severity::warning); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings, this); (checkClass.checkUnsafeClassRefMember)(); } void unsafeClassRefMember() { checkUnsafeClassRefMember("class C { C(const std::string &s) : s(s) {} const std::string &s; };"); ASSERT_EQUALS("[test.cpp:1]: (warning) Unsafe class: The const reference member 'C::s' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n", errout.str()); } #define checkThisUseAfterFree(code) checkThisUseAfterFree_(code, __FILE__, __LINE__) void checkThisUseAfterFree_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); (checkClass.checkThisUseAfterFree)(); } void thisUseAfterFree() { setMultiline(); // Calling method.. checkThisUseAfterFree("class C {\n" "public:\n" " void dostuff() { delete mInstance; hello(); }\n" "private:\n" " static C *mInstance;\n" " void hello() {}\n" "};"); ASSERT_EQUALS("test.cpp:3:warning:Calling method 'hello()' when 'this' might be invalid\n" "test.cpp:5:note:Assuming 'mInstance' is used as 'this'\n" "test.cpp:3:note:Delete 'mInstance', invalidating 'this'\n" "test.cpp:3:note:Call method when 'this' is invalid\n", errout.str()); checkThisUseAfterFree("class C {\n" "public:\n" " void dostuff() { mInstance.reset(); hello(); }\n" "private:\n" " static std::shared_ptr mInstance;\n" " void hello() {}\n" "};"); ASSERT_EQUALS("test.cpp:3:warning:Calling method 'hello()' when 'this' might be invalid\n" "test.cpp:5:note:Assuming 'mInstance' is used as 'this'\n" "test.cpp:3:note:Delete 'mInstance', invalidating 'this'\n" "test.cpp:3:note:Call method when 'this' is invalid\n", errout.str()); checkThisUseAfterFree("class C {\n" "public:\n" " void dostuff() { reset(); hello(); }\n" "private:\n" " static std::shared_ptr mInstance;\n" " void hello();\n" " void reset() { mInstance.reset(); }\n" "};"); ASSERT_EQUALS("test.cpp:3:warning:Calling method 'hello()' when 'this' might be invalid\n" "test.cpp:5:note:Assuming 'mInstance' is used as 'this'\n" "test.cpp:7:note:Delete 'mInstance', invalidating 'this'\n" "test.cpp:3:note:Call method when 'this' is invalid\n", errout.str()); // Use member.. checkThisUseAfterFree("class C {\n" "public:\n" " void dostuff() { delete self; x = 123; }\n" "private:\n" " static C *self;\n" " int x;\n" "};"); ASSERT_EQUALS("test.cpp:3:warning:Using member 'x' when 'this' might be invalid\n" "test.cpp:5:note:Assuming 'self' is used as 'this'\n" "test.cpp:3:note:Delete 'self', invalidating 'this'\n" "test.cpp:3:note:Call method when 'this' is invalid\n", errout.str()); checkThisUseAfterFree("class C {\n" "public:\n" " void dostuff() { delete self; x[1] = 123; }\n" "private:\n" " static C *self;\n" " std::map x;\n" "};"); ASSERT_EQUALS("test.cpp:3:warning:Using member 'x' when 'this' might be invalid\n" "test.cpp:5:note:Assuming 'self' is used as 'this'\n" "test.cpp:3:note:Delete 'self', invalidating 'this'\n" "test.cpp:3:note:Call method when 'this' is invalid\n", errout.str()); // Assign 'shared_from_this()' to non-static smart pointer checkThisUseAfterFree("class C {\n" "public:\n" " void hold() { mInstance = shared_from_this(); }\n" " void dostuff() { mInstance.reset(); hello(); }\n" "private:\n" " std::shared_ptr mInstance;\n" " void hello() {}\n" "};"); ASSERT_EQUALS("test.cpp:4:warning:Calling method 'hello()' when 'this' might be invalid\n" "test.cpp:6:note:Assuming 'mInstance' is used as 'this'\n" "test.cpp:4:note:Delete 'mInstance', invalidating 'this'\n" "test.cpp:4:note:Call method when 'this' is invalid\n", errout.str()); // Avoid FP.. checkThisUseAfterFree("class C {\n" "public:\n" " void dostuff() { delete self; x = 123; }\n" "private:\n" " C *self;\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkThisUseAfterFree("class C {\n" "public:\n" " void hold() { mInstance = shared_from_this(); }\n" " void dostuff() { if (x) { mInstance.reset(); return; } hello(); }\n" "private:\n" " std::shared_ptr mInstance;\n" " void hello() {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkThisUseAfterFree("class C\n" "{\n" "public:\n" " explicit C(const QString& path) : mPath( path ) {}\n" "\n" " static void initialize(const QString& path) {\n" // <- avoid fp in static method " if (instanceSingleton)\n" " delete instanceSingleton;\n" " instanceSingleton = new C(path);\n" " }\n" "private:\n" " static C* instanceSingleton;\n" "};\n" "\n" "C* C::instanceSingleton;"); ASSERT_EQUALS("", errout.str()); // Avoid false positive when pointer is deleted in lambda checkThisUseAfterFree("class C {\n" "public:\n" " void foo();\n" " void set() { p = this; }\n" " void dostuff() {}\n" " C* p;\n" "};\n" "\n" "void C::foo() {\n" " auto done = [this] () { delete p; };\n" " dostuff();\n" " done();\n" "}"); ASSERT_EQUALS("", errout.str()); } void ctu(const std::vector &code) { Settings settings; CheckClass check; // getFileInfo std::list fileInfo; for (const std::string& c: code) { Tokenizer tokenizer(&settings, this); std::istringstream istr(c); ASSERT(tokenizer.tokenize(istr, (std::to_string(fileInfo.size()) + ".cpp").c_str())); fileInfo.push_back(check.getFileInfo(&tokenizer, &settings)); } // Check code.. errout.str(""); check.analyseWholeProgram(nullptr, fileInfo, settings, *this); while (!fileInfo.empty()) { delete fileInfo.back(); fileInfo.pop_back(); } } void ctuOneDefinitionRule() { ctu({"class C { C() { std::cout << 0; } };", "class C { C() { std::cout << 1; } };"}); ASSERT_EQUALS("[1.cpp:1] -> [0.cpp:1]: (error) The one definition rule is violated, different classes/structs have the same name 'C'\n", errout.str()); ctu({"class C { C(); }; C::C() { std::cout << 0; }", "class C { C(); }; C::C() { std::cout << 1; }"}); ASSERT_EQUALS("[1.cpp:1] -> [0.cpp:1]: (error) The one definition rule is violated, different classes/structs have the same name 'C'\n", errout.str()); ctu({"class C { C() {} };\n", "class C { C() {} };\n"}); ASSERT_EQUALS("", errout.str()); ctu({"class C { C(); }; C::C(){}", "class C { C(); }; C::C(){}"}); ASSERT_EQUALS("", errout.str()); ctu({"class A::C { C() { std::cout << 0; } };", "class B::C { C() { std::cout << 1; } };"}); ASSERT_EQUALS("", errout.str()); } #define getFileInfo(code) getFileInfo_(code, __FILE__, __LINE__) void getFileInfo_(const char code[], const char* file, int line) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); Check::FileInfo * fileInfo = (checkClass.getFileInfo)(&tokenizer, &settings1); delete fileInfo; } void testGetFileInfo() { getFileInfo("void foo() { union { struct { }; }; }"); // don't crash getFileInfo("struct sometype { sometype(); }; sometype::sometype() = delete;"); // don't crash } }; REGISTER_TEST(TestClass) cppcheck-2.7/test/testcmdlineparser.cpp000066400000000000000000001137601417746362400204030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cmdlineparser.h" #include "config.h" #include "errortypes.h" #include "platform.h" #include "redirect.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include "testsuite.h" #include "timer.h" #include #include #include #include class TestCmdlineParser : public TestFixture { public: TestCmdlineParser() : TestFixture("TestCmdlineParser") , defParser(&settings) {} private: Settings settings; CmdLineParser defParser; void run() OVERRIDE { TEST_CASE(nooptions); TEST_CASE(helpshort); TEST_CASE(helplong); TEST_CASE(showversion); TEST_CASE(onefile); TEST_CASE(onepath); TEST_CASE(optionwithoutfile); TEST_CASE(verboseshort); TEST_CASE(verboselong); TEST_CASE(debugSimplified); TEST_CASE(debugwarnings); TEST_CASE(forceshort); TEST_CASE(forcelong); TEST_CASE(relativePaths); TEST_CASE(quietshort); TEST_CASE(quietlong); TEST_CASE(defines_noarg); TEST_CASE(defines_noarg2); TEST_CASE(defines_noarg3); TEST_CASE(defines); TEST_CASE(defines2); TEST_CASE(defines3); TEST_CASE(defines4); TEST_CASE(enforceLanguage); TEST_CASE(includesnopath); TEST_CASE(includes); TEST_CASE(includesslash); TEST_CASE(includesbackslash); TEST_CASE(includesnospace); TEST_CASE(includes2); TEST_CASE(includesFile); TEST_CASE(configExcludesFile); TEST_CASE(enabledAll); TEST_CASE(enabledStyle); TEST_CASE(enabledPerformance); TEST_CASE(enabledPortability); TEST_CASE(enabledUnusedFunction); TEST_CASE(enabledMissingInclude); #ifdef CHECK_INTERNAL TEST_CASE(enabledInternal); #endif TEST_CASE(enabledMultiple); TEST_CASE(inconclusive); TEST_CASE(errorExitcode); TEST_CASE(errorExitcodeMissing); TEST_CASE(errorExitcodeStr); TEST_CASE(exitcodeSuppressionsOld); // TODO: Create and test real suppression file TEST_CASE(exitcodeSuppressions); TEST_CASE(exitcodeSuppressionsNoFile); TEST_CASE(fileList); // TODO: Create and test real file listing file // TEST_CASE(fileListStdin); // Disabled since hangs the test run TEST_CASE(inlineSuppr); TEST_CASE(jobs); TEST_CASE(jobsMissingCount); TEST_CASE(jobsInvalid); TEST_CASE(maxConfigs); TEST_CASE(maxConfigsMissingCount); TEST_CASE(maxConfigsInvalid); TEST_CASE(maxConfigsTooSmall); TEST_CASE(reportProgressTest); // "Test" suffix to avoid hiding the parent's reportProgress TEST_CASE(stdc99); TEST_CASE(stdcpp11); TEST_CASE(platform); TEST_CASE(plistEmpty); TEST_CASE(plistDoesNotExist); TEST_CASE(suppressionsOld); // TODO: Create and test real suppression file TEST_CASE(suppressions); TEST_CASE(suppressionsNoFile); TEST_CASE(suppressionSingle); TEST_CASE(suppressionSingleFile); TEST_CASE(suppressionTwo); TEST_CASE(suppressionTwoSeparate); TEST_CASE(templates); TEST_CASE(templatesGcc); TEST_CASE(templatesVs); TEST_CASE(templatesEdit); TEST_CASE(xml); TEST_CASE(xmlver2); TEST_CASE(xmlver2both); TEST_CASE(xmlver2both2); TEST_CASE(xmlverunknown); TEST_CASE(xmlverinvalid); TEST_CASE(doc); TEST_CASE(showtime); TEST_CASE(errorlist1); TEST_CASE(errorlistverbose1); TEST_CASE(errorlistverbose2); TEST_CASE(ignorepathsnopath); // TODO // Disabling these tests since they use relative paths to the // testrunner executable. //TEST_CASE(ignorepaths1); //TEST_CASE(ignorepaths2); //TEST_CASE(ignorepaths3); //TEST_CASE(ignorepaths4); //TEST_CASE(ignorefilepaths1); //TEST_CASE(ignorefilepaths2); TEST_CASE(checkconfig); TEST_CASE(unknownParam); TEST_CASE(undefs_noarg); TEST_CASE(undefs_noarg2); TEST_CASE(undefs_noarg3); TEST_CASE(undefs); TEST_CASE(undefs2); } void nooptions() { REDIRECT; const char * const argv[] = {"cppcheck"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(1, argv)); ASSERT_EQUALS(true, parser.getShowHelp()); } void helpshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-h"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.getShowHelp()); } void helplong() { REDIRECT; const char * const argv[] = {"cppcheck", "--help"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.getShowHelp()); } void showversion() { REDIRECT; const char * const argv[] = {"cppcheck", "--version"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.getShowVersion()); } void onefile() { REDIRECT; const char * const argv[] = {"cppcheck", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(1, (int)parser.getPathNames().size()); ASSERT_EQUALS("file.cpp", parser.getPathNames().at(0)); } void onepath() { REDIRECT; const char * const argv[] = {"cppcheck", "src"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(1, (int)parser.getPathNames().size()); ASSERT_EQUALS("src", parser.getPathNames().at(0)); } void optionwithoutfile() { REDIRECT; const char * const argv[] = {"cppcheck", "-v"}; CmdLineParser parser(&settings); ASSERT_EQUALS(false, parser.parseFromArgs(2, argv)); ASSERT_EQUALS(0, (int)parser.getPathNames().size()); } void verboseshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-v", "file.cpp"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.verbose); } void verboselong() { REDIRECT; const char * const argv[] = {"cppcheck", "--verbose", "file.cpp"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.verbose); } void debugSimplified() { REDIRECT; const char * const argv[] = {"cppcheck", "--debug-simplified", "file.cpp"}; settings.debugSimplified = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.debugSimplified); } void debugwarnings() { REDIRECT; const char * const argv[] = {"cppcheck", "--debug-warnings", "file.cpp"}; settings.debugwarnings = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.debugwarnings); } void forceshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-f", "file.cpp"}; settings.force = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.force); } void forcelong() { REDIRECT; const char * const argv[] = {"cppcheck", "--force", "file.cpp"}; settings.force = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.force); } void relativePaths() { REDIRECT; settings.relativePaths = false; const char * const argvs[] = {"cppcheck", "-rp", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvs)); ASSERT_EQUALS(true, settings.relativePaths); settings.relativePaths = false; const char * const argvl[] = {"cppcheck", "--relative-paths", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvl)); ASSERT_EQUALS(true, settings.relativePaths); settings.relativePaths = false; settings.basePaths.clear(); const char * const argvsp[] = {"cppcheck", "-rp=C:/foo;C:\\bar", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvsp)); ASSERT_EQUALS(true, settings.relativePaths); ASSERT_EQUALS(2, settings.basePaths.size()); ASSERT_EQUALS("C:/foo", settings.basePaths[0]); ASSERT_EQUALS("C:/bar", settings.basePaths[1]); settings.relativePaths = false; settings.basePaths.clear(); const char * const argvlp[] = {"cppcheck", "--relative-paths=C:/foo;C:\\bar", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvlp)); ASSERT_EQUALS(true, settings.relativePaths); ASSERT_EQUALS(2, settings.basePaths.size()); ASSERT_EQUALS("C:/foo", settings.basePaths[0]); ASSERT_EQUALS("C:/bar", settings.basePaths[1]); } void quietshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-q", "file.cpp"}; settings.quiet = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.quiet); } void quietlong() { REDIRECT; const char * const argv[] = {"cppcheck", "--quiet", "file.cpp"}; settings.quiet = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.quiet); } void defines_noarg() { REDIRECT; const char * const argv[] = {"cppcheck", "-D"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.parseFromArgs(2, argv)); } void defines_noarg2() { REDIRECT; const char * const argv[] = {"cppcheck", "-D", "-v", "file.cpp"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void defines_noarg3() { REDIRECT; const char * const argv[] = {"cppcheck", "-D", "--quiet", "file.cpp"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void defines() { REDIRECT; const char * const argv[] = {"cppcheck", "-D_WIN32", "file.cpp"}; settings.userDefines.clear(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS("_WIN32=1", settings.userDefines); } void defines2() { REDIRECT; const char * const argv[] = {"cppcheck", "-D_WIN32", "-DNODEBUG", "file.cpp"}; settings.userDefines.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("_WIN32=1;NODEBUG=1", settings.userDefines); } void defines3() { REDIRECT; const char * const argv[] = {"cppcheck", "-D", "DEBUG", "file.cpp"}; settings.userDefines.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("DEBUG=1", settings.userDefines); } void defines4() { REDIRECT; const char * const argv[] = {"cppcheck", "-DDEBUG=", "file.cpp"}; // #5137 - defining empty macro settings.userDefines.clear(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS("DEBUG=", settings.userDefines); } void enforceLanguage() { REDIRECT; { const char * const argv[] = {"cppcheck", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT_EQUALS(Settings::None, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "-x", "c++", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(Settings::CPP, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "-x"}; ASSERT(!defParser.parseFromArgs(2, argv)); } { const char * const argv[] = {"cppcheck", "-x", "--inconclusive", "file.cpp"}; ASSERT(!defParser.parseFromArgs(4, argv)); } { const char * const argv[] = {"cppcheck", "--language=c++", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(Settings::CPP, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "--language=c", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(Settings::C, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "--language=unknownLanguage", "file.cpp"}; ASSERT(!defParser.parseFromArgs(3, argv)); } } void includesnopath() { REDIRECT; const char * const argv[] = {"cppcheck", "-I"}; // Fails since -I has no param ASSERT_EQUALS(false, defParser.parseFromArgs(2, argv)); } void includes() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includesslash() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include/", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includesbackslash() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include\\", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includesnospace() { REDIRECT; const char * const argv[] = {"cppcheck", "-Iinclude", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includes2() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include/", "-I", "framework/", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(6, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); settings.includePaths.pop_front(); ASSERT_EQUALS("framework/", settings.includePaths.front()); } void includesFile() { REDIRECT; const char * const argv[] = {"cppcheck", "--includes-file=fileThatDoesNotExist.txt", "file.cpp"}; settings.includePaths.clear(); ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void configExcludesFile() { REDIRECT; const char * const argv[] = {"cppcheck", "--config-excludes-file=fileThatDoesNotExist.txt", "file.cpp"}; settings.includePaths.clear(); ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void enabledAll() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=all", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.severity.isEnabled(Severity::style)); ASSERT(settings.severity.isEnabled(Severity::warning)); ASSERT(settings.checks.isEnabled(Checks::unusedFunction)); ASSERT(settings.checks.isEnabled(Checks::missingInclude)); ASSERT(!settings.checks.isEnabled(Checks::internalCheck)); } void enabledStyle() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=style", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.severity.isEnabled(Severity::style)); ASSERT(settings.severity.isEnabled(Severity::warning)); ASSERT(settings.severity.isEnabled(Severity::performance)); ASSERT(settings.severity.isEnabled(Severity::portability)); ASSERT(!settings.checks.isEnabled(Checks::unusedFunction)); ASSERT(!settings.checks.isEnabled(Checks::internalCheck)); } void enabledPerformance() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=performance", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(!settings.severity.isEnabled(Severity::style)); ASSERT(!settings.severity.isEnabled(Severity::warning)); ASSERT(settings.severity.isEnabled(Severity::performance)); ASSERT(!settings.severity.isEnabled(Severity::portability)); ASSERT(!settings.checks.isEnabled(Checks::unusedFunction)); ASSERT(!settings.checks.isEnabled(Checks::missingInclude)); } void enabledPortability() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=portability", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(!settings.severity.isEnabled(Severity::style)); ASSERT(!settings.severity.isEnabled(Severity::warning)); ASSERT(!settings.severity.isEnabled(Severity::performance)); ASSERT(settings.severity.isEnabled(Severity::portability)); ASSERT(!settings.checks.isEnabled(Checks::unusedFunction)); ASSERT(!settings.checks.isEnabled(Checks::missingInclude)); } void enabledUnusedFunction() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=unusedFunction", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.checks.isEnabled(Checks::unusedFunction)); } void enabledMissingInclude() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=missingInclude", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.checks.isEnabled(Checks::missingInclude)); } #ifdef CHECK_INTERNAL void enabledInternal() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=internal", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.checks.isEnabled(Checks::internalCheck)); } #endif void enabledMultiple() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=missingInclude,portability,warning", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(!settings.severity.isEnabled(Severity::style)); ASSERT(settings.severity.isEnabled(Severity::warning)); ASSERT(!settings.severity.isEnabled(Severity::performance)); ASSERT(settings.severity.isEnabled(Severity::portability)); ASSERT(!settings.checks.isEnabled(Checks::unusedFunction)); ASSERT(settings.checks.isEnabled(Checks::missingInclude)); } void inconclusive() { REDIRECT; const char * const argv[] = {"cppcheck", "--inconclusive"}; settings.certainty.clear(); ASSERT(defParser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, settings.certainty.isEnabled(Certainty::inconclusive)); } void errorExitcode() { REDIRECT; const char * const argv[] = {"cppcheck", "--error-exitcode=5", "file.cpp"}; settings.exitCode = 0; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(5, settings.exitCode); } void errorExitcodeMissing() { REDIRECT; const char * const argv[] = {"cppcheck", "--error-exitcode=", "file.cpp"}; settings.exitCode = 0; // Fails since exit code not given ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void errorExitcodeStr() { REDIRECT; const char * const argv[] = {"cppcheck", "--error-exitcode=foo", "file.cpp"}; settings.exitCode = 0; // Fails since invalid exit code ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void exitcodeSuppressionsOld() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--exitcode-suppressions", "suppr.txt", "file.cpp"}; settings.exitCode = 0; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(4, argv)); } void exitcodeSuppressions() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--exitcode-suppressions=suppr.txt", "file.cpp"}; settings.exitCode = 0; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } void exitcodeSuppressionsNoFile() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--exitcode-suppressions", "file.cpp"}; settings.exitCode = 0; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } void fileList() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--file-list", "files.txt", "file.cpp"}; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(4, argv)); } /* void fileListStdin() { // TODO: Give it some stdin to read from, fails because the list of // files in stdin (_pathnames) is empty REDIRECT; const char * const argv[] = {"cppcheck", "--file-list=-", "file.cpp"}; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } */ void inlineSuppr() { REDIRECT; const char * const argv[] = {"cppcheck", "--inline-suppr", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argv)); } void jobs() { REDIRECT; const char * const argv[] = {"cppcheck", "-j", "3", "file.cpp"}; settings.jobs = 0; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(3, settings.jobs); } void jobsMissingCount() { REDIRECT; const char * const argv[] = {"cppcheck", "-j", "file.cpp"}; settings.jobs = 0; // Fails since -j is missing thread count ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void jobsInvalid() { REDIRECT; const char * const argv[] = {"cppcheck", "-j", "e", "file.cpp"}; settings.jobs = 0; // Fails since invalid count given for -j ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void maxConfigs() { REDIRECT; const char * const argv[] = {"cppcheck", "-f", "--max-configs=12", "file.cpp"}; settings.force = false; settings.maxConfigs = 12; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(12, settings.maxConfigs); ASSERT_EQUALS(false, settings.force); } void maxConfigsMissingCount() { REDIRECT; const char * const argv[] = {"cppcheck", "--max-configs=", "file.cpp"}; // Fails since --max-configs= is missing limit ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void maxConfigsInvalid() { REDIRECT; const char * const argv[] = {"cppcheck", "--max-configs=e", "file.cpp"}; // Fails since invalid count given for --max-configs= ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void maxConfigsTooSmall() { REDIRECT; const char * const argv[] = {"cppcheck", "--max-configs=0", "file.cpp"}; // Fails since limit must be greater than 0 ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void reportProgressTest() { REDIRECT; const char * const argv[] = {"cppcheck", "--report-progress", "file.cpp"}; settings.reportProgress = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.reportProgress); } void stdc99() { REDIRECT; const char * const argv[] = {"cppcheck", "--std=c99", "file.cpp"}; settings.standards.c = Standards::C89; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.standards.c == Standards::C99); } void stdcpp11() { REDIRECT; const char * const argv[] = {"cppcheck", "--std=c++11", "file.cpp"}; settings.standards.cpp = Standards::CPP03; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.standards.cpp == Standards::CPP11); } void platform() { REDIRECT; const char * const argv[] = {"cppcheck", "--platform=win64", "file.cpp"}; settings.platform(Settings::Unspecified); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.platformType == Settings::Win64); } void plistEmpty() { REDIRECT; const char * const argv[] = {"cppcheck", "--plist-output=", "file.cpp"}; settings.plistOutput = ""; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.plistOutput == "./"); } void plistDoesNotExist() { REDIRECT; const char * const argv[] = {"cppcheck", "--plist-output=./cppcheck_reports", "file.cpp"}; settings.plistOutput = ""; // Fails since folder pointed by --plist-output= does not exist ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void suppressionsOld() { // TODO: Fails because there is no suppr.txt file! REDIRECT; const char * const argv[] = {"cppcheck", "--suppressions", "suppr.txt", "file.cpp"}; ASSERT(!defParser.parseFromArgs(4, argv)); } void suppressions() { // TODO: Fails because there is no suppr.txt file! REDIRECT; const char * const argv[] = {"cppcheck", "--suppressions-list=suppr.txt", "file.cpp"}; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } void suppressionsNoFile() { REDIRECT; { CLEAR_REDIRECT_OUTPUT; const char * const argv[] = {"cppcheck", "--suppressions-list=", "file.cpp"}; ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(false, GET_REDIRECT_OUTPUT.find("If you want to pass two files") != std::string::npos); } { CLEAR_REDIRECT_OUTPUT; const char * const argv[] = {"cppcheck", "--suppressions-list=a.suppr,b.suppr", "file.cpp"}; ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, GET_REDIRECT_OUTPUT.find("If you want to pass two files") != std::string::npos); } { CLEAR_REDIRECT_OUTPUT; const char * const argv[] = {"cppcheck", "--suppressions-list=a.suppr b.suppr", "file.cpp"}; ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, GET_REDIRECT_OUTPUT.find("If you want to pass two files") != std::string::npos); } } static Suppressions::ErrorMessage errorMessage(const std::string &errorId, const std::string &fileName, int lineNumber) { Suppressions::ErrorMessage e; e.errorId = errorId; e.setFileName(fileName); e.lineNumber = lineNumber; return e; } void suppressionSingle() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1))); } void suppressionSingleFile() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar:file.cpp", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); } void suppressionTwo() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar,noConstructor", "file.cpp"}; settings = Settings(); TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U))); } void suppressionTwoSeparate() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar", "--suppress=noConstructor", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U))); } void templates() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "{file}:{line},{severity},{id},{message}", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{file}:{line},{severity},{id},{message}", settings.templateFormat); } void templatesGcc() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "gcc", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{bold}{file}:{line}:{column}: {magenta}warning:{default} {message} [{id}]{reset}\\n{code}", settings.templateFormat); } void templatesVs() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "vs", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{file}({line}): {severity}: {message}", settings.templateFormat); } void templatesEdit() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "edit", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{file} +{line}: {severity}: {message}", settings.templateFormat); } void xml() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.xml); ASSERT_EQUALS(1, settings.xml_version); } void xmlver2() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml-version=2", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.xml); ASSERT_EQUALS(2, settings.xml_version); } void xmlver2both() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "--xml-version=2", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT(settings.xml); ASSERT_EQUALS(2, settings.xml_version); } void xmlver2both2() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml-version=2", "--xml", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT(settings.xml); ASSERT_EQUALS(2, settings.xml_version); } void xmlverunknown() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "--xml-version=3", "file.cpp"}; // FAils since unknown XML format version ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void xmlverinvalid() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "--xml-version=a", "file.cpp"}; // FAils since unknown XML format version ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void doc() { REDIRECT; const char * const argv[] = {"cppcheck", "--doc"}; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT(defParser.exitAfterPrinting()); } void showtime() { REDIRECT; const char * const argv[] = {"cppcheck", "--showtime=summary"}; settings.showtime = SHOWTIME_MODES::SHOWTIME_NONE; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT(settings.showtime == SHOWTIME_MODES::SHOWTIME_SUMMARY); } void errorlist1() { REDIRECT; const char * const argv[] = {"cppcheck", "--errorlist"}; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT(defParser.getShowErrorMessages()); } void errorlistverbose1() { REDIRECT; const char * const argv[] = {"cppcheck", "--verbose", "--errorlist"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.verbose); } void errorlistverbose2() { REDIRECT; const char * const argv[] = {"cppcheck", "--errorlist", "--verbose"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.verbose); } void ignorepathsnopath() { REDIRECT; const char * const argv[] = {"cppcheck", "-i"}; CmdLineParser parser(&settings); // Fails since no ignored path given ASSERT_EQUALS(false, parser.parseFromArgs(2, argv)); ASSERT_EQUALS(0, parser.getIgnoredPaths().size()); } /* void ignorepaths1() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); } void ignorepaths2() { REDIRECT; const char * const argv[] = {"cppcheck", "-i", "src", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(4, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); } void ignorepaths3() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc", "-imodule", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(4, argv)); ASSERT_EQUALS(2, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); ASSERT_EQUALS("module/", parser.getIgnoredPaths()[1]); } void ignorepaths4() { REDIRECT; const char * const argv[] = {"cppcheck", "-i", "src", "-i", "module", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(6, argv)); ASSERT_EQUALS(2, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); ASSERT_EQUALS("module/", parser.getIgnoredPaths()[1]); } void ignorefilepaths1() { REDIRECT; const char * const argv[] = {"cppcheck", "-ifoo.cpp", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("foo.cpp", parser.getIgnoredPaths()[0]); } void ignorefilepaths2() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc/foo.cpp", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/foo.cpp", parser.getIgnoredPaths()[0]); } */ void checkconfig() { REDIRECT; const char * const argv[] = {"cppcheck", "--check-config", "file.cpp"}; settings.checkConfiguration = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.checkConfiguration); } void unknownParam() { REDIRECT; const char * const argv[] = {"cppcheck", "--foo", "file.cpp"}; ASSERT(!defParser.parseFromArgs(3, argv)); } void undefs() { REDIRECT; const char * const argv[] = {"cppcheck", "-U_WIN32", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, settings.userUndefs.size()); ASSERT(settings.userUndefs.find("_WIN32") != settings.userUndefs.end()); } void undefs2() { REDIRECT; const char * const argv[] = {"cppcheck", "-U_WIN32", "-UNODEBUG", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(2, settings.userUndefs.size()); ASSERT(settings.userUndefs.find("_WIN32") != settings.userUndefs.end()); ASSERT(settings.userUndefs.find("NODEBUG") != settings.userUndefs.end()); } void undefs_noarg() { REDIRECT; const char * const argv[] = {"cppcheck", "-U"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.parseFromArgs(2, argv)); } void undefs_noarg2() { REDIRECT; const char * const argv[] = {"cppcheck", "-U", "-v", "file.cpp"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void undefs_noarg3() { REDIRECT; const char * const argv[] = {"cppcheck", "-U", "--quiet", "file.cpp"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } }; REGISTER_TEST(TestCmdlineParser) cppcheck-2.7/test/testcondition.cpp000066400000000000000000005557301417746362400175500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkcondition.h" #include "config.h" #include "errortypes.h" #include "library.h" #include "platform.h" #include "preprocessor.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include #include #include class TestCondition : public TestFixture { public: TestCondition() : TestFixture("TestCondition") {} private: Settings settings0; Settings settings1; void run() OVERRIDE { // known platform.. settings0.platform(cppcheck::Platform::PlatformType::Native); settings1.platform(cppcheck::Platform::PlatformType::Native); LOAD_LIB_2(settings0.library, "qt.cfg"); LOAD_LIB_2(settings0.library, "std.cfg"); settings0.severity.enable(Severity::style); settings0.severity.enable(Severity::warning); const char cfg[] = "\n" "\n" " \n" ""; tinyxml2::XMLDocument xmldoc; xmldoc.Parse(cfg, sizeof(cfg)); settings1.severity.enable(Severity::style); settings1.severity.enable(Severity::warning); settings1.library.load(xmldoc); TEST_CASE(assignAndCompare); // assignment and comparison don't match TEST_CASE(mismatchingBitAnd); // overlapping bitmasks TEST_CASE(comparison); // CheckCondition::comparison test cases TEST_CASE(multicompare); // mismatching comparisons TEST_CASE(overlappingElseIfCondition); // overlapping conditions in if and else-if TEST_CASE(oppositeElseIfCondition); // opposite conditions in if and else-if TEST_CASE(checkBadBitmaskCheck); TEST_CASE(incorrectLogicOperator1); TEST_CASE(incorrectLogicOperator2); TEST_CASE(incorrectLogicOperator3); TEST_CASE(incorrectLogicOperator4); TEST_CASE(incorrectLogicOperator5); // complex expressions TEST_CASE(incorrectLogicOperator6); // char literals TEST_CASE(incorrectLogicOperator7); // opposite expressions: (expr || !expr) TEST_CASE(incorrectLogicOperator8); // ! TEST_CASE(incorrectLogicOperator9); TEST_CASE(incorrectLogicOperator10); // enum TEST_CASE(incorrectLogicOperator11); TEST_CASE(incorrectLogicOperator12); TEST_CASE(incorrectLogicOperator13); TEST_CASE(incorrectLogicOperator14); TEST_CASE(incorrectLogicOperator15); TEST_CASE(incorrectLogicOperator16); // #10070 TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); TEST_CASE(incorrectLogicOp_condSwapping); TEST_CASE(testBug5895); TEST_CASE(testBug5309); TEST_CASE(modulo); TEST_CASE(oppositeInnerCondition); TEST_CASE(oppositeInnerConditionPointers); TEST_CASE(oppositeInnerConditionClass); TEST_CASE(oppositeInnerConditionUndeclaredVariable); TEST_CASE(oppositeInnerConditionAlias); TEST_CASE(oppositeInnerCondition2); TEST_CASE(oppositeInnerCondition3); TEST_CASE(oppositeInnerConditionAnd); TEST_CASE(oppositeInnerConditionEmpty); TEST_CASE(oppositeInnerConditionFollowVar); TEST_CASE(identicalInnerCondition); TEST_CASE(identicalConditionAfterEarlyExit); TEST_CASE(innerConditionModified); TEST_CASE(clarifyCondition1); // if (a = b() < 0) TEST_CASE(clarifyCondition2); // if (a & b == c) TEST_CASE(clarifyCondition3); // if (! a & b) TEST_CASE(clarifyCondition4); // ticket #3110 TEST_CASE(clarifyCondition5); // #3609 CWinTraits.. TEST_CASE(clarifyCondition6); // #3818 TEST_CASE(clarifyCondition7); TEST_CASE(clarifyCondition8); TEST_CASE(alwaysTrue); TEST_CASE(alwaysTrueSymbolic); TEST_CASE(alwaysTrueInfer); TEST_CASE(alwaysTrueContainer); TEST_CASE(alwaysTrueLoop); TEST_CASE(alwaysTrueTryCatch); TEST_CASE(multiConditionAlwaysTrue); TEST_CASE(duplicateCondition); TEST_CASE(checkInvalidTestForOverflow); TEST_CASE(checkConditionIsAlwaysTrueOrFalseInsideIfWhile); TEST_CASE(alwaysTrueFalseInLogicalOperators); TEST_CASE(pointerAdditionResultNotNull); TEST_CASE(duplicateConditionalAssign); TEST_CASE(checkAssignmentInCondition); TEST_CASE(compareOutOfTypeRange); TEST_CASE(knownConditionCast); // #9976 TEST_CASE(knownConditionIncrementLoop); // #9808 } void check(const char code[], Settings *settings, const char* filename = "test.cpp") { // Clear the error buffer.. errout.str(""); // Raw tokens.. std::vector files(1, filename); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); Preprocessor preprocessor(*settings, nullptr); preprocessor.setDirectives(tokens1); // Tokenizer.. Tokenizer tokenizer(settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); tokenizer.setPreprocessor(&preprocessor); // Run checks.. CheckCondition checkCondition; checkCondition.runChecks(&tokenizer, settings, this); } void check(const char code[], const char* filename = "test.cpp", bool inconclusive = false) { settings0.certainty.setEnabled(Certainty::inconclusive, inconclusive); check(code, &settings0, filename); } void assignAndCompare() { // & check("void foo(int x)\n" "{\n" " int y = x & 4;\n" " if (y == 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void foo(int x)\n" "{\n" " int y = x & 4;\n" " if (y != 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y!=3' is always true.\n", errout.str()); // | check("void foo(int x) {\n" " int y = x | 0x14;\n" " if (y == 0x710);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==0x710' is always false.\n", errout.str()); check("void foo(int x) {\n" " int y = x | 0x14;\n" " if (y == 0x71f);\n" "}"); ASSERT_EQUALS("", errout.str()); // various simple assignments check("void foo(int x) {\n" " int y = (x+1) | 1;\n" " if (y == 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout.str()); check("void foo() {\n" " int y = 1 | x();\n" " if (y == 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout.str()); // multiple conditions check("void foo(int x) {\n" " int y = x & 4;\n" " if ((y == 3) && (z == 1));\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void foo(int x) {\n" " int y = x & 4;\n" " if ((x==123) || ((y == 3) && (z == 1)));\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void f(int x) {\n" " int y = x & 7;\n" " if (setvalue(&y) && y != 8);\n" "}"); ASSERT_EQUALS("", errout.str()); // recursive checking into scopes check("void f(int x) {\n" " int y = x & 7;\n" " if (z) y=0;\n" " else { if (y==8); }\n" // always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); // while check("void f(int x) {\n" " int y = x & 7;\n" " while (y==8);\n" // local variable => always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " extern int y; y = x & 7;\n" " while (y==8);\n" // non-local variable => no error "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) y--;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int x);\n" "void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) g(y);\n" " }\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:6]: (style) Mismatching assignment and comparison, comparison 'y!=0' is always true.\n", errout.str()); check("void g(int &x);\n" "void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) g(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // calling function check("void f(int x) {\n" " int y = x & 7;\n" " do_something();\n" " if (y==8);\n" // local variable => always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " int y = x & 7;\n" " do_something(&y);\n" // passing variable => no error " if (y==8);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void do_something(int);\n" "void f(int x) {\n" " int y = x & 7;\n" " do_something(y);\n" " if (y==8);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " extern int y; y = x & 7;\n" " do_something();\n" " if (y==8);\n" // non-local variable => no error "}"); ASSERT_EQUALS("", errout.str()); // #4434 : false positive: ?: check("void f(int x) {\n" " x = x & 1;\n" " x = x & 1 ? 1 : -1;\n" " if(x != -1) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #4735 check("void f() {\n" " int x = *(char*)&0x12345678;\n" " if (x==18) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: no variable info check("void foo(int x) {\n" " y = 2 | x;\n" // y not declared => no error " if(y == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: negative number check("void foo(int x) {\n" " int y = -2 | x;\n" // negative number => no error " if (y==1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: pass variable to function check("void foo(int x) {\n" " int y = 2 | x;\n" " bar(&y);\n" // pass variable to function => no error " if (y==1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // no crash on unary operator& (#5643) check("SdrObject* ApplyGraphicToObject() {\n" " if (&rHitObject) {}\n" " else if (rHitObject.IsClosedObj() && !&rHitObject) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #5695: increment check("void f(int a0, int n) {\n" " int c = a0 & 3;\n" " for (int a = 0; a < n; a++) {\n" " c++;\n" " if (c == 4)\n" " c = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n" // #6662 " int x = a & 1;\n" " while (x <= 4) {\n" " if (x != 5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'x!=5' is always true.\n", errout.str()); check("void f(int a) {\n" // #6662 " int x = a & 1;\n" " while ((x += 4) < 10) {\n" " if (x != 5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int x);\n" "void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'x' is always true\n", errout.str()); check("void g(int & x);\n" "void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatchingBitAnd() { check("void f(int a) {\n" " int b = a & 0xf0;\n" " b &= 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout.str()); check("void f(int a) {\n" " int b = a & 0xf0;\n" " int c = b & 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout.str()); check("void f(int a) {\n" " int b = a;" " switch (x) {\n" " case 1: b &= 1; break;\n" " case 2: b &= 2; break;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparison() { // CheckCondition::comparison test cases // '==' check("void f(int a) {\n assert( (a & 0x07) == 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) == 0x8' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & b & 4 & c ) == 3 );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout.str()); check("void f(int a) {\n assert( (a | 0x07) == 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) == 0x8' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) == 7U );\n}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n assert( (a | 0x01) == -15 );\n}"); ASSERT_EQUALS("", errout.str()); // '!=' check("void f(int a) {\n assert( (a & 0x07) != 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) != 0x8' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a | 0x07) != 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) != 0x8' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) != 7U );\n}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n assert( (a | 0x07) != 7U );\n}"); ASSERT_EQUALS("", errout.str()); // '>=' check("void f(int a) {\n assert( (a & 0x07) >= 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) >= 0x8' is always false.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x7) >= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) >= 0x7' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) >= 7U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) >= 8U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '>' check("void f(int a) {\n assert( (a & 0x07) > 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) > 0x7' is always false.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x7) > 6U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) > 0x6' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) > 6U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) > 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '<=' check("void f(int a) {\n assert( (a & 0x07) <= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) <= 0x7' is always true.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x08) <= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x8) <= 0x7' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) <= 6U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x08) <= 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '<' check("void f(int a) {\n assert( (a & 0x07) < 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) < 0x8' is always true.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x07) < 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) < 0x7' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) < 3U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) < 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' } #define checkPureFunction(code) checkPureFunction_(code, __FILE__, __LINE__) void multicompare() { check("void foo(int x)\n" "{\n" " if (x & 7);\n" " else { if (x == 1); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("void foo(int x)\n" "{\n" " if (x & 7);\n" " else { if (x & 1); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("extern int bar() __attribute__((pure));\n" "void foo(int x)\n" "{\n" " if ( bar() >1 && b) {}\n" " else if (bar() >1 && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout.str()); checkPureFunction("extern int bar();\n" "void foo(int x)\n" "{\n" " if ( bar() >1 && b) {}\n" " else if (bar() >1 && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout.str()); // 7284 check("void foo() {\n" " if (a) {}\n" " else if (!!a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); } void checkPureFunction_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckCondition checkCondition; checkCondition.runChecks(&tokenizer, &settings1, this); } void overlappingElseIfCondition() { check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 1) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 2) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a++) { b = 1; }\n" " else { if (a++) { b = 2; }\n" " else { if (a++) { b = 3; } } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int &b) {\n" " if (!strtok(NULL, \" \")) { b = 1; }\n" " else { if (!strtok(NULL, \" \")) { b = 2; } }\n" "}"); ASSERT_EQUALS("", errout.str()); { check("void f(Class &c) {\n" " if (c.dostuff() == 3) {}\n" " else { if (c.dostuff() == 3) {} }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Class &c) {\n" " if (c.dostuff() == 3) {}\n" " else { if (c.dostuff() == 3) {} }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); } check("void f(int a, int &b) {\n" " x = x / 2;\n" " if (x < 100) { b = 1; }\n" " else { x = x / 2; if (x < 100) { b = 2; } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " if(i == 0x02e2000000 || i == 0xa0c6000000)\n" " foo(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket 3689 ( avoid false positive ) check("int fitInt(long long int nValue){\n" " if( nValue < 0x7fffffffLL )\n" " {\n" " return 32;\n" " }\n" " if( nValue < 0x7fffffffffffLL )\n" " {\n" " return 48;\n" " }\n" " else {\n" " if( nValue < 0x7fffffffffffffffLL )\n" " {\n" " return 64;\n" " } else\n" " {\n" " return -1;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(WIDGET *widget) {\n" " if (dynamic_cast(widget)){}\n" " else if (dynamic_cast(widget)){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" // #6482 " if (x & 1) {}\n" " else if (x == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 15) {}\n" " else if (x == 40) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int x) {\n" " if (x == sizeof(double)) {}\n" " else { if (x == sizeof(long double)) {} }" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 0x08) {}\n" " else if (x & 0xF8) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 0xF8) {}\n" " else if (x & 0x08) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && !!a){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && a){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( b && !!a){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( b && !(!a)){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && !(!a)){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!(b) && !!(a+b)){}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8168 check("enum MaskValues\n" "{\n" " Value1 = 0x00000001,\n" " Value2 = 0x00000002\n" "};\n" "void TestFunction(int value) {\n" " if ( value & (int)Value1 ) {}\n" " else if ( value & (int)Value2 ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(size_t x) {\n" " if (x == sizeof(int)) {}\n" " else { if (x == sizeof(long))} {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(size_t x) {\n" " if (x == sizeof(long)) {}\n" " else { if (x == sizeof(long long))} {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void oppositeElseIfCondition() { setMultiline(); check("void f(int x) {\n" " if (x) {}\n" " else if (!x) {}\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Expression is always true because 'else if' condition is opposite to previous condition at line 2.\n" "test.cpp:2:note:first condition\n" "test.cpp:3:note:else if condition is opposite to first condition\n", errout.str()); check("void f(int x) {\n" " int y = x;\n" " if (x) {}\n" " else if (!y) {}\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Expression is always true because 'else if' condition is opposite to previous condition at line 3.\n" "test.cpp:2:note:'y' is assigned value 'x' here.\n" "test.cpp:3:note:first condition\n" "test.cpp:4:note:else if condition is opposite to first condition\n", errout.str()); } void checkBadBitmaskCheck() { check("bool f(int x) {\n" " bool b = x | 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " bool b = 0x02 | x;\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " int b = x | 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " bool b = x & 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " if(x | 0x02)\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " int y = 0x1;\n" " if(b) y = 0;\n" " if(x | y)\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " foo(a && (x | 0x02));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return (x | 0x02) ? 0 : 5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return x ? (x | 0x02) : 5;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " if (x) {\n" " return x | 0x02;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("const bool f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("struct F {\n" " static const bool f(int x) {\n" " return x | 0x02;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("struct F {\n" " typedef bool b_t;\n" "};\n" "F::b_t f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void create_rop_masks_4( rop_mask_bits *bits) {\n" "DWORD mask_offset;\n" "BYTE *and_bits = bits->and;\n" "rop_mask *rop_mask;\n" "and_bits[mask_offset] |= (rop_mask->and & 0x0f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(unsigned a, unsigned b) {\n" " unsigned cmd1 = b & 0x0F;\n" " if (cmd1 | a) {\n" " if (b == 0x0C) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator1() { check("void f(int x) {\n" " if ((x != 1) || (x != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str()); check("void f(int x) {\n" " if (1 != x || 3 != x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str()); check("void f(int x) {\n" " if (x<0 && !x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 0 && !x.\n", errout.str()); check("void f(int x) {\n" " if (x==0 && x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 0 && x.\n", errout.str()); check("void f(int x) {\n" // ast.. " if (y == 1 && x == 1 && x == 7) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 7.\n", errout.str()); check("void f(int x, int y) {\n" " if (x != 1 || y != 1)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((y == 1) && (x != 1) || (x != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 1) || (x != 3) && (y == 1))\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=3' is always true\n", errout.str()); check("void f(int x) {\n" " if ((x != 1) && (x != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x == 1) || (x == 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 1) || (y != 3))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != hotdog) || (y != hotdog))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 5) || (y != 5))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x != 5) || (x != 6))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 5 || x != 6.\n", errout.str()); check("void f(unsigned int a, unsigned int b, unsigned int c) {\n" " if((a != b) || (c != b) || (c != a))\n" " {\n" " return true;\n" " }\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'c!=a' is always false\n", errout.str()); } void incorrectLogicOperator2() { check("void f(float x) {\n" " if ((x == 1) && (x == 1.0))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x == 1) && (x == 0x00000001))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==0x00000001' is always true\n", errout.str()); check("void f(int x) {\n" " if (x == 1 && x == 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 3.\n", errout.str()); check("void f(int x) {\n" " if (x == 1.0 && x == 3.0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); // float comparisons with == and != are not checked right now - such comparison is a bad idea check("void f(float x) {\n" " if (x == 1 && x == 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void bar(float f) {\n" // #5246 " if ((f > 0) && (f < 1)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.\n", errout.str()); check("void f(int x) {\n" " if (x < 1.0 && x > 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 1.0.\n", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.0.\n", errout.str()); check("void f(int x) {\n" " if (x >= 1.0 && x <= 1.001)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(float x) {\n" " if (x < 1.0 && x > 3.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 3.0.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x < 3 && x > 1)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 3 || x < 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x < 10.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x <= 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 10.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x < 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 10.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x <= 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 10.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x < 3)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x <= 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 3.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x < 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 3.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x <= 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 3.\n", errout.str()); check("void f(int x) {\n" " if((x==3) && (x!=4))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(const std::string &s) {\n" // #8860 " const std::size_t p = s.find(\"42\");\n" " const std::size_t * const ptr = &p;\n" " if(p != std::string::npos && p == 0 && *ptr != 1){;}\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition '*ptr!=1' is always true\n", errout.str()); check("void f(int x) {\n" " if ((x!=4) && (x==3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x==3) || (x!=4))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x!=4) || (x==3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x==3) && (x!=3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 3 && x != 3.\n", errout.str()); check("void f(int x) {\n" " if ((x==6) || (x!=6))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x == 6 || x != 6.\n", errout.str()); check("void f(int x) {\n" " if (x > 10 || x < 3)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 5 && x == 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n", errout.str()); check("void f(int x) {\n" " if (x > 5 && x == 6)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 6', the comparison 'x > 5' is always true.\n", errout.str()); // #3419 check("void f() {\n" " if ( &q != &a && &q != &b ) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3676 check("void f(int m_x2, int w, int x) {\n" " if (x + w - 1 > m_x2 || m_x2 < 0 )\n" " m_x2 = x + w - 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x <= 1.0e20 && x >= -1.0e20) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x >= 1.0e20 && x <= 1.0e21) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x <= -1.0e20 && x >= -1.0e21) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator3() { check("void f(int x, bool& b) {\n" " b = x > 5 && x == 1;\n" " c = x < 1 && x == 3;\n" " d = x >= 5 && x == 1;\n" " e = x <= 1 && x == 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n" "[test.cpp:3]: (warning) Logical conjunction always evaluates to false: x < 1 && x == 3.\n" "[test.cpp:4]: (warning) Logical conjunction always evaluates to false: x >= 5 && x == 1.\n" "[test.cpp:5]: (warning) Logical conjunction always evaluates to false: x <= 1 && x == 3.\n", errout.str()); } void incorrectLogicOperator4() { check("#define ZERO 0\n" "void f(int x) {\n" " if (x && x != ZERO) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator5() { // complex expressions check("void f(int x) {\n" " if (x+3 > 2 || x+3 < 10) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: EXPR > 2 || EXPR < 10.\n", errout.str()); } void incorrectLogicOperator6() { // char literals check("void f(char x) {\n" " if (x == '1' || x == '2') {}\n" "}", "test.cpp", true); ASSERT_EQUALS("", errout.str()); check("void f(char x) {\n" " if (x == '1' && x == '2') {}\n" "}", "test.cpp", true); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == '1' && x == '2'.\n", errout.str()); check("int f(char c) {\n" " return (c >= 'a' && c <= 'z');\n" "}", "test.cpp", true); ASSERT_EQUALS("", errout.str()); check("int f(char c) {\n" " return (c <= 'a' && c >= 'z');\n" "}", "test.cpp", true); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Logical conjunction always evaluates to false: c <= 'a' && c >= 'z'.\n", errout.str()); check("int f(char c) {\n" " return (c <= 'a' && c >= 'z');\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator7() { // opposite expressions check("void f(int i) {\n" " if (i || !i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: i || !(i).\n", errout.str()); check("void f(int a, int b) {\n" " if (a>b || a<=b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: a > b || a <= b.\n", errout.str()); check("void f(int a, int b) {\n" " if (a>b || a T icdf( const T uniform ) {\n" " if ((0 -1.0 - 1.0e-12))\n" " return;\n" " else\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator8() { // opposite expressions check("void f(int i) {\n" " if (!(i!=10) && !(i!=20)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: !(i != 10) && !(i != 20).\n", errout.str()); } void incorrectLogicOperator9() { // #6069 "False positive incorrectLogicOperator due to dynamic_cast" check("class MyType;\n" "class OtherType;\n" "void foo (OtherType* obj) {\n" " assert((!obj) || dynamic_cast(obj));\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator10() { // #7794 - enum check("typedef enum { A, B } Type_t;\n" "void f(Type_t t) {\n" " if ((t == A) && (t == B))\n" " {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: t == 0 && t == 1.\n", errout.str()); } void incorrectLogicOperator11() { check("void foo(int i, const int n) { if ( i < n && i == n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i < n && i == n.\n", errout.str()); check("void foo(int i, const int n) { if ( i > n && i == n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i > n && i == n.\n", errout.str()); check("void foo(int i, const int n) { if ( i == n && i > n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i > n.\n", errout.str()); check("void foo(int i, const int n) { if ( i == n && i < n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i < n.\n", errout.str()); } void incorrectLogicOperator12() { // #8696 check("struct A {\n" " void f() const;\n" "};\n" "void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:6] -> [test.cpp:8]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", errout.str()); check("struct A {\n" " void f();\n" "};\n" "void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(A a) {\n" " const A x = a;\n" " const A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:5]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", errout.str()); } void incorrectLogicOperator13() { // 8780 check("void f(const int &v) {\n" " const int x=v;\n" " if ((v == 1) && (x == 2)) {;}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Logical conjunction always evaluates to false: v == 1 && x == 2.\n", errout.str()); check("void f2(const int *v) {\n" " const int *x=v;\n" " if ((*v == 1) && (*x == 2)) {;}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Logical conjunction always evaluates to false: *(v) == 1 && *(x) == 2.\n", errout.str()); } void incorrectLogicOperator14() { check("static const std ::string h;\n" "class i {\n" "public:\n" " struct j {\n" " std ::string k;\n" " std ::string l;\n" " };\n" " struct a {\n" " enum { m = 1 };\n" " };\n" "} b;\n" "namespace n {\n" "class c;\n" "}\n" "struct o {\n" " enum { p, d, q, r };\n" " enum { e, f };\n" "\n" "public:\n" " class j {\n" " public:\n" " class s {\n" " std ::string a;\n" " };\n" " };\n" "};\n" "namespace n {\n" "class b;\n" "}\n" "namespace aa {\n" "class d {\n" "public:\n" " char t;\n" " enum {} u;\n" "};\n" "} // namespace aa\n" "namespace aa {\n" "struct e {};\n" "} // namespace aa\n" "class a;\n" "class w {\n" "public:\n" " enum { x };\n" " struct {\n" " } y;\n" " std ::string z;\n" "};\n" "class ab {\n" " friend class c;\n" "\n" "public:\n" " class ac {\n" " void e(const ac &v) const;\n" " };\n" "};\n" "class f;\n" "class ad {\n" " friend class e;\n" " enum { e, ae, ag, ah, ai, aj, ak, a, b };\n" " class c {};\n" " class d {\n" " enum am { f, an, ao, ap, aq, ar, b, as, at, c, au };\n" " enum av { aw, ax, ay, az, e, ba, bb, bc, bd, a };\n" " struct b {\n" " am action;\n" " av c;\n" " };\n" " };\n" " class e {\n" " public:\n" " std ::string e;\n" " class f {\n" " } f;\n" " class be {\n" " public:\n" " };\n" " std ::vector bf;\n" " enum { bg, b } c;\n" " };\n" " struct bh {\n" " std ::map b;\n" " };\n" " std ::map bi;\n" " struct {\n" " int b;\n" " char bj;\n" " } bk;\n" " class a {\n" " public:\n" " std ::set b;\n" " };\n" "};\n" "class bl;\n" "class al;\n" "class bm;\n" "class f;\n" "class b;\n" "class bn;\n" "namespace bo {\n" "class bp {\n" "public:\n" " typedef std ::pair bq;\n" " typedef std ::list br;\n" "};\n" "const bo ::bp *dg(const f *a, const al *b);\n" "} // namespace bo\n" "const bn *dh(const f *d, bo ::bp ::br &bs);\n" "class f {\n" "public:\n" " struct bt {};\n" " std ::vector f;\n" "};\n" "class bu;\n" "class a;\n" "class c;\n" "struct bv {};\n" "class af {\n" "private:\n" "public:\n" " enum { b, d, e, f, c, bw };\n" " void a(int c);\n" " af *bx() const;\n" "};\n" "namespace by {\n" "class b;\n" "}\n" "class b {\n" "public:\n" " bool d, c;\n" "};\n" "class bz;\n" "class f;\n" "class ca {\n" " friend class b;\n" "\n" "public:\n" " const bm *cb() const { return cc; }\n" " f *d(f *e, bool f) const;\n" " int e() { return ++cd; }\n" " bl *const c;\n" " bm *cc;\n" " std ::map ce;\n" " int cd;\n" " bz *a;\n" "};\n" "namespace n {\n" "class c;\n" "class d;\n" "} // namespace n\n" "class cf {\n" "public:\n" " explicit cf(const std ::string &aname);\n" " cf(const std ::string &aname, const ca *cg, const al *ch, bl *ci)\n" " : cj(cg), ck(ch), cl(ci), cn(aname) {}\n" "\n" "protected:\n" " const ca *const cj;\n" " const al *const ck;\n" " bl *const cl;\n" " const std ::string cn;\n" "};\n" "class cm : public cf {\n" "public:\n" " void cp();\n" " std ::string d() const;\n" "};\n" "struct co {\n" " co();\n" " const bu *a;\n" " enum f {};\n" " enum {\n" " b = (1 << 0),\n" " c = (1 << 1),\n" " };\n" " void d(bool e);\n" "};\n" "class bu {\n" " friend class e;\n" "\n" "public:\n" " struct f {};\n" " enum { d, cr, cq, ct, cs, e, a, b, c, dd, cu, cv, cw, cx, cy, cz, da };\n" " const f *db;\n" " const af *dc;\n" "} f{};\n" "class bm {\n" "public:\n" " std ::list df;\n" " std ::vector de;\n" " mutable std ::set f;\n" "};\n" "void cm ::cp() {\n" " const bm *a = cj->cb();\n" " for (const bu *b : a->de)\n" " for (af *c = b->dc->bx();;) {\n" " af *d = c;\n" " af *e = c;\n" " bool f(d);\n" " bool g(e);\n" " if (f && g)\n" " ;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:200] -> [test.cpp:200]: (style) Condition 'g' is always true\n", errout.str()); } void incorrectLogicOperator15() { // 10022 check("struct PipeRoute {\n" " std::deque points;\n" " std::deque estimates;\n" "};\n" "void CleanPipeRoutes(std::map& pipeRoutes) {\n" " for (auto it = pipeRoutes.begin(); it != pipeRoutes.end(); ) {\n" " PipeRoute* curRoute = it->second;\n" " if (curRoute->points.empty() && curRoute->estimates.size() != 2)\n" " {\n" " delete curRoute;\n" " it = pipeRoutes.erase(it);\n" " }\n" " else\n" " {\n" " ++it;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator16() { // #10070 check("void foo(void* p) {\n" " if (!p || p == -1) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void secondAlwaysTrueFalseWhenFirstTrueError() { check("void f(int x) {\n" " if (x > 5 && x != 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n", errout.str()); check("void f(int x) {\n" " if (x > 5 && x != 6)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x > 5) && (x != 1))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x > 5) && (x != 6))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 3 || x == 4;\n" " c = x < 5 || x == 4;\n" " d = x >= 3 || x == 4;\n" " e = x <= 5 || x == 4;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 4', the comparison 'x > 3' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x == 4', the comparison 'x < 5' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x == 4', the comparison 'x >= 3' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x == 4', the comparison 'x <= 5' is always true.\n", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 5 || x != 1;\n" " c = x < 1 || x != 3;\n" " d = x >= 5 || x != 1;\n" " e = x <= 1 || x != 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x < 1', the comparison 'x != 3' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x >= 5', the comparison 'x != 1' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x <= 1', the comparison 'x != 3' is always true.\n", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 6 && x > 5;\n" " c = x > 5 || x > 6;\n" " d = x < 6 && x < 5;\n" " e = x < 5 || x < 6;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 6', the comparison 'x > 5' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x > 6', the comparison 'x > 5' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x < 5', the comparison 'x < 6' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x < 5', the comparison 'x < 6' is always true.\n", errout.str()); } void incorrectLogicOp_condSwapping() { check("void f(int x) {\n" " if (x < 1 && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x < 1 && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 && x < 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (3 < x && x < 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 && 1 > x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (3 < x && 1 > x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); } void modulo() { check("bool f(bool& b1, bool& b2, bool& b3) {\n" " b1 = a % 5 == 4;\n" " b2 = a % c == 100000;\n" " b3 = a % 5 == c;\n" " return a % 5 == 5-p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool& b1, bool& b2, bool& b3, bool& b4, bool& b5) {\n" " b1 = a % 5 < 5;\n" " b2 = a % 5 <= 5;\n" " b3 = a % 5 == 5;\n" " b4 = a % 5 != 5;\n" " b5 = a % 5 >= 5;\n" " return a % 5 > 5;\n" "}"); ASSERT_EQUALS( "[test.cpp:7]: (style) Condition 'a%5>5' is always false\n" "[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:5]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:6]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:7]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", errout.str()); check("void f(bool& b1, bool& b2) {\n" " b1 = bar() % 5 < 889;\n" " if(x[593] % 5 <= 5)\n" " b2 = x.a % 5 == 5;\n" "}"); ASSERT_EQUALS( "[test.cpp:3]: (style) Condition 'x[593]%5<=5' is always true\n" "[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", errout.str()); check("void f() {\n" " if (a % 2 + b % 2 == 2)\n" " foo();\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerCondition() { check("void foo(int a, int b) {\n" " if(a==b)\n" " if(a!=b)\n" " cout << a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("bool foo(int a, int b) {\n" " if(a==b)\n" " return a!=b;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'return' condition leads to a dead code block.\n", errout.str()); check("void foo(int a, int b) {\n" " if(a==b)\n" " if(b!=a)\n" " cout << a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(int a) {\n" " if(a >= 50) {\n" " if(a < 50)\n" " cout << a;\n" " else\n" " cout << 100;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #4186 check("void foo(int a) {\n" " if(a >= 50) {\n" " if(a > 50)\n" " cout << a;\n" " else\n" " cout << 100;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // 4170 check("class foo {\n" " void bar() {\n" " if (tok == '(') {\n" " next();\n" " if (tok == ',') {\n" " next();\n" " if (tok != ',') {\n" " op->reg2 = asm_parse_reg();\n" " }\n" " skip(',');\n" " }\n" " }\n" " }\n" " void next();\n" " const char *tok;\n" "};"); ASSERT_EQUALS("", errout.str()); check("void foo(int i)\n" "{\n" " if(i > 5) {\n" " i = bar();\n" " if(i < 5) {\n" " cout << a;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i) {\n" " i=6;\n" "}\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i);\n" "void bar() {\n" " int i; i = func();\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int i);\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(const int &i);\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(int i);\n" "void bar() {\n" " int i; i = func();\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("class C { void f(int &i) const; };\n" // #7028 - variable is changed by const method "void foo(C c, int i) {\n" " if (i==5) {\n" " c.f(i);\n" " if (i != 5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // see linux revision 1f80c0cc check("int generic_write_sync(int,int,int);\n" "\n" "void cifs_writev(int i) {\n" " int rc = __generic_file_aio_write();\n" " if (rc > 0){\n" " err = generic_write_sync(file, iocb->ki_pos - rc, rc);\n" " if(rc < 0) {\n" // <- condition is always false " err = rc;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:7]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #5874 - array check("void testOppositeConditions2() {\n" " int array[2] = { 0, 0 };\n" " if (array[0] < 2) {\n" " array[0] += 5;\n" " if (array[0] > 2) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6227 - FP caused by simplifications of casts and known variables check("void foo(A *a) {\n" " if(a) {\n" " B *b = dynamic_cast(a);\n" " if(!b) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a) {\n" " if(a) {\n" " int b = a;\n" " if(!b) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(unsigned u) {\n" " if (u != 0) {\n" " for (int i=0; i<32; i++) {\n" " if (u == 0) {}\n" // <- don't warn " u = x;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #8186 check("void f() {\n" " for (int i=0;i<4;i++) {\n" " if (i==5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #8938 check("void Delete(SS_CELLCOORD upperleft) {\n" " if ((upperleft.Col == -1) && (upperleft.Row == -1)) {\n" " GetActiveCell(&(upperleft.Col), &(upperleft.Row));\n" " if (upperleft.Row == -1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #9702 check("struct A {\n" " void DoTest() {\n" " if (!IsSet()) {\n" " m_value = true;\n" " if (IsSet());\n" " }\n" " }\n" " bool IsSet() const { return m_value; }\n" " bool m_value = false;\n" "};"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionPointers() { check("void f(struct ABC *abc) {\n" " struct AB *ab = abc->ab;\n" " if (ab->a == 123){\n" " do_something(abc);\n" // might change ab->a " if (ab->a != 123) {\n" " err = rc;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void Fred::f() {\n" // daca: ace " if (this->next_ == map_man_->table_) {\n" " this->next_ = n;\n" " if (this->next_ != map_man_->table_) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(float *f) {\n" // #7405 " if(*f>10) {\n" " (*f) += 0.1f;\n" " if(*f<10) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int * f(int * x, int * y) {\n" " if(!x) return x;\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionClass() { // #6095 - calling member function that might change the state check("void f() {\n" " const Fred fred;\n" // <- fred is const, warn " if (fred.isValid()) {\n" " fred.dostuff();\n" " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("class Fred { public: bool isValid() const; void dostuff() const; };\n" "void f() {\n" " Fred fred;\n" " if (fred.isValid()) {\n" " fred.dostuff();\n" // <- dostuff() is const, warn " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f() {\n" " Fred fred;\n" " if (fred.isValid()) {\n" " fred.dostuff();\n" " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6385 "crash in Variable::getFlag()" check("class TranslationHandler {\n" "QTranslator *mTranslator;\n" "void SetLanguage() {\n" " if (mTranslator) {\n" " qApp->removeTranslator(mTranslator);\n" " }\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // just don't crash... check("bool f(std::ofstream &CFileStream) {\n" // #8198 " if(!CFileStream.good()) { return; }\n" " CFileStream << \"abc\";\n" " if (!CFileStream.good()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionUndeclaredVariable() { // #5731 - fp when undeclared variable is used check("void f() {\n" " if (x == -1){\n" " x = do_something();\n" " if (x != -1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #5750 - another fp when undeclared variable is used check("void f() {\n" " if (r < w){\n" " r += 3;\n" " if (r > w) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6574 - another fp when undeclared variable is used check("void foo() {\n" " if(i) {\n" " i++;\n" " if(!i) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // undeclared array check("void f(int x) {\n" " if (a[x] > 0) {\n" " a[x] -= dt;\n" " if (a[x] < 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6313 - false positive: opposite conditions in nested if blocks when condition changed check("void Foo::Bar() {\n" " if(var){\n" " --var;\n" " if(!var){}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // daca hyphy check("bool f() {\n" " if (rec.lLength==0) {\n" " rec.Delete(i);\n" " if (rec.lLength!=0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionAlias() { check("void f() {\n" " struct S s;\n" " bool hasFailed = false;\n" " s.status = &hasFailed;\n" "\n" " if (! hasFailed) {\n" " doStuff(&s);\n" " if (hasFailed) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Condition '!hasFailed' is always true\n", errout.str()); } void oppositeInnerCondition2() { // first comparison: < check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x==5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x!=5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x!=5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x>5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x>=5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x<5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x<=5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<=5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x==4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x!=4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x!=6) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x!=6' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x>4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>4' is always false\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x>=4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x<4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x<=4) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<=4' is always true\n", errout.str()); // first comparison: > check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x==5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x>5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x>=5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>=5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x<5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<5' is always false\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x<=5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x==4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x>4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>4' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x>=4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>=4' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x<4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x<=4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" " if (x < 4) {\n" " if (10 < x) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerCondition3() { check("void f3(char c) { if(c=='x') if(c=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f4(char *p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f5(const char * const p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f5(const char * const p) { if('x'==*p) if('y'==*p) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f6(char * const p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f7(const char * p) { if(*p=='x') if(*p=='y') {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f8(int i) { if(i==4) if(i==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f9(int *p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f10(int * const p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f11(const int *p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f12(const int * const p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("struct foo {\n" " int a;\n" " int b;\n" "};\n" "void f(foo x) { if(x.a==4) if(x.b==2) {}}"); ASSERT_EQUALS("", errout.str()); check("struct foo {\n" " int a;\n" " int b;\n" "};\n" "void f(foo x) { if(x.a==4) if(x.b==4) {}}"); ASSERT_EQUALS("", errout.str()); check("void f3(char a, char b) { if(a==b) if(a==0) {}}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { if (x == 1) if (x != 1) {} }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionAnd() { check("void f(int x) {\n" " if (a>3 && x > 100) {\n" " if (x < 10) {}\n" " }" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(bool x, const int a, const int b) {\n" " if(x && a < b)\n" " if( x && a > b){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionEmpty() { check("void f1(const std::string &s) { if(s.size() > 42) if(s.empty()) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s) { if(s.size() > 0) if(s.empty()) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s) { if(s.size() < 0) if(s.empty()) {}} "); // <- CheckOther reports: checking if unsigned expression is less than zero ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.empty()) if(s.size() > 42) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("template void f1(const T &s) { if(s.size() > 42) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); //We don't know the type of T so we don't know the relationship between size() and empty(). e.g. s might be a 50 tonne truck with nothing in it. check("void f2(const std::wstring &s) { if(s.empty()) if(s.size() > 42) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(QString s) { if(s.isEmpty()) if(s.length() > 42) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s, bool b) { if(s.empty() || ((s.size() == 1) && b)) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &x, const std::string &y) { if(x.size() > 42) if(y.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &x, const std::string &y) { if(y.empty()) if(x.size() > 42) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string v[10]) { if(v[0].size() > 42) if(v[1].empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() <= 1) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() <= 2) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() < 2) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() >= 0) if(s.empty()) {}} "); // CheckOther says: Unsigned expression 's.size()' can't be negative so it is unnecessary to test it. [unsignedPositive] ASSERT_EQUALS("", errout.str()); // TODO: These are identical condition since size cannot be negative check("void f1(const std::string &s) { if(s.size() <= 0) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); // TODO: These are identical condition since size cannot be negative check("void f1(const std::string &s) { if(s.size() < 1) if(s.empty()) {}}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionFollowVar() { check("struct X {\n" " void f() {\n" " const int flag = get();\n" " if (flag) {\n" " bar();\n" " if (!get()) {}\n" " }\n" " }\n" " void bar();\n" " int get() const;\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct CD {\n" " bool state;\n" " void foo() {\n" " const bool flag = this->get();\n" " if (flag) {\n" " this->bar();\n" " if (!this->get()) return;\n" " }\n" " }\n" " bool get() const;\n" " void bar();\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("class C {\n" "public:\n" " bool f() const { return x > 0; }\n" " void g();\n" " int x = 0;\n" "};\n" "\n" "void C::g() {\n" " bool b = f();\n" " x += 1;\n" " if (!b && f()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void identicalInnerCondition() { check("void f1(int a, int b) { if(a==b) if(a==b) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); check("void f2(int a, int b) { if(a!=b) if(a!=b) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); // #6645 false negative: condition is always false check("void f(bool a, bool b) {\n" " if(a && b) {\n" " if(a) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); check("bool f(int a, int b) {\n" " if(a == b) { return a == b; }\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Identical inner 'return' condition is always true.\n", errout.str()); check("bool f(bool a) {\n" " if(a) { return a; }\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(int* a, int * b) {\n" " if(a) { return a; }\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(std::shared_ptr a, std::shared_ptr b) {\n" " if(a.get()) { return a.get(); }\n" " return b.get();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int * x; };\n" "int* f(A a, int * b) {\n" " if(a.x) { return a.x; }\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " uint32_t value;\n" " get_value(&value);\n" " int opt_function_capable = (value >> 28) & 1;\n" " if (opt_function_capable) {\n" " value = 0;\n" " get_value (&value);\n" " if ((value >> 28) & 1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void identicalConditionAfterEarlyExit() { check("void f(int x) {\n" // #8137 " if (x > 100) { return; }\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("bool f(int x) {\n" " if (x > 100) { return false; }\n" " return x > 100;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition and return expression 'x>100', return value is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100 || y > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100 && y > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (abc) {}\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " while (abc) { y = x; }\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" // #8217 - crash for incomplete code " if (x > 100) { return; }\n" " X(do);\n" " if (x > 100) {}\n" "}"); // TODO: we should probably throw unknownMacro InternalError. Complain that the macro X must be defined. We can't check the code well without the definition. ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x>100' is always false\n", errout.str()); check("void f(const int *i) {\n" " if (!i) return;\n" " if (!num1tok) { *num1 = *num2; }\n" " if (!i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition '!i', second condition is always false\n", errout.str()); check("void C::f(Tree &coreTree) {\n" // daca " if(!coreTree.build())\n" " return;\n" " coreTree.dostuff();\n" " if(!coreTree.build()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct C { void f(const Tree &coreTree); };\n" "void C::f(const Tree &coreTree) {\n" " if(!coreTree.build())\n" " return;\n" " coreTree.dostuff();\n" " if(!coreTree.build()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Identical condition '!coreTree.build()', second condition is always false\n", errout.str()); check("void f(int x) {\n" // daca: labplot " switch(type) {\n" " case 1:\n" " if (x == 0) return 1;\n" " else return 2;\n" " case 2:\n" " if (x == 0) return 3;\n" " else return 4;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static int failed = 0;\n" "void f() {\n" " if (failed) return;\n" " checkBuffer();\n" " if (failed) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // daca icu check("void f(const uint32_t *section, int32_t start) {\n" " if(10<=section[start]) { return; }\n" " if(++start<100 && 10<=section[start]) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // daca iqtree check("void readNCBITree(std::istream &in) {\n" " char ch;\n" " in >> ch;\n" " if (ch != '|') return;\n" " in >> ch;\n" " if (ch != '|') {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8924 check("struct A {\n" " void f() {\n" " if (this->FileIndex >= 0) return;\n" " this->FileIndex = 1 ;\n" " if (this->FileIndex < 0) return;\n" " }\n" " int FileIndex;\n" "};"); ASSERT_EQUALS("", errout.str()); // #8858 - #if check("short Do() {\n" " short ret = bar1();\n" " if ( ret )\n" " return ret;\n" "#ifdef FEATURE\n" " ret = bar2();\n" "#endif\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); // #10456 check("int f() {\n" " int i = 0;\n" " auto f = [&](bool b) { if (b) ++i; };\n" " if (i) return i;\n" " f(true);\n" " if (i) return i;\n" " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void innerConditionModified() { check("void f(int x, int y) {\n" " if (x == 0) {\n" " x += y;\n" " if (x == 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x == 0) {\n" " x += y;\n" " if (x == 1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int * x, int * y) {\n" " if (x[*y] == 0) {\n" " (*y)++;\n" " if (x[*y] == 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } // clarify conditions with = and comparison void clarifyCondition1() { check("void f() {\n" " if (x = b() < 0) {}\n" // don't simplify and verify this code "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (assignment + comparison); Clarify expression with parentheses.\n", errout.str()); check("void f(int i) {\n" " for (i = 0; i < 10; i++) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " x = a(); if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (x = b < 0 ? 1 : 2) {}\n" // don't simplify and verify this code "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y || (!y && z));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y || !y && z);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout.str()); check("void f() {\n" " if (!a || a && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. '!a || (a && b)' is equivalent to '!a || b'\n", errout.str()); check("void f(const Token *tok) {\n" " if (!tok->next()->function() ||\n" " (tok->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next()->function(). '!A || (A && B)' is equivalent to '!A || B'\n", errout.str()); check("void f() {\n" " if (!tok->next()->function() ||\n" " (!tok->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (!tok->next()->function() ||\n" " (!tok2->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Token *tok) {\n" " if (!tok->next(1)->function(1) ||\n" " (tok->next(1)->function(1) && tok->next(1)->function(1)->isConstructor()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next(1)->function(1). '!A || (A && B)' is equivalent to '!A || B'\n", errout.str()); check("void f() {\n" " if (!tok->next()->function(1) ||\n" " (tok->next()->function(2) && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y==0 || y!=0 && z);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: y!=0. 'y==0 || (y!=0 && z)' is equivalent to 'y==0 || z'\n", errout.str()); check("void f() {\n" " if (x>0 || (x<0 && y)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Test Token::expressionString, TODO move this test check("void f() {\n" " if (!dead || (dead && (*it).ticks > 0)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: dead. '!dead || (dead && (*it).ticks>0)' is equivalent to '!dead || (*it).ticks>0'\n", errout.str()); check("void f() {\n" " if (!x || (x && (2>(y-1)))) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: x. '!x || (x && 2>(y-1))' is equivalent to '!x || 2>(y-1)'\n", errout.str()); check("void f(bool a, bool b) {\n" " if (a || (a && b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. 'a || (a && b)' is equivalent to 'a'\n", errout.str()); check("void f(bool a, bool b) {\n" " if (a && (a || b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. 'a && (a || b)' is equivalent to 'a'\n", errout.str()); } // clarify conditions with bitwise operator and comparison void clarifyCondition2() { check("void f() {\n" " if (x & 3 == 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Condition 'x&3==2' is always false\n", errout.str()); check("void f() {\n" " if (a & fred1.x == fred2.y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" , errout.str()); } // clarify condition that uses ! operator and then bitwise operator void clarifyCondition3() { check("void f(int w) {\n" " if(!w & 0x8000) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f(int w) {\n" " if((!w) & 0x8000) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (x == foo() & 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f() {\n" " if (2 & x == foo()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f() {\n" " if (2 & (x == foo())) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::list &ints) { }"); ASSERT_EQUALS("", errout.str()); check("void f() { A a; }"); ASSERT_EQUALS("", errout.str()); check("void f() { a(x there are never templates ASSERT_EQUALS("[test.c:1]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("class A;", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (result != (char *)&inline_result) { }\n" // don't simplify and verify cast "}"); ASSERT_EQUALS("", errout.str()); // #8495 check("void f(bool a, bool b) {\n" " C & a & b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition4() { // ticket #3110 check("typedef double SomeType;\n" "typedef std::pair PairType;\n" "struct S\n" "{\n" " bool operator()\n" " ( PairType const & left\n" " , PairType const & right) const\n" " {\n" " return left.first < right.first;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition5() { // ticket #3609 (using | in template instantiation) check("template struct CWinTraits;\n" "CWinTraits::GetWndStyle(0);"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition6() { check("template\n" "SharedPtr& operator=( SharedPtr const & r ) {\n" " px = r.px;\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition7() { // Ensure that binary and unary &, and & in declarations are distinguished properly check("void f(bool error) {\n" " bool & withoutSideEffects=found.first->second;\n" // Declaring a reference to a boolean; & is no operator at all " execute(secondExpression, &programMemory, &result, &error);\n" // Unary & "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition8() { // don't warn when boolean result comes from function call, array index, etc // the operator precedence is not unknown then check("bool a();\n" "bool f(bool b) {\n" " return (a() & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool *a, bool b) {\n" " return (a[10] & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { bool a; };\n" "bool f(struct A a, bool b) {\n" " return (a.a & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { bool a; };\n" "bool f(struct A a, bool b) {\n" " return (A::a & b);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testBug5895() { check("void png_parse(uint64_t init, int buf_size) {\n" " if (init == 0x89504e470d0a1a0a || init == 0x8a4d4e470d0a1a0a)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testBug5309() { check("extern uint64_t value;\n" "void foo() {\n" " if( ( value >= 0x7ff0000000000001ULL )\n" " && ( value <= 0x7fffffffffffffffULL ) );\n" "}"); ASSERT_EQUALS("", errout.str()); } void alwaysTrue() { check("void f(const struct S *s) {\n" //#8196 " int x1 = s->x;\n" " int x2 = s->x;\n" " if (x1 == 10 && x2 == 10) {}\n" // << "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition 'x2==10' is always true\n", errout.str()); check("void f ()\n"// #8220 "{\n" " int a;\n" " int b = 0;\n" " int ret;\n" " \n" " a = rand();\n" " while (((0 < a) && (a < 2)) && ((8 < a) && (a < 10))) \n" " {\n" " b += a;\n" " a ++;\n" " }\n" " ret = b;\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:8]: (style) Condition '8 [test.cpp:2]: (style) Condition 'x==0' is always false\n", errout.str()); check("void f() {\n" // #6898 (Token::expressionString) " int x = 0;\n" " A(x++ == 1);\n" " A(x++ == 2);\n" "}"); TODO_ASSERT_EQUALS("function argument is always true? however is code really weird/suspicious?", "", errout.str()); check("bool foo(int bar) {\n" " bool ret = false;\n" " if (bar == 1)\n" " return ret;\n" // <- #9326 - FP condition is always false " if (bar == 2)\n" " ret = true;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.empty()) if(s.size() == 0) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition 's.size()==0' is always true\n", errout.str()); check("void f() {\n" " int buf[42];\n" " if( buf != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf!=0' is always true\n", errout.str()); // #8924 check("void f() {\n" " int buf[42];\n" " if( !buf ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!buf' is always false\n", errout.str()); check("void f() {\n" " int buf[42];\n" " bool b = buf;\n" " if( b ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'b' is always true\n", errout.str()); check("void f() {\n" " int buf[42];\n" " bool b = buf;\n" " if( !b ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always false\n", errout.str()); check("void f() {\n" " int buf[42];\n" " int * p = nullptr;\n" " if( buf == p ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'buf==p' is always false\n", errout.str()); check("void f(bool x) {\n" " int buf[42];\n" " if( buf || x ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf' is always true\n", errout.str()); check("void f(int * p) {\n" " int buf[42];\n" " if( buf == p ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int buf[42];\n" " int p[42];\n" " if( buf == p ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int buf[42];\n" " if( buf == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Avoid FP when condition comes from macro check("#define NOT !\n" "void f() {\n" " int x = 0;\n" " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels " if (NOT x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("#define M x != 0\n" "void f() {\n" " int x = 0;\n" " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels " if (M) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("#define IF(X) if (X && x())\n" "void f() {\n" " IF(1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Avoid FP for sizeof condition check("void f() {\n" " if (sizeof(char) != 123) {}\n" " if (123 != sizeof(char)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 123;\n" " if (sizeof(char) != x) {}\n" " if (x != sizeof(char)) {}\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'sizeof(char)!=x' is always true\n" "[test.cpp:4]: (style) Condition 'x!=sizeof(char)' is always true\n", "", errout.str()); // Don't warn in assertions. Condition is often 'always true' by intention. // If platform,defines,etc cause an 'always false' assertion then that is not very dangerous neither check("void f() {\n" " int x = 0;\n" " assert(x == 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // #9363 - do not warn about value passed to function check("void f(bool b) {\n" " if (b) {\n" " if (bar(!b)) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #7783 FP knownConditionTrueFalse on assert(0 && "message") check("void foo(int x) {\n" " if (x<0)\n" " {\n" " assert(0 && \"bla\");\n" " ASSERT(0 && \"bla\");\n" " assert_foo(0 && \"bla\");\n" " ASSERT_FOO(0 && \"bla\");\n" " assert((int)(0==0));\n" " assert((int)(0==0) && \"bla\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #7750 char literals in boolean expressions check("void f() {\n" " if('a'){}\n" " if(L'b'){}\n" " if(1 && 'c'){}\n" " int x = 'd' ? 1 : 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // #8206 - knownCondition always false check("void f(int i)\n" "{\n" " if(i > 4)\n" " for( int x = 0; i < 3; ++x){}\n" // << "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'i<3' is always false\n", errout.str()); // Skip literals check("void f() { if(true) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(false) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(!true) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(!false) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(0) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(1) {} }"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " bool b = false;\n" " if (i == 0) b = true;\n" " else if (!b && i == 1) {}\n" " if (b)\n" " {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always true\n", errout.str()); check("bool f() { return nullptr; }"); ASSERT_EQUALS("", errout.str()); check("enum E { A };\n" "bool f() { return A; }"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " const int x = 0;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(void){return 1/abs(10);}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " int x = 0;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " const int a = 50;\n" " const int b = 52;\n" " return a+b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int a = 50;\n" " int b = 52;\n" " a++;\n" " b++;\n" " return a+b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool& g();\n" "bool f() {\n" " bool & b = g();\n" " b = false;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " bool b;\n" " bool f() {\n" " b = false;\n" " return b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("bool f(long maxtime) {\n" " if (std::time(0) > maxtime)\n" " return std::time(0) > maxtime;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(double param) {\n" " while(bar()) {\n" " if (param<0.)\n" " return;\n" " }\n" " if (param<0.)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int i) {\n" " if (i==42)\n" " {\n" " bar();\n" " }\n" " if (cond && (42==i))\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); // 8842 crash check("class a {\n" " int b;\n" " c(b);\n" " void f() {\n" " if (b) return;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f(const char* x, const char* t) {\n" " if (!(strcmp(x, y) == 0)) { return; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int a[]){ if (a == 0){} }"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " bool operator<(const S&);\n" "};\n" "int main() {\n" " S s;\n" " bool c = s [test.cpp:3]: (style) Condition 'handle' is always true\n", errout.str()); check("int f(void *handle) {\n" " if (handle == 0) return 0;\n" " if (handle) return 1;\n" " else return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'handle' is always true\n", errout.str()); check("int f(void *handle) {\n" " if (handle != 0) return 0;\n" " if (handle) return 1;\n" " else return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'handle!=0', second condition is always false\n", errout.str()); check("void f(void* x, void* y) {\n" " if (x == nullptr && y == nullptr)\n" " return;\n" " if (x == nullptr || y == nullptr)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* g();\n" "void f(void* a, void* b) {\n" " while (a) {\n" " a = g();\n" " if (a == b)\n" " break;\n" " }\n" " if (a) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* g();\n" "void f(void* a, void* b) {\n" " while (a) {\n" " a = g();\n" " }\n" " if (a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'a' is always false\n", errout.str()); check("void f(int * x, bool b) {\n" " if (!x && b) {}\n" " else if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const std::string x=\"xyz\";\n" " if(!x.empty()){}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!x.empty()' is always true\n", errout.str()); check("std::string g();\n" "void f() {\n" " const std::string msg = g();\n" " if(!msg.empty()){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *array, int size ) {\n" " for(int i = 0; i < size; ++i) {\n" " if(array == 0)\n" " continue;\n" " if(array){}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'array' is always true\n", errout.str()); check("void f(int *array, int size ) {\n" " for(int i = 0; i < size; ++i) {\n" " if(array == 0)\n" " continue;\n" " else if(array){}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'array' is always true\n", errout.str()); // #9277 check("int f() {\n" " constexpr bool x = true;\n" " if constexpr (x)\n" " return 0;\n" " else\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9319 check("struct S {\n" " int a;\n" " int b;\n" "};\n" "void g(S s, bool& x);\n" "void f() {\n" " bool x = false;\n" " g({0, 1}, x);\n" " if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9318 check("class A {};\n" "class B : public A {};\n" "void f(A* x) {\n" " if (!x)\n" " return;\n" " auto b = dynamic_cast(x);\n" " if (b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " auto x = getX();\n" " if (x == nullptr)\n" " return 1;\n" " auto y = dynamic_cast(x)\n" " if (y == nullptr)\n" " return 2;\n" " return 3;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // handleKnownValuesInLoop check("bool g();\n" "void f(bool x) {\n" " if (x) while(x) x = g();\n" "}"); ASSERT_EQUALS("", errout.str()); // isLikelyStream check("void f(std::istringstream& iss) {\n" " std::string x;\n" " while (iss) {\n" " iss >> x;\n" " if (!iss) break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #9332 check("struct A { void* g(); };\n" "void f() {\n" " A a;\n" " void* b = a.g();\n" " if (!b) return;\n" " void* c = a.g();\n" " if (!c) return;\n" " bool compare = c == b;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9361 check("void f(char c) {\n" " if (c == '.') {}\n" " else if (isdigit(c) != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9351 check("int f(int x) {\n" " const bool b = x < 42;\n" " if(b) return b?0:-1;\n" " return 42;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (style) Condition 'b' is always true\n", errout.str()); // #9362 check("uint8_t g();\n" "void f() {\n" " const uint8_t v = g();\n" " if((v != 0x00)) {\n" " if( (v & 0x01) == 0x00) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #9367 check("void f(long x) {\n" " if (x <= 0L)\n" " return;\n" " if (x % 360L == 0)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a, int b) {\n" " static const int x = 10;\n" " return x == 1 ? a : b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("const bool x = false;\n" "void f() {\n" " if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("const bool x = false;\n" "void f() {\n" " if (!x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9709 check("void f(int a) {\n" " bool ok = false;\n" " const char * r = nullptr;\n" " do_something(&r);\n" " if (r != nullptr)\n" " ok = a != 0;\n" " if (ok) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9816 check("bool g();\n" "void f() {\n" " bool b = false;\n" " do {\n" " do {\n" " if (g())\n" " break;\n" " b = true;\n" " } while(false);\n" " } while(!b);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9865 check("void f(const std::string &s) {\n" " for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {\n" " const unsigned char c = static_cast(*it);\n" " if (c == '0') {}\n" " else if ((c == 'a' || c == 'A')\n" " || (c == 'b' || c == 'B')) {}\n" " else {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9711 check("int main(int argc, char* argv[]) {\n" " int foo = 0;\n" " struct option options[] = {\n" " {\"foo\", no_argument, &foo, \'f\'},\n" " {NULL, 0, NULL, 0},\n" " };\n" " getopt_long(argc, argv, \"f\", options, NULL);\n" " if (foo) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " int *b();\n" "};\n" "bool g(a c, a* d) {\n" " a *v, *e = v = &c;\n" " if (!v)\n" " return true;\n" " int *f = v->b();\n" " if (f)\n" " v = nullptr;\n" " if (v == nullptr && e) {}\n" " return d;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10037 check("struct a {\n" " int* p;\n" "};\n" "void g(a*);\n" "void f() {\n" " struct a b;\n" " uint32_t p = (uint32_t) -1;\n" " b.p = (void *) &p;\n" " int r = g(&b);\n" " if (r == 0)\n" " if (p != (uint32_t) -1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9890 check("int g(int);\n" "bool h(int*);\n" "int f(int *x) {\n" " int y = g(0);\n" " if (!y) {\n" " if (h(x)) {\n" " y = g(1);\n" " if (y) {}\n" " return 0;\n" " }\n" " if (!y) {}\n" " }\n" " return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:11]: (style) Condition '!y' is always true\n", errout.str()); // #10134 check("bool foo(bool b);\n" "bool thud(const std::vector& Arr, const std::wstring& Str) {\n" " if (Arr.empty() && Str.empty())\n" " return false;\n" " bool OldFormat = Arr.empty() && !Str.empty();\n" " if (OldFormat)\n" " return foo(OldFormat);\n" " return false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10208 check("bool GetFirst(std::string &first);\n" "bool GetNext(std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool GetFirst(std::string &first);\n" "bool GetNext(std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name{}; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool GetFirst(std::string &first);\n" "bool GetNext(std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name{'a', 'b'}; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool GetFirst(const std::string &first);\n" "bool GetNext(const std::string &next);\n" "void g(const std::string& name);\n" "void f() {\n" " for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n" " g(name);\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'name.empty()' is always true\n", errout.str()); // #10278 check("void foo(unsigned int x) {\n" " if ((100 - x) > 0) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10298 check("void foo(unsigned int x) {\n" " if (x == -1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10121 check("struct AB {\n" " int a;\n" "};\n" "struct ABC {\n" " AB* ab;\n" "};\n" "void g(ABC*);\n" "int f(struct ABC *abc) {\n" " int err = 0;\n" " AB *ab = abc->ab;\n" " if (ab->a == 123){\n" " g(abc);\n" " if (ab->a != 123) {\n" " err = 1;\n" " }\n" " }\n" " return err;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10323 check("void foo(int x) {\n" " if(x)\n" " if(x == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " if(x) {}\n" " else\n" " if(x == 1) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==1' is always false\n", errout.str()); // do not report both unsignedLessThanZero and knownConditionTrueFalse check("void foo(unsigned int max) {\n" " unsigned int num = max - 1;\n" " if (num < 0) {}\n" // <- do not report knownConditionTrueFalse "}"); ASSERT_EQUALS("", errout.str()); // #10297 check("void foo(size_t len, int start) {\n" " if (start < 0) {\n" " start = len+start;\n" " if (start < 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10362 check("int tok;\n" "void next();\n" "void parse_attribute() {\n" " if (tok == '(') {\n" " int parenthesis = 0;\n" " do {\n" " if (tok == '(')\n" " parenthesis++;\n" " else if (tok == ')')\n" " parenthesis--;\n" " next();\n" " } while (parenthesis && tok != -1);\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #7843 check("void f(int i) {\n" " if(abs(i) == -1) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'abs(i)==-1' is always false\n", errout.str()); // #7844 check("void f(int i) {\n" " if(i > 0 && abs(i) == i) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'abs(i)==i' is always true\n", errout.str()); check("void f(int i) {\n" " if(i < 0 && abs(i) == i) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'abs(i)==i' is always false\n", errout.str()); check("void f(int i) {\n" " if(i > -3 && abs(i) == i) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9948 check("bool f(bool a, bool b) {\n" " return a || ! b || ! a;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition '!a' is always true\n", errout.str()); // #10148 check("void f(int i) {\n" " if (i >= 64) {}\n" " else if (i >= 32) {\n" " i &= 31;\n" " if (i == 0) {}\n" " else {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10548 check("void f() {\n" " int i = 0;\n" " do {} while (i++ == 0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10582 check("static void fun(message_t *message) {\n" " if (message->length >= 1) {\n" " switch (data[0]) {}\n" " }\n" " uint8_t d0 = message->length > 0 ? data[0] : 0xff;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8266 check("void f(bool b) {\n" " if (b)\n" " return;\n" " if (g(&b) || b)\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9720 check("bool bar(int &);\n" "void f(int a, int b) {\n" " if (a + b == 3)\n" " return;\n" " if (bar(a) && (a + b == 3)) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10437 check("void f() {\n" " Obj* PObj = nullptr;\n" " bool b = false;\n" " if (GetObj(PObj) && PObj != nullptr)\n" " b = true;\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10223 check("static volatile sig_atomic_t is_running;\n" "static void handler(int signum) {\n" " is_running = 0;\n" "}\n" "void f() {\n" " signal(SIGINT, &handler);\n" " is_running = 1;\n" " while (is_running) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10659 check("auto func(const std::tuple& t) {\n" " auto& [foo, bar] = t;\n" " std::cout << foo << bar << std::endl;\n" " return foo < bar;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10484 check("void f() {\n" " static bool init = true;\n" " if (init)\n" " init = false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static bool init(true);\n" " if (init)\n" " init = false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static bool init{ true };\n" " if (init)\n" " init = false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10248 check("void f() {\n" " static int var(1);\n" " if (var == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int var{ 1 };\n" " if (var == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "using Fn = void (*)();\n" "void f() {\n" " static Fn logger = nullptr;\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "using Fn = void (*)();\n" "void f() {\n" " static Fn logger(nullptr);\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "using Fn = void (*)();\n" "void f() {\n" " static Fn logger{ nullptr };\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "typedef void (*Fn)();\n" "void f() {\n" " static Fn logger = nullptr;\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); TODO_ASSERT_EQUALS("", "[test.cpp:5]: (style) Condition 'logger==nullptr' is always true\n", errout.str()); check("void Fun();\n" "typedef void (*Fn)();\n" "void f() {\n" " static Fn logger(nullptr);\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void Fun();\n" "typedef void (*Fn)();\n" "void f() {\n" " static Fn logger{ nullptr };\n" " if (logger == nullptr)\n" " logger = Fun;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9256 check("bool f() {\n" " bool b = false;\n" " b = true;\n" " return b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueSymbolic() { check("void f(const uint32_t x) {\n" " uint32_t y[1];\n" " y[0]=x;\n" " if(x > 0 || y[0] < 42){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition 'y[0]<42' is always true\n", errout.str()); check("void f(int x, int y) {\n" " if(x < y && x < 42) {\n" " --x;\n" " if(x == y) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==y' is always false\n", errout.str()); check("void f(bool a, bool b) { if (a == b && a && !b){} }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!b' is always false\n", errout.str()); check("bool f(bool a, bool b) { if(a && b && (!a)){} }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!a' is always false\n", errout.str()); check("void f(int x, int y) {\n" " if (x < y) {\n" " auto z = y - x;\n" " if (z < 1) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'z<1' is always false\n", errout.str()); check("bool f(int &index, const int s, const double * const array, double & x) {\n" " if (index >= s)\n" " return false;\n" " else {\n" " x = array[index];\n" " return (index++) >= s;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (style) Condition '(index++)>=s' is always false\n", errout.str()); check("struct a {\n" " a *b() const;\n" "} c;\n" "void d() {\n" " a *e = nullptr;\n" " e = c.b();\n" " if (e) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int g(int i) {\n" " if (i < 256)\n" " return 1;\n" " const int N = 2 * i;\n" " i -= 256;\n" " if (i == 0)\n" " return 0;\n" " return N;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int i, int j) {\n" " if (i < j) {\n" " i++;\n" " if (i >= j)\n" " return;\n" " i++;\n" " if (i >= j) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int get_delta() {\n" " clock_t now_ms = (clock() / (CLOCKS_PER_SEC / 1000));\n" " static clock_t last_clock_ms = now_ms;\n" " clock_t delta = now_ms - last_clock_ms;\n" " last_clock_ms = now_ms;\n" " if (delta > 50)\n" " delta = 50;\n" " return delta;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10555 check("struct C {\n" " int GetI() const { return i; }\n" " int i{};\n" "};\n" "struct B {\n" " C *m_PC{};\n" " Modify();\n" "};\n" "struct D : B {\n" " void test(); \n" "};\n" "void D::test() {\n" " const int I = m_PC->GetI();\n" " Modify();\n" " if (m_PC->GetI() != I) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10624 check("struct Data {\n" " Base* PBase{};\n" "};\n" "void f(Data* BaseData) {\n" " Base* PObj = BaseData->PBase;\n" " if (PObj == nullptr)\n" " return;\n" " Derived* pD = dynamic_cast(PObj);\n" " if (pD) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9549 check("void f(const uint32_t v) {\n" " const uint32_t v16 = v >> 16;\n" " if (v16) {\n" " const uint32_t v8 = v16 >> 8;\n" " if (v8) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10649 check("void foo(struct diag_msg *msg) {\n" " msg = msg->next;\n" " if (msg == NULL)\n" " return CMD_OK;\n" " msg = msg->next;\n" " if (msg == NULL)\n" " return CMD_OK;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueInfer() { check("void f(int x) {\n" " if (x > 5) {\n" " x++;\n" " if (x == 1) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==1' is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 5) {\n" " x++;\n" " if (x != 1) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x!=1' is always true\n", errout.str()); // #6890 check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x == -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x==-1' is always false\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x != -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x!=-1' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x >= -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>=-1' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>-1' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x < -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x<-1' is always false\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x <= -1) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x<=-1' is always false\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > 7) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>7' is always true\n", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > 9) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " int x = i;\n" " if (x >= 1) {}\n" " else {\n" " x = 8 - x;\n" " if (x > 10) {}\n" " else {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #9541 check("int f(int pos, int a) {\n" " if (pos <= 0)\n" " pos = 0;\n" " else if (pos < a)\n" " if(pos > 0)\n" " --pos;\n" " return pos;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) Condition 'pos>0' is always true\n", errout.str()); // #9721 check("void f(int x) {\n" " if (x > 127) {\n" " if ( (x>255) || (-128>x) )\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition '-128>x' is always false\n", errout.str()); // #8778 check("void f() {\n" " for(int i = 0; i < 19; ++i)\n" " if(i<=18) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i<=18' is always true\n", errout.str()); // #8209 check("void f() {\n" " for(int x = 0; x < 3; ++x)\n" " if(x == -5) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'x==-5' is always false\n", errout.str()); // #8407 check("int f(void) {\n" " for(int i = 0; i <1; ++i)\n" " if(i == 0) return 1; \n" // << " else return 0;\n" " return -1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i==0' is always true\n", errout.str()); check("void f(unsigned int u1, unsigned int u2) {\n" " if (u1 <= 10 && u2 >= 20) {\n" " if (u1 != u2) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition 'u1!=u2' is always true\n", errout.str()); // #10544 check("void f(int N) {\n" " if (N > 0) {\n" " while (N)\n" " N = test();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueContainer() { // #9329 check("void c1(std::vector&);\n" "void c2(std::vector&);\n" "void foo(int flag) {\n" " std::vector g;\n" " if (flag)\n" " c1(g );\n" " else\n" " c2(g );\n" " if ( !g.empty() )\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int flag) {\n" " std::vector g;\n" " if (flag)\n" " c1(g );\n" " else\n" " c2(g );\n" " if ( !g.empty() )\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " void g();\n" " void f(bool b) {\n" " v.clear();\n" " g();\n" " return !v.empty();\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); // #10409 check("void foo(const std::string& s) {\n" " if( s.size() < 2 ) return;\n" " if( s == \"ab\" ) return;\n" " if( s.size() < 3 ) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(const std::string& s) {\n" " if( s.size() < 2 ) return;\n" " if( s != \"ab\" )\n" " if( s.size() < 3 ) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10226 check("int f(std::vector::iterator it, const std::vector& vector) {\n" " if (!(it != vector.end() && it != vector.begin()))\n" " throw 0;\n" " if (it != vector.end() && *it == 0)\n" " return -1;\n" " return *it;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'it!=vector.end()' is always true\n", errout.str()); } void alwaysTrueLoop() { check("long foo() {\n" " bool bUpdated = false;\n" " long Ret{};\n" " do {\n" " Ret = bar();\n" " if (Ret == 0) {\n" " if (bUpdated)\n" " return 1;\n" " bUpdated = true;\n" " }\n" " else\n" " bUpdated = false;\n" " }\n" " while (bUpdated);\n" " return Ret;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool foo() {\n" " bool bFirst = true;\n" " do {\n" " if (bFirst)\n" " bar();\n" " if (baz())\n" " break; \n" " bFirst = false;\n" " } while (true);\n" " return bFirst;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " void * pool = NULL;\n" " do {\n" " pool = malloc(40);\n" " if (dostuff())\n" " break;\n" " pool = NULL;\n" " }\n" " while (0);\n" " if (pool) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueTryCatch() { check("void g();\n" "void f(int x)\n" "{\n" " if( x ) {\n" " try {\n" " g();\n" " }\n" " catch(...) {\n" " return;\n" " }\n" " }\n" " g();\n" " if( x ) {\n" " g();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g();\n" "void h();\n" "void f(int x) {\n" " if( x ) {\n" " try {\n" " g();\n" " return;\n" " }\n" " catch( ... ) {}\n" " }\n" " h();\n" " if( x ) {\n" " g();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void multiConditionAlwaysTrue() { check("void f() {\n" " int val = 0;\n" " if (val < 0) continue;\n" " if (val > 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if (val > 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if (val < 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int activate = 0;\n" " int foo = 0;\n" " if (activate) {}\n" " else if (foo) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'activate' is always false\n" "[test.cpp:5]: (style) Condition 'foo' is always false\n", errout.str()); // #6904 check("void f() {\n" " const int b[2] = { 1,0 };\n" " if(b[1] == 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'b[1]==2' is always false\n", errout.str()); // #9878 check("void f(bool a, bool b) {\n" " if (a && b){;}\n" " else if (!a && b){;}\n" " else if (!a && !b){;}\n" " else {;}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateCondition() { check("void f(bool x) {\n" " if(x) {}\n" " if(x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x == 1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x == 2) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x != 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x) {\n" " if(x) {}\n" " g();\n" " if(x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if(x == 1) { x++; }\n" " if(x == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8996 check("void g(int** v);\n" "void f() {\n" " int a = 0;\n" " int b = 0;\n" " int* d[] = {&a, &b};\n" " g(d);\n" " if (a) {}\n" " if (b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #9311 check("struct c {\n" " int* p;\n" "};\n" "void g(struct c* v);\n" "void f() {\n" " int a = 0;\n" " int b = 0;\n" " struct c d[] = {{&a}, {&b}};\n" " g(d);\n" " if (a) {}\n" " if (b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8993 check("void f(const std::string& x) {\n" " auto y = x;\n" " if (x.empty()) y = \"1\";\n" " if (y.empty()) return;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9106 check("struct A {int b;};\n" "void f(A a, int c) {\n" " if (a.b) a.b = c;\n" " if (a.b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " void b() const {\n" " return a == 1;\n" " }\n" " void c();\n" " void d() {\n" " if(b()) {\n" " c();\n" " }\n" " if (b()) {\n" " a = 3;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " void b() const {\n" " return a == 1;\n" " }\n" " void d() {\n" " if(b()) {\n" " a = 2;\n" " }\n" " if (b()) {\n" " a = 3;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " void b() const {\n" " return a == 1;\n" " }\n" " void d() {\n" " if(b()) {\n" " }\n" " if (b()) {\n" " a = 3;\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(bool a, bool b) {\n" " auto g = [&] { b = !a; };\n" " if (b)\n" " g();\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(bool& a);\n" "void f(bool b) {\n" " auto h = std::bind(&g, std::ref(b));\n" " if (b)\n" " h();\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int *i) {\n" " if (*i == 0) {\n" " *i = 1;\n" " }\n" " if (*i == 0) {\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(std::function);\n" "void f(std::vector v) {\n" " auto x = [&v] { v.push_back(1); };\n" " if(v.empty()) {\n" " g(x);\n" " }\n" " if(v.empty())\n" " return;\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // do not crash check("void assign(const MMA& other) {\n" " if (mPA.cols != other.mPA.cols || mPA.rows != other.mPA.rows)\n" " ;\n" " if (other.mPA.cols > 0 && other.mPA.rows > 0)\n" " ;\n" "}"); } void checkInvalidTestForOverflow() { check("void f(char *p, unsigned int x) {\n" " assert((p + x) < p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)= p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)>=p'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always true.\n", errout.str()); check("void f(char *p, unsigned int x) {\n" " assert(p > (p + x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p>(p+x)'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always false.\n", errout.str()); check("void f(char *p, unsigned int x) {\n" " assert(p <= (p + x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p<=(p+x)'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always true.\n", errout.str()); check("void f(signed int x) {\n" // unsigned overflow => don't warn " assert(x + 100U < x);\n" "}"); ASSERT_EQUALS("", errout.str()); // x + c < x #define MSG(EXPR, RESULT) "[test.cpp:1]: (warning) Invalid test for overflow '" EXPR "'; signed integer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " RESULT ".\n" check("int f(int x) { return x + 10 > x; }"); ASSERT_EQUALS(MSG("x+10>x", "true"), errout.str()); check("int f(int x) { return x + 10 >= x; }"); ASSERT_EQUALS(MSG("x+10>=x", "true"), errout.str()); check("int f(int x) { return x + 10 < x; }"); ASSERT_EQUALS(MSG("x+10 x; }"); ASSERT_EQUALS(MSG("x-10>x", "false"), errout.str()); check("int f(int x) { return x - 10 >= x; }"); ASSERT_EQUALS(MSG("x-10>=x", "false"), errout.str()); check("int f(int x) { return x - 10 < x; }"); ASSERT_EQUALS(MSG("x-10 x; }"); ASSERT_EQUALS(MSG("x+y>x", "y>0"), errout.str()); check("int f(int x, int y) { return x + y >= x; }"); ASSERT_EQUALS(MSG("x+y>=x", "y>=0"), errout.str()); // x - y < x check("int f(int x, int y) { return x - y < x; }"); ASSERT_EQUALS(MSG("x-y0"), errout.str()); check("int f(int x, int y) { return x - y <= x; }"); ASSERT_EQUALS(MSG("x-y<=x", "y>=0"), errout.str()); check("int f(int x, int y) { return x - y > x; }"); ASSERT_EQUALS(MSG("x-y>x", "y<0"), errout.str()); check("int f(int x, int y) { return x - y >= x; }"); ASSERT_EQUALS(MSG("x-y>=x", "y<=0"), errout.str()); } void checkConditionIsAlwaysTrueOrFalseInsideIfWhile() { check("void f() {\n" " enum states {A,B,C};\n" " const unsigned g_flags = B|C;\n" " if(g_flags & A) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'g_flags&A' is always false\n", errout.str()); check("void f() {\n" " int a = 5;" " if(a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a' is always true\n", errout.str()); check("void f() {\n" " int a = 5;" " while(a + 1) { a--; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 5;" " while(a + 1) { return; }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a+1' is always true\n", errout.str()); } void alwaysTrueFalseInLogicalOperators() { check("bool f();\n" "void foo() { bool x = true; if(x||f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str()); check("void foo(bool b) { bool x = true; if(x||b) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always true\n", errout.str()); check("void foo(bool b) { if(true||b) {}}"); ASSERT_EQUALS("", errout.str()); check("bool f();\n" "void foo() { bool x = false; if(x||f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout.str()); check("bool f();\n" "void foo() { bool x = false; if(x&&f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout.str()); check("void foo(bool b) { bool x = false; if(x&&b) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always false\n", errout.str()); check("void foo(bool b) { if(false&&b) {}}"); ASSERT_EQUALS("", errout.str()); check("bool f();\n" "void foo() { bool x = true; if(x&&f()) {}}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str()); // #9578 check("bool f(const std::string &s) {\n" " return s.size()>2U && s[0]=='4' && s[0]=='2';\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 's[0]=='2'' is always false\n", errout.str()); } void pointerAdditionResultNotNull() { check("void f(char *ptr) {\n" " if (ptr + 1 != 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison is wrong. Result of 'ptr+1' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour.\n", errout.str()); } void duplicateConditionalAssign() { setMultiline(); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y;\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" "test.cpp:2:note:Condition 'x==y'\n" "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x != y)\n" " x = y;\n" "}"); ASSERT_EQUALS("test.cpp:2:style:The statement 'if (x!=y) x=y' is logically equivalent to 'x=y'.\n" "test.cpp:3:note:Assignment 'x=y'\n" "test.cpp:2:note:Condition 'x!=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y;\n" " else\n" " x = 1;\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" "test.cpp:2:note:Condition 'x==y'\n" "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x != y)\n" " x = y;\n" " else\n" " x = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y + 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g();\n" "void f(int& x, int y) {\n" " if (x == y) {\n" " x = y;\n" " g();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkAssignmentInCondition() { check("void f(std::string s) {\n" " if (s=\"123\"){}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious assignment in condition. Condition 's=\"123\"' is always true.\n", errout.str()); check("void f(std::string *p) {\n" " if (p=foo()){}\n" "}"); ASSERT_EQUALS("", errout.str()); } void compareOutOfTypeRange() { Settings settingsUnix64; settingsUnix64.severity.enable(Severity::style); settingsUnix64.platform(cppcheck::Platform::PlatformType::Unix64); check("void f(unsigned char c) {\n" " if (c == 256) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false.\n", errout.str()); check("void f(unsigned char c) {\n" " if (c == 255) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " if (b == true) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); // #10372 check("void f(signed char x) {\n" " if (x == 0xff) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed char' against value 255. Condition is always false.\n", errout.str()); check("void f(short x) {\n" " if (x == 0xffff) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed short' against value 65535. Condition is always false.\n", errout.str()); check("void f(int x) {\n" " if (x == 0xffffffff) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(long x) {\n" " if (x == ~0L) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(long long x) {\n" " if (x == ~0LL) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c;\n" " if ((c = foo()) != -1) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 3000000000) {}\n" "}", &settingsUnix64); ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed int' against value 3000000000. Condition is always true.\n", errout.str()); } void knownConditionCast() { // #9976 check("void f(int i) {\n" " if (i < 0 || (unsigned)i > 5) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void knownConditionIncrementLoop() { // #9808 check("void f() {\n" " int a = 0;\n" " while (++a < 5) {}\n" " if (a == 1) {}\n" " std::cout << a;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestCondition) cppcheck-2.7/test/testconstructors.cpp000066400000000000000000004120471417746362400203230ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkclass.h" #include "config.h" #include "errortypes.h" #include "standards.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include class TestConstructors : public TestFixture { public: TestConstructors() : TestFixture("TestConstructors") {} private: Settings settings; #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], bool inconclusive = false) { // Clear the error buffer.. errout.str(""); settings.certainty.setEnabled(Certainty::inconclusive, inconclusive); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check class constructors.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.constructors(); } void check_(const char* file, int line, const char code[], const Settings &s) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&s, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check class constructors.. CheckClass checkClass(&tokenizer, &s, this); checkClass.constructors(); } void run() OVERRIDE { settings.severity.enable(Severity::style); settings.severity.enable(Severity::warning); TEST_CASE(simple1); TEST_CASE(simple2); TEST_CASE(simple3); TEST_CASE(simple4); TEST_CASE(simple5); // ticket #2560 TEST_CASE(simple6); // ticket #4085 - uninstantiated template class TEST_CASE(simple7); // ticket #4531 TEST_CASE(simple8); TEST_CASE(simple9); // ticket #4574 TEST_CASE(simple10); // ticket #4388 TEST_CASE(simple11); // ticket #4536, #6214 TEST_CASE(simple12); // ticket #4620 TEST_CASE(simple13); // #5498 - no constructor, c++11 assignments TEST_CASE(simple14); // #6253 template base TEST_CASE(simple15); // #8942 multiple arguments, decltype TEST_CASE(noConstructor1); TEST_CASE(noConstructor2); TEST_CASE(noConstructor3); TEST_CASE(noConstructor4); TEST_CASE(noConstructor5); TEST_CASE(noConstructor6); // ticket #4386 TEST_CASE(noConstructor7); // ticket #4391 TEST_CASE(noConstructor8); // ticket #4404 TEST_CASE(noConstructor9); // ticket #4419 TEST_CASE(noConstructor10); // ticket #6614 TEST_CASE(noConstructor11); // ticket #3552 TEST_CASE(noConstructor12); // #8951 - member initialization TEST_CASE(noConstructor13); // #9998 TEST_CASE(forwardDeclaration); // ticket #4290/#3190 TEST_CASE(initvar_with_this); // BUG 2190300 TEST_CASE(initvar_if); // BUG 2190290 TEST_CASE(initvar_operator_eq1); // BUG 2190376 TEST_CASE(initvar_operator_eq2); // BUG 2190376 TEST_CASE(initvar_operator_eq3); TEST_CASE(initvar_operator_eq4); // ticket #2204 TEST_CASE(initvar_operator_eq5); // ticket #4119 TEST_CASE(initvar_operator_eq6); TEST_CASE(initvar_same_classname); // BUG 2208157 TEST_CASE(initvar_chained_assign); // BUG 2270433 TEST_CASE(initvar_2constructors); // BUG 2270353 TEST_CASE(initvar_constvar); TEST_CASE(initvar_staticvar); TEST_CASE(initvar_brace_init); TEST_CASE(initvar_union); TEST_CASE(initvar_delegate); // ticket #4302 TEST_CASE(initvar_delegate2); TEST_CASE(initvar_derived_class); // ticket #10161 TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor TEST_CASE(initvar_copy_constructor); // ticket #1611 TEST_CASE(initvar_nested_constructor); // ticket #1375 TEST_CASE(initvar_nocopy1); // ticket #2474 TEST_CASE(initvar_nocopy2); // ticket #2484 TEST_CASE(initvar_nocopy3); // ticket #3611 TEST_CASE(initvar_nocopy4); // ticket #9247 TEST_CASE(initvar_with_member_function_this); // ticket #4824 TEST_CASE(initvar_destructor); // No variables need to be initialized in a destructor TEST_CASE(initvar_func_ret_func_ptr); // ticket #4449 TEST_CASE(initvar_alias); // #6921 TEST_CASE(initvar_templateMember); // #7205 TEST_CASE(initvar_smartptr); // #10237 TEST_CASE(operatorEqSTL); TEST_CASE(uninitVar1); TEST_CASE(uninitVar2); TEST_CASE(uninitVar3); TEST_CASE(uninitVar4); TEST_CASE(uninitVar5); TEST_CASE(uninitVar6); TEST_CASE(uninitVar7); TEST_CASE(uninitVar8); TEST_CASE(uninitVar9); // ticket #1730 TEST_CASE(uninitVar10); // ticket #1993 TEST_CASE(uninitVar11); TEST_CASE(uninitVar12); // ticket #2078 TEST_CASE(uninitVar13); // ticket #1195 TEST_CASE(uninitVar14); // ticket #2149 TEST_CASE(uninitVar15); TEST_CASE(uninitVar16); TEST_CASE(uninitVar17); TEST_CASE(uninitVar18); // ticket #2465 TEST_CASE(uninitVar19); // ticket #2792 TEST_CASE(uninitVar20); // ticket #2867 TEST_CASE(uninitVar21); // ticket #2947 TEST_CASE(uninitVar22); // ticket #3043 TEST_CASE(uninitVar23); // ticket #3702 TEST_CASE(uninitVar24); // ticket #3190 TEST_CASE(uninitVar25); // ticket #4789 TEST_CASE(uninitVar26); TEST_CASE(uninitVar27); // ticket #5170 - rtl::math::setNan(&d) TEST_CASE(uninitVar28); // ticket #6258 TEST_CASE(uninitVar29); TEST_CASE(uninitVar30); // ticket #6417 TEST_CASE(uninitVar31); // ticket #8271 TEST_CASE(uninitVar32); // ticket #8835 TEST_CASE(uninitVar33); // ticket #10295 TEST_CASE(uninitVarEnum1); TEST_CASE(uninitVarEnum2); // ticket #8146 TEST_CASE(uninitVarStream); TEST_CASE(uninitVarTypedef); TEST_CASE(uninitVarMemset); TEST_CASE(uninitVarArray1); TEST_CASE(uninitVarArray2); TEST_CASE(uninitVarArray3); TEST_CASE(uninitVarArray4); TEST_CASE(uninitVarArray5); TEST_CASE(uninitVarArray6); TEST_CASE(uninitVarArray7); TEST_CASE(uninitVarArray8); TEST_CASE(uninitVarArray9); // ticket #6957, #6959 TEST_CASE(uninitVarArray2D); TEST_CASE(uninitVarArray3D); TEST_CASE(uninitVarCpp11Init1); TEST_CASE(uninitVarCpp11Init2); TEST_CASE(uninitVarStruct1); // ticket #2172 TEST_CASE(uninitVarStruct2); // ticket #838 TEST_CASE(uninitVarUnion1); // ticket #3196 TEST_CASE(uninitVarUnion2); TEST_CASE(uninitMissingFuncDef); // can't expand function in constructor TEST_CASE(privateCtor1); // If constructor is private.. TEST_CASE(privateCtor2); // If constructor is private.. TEST_CASE(function); // Function is not variable TEST_CASE(uninitVarPublished); // Borland C++: Variables in the published section are auto-initialized TEST_CASE(uninitVarInheritClassInit); // Borland C++: if class inherits from TObject, all variables are initialized TEST_CASE(uninitOperator); // No FP about uninitialized 'operator[]' TEST_CASE(uninitFunction1); // No FP when initialized in function TEST_CASE(uninitFunction2); // No FP when initialized in function TEST_CASE(uninitFunction3); // No FP when initialized in function TEST_CASE(uninitFunction4); TEST_CASE(uninitFunction5); TEST_CASE(uninitSameClassName); // No FP when two classes have the same name TEST_CASE(uninitFunctionOverload); // No FP when there are overloaded functions TEST_CASE(uninitVarOperatorEqual); // ticket #2415 TEST_CASE(uninitVarPointer); // ticket #3801 TEST_CASE(uninitConstVar); TEST_CASE(constructors_crash1); // ticket #5641 TEST_CASE(classWithOperatorInName);// ticket #2827 TEST_CASE(templateConstructor); // ticket #7942 TEST_CASE(typedefArray); // ticket #5766 TEST_CASE(uninitAssignmentWithOperator); // ticket #7429 TEST_CASE(uninitCompoundAssignment); // ticket #7429 TEST_CASE(uninitComparisonAssignment); // ticket #7429 TEST_CASE(uninitTemplate1); // ticket #7372 TEST_CASE(unknownTemplateType); } void simple1() { check("class Fred\n" "{\n" "public:\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) The class 'Fred' does not declare a constructor although it has private member variables which likely require initialization.\n", errout.str()); check("struct Fred\n" "{\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) The struct 'Fred' does not declare a constructor although it has private member variables which likely require initialization.\n", errout.str()); } void simple2() { check("class Fred\n" "{\n" "public:\n" " Fred() : i(0) { }\n" " Fred(Fred const & other) : i(other.i) {}\n" " Fred(Fred && other) : i(other.i) {}\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { i = 0; }\n" " Fred(Fred const & other) {i=other.i}\n" " Fred(Fred && other) {i=other.i}\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { }\n" " Fred(Fred const & other) {}\n" " Fred(Fred && other) {}\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); check("struct Fred\n" "{\n" " Fred() { }\n" " Fred(Fred const & other) {}\n" " Fred(Fred && other) {}\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void simple3() { check("struct Fred\n" "{\n" " Fred();\n" " int i;\n" "};\n" "Fred::Fred() :i(0)\n" "{ }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " Fred();\n" " int i;\n" "};\n" "Fred::Fred()\n" "{ i = 0; }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " Fred();\n" " int i;\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void simple4() { check("struct Fred\n" "{\n" " Fred();\n" " explicit Fred(int _i);\n" " Fred(Fred const & other);\n" " int i;\n" "};\n" "Fred::Fred()\n" "{ }\n" "Fred::Fred(int _i)\n" "{\n" " i = _i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:8]: (warning, inconclusive) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void simple5() { // ticket #2560 check("namespace Nsp\n" "{\n" " class B { };\n" "}\n" "class Altren : public Nsp::B\n" "{\n" "public:\n" " Altren () : Nsp::B(), mValue(0)\n" " {\n" " }\n" "private:\n" " int mValue;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple6() { // ticket #4085 - uninstantiated template class check("template struct A {\n" " A() { x = 0; }\n" " A(const T & t) { x = t.x; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); check("template struct A {\n" " A() : x(0) { }\n" " A(const T & t) : x(t.x) { }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); check("template struct A {\n" " A() : x(0) { }\n" " A(const T & t) : x(t.x) { }\n" "private:\n" " int x;\n" " int y;\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (warning) Member variable 'A::y' is not initialized in the constructor.\n" "[test.cpp:3]: (warning) Member variable 'A::y' is not initialized in the constructor.\n", errout.str()); } void simple7() { // ticket #4531 check("class Fred;\n" "struct Fred {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple8() { check("struct Fred { int x; };\n" "class Barney { Fred fred; };\n" "class Wilma { struct Betty { int x; } betty; };"); ASSERT_EQUALS("[test.cpp:2]: (style) The class 'Barney' does not declare a constructor although it has private member variables which likely require initialization.\n" "[test.cpp:3]: (style) The class 'Wilma' does not declare a constructor although it has private member variables which likely require initialization.\n", errout.str()); } void simple9() { // ticket #4574 check("class Unknown::Fred {\n" "public:\n" " Fred() : x(0) { }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple10() { // ticket #4388 check("class Fred {\n" "public:\n" " Fred() = default;\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple11() { // ticket #4536, #6214 check("class Fred {\n" "public:\n" " Fred() {}\n" "private:\n" " int x = 0;\n" " int y = f();\n" " int z{0};\n" " int (*pf[2])(){nullptr, nullptr};\n" " int a[2][3] = {{1,2,3},{4,5,6}};\n" " int b{1}, c{2};\n" " int d, e{3};\n" " int f{4}, g;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n" "[test.cpp:3]: (warning) Member variable 'Fred::g' is not initialized in the constructor.\n", errout.str()); } void simple12() { // ticket #4620 check("class Fred {\n" " int x;\n" "public:\n" " Fred() { Init(); }\n" " void Init(int i = 0);\n" "};\n" "void Fred::Init(int i) { x = i; }"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " int x;\n" " int y;\n" "public:\n" " Fred() { Init(0); }\n" " void Init(int i, int j = 0);\n" "};\n" "void Fred::Init(int i, int j) { x = i; y = j; }"); ASSERT_EQUALS("", errout.str()); } void simple13() { // #5498 check("class Fred {\n" " int x=1;\n" " int *y=0;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple14() { // #6253 template base check("class Fred : public Base {" "public:" " Fred()\n" " :Base(1),\n" " x(1)\n" " {}\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred : public Base {" "public:" " Fred()\n" " :Base{1},\n" " x{1}\n" " {}\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple15() { // #8942 check("class A\n" "{\n" "public:\n" " int member;\n" "};\n" "class B\n" "{\n" "public:\n" " B(const decltype(A::member)& x, const decltype(A::member)& y) : x(x), y(y) {}\n" "private:\n" " const decltype(A::member)& x;\n" " const decltype(A::member)& y;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void noConstructor1() { // There are nonstatic member variables - constructor is needed check("class Fred\n" "{\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) The class 'Fred' does not declare a constructor although it has private member variables which likely require initialization.\n", errout.str()); } void noConstructor2() { check("class Fred\n" "{\n" "public:\n" " static void foobar();\n" "};\n" "\n" "void Fred::foobar()\n" "{ }"); ASSERT_EQUALS("", errout.str()); } void noConstructor3() { check("class Fred\n" "{\n" "private:\n" " static int foobar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor4() { check("class Fred\n" "{\n" "public:\n" " int foobar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor5() { check("namespace Foo\n" "{\n" " int i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void noConstructor6() { // ticket #4386 check("class Ccpucycles {\n" " friend class foo::bar;\n" " Ccpucycles() :\n" " m_v(0), m_b(true)\n" " {}\n" "private:\n" " cpucyclesT m_v;\n" " bool m_b;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor7() { // ticket #4391 check("short bar;\n" "class foo;"); ASSERT_EQUALS("", errout.str()); } void noConstructor8() { // ticket #4404 check("class LineSegment;\n" "class PointArray { };\n" "void* tech_ = NULL;"); ASSERT_EQUALS("", errout.str()); } void noConstructor9() { // ticket #4419 check("class CGreeting : public CGreetingBase {\n" "public:\n" " CGreeting() : CGreetingBase(), MessageSet(false) {}\n" "private:\n" " bool MessageSet;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor10() { // ticket #6614 check("class A : public wxDialog\n" "{\n" "private:\n" " DECLARE_EVENT_TABLE()\n" "public:\n" " A(wxWindow *parent,\n" " wxWindowID id = 1,\n" " const wxString &title = wxT(" "),\n" " const wxPoint& pos = wxDefaultPosition,\n" " const wxSize& size = wxDefaultSize,\n" " long style = wxDIALOG_NO_PARENT | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX);\n" " virtual ~A();\n" "private:\n" " wxTimer *WxTimer1;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor11() { // #3552 check("class Fred { int x; };\n" "union U { int y; Fred fred; };"); ASSERT_EQUALS("", errout.str()); } void noConstructor12() { // #8951 check("class Fred { int x{0}; };"); ASSERT_EQUALS("", errout.str()); check("class Fred { int x=0; };"); ASSERT_EQUALS("", errout.str()); check("class Fred { int x[1]={0}; };"); // #8850 ASSERT_EQUALS("", errout.str()); check("class Fred { int x[1]{0}; };"); ASSERT_EQUALS("", errout.str()); } void noConstructor13() { // #9998 check("struct C { int v; };\n" "struct B { C c[5] = {}; };\n" "struct A {\n" " A() {}\n" " B b;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } // ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor." // ticket #3190 "SymbolDatabase: Parse of sub class constructor fails" void forwardDeclaration() { check("class foo;\n" "int bar;"); ASSERT_EQUALS("", errout.str()); check("class foo;\n" "class foo;"); ASSERT_EQUALS("", errout.str()); check("class foo{};\n" "class foo;"); ASSERT_EQUALS("", errout.str()); } void initvar_with_this() { check("struct Fred\n" "{\n" " Fred()\n" " { this->i = 0; }\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_if() { check("struct Fred\n" "{\n" " Fred()\n" " {\n" " if (true)\n" " i = 0;\n" " else\n" " i = 1;\n" " }\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq1() { // Bug 2190376 and #3820 - False positive, Uninitialized member variable with operator= check("struct Fred\n" "{\n" " int i;\n" "\n" " Fred()\n" " { i = 0; }\n" "\n" " Fred(const Fred &fred)\n" " { *this = fred; }\n" "\n" " const Fred & operator=(const Fred &fred)\n" " { i = fred.i; return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Fred {\n" " int i;\n" "\n" " Fred(const Fred &fred)\n" " { (*this) = fred; }\n" "\n" " const Fred & operator=(const Fred &fred)\n" " { i = fred.i; return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " A() : i(0), j(0) {}\n" "\n" " A &operator=(const int &value)\n" " {\n" " i = value;\n" " return (*this);\n" " }\n" "\n" " int i;\n" " int j;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq2() { check("struct Fred\n" "{\n" " Fred() { i = 0; }\n" " void operator=(const Fred &fred) { }\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void initvar_operator_eq3() { check("struct Fred\n" "{\n" " Fred() { Init(); }\n" " void operator=(const Fred &fred) { Init(); }\n" "private:\n" " void Init() { i = 0; }\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq4() { check("class Fred\n" "{\n" " int i;\n" "public:\n" " Fred() : i(5) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("class Fred\n" "{\n" " int * i;\n" "public:\n" " Fred() : i(NULL) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("class Fred\n" "{\n" " const int * i;\n" "public:\n" " Fred() : i(NULL) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("class Fred\n" "{\n" " const int i;\n" "public:\n" " Fred() : i(5) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq5() { // #4119 - false positive when using swap to assign variables check("class Fred {\n" " int i;\n" "public:\n" " Fred() : i(5) { }\n" " ~Fred() { }\n" " Fred(const Fred &fred) : i(fred.i) { }\n" " Fred & operator=(const Fred &rhs) {\n" " Fred(rhs).swap(*this);\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq6() { // std::vector check("struct Fred {\n" " uint8_t data;\n" " Fred & operator=(const Fred &rhs) {\n" " return *this;\n" " }\n" "};",true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::data' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("struct Fred {\n" " std::vector ints;\n" " Fred & operator=(const Fred &rhs) {\n" " return *this;\n" " }\n" "};",true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::ints' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("struct Fred {\n" " Data data;\n" " Fred & operator=(const Fred &rhs) {\n" " return *this;\n" " }\n" "};",true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::data' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void initvar_same_classname() { // Bug 2208157 - False positive: Uninitialized variable, same class name check("void func1()\n" "{\n" " class Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" "}\n" "\n" "void func2()\n" "{\n" " class Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("void func1()\n" "{\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" "}\n" "\n" "void func2()\n" "{\n" " class Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("void func1()\n" "{\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" "}\n" "\n" "void func2()\n" "{\n" " struct Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Foo {\n" " void func1()\n" " {\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" " }\n" "\n" " void func2()\n" " {\n" " struct Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo {\n" " void func1()\n" " {\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { }\n" " };\n" " }\n" "\n" " void func2()\n" " {\n" " struct Fred\n" " {\n" " int b;\n" " Fred() { }\n" " };\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::a' is not initialized in the constructor.\n" "[test.cpp:16]: (warning) Member variable 'Fred::b' is not initialized in the constructor.\n", errout.str()); } void initvar_chained_assign() { // Bug 2270433 - Uninitialized variable false positive on chained assigns check("struct c\n" "{\n" " c();\n" "\n" " int m_iMyInt1;\n" " int m_iMyInt2;\n" "}\n" "\n" "c::c()\n" "{\n" " m_iMyInt1 = m_iMyInt2 = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void initvar_2constructors() { check("struct c\n" "{\n" " c();\n" " explicit c(bool b);" "\n" " void InitInt();\n" "\n" " int m_iMyInt;\n" " int m_bMyBool;\n" "}\n" "\n" "c::c()\n" "{\n" " m_bMyBool = false;\n" " InitInt();" "}\n" "\n" "c::c(bool b)\n" "{\n" " m_bMyBool = b;\n" " InitInt();\n" "}\n" "\n" "void c::InitInt()\n" "{\n" " m_iMyInt = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void initvar_constvar() { check("struct Fred\n" "{\n" " const char *s;\n" " Fred();\n" "};\n" "Fred::Fred() : s(NULL)\n" "{ }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " const char *s;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ s = NULL; }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " const char *s;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::s' is not initialized in the constructor.\n", errout.str()); } void initvar_staticvar() { check("class Fred\n" "{\n" "public:\n" " Fred() { }\n" " static void *p;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_brace_init() { // #10142 check("class C\n" "{\n" "public:\n" " C() {}\n" "\n" "private:\n" " std::map * values_{};\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_union() { check("class Fred\n" "{\n" " union\n" " {\n" " int a;\n" " char b[4];\n" " } U;\n" "public:\n" " Fred()\n" " {\n" " U.a = 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" " union\n" " {\n" " int a;\n" " char b[4];\n" " } U;\n" "public:\n" " Fred()\n" " {\n" " }\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Fred::U' is not initialized in the constructor.\n", "", errout.str()); } void initvar_delegate() { check("class A {\n" " int number;\n" "public:\n" " A(int n) { }\n" " A() : A(42) {}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) { number = n; }\n" " A() : A(42) {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A() { }\n" " A() {}\n" "};", true); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning, inconclusive) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A() { }\n" " A() { number = 42; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) { }\n" " A() : A{42} {}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) { number = n; }\n" " A() : A{42} {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A{} { }\n" " A() {}\n" "};", true); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning, inconclusive) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A{} { }\n" " A() { number = 42; }\n" "};"); ASSERT_EQUALS("", errout.str()); // Ticket #6675 check("struct Foo {\n" " Foo();\n" " Foo(int foo);\n" " int foo_;\n" "};\n" "Foo::Foo() : Foo(0) {}\n" "Foo::Foo(int foo) : foo_(foo) {}"); ASSERT_EQUALS("", errout.str()); // Noexcept ctors check("class A {\n" "private:\n" " int _a;\n" "public:\n" " A(const int a) noexcept : _a{a} {}\n" " A() noexcept;\n" "};\n" "\n" "A::A() noexcept: A(0) {}"); ASSERT_EQUALS("", errout.str()); // Ticket #8581 check("class A {\n" "private:\n" " int _a;\n" "public:\n" " A(int a) : _a(a) {}\n" " A(float a) : A(int(a)) {}\n" "};"); ASSERT_EQUALS("", errout.str()); // Ticket #8258 check("struct F{};\n" "struct Foo {\n" " Foo(int a, F&& f, int b = 21) : _a(a), _b(b), _f(f) {}\n" " Foo(int x, const char* value) : Foo(x, F(), 42) {}\n" " Foo(int x, int* value) : Foo(x, F()) {}\n" " int _a;\n" " int _b;\n" " F _f;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_delegate2() { check("class Foo {\n" "public:\n" " explicit Foo(const Bar bar);\n" " Foo(const std::string& id);\n" " virtual ~RtpSession() { }\n" "protected:\n" " bool a;\n" " uint16_t b;\n" "};\n" "\n" "Foo::Foo(const Bar var)\n" " : Foo(bar->getId())\n" "{\n" "}\n" "\n" "Foo::Foo(const std::string& id)\n" " : a(true)\n" " , b(0)\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); } void initvar_derived_class() { check("class Base {\n" "public:\n" " virtual void foo() = 0;\n" " int x;\n" // <- uninitialized "};\n" "\n" "class Derived: public Base {\n" "public:\n" " Derived() {}\n" " void foo() override;\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Base::x' is not initialized in the constructor. Maybe it should be initialized directly in the class Base?\n", errout.str()); } void initvar_private_constructor() { settings.standards.cpp = Standards::CPP11; check("class Fred\n" "{\n" "private:\n" " int var;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::var' is not initialized in the constructor.\n", errout.str()); settings.standards.cpp = Standards::CPP03; check("class Fred\n" "{\n" "private:\n" " int var;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("", errout.str()); } void initvar_copy_constructor() { // ticket #1611 check("class Fred\n" "{\n" "private:\n" " std::string var;\n" "public:\n" " Fred() { };\n" " Fred(const Fred &) { };\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "private:\n" " std::string var;\n" "public:\n" " Fred() { };\n" " Fred(const Fred &) { };\n" "};", true); ASSERT_EQUALS("[test.cpp:7]: (warning, inconclusive) Member variable 'Fred::var' is not assigned in the copy constructor. Should it be copied?\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " std::string var;\n" "public:\n" " Fred();\n" " Fred(const Fred &);\n" "};\n" "Fred::Fred() { };\n" "Fred::Fred(const Fred &) { };\n", true); ASSERT_EQUALS("[test.cpp:10]: (warning, inconclusive) Member variable 'Fred::var' is not assigned in the copy constructor. Should it be copied?\n", errout.str()); } void initvar_nested_constructor() { // ticket #1375 check("class A {\n" "public:\n" " A();\n" " struct B {\n" " explicit B(int x);\n" " struct C {\n" " explicit C(int y);\n" " struct D {\n" " int d;\n" " explicit D(int z);\n" " };\n" " int c;\n" " };\n" " int b;\n" " };\n" "private:\n" " int a;\n" " B b;\n" "};\n" "A::A(){}\n" "A::B::B(int x){}\n" "A::B::C::C(int y){}\n" "A::B::C::D::D(int z){}"); // Note that the example code is not compilable. The A constructor must // explicitly initialize A::b. A warning for A::b is not necessary. ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:23]: (warning) Member variable 'D::d' is not initialized in the constructor.\n", errout.str()); check("class A {\n" "public:\n" " A();\n" " struct B {\n" " explicit B(int x);\n" " struct C {\n" " explicit C(int y);\n" " struct D {\n" " D(const D &);\n" " int d;\n" " };\n" " int c;\n" " };\n" " int b;\n" " };\n" "private:\n" " int a;\n" " B b;\n" "};\n" "A::A(){}\n" "A::B::B(int x){}\n" "A::B::C::C(int y){}\n" "A::B::C::D::D(const A::B::C::D & d){}"); // Note that the example code is not compilable. The A constructor must // explicitly initialize A::b. A warning for A::b is not necessary. ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:23]: (warning) Member variable 'D::d' is not initialized in the constructor.\n", errout.str()); check("class A {\n" "public:\n" " A();\n" " struct B {\n" " explicit B(int x);\n" " struct C {\n" " explicit C(int y);\n" " struct D {\n" " struct E { int e; };\n" " struct E d;\n" " explicit D(const E &);\n" " };\n" " int c;\n" " };\n" " int b;\n" " };\n" "private:\n" " int a;\n" " B b;\n" "};\n" "A::A(){}\n" "A::B::B(int x){}\n" "A::B::C::C(int y){}\n" "A::B::C::D::D(const A::B::C::D::E & e){}"); // Note that the example code is not compilable. The A constructor must // explicitly initialize A::b. A warning for A::b is not necessary. ASSERT_EQUALS("[test.cpp:21]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:23]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:24]: (warning) Member variable 'D::d' is not initialized in the constructor.\n", errout.str()); } void initvar_nocopy1() { // ticket #2474 check("class B\n" "{\n" " B (const B & Var);\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" " B (B && Var);\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" " B & operator= (const B & Var);\n" "public:\n" " B ();\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" "public:\n" " B (const B & Var);\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A : public std::vector\n" "{\n" "public:\n" " A(const A &a);\n" "};\n" "class B\n" "{\n" " A a;\n" "public:\n" " B(){}\n" " B(const B&){}\n" " B(B &&){}\n" " const B& operator=(const B&){return *this;}\n" "};", true); ASSERT_EQUALS("[test.cpp:11]: (warning, inconclusive) Member variable 'B::a' is not assigned in the copy constructor. Should it be copied?\n" "[test.cpp:12]: (warning, inconclusive) Member variable 'B::a' is not assigned in the copy constructor. Should it be copied?\n" "[test.cpp:13]: (warning, inconclusive) Member variable 'B::a' is not assigned a value in 'B::operator='.\n", errout.str()); check("class B\n" "{\n" "public:\n" " B (B && Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n", errout.str()); check("class B\n" "{\n" "public:\n" " B ();\n" " B & operator= (const B & Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_nocopy2() { // ticket #2484 check("class B\n" "{\n" " B (B & Var);\n" " B & operator= (const B & Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " const A& operator=(const A&){return *this;}\n" "};", true); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" "public:\n" " B (B & Var);\n" " B & operator= (const B & Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " const A& operator=(const A&){return *this;}\n" "};", true); ASSERT_EQUALS("[test.cpp:12]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n" "[test.cpp:13]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'A::m_SemVar' is not assigned a value in 'A::operator='.\n", errout.str()); } void initvar_nocopy3() { // #3611 - unknown type is non-copyable check("struct A {\n" " B b;\n" " A() {}\n" " A(const A& rhs) {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " B b;\n" " A() {}\n" " A(const A& rhs) {}\n" "};", true); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Member variable 'A::b' is not assigned in the copy constructor. Should it be copied?\n", errout.str()); } void initvar_nocopy4() { // #9247 check("struct S {\n" " S(const S & s);\n" " void S::Set(const T& val);\n" " void S::Set(const U& val);\n" " T t;\n" "};\n" "S::S(const S& s) {\n" " Set(s.t);\n" "}\n" "void S::Set(const T& val) {\n" " t = val;\n" "}", /*inconclusive*/ true); ASSERT_EQUALS("", errout.str()); } void initvar_with_member_function_this() { check("struct Foo {\n" " Foo(int m) { this->setMember(m); }\n" " void setMember(int m) { member = m; }\n" " int member;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_destructor() { check("class Fred\n" "{\n" "private:\n" " int var;\n" "public:\n" " Fred() : var(0) {}\n" " ~Fred() {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_func_ret_func_ptr() { // ticket #4449 (segmentation fault) check("class something {\n" " int * ( something :: * process()) () { return 0; }\n" " something() { process(); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_alias() { // #6921 check("struct S {\n" " int a;\n" " S() {\n" " int& pa = a;\n" " pa = 4;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int a;\n" " S() {\n" " int* pa = &a;\n" " *pa = 4;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int a[2];\n" " S() {\n" " int* pa = a;\n" " for (int i = 0; i < 2; i++)\n" " *pa++ = i;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* a[2];\n" " S() {\n" " int* pa = a[1];\n" " *pa = 0;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'S::a' is not initialized in the constructor.\n", errout.str()); check("struct S {\n" " int a;\n" " S() {\n" " int pa = a;\n" " pa = 4;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'S::a' is not initialized in the constructor.\n", errout.str()); } void initvar_templateMember() { check("template\n" "struct Wrapper {\n" " static void foo(int * x) {\n" " for (int i(0); i <= n_; ++i)\n" " x[i] = 5;\n" " }\n" "};\n" "class A {\n" "public:\n" " static constexpr int dim = 5;\n" " int x[dim + 1];\n" " A() {\n" " Wrapper::foo(x);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_smartptr() { // #10237 Settings s; s.libraries.emplace_back("std"); check("struct S {\n" " explicit S(const std::shared_ptr& sp) {\n" " set(*sp);\n" " }\n" " double get() const {\n" " return d;\n" " }\n" " void set(const S& rhs) {\n" " d = rhs.get();\n" " }\n" " double d;\n" "};", s); ASSERT_EQUALS("", errout.str()); } void operatorEqSTL() { check("class Fred\n" "{\n" "private:\n" " std::vector ints;\n" "public:\n" " Fred();\n" " void operator=(const Fred &f);\n" "};\n" "\n" "Fred::Fred()\n" "{ }\n" "\n" "void Fred::operator=(const Fred &f)\n" "{ }", true); ASSERT_EQUALS("[test.cpp:13]: (warning, inconclusive) Member variable 'Fred::ints' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void uninitVar1() { check("enum ECODES\n" "{\n" " CODE_1 = 0,\n" " CODE_2 = 1\n" "};\n" "\n" "class Fred\n" "{\n" "public:\n" " Fred() {}\n" "\n" "private:\n" " ECODES _code;\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Fred::_code' is not initialized in the constructor.\n", errout.str()); check("class A{};\n" "\n" "class B : public A\n" "{\n" "public:\n" " B() {}\n" "private:\n" " float f;\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'B::f' is not initialized in the constructor.\n", errout.str()); check("class C\n" "{\n" " FILE *fp;\n" "\n" "public:\n" " explicit C(FILE *fp);\n" "};\n" "\n" "C::C(FILE *fp) {\n" " C::fp = fp;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar2() { check("class John\n" "{\n" "public:\n" " John() { (*this).i = 0; }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar3() { // No FP when struct has constructor check("class Foo\n" "{\n" "public:\n" " Foo() { }\n" "private:\n" " struct Bar {\n" " Bar();\n" " };\n" " Bar bars[2];\n" "};"); ASSERT_EQUALS("", errout.str()); // Using struct that doesn't have constructor check("class Foo\n" "{\n" "public:\n" " Foo() { }\n" "private:\n" " struct Bar {\n" " int x;\n" " };\n" " Bar bars[2];\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::bars' is not initialized in the constructor.\n", errout.str()); } void uninitVar4() { check("class Foo\n" "{\n" "public:\n" " Foo() { bar.x = 0; }\n" "private:\n" " struct Bar {\n" " int x;\n" " };\n" " struct Bar bar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar5() { check("class Foo\n" "{\n" "public:\n" " Foo() { }\n" " Foo &operator=(const Foo &)\n" " { return *this; }\n" " static int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar6() { check("class Foo : public Bar\n" "{\n" "public:\n" " explicit Foo(int i) : Bar(mi=i) { }\n" "private:\n" " int mi;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo : public Bar\n" "{\n" "public:\n" " explicit Foo(int i) : Bar{mi=i} { }\n" "private:\n" " int mi;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar7() { check("class Foo {\n" " int a;\n" "public:\n" " Foo() : a(0) {}\n" " Foo& operator=(const Foo&);\n" " void Swap(Foo& rhs);\n" "};\n" "\n" "void Foo::Swap(Foo& rhs) {\n" " std::swap(a,rhs.a);\n" "}\n" "\n" "Foo& Foo::operator=(const Foo& rhs) {\n" " Foo copy(rhs);\n" " copy.Swap(*this);\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar8() { check("class Foo {\n" " int a;\n" "public:\n" " Foo() : a(0) {}\n" " Foo& operator=(const Foo&);\n" "};\n" "\n" "Foo& Foo::operator=(const Foo& rhs) {\n" " if (&rhs != this)\n" " {\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'Foo::a' is not assigned a value in 'Foo::operator='.\n", errout.str()); } void uninitVar9() { // ticket #1730 check("class Prefs {\n" "private:\n" " int xasd;\n" "public:\n" " explicit Prefs(wxSize size);\n" "};\n" "Prefs::Prefs(wxSize size)\n" "{\n" " SetMinSize( wxSize( 48,48 ) );\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Prefs::xasd' is not initialized in the constructor.\n", errout.str()); } void uninitVar10() { // ticket #1993 check("class A {\n" "public:\n" " A();\n" "private:\n" " int var1;\n" " int var2;\n" "};\n" "A::A() : var1(0) { }"); ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'A::var2' is not initialized in the constructor.\n", errout.str()); } void uninitVar11() { check("class A {\n" "public:\n" " explicit A(int a = 0);\n" "private:\n" " int var;\n" "};\n" "A::A(int a) { }"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::var' is not initialized in the constructor.\n", errout.str()); } void uninitVar12() { // ticket #2078 check("class Point\n" "{\n" "public:\n" " Point()\n" " {\n" " Point(0, 0);\n" " }\n" " Point(int x, int y)\n" " : x(x), y(y)\n" " {}\n" " int x, y;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Point::x' is not initialized in the constructor.\n" "[test.cpp:4]: (warning) Member variable 'Point::y' is not initialized in the constructor.\n", errout.str()); } void uninitVar13() { // ticket #1195 check("class A {\n" "private:\n" " std::vector *ints;\n" "public:\n" " A()\n" " {}\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'A::ints' is not initialized in the constructor.\n", errout.str()); } void uninitVar14() { // ticket #2149 // no namespace check("class Foo\n" "{\n" "public:\n" " Foo();\n" "private:\n" " bool mMember;\n" "};\n" "Foo::Foo()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // single namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor outside namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "Foo::Foo()\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor outside namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "Output::Foo::Foo()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor outside namespace with using, #4792 check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "using namespace Output;" "Foo::Foo()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor in separate namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "namespace Output\n" "{\n" " Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor in different separate namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "namespace Input\n" "{\n" " Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor in different separate namespace (won't compile) check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "namespace Input\n" "{\n" " Output::Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor in nested separate namespace check("namespace A\n" "{\n" " namespace Output\n" " {\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " }\n" " namespace Output\n" " {\n" " Foo::Foo()\n" " {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:15]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor in nested different separate namespace check("namespace A\n" "{\n" " namespace Output\n" " {\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " }\n" " namespace Input\n" " {\n" " Foo::Foo()\n" " {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor in nested different separate namespace check("namespace A\n" "{\n" " namespace Output\n" " {\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " }\n" " namespace Input\n" " {\n" " Output::Foo::Foo()\n" " {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar15() { check("class Fred\n" "{\n" " int a;\n" "public:\n" " Fred();\n" " ~Fred();\n" "};\n" "Fred::~Fred()\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar16() { check("struct Foo\n" "{\n" " int a;\n" " void set(int x) { a = x; }\n" "};\n" "class Bar\n" "{\n" " Foo foo;\n" "public:\n" " Bar()\n" " {\n" " foo.set(0);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Foo\n" "{\n" " int a;\n" " void set(int x) { a = x; }\n" "};\n" "class Bar\n" "{\n" " Foo foo;\n" "public:\n" " Bar()\n" " {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Bar::foo' is not initialized in the constructor.\n", errout.str()); } void uninitVar17() { check("struct Foo\n" "{\n" " int a;\n" "};\n" "class Bar\n" "{\n" " Foo foo[10];\n" "public:\n" " Bar()\n" " {\n" " foo[0].a = 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Foo\n" "{\n" " int a;\n" "};\n" "class Bar\n" "{\n" " Foo foo[10];\n" "public:\n" " Bar()\n" " {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Bar::foo' is not initialized in the constructor.\n", errout.str()); } void uninitVar18() { // ticket #2465 check("struct Altren\n" "{\n" " explicit Altren(int _a = 0) : value(0) { }\n" " int value;\n" "};\n" "class A\n" "{\n" "public:\n" " A() { }\n" "private:\n" " Altren value;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar19() { // ticket #2792 check("class mystring\n" "{\n" " char* m_str;\n" " int m_len;\n" "public:\n" " explicit mystring(const char* str)\n" " {\n" " m_len = strlen(str);\n" " m_str = (char*) malloc(m_len+1);\n" " memcpy(m_str, str, m_len+1);\n" " }\n" " mystring& operator=(const mystring& copy)\n" " {\n" " return (*this = copy.m_str);\n" " }\n" " ~mystring()\n" " {\n" " free(m_str);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar20() { // ticket #2867 check("Object::MemFunc() {\n" " class LocalClass {\n" " public:\n" " LocalClass() : dataLength_(0) {}\n" " std::streamsize dataLength_;\n" " double bitsInData_;\n" " } obj;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout.str()); check("struct copy_protected;\n" "Object::MemFunc() {\n" " class LocalClass : public copy_protected {\n" " public:\n" " LocalClass() : copy_protected(1), dataLength_(0) {}\n" " std::streamsize dataLength_;\n" " double bitsInData_;\n" " } obj;\n" "};"); ASSERT_EQUALS( "[test.cpp:5]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout.str()); check("struct copy_protected;\n" "Object::MemFunc() {\n" " class LocalClass : ::copy_protected {\n" " public:\n" " LocalClass() : copy_protected(1), dataLength_(0) {}\n" " std::streamsize dataLength_;\n" " double bitsInData_;\n" " } obj;\n" "};"); ASSERT_EQUALS( "[test.cpp:5]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout.str()); } void uninitVar21() { // ticket #2947 check("class Fred {\n" "private:\n" " int a[23];\n" "public:\n" " Fred();\n" "};\n" "Fred::Fred() {\n" " a[x::y] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar22() { // ticket #3043 check("class Fred {\n" " public:\n" " Fred & operator=(const Fred &);\n" " virtual Fred & clone(const Fred & other);\n" " int x;\n" "};\n" "Fred & Fred::operator=(const Fred & other) {\n" " return clone(other);\n" "}\n" "Fred & Fred::clone(const Fred & other) {\n" " x = 0;\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " public:\n" " Fred & operator=(const Fred &);\n" " virtual Fred & clone(const Fred & other);\n" " int x;\n" "};\n" "Fred & Fred::operator=(const Fred & other) {\n" " return clone(other);\n" "}\n" "Fred & Fred::clone(const Fred & other) {\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::x' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void uninitVar23() { // ticket #3702 check("class Fred {\n" " int x;\n" "public:\n" " Fred(struct A a, struct B b);\n" " Fred(C c, struct D d);\n" " Fred(struct E e, F f);\n" " Fred(struct G, struct H);\n" " Fred(I, J);\n" "};\n" "Fred::Fred(A a, B b) { }\n" "Fred::Fred(struct C c, D d) { }\n" "Fred::Fred(E e, struct F f) { }\n" "Fred::Fred(G g, H h) { }\n" "Fred::Fred(struct I i, struct J j) { }"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:11]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:12]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:13]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n", errout.str()); } void uninitVar24() { // ticket #3190 check("class Foo;\n" "class Bar;\n" "class Sub;\n" "class Foo { class Sub; };\n" "class Bar { class Sub; };\n" "class Bar::Sub {\n" " int b;\n" "public:\n" " Sub() { }\n" " Sub(int);\n" "};\n" "Bar::Sub::Sub(int) { };\n" "class ::Foo::Sub {\n" " int f;\n" "public:\n" " ~Sub();\n" " Sub();\n" "};\n" "::Foo::Sub::~Sub() { }\n" "::Foo::Sub::Sub() { }\n" "class Foo;\n" "class Bar;\n" "class Sub;\n", true); ASSERT_EQUALS("[test.cpp:9]: (warning, inconclusive) Member variable 'Sub::b' is not initialized in the constructor.\n" "[test.cpp:12]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n" "[test.cpp:20]: (warning) Member variable 'Sub::f' is not initialized in the constructor.\n", errout.str()); } void uninitVar25() { // ticket #4789 check("struct A {\n" " int a;\n" " int b;\n" " int c;\n" " A(int x = 0, int y = 0, int z = 0);\n" "};\n" "A::A(int x = 0, int y = 0, int z = 0) { }\n" "struct B {\n" " int a;\n" " int b;\n" " int c;\n" " B(int x = 0, int y = 0, int z = 0);\n" "};\n" "B::B(int x, int y, int z) { }\n" "struct C {\n" " int a;\n" " int b;\n" " int c;\n" " C(int, int, int);\n" "};\n" "C::C(int x = 0, int y = 0, int z = 0) { }\n" "struct D {\n" " int a;\n" " int b;\n" " int c;\n" " D(int, int, int);\n" "};\n" "D::D(int x, int y, int z) { }\n" "struct E {\n" " int a;\n" " int b;\n" " int c;\n" " E(int x, int y, int z);\n" "};\n" "E::E(int, int, int) { }\n" "struct F {\n" " int a;\n" " int b;\n" " int c;\n" " F(int x = 0, int y = 0, int z = 0);\n" "};\n" "F::F(int, int, int) { }\n", true); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:7]: (warning) Member variable 'A::b' is not initialized in the constructor.\n" "[test.cpp:7]: (warning) Member variable 'A::c' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'B::c' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'C::a' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'C::b' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:28]: (warning) Member variable 'D::a' is not initialized in the constructor.\n" "[test.cpp:28]: (warning) Member variable 'D::b' is not initialized in the constructor.\n" "[test.cpp:28]: (warning) Member variable 'D::c' is not initialized in the constructor.\n" "[test.cpp:35]: (warning) Member variable 'E::a' is not initialized in the constructor.\n" "[test.cpp:35]: (warning) Member variable 'E::b' is not initialized in the constructor.\n" "[test.cpp:35]: (warning) Member variable 'E::c' is not initialized in the constructor.\n" "[test.cpp:42]: (warning) Member variable 'F::a' is not initialized in the constructor.\n" "[test.cpp:42]: (warning) Member variable 'F::b' is not initialized in the constructor.\n" "[test.cpp:42]: (warning) Member variable 'F::c' is not initialized in the constructor.\n", errout.str()); } void uninitVar26() { check("class A {\n" " int * v;\n" " int sz;\n" "public:\n" " A(int s) {\n" " v = new int [sz = s];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar27() { check("class A {\n" " double d;\n" "public:\n" " A() {\n" " rtl::math::setNan(&d);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " double d;\n" "public:\n" " A() {\n" " ::rtl::math::setNan(&d);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar28() { check("class Fred {\n" " int i;\n" " float f;\n" "public:\n" " Fred() {\n" " foo(1);\n" " foo(1.0f);\n" " }\n" " void foo(int a) { i = a; }\n" " void foo(float a) { f = a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar29() { check("class A {\n" " int i;\n" "public:\n" " A() { foo(); }\n" " void foo() const { };\n" " void foo() { i = 0; }\n" "};\n" "class B {\n" " int i;\n" "public:\n" " B() { foo(); }\n" " void foo() { i = 0; }\n" " void foo() const { }\n" "};\n" "class C {\n" " int i;\n" "public:\n" " C() { foo(); }\n" " void foo() const { i = 0; }\n" " void foo() { }\n" "};\n" "class D {\n" " int i;\n" "public:\n" " D() { foo(); }\n" " void foo() { }\n" " void foo() const { i = 0; }\n" "};"); ASSERT_EQUALS("[test.cpp:18]: (warning) Member variable 'C::i' is not initialized in the constructor.\n" "[test.cpp:25]: (warning) Member variable 'D::i' is not initialized in the constructor.\n", errout.str()); } void uninitVar30() { // ticket #6417 check("namespace NS {\n" " class MyClass {\n" " public:\n" " MyClass();\n" " ~MyClass();\n" " private:\n" " bool SomeVar;\n" " };\n" "}\n" "using namespace NS;\n" "MyClass::~MyClass() { }\n" "MyClass::MyClass() : SomeVar(false) { }"); ASSERT_EQUALS("", errout.str()); } void uninitVar31() { // ticket #8271 check("void bar();\n" "class MyClass {\n" "public:\n" " MyClass();\n" " void Restart();\n" "protected:\n" " int m_retCode;\n" "};\n" "MyClass::MyClass() {\n" " bar(),Restart();\n" "}\n" "void MyClass::Restart() {\n" " m_retCode = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar32() { // ticket #8835 check("class Foo {\n" " friend class Bar;\n" " int member;\n" "public:\n" " Foo()\n" " {\n" " if (1) {}\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); check("class Foo {\n" " friend class Bar;\n" " int member;\n" "public:\n" " Foo()\n" " {\n" " while (1) {}\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); check("class Foo {\n" " friend class Bar;\n" " int member;\n" "public:\n" " Foo()\n" " {\n" " for (;;) {}\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); } void uninitVar33() { // ticket #10295 check("namespace app {\n" " class B {\n" " public:\n" " B(void);\n" " int x;\n" " };\n" "};\n" "app::B::B(void){}"); ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'B::x' is not initialized in the constructor.\n", errout.str()); } void uninitVarArray1() { check("class John\n" "{\n" "public:\n" " John() {}\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'John::name' is not initialized in the constructor.\n", errout.str()); check("class John\n" "{\n" "public:\n" " John() {John::name[0] = '\\0';}\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); check("class John\n" "{\n" "public:\n" " John() { strcpy(name, \"\"); }\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); check("class John\n" "{\n" "public:\n" " John() { }\n" "\n" " double operator[](const unsigned long i);\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A;\n" "class John\n" "{\n" "public:\n" " John() { }\n" " A a[5];\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A;\n" "class John\n" "{\n" "public:\n" " John() { }\n" " A (*a)[5];\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'John::a' is not initialized in the constructor.\n", errout.str()); } void uninitVarArray2() { check("class John\n" "{\n" "public:\n" " John() { *name = 0; }\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); // #5754 check("class John\n" "{\n" "public:\n" " John() {*this->name = '\\0';}\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray3() { check("class John\n" "{\n" "private:\n" " int a[100];\n" " int b[100];\n" "\n" "public:\n" " John()\n" " {\n" " memset(a,0,sizeof(a));\n" " memset(b,0,sizeof(b));\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray4() { check("class John\n" "{\n" "private:\n" " int a[100];\n" " int b[100];\n" "\n" "public:\n" " John()\n" " {\n" " if (snprintf(a,10,\"a\")) { }\n" " if (snprintf(b,10,\"b\")) { }\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray5() { check("class Foo\n" "{\n" "private:\n" " Bar bars[10];\n" "public:\n" " Foo()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray6() { check("class Foo\n" "{\n" "public:\n" " Foo();\n" " static const char STR[];\n" "};\n" "const char Foo::STR[] = \"abc\";\n" "Foo::Foo() { }"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray7() { check("class Foo\n" "{\n" " int array[10];\n" "public:\n" " Foo() { }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::array' is not initialized in the constructor.\n", errout.str()); check("class Foo\n" "{\n" " int array[10];\n" "public:\n" " Foo() { memset(array, 0, sizeof(array)); }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo\n" "{\n" " int array[10];\n" "public:\n" " Foo() { ::memset(array, 0, sizeof(array)); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray8() { check("class Foo {\n" " char a[10];\n" "public:\n" " Foo() { ::ZeroMemory(a); }\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray9() { // #6957 check("class BaseGDL;\n" "struct IxExprListT {\n" "private:\n" " BaseGDL* eArr[3];\n" "public:\n" " IxExprListT() {}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'IxExprListT::eArr' is not initialized in the constructor.\n", errout.str()); check("struct sRAIUnitDefBL {\n" " sRAIUnitDefBL();\n" " ~sRAIUnitDefBL();\n" "};\n" "struct sRAIUnitDef {\n" " sRAIUnitDef() {}\n" " sRAIUnitDefBL *List[35];\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'sRAIUnitDef::List' is not initialized in the constructor.\n", errout.str()); } void uninitVarArray2D() { check("class John\n" "{\n" "public:\n" " John() { a[0][0] = 0; }\n" "\n" "private:\n" " char a[2][2];\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray3D() { check("class John\n" "{\n" "private:\n" " char a[2][2][2];\n" "public:\n" " John() { a[0][0][0] = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarCpp11Init1() { check("class Foo {\n" " std::vector bar;\n" "public:\n" " Foo()\n" " : bar({\"a\", \"b\"})\n" " {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarCpp11Init2() { check("class Fred {\n" " struct Foo {\n" " int a;\n" " bool b;\n" " };\n" " Foo f;\n" " float g;\n" "public:\n" " Fred() : f{0, true} { }\n" " float get() const;\n" "};\n" "float Fred::get() const { return g; }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Fred::g' is not initialized in the constructor.\n", errout.str()); } void uninitVarStruct1() { // ticket #2172 check("class A\n" "{\n" "private:\n" " struct B {\n" " std::string str1;\n" " std::string str2;\n" " }\n" " struct B b;\n" "public:\n" " A() {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "private:\n" " struct B {\n" " char *str1;\n" " char *str2;\n" " }\n" " struct B b;\n" "public:\n" " A() {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'A::b' is not initialized in the constructor.\n", errout.str()); check("class A\n" "{\n" "private:\n" " struct B {\n" " char *str1;\n" " char *str2;\n" " B() : str1(NULL), str2(NULL) { }\n" " }\n" " struct B b;\n" "public:\n" " A() {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarStruct2() { // ticket #838 check("struct POINT\n" "{\n" " int x;\n" " int y;\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Fred::p' is not initialized in the constructor.\n", errout.str()); check("struct POINT\n" "{\n" " int x;\n" " int y;\n" " POINT();\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct POINT\n" "{\n" " int x;\n" " int y;\n" " POINT() :x(0), y(0) { }\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); // non static data-member initialization check("struct POINT\n" "{\n" " int x=0;\n" " int y=0;\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarUnion1() { // ticket #3196 check("class Fred\n" "{\n" "private:\n" " union { int a; int b; };\n" "public:\n" " Fred()\n" " { a = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarUnion2() { // If the "data_type" is 0 it means union member "data" is invalid. // So it's ok to not initialize "data". // related forum: http://sourceforge.net/apps/phpbb/cppcheck/viewtopic.php?f=3&p=1806 check("union Data { int id; int *ptr; };\n" "\n" "class Fred {\n" "private:\n" " int data_type;\n" " Data data;\n" "public:\n" " Fred() : data_type(0)\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitMissingFuncDef() { // Unknown member function check("class Fred\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " void Init();" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown non-member function (friend class) check("class Fred\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " friend ABC;\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown non-member function (is Init a virtual function?) check("class Fred : private ABC\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); // Unknown non-member function check("class Fred\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); // Unknown non-member function check("class ABC { };\n" "class Fred : private ABC\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void uninitVarEnum1() { check("class Fred\n" "{\n" "public:\n" " enum abc {a,b,c};\n" " Fred() {}\n" "private:\n" " unsigned int i;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void uninitVarEnum2() { // ticket #8146 check("enum E { E1 };\n" "struct X { E e = E1; };\n" "struct Y {\n" " Y() {}\n" " X x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarStream() { check("class Foo\n" "{\n" "private:\n" " int foo;\n" "public:\n" " explicit Foo(std::istream &in)\n" " {\n" " if(!(in >> foo))\n" " throw 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarTypedef() { check("class Foo\n" "{\n" "public:\n" " typedef int * pointer;\n" " Foo() : a(0) {}\n" " pointer a;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarMemset() { check("class Foo\n" "{\n" "public:\n" " int * pointer;\n" " Foo() { memset(this, 0, sizeof(*this)); }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo\n" "{\n" "public:\n" " int * pointer;\n" " Foo() { ::memset(this, 0, sizeof(*this)); }\n" "};"); ASSERT_EQUALS("", errout.str()); // Ticket #7068 check("struct Foo {\n" " int * p;\n" " char c;\n" " Foo() { memset(p, 0, sizeof(int)); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::c' is not initialized in the constructor.\n", errout.str()); check("struct Foo {\n" " int i;\n" " char c;\n" " Foo() { memset(&i, 0, sizeof(int)); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::c' is not initialized in the constructor.\n", errout.str()); check("struct Foo { int f; };\n" "struct Bar { int b; };\n" "struct FooBar {\n" " FooBar() {\n" " memset(&foo, 0, sizeof(foo));\n" " }\n" " Foo foo;\n" " Bar bar;\n" "};\n" "int main() {\n" " FooBar foobar;\n" " return foobar.foo.f + foobar.bar.b;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'FooBar::bar' is not initialized in the constructor.\n", errout.str()); check("struct Foo { int f; };\n" "struct Bar { int b; };\n" "struct FooBar {\n" " FooBar() {\n" " memset(&this->foo, 0, sizeof(this->foo));\n" " }\n" " Foo foo;\n" " Bar bar;\n" "};\n" "int main() {\n" " FooBar foobar;\n" " return foobar.foo.f + foobar.bar.b;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'FooBar::bar' is not initialized in the constructor.\n", errout.str()); // #7755 check("struct A {\n" " A() {\n" " memset(this->data, 0, 42);\n" " }\n" " char data[42];\n" "};"); ASSERT_EQUALS("", errout.str()); } void privateCtor1() { settings.standards.cpp = Standards::CPP03; check("class Foo {\n" " int foo;\n" " Foo() { }\n" "};"); ASSERT_EQUALS("", errout.str()); settings.standards.cpp = Standards::CPP11; check("class Foo {\n" " int foo;\n" " Foo() { }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Foo::foo' is not initialized in the constructor.\n", errout.str()); } void privateCtor2() { check("class Foo\n" "{\n" "private:\n" " int foo;\n" " Foo() { }\n" "public:\n" " explicit Foo(int _i) { }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Foo::foo' is not initialized in the constructor.\n", errout.str()); } void function() { check("class A\n" "{\n" "public:\n" " A();\n" " int* f(int*);\n" "};\n" "\n" "A::A()\n" "{\n" "}\n" "\n" "int* A::f(int* p)\n" "{\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Borland C++: No FP for published pointers - they are automatically initialized void uninitVarPublished() { check("class Fred\n" "{\n" "__published:\n" " int *i;\n" "public:\n" " Fred() { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarInheritClassInit() { Settings s; s.libraries.emplace_back("vcl"); check("class Fred: public TObject\n" "{\n" "public:\n" " Fred() { }\n" "private:\n" " int x;\n" "};", s); ASSERT_EQUALS("", errout.str()); } void uninitOperator() { check("class Fred\n" "{\n" "public:\n" " Fred() { }\n" " int *operator [] (int index) { return 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitFunction1() { check("class Fred\n" "{\n" "public:\n" " Fred() { init(*this); }\n" "\n" " static void init(Fred &f)\n" " { f.d = 0; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { init(*this); }\n" "\n" " static void init(Fred &f)\n" " { }\n" "\n" " double d;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", "", errout.str()); } void uninitFunction2() { check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init(*this)); }\n" "\n" " static bool init(Fred &f)\n" " { f.d = 0; return true; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init(*this)); }\n" "\n" " static bool init(Fred &f)\n" " { return true; }\n" "\n" " double d;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", "", errout.str()); } void uninitFunction3() { check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init()); }\n" "\n" " bool init()\n" " { d = 0; return true; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init()); }\n" "\n" " bool init()\n" " { return true; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", errout.str()); } void uninitFunction4() { check("class Fred\n" "{\n" "public:\n" " Fred() { init(this); }\n" "\n" " init(Fred *f)\n" " { f.d = 0; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { init(this); }\n" "\n" " init(Fred *f)\n" " { }\n" "\n" " double d;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", "", errout.str()); } void uninitFunction5() { // #4072 - FP about struct that is initialized in function check("struct Structure {\n" " int C;\n" "};\n" "\n" "class A {\n" " Structure B;\n" "public:\n" " A() { Init( B ); };\n" " void Init( Structure& S ) { S.C = 0; };\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitSameClassName() { check("class B\n" "{\n" "public:\n" " B();\n" " int j;\n" "};\n" "\n" "class A\n" "{\n" " class B\n" " {\n" " public:\n" " B();\n" " int i;\n" " };\n" "};\n" "\n" "A::B::B()\n" "{\n" " i = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" "public:\n" " B();\n" " int j;\n" "};\n" "\n" "class A\n" "{\n" " class B\n" " {\n" " public:\n" " B();\n" " int i;\n" " };\n" "};\n" "\n" "B::B()\n" "{\n" "}\n" "\n" "A::B::B()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:18]: (warning) Member variable 'B::j' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'B::i' is not initialized in the constructor.\n", errout.str()); // Ticket #1700 check("namespace n1\n" "{\n" "class Foo {" "public:\n" " Foo() : i(0) { }\n" "private:\n" " int i;\n" "};\n" "}\n" "\n" "namespace n2\n" "{\n" "class Foo {" "public:\n" " Foo() { }\n" "};\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace n1\n" "{\n" "class Foo {\n" "public:\n" " Foo();\n" "private:\n" " int i;\n" "};\n" "}\n" "\n" "n1::Foo::Foo()\n" "{ }\n" "\n" "namespace n2\n" "{\n" "class Foo {\n" "public:\n" " Foo() { }\n" "};\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Foo::i' is not initialized in the constructor.\n", errout.str()); check("namespace n1\n" "{\n" "class Foo {" "public:\n" " Foo();\n" "private:\n" " int i;\n" "};\n" "}\n" "\n" "n1::Foo::Foo() : i(0)\n" "{ }\n" "\n" "namespace n2\n" "{\n" "class Foo {" "public:\n" " Foo() { }\n" "};\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitFunctionOverload() { // Ticket #1783 - overloaded "init" functions check("class A\n" "{\n" "private:\n" " int i;\n" "\n" "public:\n" " A()\n" " {\n" " init();\n" " }\n" "\n" " void init() { init(0); }\n" "\n" " void init(int value)\n" " { i = value; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "private:\n" " int i;\n" "\n" "public:\n" " A()\n" " {\n" " init();\n" " }\n" "\n" " void init() { init(0); }\n" "\n" " void init(int value)\n" " { }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::i' is not initialized in the constructor.\n", errout.str()); check("class bar {\n" // #9887 " int length;\n" " bar() { length = 0; }\n" "};\n" "class foo {\n" " int x;\n" " foo() { Set(bar()); }\n" " void Set(int num) { x = 1; }\n" " void Set(bar num) { x = num.length; }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void uninitVarOperatorEqual() { // ticket #2415 check("struct A {\n" " int a;\n" " A() { a=0; }\n" " A(A const &a) { operator=(a); }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " A() { a=0; }\n" " A(A const &a) { operator=(a); }\n" " A & operator = (const A & rhs) {\n" " a = rhs.a;\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " A() { a=0; }\n" " A(A const &a) { operator=(a); }\n" " A & operator = (const A & rhs) {\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'A::a' is not assigned a value in 'A::operator='.\n", errout.str()); } void uninitVarPointer() { // #3801 check("struct A {\n" " int a;\n" "};\n" "struct B {\n" " A* a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " A* a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " const A* a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " A* const a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("class Test {\n" // #8498 "public:\n" " Test() {}\n" " std::map* pMap = nullptr;\n" " std::string* pStr = nullptr;\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("template \n" "class C1 {}; \n" "template \n" "class C2 {};\n" "namespace A {\n" " template \n" " class D1 {};\n" " template \n" " class D2 {};\n" "}\n" "class Test {\n" "public:\n" " Test() {}\n" " C1* c1 = nullptr;\n" " C2* c2 = nullptr;\n" " A::D1* d1 = nullptr;\n" " A::D2* d2 = nullptr;\n" " std::map* pMap = nullptr;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void uninitConstVar() { check("struct A;\n" "struct B {\n" " A* const a;\n" " B() { }\n" " B(B& b) { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " A* const a;\n" " B& operator=(const B& r) { }\n" "};"); ASSERT_EQUALS("", errout.str()); // #3804 check("struct B {\n" " const int a;\n" " B() { }\n" " B(B& b) { }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct B {\n" " const int a;\n" " B& operator=(const B& r) { }\n" "};"); ASSERT_EQUALS("", errout.str()); } // Ticket #5641 "Regression. Crash for 'C() _STLP_NOTHROW {}'" void constructors_crash1() { check("class C {\n" "public:\n" " C() _STLP_NOTHROW {}\n" " C(const C&) _STLP_NOTHROW {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void classWithOperatorInName() { // ticket #2827 check("class operatorX {\n" " int mValue;\n" "public:\n" " operatorX() : mValue(0) {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void templateConstructor() { // ticket #7942 check("template struct Container {\n" " Container();\n" " T* mElements;\n" "};\n" "template Container::Container() : mElements(nullptr) {}\n" "Container intContainer;"); ASSERT_EQUALS("", errout.str()); } void typedefArray() { // ticket #5766 check("typedef float rvec[3];\n" "class SelectionPosition {\n" "public:\n" " SelectionPosition() {}\n" " const rvec &x() const;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitAssignmentWithOperator() { check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b = b && SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};", true); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'C::x' is not initialized in the constructor.\n", "[test.cpp:3]: (warning) Member variable 'C::x' is not initialized in the constructor.\n", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b = true || SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};", true); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'C::x' is not initialized in the constructor.\n", "[test.cpp:3]: (warning) Member variable 'C::x' is not initialized in the constructor.\n", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = true;\n" " b = b & SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b = true | SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i * SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i / SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i % SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i + SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i - SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i << SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i >> SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i ^ SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitCompoundAssignment() { check("struct C {\n" " int x;\n" " C() {\n" " bool b = true;\n" " b &= SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b |= SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i *= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i /= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i %= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i += SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i -= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i <<= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i >>= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i ^= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitComparisonAssignment() { check("struct C {\n" " int x;\n" " C() {\n" " bool b = true;\n" " b = (true == SetValue());\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b |= (true != SetValue());\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 < SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 <= SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 > SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 >= SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitTemplate1() { check("template class C;\n" "template \n" "class C {\n" " public:\n" " C() : b(0) { }\n" " C(A* a) : b(a) { }\n" " private:\n" " A* b;\n" "};\n" "template \n" "class C {\n" " private:\n" " A* b;\n" "};"); ASSERT_EQUALS("", errout.str()); check("template class A{};\n" "template class B{};\n" "template\n" "class A> {\n" " public:\n" " A();\n" " bool m_value;\n" "};\n" "template\n" "A>::A() : m_value(false) {}"); ASSERT_EQUALS("", errout.str()); } void unknownTemplateType() { check("template class A {\n" "private:\n" " T m;\n" "public:\n" " A& operator=() { return *this; }\n" "};\n" "A a;"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestConstructors) cppcheck-2.7/test/testcppcheck.cpp000066400000000000000000000070531417746362400173300ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check.h" #include "color.h" #include "config.h" #include "cppcheck.h" #include "errorlogger.h" #include "testsuite.h" #include #include #include #include class TestCppcheck : public TestFixture { public: TestCppcheck() : TestFixture("TestCppcheck") {} private: class ErrorLogger2 : public ErrorLogger { public: std::list id; void reportOut(const std::string & /*outmsg*/, Color = Color::Reset) OVERRIDE {} void bughuntingReport(const std::string & /*str*/) OVERRIDE {} void reportErr(const ErrorMessage &msg) OVERRIDE { id.push_back(msg.id); } }; void run() OVERRIDE { TEST_CASE(instancesSorted); TEST_CASE(classInfoFormat); TEST_CASE(getErrorMessages); } void instancesSorted() const { for (std::list::const_iterator i = Check::instances().begin(); i != Check::instances().end(); ++i) { std::list::const_iterator j = i; ++j; if (j != Check::instances().end()) { ASSERT_EQUALS(true, (*i)->name() < (*j)->name()); } } } void classInfoFormat() const { for (std::list::const_iterator i = Check::instances().begin(); i != Check::instances().end(); ++i) { const std::string info = (*i)->classInfo(); if (!info.empty()) { ASSERT('\n' != info[0]); // No \n in the beginning ASSERT('\n' == info.back()); // \n at end if (info.size() > 1) ASSERT('\n' != info[info.length()-2]); // Only one \n at end } } } void getErrorMessages() const { ErrorLogger2 errorLogger; CppCheck cppCheck(errorLogger, true, nullptr); cppCheck.getErrorMessages(); ASSERT(!errorLogger.id.empty()); // Check if there are duplicate error ids in errorLogger.id std::string duplicate; for (std::list::iterator it = errorLogger.id.begin(); it != errorLogger.id.end(); ++it) { if (std::find(errorLogger.id.begin(), it, *it) != it) { duplicate = "Duplicate ID: " + *it; break; } } ASSERT_EQUALS("", duplicate); // Check for error ids from this class. bool foundPurgedConfiguration = false; bool foundTooManyConfigs = false; for (const std::string & it : errorLogger.id) { if (it == "purgedConfiguration") foundPurgedConfiguration = true; else if (it == "toomanyconfigs") foundTooManyConfigs = true; } ASSERT(foundPurgedConfiguration); ASSERT(foundTooManyConfigs); } }; REGISTER_TEST(TestCppcheck) cppcheck-2.7/test/testerrorlogger.cpp000066400000000000000000000461721417746362400201060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "cppcheck.h" #include "errorlogger.h" #include "errortypes.h" #include "suppressions.h" #include "testsuite.h" #include #include #include #include #include class TestErrorLogger : public TestFixture { public: TestErrorLogger() : TestFixture("TestErrorLogger"), fooCpp5("foo.cpp", 5, 1), barCpp8("bar.cpp", 8, 1) {} private: const ErrorMessage::FileLocation fooCpp5; const ErrorMessage::FileLocation barCpp8; void run() OVERRIDE { TEST_CASE(PatternSearchReplace); TEST_CASE(FileLocationDefaults); TEST_CASE(FileLocationSetFile); TEST_CASE(ErrorMessageConstruct); TEST_CASE(ErrorMessageConstructLocations); TEST_CASE(ErrorMessageVerbose); TEST_CASE(ErrorMessageVerboseLocations); TEST_CASE(CustomFormat); TEST_CASE(CustomFormat2); TEST_CASE(CustomFormatLocations); TEST_CASE(ToXmlV2); TEST_CASE(ToXmlV2Locations); TEST_CASE(ToXmlV2Encoding); TEST_CASE(FromXmlV2); // Inconclusive results in xml reports.. TEST_CASE(InconclusiveXml); // Serialize / Deserialize inconclusive message TEST_CASE(SerializeInconclusiveMessage); TEST_CASE(DeserializeInvalidInput); TEST_CASE(SerializeSanitize); TEST_CASE(SerializeFileLocation); TEST_CASE(suppressUnmatchedSuppressions); } void TestPatternSearchReplace(const std::string& idPlaceholder, const std::string& id) const { const std::string plainText = "text"; ErrorMessage message; message.id = id; std::string serialized = message.toString(true, idPlaceholder + plainText + idPlaceholder); ASSERT_EQUALS(id + plainText + id, serialized); serialized = message.toString(true, idPlaceholder + idPlaceholder); ASSERT_EQUALS(id + id, serialized); serialized = message.toString(true, plainText + idPlaceholder + plainText); ASSERT_EQUALS(plainText + id + plainText, serialized); } void PatternSearchReplace() const { const std::string idPlaceholder = "{id}"; const std::string empty; TestPatternSearchReplace(idPlaceholder, empty); const std::string shortIdValue = "ID"; ASSERT_EQUALS(true, shortIdValue.length() < idPlaceholder.length()); TestPatternSearchReplace(idPlaceholder, shortIdValue); const std::string mediumIdValue = "_ID_"; ASSERT_EQUALS(mediumIdValue.length(), idPlaceholder.length()); TestPatternSearchReplace(idPlaceholder, mediumIdValue); const std::string longIdValue = "longId"; ASSERT_EQUALS(true, longIdValue.length() > idPlaceholder.length()); TestPatternSearchReplace(idPlaceholder, longIdValue); } void FileLocationDefaults() const { ErrorMessage::FileLocation loc; ASSERT_EQUALS("", loc.getfile()); ASSERT_EQUALS(0, loc.line); } void FileLocationSetFile() const { ErrorMessage::FileLocation loc; loc.setfile("foo.cpp"); ASSERT_EQUALS("foo.cpp", loc.getfile()); ASSERT_EQUALS(0, loc.line); } void ErrorMessageConstruct() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.", "errorId", Certainty::normal); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Programming error.", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(true)); } void ErrorMessageConstructLocations() const { std::list locs = { fooCpp5, barCpp8 }; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.", "errorId", Certainty::normal); ASSERT_EQUALS(2, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Programming error.", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(true)); } void ErrorMessageVerbose() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5]: (error) Verbose error", msg.toString(true)); } void ErrorMessageVerboseLocations() const { std::list locs = { fooCpp5, barCpp8 }; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); ASSERT_EQUALS(2, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Verbose error", msg.toString(true)); } void CustomFormat() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("foo.cpp:5,error,errorId,Programming error.", msg.toString(false, "{file}:{line},{severity},{id},{message}")); ASSERT_EQUALS("foo.cpp:5,error,errorId,Verbose error", msg.toString(true, "{file}:{line},{severity},{id},{message}")); } void CustomFormat2() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("Programming error. - foo.cpp(5):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})")); ASSERT_EQUALS("Verbose error - foo.cpp(5):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})")); } void CustomFormatLocations() const { // Check that first location from location stack is used in template std::list locs = { fooCpp5, barCpp8 }; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); ASSERT_EQUALS(2, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("Programming error. - bar.cpp(8):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})")); ASSERT_EQUALS("Verbose error - bar.cpp(8):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})")); } void ToXmlV2() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); std::string header("\n\n"); header += " \n "; ASSERT_EQUALS(header, ErrorMessage::getXMLHeader()); ASSERT_EQUALS(" \n", ErrorMessage::getXMLFooter()); std::string message(" \n"; message += " \n "; ASSERT_EQUALS(message, msg.toXML()); } void ToXmlV2Locations() const { std::list locs = { fooCpp5, barCpp8 }; locs.back().setinfo("ä"); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); std::string header("\n\n"); header += " \n "; ASSERT_EQUALS(header, ErrorMessage::getXMLHeader()); ASSERT_EQUALS(" \n", ErrorMessage::getXMLFooter()); std::string message(" \n"; message += " \n"; message += " \n "; ASSERT_EQUALS(message, msg.toXML()); } void ToXmlV2Encoding() const { { std::list locs; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nComparing \"\203\" with \"\003\"", "errorId", Certainty::normal); const std::string expected(" "); ASSERT_EQUALS(expected, msg.toXML()); } { const char code1[]="äöü"; const char code2[]="\x12\x00\x00\x01"; std::list locs; ErrorMessage msg1(locs, emptyString, Severity::error, std::string("Programming error.\nReading \"")+code1+"\"", "errorId", Certainty::normal); ASSERT_EQUALS(" ", msg1.toXML()); ErrorMessage msg2(locs, emptyString, Severity::error, std::string("Programming error.\nReading \"")+code2+"\"", "errorId", Certainty::normal); ASSERT_EQUALS(" ", msg2.toXML()); } } void FromXmlV2() const { const char xmldata[] = "\n" "\n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); ErrorMessage msg(doc.FirstChildElement()); ASSERT_EQUALS("errorId", msg.id); ASSERT_EQUALS(Severity::error, msg.severity); ASSERT_EQUALS(123u, msg.cwe.id); ASSERT_EQUALS(Certainty::inconclusive, msg.certainty); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS(456u, msg.hash); ASSERT_EQUALS(2u, msg.callStack.size()); ASSERT_EQUALS("foo.cpp", msg.callStack.front().getfile()); ASSERT_EQUALS(5, msg.callStack.front().line); ASSERT_EQUALS(2u, msg.callStack.front().column); ASSERT_EQUALS("bar.cpp", msg.callStack.back().getfile()); ASSERT_EQUALS(8, msg.callStack.back().line); ASSERT_EQUALS(1u, msg.callStack.back().column); } void InconclusiveXml() const { // Location std::list locs(1, fooCpp5); // Inconclusive error message ErrorMessage msg(locs, emptyString, Severity::error, "Programming error", "errorId", Certainty::inconclusive); // xml version 2 error message ASSERT_EQUALS(" \n" " \n" " ", msg.toXML()); } void SerializeInconclusiveMessage() const { // Inconclusive error message std::list locs; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error", "errorId", Certainty::inconclusive); msg.file0 = "test.cpp"; ASSERT_EQUALS("7 errorId" "5 error" "1 0" "1 0" "8 test.cpp" "12 inconclusive" "17 Programming error" "17 Programming error" "0 ", msg.serialize()); ErrorMessage msg2; msg2.deserialize(msg.serialize()); ASSERT_EQUALS("errorId", msg2.id); ASSERT_EQUALS(Severity::error, msg2.severity); ASSERT_EQUALS("test.cpp", msg2.file0); ASSERT_EQUALS(Certainty::inconclusive, msg2.certainty); ASSERT_EQUALS("Programming error", msg2.shortMessage()); ASSERT_EQUALS("Programming error", msg2.verboseMessage()); } void DeserializeInvalidInput() const { ErrorMessage msg; ASSERT_THROW(msg.deserialize("500foobar"), InternalError); } void SerializeSanitize() const { std::list locs; ErrorMessage msg(locs, emptyString, Severity::error, std::string("Illegal character in \"foo\001bar\""), "errorId", Certainty::normal); msg.file0 = "1.c"; ASSERT_EQUALS("7 errorId" "5 error" "1 0" "1 0" "3 1.c" "33 Illegal character in \"foo\\001bar\"" "33 Illegal character in \"foo\\001bar\"" "0 ", msg.serialize()); ErrorMessage msg2; msg2.deserialize(msg.serialize()); ASSERT_EQUALS("errorId", msg2.id); ASSERT_EQUALS(Severity::error, msg2.severity); ASSERT_EQUALS("1.c", msg2.file0); ASSERT_EQUALS("Illegal character in \"foo\\001bar\"", msg2.shortMessage()); ASSERT_EQUALS("Illegal character in \"foo\\001bar\"", msg2.verboseMessage()); } void SerializeFileLocation() const { ErrorMessage::FileLocation loc1(":/,;", 654, 33); loc1.setfile("[]:;,()"); loc1.setinfo("abcd:/,"); std::list locs{loc1}; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error", "errorId", Certainty::inconclusive); ErrorMessage msg2; msg2.deserialize(msg.serialize()); ASSERT_EQUALS("[]:;,()", msg2.callStack.front().getfile(false)); ASSERT_EQUALS(":/,;", msg2.callStack.front().getOrigFile(false)); ASSERT_EQUALS(654, msg2.callStack.front().line); ASSERT_EQUALS(33, msg2.callStack.front().column); ASSERT_EQUALS("abcd:/,", msg2.callStack.front().getinfo()); } void suppressUnmatchedSuppressions() { std::list suppressions; // No unmatched suppression errout.str(""); suppressions.clear(); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "*", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression (corresponds to "--suppress=unmatchedSuppression") errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression in a.c errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress unmatchedSuppression in a.c at line 10 errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", 10U); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // don't suppress unmatchedSuppression when file is mismatching errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "b.c", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str()); // don't suppress unmatchedSuppression when line is mismatching errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", 1U); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str()); } }; REGISTER_TEST(TestErrorLogger) cppcheck-2.7/test/testexceptionsafety.cpp000066400000000000000000000371761417746362400207730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkexceptionsafety.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestExceptionSafety : public TestFixture { public: TestExceptionSafety() : TestFixture("TestExceptionSafety") {} private: Settings settings; void run() OVERRIDE { settings.severity.fill(); TEST_CASE(destructors); TEST_CASE(deallocThrow1); TEST_CASE(deallocThrow2); TEST_CASE(deallocThrow3); TEST_CASE(rethrowCopy1); TEST_CASE(rethrowCopy2); TEST_CASE(rethrowCopy3); TEST_CASE(rethrowCopy4); TEST_CASE(rethrowCopy5); TEST_CASE(catchExceptionByValue); TEST_CASE(noexceptThrow); TEST_CASE(nothrowThrow); TEST_CASE(unhandledExceptionSpecification1); // #4800 TEST_CASE(unhandledExceptionSpecification2); TEST_CASE(nothrowAttributeThrow); TEST_CASE(nothrowAttributeThrow2); // #5703 TEST_CASE(nothrowDeclspecThrow); TEST_CASE(rethrowNoCurrentException1); TEST_CASE(rethrowNoCurrentException2); TEST_CASE(rethrowNoCurrentException3); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], bool inconclusive = false) { // Clear the error buffer.. errout.str(""); settings.certainty.setEnabled(Certainty::inconclusive, inconclusive); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check char variable usage.. CheckExceptionSafety checkExceptionSafety(&tokenizer, &settings, this); checkExceptionSafety.runChecks(&tokenizer, &settings, this); } void destructors() { check("class x {\n" " ~x() {\n" " throw e;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Class x is not safe, destructor throws exception\n", errout.str()); check("class x {\n" " ~x();\n" "};\n" "x::~x() {\n" " throw e;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Class x is not safe, destructor throws exception\n", errout.str()); // #3858 - throwing exception in try block in destructor. check("class x {\n" " ~x() {\n" " try {\n" " throw e;\n" " } catch (...) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("class x {\n" " ~x() {\n" " if(!std::uncaught_exception()) {\n" " throw e;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void deallocThrow1() { check("int * p;\n" "void f(int x) {\n" " delete p;\n" " if (x)\n" " throw 123;\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout.str()); check("void f() {\n" " static int* p = foo;\n" " delete p;\n" " if (foo)\n" " throw 1;\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout.str()); } void deallocThrow2() { check("void f() {\n" " int* p = 0;\n" " delete p;\n" " if (foo)\n" " throw 1;\n" " p = new int;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int* p = 0;\n" " delete p;\n" " reset(p);\n" " throw 1;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void deallocThrow3() { check("void f() {\n" " static int* p = 0;\n" " delete p;\n" " throw 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int* p = 0;\n" " delete p;\n" " throw 1;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout.str()); } void rethrowCopy1() { check("void f() {\n" " try\n" " {\n" " foo();\n" " }\n" " catch(const exception& err)\n" " {\n" " throw err;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); } void rethrowCopy2() { check("void f() {\n" " try\n" " {\n" " foo();\n" " }\n" " catch(exception& err)\n" " {\n" " throw err;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); } void rethrowCopy3() { check("void f() {\n" " try {\n" " foo();\n" " }\n" " catch(std::runtime_error& err) {\n" " throw err;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); } void rethrowCopy4() { check("void f() {\n" " try\n" " {\n" " foo();\n" " }\n" " catch(const exception& err)\n" " {\n" " exception err2;\n" " throw err2;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void rethrowCopy5() { check("void f() {\n" " try {\n" " foo();\n" " }\n" " catch(const exception& outer) {\n" " try {\n" " foo(outer);\n" " }\n" " catch(const exception& inner) {\n" " throw inner;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); check("void f() {\n" " try {\n" " foo();\n" " }\n" " catch(const exception& outer) {\n" " try {\n" " foo(outer);\n" " }\n" " catch(const exception& inner) {\n" " throw outer;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void catchExceptionByValue() { check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch( ::std::exception err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Exception should be caught by reference.\n", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(const exception err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Exception should be caught by reference.\n", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch( ::std::exception& err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(exception* err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(const exception& err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(int err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(exception* const err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void noexceptThrow() { check("void func1() noexcept(false) { try {} catch(...) {;} throw 1; }\n" "void func2() noexcept { throw 1; }\n" "void func3() noexcept(true) { throw 1; }\n" "void func4() noexcept(false) { throw 1; }\n" "void func5() noexcept(true) { func1(); }\n" "void func6() noexcept(false) { func1(); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:5]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() noexcept { return 0; }\n" "const char *func1() noexcept { try { throw 1; } catch(...) {} return 0; }"); ASSERT_EQUALS("", errout.str()); } void nothrowThrow() { check("void func1() throw(int) { try {;} catch(...) { throw 1; } ; }\n" "void func2() throw() { throw 1; }\n" "void func3() throw(int) { throw 1; }\n" "void func4() throw() { func1(); }\n" "void func5() throw(int) { func1(); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:4]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() throw() { return 0; }"); ASSERT_EQUALS("", errout.str()); } void unhandledExceptionSpecification1() { // #4800 check("void myThrowingFoo() throw(MyException) {\n" " throw MyException();\n" "}\n" "void myNonCatchingFoo() {\n" " myThrowingFoo();\n" "}\n" "void myCatchingFoo() {\n" " try {\n" " myThrowingFoo();\n" " } catch(MyException &) {}\n" "}\n", true); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (style, inconclusive) Unhandled exception specification when calling function myThrowingFoo().\n", errout.str()); } void unhandledExceptionSpecification2() { check("void f() const throw (std::runtime_error);\n" "int main()\n" "{\n" " f();\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nothrowAttributeThrow() { check("void func1() throw(int) { throw 1; }\n" "void func2() __attribute((nothrow)); void func2() { throw 1; }\n" "void func3() __attribute((nothrow)); void func3() { func1(); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() __attribute((nothrow)); void func1() { return 0; }"); ASSERT_EQUALS("", errout.str()); } void nothrowAttributeThrow2() { check("class foo {\n" " void copyMemberValues() throw () {\n" " copyMemberValues();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void nothrowDeclspecThrow() { check("void func1() throw(int) { throw 1; }\n" "void __declspec(nothrow) func2() { throw 1; }\n" "void __declspec(nothrow) func3() { func1(); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() __attribute((nothrow)); void func1() { return 0; }"); ASSERT_EQUALS("", errout.str()); } void rethrowNoCurrentException1() { check("void func1(const bool flag) { try{ if(!flag) throw; } catch (int&) { ; } }"); ASSERT_EQUALS("[test.cpp:1]: (error) Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow." " If there is no current exception this calls std::terminate(). More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object\n", errout.str()); } void rethrowNoCurrentException2() { check("void func1() { try{ ; } catch (...) { ; } throw; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow." " If there is no current exception this calls std::terminate(). More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object\n", errout.str()); } void rethrowNoCurrentException3() { check("void on_error() { try { throw; } catch (const int &) { ; } catch (...) { ; } }\n" // exception dispatcher idiom "void func2() { try{ ; } catch (const int&) { throw; } ; }\n" "void func3() { throw 0; }"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestExceptionSafety) cppcheck-2.7/test/testexprengine.cpp000066400000000000000000001152061417746362400177140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "exprengine.h" #include "library.h" #include "platform.h" #include "settings.h" #include "token.h" #include "tokenize.h" #include "testsuite.h" #include #include #include #include #include #include class TestExprEngine : public TestFixture { public: TestExprEngine() : TestFixture("TestExprEngine") {} private: void run() OVERRIDE { #ifdef USE_Z3 TEST_CASE(annotation1); TEST_CASE(annotation2); TEST_CASE(expr1); TEST_CASE(expr2); TEST_CASE(expr3); TEST_CASE(expr4); TEST_CASE(expr5); TEST_CASE(expr6); TEST_CASE(expr7); TEST_CASE(expr8); TEST_CASE(expr9); TEST_CASE(exprAssign1); TEST_CASE(exprAssign2); // Truncation TEST_CASE(exprNot); TEST_CASE(getValueConst1); TEST_CASE(inc1); TEST_CASE(inc2); TEST_CASE(if1); TEST_CASE(if2); TEST_CASE(if3); TEST_CASE(if4); TEST_CASE(if5); TEST_CASE(ifAlwaysTrue1); TEST_CASE(ifAlwaysTrue2); TEST_CASE(ifAlwaysTrue3); TEST_CASE(ifAlwaysFalse1); TEST_CASE(ifAlwaysFalse2); TEST_CASE(ifelse1); TEST_CASE(ifif); TEST_CASE(ifreturn); TEST_CASE(ifIntRangeAlwaysFalse); TEST_CASE(ifIntRangeAlwaysTrue); TEST_CASE(istream); TEST_CASE(switch1); TEST_CASE(switch2); TEST_CASE(for1); TEST_CASE(forAlwaysFalse1); TEST_CASE(while1); TEST_CASE(while2); TEST_CASE(while3); TEST_CASE(while4); TEST_CASE(while5); TEST_CASE(array1); TEST_CASE(array2); TEST_CASE(array3); TEST_CASE(array4); TEST_CASE(array5); TEST_CASE(array6); TEST_CASE(array7); TEST_CASE(arrayInit1); TEST_CASE(arrayInit2); TEST_CASE(arrayInit3); TEST_CASE(arrayUninit); TEST_CASE(arrayInLoop); TEST_CASE(floatValue1); TEST_CASE(floatValue2); TEST_CASE(floatValue3); TEST_CASE(floatValue4); TEST_CASE(floatValue5); TEST_CASE(functionCall1); TEST_CASE(functionCall2); TEST_CASE(functionCall3); TEST_CASE(functionCall4); TEST_CASE(functionCall5); TEST_CASE(functionCallContract1); TEST_CASE(functionCallContract2); TEST_CASE(int1); TEST_CASE(pointer1); TEST_CASE(pointer2); TEST_CASE(pointer3); TEST_CASE(pointerAlias1); TEST_CASE(pointerAlias2); TEST_CASE(pointerAlias3); TEST_CASE(pointerAlias4); TEST_CASE(pointerNull1); TEST_CASE(structMember1); TEST_CASE(structMember2); TEST_CASE(structMember3); TEST_CASE(pointerToStructInLoop); TEST_CASE(ternaryOperator1); #endif } static void replace(std::string& str, const std::string& from, const std::string& to) { size_t pos = 0; while ((pos = str.find(from, pos)) != std::string::npos) str.replace(pos, from.length(), to); } static std::string cleanupExpr(std::string rawexpr) { std::string ret; std::istringstream istr(rawexpr); std::string line; while (std::getline(istr, line)) { if (line.empty()) continue; line = line.substr(line.find_first_not_of(" ")); if (line.compare(0,13,"(declare-fun ") == 0) continue; if (line == "(solver") continue; if (line.compare(0,9,"(assert (") == 0) { line.erase(0,8); line.erase(line.size()-1); } replace(line, "(fp.gt ", "(> "); replace(line, "(fp.lt ", "(< "); replace(line, "(_ +zero 11 53)", "0.0"); replace(line, "(fp #b0 #b10000000010 #x899999999999a)", "12.3"); replace(line, "(/ 123.0 10.0)", "12.3"); int par = 0; for (char pos : line) { if (pos == '(') par++; else if (pos == ')') --par; } if (par < 0) line.erase(line.size() - 1); ret += line + "\n"; } return ret; } #define expr(code, binop) expr_(code, binop, __FILE__, __LINE__) std::string expr_(const char code[], const std::string &binop, const char* file, int line) { Settings settings; settings.platform(cppcheck::Platform::Unix64); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); std::string ret; ExprEngine::Callback f = [&](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (tok->str() != binop) return; const auto *b = dynamic_cast(&value); if (!b) return; ret += TestExprEngine::cleanupExpr(b->getExpr(dataBase)); }; std::vector callbacks; callbacks.push_back(f); std::ostringstream trace; ExprEngine::executeAllFunctions(this, &tokenizer, &settings, callbacks, trace); return ret; } #define functionCallContractExpr(...) functionCallContractExpr_(code, s, __FILE__, __LINE__) std::string functionCallContractExpr_(const char code[], const Settings &s, const char* file, int line) { Settings settings; settings.bugHunting = true; settings.debugBugHunting = true; settings.functionContracts = s.functionContracts; settings.platform(cppcheck::Platform::Unix64); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); std::vector callbacks; std::ostringstream trace; ExprEngine::executeAllFunctions(this, &tokenizer, &settings, callbacks, trace); std::string ret = trace.str(); std::string::size_type pos1 = ret.find("checkContract:{"); std::string::size_type pos2 = ret.find("}", pos1); if (pos2 == std::string::npos) return "Error:" + ret; return TestExprEngine::cleanupExpr(ret.substr(pos1, pos2+1-pos1)); } #define getRange(...) getRange_(__FILE__, __LINE__, __VA_ARGS__) std::string getRange_(const char* file, int line, const char code[], const std::string &str, int linenr = 0) { Settings settings; settings.platform(cppcheck::Platform::Unix64); settings.library.smartPointers["std::shared_ptr"]; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); std::string ret; ExprEngine::Callback f = [&](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { (void)dataBase; if ((linenr == 0 || linenr == tok->linenr()) && tok->expressionString() == str) { if (!ret.empty()) ret += ","; ret += (value.getRange)(); } }; std::vector callbacks; callbacks.push_back(f); std::ostringstream trace; ExprEngine::executeAllFunctions(this, &tokenizer, &settings, callbacks, trace); return ret; } #define trackExecution(...) trackExecution_(__FILE__, __LINE__, __VA_ARGS__) std::string trackExecution_(const char* file, int line, const char code[], Settings *settings = nullptr) { Settings s; if (!settings) settings = &s; settings->bugHunting = true; settings->debugBugHunting = true; settings->platform(cppcheck::Platform::Unix64); settings->library.smartPointers["std::shared_ptr"]; Tokenizer tokenizer(settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); std::vector callbacks; std::ostringstream ret; ExprEngine::executeAllFunctions(this, &tokenizer, settings, callbacks, ret); return ret.str(); } void annotation1() { const char code[] = "void f(__cppcheck_low__(100) short x) {\n" " return x < 10;\n" "}"; const char expected[] = "(>= $1 100)\n" // <- annotation "(and (>= $1 (- 32768)) (<= $1 32767))\n" "(< $1 10)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "<")); } void annotation2() { const char code[] = "__cppcheck_low__(100) short x;\n" " void f() {\n" " return x < 10;\n" "}"; const char expected[] = "(>= $1 100)\n" // <- annotation "(and (>= $1 (- 32768)) (<= $1 32767))\n" "(< $1 10)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "<")); } void expr1() { ASSERT_EQUALS("-32768:32767", getRange("void f(short x) { a = x; }", "x")); } void expr2() { ASSERT_EQUALS("($1)+($1)", getRange("void f(short x) { a = x + x; }", "x+x")); } void expr3() { ASSERT_EQUALS("($1)+($1)", getRange("int f(short x) { int a = x + x; return a; }", "return a")); } void expr4() { ASSERT_EQUALS("($1)-($1)", getRange("int f(short x) { int a = x - x; return a; }", "return a")); } void expr5() { ASSERT_EQUALS("($1)+($2)", getRange("void f(short a, short b, short c, short d) { if (a+b 1000;" "}"; ASSERT_EQUALS("(8)-($1)", getRange(code, "8-x")); ASSERT_EQUALS("(and (>= $1 0) (<= $1 255))\n" "(> (- 8 $1) 1000)\n" "z3::unsat\n", expr(code, ">")); } void expr7() { const char code[] = "void f(bool a, bool b, int c) {\n" " if (a||b) {}\n" " c > 1000;" "}"; ASSERT_EQUALS("(or (distinct $1 0) (distinct $2 0))\n" "(and (>= $3 (- 2147483648)) (<= $3 2147483647))\n" "(and (>= $1 0) (<= $1 1))\n" "(and (>= $2 0) (<= $2 1))\n" "(> $3 1000)\n" "z3::sat\n" "(= (ite (or (distinct $1 0) (distinct $2 0)) 1 0) 0)\n" "(and (>= $3 (- 2147483648)) (<= $3 2147483647))\n" "(and (>= $1 0) (<= $1 1))\n" "(and (>= $2 0) (<= $2 1))\n" "(> $3 1000)\n" "z3::sat\n", expr(code, ">")); } void expr8() { const char code[] = "void foo(int x, int y) {\n" " if (x % 32) {}\n" " y==3;\n" "}"; // Do not crash expr(code, "=="); } void expr9() { Settings settings; LOAD_LIB_2(settings.library, "std.cfg"); ASSERT_EQUALS("1:26: $4=ArrayValue([$3],[:]=$2)\n" "1:26: $3=IntRange(0:2147483647)\n" "1:26: $2=IntRange(-128:127)\n" "1:27: D0:memory:{s=($4,[$3],[:]=$2)}\n", trackExecution("void foo() { std::string s; }", &settings)); ASSERT_EQUALS("1:52: $4=ArrayValue([$3],[:]=$2)\n" "1:52: $3=IntRange(0:2147483647)\n" "1:52: $2=IntRange(-128:127)\n" "1:66: D0:memory:{s=($4,[$3],[:]=$2)}\n", trackExecution("std::string getName(int); void foo() { std::string s = getName(1); }", &settings)); } void exprAssign1() { ASSERT_EQUALS("($1)+(1)", getRange("void f(unsigned char a) { a += 1; }", "a+=1")); } void exprAssign2() { ASSERT_EQUALS("2", getRange("void f(unsigned char x) { x = 258; int a = x }", "a=x")); } void exprNot() { ASSERT_EQUALS("($1)==(0)", getRange("void f(unsigned char a) { return !a; }", "!a")); } void getValueConst1() { // Data::getValue ASSERT_EQUALS("512", getRange("const int x=512; void func() { x=x }", "x=x")); } void inc1() { ASSERT_EQUALS("(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= (+ $1 1) $1)\n" "z3::unsat\n", expr("void f(int x) { int y = x++; x == y; }", "==")); ASSERT_EQUALS("(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= (+ $1 1) (+ $1 1))\n" "z3::sat\n", expr("void f(int x) { int y = ++x; x == y; }", "==")); } void inc2() { ASSERT_EQUALS("(= 2 2)\n" "z3::sat\n", expr("void f() { unsigned char a[2]; a[0] = 1; a[0]++; a[0] == a[0]; }", "==")); } void if1() { ASSERT_EQUALS("(< $1 $2)\n" "(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(and (>= $2 (- 2147483648)) (<= $2 2147483647))\n" "(= $1 $2)\n" "z3::unsat\n", expr("void f(int x, int y) { if (x < y) return x == y; }", "==")); } void if2() { const char code[] = "void foo(int x) {\n" " if (x > 0 && x == 20) {}\n" "}"; // In expression "x + x < 20", "x" is greater than 0 const char expected[] = "(> $1 0)\n" "(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= $1 20)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void if3() { const char code[] = "void foo(int x) {\n" " if (x > 0 || x == 20) {}\n" "}"; // In expression "x + x < 20", "x" is greater than 0 const char expected[] = "(<= $1 0)\n" "(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= $1 20)\n" "z3::unsat\n"; // "x == 20" is unsat ASSERT_EQUALS(expected, expr(code, "==")); } void if4() { const char code[] = "void foo(unsigned int x, unsigned int y) {\n" " unsigned int z = y;" " if (x < z) { return z == 0; }\n" "}"; const char expected[] = "(< $1 $2)\n" "(>= $2 0)\n" "(>= $1 0)\n" "(= $2 0)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void if5() { ASSERT_EQUALS("(> |$2:0| 12)\n" "(and (>= |$2:0| (- 2147483648)) (<= |$2:0| 2147483647))\n" "(= |$2:0| 5)\n" "z3::unsat\n", expr("void foo(const int *x) { if (f1() && *x > 12) dostuff(*x == 5); }", "==")); } void ifAlwaysTrue1() { const char code[] = "int foo() {\n" " int a = 42;\n" " if (1) {\n" " a = 0;\n" " }\n" " return a == 0;\n" "}"; const char expected[] = "(= 0 0)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void ifAlwaysTrue2() { const char code[] = "int foo() {\n" " int a = 42;\n" " if (12.3) {\n" " a = 0;\n" " }\n" " return a == 0;\n" "}"; const char expected[] = "(= 0 0)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void ifAlwaysTrue3() { const char code[] = "int foo() {\n" " int a = 42;\n" " if (\"test\") {\n" " a = 0;\n" " }\n" " return a == 0;\n" "}"; // String literals are always true. z3 will not be involved. ASSERT_EQUALS("(= 0 0)\n" "z3::sat\n", expr(code, "==")); } void ifAlwaysFalse1() { const char code[] = "int foo() {\n" " int a = 42;\n" " if (0) {\n" " a = 0;\n" " }\n" " return a == 0;\n" "}"; const char expected[] = "(= 42 0)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void ifAlwaysFalse2() { const char code[] = "int foo() {\n" " int a = 42;\n" " if (0.0) {\n" " a = 0;\n" " }\n" " return a == 0;\n" "}"; const char expected[] = "(= 42 0)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void ifelse1() { ASSERT_EQUALS("(<= $1 5)\n" "(and (>= $1 (- 32768)) (<= $1 32767))\n" "(= (+ $1 2) 40)\n" "z3::unsat\n", expr("void f(short x) { if (x > 5) ; else if (x+2==40); }", "==")); } void ifif() { const char code[] = "void foo(unsigned char x) {\n" " if (x > 5) {}\n" " if (x > 5) {}\n" " return x == 13;\n" "}"; ASSERT_EQUALS("(> $1 5)\n" "(and (>= $1 0) (<= $1 255))\n" "(= $1 13)\n" "z3::sat\n" "(<= $1 5)\n" "(and (>= $1 0) (<= $1 255))\n" "(= $1 13)\n" "z3::unsat\n", expr(code, "==")); } void ifreturn() { // Early return const char code[] = "void foo(unsigned char x) {\n" " if (x > 5) { return; }\n" " return x == 13;\n" "}"; ASSERT_EQUALS("(<= $1 5)\n" "(and (>= $1 0) (<= $1 255))\n" "(= $1 13)\n" "z3::unsat\n", expr(code, "==")); } void ifIntRangeAlwaysFalse() { const char code[] = "void foo(unsigned char x) {\n" " if (x > 0)\n" " return;\n" " if (x) {\n" // <-- condition should be "always false". " x++;\n" " }\n" " return x == 0;\n" // <- sat "}"; ASSERT_EQUALS("(<= $1 0)\n" "(and (>= $1 0) (<= $1 255))\n" "(= $1 0)\n" "z3::sat\n", expr(code, "==")); } void ifIntRangeAlwaysTrue() { const char code[] = "void foo(unsigned char x) {\n" " if (x < 1)\n" " return;\n" " if (x) {\n" // <-- condition should be "always true". " x++;\n" " }\n" " return x == 0;\n" // <- unsat "}"; ASSERT_EQUALS("(>= $1 1)\n" "(and (>= $1 0) (<= $1 255))\n" "(= (+ $1 1) 0)\n" "z3::unsat\n", expr(code, "==")); } void istream() { const char code[] = "void foo(const std::string& in) {\n" " std::istringstream istr(in);\n" " unsigned short x=5;\n" " istr >> x;\n" " x==3;\n" "}"; ASSERT_EQUALS("(and (>= $1 0) (<= $1 65535))\n" "(= $1 3)\n" "z3::sat\n", expr(code, "==")); } void switch1() { const char code[] = "void f(int x) {\n" " switch (x) {\n" " case 1: x==3; break;\n" " case 2: x>0; break;\n" " };\n" " x<=4;\n" "}"; ASSERT_EQUALS("(= $1 1)\n" "(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= $1 3)\n" "z3::unsat\n", expr(code, "==")); } void switch2() { const char code[] = "void foo(char type, int mcc) {\n" " switch (type) {\n" " case '1':\n" " case '3':\n" " break;\n" " default:\n" " return false;\n" " }\n" " p[0] = mcc == 0;\n" "}"; ASSERT_EQUALS("(= $1 49)\n" "(and (>= $2 (- 2147483648)) (<= $2 2147483647))\n" "(and (>= $1 (- 128)) (<= $1 127))\n" "(= $2 0)\n" "z3::sat\n" "(= $1 51)\n" "(and (>= $2 (- 2147483648)) (<= $2 2147483647))\n" "(and (>= $1 (- 128)) (<= $1 127))\n" "(= $2 0)\n" "z3::sat\n", expr(code, "==")); } void for1() { const char code[] = "void f() {\n" " int x[10];\n" " for (int i = 0; i < 10; i++) x[i] = 0;\n" " x[4] == 67;\n" "}"; ASSERT_EQUALS("(= 0 67)\n" "z3::unsat\n", expr(code, "==")); } void forAlwaysFalse1() { const char code[] = "int f() {\n" " int a = 19;\n" " for (int i = 0; i < 0; i++)\n" " a += 8;\n" " for (int i = 0; i < 1; i++)\n" " a += 23;\n" " for (int i = 100; i >= 1; i--)\n" " a += 23;\n" " return a == 42;\n" "}"; const char expected[] = "(and (>= $4 (- 2147483648)) (<= $4 2147483647))\n" "(= (+ $4 23) 42)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void while1() { const char code[] = "void f(int y) {\n" " int x = 0;\n" " while (x < y)\n" " x = x + 34;\n" " x == 340;\n" "}"; const char expected[] = "(< 0 $1)\n" "(and (>= $2 (- 2147483648)) (<= $2 2147483647))\n" "(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= (+ $2 34) 340)\n" "z3::sat\n" "(= 0 340)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void while2() { const char code[] = "void f(int y) {\n" " int x = 0;\n" " while (x < y)\n" " x++;\n" " x == 1;\n" "}"; const char expected[] = "(< 0 $1)\n" "(and (>= $2 (- 2147483648)) (<= $2 2147483647))\n" "(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= (+ $2 1) 1)\n" "z3::sat\n" "(= 0 1)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void while3() { const char code[] = "struct AB {int a; int b;};\n" "void f() {\n" " struct AB ab;\n" " while (1)\n" " ab.a = 3;\n" " ab.a == 0;\n" "}"; ASSERT_EQUALS("(= 3 0)\n" "z3::unsat\n", expr(code, "==")); } void while4() { const char code[] = "void f(const char *host, int *len) {\n" " while (*host)\n" " *len = 0;\n" " *len == 0;\n" "}"; const char expected[] = "(distinct |$2:0| 0)\n" "(and (>= |$2:0| (- 128)) (<= |$2:0| 127))\n" "(= 0 0)\n" "z3::sat\n" "(and (>= $8 (- 2147483648)) (<= $8 2147483647))\n" "(= $8 0)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void while5() { const char code[] = "void f() {\n" " int x;\n" " while (cond)\n" " x += 4;\n" "}"; ASSERT(getRange(code, "x", 4).find("?") != std::string::npos); } void array1() { ASSERT_EQUALS("(= 5 0)\nz3::unsat\n", expr("int f() { int arr[10]; arr[4] = 5; return arr[4]==0; }", "==")); } void array2() { ASSERT_EQUALS("(and (>= |$4:4| 0) (<= |$4:4| 255))\n" "(= |$4:4| 365)\n" "z3::unsat\n", expr("void dostuff(unsigned char *); int f() { unsigned char arr[10] = \"\"; dostuff(arr); return arr[4] == 365; }", "==")); } void array3() { const char code[] = "void f(unsigned char x) { int arr[10]; arr[4] = 43; return arr[x] == 12; }"; ASSERT_EQUALS("?,43", getRange(code, "arr[x]")); ASSERT_EQUALS("(and (>= $1 0) (<= $1 255))\n" "(= (ite (= $1 4) 43 0) 12)\n" "z3::unsat\n", expr(code, "==")); } void array4() { const char code[] = "int buf[10];\n" "void f() { int x = buf[0]; }"; ASSERT_EQUALS("2:16: $2:0=IntRange(-2147483648:2147483647)\n" "2:20: $1=ArrayValue([10],[:]=$2)\n" "2:20: $2=IntRange(-2147483648:2147483647)\n" "2:26: D0:memory:{buf=($1,[10],[:]=$2) x=$2:0}\n", trackExecution(code)); } void array5() { const char code[] = "int f(int x) {\n" " int buf[3][4][5];\n" " buf[x][1][2] = 10;\n" " return buf[0][1][2];\n" "}"; ASSERT_EQUALS("1:14: $1=IntRange(-2147483648:2147483647)\n" "1:14: D0:memory:{x=$1}\n" "2:7: $2=ArrayValue([3][4][5],[:]=?)\n" "2:19: D0:memory:{x=$1 buf=($2,[3][4][5],[:]=?)}\n" "3:20: D0:memory:{x=$1 buf=($2,[3][4][5],[:]=?,[((20)*($1))+(7)]=10)}\n", trackExecution(code)); } void array6() { const char code[] = "void foo(int *x) {\n" " *x = 2;\n" " if (*x == 21) {}" "}"; ASSERT_EQUALS("(= 2 21)\n" "z3::unsat\n", expr(code, "==")); } void array7() { const char code[] = "void foo(unsigned char *x) {\n" " *x = 2;\n" " *x = 1;\n" "}"; ASSERT_EQUALS("1:28: $2=ArrayValue([$1],[:]=?,null)\n" "1:28: $1=IntRange(1:2147483647)\n" "1:28: D0:memory:{x=($2,[$1],[:]=?)}\n" "2:9: D0:memory:{x=($2,[$1],[:]=?,[0]=2)}\n" "3:9: D0:memory:{x=($2,[$1],[:]=?,[0]=1)}\n", trackExecution(code)); } void arrayInit1() { ASSERT_EQUALS("0", getRange("inf f() { char arr[10] = \"\"; return arr[4]; }", "arr[4]")); } void arrayInit2() { ASSERT_EQUALS("66", getRange("void f() { char str[] = \"hello\"; str[0] = \'B\'; }", "str[0]=\'B\'")); } void arrayInit3() { ASSERT_EQUALS("-32768:32767", getRange("void f() { short buf[5] = {2, 1, 0, 3, 4}; ret = buf[2]; }", "buf[2]")); } void arrayUninit() { ASSERT_EQUALS("?", getRange("int f() { int arr[10]; return arr[4]; }", "arr[4]")); } void arrayInLoop() { const char code[] = "void f() {\n" " int arr[3][3];\n" " for (int i = 0; i < 3; i++) arr[i][0] = arr[1][2];\n" " return arr[0][0];" "}"; ASSERT_EQUALS("?", getRange(code, "arr[1][2]")); } void floatValue1() { ASSERT_EQUALS("-inf:inf", getRange("float f; void func() { f=f; }", "f=f")); } void floatValue2() { ASSERT_EQUALS("(29.0)/(2.0)", getRange("void func() { float f = 29.0; f = f / 2.0; }", "f/2.0")); } void floatValue3() { const char code[] = "void foo(float f) { return f > 12.3; }"; const char expected[] = "(> $1 12.3)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, ">")); } void floatValue4() { const char code[] = "void foo(float f) { return f > 12.3f; }"; const char expected[] = "(> $1 12.3)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, ">")); } void floatValue5() { // float < int const char code[] = "void foo(float f) { if (f < 1){} }"; const char expected[] = "(< $1 (to_real 1))\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "<")); } void functionCall1() { ASSERT_EQUALS("-2147483648:2147483647", getRange("int atoi(const char *p); void f() { int x = atoi(a); x = x; }", "x=x")); } void functionCall2() { const char code[] = "namespace NS {\n" " short getValue();\n" "}" "void f() {\n" " short value = NS::getValue();\n" " value = value;\n" "}"; ASSERT_EQUALS("-32768:32767", getRange(code, "value=value")); } void functionCall3() { ASSERT_EQUALS("-2147483648:2147483647", getRange("int fgets(int, const char *, void *); void f() { int x = -1; fgets(stdin, \"%d\", &x); x=x; }", "x=x")); } void functionCall4() { ASSERT_EQUALS("1:2147483647", getRange("void f() { sizeof(data); }", "sizeof(data)")); } void functionCall5() { // unknown result from function, pointer type.. ASSERT_EQUALS("1:36: $3=ArrayValue([$2],[:]=bailout,null)\n" "1:36: $2=IntRange(1:2147483647)\n" "1:36: bailout=BailoutValue(bailout)\n" "1:46: D0:memory:{p=($3,[$2],[:]=bailout)}\n", trackExecution("char *foo(int); void bar() { char *p = foo(1); }")); } void functionCallContract1() { const char code[] = "void foo(int x);\n" "void bar(unsigned short x) { foo(x); }"; Settings s; s.functionContracts["foo(x)"] = "x < 1000"; ASSERT_EQUALS("checkContract:{\n" "(ite (< $2 1000) false true)\n" "(= $2 $1)\n" "(and (>= $2 (- 2147483648)) (<= $2 2147483647))\n" "(and (>= $1 0) (<= $1 65535))\n" "}\n", functionCallContractExpr(code, s)); } void functionCallContract2() { const char code[] = "void foo(float x);\n" "void bar(float x) { foo(x); }"; Settings s; s.functionContracts["foo(x)"] = "x < 12.3"; ASSERT_EQUALS("checkContract:{\n" "(ite (< $2 12.3) false true)\n" "}\n", functionCallContractExpr(code, s)); } void int1() { ASSERT_EQUALS("(and (>= $1 (- 2147483648)) (<= $1 2147483647))\n" "(= (+ 2 $1) 3)\n" "z3::sat\n", expr("void f(int x) { return 2+x==3; }", "==")); } void pointer1() { const char code[] = "void f(unsigned char *p) { return *p == 7; }"; ASSERT_EQUALS("[$1],[:]=?,null", getRange(code, "p")); ASSERT_EQUALS("(and (>= $3 0) (<= $3 255))\n" "(= $3 7)\n" "z3::sat\n", expr(code, "==")); } void pointer2() { const char code[] = "void f(unsigned char *p) { return p[2] == 7; }"; ASSERT_EQUALS("(and (>= $3 0) (<= $3 255))\n" "(= $3 7)\n" "z3::sat\n", expr(code, "==")); } void pointer3() { const char code[] = "void f(void *p) {\n" " double *data = (double *)p;\n" " return *data;" "}"; ASSERT_EQUALS("[$1],[:]=?,null", getRange(code, "p")); ASSERT_EQUALS("[$4],[:]=?,null", getRange(code, "data")); } void pointerAlias1() { ASSERT_EQUALS("3", getRange("int f() { int x; int *p = &x; x = 3; return *p; }", "return*p")); } void pointerAlias2() { ASSERT_EQUALS("1", getRange("int f() { int x; int *p = &x; *p = 1; return *p; }", "return*p")); } void pointerAlias3() { ASSERT_EQUALS("7", getRange("int f() {\n" " int x = 18;\n" " int *p = &x;\n" " *p = 7;\n" " return x;\n" "}", "x", 5)); } void pointerAlias4() { ASSERT_EQUALS("71", getRange("int f() { int x[10]; int *p = x+3; *p = 71; return x[3]; }", "x[3]")); } void pointerNull1() { ASSERT_EQUALS("1", getRange("void f(void *p) { p = NULL; p += 1; }", "p+=1")); } void structMember1() { ASSERT_EQUALS("(and (>= $2 0) (<= $2 255))\n" "(and (>= $3 0) (<= $3 255))\n" "(= (+ $2 $3) 0)\n" "z3::sat\n", expr("struct S {\n" " unsigned char a;\n" " unsigned char b;\n" "};\n" "void f(struct S s) { return s.a + s.b == 0; }", "==")); } void structMember2() { const char code[] = "struct S { int x; };\n" "void foo(struct S *s) { return s->x == 123; }"; const char expected[] = "(and (>= $3 (- 2147483648)) (<= $3 2147483647))\n" "(= $3 123)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void structMember3() { const char code[] = "struct S { int x; };\n" "void foo(struct S *s) {\n" " s->x = iter->second.data;\n" // assign some unknown value " return s->x == 1;\n" "}"; const char expected[] = "(and (>= $3 (- 2147483648)) (<= $3 2147483647))\n" "(= $3 1)\n" "z3::sat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } void pointerToStructInLoop() { const char code[] = "struct S { int x; };\n" "void foo(struct S *s) {\n" " while (1)\n" " s->x = 42; \n" "}"; const char expected[] = "(and (>= $3 (- 2147483648)) (<= $3 2147483647))\n" "(= $3 42)\n" "z3::sat\n"; TODO_ASSERT_EQUALS(expected, "", expr(code, "==")); } void ternaryOperator1() { const char code[] = "void foo(signed char x) {\n" " x = (x > 0) ? (0==x) : 0;\n" "}"; const char expected[] = "(> $1 0)\n" "(and (>= $1 (- 128)) (<= $1 127))\n" "(= 0 $1)\n" "z3::unsat\n"; ASSERT_EQUALS(expected, expr(code, "==")); } }; REGISTER_TEST(TestExprEngine) cppcheck-2.7/test/testfilelister.cpp000066400000000000000000000054241417746362400177120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "filelister.h" #include "pathmatch.h" #include "testsuite.h" #include #include #include #include #include #include class TestFileLister : public TestFixture { public: TestFileLister() : TestFixture("TestFileLister") {} private: void run() OVERRIDE { // bail out if the tests are not executed from the base folder { std::ifstream fin("test/testfilelister.cpp"); if (!fin.is_open()) return; } TEST_CASE(isDirectory); TEST_CASE(recursiveAddFiles); TEST_CASE(fileExists); } void isDirectory() const { ASSERT_EQUALS(false, FileLister::isDirectory("readme.txt")); ASSERT_EQUALS(true, FileLister::isDirectory("lib")); } void recursiveAddFiles() const { // Recursively add add files.. std::map files; std::vector masks; PathMatch matcher(masks); std::string err = FileLister::recursiveAddFiles(files, ".", matcher); ASSERT(err.empty()); // In case there are leading "./".. for (std::map::iterator i = files.begin(); i != files.end();) { if (i->first.compare(0,2,"./") == 0) { files[i->first.substr(2)] = i->second; files.erase(i++); } else ++i; } // Make sure source files are added.. ASSERT(files.find("cli/main.cpp") != files.end()); ASSERT(files.find("lib/token.cpp") != files.end()); ASSERT(files.find("lib/tokenize.cpp") != files.end()); ASSERT(files.find("test/testfilelister.cpp") != files.end()); // Make sure headers are not added.. ASSERT(files.find("lib/tokenize.h") == files.end()); } void fileExists() const { ASSERT_EQUALS(false, FileLister::fileExists("lib")); ASSERT_EQUALS(true, FileLister::fileExists("readme.txt")); } }; REGISTER_TEST(TestFileLister) cppcheck-2.7/test/testfunctions.cpp000066400000000000000000002305651417746362400175660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkfunctions.h" #include "config.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include class TestFunctions : public TestFixture { public: TestFunctions() : TestFixture("TestFunctions") {} private: Settings settings; void run() OVERRIDE { settings.severity.enable(Severity::style); settings.severity.enable(Severity::warning); settings.severity.enable(Severity::performance); settings.severity.enable(Severity::portability); settings.certainty.enable(Certainty::inconclusive); settings.libraries.emplace_back("posix"); settings.standards.c = Standards::C11; settings.standards.cpp = Standards::CPP11; LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "posix.cfg"); // Prohibited functions TEST_CASE(prohibitedFunctions_posix); TEST_CASE(prohibitedFunctions_index); TEST_CASE(prohibitedFunctions_qt_index); // FP when using the Qt function 'index'? TEST_CASE(prohibitedFunctions_rindex); TEST_CASE(prohibitedFunctions_var); // no false positives for variables TEST_CASE(prohibitedFunctions_gets); // dangerous function TEST_CASE(prohibitedFunctions_alloca); TEST_CASE(prohibitedFunctions_declaredFunction); // declared function ticket #3121 TEST_CASE(prohibitedFunctions_std_gets); // test std::gets TEST_CASE(prohibitedFunctions_multiple); // multiple use of obsolete functions TEST_CASE(prohibitedFunctions_c_declaration); // c declared function TEST_CASE(prohibitedFunctions_functionWithBody); // function with body TEST_CASE(prohibitedFunctions_crypt); // Non-reentrant function TEST_CASE(prohibitedFunctions_namespaceHandling); // Invalid function usage TEST_CASE(invalidFunctionUsage1); TEST_CASE(invalidFunctionUsageStrings); // Math function usage TEST_CASE(mathfunctionCall_fmod); TEST_CASE(mathfunctionCall_sqrt); TEST_CASE(mathfunctionCall_log); TEST_CASE(mathfunctionCall_acos); TEST_CASE(mathfunctionCall_asin); TEST_CASE(mathfunctionCall_pow); TEST_CASE(mathfunctionCall_atan2); TEST_CASE(mathfunctionCall_precision); // Ignored return value TEST_CASE(checkIgnoredReturnValue); TEST_CASE(checkIgnoredErrorCode); // memset.. TEST_CASE(memsetZeroBytes); TEST_CASE(memsetInvalid2ndParam); // missing "return" TEST_CASE(checkMissingReturn); // std::move for locar variable TEST_CASE(returnLocalStdMove1); TEST_CASE(returnLocalStdMove2); TEST_CASE(returnLocalStdMove3); TEST_CASE(returnLocalStdMove4); TEST_CASE(returnLocalStdMove5); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], const char filename[] = "test.cpp", const Settings* settings_ = nullptr) { // Clear the error buffer.. errout.str(""); if (!settings_) settings_ = &settings; // Tokenize.. Tokenizer tokenizer(settings_, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); CheckFunctions checkFunctions(&tokenizer, settings_, this); checkFunctions.runChecks(&tokenizer, settings_, this); } void prohibitedFunctions_posix() { check("void f()\n" "{\n" " bsd_signal(SIGABRT, SIG_IGN);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolescent function 'bsd_signal' called. It is recommended to use 'sigaction' instead.\n", errout.str()); check("int f()\n" "{\n" " int bsd_signal(0);\n" " return bsd_signal;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " struct hostent *hp;\n" " if(!hp = gethostbyname(\"127.0.0.1\")) {\n" " exit(1);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'gethostbyname' called. It is recommended to use 'getaddrinfo' instead.\n", errout.str()); check("void f()\n" "{\n" " long addr;\n" " addr = inet_addr(\"127.0.0.1\");\n" " if(!hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) {\n" " exit(1);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Obsolescent function 'gethostbyaddr' called. It is recommended to use 'getnameinfo' instead.\n", errout.str()); check("void f()\n" "{\n" " usleep( 1000 );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout.str()); } void prohibitedFunctions_index() { check("namespace n1 {\n" " int index(){ return 1; };\n" "}\n" "int main()\n" "{\n" " n1::index();\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::size_t f()\n" "{\n" " std::size_t index(0);\n" " index++;\n" " return index;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f()\n" "{\n" " return this->index();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int index( 0 );\n" "}"); ASSERT_EQUALS("", errout.str()); check("const char f()\n" "{\n" " const char var[6] = \"index\";\n" " const char i = index(var, 0);\n" " return i;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n", errout.str()); } void prohibitedFunctions_qt_index() { check("void TDataModel::forceRowRefresh(int row) {\n" " emit dataChanged(index(row, 0), index(row, columnCount() - 1));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n", errout.str()); } void prohibitedFunctions_rindex() { check("void f()\n" "{\n" " int rindex( 0 );\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " const char var[7] = \"rindex\";\n" " print(rindex(var, 0));\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'rindex' called. It is recommended to use 'strrchr' instead.\n", errout.str()); } void prohibitedFunctions_var() { check("class Fred {\n" "public:\n" " Fred() : index(0) { }\n" " int index;\n" "};"); ASSERT_EQUALS("", errout.str()); } void prohibitedFunctions_gets() { check("void f()\n" "{\n" " char *x = gets(a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); check("void f()\n" "{\n" " foo(x, gets(a));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); } void prohibitedFunctions_alloca() { check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.cpp"); // #4382 - there are no VLAs in C++ ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'alloca' called.\n", errout.str()); check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (warning) Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n", errout.str()); settings.standards.c = Standards::C89; settings.standards.cpp = Standards::CPP03; check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.cpp"); // #4382 - there are no VLAs in C++ ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.c"); // #7558 - no alternative to alloca in C89 ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); settings.standards.c = Standards::C11; settings.standards.cpp = Standards::CPP11; } // ticket #3121 void prohibitedFunctions_declaredFunction() { check("int ftime ( int a )\n" "{\n" " return a;\n" "}\n" "int main ()\n" "{\n" " int b ; b = ftime ( 1 ) ;\n" " return 0 ;\n" "}"); ASSERT_EQUALS("", errout.str()); } // test std::gets void prohibitedFunctions_std_gets() { check("void f(char * str)\n" "{\n" " char *x = std::gets(str);\n" " char *y = gets(str);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n" "[test.cpp:4]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); } // multiple use void prohibitedFunctions_multiple() { check("void f(char * str)\n" "{\n" " char *x = std::gets(str);\n" " usleep( 1000 );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n" "[test.cpp:4]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout.str()); } void prohibitedFunctions_c_declaration() { check("char * gets ( char * c ) ;\n" "int main ()\n" "{\n" " char s [ 10 ] ;\n" " gets ( s ) ;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); check("int getcontext(ucontext_t *ucp);\n" "void f (ucontext_t *ucp)\n" "{\n" " getcontext ( ucp ) ;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads.\n", errout.str()); } void prohibitedFunctions_functionWithBody() { check("char * gets ( char * c ) { return c; }\n" "int main ()\n" "{\n" " char s [ 10 ] ;\n" " gets ( s ) ;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void prohibitedFunctions_crypt() { check("void f(char *pwd)\n" "{\n" " char *cpwd;" " crypt(pwd, cpwd);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n" "[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str()); check("void f()\n" "{\n" " char *pwd = getpass(\"Password:\");" " char *cpwd;" " crypt(pwd, cpwd);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n" "[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str()); check("int f()\n" "{\n" " int crypt = 0;" " return crypt;\n" "}"); ASSERT_EQUALS("", errout.str()); } void prohibitedFunctions_namespaceHandling() { check("void f()\n" "{\n" " time_t t = 0;" " std::localtime(&t);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'.\n", errout.str()); // Passed as function argument check("void f()\n" "{\n" " printf(\"Magic guess: %d\", getpwent());\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout.str()); // Pass return value check("void f()\n" "{\n" " time_t t = 0;" " struct tm *foo = localtime(&t);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'.\n", errout.str()); // Access via global namespace check("void f()\n" "{\n" " ::getpwent();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function getpwent() is not used.\n" "[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout.str()); // Be quiet on function definitions check("int getpwent()\n" "{\n" " return 123;\n" "}"); ASSERT_EQUALS("", errout.str()); // Be quiet on other namespaces check("void f()\n" "{\n" " foobar::getpwent();\n" "}"); ASSERT_EQUALS("", errout.str()); // Be quiet on class member functions check("void f()\n" "{\n" " foobar.getpwent();\n" "}"); ASSERT_EQUALS("", errout.str()); } void invalidFunctionUsage1() { check("void f() { memset(a,b,sizeof(a)!=12); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("void f() { memset(a,b,sizeof(a)!=0); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("void f() { memset(a,b,!c); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); // Ticket #6990 check("void f(bool c) { memset(a,b,c); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("void f() { memset(a,b,true); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); // Ticket #6588 (c mode) check("void record(char* buf, int n) {\n" " memset(buf, 0, n < 255);\n" /* KO */ " memset(buf, 0, n < 255 ? n : 255);\n" /* OK */ "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); // Ticket #6588 (c++ mode) check("void record(char* buf, int n) {\n" " memset(buf, 0, n < 255);\n" /* KO */ " memset(buf, 0, n < 255 ? n : 255);\n" /* OK */ "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("int boolArgZeroIsInvalidButOneIsValid(int a, int param) {\n" " return div(a, param > 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid div() argument nr 2. The value is 0 or 1 (boolean) but the valid values are ':-1,1:'.\n", errout.str()); check("void boolArgZeroIsValidButOneIsInvalid(int param) {\n" " strtol(a, b, param > 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid strtol() argument nr 3. The value is 0 or 1 (boolean) but the valid values are '0,2:36'.\n", errout.str()); check("void f() { strtol(a,b,1); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 1 but the valid values are '0,2:36'.\n", errout.str()); check("void f() { strtol(a,b,10); }"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" // #10754 " int N = -1;\n" " for (long i = 0; i < g(); i++)\n" " N = h(N);\n" " v.resize(N);\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (warning) Invalid v.resize() argument nr 1. The value is -1 but the valid values are '0:'.\n", errout.str()); check("void f(std::vector& v, int N) {\n" " if (N < -1)\n" " return;\n" " v.resize(N);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'N<-1' is redundant or v.resize() argument nr 1 can have invalid value. The value is -1 but the valid values are '0:'.\n", errout.str()); check("void f(std::vector& v, int N) {\n" " if (N == -1) {}\n" " v.resize(N);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'N==-1' is redundant or v.resize() argument nr 1 can have invalid value. The value is -1 but the valid values are '0:'.\n", errout.str()); check("void f(std::vector& v, int N, bool b) {\n" " if (b)\n" " N = -1;\n" " v.resize(N);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (warning) Invalid v.resize() argument nr 1. The value is -1 but the valid values are '0:'.\n", errout.str()); check("void f(std::vector& v) {\n" " int N = -1;\n" " v.resize(N);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid v.resize() argument nr 1. The value is -1 but the valid values are '0:'.\n", errout.str()); } void invalidFunctionUsageStrings() { check("size_t f() { char x = 'x'; return strlen(&x); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("size_t f() { return strlen(&x); }"); ASSERT_EQUALS("", errout.str()); check("size_t f(char x) { return strlen(&x); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("size_t f() { char x = '\\0'; return strlen(&x); }"); ASSERT_EQUALS("", errout.str()); check("size_t f() {\n" " char x;\n" " if (y)\n" " x = '\\0';\n" " else\n" " x = 'a';\n" " return strlen(&x);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("int f() { char x = '\\0'; return strcmp(\"Hello world\", &x); }"); ASSERT_EQUALS("", errout.str()); check("int f() { char x = 'x'; return strcmp(\"Hello world\", &x); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str()); check("size_t f(char x) { char * y = &x; return strlen(y); }"); TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); check("size_t f(char x) { char * y = &x; char *z = y; return strlen(z); }"); TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); check("size_t f() { char x = 'x'; char * y = &x; char *z = y; return strlen(z); }"); TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); check("size_t f() { char x = '\\0'; char * y = &x; char *z = y; return strlen(z); }"); ASSERT_EQUALS("", errout.str()); check("size_t f() { char x[] = \"Hello world\"; return strlen(x); }"); ASSERT_EQUALS("", errout.str()); check("size_t f(char x[]) { return strlen(x); }"); ASSERT_EQUALS("", errout.str()); check("int f(char x, char y) { return strcmp(&x, &y); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 1. A nul-terminated string is required.\n" "[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str()); check("size_t f() { char x[] = \"Hello world\"; return strlen(&x[0]); }"); ASSERT_EQUALS("", errout.str()); check("size_t f() { char* x = \"Hello world\"; return strlen(&x[0]); }"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " char x;\n" "};\n" "size_t f() {\n" " S s1 = {0};\n" " S s2;\n;" " s2.x = 'x';\n" " size_t l1 = strlen(&s1.x);\n" " size_t l2 = strlen(&s2.x);\n" " return l1 + l2;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); check("const char x = 'x'; size_t f() { return strlen(&x); }"); TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); check("const char x = 'x'; size_t f() { char y = x; return strlen(&y); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("const char x = '\\0'; size_t f() { return strlen(&x); }"); ASSERT_EQUALS("", errout.str()); check("const char x = '\\0'; size_t f() { char y = x; return strlen(&y); }"); ASSERT_EQUALS("", errout.str()); check("size_t f() {\n" " char * a = \"Hello world\";\n" " char ** b = &a;\n" " return strlen(&b[0][0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("size_t f() {\n" " char ca[] = \"asdf\";\n" " return strlen((char*) &ca);\n" "}"); ASSERT_EQUALS("", errout.str()); // #5225 check("int main(void)\n" "{\n" " char str[80] = \"hello worl\";\n" " char d = 'd';\n" " strcat(str, &d);\n" " puts(str);\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Invalid strcat() argument nr 2. A nul-terminated string is required.\n", errout.str()); } void mathfunctionCall_sqrt() { // sqrt, sqrtf, sqrtl check("void foo()\n" "{\n" " std::cout << sqrt(-1) << std::endl;\n" " std::cout << sqrtf(-1) << std::endl;\n" " std::cout << sqrtl(-1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid sqrt() argument nr 1. The value is -1 but the valid values are '0.0:'.\n" "[test.cpp:4]: (error) Invalid sqrtf() argument nr 1. The value is -1 but the valid values are '0.0:'.\n" "[test.cpp:5]: (error) Invalid sqrtl() argument nr 1. The value is -1 but the valid values are '0.0:'.\n", errout.str()); // implementation-defined behaviour for "finite values of x<0" only: check("void foo()\n" "{\n" " std::cout << sqrt(-0.) << std::endl;\n" " std::cout << sqrtf(-0.) << std::endl;\n" " std::cout << sqrtl(-0.) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << sqrt(1) << std::endl;\n" " std::cout << sqrtf(1) << std::endl;\n" " std::cout << sqrtl(1) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_log() { // log,log10,logf,logl,log10f,log10l,log2,log2f,log2l,log1p,log1pf,log1pl check("void foo()\n" "{\n" " std::cout << log(-2) << std::endl;\n" " std::cout << logf(-2) << std::endl;\n" " std::cout << logl(-2) << std::endl;\n" " std::cout << log10(-2) << std::endl;\n" " std::cout << log10f(-2) << std::endl;\n" " std::cout << log10l(-2) << std::endl;\n" " std::cout << log2(-2) << std::endl;\n" " std::cout << log2f(-2) << std::endl;\n" " std::cout << log2l(-2) << std::endl;\n" " std::cout << log1p(-3) << std::endl;\n" " std::cout << log1pf(-3) << std::endl;\n" " std::cout << log1pl(-3) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -2 but the valid values are '1.4013e-45:'.\n" "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -2 but the valid values are '1.4013e-45:'.\n" "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -2 but the valid values are '1.4013e-45:'.\n" "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" "[test.cpp:3]: (warning) Passing value -2 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -2 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -2 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -2 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -2 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -2 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -2 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -2 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -2 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -3 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -3 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -3 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(-1) << std::endl;\n" " std::cout << logf(-1) << std::endl;\n" " std::cout << logl(-1) << std::endl;\n" " std::cout << log10(-1) << std::endl;\n" " std::cout << log10f(-1) << std::endl;\n" " std::cout << log10l(-1) << std::endl;\n" " std::cout << log2(-1) << std::endl;\n" " std::cout << log2f(-1) << std::endl;\n" " std::cout << log2l(-1) << std::endl;\n" " std::cout << log1p(-2) << std::endl;\n" " std::cout << log1pf(-2) << std::endl;\n" " std::cout << log1pl(-2) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:3]: (warning) Passing value -1 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -1 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -1 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -1 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -1 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -1 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -1 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -1 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -1 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -2 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -2 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -2 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(-1.0) << std::endl;\n" " std::cout << logf(-1.0) << std::endl;\n" " std::cout << logl(-1.0) << std::endl;\n" " std::cout << log10(-1.0) << std::endl;\n" " std::cout << log10f(-1.0) << std::endl;\n" " std::cout << log10l(-1.0) << std::endl;\n" " std::cout << log2(-1.0) << std::endl;\n" " std::cout << log2f(-1.0) << std::endl;\n" " std::cout << log2l(-1.0) << std::endl;\n" " std::cout << log1p(-2.0) << std::endl;\n" " std::cout << log1pf(-2.0) << std::endl;\n" " std::cout << log1pl(-2.0) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:3]: (warning) Passing value -1.0 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -1.0 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -1.0 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -1.0 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -1.0 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -1.0 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -1.0 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -1.0 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -1.0 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -2.0 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -2.0 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -2.0 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(-0.1) << std::endl;\n" " std::cout << logf(-0.1) << std::endl;\n" " std::cout << logl(-0.1) << std::endl;\n" " std::cout << log10(-0.1) << std::endl;\n" " std::cout << log10f(-0.1) << std::endl;\n" " std::cout << log10l(-0.1) << std::endl;\n" " std::cout << log2(-0.1) << std::endl;\n" " std::cout << log2f(-0.1) << std::endl;\n" " std::cout << log2l(-0.1) << std::endl;\n" " std::cout << log1p(-1.1) << std::endl;\n" " std::cout << log1pf(-1.1) << std::endl;\n" " std::cout << log1pl(-1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -0.1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -0.1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -0.1 but the valid values are '1.4013e-45:'.\n" "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" "[test.cpp:3]: (warning) Passing value -0.1 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -0.1 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -0.1 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -0.1 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -0.1 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -0.1 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -0.1 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -0.1 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -0.1 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -1.1 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -1.1 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -1.1 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(0) << std::endl;\n" " std::cout << logf(0.) << std::endl;\n" " std::cout << logl(0.0) << std::endl;\n" " std::cout << log10(0.0) << std::endl;\n" " std::cout << log10f(0) << std::endl;\n" " std::cout << log10l(0.) << std::endl;\n" " std::cout << log2(0.) << std::endl;\n" " std::cout << log2f(0.0) << std::endl;\n" " std::cout << log2l(0) << std::endl;\n" " std::cout << log1p(-1.) << std::endl;\n" " std::cout << log1pf(-1.0) << std::endl;\n" " std::cout << log1pl(-1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is 0 but the valid values are '1.4013e-45:'.\n" "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is 0 but the valid values are '1.4013e-45:'.\n" "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is 0 but the valid values are '1.4013e-45:'.\n" "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" "[test.cpp:3]: (warning) Passing value 0 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value 0. to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value 0.0 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value 0.0 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value 0 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value 0. to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value 0. to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value 0.0 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value 0 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -1. to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -1.0 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -1 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(1E-3) << std::endl;\n" " std::cout << logf(1E-3) << std::endl;\n" " std::cout << logl(1E-3) << std::endl;\n" " std::cout << log10(1E-3) << std::endl;\n" " std::cout << log10f(1E-3) << std::endl;\n" " std::cout << log10l(1E-3) << std::endl;\n" " std::cout << log2(1E-3) << std::endl;\n" " std::cout << log2f(1E-3) << std::endl;\n" " std::cout << log2l(1E-3) << std::endl;\n" " std::cout << log1p(-1+1E-3) << std::endl;\n" " std::cout << log1pf(-1+1E-3) << std::endl;\n" " std::cout << log1pl(-1+1E-3) << std::endl;\n" " std::cout << log(1.0E-3) << std::endl;\n" " std::cout << logf(1.0E-3) << std::endl;\n" " std::cout << logl(1.0E-3) << std::endl;\n" " std::cout << log10(1.0E-3) << std::endl;\n" " std::cout << log10f(1.0E-3) << std::endl;\n" " std::cout << log10l(1.0E-3) << std::endl;\n" " std::cout << log2(1.0E-3) << std::endl;\n" " std::cout << log2f(1.0E-3) << std::endl;\n" " std::cout << log2l(1.0E-3) << std::endl;\n" " std::cout << log1p(-1+1.0E-3) << std::endl;\n" " std::cout << log1pf(-1+1.0E-3) << std::endl;\n" " std::cout << log1pl(-1+1.0E-3) << std::endl;\n" " std::cout << log(1.0E+3) << std::endl;\n" " std::cout << logf(1.0E+3) << std::endl;\n" " std::cout << logl(1.0E+3) << std::endl;\n" " std::cout << log10(1.0E+3) << std::endl;\n" " std::cout << log10f(1.0E+3) << std::endl;\n" " std::cout << log10l(1.0E+3) << std::endl;\n" " std::cout << log2(1.0E+3) << std::endl;\n" " std::cout << log2f(1.0E+3) << std::endl;\n" " std::cout << log2l(1.0E+3) << std::endl;\n" " std::cout << log1p(1.0E+3) << std::endl;\n" " std::cout << log1pf(1.0E+3) << std::endl;\n" " std::cout << log1pl(1.0E+3) << std::endl;\n" " std::cout << log(2.0) << std::endl;\n" " std::cout << logf(2.0) << std::endl;\n" " std::cout << logf(2.0f) << std::endl;\n" " std::cout << log10(2.0) << std::endl;\n" " std::cout << log10f(2.0) << std::endl;\n" " std::cout << log10f(2.0f) << std::endl;\n" " std::cout << log2(2.0) << std::endl;\n" " std::cout << log2f(2.0) << std::endl;\n" " std::cout << log2f(2.0f) << std::endl;\n" " std::cout << log1p(2.0) << std::endl;\n" " std::cout << log1pf(2.0) << std::endl;\n" " std::cout << log1pf(2.0f) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::string *log(0);\n" "}"); ASSERT_EQUALS("", errout.str()); // #3473 - no warning if "log" is a variable check("Fred::Fred() : log(0) { }"); ASSERT_EQUALS("", errout.str()); // #5748 check("void f() { foo.log(0); }"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_acos() { // acos, acosf, acosl check("void foo()\n" "{\n" " return acos(-1) \n" " + acos(0.1) \n" " + acos(0.0001) \n" " + acos(0.01) \n" " + acos(1.0E-1) \n" " + acos(-1.0E-1) \n" " + acos(+1.0E-1) \n" " + acos(0.1E-1) \n" " + acos(+0.1E-1) \n" " + acos(-0.1E-1) \n" " + acosf(-1) \n" " + acosf(0.1) \n" " + acosf(0.0001) \n" " + acosf(0.01) \n" " + acosf(1.0E-1) \n" " + acosf(-1.0E-1) \n" " + acosf(+1.0E-1) \n" " + acosf(0.1E-1) \n" " + acosf(+0.1E-1) \n" " + acosf(-0.1E-1) \n" " + acosl(-1) \n" " + acosl(0.1) \n" " + acosl(0.0001) \n" " + acosl(0.01) \n" " + acosl(1.0E-1) \n" " + acosl(-1.0E-1) \n" " + acosl(+1.0E-1) \n" " + acosl(0.1E-1) \n" " + acosl(+0.1E-1) \n" " + acosl(-0.1E-1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << acos(1.1) << std::endl;\n" " std::cout << acosf(1.1) << std::endl;\n" " std::cout << acosl(1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << acos(-1.1) << std::endl;\n" " std::cout << acosf(-1.1) << std::endl;\n" " std::cout << acosl(-1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); } void mathfunctionCall_asin() { // asin, asinf, asinl check("void foo()\n" "{\n" " return asin(1) \n" " + asin(-1) \n" " + asin(0.1) \n" " + asin(0.0001) \n" " + asin(0.01) \n" " + asin(1.0E-1) \n" " + asin(-1.0E-1) \n" " + asin(+1.0E-1) \n" " + asin(0.1E-1) \n" " + asin(+0.1E-1) \n" " + asin(-0.1E-1) \n" " + asinf(1) \n" " + asinf(-1) \n" " + asinf(0.1) \n" " + asinf(0.0001) \n" " + asinf(0.01) \n" " + asinf(1.0E-1) \n" " + asinf(-1.0E-1) \n" " + asinf(+1.0E-1) \n" " + asinf(0.1E-1) \n" " + asinf(+0.1E-1) \n" " + asinf(-0.1E-1) \n" " + asinl(1) \n" " + asinl(-1) \n" " + asinl(0.1) \n" " + asinl(0.0001) \n" " + asinl(0.01) \n" " + asinl(1.0E-1) \n" " + asinl(-1.0E-1) \n" " + asinl(+1.0E-1) \n" " + asinl(0.1E-1) \n" " + asinl(+0.1E-1) \n" " + asinl(-0.1E-1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << asin(1.1) << std::endl;\n" " std::cout << asinf(1.1) << std::endl;\n" " std::cout << asinl(1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << asin(-1.1) << std::endl;\n" " std::cout << asinf(-1.1) << std::endl;\n" " std::cout << asinl(-1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); } void mathfunctionCall_pow() { // pow, powf, powl check("void foo()\n" "{\n" " std::cout << pow(0,-10) << std::endl;\n" " std::cout << powf(0,-10) << std::endl;\n" " std::cout << powl(0,-10) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and -10 to pow() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing values 0 and -10 to powf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing values 0 and -10 to powl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << pow(0,10) << std::endl;\n" " std::cout << powf(0,10) << std::endl;\n" " std::cout << powl(0,10) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_atan2() { // atan2 check("void foo()\n" "{\n" " std::cout << atan2(1,1) ;\n" " std::cout << atan2(-1,-1) ;\n" " std::cout << atan2(0.1,1) ;\n" " std::cout << atan2(0.0001,100) ;\n" " std::cout << atan2(0.0,1e-1) ;\n" " std::cout << atan2(1.0E-1,-3) ;\n" " std::cout << atan2(-1.0E-1,+2) ;\n" " std::cout << atan2(+1.0E-1,0) ;\n" " std::cout << atan2(0.1E-1,3) ;\n" " std::cout << atan2(+0.1E-1,1) ;\n" " std::cout << atan2(-0.1E-1,8) ;\n" " std::cout << atan2f(1,1) ;\n" " std::cout << atan2f(-1,-1) ;\n" " std::cout << atan2f(0.1,1) ;\n" " std::cout << atan2f(0.0001,100) ;\n" " std::cout << atan2f(0.0,1e-1) ;\n" " std::cout << atan2f(1.0E-1,-3) ;\n" " std::cout << atan2f(-1.0E-1,+2) ;\n" " std::cout << atan2f(+1.0E-1,0) ;\n" " std::cout << atan2f(0.1E-1,3) ;\n" " std::cout << atan2f(+0.1E-1,1) ;\n" " std::cout << atan2f(-0.1E-1,8) ;\n" " std::cout << atan2l(1,1) ;\n" " std::cout << atan2l(-1,-1) ;\n" " std::cout << atan2l(0.1,1) ;\n" " std::cout << atan2l(0.0001,100) ;\n" " std::cout << atan2l(0.0,1e-1) ;\n" " std::cout << atan2l(1.0E-1,-3) ;\n" " std::cout << atan2l(-1.0E-1,+2) ;\n" " std::cout << atan2l(+1.0E-1,0) ;\n" " std::cout << atan2l(0.1E-1,3) ;\n" " std::cout << atan2l(+0.1E-1,1) ;\n" " std::cout << atan2l(-0.1E-1,8) ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << atan2(0,0) << std::endl;\n" " std::cout << atan2f(0,0) << std::endl;\n" " std::cout << atan2l(0,0) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and 0 to atan2() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing values 0 and 0 to atan2f() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing values 0 and 0 to atan2l() leads to implementation-defined result.\n", errout.str()); } void mathfunctionCall_fmod() { // fmod, fmodl, fmodf check("void foo()\n" "{\n" " std::cout << fmod(1.0,0) << std::endl;\n" " std::cout << fmodf(1.0,0) << std::endl;\n" " std::cout << fmodl(1.0,0) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 1.0 and 0 to fmod() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing values 1.0 and 0 to fmodf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing values 1.0 and 0 to fmodl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << fmod(1.0,1) << std::endl;\n" " std::cout << fmodf(1.0,1) << std::endl;\n" " std::cout << fmodl(1.0,1) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_precision() { check("void foo() {\n" " print(exp(x) - 1);\n" " print(log(1 + x));\n" " print(1 - erf(x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str()); check("void foo() {\n" " print(exp(x) - 1.0);\n" " print(log(1.0 + x));\n" " print(1.0 - erf(x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str()); check("void foo() {\n" " print(exp(3 + x*f(a)) - 1);\n" " print(log(x*4 + 1));\n" " print(1 - erf(34*x + f(x) - c));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str()); check("void foo() {\n" " print(2*exp(x) - 1);\n" " print(1 - erf(x)/2.0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkIgnoredReturnValue() { Settings settings2; settings2.severity.enable(Severity::warning); const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings2.library.load(doc); check("void foo() {\n" " mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout.str()); check("void foo() {\n" " foo::mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function foo::mystrcmp() is not used.\n", errout.str()); check("void f() {\n" " foo x;\n" " x.mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function x.mystrcmp() is not used.\n", errout.str()); check("bool mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition, but it returns a value. Assume it is the one specified in the library. "void foo() {\n" " mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function mystrcmp() is not used.\n", errout.str()); check("void mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition which returns void! "void foo() {\n" " mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " class mystrcmp { mystrcmp() {} };\n" // strcmp is a constructor definition here "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " return mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " return foo::mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if(mystrcmp(a, b));\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " bool b = mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #6194 check("void foo() {\n" " MyStrCmp mystrcmp(x, y);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #6197 check("void foo() {\n" " abc::def.mystrcmp(a,b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #6233 check("int main() {\n" " auto lambda = [](double value) {\n" " double rounded = floor(value + 0.5);\n" " printf(\"Rounded value = %f\\n\", rounded);\n" " };\n" " lambda(13.3);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6669 check("void foo(size_t size) {\n" " void * res{malloc(size)};\n" "}"); ASSERT_EQUALS("", errout.str()); // #7447 check("void foo() {\n" " int x{mystrcmp(a,b)};\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #7905 check("void foo() {\n" " int x({mystrcmp(a,b)});\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // don't crash " DEBUG(123)(mystrcmp(a,b))(fd);\n" "}", "test.c", &settings2); check("struct teststruct {\n" " int testfunc1() __attribute__ ((warn_unused_result)) { return 1; }\n" " [[nodiscard]] int testfunc2() { return 1; }\n" " void foo() { testfunc1(); testfunc2(); }\n" "};\n" "int main() {\n" " teststruct TestStruct1;\n" " TestStruct1.testfunc1();\n" " TestStruct1.testfunc2();\n" " return 0;\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:4]: (warning) Return value of function testfunc1() is not used.\n" "[test.cpp:4]: (warning) Return value of function testfunc2() is not used.\n" "[test.cpp:8]: (warning) Return value of function TestStruct1.testfunc1() is not used.\n" "[test.cpp:9]: (warning) Return value of function TestStruct1.testfunc2() is not used.\n", errout.str()); // #9006 check("template uint8_t b(std::tuple d) {\n" " std::tuple c{std::move(d)};\n" " return std::get<0>(c);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int x; };\n" "template \n" "A f(int x, Ts... xs) {\n" " return {std::move(x), static_cast(xs)...};\n" "}\n" "A g() { return f(1); }"); ASSERT_EQUALS("", errout.str()); // #8412 - unused operator result check("void foo() {\n" " !mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout.str()); } void checkIgnoredErrorCode() { Settings settings2; settings2.addEnabled("style"); const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings2.library.load(doc); check("void foo() {\n" " mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:2]: (style) Error code from the return value of function mystrcmp() is not used.\n", errout.str()); } void memsetZeroBytes() { check("void f() {\n" " memset(p, 10, 0x0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout.str()); check("void f() {\n" " memset(p, sizeof(p), 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout.str()); check("void f() {\n" " memset(p, sizeof(p), i);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6269 false positives in case of overloaded standard library functions check("class c {\n" " void memset( int i );\n" " void f( void ) {\n" " memset( 0 );\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // #7285 check("void f() {\n" " memset(&tm, sizeof(tm), 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout.str()); } void memsetInvalid2ndParam() { check("void f() {\n" " int* is = new int[10];\n" " memset(is, 1.0f, 40);\n" " int* is2 = new int[10];\n" " memset(is2, 0.1f, 40);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) The 2nd memset() argument '1.0f' is a float, its representation is implementation defined.\n" "[test.cpp:5]: (portability) The 2nd memset() argument '0.1f' is a float, its representation is implementation defined.\n", errout.str()); check("void f() {\n" " int* is = new int[10];\n" " float g = computeG();\n" " memset(is, g, 40);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) The 2nd memset() argument 'g' is a float, its representation is implementation defined.\n", errout.str()); check("void f() {\n" " int* is = new int[10];\n" " memset(is, 0.0f, 40);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // FP " float x = 2.3f;\n" " memset(a, (x?64:0), 40);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " short ss[] = {1, 2};\n" " memset(ss, 256, 4);\n" " short ss2[2];\n" " memset(ss2, -129, 4);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) The 2nd memset() argument '256' doesn't fit into an 'unsigned char'.\n" "[test.cpp:5]: (warning) The 2nd memset() argument '-129' doesn't fit into an 'unsigned char'.\n", errout.str()); check("void f() {\n" " int is[10];\n" " memset(is, 0xEE, 40);\n" " unsigned char* cs = malloc(256);\n" " memset(cs, -1, 256);\n" " short* ss[30];\n" " memset(ss, -128, 60);\n" " char cs2[30];\n" " memset(cs2, 255, 30);\n" " char cs3[30];\n" " memset(cs3, 0, 30);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int is[10];\n" " const int i = g();\n" " memset(is, 1.0f + i, 40);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) The 2nd memset() argument '1.0f+i' is a float, its representation is implementation defined.\n", errout.str()); } void checkMissingReturn() { check("int f() {}"); ASSERT_EQUALS("[test.cpp:1]: (error) Found a exit path from function with non-void return type that has missing return statement\n", errout.str()); { const char code[] = "int main(void) {}"; Settings s; s.standards.c = Standards::C89; check(code, "test.c", &s); // c code (c89) ASSERT_EQUALS("[test.c:1]: (error) Found a exit path from function with non-void return type that has missing return statement\n", errout.str()); s.standards.c = Standards::C99; check(code, "test.c", &s); // c code (c99) ASSERT_EQUALS("", errout.str()); check(code, "test.cpp", &s); // c++ code ASSERT_EQUALS("", errout.str()); } check("F(A,B) { x=1; }"); ASSERT_EQUALS("", errout.str()); check("auto foo4() -> void {}"); ASSERT_EQUALS("", errout.str()); check("void STDCALL foo() {}"); ASSERT_EQUALS("", errout.str()); check("void operator=(int y) { x=y; }"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" "back:\n" " return 0;\n" "ng:\n" " x=y;\n" " goto back;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // unreachable code.. check("int foo(int x) {\n" " return 1;\n" " (void)x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(int x) {\n" " if (x) goto out;\n" " return 1;\n" "out:\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found a exit path from function with non-void return type that has missing return statement\n", errout.str()); // switch check("int f() {\n" " switch (x) {\n" " case 1: break;\n" // <- error " case 2: return 1;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found a exit path from function with non-void return type that has missing return statement\n", errout.str()); check("int f() {\n" " switch (x) {\n" " case 1: return 2; break;\n" " default: return 1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool test(unsigned char v1, int v2) {\n" " switch (v1) {\n" " case 0:\n" " switch (v2) {\n" " case 48000:\n" " break;\n" " }\n" " return false;\n" " default:\n" " return true;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // if/else check("int f(int x) {\n" " if (x) {\n" " return 1;\n" " }\n" // <- error (missing else) "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Found a exit path from function with non-void return type that has missing return statement\n", errout.str()); check("int f(int x) {\n" " if (x) {\n" " ;\n" // <- error " } else {\n" " return 1;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found a exit path from function with non-void return type that has missing return statement\n", errout.str()); check("int f() {\n" " if (!0) {\n" " return 1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " if (!0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Found a exit path from function with non-void return type that has missing return statement\n", errout.str()); // loop check("int f(int x) {\n" " while (1) {\n" " dostuff();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // return {..} check("std::pair typeDecl(int tok) {\n" " if (!tok)\n" " return {};\n" " else\n" " return {1, 2};\n" "}"); ASSERT_EQUALS("", errout.str()); // noreturn function check("int f(int x) { exit(0); }"); ASSERT_EQUALS("", errout.str()); check("int f(int x) { assert(0); }"); ASSERT_EQUALS("", errout.str()); check("int f(int x) { if (x) return 1; else return bar({1}, {}); }"); ASSERT_EQUALS("", errout.str()); check("auto f() -> void {}"); // #10342 ASSERT_EQUALS("", errout.str()); } // NRVO check void returnLocalStdMove1() { check("struct A{}; A f() { A var; return std::move(var); }"); ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str()); } // RVO, C++03 ctor style void returnLocalStdMove2() { check("struct A{}; A f() { return std::move( A() ); }"); ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str()); } // RVO, new ctor style void returnLocalStdMove3() { check("struct A{}; A f() { return std::move(A{}); }"); ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str()); } // Function argument void returnLocalStdMove4() { check("struct A{}; A f(A a) { return std::move(A{}); }"); ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout.str()); } void returnLocalStdMove5() { check("struct A{} a; A f1() { return std::move(a); }\n" "A f2() { volatile A var; return std::move(var); }"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestFunctions) cppcheck-2.7/test/testgarbage.cpp000066400000000000000000002162411417746362400171410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check.h" #include "config.h" #include "errortypes.h" #include "mathlib.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include #include #include class TestGarbage : public TestFixture { public: TestGarbage() : TestFixture("TestGarbage") {} private: Settings settings; void run() OVERRIDE { settings.debugwarnings = true; settings.severity.fill(); settings.certainty.fill(); // don't freak out when the syntax is wrong TEST_CASE(final_class_x); TEST_CASE(wrong_syntax1); TEST_CASE(wrong_syntax2); TEST_CASE(wrong_syntax3); // #3544 TEST_CASE(wrong_syntax4); // #3618 TEST_CASE(wrong_syntax_if_macro); // #2518 - if MACRO() TEST_CASE(wrong_syntax_class_x_y); // #3585 - class x y { }; TEST_CASE(wrong_syntax_anonymous_struct); TEST_CASE(syntax_case_default); TEST_CASE(garbageCode1); TEST_CASE(garbageCode2); // #4300 TEST_CASE(garbageCode3); // #4869 TEST_CASE(garbageCode4); // #4887 TEST_CASE(garbageCode5); // #5168 TEST_CASE(garbageCode6); // #5214 TEST_CASE(garbageCode7); TEST_CASE(garbageCode8); // #5511 TEST_CASE(garbageCode9); // #5604 TEST_CASE(garbageCode10); // #6127 TEST_CASE(garbageCode12); TEST_CASE(garbageCode13); // #2607 TEST_CASE(garbageCode15); // #5203 TEST_CASE(garbageCode16); TEST_CASE(garbageCode17); TEST_CASE(garbageCode18); TEST_CASE(garbageCode20); TEST_CASE(garbageCode21); TEST_CASE(garbageCode22); TEST_CASE(garbageCode23); TEST_CASE(garbageCode24); // #6361 TEST_CASE(garbageCode25); TEST_CASE(garbageCode26); TEST_CASE(garbageCode27); TEST_CASE(garbageCode28); TEST_CASE(garbageCode30); // #5867 TEST_CASE(garbageCode31); // #6539 TEST_CASE(garbageCode33); // #6613 TEST_CASE(garbageCode34); // #6626 TEST_CASE(garbageCode35); // #2604 TEST_CASE(garbageCode36); // #6334 TEST_CASE(garbageCode37); // #5166 TEST_CASE(garbageCode38); // #6666 TEST_CASE(garbageCode40); // #6620 TEST_CASE(garbageCode41); // #6685 TEST_CASE(garbageCode42); // #5760 TEST_CASE(garbageCode43); // #6703 TEST_CASE(garbageCode44); // #6704 TEST_CASE(garbageCode45); // #6608 TEST_CASE(garbageCode46); // #6705 TEST_CASE(garbageCode47); // #6706 TEST_CASE(garbageCode48); // #6712 TEST_CASE(garbageCode49); // #6715 TEST_CASE(garbageCode51); // #6719 TEST_CASE(garbageCode53); // #6721 TEST_CASE(garbageCode54); // #6722 TEST_CASE(garbageCode55); // #6724 TEST_CASE(garbageCode56); // #6713 TEST_CASE(garbageCode57); // #6733 TEST_CASE(garbageCode58); // #6732 TEST_CASE(garbageCode59); // #6735 TEST_CASE(garbageCode60); // #6736 TEST_CASE(garbageCode61); TEST_CASE(garbageCode63); TEST_CASE(garbageCode64); TEST_CASE(garbageCode65); TEST_CASE(garbageCode66); TEST_CASE(garbageCode68); TEST_CASE(garbageCode69); TEST_CASE(garbageCode70); TEST_CASE(garbageCode71); TEST_CASE(garbageCode72); TEST_CASE(garbageCode73); TEST_CASE(garbageCode74); TEST_CASE(garbageCode76); TEST_CASE(garbageCode77); TEST_CASE(garbageCode78); TEST_CASE(garbageCode79); TEST_CASE(garbageCode80); TEST_CASE(garbageCode81); TEST_CASE(garbageCode82); TEST_CASE(garbageCode83); TEST_CASE(garbageCode84); TEST_CASE(garbageCode85); TEST_CASE(garbageCode86); TEST_CASE(garbageCode87); TEST_CASE(garbageCode88); TEST_CASE(garbageCode90); TEST_CASE(garbageCode91); TEST_CASE(garbageCode92); TEST_CASE(garbageCode94); TEST_CASE(garbageCode95); TEST_CASE(garbageCode96); TEST_CASE(garbageCode97); TEST_CASE(garbageCode98); TEST_CASE(garbageCode99); TEST_CASE(garbageCode100); TEST_CASE(garbageCode101); // #6835 TEST_CASE(garbageCode102); // #6846 TEST_CASE(garbageCode103); // #6824 TEST_CASE(garbageCode104); // #6847 TEST_CASE(garbageCode105); // #6859 TEST_CASE(garbageCode106); TEST_CASE(garbageCode107); TEST_CASE(garbageCode108); TEST_CASE(garbageCode109); TEST_CASE(garbageCode110); TEST_CASE(garbageCode111); TEST_CASE(garbageCode112); TEST_CASE(garbageCode114); // #2118 TEST_CASE(garbageCode115); // #5506 TEST_CASE(garbageCode116); // #5356 TEST_CASE(garbageCode117); // #6121 TEST_CASE(garbageCode118); // #5600 TEST_CASE(garbageCode119); // #5598 TEST_CASE(garbageCode120); // #4927 TEST_CASE(garbageCode121); // #2585 TEST_CASE(garbageCode122); // #6303 TEST_CASE(garbageCode123); TEST_CASE(garbageCode125); // 6782, 6834 TEST_CASE(garbageCode126); // #6997 TEST_CASE(garbageCode127); // #6667 TEST_CASE(garbageCode128); // #7018 TEST_CASE(garbageCode129); // #7020 TEST_CASE(garbageCode130); // #7021 TEST_CASE(garbageCode131); // #7023 TEST_CASE(garbageCode132); // #7022 TEST_CASE(garbageCode133); TEST_CASE(garbageCode134); TEST_CASE(garbageCode135); // #4994 TEST_CASE(garbageCode136); // #7033 TEST_CASE(garbageCode137); // #7034 TEST_CASE(garbageCode138); // #6660 TEST_CASE(garbageCode139); // #6659 TEST_CASE(garbageCode140); // #7035 TEST_CASE(garbageCode141); // #7043 TEST_CASE(garbageCode142); // #7050 TEST_CASE(garbageCode143); // #6922 TEST_CASE(garbageCode144); // #6865 TEST_CASE(garbageCode146); // #7081 TEST_CASE(garbageCode147); // #7082 TEST_CASE(garbageCode148); // #7090 TEST_CASE(garbageCode149); // #7085 TEST_CASE(garbageCode150); // #7089 TEST_CASE(garbageCode151); // #4911 TEST_CASE(garbageCode152); // travis after 9c7271a5 TEST_CASE(garbageCode153); TEST_CASE(garbageCode154); // #7112 TEST_CASE(garbageCode156); // #7120 TEST_CASE(garbageCode157); // #7131 TEST_CASE(garbageCode158); // #3238 TEST_CASE(garbageCode159); // #7119 TEST_CASE(garbageCode160); // #7190 TEST_CASE(garbageCode161); // #7200 TEST_CASE(garbageCode162); // #7208 TEST_CASE(garbageCode163); // #7228 TEST_CASE(garbageCode164); // #7234 TEST_CASE(garbageCode165); // #7235 TEST_CASE(garbageCode167); // #7237 TEST_CASE(garbageCode168); // #7246 TEST_CASE(garbageCode169); // #6731 TEST_CASE(garbageCode170); TEST_CASE(garbageCode171); TEST_CASE(garbageCode172); TEST_CASE(garbageCode173); // #6781 TEST_CASE(garbageCode174); // #7356 TEST_CASE(garbageCode175); TEST_CASE(garbageCode176); // #7527 TEST_CASE(garbageCode181); TEST_CASE(garbageCode182); // #4195 TEST_CASE(garbageCode183); // #7505 TEST_CASE(garbageCode184); // #7699 TEST_CASE(garbageCode185); // #6011 TEST_CASE(garbageCode186); // #8151 TEST_CASE(garbageCode187); TEST_CASE(garbageCode188); TEST_CASE(garbageCode189); // #8317 TEST_CASE(garbageCode190); // #8307 TEST_CASE(garbageCode191); // #8333 TEST_CASE(garbageCode192); // #8386 (segmentation fault) TEST_CASE(garbageCode193); // #8740 TEST_CASE(garbageCode194); // #8384 TEST_CASE(garbageCode195); // #8709 TEST_CASE(garbageCode196); // #8265 TEST_CASE(garbageCode197); // #8385 TEST_CASE(garbageCode198); // #8383 TEST_CASE(garbageCode199); // #8752 TEST_CASE(garbageCode200); // #8757 TEST_CASE(garbageCode201); // #8873 TEST_CASE(garbageCode202); // #8907 TEST_CASE(garbageCode203); // #8972 TEST_CASE(garbageCode204); TEST_CASE(garbageCode205); TEST_CASE(garbageCode206); TEST_CASE(garbageCode207); // #8750 TEST_CASE(garbageCode208); // #8753 TEST_CASE(garbageCode209); // #8756 TEST_CASE(garbageCode210); // #8762 TEST_CASE(garbageCode211); // #8764 TEST_CASE(garbageCode212); // #8765 TEST_CASE(garbageCode213); // #8758 TEST_CASE(garbageCode214); TEST_CASE(garbageCode215); // daca@home script with extension .c TEST_CASE(garbageCode216); // #7884 TEST_CASE(garbageCode217); // #10011 TEST_CASE(garbageCode218); // #8763 TEST_CASE(garbageCode219); // #10101 TEST_CASE(garbageCodeFuzzerClientMode1); // test cases created with the fuzzer client, mode 1 TEST_CASE(garbageValueFlow); TEST_CASE(garbageSymbolDatabase); TEST_CASE(garbageAST); TEST_CASE(templateSimplifierCrashes); TEST_CASE(syntaxErrorFirstToken); // Make sure syntax errors are detected and reported TEST_CASE(syntaxErrorLastToken); // Make sure syntax errors are detected and reported TEST_CASE(syntaxErrorCase); TEST_CASE(syntaxErrorFuzzerCliType1); TEST_CASE(cliCode); TEST_CASE(enumTrailingComma); TEST_CASE(nonGarbageCode1); // #8346 } #define checkCodeInternal(code, filename) checkCodeInternal_(code, filename, __FILE__, __LINE__) std::string checkCode(const std::string &code, bool cpp = true) { // double the tests - run each example as C as well as C++ const char* const filename = cpp ? "test.cpp" : "test.c"; const char* const alternatefilename = cpp ? "test.c" : "test.cpp"; // run alternate check first. It should only ensure stability - so we catch exceptions here. try { checkCodeInternal(code, alternatefilename); } catch (const InternalError&) {} return checkCodeInternal(code, filename); } std::string checkCodeInternal_(const std::string &code, const char* filename, const char* file, int line) { errout.str(""); // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); // call all "runChecks" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { (*it)->runChecks(&tokenizer, &settings, this); } return tokenizer.tokens()->stringifyList(false, false, false, true, false, nullptr, nullptr); } #define getSyntaxError(code) getSyntaxError_(code, __FILE__, __LINE__) std::string getSyntaxError_(const char code[], const char* file, int line) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); try { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); } catch (InternalError& e) { if (e.id != "syntaxError") return ""; return "[test.cpp:" + MathLib::toString(e.token->linenr()) + "] " + e.errorMessage; } return ""; } void final_class_x() { const char code[] = "class __declspec(dllexport) x final { };"; { errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT(tokenizer.tokenize(istr, "test.cpp")); ASSERT_EQUALS("", errout.str()); } } void wrong_syntax1() { { const char code[] ="TR(kvmpio, PROTO(int rw), ARGS(rw), TP_(aa->rw;))"; ASSERT_THROW(checkCode(code), InternalError); ASSERT_EQUALS("", errout.str()); } { const char code[] ="struct A { template struct { }; };"; ASSERT_THROW(checkCode(code), InternalError); } { const char code[] ="enum ABC { A,B, typedef enum { C } };"; ASSERT_THROW(checkCode(code), InternalError); } } void wrong_syntax2() { // #3504 const char code[] = "void f() {\n" " X x;\n" " Y y;\n" "}\n" "\n" "void G( template class (j) ) {}"; // don't segfault.. ASSERT_THROW(checkCode(code), InternalError); } void wrong_syntax3() { // #3544 const char code[] = "X #define\n" "{\n" " (\n" " for( #endif typedef typedef cb[N] )\n" " ca[N]; = cb[i]\n" " )\n" "}"; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); try { ASSERT(tokenizer.tokenize(istr, "test.cpp")); assertThrowFail(__FILE__, __LINE__); } catch (InternalError& e) { ASSERT_EQUALS("syntax error", e.errorMessage); ASSERT_EQUALS("syntaxError", e.id); ASSERT_EQUALS(4, e.token->linenr()); } } void wrong_syntax4() { // #3618 const char code[] = "typedef void (x) (int); return x&"; ASSERT_THROW(checkCode(code), InternalError); } void wrong_syntax_if_macro() { // #2518 #4171 ASSERT_THROW(checkCode("void f() { if MACRO(); }"), InternalError); // #4668 - note there is no semicolon after MACRO() ASSERT_THROW(checkCode("void f() { if (x) MACRO() {} }"), InternalError); // #4810 - note there is no semicolon after MACRO() ASSERT_THROW(checkCode("void f() { if (x) MACRO() else ; }"), InternalError); } void wrong_syntax_class_x_y() { // #3585 const char code[] = "class x y { };"; { errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT(tokenizer.tokenize(istr, "test.c")); ASSERT_EQUALS("", errout.str()); } { errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT(tokenizer.tokenize(istr, "test.cpp")); ASSERT_EQUALS("[test.cpp:1]: (information) The code 'class x y {' is not handled. You can use -I or --include to add handling of this code.\n", errout.str()); } } void wrong_syntax_anonymous_struct() { ASSERT_THROW(checkCode("struct { int x; } = {0};"), InternalError); ASSERT_THROW(checkCode("struct { int x; } * = {0};"), InternalError); } void syntax_case_default() { ASSERT_THROW(checkCode("void f() {switch (n) { case: z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case;: z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case {}: z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case 0?{1}:{2} : z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case 0?1;:{2} : z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case 0?(1?{3:4}):2 : z(); break;}}"), InternalError); //ticket #4234 ASSERT_THROW(checkCode("( ) { switch break ; { switch ( x ) { case } y break ; : } }"), InternalError); //ticket #4267 ASSERT_THROW(checkCode("f ( ) { switch break; { switch ( x ) { case } case break; -6: ( ) ; } }"), InternalError); // Missing semicolon ASSERT_THROW(checkCode("void foo () { switch(0) case 0 : default : }"), InternalError); } void garbageCode1() { checkCode("struct x foo_t; foo_t typedef y;"); } void garbageCode2() { //#4300 (segmentation fault) TODO_ASSERT_THROW(checkCode("enum { D = 1 struct { } ; } s.b = D;"), InternalError); } void garbageCode3() { //#4849 (segmentation fault in Tokenizer::simplifyStructDecl (invalid code)) TODO_ASSERT_THROW(checkCode("enum { D = 2 s ; struct y { x } ; } { s.a = C ; s.b = D ; }"), InternalError); } void garbageCode4() { // #4887 ASSERT_THROW(checkCode("void f ( ) { = a ; if ( 1 ) if = ( 0 ) ; }"), InternalError); } void garbageCode5() { // #5168 checkCode("( asm : ; void : );"); } void garbageCode6() { // #5214 ASSERT_THROW(checkCode("int b = ( 0 ? ? ) 1 : 0 ;"), InternalError); ASSERT_THROW(checkCode("int a = int b = ( 0 ? ? ) 1 : 0 ;"), InternalError); } void garbageCode7() { ASSERT_THROW(checkCode("1 (int j) { return return (c) * sizeof } y[1];"), InternalError); ASSERT_THROW(checkCode("foo(Args&&...) fn void = { } auto template { { e = T::error }; };\n" "ScopedEnum1 se1; { enum class E : T { e = 0 = e ScopedEnum2 struct UnscopedEnum3 { T{ e = 4 }; };\n" "arr[(int) E::e]; }; UnscopedEnum3 e2 = f()\n" "{ { e = e1; T::error } int test1 ue2; g() { enum class E { e = T::error }; return E::e; } int test2 = }\n" "namespace UnscopedEnum { template struct UnscopedEnum1 { E{ e = T::error }; }; UnscopedEnum1 { enum E : { e = 0 }; };\n" "UnscopedEnum2 ue3; template struct UnscopedEnum3 { enum { }; }; int arr[E::e]; };\n" "UnscopedEnum3 namespace template int f() { enum E { e }; T::error }; return (int) E(); } int test1 int g() { enum E { e = E };\n" "E::e; } int test2 = g(); }"), InternalError); } void garbageCode9() { TODO_ASSERT_THROW(checkCode("enum { e = { } } ( ) { { enum { } } } { e } "), InternalError); } void garbageCode10() { // #6127 ASSERT_THROW(checkCode("for( rl=reslist; rl!=NULL; rl=rl->next )"), InternalError); } void garbageCode12() { // do not crash checkCode("{ g; S (void) { struct } { } int &g; }"); } void garbageCode13() { checkCode("struct C {} {} x"); } void garbageCode15() { // Ticket #5203 ASSERT_THROW(checkCode("int f ( int* r ) { { int s[2] ; f ( s ) ; if ( ) } }"), InternalError); } void garbageCode16() { checkCode("{ } A() { delete }"); // #6080 } void garbageCode17() { ASSERT_THROW(checkCode("void h(int l) {\n" " while\n" // Don't crash (#3870) "}"), InternalError); } void garbageCode18() { ASSERT_THROW(checkCode("switch(){case}"), InternalError); } void garbageCode20() { // #3953 (valgrind errors on garbage code) ASSERT_EQUALS("void f ( 0 * ) ;", checkCode("void f ( 0 * ) ;")); } void garbageCode21() { // Ticket #3486 - Don't crash garbage code ASSERT_THROW(checkCode("void f()\n" "{\n" " (\n" " x;\n" " int a, a2, a2*x; if () ;\n" " )\n" "}"), InternalError); } void garbageCode22() { // Ticket #3480 - Don't crash garbage code ASSERT_THROW(checkCode("int f()\n" "{\n" " return if\n" "}"), InternalError); } void garbageCode23() { //garbage code : don't crash (#3481) checkCode("{\n" " if (1) = x\n" " else abort s[2]\n" "}"); ASSERT_EQUALS("", errout.str()); } void garbageCode24() { // don't crash (example from #6361) ASSERT_THROW(checkCode("float buffer[64];\n" "main (void)\n" "{\n" " char *cptr;\n" " cptr = (char *)buffer;\n" " cptr += (-(long int) buffer & (16 * sizeof (float) - 1));\n" "}\n"), InternalError); } void garbageCode25() { // Ticket #2386 - Segmentation fault upon strange syntax ASSERT_THROW(checkCode("void f() {\n" " switch ( x ) {\n" " case struct Tree : break;\n" " }\n" "}"), InternalError); } void garbageCode26() { // See tickets #2518 #2555 #4171 ASSERT_THROW(checkCode("void f() {\n" " switch MAKEWORD(1)\n" " {\n" " case 0:\n" " return;\n" " }\n" "}"), InternalError); } void garbageCode27() { ASSERT_THROW(checkCode("int f() {\n" " return if\n" "}"), InternalError); } void garbageCode28() { // 5702 checkCode("struct R1 {\n" " int a;\n" " R1 () : a { }\n" "};"); } void garbageCode30() { // simply survive - a syntax error would be even better (#5867) checkCode("void f(int x) {\n" " x = 42\n" "}"); } void garbageCode31() { ASSERT_THROW(checkCode("typedef struct{}x[([],)]typedef e y;(y,x 0){}"), InternalError); } void garbageCode33() { // #6613 checkCode("main(()B{});"); } // Bug #6626 crash: Token::astOperand2() const ( do while ) void garbageCode34() { const char code[] = "void foo(void) {\n" " do\n" " while (0);\n" "}"; ASSERT_THROW(checkCode(code), InternalError); } void garbageCode35() { // ticket #2604 segmentation fault ASSERT_THROW(checkCode("sizeof <= A"), InternalError); } void garbageCode36() { // #6334 ASSERT_THROW(checkCode("{ } < class template < > , { = } ; class... >\n" "struct Y { }\n" "class Types { }\n" "( X < int > \"uses template\" ) ( < ( ) \"uses ;" "( int int ::primary \"uses template\" ) int double \"uses )" "::primary , \"uses template\" ;\n"), InternalError); } void garbageCode37() { // #5166 segmentation fault (invalid code) in lib/checkother.cpp:329 ( void * f { } void b ( ) { * f } ) checkCode("void * f { } void b ( ) { * f }"); } void garbageCode38() { // Ticket #6666 checkCode("{ f2 { } } void f3 () { delete[] } { }"); } void garbageCode40() { // #6620 checkCode("{ ( ) () { virtual } ; { } E } A { : { } ( ) } * const ( ) const { }"); // test doesn't seem to work on any platform: ASSERT_THROW(checkCode("{ ( ) () { virtual } ; { } E } A { : { } ( ) } * const ( ) const { }", "test.c"), InternalError); } void garbageCode41() { // #6685 checkCode(" { } { return } *malloc(__SIZE_TYPE__ size); *memcpy(void n); static * const () { memcpy (*slot, 3); } { (); } { }"); } void garbageCode42() { // #5760 checkCode("{ } * const ( ) { }"); } void garbageCode43() { // #6703 ASSERT_THROW(checkCode("int { }; struct A a = { }"), InternalError); } void garbageCode44() { // #6704 ASSERT_THROW(checkCode("{ { }; }; { class A : }; public typedef b;"), InternalError); } void garbageCode45() { // #6608 ASSERT_THROW(checkCode("struct true template < > { = } > struct Types \"s\" ; static_assert < int > ;"), InternalError); } void garbageCode46() { // #6705 checkCode(" { bar(char *x); void foo (int ...) { struct } va_list ap; va_start(ap, size); va_arg(ap, (d)); }"); } void garbageCode47() { // #6706 checkCode(" { { }; }; * new private: B: B;"); } void garbageCode48() { // #6712 checkCode(" { d\" ) d ...\" } int main ( ) { ( ) catch ( A a ) { { } catch ( ) \"\" } }"); } void garbageCode49() { // #6715 ASSERT_THROW(checkCode(" ( ( ) ) { } ( { ( __builtin_va_arg_pack ( ) ) ; } ) { ( int { ( ) ( ( ) ) } ( ) { } ( ) ) += ( ) }"), InternalError); } void garbageCode51() { // #6719 ASSERT_THROW(checkCode(" (const \"C\" ...); struct base { int f2; base (int arg1, int arg2); }; global_base(0x55, 0xff); { ((global_base.f1 0x55) (global_base.f2 0xff)) { } } base::base(int arg1, int arg2) { f2 = }"), InternalError); } void garbageCode53() { // #6721 ASSERT_THROW(checkCode("{ { } }; void foo (struct int i) { x->b[i] = = }"), InternalError); } void garbageCode54() { // #6722 ASSERT_THROW(checkCode("{ typedef long ((pf) p) (); }"), InternalError); } void garbageCode55() { // #6724 ASSERT_THROW(checkCode("() __attribute__((constructor)); { } { }"), InternalError); } void garbageCode56() { // #6713 ASSERT_THROW(checkCode("void foo() { int a = 0; int b = ???; }"), InternalError); } void garbageCode57() { // #6731 ASSERT_THROW(checkCode("{ } if () try { } catch (...) B::~B { }"), InternalError); } void garbageCode58() { // #6732, #6762 ASSERT_THROW(checkCode("{ }> {= ~A()^{} }P { }"), InternalError); ASSERT_THROW(checkCode("{= ~A()^{} }P { } { }> is"), InternalError); } void garbageCode59() { // #6735 ASSERT_THROW(checkCode("{ { } }; char font8x8[256][8]"), InternalError); } void garbageCode60() { // #6736 ASSERT_THROW(checkCode("{ } { } typedef int int_array[]; int_array &right ="), InternalError); } void garbageCode61() { // #6737 ASSERT_THROW(checkCode("{ (const U&) }; { }; { }; struct U : virtual public"), InternalError); } void garbageCode63() { // #6739 ASSERT_THROW(checkCode("{ } { } typedef int u_array[]; typedef u_array &u_array_ref; (u_array_ref arg) { } u_array_ref u_array_ref_gbl_obj0"), InternalError); } void garbageCode64() { // #6740 ASSERT_THROW(checkCode("{ } foo(void (*bar)(void))"), InternalError); } void garbageCode65() { // #6741 ASSERT_THROW(checkCode("{ } { } typedef int u_array[]; typedef u_array &u_array_ref; (u_array_ref arg) { } u_array_ref"), InternalError); } void garbageCode66() { // #6742 ASSERT_THROW(checkCode("{ { } }; { { } }; { }; class bar : public virtual"), InternalError); } void garbageCode68() { // #6745 checkCode("(int a[3]); typedef void (*fp) (void); fp"); } void garbageCode69() { // #6746 ASSERT_THROW(checkCode("{ (make_mess, aux); } typedef void F(void); aux(void (*x)()) { } (void (*y)()) { } F*"), InternalError); } void garbageCode70() { // #6747 ASSERT_THROW(checkCode("{ } __attribute__((constructor)) void"), InternalError); } void garbageCode71() { // #6748 ASSERT_THROW(checkCode("( ) { } typedef void noattr_t ( ) ; noattr_t __attribute__ ( )"), InternalError); } void garbageCode72() { // #6749 ASSERT_THROW(checkCode("{ } { } typedef void voidfn(void); = } } unsigned *d = (b b--) --*d"), InternalError); } void garbageCode78() { // #6756 ASSERT_THROW(checkCode("( ) { [ ] } ( ) { } const_array_of_int ( ) { } typedef int A [ ] [ ] ; A a = { { } { } }"), InternalError); } void garbageCode79() { // #6757 ASSERT_THROW(checkCode("{ } { } typedef void ( func_type ) ( ) ; func_type & ( )"), InternalError); } void garbageCode80() { // #6759 ASSERT_THROW(checkCode("( ) { ; ( ) ; ( * ) [ ] ; [ ] = ( ( ) ( ) h ) ! ( ( ) ) } { ; } { } head heads [ ] = ; = & heads [ 2 ]"), InternalError); } void garbageCode81() { // #6760 ASSERT_THROW(checkCode("{ } [ ] { ( ) } { } typedef void ( *fptr1 ) ( ) const"), InternalError); } void garbageCode82() { // #6761 ASSERT_THROW(checkCode("p(\"Hello \" 14) _yn(const size_t) typedef bool pfunk (*pfunk)(const size_t)"), InternalError); } void garbageCode83() { // #6771 ASSERT_THROW(checkCode("namespace A { class } class A { friend C ; } { } ;"), InternalError); } void garbageCode84() { // #6780 ASSERT_THROW(checkCode("int main ( [ ] ) { " " [ ] ; int i = 0 ; do { } ; } ( [ ] ) { }"), InternalError); // do not crash } void garbageCode85() { // #6784 ASSERT_THROW(checkCode("{ } { } typedef void ( *VoidFunc() ) ( ) ; VoidFunc"), InternalError); // do not crash } void garbageCode86() { // #6785 ASSERT_THROW(checkCode("{ } typedef char ( *( X ) ( void) , char ) ;"), InternalError); // do not crash } void garbageCode87() { // #6788 ASSERT_THROW(checkCode("((X (128))) (int a) { v[ = {} (x 42) a] += }"), InternalError); // do not crash } void garbageCode88() { // #6786 ASSERT_THROW(checkCode("( ) { ( 0 ) { ( ) } } g ( ) { i( ( false ?) ( ) : 1 ) ; } ;"), InternalError); // do not crash } void garbageCode90() { // #6790 ASSERT_THROW(checkCode("{ } { } typedef int u_array [[ ] ; typedef u_array & u_array_ref] ( ) { } u_array_ref_gbl_obj0"), InternalError); // do not crash } void garbageCode91() { // #6791 ASSERT_THROW(checkCode("typedef __attribute__((vector_size (16))) { return[ (v2df){ } ;] }"), InternalError); // throw syntax error } void garbageCode92() { // #6792 ASSERT_THROW(checkCode("template < typename _Tp ( ( ) ; _Tp ) , decltype > { } { ( ) ( ) }"), InternalError); // do not crash } void garbageCode94() { // #6803 //checkCode("typedef long __m256i __attribute__ ( ( ( ) ) )[ ; ( ) { } typedef __m256i __attribute__ ( ( ( ) ) ) < ] ( ) { ; }"); ASSERT_THROW(checkCode("typedef long __m256i __attribute__ ( ( ( ) ) )[ ; ( ) { } typedef __m256i __attribute__ ( ( ( ) ) ) < ] ( ) { ; }"), InternalError); } void garbageCode95() { // #6804 ASSERT_THROW(checkCode("{ } x x ; { } h h [ ] ( ) ( ) { struct x ( x ) ; int __attribute__ ( ) f ( ) { h - > first = & x ; struct x * n = h - > first ; ( ) n > } }"), InternalError); // do not crash } void garbageCode96() { // #6807 ASSERT_THROW(checkCode("typedef J J[ ; typedef ( ) ( ) { ; } typedef J J ;] ( ) ( J cx ) { n } ;"), InternalError); // throw syntax error } void garbageCode97() { // #6808 ASSERT_THROW(checkCode("namespace A {> } class A{ { }} class A : T< ;"), InternalError); } void garbageCode98() { // #6838 ASSERT_THROW(checkCode("for (cocon To::ta@Taaaaaforconst oken aaaaaaaaaaaa5Dl()\n" "const unsigned in;\n" "fon *tok = f);.s(Token i = d-)L;"), InternalError); } void garbageCode99() { // #6726 ASSERT_THROW(checkCode("{ xs :: i(:) ! ! x/5 ! !\n" "i, :: a :: b integer, } foo2(x) :: j(:)\n" "b type(*), d(:), a x :: end d(..), foo end\n" "foo4 b d(..), a a x type(*), b foo2 b"), InternalError); } void garbageCode100() { // #6840 ASSERT_THROW(checkCode("( ) { ( i< ) } int foo ( ) { int i ; ( for ( i => 1 ) ; ) }"), InternalError); } void garbageCode101() { // #6835 // Reported case ASSERT_THROW(checkCode("template < class , =( , int) X = 1 > struct A { } ( ) { = } [ { } ] ( ) { A < void > 0 }"), InternalError); // Reduced case ASSERT_THROW(checkCode("template < class =( , ) X = 1> struct A {}; A a;"), InternalError); } void garbageCode102() { // #6846 checkCode("struct Object { ( ) ; Object & operator= ( Object ) { ( ) { } if ( this != & b ) } }"); } void garbageCode103() { // #6824 ASSERT_THROW(checkCode("a f(r) int * r; { { int s[2]; [f(s); if () ] } }"), InternalError); } void garbageCode104() { // #6847 ASSERT_THROW(checkCode("template < Types > struct S {> ( S < ) S >} { ( ) { } } ( ) { return S < void > ( ) } { ( )> >} { ( ) { } } ( ) { ( ) }"), InternalError); } void garbageCode105() { // #6859 ASSERT_THROW(checkCode("void foo (int i) { int a , for (a 1; a( < 4; a++) if (a) (b b++) (b);) n++; }"), InternalError); } void garbageCode106() { // #6880 ASSERT_THROW(checkCode("[ ] typedef typedef b_array b_array_ref [ ; ] ( ) b_array_ref b_array_ref_gbl_obj0 { ; { b_array_ref b_array_ref_gbl_obj0 } }"), InternalError); } void garbageCode107() { // #6881 TODO_ASSERT_THROW(checkCode("enum { val = 1{ }; { const} }; { } Bar { const int A = val const } ;"), InternalError); } void garbageCode108() { // #6895 "segmentation fault (invalid code) in CheckCondition::isOppositeCond" ASSERT_THROW(checkCode("A( ) { } bool f( ) { ( ) F; ( ) { ( == ) if ( !=< || ( !A( ) && r[2] ) ) ( !A( ) ) ( ) } }"), InternalError); } void garbageCode109() { // #6900 "segmentation fault (invalid code) in CheckStl::runSimplifiedChecks" checkCode("( *const<> (( ) ) { } ( *const ( ) ( ) ) { } ( * const<> ( size_t )) ) { } ( * const ( ) ( ) ) { }"); } void garbageCode110() { // #6902 "segmentation fault (invalid code) in CheckStl::string_c_str" ASSERT_THROW(checkCode("( *const<> ( size_t ) ; foo ) { } * ( *const ( size_t ) ( ) ;> foo )< { }"), InternalError); } void garbageCode111() { // #6907 TODO_ASSERT_THROW(checkCode("enum { FOO = 1( ,) } {{ FOO }} ;"), InternalError); } void garbageCode112() { // #6909 TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ) } {{ }}>> enum { FOO< = ( ) } { { } } ;"), InternalError); } void garbageCode114() { // #2118 checkCode("Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n" " for (unsigned i = 0 ; i < count; i++) {\n" " }\n" "});"); } void garbageCode115() { // #5506 ASSERT_THROW(checkCode("A template < int { int = -1 ; } template < int N > struct B { int [ A < N > :: zero ] ; } ; B < 0 > b ;"), InternalError); } void garbageCode116() { // #5356 ASSERT_THROW(checkCode("struct template struct B { }; B < 0 > b;"), InternalError); } void garbageCode117() { // #6121 TODO_ASSERT_THROW(checkCode("enum E { f = {} };\n" "int a = f;"), InternalError); } void garbageCode118() { // #5600 - missing include causes invalid enum ASSERT_THROW(checkCode("enum {\n" " NUM_OPCODES =\n" // #include "definition" "};\n" "struct bytecode {};\n" "jv jq_next() { opcode = ((opcode) +NUM_OPCODES);\n" "}"), InternalError); } void garbageCode119() { // #5598 checkCode("{ { void foo() { struct }; template struct S { Used x; void bar() } auto f = [this] { }; } };"); } void garbageCode120() { // #4927 checkCode("int main() {\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); } void garbageCode121() { // #2585 ASSERT_THROW(checkCode("abcdef?" "?<" "123456?" "?>" "+?" "?="), InternalError); } void garbageCode122() { // #6303 checkCode("void foo() {\n" "char *a = malloc(10);\n" "a[0]\n" "}"); } void garbageCode123() { checkCode("namespace pr16989 {\n" " class C {\n" " C tpl_mem(T *) { return }\n" " };\n" "}"); } void garbageCode125() { ASSERT_THROW(checkCode("{ T struct B : T valueA_AA ; } T : [ T > ( ) { B } template < T > struct A < > : ] { ( ) { return valueA_AC struct { : } } b A < int > AC ( ) a_aa.M ; ( ) ( ) }"), InternalError); ASSERT_THROW(checkCode("template < Types > struct S :{ ( S < ) S >} { ( ) { } } ( ) { return S < void > ( ) }"), InternalError); } void garbageCode126() { ASSERT_THROW(checkCode("{ } float __ieee754_sinhf ( float x ) { float t , , do { gf_u ( jx ) { } ( 0 ) return ; ( ) { } t } ( 0x42b17180 ) { } }"), InternalError); } void garbageCode127() { // #6667 checkCode("extern \"C\" int printf(const char* fmt, ...);\n" "class A {\n" "public:\n" " int Var;\n" " A(int arg) { Var = arg; }\n" " ~A() { printf(\"A d'tor\\n\"); }\n" "};\n" " const A& foo(const A& arg) { return arg; }\n" " foo(A(12)).Var"); } void garbageCode128() { TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ) } {{ }} enum {{ FOO << = } ( ) } {{ }} ;"), InternalError); } void garbageCode129() { ASSERT_THROW(checkCode("operator - ( { } typedef typename x ; ( ) ) { ( { { ( ( ) ) } ( { } ) } ) }"), InternalError); } void garbageCode130() { TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ){ } { { } } { { FOO} = } ( ) } { { } } enumL\" ( enumL\" { { FOO } ( ) } { { } } ;"), InternalError); } void garbageCode131() { ASSERT_THROW(checkCode("( void ) { ( ) } ( ) / { ( ) }"), InternalError); // actually the invalid code should trigger an syntax error... } void garbageCode132() { // #7022 ASSERT_THROW(checkCode("() () { } { () () ({}) i() } void i(void(*ptr) ()) { ptr(!) () }"), InternalError); } void garbageCode133() { ASSERT_THROW(checkCode("void f() {{}"), InternalError); ASSERT_THROW(checkCode("void f()) {}"), InternalError); ASSERT_THROW(checkCode("void f()\n" "{\n" " foo(;\n" "}\n"), InternalError); ASSERT_THROW(checkCode("void f()\n" "{\n" " for(;;){ foo();\n" "}\n"), InternalError); ASSERT_THROW(checkCode("void f()\n" "{\n" " a[10;\n" "}\n"), InternalError); { const char code[] = "{\n" " a(\n" // <- error "}\n" "{\n" " b());\n" "}\n"; ASSERT_EQUALS("[test.cpp:2] Unmatched '('. Configuration: ''.", getSyntaxError(code)); } { const char code[] = "void f() {\n" " int x = 3) + 0;\n" // <- error: unmatched ) "}\n"; ASSERT_EQUALS("[test.cpp:2] Unmatched ')'. Configuration: ''.", getSyntaxError(code)); } { const char code[] = "void f() {\n" " int x = (3] + 0;\n" // <- error: unmatched ] "}\n"; ASSERT_EQUALS("[test.cpp:2] Unmatched ']'. Configuration: ''.", getSyntaxError(code)); } { const char code[] = "void f() {\n" // <- error: unmatched { " {\n" "}\n"; ASSERT_EQUALS("[test.cpp:1] Unmatched '{'. Configuration: ''.", getSyntaxError(code)); } } void garbageCode134() { // Ticket #5605, #5759, #5762, #5774, #5823, #6059 ASSERT_THROW(checkCode("foo() template struct tuple Args> tuple { } main() { foo(); }"), InternalError); ASSERT_THROW(checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"), InternalError); ASSERT_THROW(checkCode("() template < T = typename = x > struct a {} { f () }"), InternalError); ASSERT_THROW(checkCode("template < T = typename = > struct a { f }"), InternalError); checkCode("struct S { int i, j; }; " "template struct X {}; " "X<&S::i, int> x = X<&S::i, int>(); " "X<&S::j, int> y = X<&S::j, int>();"); checkCode("template struct A {}; " "template <> struct A {}; " "void foo(const void* f = 0) {}"); checkCode("template struct A { " " static const int s = 0; " "}; " "A a;"); checkCode("template class A {}; " "template > class B {}; " "template > class C { " " C() : _a(0), _b(0) {} " " int _a, _b; " "};"); checkCode("template struct A { " " static int i; " "}; " "void f() { A::i = 0; }"); } void garbageCode135() { // #4994 checkCode("long f () {\n" " return a >> extern\n" "}\n" "long a = 1 ;\n" "long b = 2 ;"); } void garbageCode136() { // #7033 ASSERT_THROW(checkCode("{ } () { void f() { node_t * n; for (; -n) {} } } { }"), InternalError); } void garbageCode137() { // #7034 ASSERT_THROW(checkCode("\" \" typedef signed char f; \" \"; void a() { f * s = () &[]; (; ) (; ) }"), InternalError); } void garbageCode138() { // #6660 checkCode("CS_PLUGIN_NAMESPACE_BEGIN(csparser)\n" "{\n" " struct foo\n" " {\n" " union\n" " {};\n" " } halo;\n" "}\n" "CS_PLUGIN_NAMESPACE_END(csparser)"); } void garbageCode139() { // #6659 heap user after free: kernel: sm750_accel.c ASSERT_THROW(checkCode("void hw_copyarea() {\n" " de_ctrl = (nDirection == RIGHT_TO_LEFT) ?\n" " ( (0 & ~(((1 << (1 - (0 ? DE_CONTROL_DIRECTION))) - 1) << (0 ? DE_CONTROL_DIRECTION))) )\n" " : 42;\n" "}"), InternalError); } void garbageCode140() { // #7035 ASSERT_THROW(checkCode("int foo(int align) { int off(= 0 % align; return off) ? \\ align - off : 0; \\ }"), InternalError); } void garbageCode141() { // #7043 TODO_ASSERT_THROW(checkCode("enum { X = << { X } } enum { X = X } = X ;"), InternalError); } void garbageCode142() { // #7050 checkCode("{ } ( ) { void mapGraphs ( ) { node_t * n ; for (!oid n ) { } } } { }"); } void garbageCode143() { // #6922 ASSERT_THROW(checkCode("void neoProgramShadowRegs() {\n" " int i;\n" " Bool noProgramShadowRegs;\n" " if (noProgramShadowRegs) {\n" " } else {\n" " switch (nPtr->NeoPanelWidth) {\n" " case 1280:\n" " VGAwCR(0x64,0x?? );\n" " }\n" " }\n" "}"), InternalError); } void garbageCode144() { // #6865 ASSERT_THROW(checkCode("template < typename > struct A { } ; template < typename > struct A < INVALID > : A < int[ > { }] ;"), InternalError); } void garbageCode146() { // #7081 ASSERT_THROW(checkCode("void foo() {\n" " ? std::cout << pow((, 1) << std::endl;\n" " double () ^ {\n" " int *p don't crash checkCode("void f() {\n" " int a;\n" " do { a=do_something() } while (a);\n" "}"); } void garbageCode152() { // happened in travis, originally from llvm clang code const char* code = "template \n" "static std::string foo(char *Bla) {\n" " while (Bla[1] && Bla[1] != ',') }\n"; checkCode(code); } void garbageCode153() { TODO_ASSERT_THROW(checkCode("enum { X = << { X } } { X X } enum { X = << { ( X ) } } { } X */"), InternalError); } void garbageCode154() { checkCode("\"abc\"[];"); } void garbageCode156() { // #7120 ASSERT_THROW(checkCode("struct {}a; d f() { c ? : } {}a.p"), InternalError); } void garbageCode157() { // #7131 ASSERT_THROW(checkCode("namespace std {\n" " template < typename >\n" " void swap();\n" "}" "template std::swap\n"), InternalError); } void garbageCode158() { // #3238 checkCode("__FBSDID(\"...\");"); } void garbageCode159() { // #7119 ASSERT_THROW(checkCode("({}typedef typename x;typename x!){({{}()})}"), InternalError); } void garbageCode160() { // #7190 ASSERT_THROW(checkCode("f(a,b,c,d)float [ a[],d;int ] b[],c;{} "), InternalError); // don't hang } void garbageCodeFuzzerClientMode1() { ASSERT_THROW(checkCode("void f() { x= name2 & name3 name2 = | 0.1 , | 0.1 , | 0.1 name4 <= >( ); }"), InternalError); ASSERT_THROW(checkCode("void f() { x = , * [ | + 0xff | > 0xff]; }"), InternalError); ASSERT_THROW(checkCode("void f() { x = , | 0xff , 0.1 < ; }"), InternalError); ASSERT_THROW(checkCode("void f() { x = [ 1 || ] ; }"), InternalError); ASSERT_THROW(checkCode("void f1() { x = name6 1 || ? name3 [ ( 1 || +) ] ; }"), InternalError); } void garbageValueFlow() { // #6089 const char* code = "{} int foo(struct, x1, struct x2, x3, int, x5, x6, x7)\n" "{\n" " (foo(s, , 2, , , 5, , 7)) abort()\n" "}\n"; ASSERT_THROW(checkCode(code), InternalError); // 6122 survive garbage code code = "; { int i ; for ( i = 0 ; = 123 ; ) - ; }"; ASSERT_THROW(checkCode(code), InternalError); code = "void f1() { for (int n = 0 n < 10 n++); }"; checkCode(code); } void garbageSymbolDatabase() { checkCode("void f( { u = 1 ; } ) { }"); ASSERT_THROW(checkCode("{ }; void namespace A::f; { g() { int } }"), InternalError); ASSERT_THROW(checkCode("class Foo {}; class Bar : public Foo"), InternalError); checkCode("YY_DECL { switch (yy_act) {\n" " case 65: YY_BREAK\n" " case YY_STATE_EOF(block):\n" " yyterminate();\n" "} }"); // #5663 } void garbageAST() { ASSERT_THROW(checkCode("N 1024 float a[N], b[N + 3], c[N]; void N; (void) i;\n" "int #define for (i = avx_test i < c[i]; i++)\n" "b[i + 3] = a[i] * {}"), InternalError); // Don't hang (#5787) checkCode("START_SECTION([EXTRA](bool isValid(const String &filename)))"); // Don't crash (#5991) // #8352 ASSERT_THROW(checkCode("else return % name5 name2 - =name1 return enum | { - name3 1 enum != >= 1 >= ++ { { || " "{ return return { | { - name3 1 enum != >= 1 >= ++ { name6 | ; ++}}}}}}}"), InternalError); ASSERT_THROW(checkCode("else return % name5 name2 - =name1 return enum | { - name3 1 enum != >= 1 >= ++ { { || " "{ return return { | { - name3 1 enum != >= 1 >= ++ { { || ; ++}}}}}}}}"), InternalError); } void templateSimplifierCrashes() { checkCode( // #5950 "struct A {\n" " template operator T*();\n" "};\n" "\n" "template <> A::operator char*(){ return 0; } // specialization\n" "\n" "int main() {\n" " A a;\n" " int *ip = a.operator int*();\n" "}\n" "\n" "namespace PR5742 {\n" " template struct A { };\n" " struct S {\n" " template operator T();\n" " } s;\n" " void f() {\n" " s.operator A >();\n" " }\n" "}"); checkCode( // #6034 "template class T, typename... Args>\n" "struct foo > {\n" " const bool value = true;\n" "};\n" "\n" "template\n" "struct int_\n" "{};\n" "\n" "int main() {\n" " foo >::value;\n" "}"); checkCode( // #6117 "template struct something_like_tuple\n" "{};\n" "template struct is_last {\n" " static const bool value = false;\n" "};\n" "template class Tuple, typename ... Head>\n" "struct is_last>\n" "{\n" " static const bool value = true;\n" "};\n" "\n" "#define SA(X) static_assert (X, #X)\n" "\n" "typedef something_like_tuple something_like_tuple_t;\n" "SA ((is_last::value == false));\n" "SA ((is_last::value == false));"); checkCode( // #6225 "template \n" "void templ_fun_with_ty_pack() {}\n" "\n" "namespace PR20047 {\n" " template \n" " struct A {};\n" " using AliasA = A;\n" "}"); // #3449 ASSERT_EQUALS("template < typename T > struct A ;\n" "struct B { template < typename T > struct C } ;\n" "{ } ;", checkCode("template struct A;\n" "struct B { template struct C };\n" "{};")); } void garbageCode161() { //7200 ASSERT_THROW(checkCode("{ }{ if () try { } catch (...)} B : : ~B { }"), InternalError); } void garbageCode162() { //7208 ASSERT_THROW(checkCode("return << >> x return << >> x ", false), InternalError); } void garbageCode163() { //7228 ASSERT_THROW(checkCode("typedef s f[](){typedef d h(;f)}", false), InternalError); } void garbageCode164() { //7234 ASSERT_THROW(checkCode("class d{k p;}(){d::d():B<()}"), InternalError); } void garbageCode165() { //7235 ASSERT_THROW(checkCode("for(;..)", false),InternalError); } void garbageCode167() { //7237 ASSERT_THROW(checkCode("class D00i000{:D00i000::}i"),InternalError); } void garbageCode168() { // 7246 checkCode("long foo(void) { return *bar; }", false); } void garbageCode169() { // 6713 ASSERT_THROW(checkCode("( ) { ( ) ; { return } switch ( ) i\n" "set case break ; default: ( ) }", false), InternalError); } void garbageCode170() { // 7255 ASSERT_THROW(checkCode("d i(){{f*s=typeid(()0,)}}", false), InternalError); } void garbageCode171() { // 7270 ASSERT_THROW(checkCode("(){case()?():}:", false), InternalError); } void garbageCode172() { // #7357 ASSERT_THROW(checkCode("p>,"), InternalError); } void garbageCode173() { // #6781 heap corruption ; TemplateSimplifier::simplifyTemplateInstantiations ASSERT_THROW(checkCode(" template < Types > struct S : >( S < ...Types... > S <) > { ( ) { } } ( ) { return S < void > ( ) }"), InternalError); } void garbageCode174() { // #7356 ASSERT_THROW(checkCode("{r e() { w*constD = (())D = cast< }}"), InternalError); } void garbageCode175() { // #7027 ASSERT_THROW(checkCode("int f() {\n" " int i , j;\n" " for ( i = t3 , i < t1 ; i++ )\n" " for ( j = 0 ; j < = j++ )\n" " return t1 ,\n" "}"), InternalError); } void garbageCode176() { // #7527 checkCode("class t { { struct } enum class f : unsigned { q } b ; operator= ( T ) { switch ( b ) { case f::q: } } { assert ( b ) ; } } { ; & ( t ) ( f::t ) ; } ;"); } void garbageCode181() { ASSERT_THROW(checkCode("int test() { int +; }"), InternalError); } // #4195 - segfault for "enum { int f ( ) { return = } r = f ( ) ; }" void garbageCode182() { ASSERT_THROW(checkCode("enum { int f ( ) { return = } r = f ( ) ; }"), InternalError); } // #7505 - segfault void garbageCode183() { ASSERT_THROW(checkCode("= { int } enum return { r = f() f(); }"), InternalError); } void garbageCode184() { // #7699 ASSERT_THROW(checkCode("unsigned int AquaSalSystem::GetDisplayScreenCount() {\n" " NSArray* pScreens = [NSScreen screens];\n" " return pScreens ? [pScreens count] : 1;\n" "}"), InternalError); } void garbageCode185() { // #6011 crash in libreoffice failure to create proper AST checkCode( "namespace binfilter\n" "{\n" " BOOL EnhWMFReader::ReadEnhWMF()\n" " {\n" " pOut->CreateObject( nIndex, GDI_BRUSH, new WinMtfFillStyle( ReadColor(), ( nStyle == BS_HOLLOW ) ? TRUE : FALSE ) );\n" " return bStatus;\n" " };\n" "}"); } // #8151 - segfault due to incorrect template syntax void garbageCode186() { ASSERT_THROW(checkCode("A<>C"), InternalError); } void garbageCode187() { // # 8152 - segfault in handling const std::string inp("0|\0|0>;\n", 8); ASSERT_THROW(checkCode(inp), InternalError); checkCode("template struct S : A< B || C > {};"); // No syntax error: #8390 checkCode("static_assert(A || B, ab);"); } void garbageCode188() { // #8255 ASSERT_THROW(checkCode("{z r(){(){for(;<(x);){if(0==0)}}}}"), InternalError); } void garbageCode189() { // #8317 checkCode("t&n(){()()[](){()}}$"); } void garbageCode190() { // #8307 ASSERT_THROW(checkCode("void foo() {\n" " int i;\n" " i *= 0;\n" " !i <;\n" "}"), InternalError); } void garbageCode191() { // #8333 ASSERT_THROW(checkCode("struct A { int f(const); };"), InternalError); ASSERT_THROW(checkCode("struct A { int f(int, const, char); };"), InternalError); ASSERT_THROW(checkCode("struct A { int f(struct); };"), InternalError); // The following code is valid and should not trigger any error checkCode("struct A { int f ( char ) ; } ;"); } void garbageCode192() { // #8386 (segmentation fault) ASSERT_THROW(checkCode("{(()[((0||0xf||))]0[])}"), InternalError); } // #8740 void garbageCode193() { ASSERT_THROW(checkCode("d f(){!=[]&&0()!=0}"), InternalError); } // #8384 void garbageCode194() { ASSERT_THROW(checkCode("{((()))(return 1||);}"), InternalError); } // #8709 - no garbage but to avoid stability regression void garbageCode195() { checkCode("a b;\n" "void c() {\n" " switch (d) { case b:; }\n" " double e(b);\n" " if(e <= 0) {}\n" "}"); } // #8265 void garbageCode196() { ASSERT_THROW(checkCode("0|,0<= void { ( ()) } 1 name3 return >= >= ( ) >= name5 (\n" " name5 name6 :nam00 [ ()])}))})})})};\n" "}"), InternalError); } // #8752 void garbageCode199() { checkCode("d f(){e n00e0[]n00e0&" "0+f=0}"); } // #8757 void garbageCode200() { ASSERT_THROW(checkCode("(){e break,{(case)!{e:[]}}}"), InternalError); } // #8873 void garbageCode201() { ASSERT_THROW(checkCode("void f() { std::string s=\"abc\"; return s + }"), InternalError); } // #8907 void garbageCode202() { ASSERT_THROW(checkCode("void f() { UNKNOWN_MACRO(return); }"), InternalError); ASSERT_THROW(checkCode("void f() { UNKNOWN_MACRO(throw); }"), InternalError); } void garbageCode203() { // #8972 checkCode("{ > () {} }"); checkCode("template <> a > ::b();"); } void garbageCode204() { ASSERT_THROW(checkCode("template ()> c; template a as() {} as>();"), InternalError); } void garbageCode205() { checkCode("class CodeSnippetsEvent : public wxCommandEvent {\n" "public :\n" " CodeSnippetsEvent ( wxEventType commandType = wxEventType , int id = 0 ) ;\n" " CodeSnippetsEvent ( const CodeSnippetsEvent & event ) ;\n" "virtual wxEvent * Clone ( ) const { return new CodeSnippetsEvent ( * this ) ; }\n" "private :\n" " int m_SnippetID ;\n" "} ;\n" "const wxEventType wxEVT_CODESNIPPETS_GETFILELINKS = wxNewEventType ( )\n" "CodeSnippetsEvent :: CodeSnippetsEvent ( wxEventType commandType , int id )\n" ": wxCommandEvent ( commandType , id ) {\n" "}\n" "CodeSnippetsEvent :: CodeSnippetsEvent ( const CodeSnippetsEvent & Event )\n" ": wxCommandEvent ( Event )\n" ", m_SnippetID ( 0 ) {\n" "}"); // don't crash } void garbageCode206() { ASSERT_EQUALS("[test.cpp:1] syntax error: operator", getSyntaxError("void foo() { for (auto operator new : int); }")); ASSERT_EQUALS("[test.cpp:1] syntax error: operator", getSyntaxError("void foo() { for (a operator== :) }")); } void garbageCode207() { // #8750 ASSERT_THROW(checkCode("d f(){(.n00e0(return%n00e0''('')));}"), InternalError); } void garbageCode208() { // #8753 ASSERT_THROW(checkCode("d f(){(for(((((0{t b;((((((((()))))))))}))))))}"), InternalError); } void garbageCode209() { // #8756 ASSERT_THROW(checkCode("{(- -##0xf/-1 0)[]}"), InternalError); } void garbageCode210() { // #8762 ASSERT_THROW(checkCode("{typedef typedef c n00e0[]c000(;n00e0&c000)}"), InternalError); } void garbageCode211() { // #8764 ASSERT_THROW(checkCode("{typedef f typedef[]({typedef e e,>;typedef(((typedef struct A {};\n" "template struct A {}; \n" "A a;"); } void garbageCode217() { // #10011 ASSERT_THROW(checkCode("void f() {\n" " auto p;\n" " if (g(p)) {}\n" " assert();\n" "}"), InternalError); } void garbageCode218() { // #8763 checkCode("d f(){t n0000 const[]n0000+0!=n0000,(0)}"); // don't crash } void garbageCode219() { // #10101 checkCode("typedef void (*func) (addr) ;\n" "void bar(void) {\n" " func f;\n" " f & = (func)42;\n" "}\n"); // don't crash } void syntaxErrorFirstToken() { ASSERT_THROW(checkCode("&operator(){[]};"), InternalError); // #7818 ASSERT_THROW(checkCode("*(*const<> (size_t); foo) { } *(*const (size_t)() ; foo) { }"), InternalError); // #6858 ASSERT_THROW(checkCode(">{ x while (y) z int = }"), InternalError); // #4175 ASSERT_THROW(checkCode("&p(!{}e x){({(0?:?){({})}()})}"), InternalError); // #7118 ASSERT_THROW(checkCode(" { struct { typename D4:typename Base }; };"), InternalError); // #3533 ASSERT_THROW(checkCode(" > template < . > struct Y < T > { = } ;\n"), InternalError); // #6108 } void syntaxErrorLastToken() { ASSERT_THROW(checkCode("int *"), InternalError); // #7821 ASSERT_THROW(checkCode("x[y]"), InternalError); // #2986 ASSERT_THROW(checkCode("( ) &"), InternalError); ASSERT_THROW(checkCode("|| #if #define <="), InternalError); // #2601 ASSERT_THROW(checkCode("f::y:y : \n"), InternalError); ASSERT_THROW(checkCode("++4++ + + E++++++++++ + ch " "tp.oed5[.]"), InternalError); // #7074 ASSERT_THROW(checkCode("d a(){f s=0()8[]s?():0}*()?:0", false), InternalError); // #7236 ASSERT_THROW(checkCode("!2 : #h2 ?:", false), InternalError); // #7769 ASSERT_THROW(checkCode("--"), InternalError); ASSERT_THROW(checkCode("volatile true , test < test < #ifdef __ppc__ true ,"), InternalError); // #4169 ASSERT_THROW(checkCode("a,b--\n"), InternalError); // #2847 ASSERT_THROW(checkCode("x a[0] ="), InternalError); // #2682 ASSERT_THROW(checkCode("auto_ptr\n"), InternalError); // #2967 ASSERT_THROW(checkCode("char a[1]\n"), InternalError); // #2865 ASSERT_THROW(checkCode("<><<"), InternalError); // #2612 ASSERT_THROW(checkCode("z"), InternalError); // #2831 ASSERT_THROW(checkCode("><,f typedef name5 | ( , ;){ } (); }"), InternalError); // #9067 ASSERT_THROW(checkCode("void f() { x= {}( ) ( 'x')[ ] (); }"), InternalError); // #9068 ASSERT_THROW(checkCode("void f() { x= y{ } name5 y[ ] + y ^ name5 ^ name5 for ( ( y y y && y y y && name5 ++ int )); }"), InternalError); // #9069 } void cliCode() { // #8913 ASSERT_NO_THROW(checkCode( "public ref class LibCecSharp : public CecCallbackMethods {\n" "array ^ FindAdapters(String ^ path) {}\n" "bool GetDeviceInformation(String ^ port, LibCECConfiguration ^configuration, uint32_t timeoutMs) {\n" "bool bReturn(false);\n" "}\n" "};")); } void enumTrailingComma() { ASSERT_THROW(checkCode("enum ssl_shutdown_t {ssl_shutdown_none = 0,ssl_shutdown_close_notify = , } ;"), InternalError); // #8079 } void nonGarbageCode1() { checkCode("template class List {\n" "public:\n" " List();\n" " virtual ~List();\n" " template< class Predicate > u_int DeleteIf( const Predicate &pred );\n" "};\n" "template< class T >\n" "template< class Predicate > int\n" "List::DeleteIf( const Predicate &pred )\n" "{}"); // #8749 checkCode( "struct A {\n" " void operator+=(A&) && = delete;\n" "};"); // #8788 checkCode( "struct foo;\n" "void f() {\n" " auto fn = []() -> foo* { return new foo(); };\n" "}"); } }; REGISTER_TEST(TestGarbage) cppcheck-2.7/test/testimportproject.cpp000066400000000000000000000357561417746362400204640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "importproject.h" #include "settings.h" #include "testsuite.h" #include #include #include #include #include #include class TestImporter : public ImportProject { public: using ImportProject::importCompileCommands; using ImportProject::importCppcheckGuiProject; bool sourceFileExists(const std::string & /*file*/) OVERRIDE { return true; } }; class TestImportProject : public TestFixture { public: TestImportProject() : TestFixture("TestImportProject") {} private: void run() OVERRIDE { TEST_CASE(setDefines); TEST_CASE(setIncludePaths1); TEST_CASE(setIncludePaths2); TEST_CASE(setIncludePaths3); // macro names are case insensitive TEST_CASE(importCompileCommands1); TEST_CASE(importCompileCommands2); // #8563, #9567 TEST_CASE(importCompileCommands3); // check with existing trailing / in directory TEST_CASE(importCompileCommands4); // only accept certain file types TEST_CASE(importCompileCommands5); // Windows/CMake/Ninja generated comile_commands.json TEST_CASE(importCompileCommands6); // Windows/CMake/Ninja generated comile_commands.json with spaces TEST_CASE(importCompileCommands7); // linux: "/home/danielm/cppcheck 2" TEST_CASE(importCompileCommands8); // Windows: "C:\Users\danielm\cppcheck" TEST_CASE(importCompileCommandsArgumentsSection); // Handle arguments section TEST_CASE(importCompileCommandsNoCommandSection); // gracefully handles malformed json TEST_CASE(importCppcheckGuiProject); TEST_CASE(ignorePaths); } void setDefines() const { ImportProject::FileSettings fs; fs.setDefines("A"); ASSERT_EQUALS("A=1", fs.defines); fs.setDefines("A;B;"); ASSERT_EQUALS("A=1;B=1", fs.defines); fs.setDefines("A;;B;"); ASSERT_EQUALS("A=1;B=1", fs.defines); fs.setDefines("A;;B"); ASSERT_EQUALS("A=1;B=1", fs.defines); } void setIncludePaths1() const { ImportProject::FileSettings fs; std::list in(1, "../include"); std::map variables; fs.setIncludePaths("abc/def/", in, variables); ASSERT_EQUALS(1U, fs.includePaths.size()); ASSERT_EQUALS("abc/include/", fs.includePaths.front()); } void setIncludePaths2() const { ImportProject::FileSettings fs; std::list in(1, "$(SolutionDir)other"); std::map variables; variables["SolutionDir"] = "c:/abc/"; fs.setIncludePaths("/home/fred", in, variables); ASSERT_EQUALS(1U, fs.includePaths.size()); ASSERT_EQUALS("c:/abc/other/", fs.includePaths.front()); } void setIncludePaths3() const { // macro names are case insensitive ImportProject::FileSettings fs; std::list in(1, "$(SOLUTIONDIR)other"); std::map variables; variables["SolutionDir"] = "c:/abc/"; fs.setIncludePaths("/home/fred", in, variables); ASSERT_EQUALS(1U, fs.includePaths.size()); ASSERT_EQUALS("c:/abc/other/", fs.includePaths.front()); } void importCompileCommands1() const { const char json[] = R"([{ "directory": "/tmp", "command": "gcc -DTEST1 -DTEST2=2 -o /tmp/src.o -c /tmp/src.c", "file": "/tmp/src.c" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("TEST1=1;TEST2=2", importer.fileSettings.begin()->defines); } void importCompileCommands2() const { // Absolute file path #ifdef _WIN32 const char json[] = R"([{ "directory": "C:/foo", "command": "gcc -c /bar.c", "file": "/bar.c" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("C:/bar.c", importer.fileSettings.begin()->filename); #else const char json[] = R"([{ "directory": "/foo", "command": "gcc -c bar.c", "file": "/bar.c" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("/bar.c", importer.fileSettings.begin()->filename); #endif } void importCompileCommands3() const { const char json[] = R"([{ "directory": "/tmp/", "command": "gcc -c src.c", "file": "src.c" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("/tmp/src.c", importer.fileSettings.begin()->filename); } void importCompileCommands4() const { const char json[] = R"([{ "directory": "/tmp/", "command": "gcc -c src.mm", "file": "src.mm" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(0, importer.fileSettings.size()); } void importCompileCommands5() const { const char json[] = R"([{ "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomylib\\CMakeFiles\\mylib.dir\\src\\foobar\\mylib.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp", "file": "C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp" }, { "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\myapp\\src -Imyapp -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomyapp\\CMakeFiles\\myapp.dir\\src\\main.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp", "file": "C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(2, importer.fileSettings.size()); ASSERT_EQUALS("C:/Users/dan/git/test-cppcheck/mylib/src/", importer.fileSettings.begin()->includePaths.front()); } void importCompileCommands6() const { const char json[] = R"([{ "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src -I\"C:\\Users\\dan\\git\\test-cppcheck\\mylib\\second src\" /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomylib\\CMakeFiles\\mylib.dir\\src\\foobar\\mylib.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp", "file": "C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp" }, { "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\myapp\\src -Imyapp -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomyapp\\CMakeFiles\\myapp.dir\\src\\main.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp", "file": "C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(2, importer.fileSettings.size()); ASSERT_EQUALS("C:/Users/dan/git/test-cppcheck/mylib/second src/", importer.fileSettings.begin()->includePaths.front()); } void importCompileCommands7() const { // cmake -DFILESDIR="/some/path" .. const char json[] = R"([{ "directory": "/home/danielm/cppcheck 2/b/lib", "command": "/usr/bin/c++ -DFILESDIR=\\\"/some/path\\\" -I\"/home/danielm/cppcheck 2/b/lib\" -isystem \"/home/danielm/cppcheck 2/externals\" \"/home/danielm/cppcheck 2/lib/astutils.cpp\"", "file": "/home/danielm/cppcheck 2/lib/astutils.cpp" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("FILESDIR=\"/some/path\"", importer.fileSettings.begin()->defines); ASSERT_EQUALS(1, importer.fileSettings.begin()->includePaths.size()); ASSERT_EQUALS("/home/danielm/cppcheck 2/b/lib/", importer.fileSettings.begin()->includePaths.front()); TODO_ASSERT_EQUALS("/home/danielm/cppcheck 2/externals/", "/home/danielm/cppcheck 2/b/lib/", importer.fileSettings.begin()->includePaths.back()); } void importCompileCommands8() const { // cmake -DFILESDIR="C:\Program Files\Cppcheck" -G"NMake Makefiles" .. const char json[] = R"([{ "directory": "C:/Users/danielm/cppcheck/build/lib", "command": "C:\\PROGRA~2\\MICROS~2\\2017\\COMMUN~1\\VC\\Tools\\MSVC\\1412~1.258\\bin\\Hostx64\\x64\\cl.exe /nologo /TP -DFILESDIR=\"\\\"C:\\Program Files\\Cppcheck\\\"\" -IC:\\Users\\danielm\\cppcheck\\build\\lib -IC:\\Users\\danielm\\cppcheck\\lib -c C:\\Users\\danielm\\cppcheck\\lib\\astutils.cpp", "file": "C:/Users/danielm/cppcheck/lib/astutils.cpp" }])"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("FILESDIR=\"C:\\Program Files\\Cppcheck\"", importer.fileSettings.begin()->defines); ASSERT_EQUALS(2, importer.fileSettings.begin()->includePaths.size()); ASSERT_EQUALS("C:/Users/danielm/cppcheck/build/lib/", importer.fileSettings.begin()->includePaths.front()); ASSERT_EQUALS("C:/Users/danielm/cppcheck/lib/", importer.fileSettings.begin()->includePaths.back()); } void importCompileCommandsArgumentsSection() const { const char json[] = "[ { \"directory\": \"/tmp/\"," "\"arguments\": [\"gcc\", \"-c\", \"src.c\"]," "\"file\": \"src.c\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("/tmp/src.c", importer.fileSettings.begin()->filename); } void importCompileCommandsNoCommandSection() const { const char json[] = "[ { \"directory\": \"/tmp/\"," "\"file\": \"src.mm\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(0, importer.fileSettings.size()); } void importCppcheckGuiProject() const { const char xml[] = "\n" "\n" " \n" " out1\n" " true\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; std::istringstream istr(xml); Settings s; TestImporter project; ASSERT_EQUALS(true, project.importCppcheckGuiProject(istr, &s)); ASSERT_EQUALS(1, project.guiProject.pathNames.size()); ASSERT_EQUALS("cli/", project.guiProject.pathNames[0]); ASSERT_EQUALS(1, s.includePaths.size()); ASSERT_EQUALS("lib/", s.includePaths.front()); } void ignorePaths() { ImportProject::FileSettings fs1, fs2; fs1.filename = "foo/bar"; fs2.filename = "qwe/rty"; TestImporter project; project.fileSettings = {fs1, fs2}; project.ignorePaths({"*foo", "bar*"}); ASSERT_EQUALS(2, project.fileSettings.size()); project.ignorePaths({"foo/*"}); ASSERT_EQUALS(1, project.fileSettings.size()); ASSERT_EQUALS("qwe/rty", project.fileSettings.front().filename); project.ignorePaths({ "*e/r*" }); ASSERT_EQUALS(0, project.fileSettings.size()); } }; REGISTER_TEST(TestImportProject) cppcheck-2.7/test/testincompletestatement.cpp000066400000000000000000000317711417746362400216400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkother.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include #include class TestIncompleteStatement : public TestFixture { public: TestIncompleteStatement() : TestFixture("TestIncompleteStatement") {} private: Settings settings; void check(const char code[], bool inconclusive = false) { // Clear the error buffer.. errout.str(""); settings.certainty.setEnabled(Certainty::inconclusive, inconclusive); // Raw tokens.. std::vector files(1, "test.cpp"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenize.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); // Check for incomplete statements.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkIncompleteStatement(); } void run() OVERRIDE { settings.severity.enable(Severity::warning); TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); TEST_CASE(test4); TEST_CASE(test5); TEST_CASE(test6); TEST_CASE(test7); TEST_CASE(test_numeric); TEST_CASE(void0); // #6327: No fp for statement "(void)0;" TEST_CASE(intarray); TEST_CASE(structarraynull); TEST_CASE(structarray); TEST_CASE(conditionalcall); // ; 0==x ? X() : Y(); TEST_CASE(structinit); // #2462 : ABC abc{1,2,3}; TEST_CASE(returnstruct); TEST_CASE(cast); // #3009 : (struct Foo *)123.a = 1; TEST_CASE(increment); // #3251 : FP for increment TEST_CASE(cpp11init); // #5493 : int i{1}; TEST_CASE(cpp11init2); // #8449 TEST_CASE(cpp11init3); // #8995 TEST_CASE(block); // ({ do_something(); 0; }) TEST_CASE(mapindex); TEST_CASE(commaoperator1); TEST_CASE(commaoperator2); TEST_CASE(redundantstmts); TEST_CASE(vardecl); TEST_CASE(archive); // ar & x TEST_CASE(ast); TEST_CASE(oror); // dostuff() || x=32; } void test1() { check("void foo()\n" "{\n" " const char def[] =\n" " \"abc\";\n" "}"); ASSERT_EQUALS("", errout.str()); } void test2() { check("void foo()\n" "{\n" " \"abc\";\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found a statement that begins with string constant.\n", errout.str()); } void test3() { check("void foo()\n" "{\n" " const char *str[] = { \"abc\" };\n" "}"); ASSERT_EQUALS("", errout.str()); } void test4() { check("void foo()\n" "{\n" "const char *a =\n" "{\n" "\"hello \"\n" "\"more \"\n" "\"world\"\n" "};\n" "}"); ASSERT_EQUALS("", errout.str()); } void test5() { check("void foo()\n" "{\n" " 50;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found a statement that begins with numeric constant.\n", errout.str()); } void test6() { // don't crash check("void f() {\n" " 1 == (two + three);\n" " 2 != (two + three);\n" " (one + two) != (two + three);\n" "}"); } void test7() { // #9335 check("namespace { std::string S = \"\"; }\n" "\n" "class C {\n" "public:\n" " explicit C(const std::string& s);\n" "};\n" "\n" "void f() {\n" " for (C c(S); ; ) {\n" " (void)c;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void test_numeric() { check("struct P {\n" " double a;\n" " double b;\n" "};\n" "void f() {\n" " const P values[2] =\n" " {\n" " { 346.1,114.1 }, { 347.1,111.1 }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void void0() { // #6327 check("void f() { (void*)0; }"); ASSERT_EQUALS("", errout.str()); check("#define X 0\n" "void f() { X; }"); ASSERT_EQUALS("", errout.str()); } void intarray() { check("int arr[] = { 100/2, 1*100 };"); ASSERT_EQUALS("", errout.str()); } void structarraynull() { check("struct st arr[] = {\n" " { 100/2, 1*100 }\n" " { 90, 70 }\n" "}"); ASSERT_EQUALS("", errout.str()); } void structarray() { check("struct st arr[] = {\n" " { 100/2, 1*100 }\n" " { 90, 70 }\n" "};"); ASSERT_EQUALS("", errout.str()); } void conditionalcall() { check("void f() {\n" " 0==x ? X() : Y();\n" "}"); ASSERT_EQUALS("", errout.str()); } void structinit() { // #2462 - C++11 struct initialization check("void f() {\n" " ABC abc{1,2,3};\n" "}"); ASSERT_EQUALS("", errout.str()); // #6260 - C++11 array initialization check("void foo() {\n" " static const char* a[][2] {\n" " {\"b\", \"\"},\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); // #2482 - false positive for empty struct check("struct A {};"); ASSERT_EQUALS("", errout.str()); // #4387 - C++11 initializer list check("A::A() : abc{0} {}"); ASSERT_EQUALS("", errout.str()); // #5042 - C++11 initializer list check("A::A() : abc::def{0} {}"); ASSERT_EQUALS("", errout.str()); // #4503 - vector init check("void f() { vector v{1}; }"); ASSERT_EQUALS("", errout.str()); } void returnstruct() { check("struct s foo() {\n" " return (struct s){0,0};\n" "}"); ASSERT_EQUALS("", errout.str()); // #4754 check("unordered_map foo() {\n" " return {\n" " {\"hi\", \"there\"},\n" " {\"happy\", \"sad\"}\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s foo() {\n" " return (struct s){0};\n" "}"); ASSERT_EQUALS("", errout.str()); } void cast() { check("void f() {\n" " ((struct foo *)(0x1234))->xy = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void increment() { check("void f() {\n" " int x = 1;\n" " x++, x++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void cpp11init() { check("void f() {\n" " int x{1};\n" "}"); ASSERT_EQUALS("", errout.str()); } void cpp11init2() { check("x handlers{\n" " { \"mode2\", []() { return 2; } },\n" "};"); ASSERT_EQUALS("", errout.str()); } void cpp11init3() { check("struct A { void operator()(int); };\n" "void f() {\n" "A{}(0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("template struct A { void operator()(int); };\n" "void f() {\n" "A{}(0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void block() { check("void f() {\n" " ({ do_something(); 0; });\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" "out:\n" " ({ do_something(); 0; });\n" "}"); ASSERT_EQUALS("", errout.str()); } void mapindex() { check("void f() {\n" " map[{\"1\",\"2\"}]=0;\n" "}"); ASSERT_EQUALS("", errout.str()); } // #8827 void commaoperator1() { check("void foo(int,const char*,int);\n" "void f(int value) {\n" " foo(42,\"test\",42),(value&42);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found suspicious operator ','\n", errout.str()); } void commaoperator2() { check("void f() {\n" " for(unsigned int a=0, b; a<10; a++ ) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } // #8451 void redundantstmts() { check("void f1(int x) {\n" " 1;\n" " (1);\n" " (char)1;\n" " ((char)1);\n" " !x;\n" " (!x);\n" " (unsigned int)!x;\n" " ~x;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:3]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:4]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:5]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:6]: (warning, inconclusive) Found suspicious operator '!'\n" "[test.cpp:7]: (warning, inconclusive) Found suspicious operator '!'\n" "[test.cpp:9]: (warning, inconclusive) Found suspicious operator '~'\n", errout.str()); check("void f1(int x) { x; }", true); ASSERT_EQUALS("[test.cpp:1]: (warning) Unused variable value 'x'\n", errout.str()); check("void f() { if (Type t; g(t)) {} }"); // #9776 ASSERT_EQUALS("", errout.str()); } void vardecl() { // #8984 check("void f() { a::b *c = d(); }", true); ASSERT_EQUALS("", errout.str()); check("void f() { std::vector *c; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { a::b &c = d(); }", true); ASSERT_EQUALS("", errout.str()); check("void f() { std::vector &c; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { a::b &&c = d(); }", true); ASSERT_EQUALS("", errout.str()); check("void f() { std::vector &&c; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { char * const * a, * const * b; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { char * const * a = 0, * volatile restrict * b; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { char * const * a = 0, * volatile const * b; }", true); ASSERT_EQUALS("", errout.str()); } void archive() { check("void f(Archive &ar) {\n" " ar & x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(int ar) {\n" " ar & x;\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '&'\n", errout.str()); } void ast() { check("struct c { void a() const { for (int x=0; x;); } };", true); ASSERT_EQUALS("", errout.str()); } void oror() { check("void foo() {\n" " params_given (params, \"overrides\") || (overrides = \"1\");\n" "}", true); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestIncompleteStatement) cppcheck-2.7/test/testinternal.cpp000066400000000000000000000511701417746362400173630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef CHECK_INTERNAL #include "tokenize.h" #include "checkinternal.h" #include "testsuite.h" class TestInternal : public TestFixture { public: TestInternal() : TestFixture("TestInternal") {} private: Settings settings; void run() OVERRIDE { settings.addEnabled("internal"); TEST_CASE(simplePatternInTokenMatch); TEST_CASE(complexPatternInTokenSimpleMatch); TEST_CASE(simplePatternSquareBrackets); TEST_CASE(simplePatternAlternatives); TEST_CASE(missingPercentCharacter); TEST_CASE(unknownPattern); TEST_CASE(redundantNextPrevious); TEST_CASE(internalError); TEST_CASE(orInComplexPattern); TEST_CASE(extraWhitespace); TEST_CASE(checkRedundantTokCheck); } #define check(code) check_(code, __FILE__, __LINE__) void check_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckInternal checkInternal; checkInternal.runChecks(&tokenizer, &settings, this); } void simplePatternInTokenMatch() { check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \";\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::Match() call: \";\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%or%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::Match() call: \"%or%\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findmatch(tok, \";\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::findmatch() call: \";\"\n", errout.str()); } void complexPatternInTokenSimpleMatch() { check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"%type%\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::findsimplematch() call: \"%type%\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"} !!else\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::findsimplematch() call: \"} !!else\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"foobar\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"%\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void simplePatternSquareBrackets() { check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[ ]\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[]\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"[]\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"] [\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"] [ [abc]\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"] [ [abc]\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[.,;]\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"[.,;]\"\n", errout.str()); } void simplePatternAlternatives() { check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"||\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"|\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"a|b\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"a|b\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"|= 0\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"| 0 )\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingPercentCharacter() { check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo %type% bar\");\n" "}"); ASSERT_EQUALS("", errout.str()); // Missing % at the end of string check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%type\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"%type\"\n", errout.str()); // Missing % in the middle of a pattern check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo %type bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo %type bar\"\n", errout.str()); // Bei quiet on single % check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo % %type% bar\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo % %type % bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo % %type % bar\"\n" "[test.cpp:3]: (error) Unknown pattern used: \"%type %\"\n", errout.str()); // Find missing % also in 'alternatives' pattern check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo|%type|bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n" , errout.str()); // Make sure we don't take %or% for a broken %oror% check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo|%oror%|bar\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void unknownPattern() { check("void f() {\n" " Token::Match(tok, \"%typ%\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Unknown pattern used: \"%typ%\"\n", errout.str()); // Make sure we don't take %or% for a broken %oror% check("void f() {\n" " Token::Match(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantNextPrevious() { check("void f() {\n" " return tok->next()->previous();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::next()' followed by 'Token::previous()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->tokAt(5)->previous();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::previous()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->previous()->linkAt(5);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::previous()' followed by 'Token::linkAt()' can be simplified.\n", errout.str()); check("void f() {\n" " tok->next()->previous(foo);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->next()->next();\n" // Simplification won't make code much shorter/readable "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->previous()->previous();\n" // Simplification won't make code much shorter/readable "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->tokAt(foo+bar)->tokAt();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::tokAt()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->tokAt(foo+bar)->link();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::link()' can be simplified.\n", errout.str()); check("void f() {\n" " tok->tokAt(foo+bar)->link(foo);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->next()->next()->str();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::next()' followed by 'Token::str()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->previous()->next()->str();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::previous()' followed by 'Token::next()' can be simplified.\n", errout.str()); } void internalError() { // Make sure cppcheck does not raise an internal error of Token::Match ( Ticket #3727 ) check("class DELPHICLASS X;\n" "class Y {\n" "private:\n" " X* x;\n" "};\n" "class Z {\n" " char z[1];\n" " Z(){\n" " z[0] = 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void orInComplexPattern() { check("void f() {\n" " Token::Match(tok, \"||\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"||\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"|\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"|\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"[|+-]\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " Token::Match(tok, \"foo | bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"foo | bar\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"foo |\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"foo |\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"bar foo|\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void extraWhitespace() { // whitespace at the end check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%str% \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \"%str% \"\n", errout.str()); // whitespace at the begin check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \" %str%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \" %str%\"\n", errout.str()); // two whitespaces or more check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%str% bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \"%str% bar\"\n", errout.str()); // test simpleMatch check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"foobar \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::simpleMatch() call: \"foobar \"\n", errout.str()); // test findmatch check("void f() {\n" " const Token *tok;\n" " Token::findmatch(tok, \"%str% \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::findmatch() call: \"%str% \"\n", errout.str()); // test findsimplematch check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"foobar \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::findsimplematch() call: \"foobar \"\n", errout.str()); } void checkRedundantTokCheck() { // findsimplematch check("void f() {\n" " const Token *tok;\n" " if(tok && Token::findsimplematch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // findmatch check("void f() {\n" " const Token *tok;\n" " if(tok && Token::findmatch(tok, \"%str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // Match check("void f() {\n" " const Token *tok;\n" " if(tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && b && tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && b && c && tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && b && c && tok && d && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // simpleMatch check("void f() {\n" " const Token *tok;\n" " if(tok && Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // Match check("void f() {\n" " const Token *tok;\n" " if(tok->previous() && Token::Match(tok->previous(), \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok->previous()\", match-function already checks if it is null.\n", errout.str()); // don't report: // tok->previous() vs tok check("void f() {\n" " const Token *tok;\n" " if(tok->previous() && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // tok vs tok->previous()) check("void f() {\n" " const Token *tok;\n" " if(tok && Token::Match(tok->previous(), \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // tok->previous() vs tok->previous()->previous()) check("void f() {\n" " const Token *tok;\n" " if(tok->previous() && Token::Match(tok->previous()->previous(), \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // if a && fn(a) triggers, make sure !a || !fn(a) triggers as well! check("void f() {\n" " const Token *tok;\n" " if(!tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a || !tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // if tok || !Token::simpleMatch... check("void f() {\n" " const Token *tok;\n" " if(tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // if !tok || Token::simpleMatch... check("void f() {\n" " const Token *tok;\n" " if(!tok || Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // something more complex check("void f() {\n" " const Token *tok;\n" " if(!tok->previous()->previous() || !Token::simpleMatch(tok->previous()->previous(), \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok->previous()->previous()\", match-function already checks if it is null.\n", errout.str()); } }; REGISTER_TEST(TestInternal) #endif // #ifdef CHECK_INTERNAL cppcheck-2.7/test/testio.cpp000066400000000000000000012144371417746362400161660ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkio.h" #include "config.h" #include "errortypes.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestIO : public TestFixture { public: TestIO() : TestFixture("TestIO") {} private: Settings settings; void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "windows.cfg"); LOAD_LIB_2(settings.library, "qt.cfg"); TEST_CASE(coutCerrMisusage); TEST_CASE(wrongMode_simple); TEST_CASE(wrongMode_complex); TEST_CASE(useClosedFile); TEST_CASE(fileIOwithoutPositioning); TEST_CASE(seekOnAppendedFile); TEST_CASE(fflushOnInputStream); TEST_CASE(incompatibleFileOpen); TEST_CASE(testScanf1); // Scanf without field limiters TEST_CASE(testScanf2); TEST_CASE(testScanf3); // #3494 TEST_CASE(testScanf4); // #ticket 2553 TEST_CASE(testScanf5); // #10632 TEST_CASE(testScanfArgument); TEST_CASE(testPrintfArgument); TEST_CASE(testPrintfArgumentVariables); TEST_CASE(testPosixPrintfScanfParameterPosition); // #4900 TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902 TEST_CASE(testMicrosoftScanfArgument); TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920 TEST_CASE(testMicrosoftSecurePrintfArgument); TEST_CASE(testMicrosoftSecureScanfArgument); TEST_CASE(testQStringFormatArguments); TEST_CASE(testTernary); // ticket #6182 TEST_CASE(testUnsignedConst); // ticket #6132 TEST_CASE(testAstType); // #7014 TEST_CASE(testPrintf0WithSuffix); // ticket #7069 TEST_CASE(testReturnValueTypeStdLib); TEST_CASE(testPrintfTypeAlias1); TEST_CASE(testPrintfAuto); // #8992 TEST_CASE(testPrintfParenthesis); // #8489 TEST_CASE(testStdDistance); // #10304 } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char* code, bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified, bool onlyFormatStr = false) { // Clear the error buffer.. errout.str(""); settings.severity.clear(); settings.severity.enable(Severity::warning); settings.severity.enable(Severity::style); if (portability) settings.severity.enable(Severity::portability); settings.certainty.setEnabled(Certainty::inconclusive, inconclusive); settings.platform(platform); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. CheckIO checkIO(&tokenizer, &settings, this); checkIO.checkWrongPrintfScanfArguments(); if (!onlyFormatStr) { checkIO.checkCoutCerrMisusage(); checkIO.checkFileUsage(); checkIO.invalidScanf(); } } void coutCerrMisusage() { check( "void foo() {\n" " std::cout << std::cout;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); check( "void foo() {\n" " std::cout << (std::cout);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); check( "void foo() {\n" " std::cout << \"xyz\" << std::cout;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); check( "void foo(int i) {\n" " std::cout << i << std::cerr;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cerr'.\n", errout.str()); check( "void foo() {\n" " std::cout << \"xyz\";\n" " std::cout << \"xyz\";\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " std::cout << std::cout.good();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " unknownObject << std::cout;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " MACRO(std::cout <<, << std::cout)\n" "}"); ASSERT_EQUALS("", errout.str()); } void wrongMode_simple() { // Read mode check("void foo(FILE*& f) {\n" " f = fopen(name, \"r\");\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfopen(name, L\"r\");\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " _wfopen_s(&f, name, L\"r\");\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"r+\");\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfopen(name, L\"r+\");\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " _wfopen_s(&f, name, L\"r+\");\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = tmpfile();\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); // Write mode check("void foo(FILE*& f) {\n" " f = fopen(name, \"w\");\n" " fwrite(buffer, 5, 6, f);\n" " rewind(f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"w+\");\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = tmpfile();\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // Append mode check("void foo(FILE*& f) {\n" " f = fopen(name, \"a\");\n" " fwrite(buffer, 5, 6, f);\n" " rewind(f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n" "[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"a+\");\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // Variable declared locally check("void foo() {\n" " FILE* f = fopen(name, \"r\");\n" " fwrite(buffer, 5, 6, f);\n" " fclose(f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); // Call unknown function check("void foo(FILE*& f) {\n" " f = fopen(name, \"a\");\n" " fwrite(buffer, 5, 6, f);\n" " bar(f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // freopen and tmpfile check("void foo(FILE*& f) {\n" " f = freopen(name, \"r\", f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfreopen(name, L\"r\", f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen(name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen(name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfreopen_s(&f, name, L\"r\", f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen_s(&f, name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen_s(&f, name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); // Crash tests check("void foo(FILE*& f) {\n" " f = fopen(name, mode);\n" // No assertion failure (#3830) " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void fopen(std::string const &filepath, std::string const &mode);"); // #3832 } void wrongMode_complex() { check("void foo(FILE* f) {\n" " if(a) f = fopen(name, \"w\");\n" " else f = fopen(name, \"r\");\n" " if(a) fwrite(buffer, 5, 6, f);\n" " else fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f;\n" " if(a) f = fopen(name, \"w\");\n" " else f = fopen(name, \"r\");\n" " if(a) fwrite(buffer, 5, 6, f);\n" " else fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f = fopen(name, \"w\");\n" " if(a) fwrite(buffer, 5, 6, f);\n" " else fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); } void useClosedFile() { check("void foo(FILE*& f) {\n" " fclose(f);\n" " fwrite(buffer, 5, 6, f);\n" " clearerr(f);\n" " fread(buffer, 5, 6, f);\n" " ungetc('a', f);\n" " ungetwc(L'a', f);\n" " rewind(f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n" "[test.cpp:4]: (error) Used file that is not opened.\n" "[test.cpp:5]: (error) Used file that is not opened.\n" "[test.cpp:6]: (error) Used file that is not opened.\n" "[test.cpp:7]: (error) Used file that is not opened.\n" "[test.cpp:8]: (error) Used file that is not opened.\n", errout.str()); check("void foo(FILE*& f) {\n" " if(!ferror(f)) {\n" " fclose(f);\n" " return;" " }\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " fclose(f);\n" " f = fopen(name, \"r\");\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"r\");\n" " f = g;\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f;\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n", errout.str()); check("void foo() {\n" " FILE* f(stdout);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // #3965 " FILE* f[3];\n" " f[0] = fopen(name, mode);\n" " fclose(f[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4368: multiple functions check("static FILE *fp = NULL;\n" "\n" "void close()\n" "{\n" " fclose(fp);\n" "}\n" "\n" "void dump()\n" "{\n" " if (fp == NULL) return;\n" " fprintf(fp, \"Here's the output.\\n\");\n" "}\n" "\n" "int main()\n" "{\n" " fp = fopen(\"test.txt\", \"w\");\n" " dump();\n" " close();\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static FILE *fp = NULL;\n" "\n" "void close()\n" "{\n" " fclose(fp);\n" "}\n" "\n" "void dump()\n" "{\n" " fclose(fp);\n" " fprintf(fp, \"Here's the output.\\n\");\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Used file that is not opened.\n", errout.str()); // #4466 check("void chdcd_parse_nero(FILE *infile) {\n" " switch (mode) {\n" " case 0x0300:\n" " fclose(infile);\n" " return;\n" " case 0x0500:\n" " fclose(infile);\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void chdcd_parse_nero(FILE *infile) {\n" " switch (mode) {\n" " case 0x0300:\n" " fclose(infile);\n" " exit(0);\n" " case 0x0500:\n" " fclose(infile);\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #4649 check("void foo() {\n" " struct {FILE *f1; FILE *f2;} a;\n" " a.f1 = fopen(name,mode);\n" " a.f2 = fopen(name,mode);\n" " fclose(a.f1);\n" " fclose(a.f2);\n" "}"); ASSERT_EQUALS("", errout.str()); // #1473 check("void foo() {\n" " FILE *a = fopen(\"aa\", \"r\");\n" " while (fclose(a)) {}\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n", "", errout.str()); // #6823 check("void foo() {\n" " FILE f[2];\n" " f[0] = fopen(\"1\", \"w\");\n" " f[1] = fopen(\"2\", \"w\");\n" " fclose(f[0]);\n" " fclose(f[1]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void fileIOwithoutPositioning() { check("void foo(FILE* f) {\n" " fwrite(buffer, 5, 6, f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); check("void foo(FILE* f, bool read) {\n" " if(read)\n" " fread(buffer, 5, 6, f);\n" " else\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fflush(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fsetpos(f, pos);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fseek(f, 0, SEEK_SET);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " long pos = ftell(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); // #6452 - member functions check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " void seek(long offset, Position p);\n" " FileStreamPrivate *d;\n" "};\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" " seek(writePosition);\n" " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " FileStreamPrivate *d;\n" "};\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" " unknown(writePosition);\n" " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " FileStreamPrivate *d;\n" "};\n" "void known(int);\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" " known(writePosition);\n" " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " FileStreamPrivate *d;\n" "};\n" "void known(int);\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(X::data(), 1, bufferLength, d->file);\n" " known(writePosition);\n" " fwrite(X::data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); } void seekOnAppendedFile() { check("void foo() {\n" " FILE* f = fopen(\"\", \"a+\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f = fopen(\"\", \"w\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f = fopen(\"\", \"a\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n", errout.str()); check("void foo() {\n" " FILE* f = fopen(\"\", \"a\");\n" " fflush(f);\n" "}"); ASSERT_EQUALS("", errout.str()); // #5578 check("void foo() {\n" " FILE* f = fopen(\"\", \"a\");\n" " fclose(f);\n" " f = fopen(\"\", \"r\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6566 } void fflushOnInputStream() { check("void foo()\n" "{\n" " fflush(stdin);\n" "}", false, true); ASSERT_EQUALS("[test.cpp:3]: (portability) fflush() called on input stream 'stdin' may result in undefined behaviour on non-linux systems.\n", errout.str()); check("void foo()\n" "{\n" " fflush(stdout);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(path, \"r\");\n" " fflush(f);\n" "}", false, true); ASSERT_EQUALS("[test.cpp:3]: (portability) fflush() called on input stream 'f' may result in undefined behaviour on non-linux systems.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(path, \"w\");\n" " fflush(f);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " fflush(f);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); } void incompatibleFileOpen() { check("void foo() {\n" " FILE *f1 = fopen(\"tmp\", \"wt\");\n" " FILE *f2 = fopen(\"tmp\", \"rt\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) The file '\"tmp\"' is opened for read and write access at the same time on different streams\n", errout.str()); } void testScanf1() { check("void foo() {\n" " int a, b;\n" " FILE *file = fopen(\"test\", \"r\");\n" " a = fscanf(file, \"aa %s\", bar);\n" " b = scanf(\"aa %S\", bar);\n" " b = scanf(\"aa %ls\", bar);\n" " sscanf(foo, \"%[^~]\", bar);\n" " scanf(\"%dx%s\", &b, bar);\n" " fclose(file);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) fscanf() without field width limits can crash with huge input data.\n" "[test.cpp:5]: (warning) scanf() without field width limits can crash with huge input data.\n" "[test.cpp:6]: (warning) scanf() without field width limits can crash with huge input data.\n" "[test.cpp:7]: (warning) sscanf() without field width limits can crash with huge input data.\n" "[test.cpp:8]: (warning) scanf() without field width limits can crash with huge input data.\n", errout.str()); } void testScanf2() { check("void foo() {\n" " scanf(\"%5s\", bar);\n" // Width specifier given " scanf(\"%5[^~]\", bar);\n" // Width specifier given " scanf(\"aa%%s\", bar);\n" // No %s " scanf(\"aa%d\", &a);\n" // No %s " scanf(\"aa%ld\", &a);\n" // No %s " scanf(\"%*[^~]\");\n" // Ignore input "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) scanf format string requires 0 parameters but 1 is given.\n", errout.str()); } void testScanf3() { // ticket #3494 check("void f() {\n" " char str[8];\n" " scanf(\"%7c\", str);\n" " scanf(\"%8c\", str);\n" " scanf(\"%9c\", str);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Width 9 given in format string (no. 1) is larger than destination buffer 'str[8]', use %8c to prevent overflowing it.\n", errout.str()); } void testScanf4() { // ticket #2553 check("void f()\n" "{\n" " char str [8];\n" " scanf (\"%70s\",str);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Width 70 given in format string (no. 1) is larger than destination buffer 'str[8]', use %7s to prevent overflowing it.\n", errout.str()); } void testScanf5() { // #10632 check("char s1[42], s2[42];\n" "void test() {\n" " scanf(\"%42s%42[a-z]\", s1, s2);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Width 42 given in format string (no. 1) is larger than destination buffer 's1[42]', use %41s to prevent overflowing it.\n" "[test.cpp:3]: (error) Width 42 given in format string (no. 2) is larger than destination buffer 's2[42]', use %41[a-z] to prevent overflowing it.\n", errout.str()); } #define TEST_SCANF_CODE(format, type) \ "void f(){" type " x; scanf(\"" format "\", &x);}" #define TEST_SCANF_ERR(format, formatStr, type) \ "[test.cpp:1]: (warning) " format " in format string (no. 1) requires '" formatStr " *' but the argument type is '" type " *'.\n" #define TEST_SCANF_ERR_AKA(format, formatStr, type, akaType) \ "[test.cpp:1]: (portability) " format " in format string (no. 1) requires '" formatStr " *' but the argument type is '" type " * {aka " akaType " *}'.\n" #define TEST_PRINTF_CODE(format, type) \ "void f(" type " x){printf(\"" format "\", x);}" #define TEST_PRINTF_ERR(format, requiredType, actualType) \ "[test.cpp:1]: (warning) " format " in format string (no. 1) requires '" requiredType "' but the argument type is '" actualType "'.\n" #define TEST_PRINTF_ERR_AKA(format, requiredType, actualType, akaType) \ "[test.cpp:1]: (portability) " format " in format string (no. 1) requires '" requiredType "' but the argument type is '" actualType " {aka " akaType "}'.\n" void testFormatStrNoWarn(const char *filename, unsigned int linenr, const char* code) { check(code, true, false, Settings::Unix32, true); assertEquals(filename, linenr, emptyString, errout.str()); check(code, true, false, Settings::Unix64, true); assertEquals(filename, linenr, emptyString, errout.str()); check(code, true, false, Settings::Win32A, true); assertEquals(filename, linenr, emptyString, errout.str()); check(code, true, false, Settings::Win64, true); assertEquals(filename, linenr, emptyString, errout.str()); } void testFormatStrWarn(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrString) { check(code, true, false, Settings::Unix32, true); assertEquals(filename, linenr, testScanfErrString, errout.str()); check(code, true, false, Settings::Unix64, true); assertEquals(filename, linenr, testScanfErrString, errout.str()); check(code, true, false, Settings::Win32A, true); assertEquals(filename, linenr, testScanfErrString, errout.str()); check(code, true, false, Settings::Win64, true); assertEquals(filename, linenr, testScanfErrString, errout.str()); } void testFormatStrWarnAka(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrAkaString, const char* testScanfErrAkaWin64String) { check(code, true, true, Settings::Unix32, true); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Unix64, true); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win32A, true); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win64, true); assertEquals(filename, linenr, testScanfErrAkaWin64String, errout.str()); } void testFormatStrWarnAkaWin64(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrAkaWin64String) { check(code, true, true, Settings::Unix32, true); assertEquals(filename, linenr, emptyString, errout.str()); check(code, true, true, Settings::Unix64, true); assertEquals(filename, linenr, emptyString, errout.str()); check(code, true, true, Settings::Win32A, true); assertEquals(filename, linenr, emptyString, errout.str()); check(code, true, true, Settings::Win64, true); assertEquals(filename, linenr, testScanfErrAkaWin64String, errout.str()); } void testFormatStrWarnAkaWin32(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrAkaString) { check(code, true, true, Settings::Unix32, true); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Unix64, true); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win32A, true); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win64, true); assertEquals(filename, linenr, emptyString, errout.str()); } #define TEST_SCANF_NOWARN(FORMAT, FORMATSTR, TYPE) \ testFormatStrNoWarn(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE)) #define TEST_SCANF_WARN(FORMAT, FORMATSTR, TYPE) \ testFormatStrWarn(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR(FORMAT, FORMATSTR, TYPE)) #define TEST_SCANF_WARN_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ testFormatStrWarnAka(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_SCANF_WARN_AKA_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ testFormatStrWarnAkaWin64(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_SCANF_WARN_AKA_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ testFormatStrWarnAkaWin32(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32)) #define TEST_PRINTF_NOWARN(FORMAT, FORMATSTR, TYPE) \ testFormatStrNoWarn(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE)) #define TEST_PRINTF_WARN(FORMAT, FORMATSTR, TYPE) \ testFormatStrWarn(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR(FORMAT, FORMATSTR, TYPE)) #define TEST_PRINTF_WARN_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ testFormatStrWarnAka(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_PRINTF_WARN_AKA_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ testFormatStrWarnAkaWin64(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_PRINTF_WARN_AKA_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ testFormatStrWarnAkaWin32(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32)) void testScanfArgument() { check("void foo() {\n" " scanf(\"%1d\", &foo);\n" " sscanf(bar, \"%1d\", &foo);\n" " scanf(\"%1u%1u\", &foo, bar());\n" " scanf(\"%*1x %1x %29s\", &count, KeyName);\n" // #3373 " fscanf(f, \"%7ms\", &ref);\n" // #3461 " sscanf(ip_port, \"%*[^:]:%4d\", &port);\n" // #3468 "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " scanf(\"\", &foo);\n" " scanf(\"%1d\", &foo, &bar);\n" " fscanf(bar, \"%1d\", &foo, &bar);\n" " scanf(\"%*1x %1x %29s\", &count, KeyName, foo);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) scanf format string requires 0 parameters but 1 is given.\n" "[test.cpp:3]: (warning) scanf format string requires 1 parameter but 2 are given.\n" "[test.cpp:4]: (warning) fscanf format string requires 1 parameter but 2 are given.\n" "[test.cpp:5]: (warning) scanf format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " scanf(\"%1d\");\n" " scanf(\"%1u%1u\", bar());\n" " sscanf(bar, \"%1d%1d\", &foo);\n" " scanf(\"%*1x %1x %29s\", &count);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) scanf format string requires 1 parameter but only 0 are given.\n" "[test.cpp:3]: (error) scanf format string requires 2 parameters but only 1 is given.\n" "[test.cpp:4]: (error) sscanf format string requires 2 parameters but only 1 is given.\n" "[test.cpp:5]: (error) scanf format string requires 2 parameters but only 1 is given.\n", errout.str()); check("void foo() {\n" " char input[10];\n" " char output[5];\n" " sscanf(input, \"%3s\", output);\n" " sscanf(input, \"%4s\", output);\n" " sscanf(input, \"%5s\", output);\n" "}", false); ASSERT_EQUALS("[test.cpp:6]: (error) Width 5 given in format string (no. 1) is larger than destination buffer 'output[5]', use %4s to prevent overflowing it.\n", errout.str()); check("void foo() {\n" " char input[10];\n" " char output[5];\n" " sscanf(input, \"%s\", output);\n" " sscanf(input, \"%3s\", output);\n" " sscanf(input, \"%4s\", output);\n" " sscanf(input, \"%5s\", output);\n" "}", true); ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Width 3 given in format string (no. 1) is smaller than destination buffer 'output[5]'.\n" "[test.cpp:7]: (error) Width 5 given in format string (no. 1) is larger than destination buffer 'output[5]', use %4s to prevent overflowing it.\n" "[test.cpp:4]: (warning) sscanf() without field width limits can crash with huge input data.\n", errout.str()); check("void foo() {\n" " const size_t BUFLENGTH(2048);\n" " typedef char bufT[BUFLENGTH];\n" " bufT line= {0};\n" " bufT projectId= {0};\n" " const int scanrc=sscanf(line, \"Project(\\\"{%36s}\\\")\", projectId);\n" " sscanf(input, \"%5s\", output);\n" "}", true); ASSERT_EQUALS("[test.cpp:6]: (warning, inconclusive) Width 36 given in format string (no. 1) is smaller than destination buffer 'projectId[2048]'.\n", errout.str()); check("void foo(unsigned int i) {\n" " scanf(\"%h\", &i);\n" " scanf(\"%hh\", &i);\n" " scanf(\"%l\", &i);\n" " scanf(\"%ll\", &i);\n" " scanf(\"%j\", &i);\n" " scanf(\"%z\", &i);\n" " scanf(\"%t\", &i);\n" " scanf(\"%L\", &i);\n" " scanf(\"%I\", &i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); // Unrecognized (and non-existent in standard library) specifiers. // Perhaps should emit warnings TEST_SCANF_NOWARN("%jb", "intmax_t", "intmax_t"); TEST_SCANF_NOWARN("%jw", "uintmax_t", "uintmax_t"); TEST_SCANF_NOWARN("%zr", "size_t", "size_t"); TEST_SCANF_NOWARN("%tm", "ptrdiff_t", "ptrdiff_t"); TEST_SCANF_NOWARN("%La", "long double", "long double"); TEST_SCANF_NOWARN("%zv", "std::size_t", "std::size_t"); TEST_SCANF_NOWARN("%tp", "std::ptrdiff_t", "std::ptrdiff_t"); TEST_SCANF_WARN("%u", "unsigned int", "bool"); TEST_SCANF_WARN("%u", "unsigned int", "char"); TEST_SCANF_WARN("%u", "unsigned int", "signed char"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned char"); TEST_SCANF_WARN("%u", "unsigned int", "signed short"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned short"); TEST_SCANF_WARN("%u", "unsigned int", "signed int"); TEST_SCANF_NOWARN("%u", "unsigned int", "unsigned int"); TEST_SCANF_WARN("%u", "unsigned int", "signed long"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned long"); TEST_SCANF_WARN("%u", "unsigned int", "signed long long"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned long long"); TEST_SCANF_WARN("%u", "unsigned int", "float"); TEST_SCANF_WARN("%u", "unsigned int", "double"); TEST_SCANF_WARN("%u", "unsigned int", "long double"); TEST_SCANF_WARN("%u", "unsigned int", "void *"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%u\", \"s3\");\n" " scanf(\"%u\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%u\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'signed long'.\n", errout.str()); TEST_SCANF_WARN("%lu","unsigned long","bool"); TEST_SCANF_WARN("%lu","unsigned long","char"); TEST_SCANF_WARN("%lu","unsigned long","signed char"); TEST_SCANF_WARN("%lu","unsigned long","unsigned char"); TEST_SCANF_WARN("%lu","unsigned long","signed short"); TEST_SCANF_WARN("%lu","unsigned long","unsigned short"); TEST_SCANF_WARN("%lu","unsigned long","signed int"); TEST_SCANF_WARN("%lu","unsigned long","unsigned int"); TEST_SCANF_WARN("%lu","unsigned long","signed long"); TEST_SCANF_NOWARN("%lu","unsigned long","unsigned long"); TEST_SCANF_WARN("%lu","unsigned long","signed long long"); TEST_SCANF_WARN("%lu","unsigned long","unsigned long long"); TEST_SCANF_WARN("%lu","unsigned long","float"); TEST_SCANF_WARN("%lu","unsigned long","double"); TEST_SCANF_WARN("%lu","unsigned long","long double"); TEST_SCANF_WARN("%lu","unsigned long","void *"); TEST_SCANF_WARN_AKA("%lu","unsigned long","size_t","unsigned long","unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","uintmax_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lu","unsigned long","uintptr_t", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::size_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lu","unsigned long","std::uintptr_t", "unsigned long long"); TEST_SCANF_WARN("%lx","unsigned long","bool"); TEST_SCANF_WARN("%lx","unsigned long","char"); TEST_SCANF_WARN("%lx","unsigned long","signed char"); TEST_SCANF_WARN("%lx","unsigned long","unsigned char"); TEST_SCANF_WARN("%lx","unsigned long","signed short"); TEST_SCANF_WARN("%lx","unsigned long","unsigned short"); TEST_SCANF_WARN("%lx","unsigned long","signed int"); TEST_SCANF_WARN("%lx","unsigned long","unsigned int"); TEST_SCANF_WARN("%lx","unsigned long","signed long"); TEST_SCANF_NOWARN("%lx","unsigned long","unsigned long"); TEST_SCANF_WARN("%lx","unsigned long","signed long long"); TEST_SCANF_WARN("%lx","unsigned long","unsigned long long"); TEST_SCANF_WARN("%lx","unsigned long","float"); TEST_SCANF_WARN("%lx","unsigned long","double"); TEST_SCANF_WARN("%lx","unsigned long","long double"); TEST_SCANF_WARN("%lx","unsigned long","void *"); TEST_SCANF_WARN_AKA("%lx","unsigned long","size_t","unsigned long","unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","uintmax_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lx","unsigned long","uintptr_t", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::size_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lx","unsigned long","std::uintptr_t", "unsigned long long"); TEST_SCANF_WARN("%ld","long","bool"); TEST_SCANF_WARN("%ld","long","char"); TEST_SCANF_NOWARN("%ld","long","signed long"); TEST_SCANF_WARN("%ld","long","unsigned long"); TEST_SCANF_WARN("%ld","long","void *"); TEST_SCANF_WARN_AKA("%ld","long","size_t","unsigned long","unsigned long long"); TEST_SCANF_WARN_AKA("%ld","long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ld","long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ld","long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%ld","long","std::intptr_t", "signed long long"); TEST_SCANF_WARN_AKA("%ld","long","std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%llu","unsigned long long","bool"); TEST_SCANF_WARN("%llu","unsigned long long","char"); TEST_SCANF_WARN("%llu","unsigned long long","signed char"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned char"); TEST_SCANF_WARN("%llu","unsigned long long","signed short"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned short"); TEST_SCANF_WARN("%llu","unsigned long long","signed int"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned int"); TEST_SCANF_WARN("%llu","unsigned long long","signed long"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned long"); TEST_SCANF_WARN("%llu","unsigned long long","signed long long"); TEST_SCANF_NOWARN("%llu","unsigned long long","unsigned long long"); TEST_SCANF_WARN("%llu","unsigned long long","float"); TEST_SCANF_WARN("%llu","unsigned long long","double"); TEST_SCANF_WARN("%llu","unsigned long long","long double"); TEST_SCANF_WARN("%llu","unsigned long long","void *"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llu","unsigned long long","uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llu","unsigned long long","std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%llx","unsigned long long","bool"); TEST_SCANF_WARN("%llx","unsigned long long","char"); TEST_SCANF_WARN("%llx","unsigned long long","signed char"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned char"); TEST_SCANF_WARN("%llx","unsigned long long","signed short"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned short"); TEST_SCANF_WARN("%llx","unsigned long long","signed int"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned int"); TEST_SCANF_WARN("%llx","unsigned long long","signed long"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned long"); TEST_SCANF_WARN("%llx","unsigned long long","signed long long"); TEST_SCANF_NOWARN("%llx","unsigned long long","unsigned long long"); TEST_SCANF_WARN("%llx","unsigned long long","float"); TEST_SCANF_WARN("%llx","unsigned long long","double"); TEST_SCANF_WARN("%llx","unsigned long long","long double"); TEST_SCANF_WARN("%llx","unsigned long long","void *"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llx","unsigned long long","uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llx","unsigned long long","std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%lld","long long","bool"); TEST_SCANF_WARN("%lld","long long","char"); TEST_SCANF_NOWARN("%lld","long long","long long"); TEST_SCANF_WARN("%lld","long long","unsigned long long"); TEST_SCANF_WARN("%lld","long long","void *"); TEST_SCANF_WARN_AKA("%lld","long long","size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lld","long long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lld","long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lld","long long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%lld","long long","std::intptr_t", "signed long"); TEST_SCANF_WARN("%hu", "unsigned short", "bool"); TEST_SCANF_WARN("%hu", "unsigned short", "char"); TEST_SCANF_WARN("%hu", "unsigned short", "signed char"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned char"); TEST_SCANF_WARN("%hu", "unsigned short", "signed short"); TEST_SCANF_NOWARN("%hu", "unsigned short", "unsigned short"); TEST_SCANF_WARN("%hu", "unsigned short", "signed int"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned int"); TEST_SCANF_WARN("%hu", "unsigned short", "signed long"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned long"); TEST_SCANF_WARN("%hu", "unsigned short", "signed long long"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned long long"); TEST_SCANF_WARN("%hu", "unsigned short", "float"); TEST_SCANF_WARN("%hu", "unsigned short", "double"); TEST_SCANF_WARN("%hu", "unsigned short", "long double"); TEST_SCANF_WARN("%hu", "unsigned short", "void *"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hx", "unsigned short", "bool"); TEST_SCANF_WARN("%hx", "unsigned short", "char"); TEST_SCANF_WARN("%hx", "unsigned short", "signed char"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned char"); TEST_SCANF_WARN("%hx", "unsigned short", "signed short"); TEST_SCANF_NOWARN("%hx", "unsigned short", "unsigned short"); TEST_SCANF_WARN("%hx", "unsigned short", "signed int"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned int"); TEST_SCANF_WARN("%hx", "unsigned short", "signed long"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned long"); TEST_SCANF_WARN("%hx", "unsigned short", "signed long long"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned long long"); TEST_SCANF_WARN("%hx", "unsigned short", "float"); TEST_SCANF_WARN("%hx", "unsigned short", "double"); TEST_SCANF_WARN("%hx", "unsigned short", "long double"); TEST_SCANF_WARN("%hx", "unsigned short", "void *"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hd", "short", "bool"); TEST_SCANF_WARN("%hd", "short", "char"); TEST_SCANF_WARN("%hd", "short", "signed char"); TEST_SCANF_WARN("%hd", "short", "unsigned char"); TEST_SCANF_NOWARN("%hd", "short", "signed short"); TEST_SCANF_WARN("%hd", "short", "unsigned short"); TEST_SCANF_WARN("%hd", "short", "signed int"); TEST_SCANF_WARN("%hd", "short", "unsigned int"); TEST_SCANF_WARN("%hd", "short", "signed long"); TEST_SCANF_WARN("%hd", "short", "void *"); TEST_SCANF_WARN_AKA("%hd", "short", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hd", "short", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN("%hhu", "unsigned char", "bool"); TEST_SCANF_WARN("%hhu", "unsigned char", "char"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed char"); TEST_SCANF_NOWARN("%hhu", "unsigned char", "unsigned char"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed short"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned short"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed int"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned int"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed long"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned long"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed long long"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned long long"); TEST_SCANF_WARN("%hhu", "unsigned char", "float"); TEST_SCANF_WARN("%hhu", "unsigned char", "double"); TEST_SCANF_WARN("%hhu", "unsigned char", "long double"); TEST_SCANF_WARN("%hhu", "unsigned char", "void *"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hhx", "unsigned char", "bool"); TEST_SCANF_WARN("%hhx", "unsigned char", "char"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed char"); TEST_SCANF_NOWARN("%hhx", "unsigned char", "unsigned char"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed short"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned short"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed int"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned int"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed long"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned long"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed long long"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned long long"); TEST_SCANF_WARN("%hhx", "unsigned char", "float"); TEST_SCANF_WARN("%hhx", "unsigned char", "double"); TEST_SCANF_WARN("%hhx", "unsigned char", "long double"); TEST_SCANF_WARN("%hhx", "unsigned char", "void *"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hhd", "char", "bool"); TEST_SCANF_NOWARN("%hhd", "char", "char"); TEST_SCANF_NOWARN("%hhd", "char", "signed char"); TEST_SCANF_WARN("%hhd", "char", "unsigned char"); TEST_SCANF_WARN("%hhd", "char", "signed short"); TEST_SCANF_WARN("%hhd", "char", "void *"); TEST_SCANF_WARN_AKA("%hhd", "char", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "bool"); TEST_SCANF_WARN("%Lu", "unsigned long long", "char"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed char"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned char"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed short"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned short"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed int"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned int"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed long long"); TEST_SCANF_NOWARN("%Lu", "unsigned long long", "unsigned long long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "float"); TEST_SCANF_WARN("%Lu", "unsigned long long", "double"); TEST_SCANF_WARN("%Lu", "unsigned long long", "long double"); TEST_SCANF_WARN("%Lu", "unsigned long long", "void *"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "bool"); TEST_SCANF_WARN("%Lx", "unsigned long long", "char"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed char"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned char"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed short"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned short"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed int"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned int"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed long long"); TEST_SCANF_NOWARN("%Lx", "unsigned long long", "unsigned long long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "float"); TEST_SCANF_WARN("%Lx", "unsigned long long", "double"); TEST_SCANF_WARN("%Lx", "unsigned long long", "long double"); TEST_SCANF_WARN("%Lx", "unsigned long long", "void *"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%Ld", "long long", "bool"); TEST_SCANF_WARN("%Ld", "long long", "char"); TEST_SCANF_WARN("%Ld", "long long", "signed char"); TEST_SCANF_WARN("%Ld", "long long", "unsigned char"); TEST_SCANF_WARN("%Ld", "long long", "signed short"); TEST_SCANF_WARN("%Ld", "long long", "unsigned short"); TEST_SCANF_WARN("%Ld", "long long", "signed int"); TEST_SCANF_WARN("%Ld", "long long", "unsigned int"); TEST_SCANF_WARN("%Ld", "long long", "signed long"); TEST_SCANF_WARN("%Ld", "long long", "unsigned long"); TEST_SCANF_NOWARN("%Ld", "long long", "signed long long"); TEST_SCANF_WARN("%Ld", "long long", "unsigned long long"); TEST_SCANF_WARN("%Ld", "long long", "float"); TEST_SCANF_WARN("%Ld", "long long", "double"); TEST_SCANF_WARN("%Ld", "long long", "long double"); TEST_SCANF_WARN("%Ld", "long long", "void *"); TEST_SCANF_WARN_AKA("%Ld", "long long", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "ssize_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "ptrdiff_t", "signed long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "intmax_t", "signed long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "std::ssize_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "std::ptrdiff_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "std::intptr_t", "signed long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%Ld\", \"s3\");\n" " scanf(\"%Ld\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(int i) {\n" " scanf(\"%Ld\", i);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'signed int'.\n", errout.str()); TEST_SCANF_WARN("%ju", "uintmax_t", "bool"); TEST_SCANF_WARN("%ju", "uintmax_t", "char"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed char"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned char"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed short"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned short"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed int"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned int"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed long"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned long"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed long long"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned long long"); TEST_SCANF_WARN("%ju", "uintmax_t", "float"); TEST_SCANF_WARN("%ju", "uintmax_t", "double"); TEST_SCANF_WARN("%ju", "uintmax_t", "long double"); TEST_SCANF_WARN("%ju", "uintmax_t", "void *"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%ju", "uintmax_t", "uintmax_t"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%ju", "uintmax_t", "std::uintmax_t"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%jx", "uintmax_t", "bool"); TEST_SCANF_WARN("%jx", "uintmax_t", "char"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed char"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned char"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed short"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned short"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed int"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned int"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed long"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned long"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed long long"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned long long"); TEST_SCANF_WARN("%jx", "uintmax_t", "float"); TEST_SCANF_WARN("%jx", "uintmax_t", "double"); TEST_SCANF_WARN("%jx", "uintmax_t", "long double"); TEST_SCANF_WARN("%jx", "uintmax_t", "void *"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%jx", "uintmax_t", "uintmax_t"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%jx", "uintmax_t", "std::uintmax_t"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%jd", "intmax_t", "long double"); TEST_SCANF_WARN("%jd", "intmax_t", "void *"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%jd", "intmax_t", "intmax_t"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%jd", "intmax_t", "std::intmax_t"); TEST_SCANF_WARN("%zu", "size_t", "bool"); TEST_SCANF_WARN("%zu", "size_t", "char"); TEST_SCANF_WARN("%zu", "size_t", "signed char"); TEST_SCANF_WARN("%zu", "size_t", "unsigned char"); TEST_SCANF_WARN("%zu", "size_t", "signed short"); TEST_SCANF_WARN("%zu", "size_t", "unsigned short"); TEST_SCANF_WARN("%zu", "size_t", "signed int"); TEST_SCANF_WARN("%zu", "size_t", "unsigned int"); TEST_SCANF_WARN("%zu", "size_t", "signed long"); TEST_SCANF_WARN("%zu", "size_t", "unsigned long"); TEST_SCANF_WARN("%zu", "size_t", "signed long long"); TEST_SCANF_WARN("%zu", "size_t", "unsigned long long"); TEST_SCANF_WARN("%zu", "size_t", "float"); TEST_SCANF_WARN("%zu", "size_t", "double"); TEST_SCANF_WARN("%zu", "size_t", "long double"); TEST_SCANF_WARN("%zu", "size_t", "void *"); TEST_SCANF_NOWARN("%zu", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%zu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%zu", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%zx", "size_t", "bool"); TEST_SCANF_WARN("%zx", "size_t", "char"); TEST_SCANF_WARN("%zx", "size_t", "signed char"); TEST_SCANF_WARN("%zx", "size_t", "unsigned char"); TEST_SCANF_WARN("%zx", "size_t", "signed short"); TEST_SCANF_WARN("%zx", "size_t", "unsigned short"); TEST_SCANF_WARN("%zx", "size_t", "signed int"); TEST_SCANF_WARN("%zx", "size_t", "unsigned int"); TEST_SCANF_WARN("%zx", "size_t", "signed long"); TEST_SCANF_WARN("%zx", "size_t", "unsigned long"); TEST_SCANF_WARN("%zx", "size_t", "signed long long"); TEST_SCANF_WARN("%zx", "size_t", "unsigned long long"); TEST_SCANF_WARN("%zx", "size_t", "float"); TEST_SCANF_WARN("%zx", "size_t", "double"); TEST_SCANF_WARN("%zx", "size_t", "long double"); TEST_SCANF_WARN("%zx", "size_t", "void *"); TEST_SCANF_NOWARN("%zx", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%zx", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%zx", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%zd", "ssize_t", "bool"); TEST_SCANF_WARN("%zd", "ssize_t", "signed short"); TEST_SCANF_WARN("%zd", "ssize_t", "void *"); TEST_SCANF_WARN_AKA("%zd", "ssize_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%zd", "ssize_t", "ssize_t"); TEST_SCANF_WARN_AKA("%zd", "ssize_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zi", "ssize_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "bool"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "char"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed char"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned char"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed short"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned short"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed int"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned int"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed long long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "float"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "double"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "long double"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "void *"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%tu", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "bool"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "char"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed char"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned char"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed short"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned short"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed int"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned int"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed long long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "float"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "double"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "long double"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "void *"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%tx", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%td", "ptrdiff_t", "long double"); TEST_SCANF_WARN("%td", "ptrdiff_t", "void *"); TEST_SCANF_NOWARN("%td", "ptrdiff_t", "ptrdiff_t"); TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Iu", "size_t", "bool"); TEST_SCANF_WARN("%Iu", "size_t", "char"); TEST_SCANF_WARN("%Iu", "size_t", "signed char"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned char"); TEST_SCANF_WARN("%Iu", "size_t", "signed short"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned short"); TEST_SCANF_WARN("%Iu", "size_t", "signed int"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned int"); TEST_SCANF_WARN("%Iu", "size_t", "signed long"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned long"); TEST_SCANF_WARN("%Iu", "size_t", "signed long long"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned long long"); TEST_SCANF_WARN("%Iu", "size_t", "float"); TEST_SCANF_WARN("%Iu", "size_t", "double"); TEST_SCANF_WARN("%Iu", "size_t", "long double"); TEST_SCANF_WARN("%Iu", "size_t", "void *"); TEST_SCANF_NOWARN("%Iu", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%Iu", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Ix", "size_t", "bool"); TEST_SCANF_WARN("%Ix", "size_t", "char"); TEST_SCANF_WARN("%Ix", "size_t", "signed char"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned char"); TEST_SCANF_WARN("%Ix", "size_t", "signed short"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned short"); TEST_SCANF_WARN("%Ix", "size_t", "signed int"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned int"); TEST_SCANF_WARN("%Ix", "size_t", "signed long"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned long"); TEST_SCANF_WARN("%Ix", "size_t", "signed long long"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned long long"); TEST_SCANF_WARN("%Ix", "size_t", "float"); TEST_SCANF_WARN("%Ix", "size_t", "double"); TEST_SCANF_WARN("%Ix", "size_t", "long double"); TEST_SCANF_WARN("%Ix", "size_t", "void *"); TEST_SCANF_NOWARN("%Ix", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%Ix", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "bool"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "char"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed char"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned char"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed short"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned short"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed int"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned int"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed long long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned long long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "float"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "double"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "long double"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "void *"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%Id", "ptrdiff_t", "ptrdiff_t"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%Id", "ptrdiff_t", "std::ptrdiff_t"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "bool"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "char"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed char"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned char"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed short"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned short"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed int"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned int"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed long long"); TEST_SCANF_NOWARN("%I64u", "unsigned __int64", "unsigned long long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "float"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "double"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "long double"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "void *"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "bool"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "char"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed char"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned char"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed short"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned short"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed int"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned int"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed long long"); TEST_SCANF_NOWARN("%I64x", "unsigned __int64", "unsigned long long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "float"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "double"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "long double"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "void *"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%I64x", "unsigned __int64", "unsigned __int64"); // TODO TEST_SCANF_WARN("%I64x", "unsigned __int64", "__int64"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%I64d", "__int64", "bool"); TEST_SCANF_WARN("%I64d", "__int64", "signed char"); TEST_SCANF_WARN("%I64d", "__int64", "unsigned char"); TEST_SCANF_WARN("%I64d", "__int64", "void *"); // TODO TEST_SCANF_WARN("%I64d", "__int64", "size_t"); TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "intmax_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "ssize_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "ptrdiff_t", "signed long"); TEST_SCANF_NOWARN("%I64d", "__int64", "__int64"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "bool"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "char"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed char"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned char"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed short"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned short"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed int"); TEST_SCANF_NOWARN("%I32u", "unsigned __int32", "unsigned int"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed long long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned long long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "float"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "double"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "long double"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "void *"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "bool"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "char"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed char"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned char"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed short"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned short"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed int"); TEST_SCANF_NOWARN("%I32x", "unsigned __int32", "unsigned int"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed long long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned long long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "float"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "double"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "long double"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "void *"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%I32d", "__int32", "bool"); TEST_SCANF_WARN("%I32d", "__int32", "void *"); TEST_SCANF_WARN_AKA("%I32d", "__int32", "size_t", "unsigned long", "unsigned long long"); //TODO TEST_SCANF_WARN_AKA_WIN32("%I32d", "__int32", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32d", "__int32", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%I32d", "__int32", "__int32"); TEST_SCANF_WARN("%d", "int", "bool"); TEST_SCANF_WARN("%d", "int", "char"); TEST_SCANF_WARN("%d", "int", "signed char"); TEST_SCANF_WARN("%d", "int", "unsigned char"); TEST_SCANF_WARN("%d", "int", "signed short"); TEST_SCANF_WARN("%d", "int", "unsigned short"); TEST_SCANF_NOWARN("%d", "int", "signed int"); TEST_SCANF_WARN("%d", "int", "unsigned int"); TEST_SCANF_WARN("%d", "int", "signed long"); TEST_SCANF_WARN("%d", "int", "unsigned long"); TEST_SCANF_WARN("%d", "int", "signed long long"); TEST_SCANF_WARN("%d", "int", "unsigned long long"); TEST_SCANF_WARN("%d", "int", "float"); TEST_SCANF_WARN("%d", "int", "double"); TEST_SCANF_WARN("%d", "int", "long double"); TEST_SCANF_WARN("%d", "int", "void *"); TEST_SCANF_WARN_AKA("%d", "int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%d\", \"s3\");\n" " scanf(\"%d\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%d\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'signed long'.\n", errout.str()); TEST_SCANF_WARN("%x", "unsigned int", "bool"); TEST_SCANF_WARN("%x", "unsigned int", "char"); TEST_SCANF_WARN("%x", "unsigned int", "signed char"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned char"); TEST_SCANF_WARN("%x", "unsigned int", "signed short"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned short"); TEST_SCANF_WARN("%x", "unsigned int", "signed int"); TEST_SCANF_NOWARN("%x", "unsigned int", "unsigned int"); TEST_SCANF_WARN("%x", "unsigned int", "signed long"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned long"); TEST_SCANF_WARN("%x", "unsigned int", "signed long long"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned long long"); TEST_SCANF_WARN("%x", "unsigned int", "float"); TEST_SCANF_WARN("%x", "unsigned int", "double"); TEST_SCANF_WARN("%x", "unsigned int", "long double"); TEST_SCANF_WARN("%x", "unsigned int", "void *"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%x\", \"s3\");\n" " scanf(\"%x\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%x\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'signed long'.\n", errout.str()); TEST_SCANF_WARN("%f", "float", "bool"); TEST_SCANF_WARN("%f", "float", "char"); TEST_SCANF_WARN("%f", "float", "signed char"); TEST_SCANF_WARN("%f", "float", "unsigned char"); TEST_SCANF_WARN("%f", "float", "signed short"); TEST_SCANF_WARN("%f", "float", "unsigned short"); TEST_SCANF_WARN("%f", "float", "signed int"); TEST_SCANF_WARN("%f", "float", "unsigned int"); TEST_SCANF_WARN("%f", "float", "signed long"); TEST_SCANF_WARN("%f", "float", "unsigned long"); TEST_SCANF_WARN("%f", "float", "signed long long"); TEST_SCANF_WARN("%f", "float", "unsigned long long"); TEST_SCANF_NOWARN("%f", "float", "float"); TEST_SCANF_WARN("%f", "float", "double"); TEST_SCANF_WARN("%f", "float", "long double"); TEST_SCANF_WARN("%f", "float", "void *"); TEST_SCANF_WARN_AKA("%f", "float", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%f\", \"s3\");\n" " scanf(\"%f\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(float f) {\n" " scanf(\"%f\", f);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'float'.\n", errout.str()); TEST_SCANF_WARN("%lf", "double", "bool"); TEST_SCANF_WARN("%lf", "double", "char"); TEST_SCANF_WARN("%lf", "double", "signed char"); TEST_SCANF_WARN("%lf", "double", "unsigned char"); TEST_SCANF_WARN("%lf", "double", "signed short"); TEST_SCANF_WARN("%lf", "double", "unsigned short"); TEST_SCANF_WARN("%lf", "double", "signed int"); TEST_SCANF_WARN("%lf", "double", "unsigned int"); TEST_SCANF_WARN("%lf", "double", "signed long"); TEST_SCANF_WARN("%lf", "double", "unsigned long"); TEST_SCANF_WARN("%lf", "double", "signed long long"); TEST_SCANF_WARN("%lf", "double", "unsigned long long"); TEST_SCANF_WARN("%lf", "double", "float"); TEST_SCANF_NOWARN("%lf", "double", "double"); TEST_SCANF_WARN("%lf", "double", "long double"); TEST_SCANF_WARN("%lf", "double", "void *"); TEST_SCANF_WARN_AKA("%lf", "double", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Lf", "long double", "bool"); TEST_SCANF_WARN("%Lf", "long double", "char"); TEST_SCANF_WARN("%Lf", "long double", "signed char"); TEST_SCANF_WARN("%Lf", "long double", "unsigned char"); TEST_SCANF_WARN("%Lf", "long double", "signed short"); TEST_SCANF_WARN("%Lf", "long double", "unsigned short"); TEST_SCANF_WARN("%Lf", "long double", "signed int"); TEST_SCANF_WARN("%Lf", "long double", "unsigned int"); TEST_SCANF_WARN("%Lf", "long double", "signed long"); TEST_SCANF_WARN("%Lf", "long double", "unsigned long"); TEST_SCANF_WARN("%Lf", "long double", "signed long long"); TEST_SCANF_WARN("%Lf", "long double", "unsigned long long"); TEST_SCANF_WARN("%Lf", "long double", "float"); TEST_SCANF_WARN("%Lf", "long double", "double"); TEST_SCANF_NOWARN("%Lf", "long double", "long double"); TEST_SCANF_WARN("%Lf", "long double", "void *"); TEST_SCANF_WARN_AKA("%Lf", "long double", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%n", "int", "bool"); TEST_SCANF_WARN("%n", "int", "char"); TEST_SCANF_WARN("%n", "int", "signed char"); TEST_SCANF_WARN("%n", "int", "unsigned char"); TEST_SCANF_WARN("%n", "int", "signed short"); TEST_SCANF_WARN("%n", "int", "unsigned short"); TEST_SCANF_NOWARN("%n", "int", "signed int"); TEST_SCANF_WARN("%n", "int", "unsigned int"); TEST_SCANF_WARN("%n", "int", "signed long"); TEST_SCANF_WARN("%n", "int", "unsigned long"); TEST_SCANF_WARN("%n", "int", "signed long long"); TEST_SCANF_WARN("%n", "int", "unsigned long long"); TEST_SCANF_WARN("%n", "int", "float"); TEST_SCANF_WARN("%n", "int", "double"); TEST_SCANF_WARN("%n", "int", "long double"); TEST_SCANF_WARN("%n", "int", "void *"); TEST_SCANF_WARN_AKA("%n", "int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%n\", \"s3\");\n" " scanf(\"%n\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%n\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed long'.\n", errout.str()); check("void g() {\n" // #5104 " myvector v1(1);\n" " scanf(\"%d\",&v1[0]);\n" " myvector v2(1);\n" " scanf(\"%u\",&v2[0]);\n" " myvector v3(1);\n" " scanf(\"%x\",&v3[0]);\n" " myvector v4(1);\n" " scanf(\"%lf\",&v4[0]);\n" " myvector v5(1);\n" " scanf(\"%10s\",v5[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); { const char * code = "void g() {\n" // #5348 " size_t s1;\n" " ptrdiff_t s2;\n" " ssize_t s3;\n" " scanf(\"%zd\", &s1);\n" " scanf(\"%zd\", &s2);\n" " scanf(\"%zd\", &s3);\n" "}\n"; const char* result("[test.cpp:5]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:6]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n"); const char* result_win64("[test.cpp:5]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:6]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n"); check(code, false, true, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, false, true, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, false, true, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, false, true, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { check("void g() {\n" " const char c[]=\"42\";\n" " scanf(\"%s\", c);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires a 'char *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) scanf() without field width limits can crash with huge input data.\n", errout.str()); } check("void f() {\n" // #7038 " scanf(\"%i\", \"abc\" + 1);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) %i in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n", errout.str()); } void testPrintfArgument() { check("void foo() {\n" " printf(\"%i\");\n" " printf(\"%i%s\", 123);\n" " printf(\"%i%s%d\", 0, bar());\n" " printf(\"%i%%%s%d\", 0, bar());\n" " printf(\"%idfd%%dfa%s%d\", 0, bar());\n" " fprintf(stderr,\"%u%s\");\n" " snprintf(str,10,\"%u%s\");\n" " sprintf(string1, \"%-*.*s\", 32, string2);\n" // #3364 " snprintf(a, 9, \"%s%d\", \"11223344\");\n" // #3655 "}"); ASSERT_EQUALS("[test.cpp:2]: (error) printf format string requires 1 parameter but only 0 are given.\n" "[test.cpp:3]: (error) printf format string requires 2 parameters but only 1 is given.\n" "[test.cpp:4]: (error) printf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:5]: (error) printf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:6]: (error) printf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:7]: (error) fprintf format string requires 2 parameters but only 0 are given.\n" "[test.cpp:8]: (error) snprintf format string requires 2 parameters but only 0 are given.\n" "[test.cpp:9]: (error) sprintf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:10]: (error) snprintf format string requires 2 parameters but only 1 is given.\n", errout.str()); check("void foo(char *str) {\n" " printf(\"\", 0);\n" " printf(\"%i\", 123, bar());\n" " printf(\"%i%s\", 0, bar(), 43123);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) printf format string requires 0 parameters but 1 is given.\n" "[test.cpp:3]: (warning) printf format string requires 1 parameter but 2 are given.\n" "[test.cpp:4]: (warning) printf format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" // swprintf exists as MSVC extension and as standard function: #4790 " swprintf(string1, L\"%i\", 32, string2);\n" // MSVC implementation " swprintf(string1, L\"%s%s\", L\"a\", string2);\n" // MSVC implementation " swprintf(string1, 6, L\"%i\", 32, string2);\n" // Standard implementation " swprintf(string1, 6, L\"%i%s\", 32, string2);\n" // Standard implementation "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) swprintf format string requires 1 parameter but 2 are given.\n" "[test.cpp:4]: (warning) swprintf format string requires 1 parameter but 2 are given.\n", errout.str()); check("void foo(char *str) {\n" " printf(\"%i\", 0);\n" " printf(\"%i%s\", 123, bar());\n" " printf(\"%i%s%d\", 0, bar(), 43123);\n" " printf(\"%i%%%s%d\", 0, bar(), 43123);\n" " printf(\"%idfd%%dfa%s%d\", 0, bar(), 43123);\n" " printf(\"%\"PRId64\"\", 123);\n" " fprintf(stderr,\"%\"PRId64\"\", 123);\n" " snprintf(str,10,\"%\"PRId64\"\", 123);\n" " fprintf(stderr, \"error: %m\");\n" // #3339 " printf(\"string: %.*s\", len, string);\n" // #3311 " fprintf(stderr, \"%*cText.\", indent, ' ');\n" // #3313 " sprintf(string1, \"%*\", 32);\n" // #3364 "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* s, const char* s2, std::string s3, int i) {\n" " printf(\"%s%s\", s, s2);\n" " printf(\"%s\", i);\n" " printf(\"%i%s\", i, i);\n" " printf(\"%s\", s3);\n" " printf(\"%s\", \"s4\");\n" " printf(\"%u\", s);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n" "[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout.str()); check("void foo(char* s, const char* s2, std::string s3, int i) {\n" " printf(\"%jd\", s);\n" " printf(\"%ji\", s);\n" " printf(\"%ju\", s2);\n" " printf(\"%jo\", s3);\n" " printf(\"%jx\", i);\n" " printf(\"%jX\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'char *'.\n" "[test.cpp:3]: (warning) %ji in format string (no. 1) requires 'intmax_t' but the argument type is 'char *'.\n" "[test.cpp:4]: (warning) %ju in format string (no. 1) requires 'uintmax_t' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %jo in format string (no. 1) requires 'uintmax_t' but the argument type is 'std::string'.\n" "[test.cpp:6]: (warning) %jx in format string (no. 1) requires 'uintmax_t' but the argument type is 'signed int'.\n" "[test.cpp:7]: (warning) %jX in format string (no. 1) requires 'uintmax_t' but the argument type is 'signed int'.\n", errout.str()); check("void foo(uintmax_t uim, std::string s3, unsigned int ui, int i) {\n" " printf(\"%ju\", uim);\n" " printf(\"%ju\", ui);\n" " printf(\"%jd\", ui);\n" " printf(\"%jd\", s3);\n" " printf(\"%jd\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %ju in format string (no. 1) requires 'uintmax_t' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'std::string'.\n" "[test.cpp:6]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'signed int'.\n", errout.str()); check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n" " printf(\"%n\", cpi);\n" " printf(\"%n\", ci);\n" " printf(\"%n\", i);\n" " printf(\"%n\", pi);\n" " printf(\"%n\", s);\n" " printf(\"%n\", \"s4\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed int'.\n" "[test.cpp:6]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'std::string'.\n" "[test.cpp:7]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n", errout.str()); check("void foo() {\n" " printf(\"%n\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, unsigned int u) {\n" " printf(\"%X\", f);\n" " printf(\"%c\", \"s4\");\n" " printf(\"%o\", d);\n" " printf(\"%x\", cpi);\n" " printf(\"%o\", b);\n" " printf(\"%X\", bp);\n" " printf(\"%X\", u);\n" " printf(\"%X\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %c in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %o in format string (no. 1) requires 'unsigned int' but the argument type is 'double'.\n" "[test.cpp:6]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const signed int *'.\n" "[test.cpp:8]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo(const char* cpc, char* pc) {\n" " printf(\"%x\", cpc);\n" " printf(\"%x\", pc);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:4]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout.str()); check("class foo {};\n" "void foo() {\n" " printf(\"%x\", L\"s5W\");\n" " printf(\"%X\", L\"s5W\");\n" " printf(\"%c\", L\"s5W\");\n" " printf(\"%o\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:4]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:5]: (warning) %c in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:6]: (warning) %o in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, unsigned int u, unsigned char uc) {\n" " printf(\"%i\", f);\n" " printf(\"%d\", \"s4\");\n" " printf(\"%d\", d);\n" " printf(\"%d\", u);\n" " printf(\"%d\", cpi);\n" " printf(\"%i\", b);\n" " printf(\"%i\", bp);\n" " printf(\"%i\", uc);\n" // char is smaller than int, so there shouldn't be a problem "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'double'.\n" "[test.cpp:6]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:7]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const signed int *'.\n" "[test.cpp:9]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo() {\n" " printf(\"%i\", L\"s5W\");\n" " printf(\"%d\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) {\n" " printf(\"%u\", f);\n" " printf(\"%u\", \"s4\");\n" " printf(\"%u\", d);\n" " printf(\"%u\", i);\n" " printf(\"%u\", cpi);\n" " printf(\"%u\", b);\n" " printf(\"%u\", bp);\n" " printf(\"%u\", bo);\n" // bool shouldn't have a negative sign "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'double'.\n" "[test.cpp:6]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const signed int *'.\n" "[test.cpp:9]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) {\n" " printf(\"%u\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, char c) {\n" " printf(\"%p\", f);\n" " printf(\"%p\", c);\n" " printf(\"%p\", bp);\n" " printf(\"%p\", cpi);\n" " printf(\"%p\", b);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %p in format string (no. 1) requires an address but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %p in format string (no. 1) requires an address but the argument type is 'char'.\n", errout.str()); check("class foo {};\n" "void foo(char* pc, const char* cpc, wchar_t* pwc, const wchar_t* cpwc) {\n" " printf(\"%p\", pc);\n" " printf(\"%p\", cpc);\n" " printf(\"%p\", pwc);\n" " printf(\"%p\", cpwc);\n" " printf(\"%p\", \"s4\");\n" " printf(\"%p\", L\"s5W\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n" " printf(\"%e\", f);\n" " printf(\"%E\", \"s4\");\n" " printf(\"%f\", cpi);\n" " printf(\"%G\", bp);\n" " printf(\"%f\", d);\n" " printf(\"%f\", b);\n" " printf(\"%f\", (float)cpi);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const signed int *'.\n" "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo(const char* cpc, char* pc) {\n" " printf(\"%e\", cpc);\n" " printf(\"%E\", pc);\n" " printf(\"%f\", cpc);\n" " printf(\"%G\", pc);\n" " printf(\"%f\", pc);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'char *'.\n" "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'char *'.\n" "[test.cpp:7]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'char *'.\n", errout.str()); check("class foo {};\n" "void foo() {\n" " printf(\"%e\", L\"s5W\");\n" " printf(\"%E\", L\"s5W\");\n" " printf(\"%f\", L\"s5W\");\n" " printf(\"%G\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo;\n" "void foo(foo f) {\n" " printf(\"%u\", f);\n" " printf(\"%f\", f);\n" " printf(\"%p\", f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'foo'.\n" "[test.cpp:5]: (warning) %p in format string (no. 1) requires an address but the argument type is 'foo'.\n", errout.str()); // Ticket #4189 (Improve check (printf("%l") not detected)) tests (according to C99 7.19.6.1.7) // False positive tests check("void foo(signed char sc, unsigned char uc, short int si, unsigned short int usi) {\n" " printf(\"%hhx %hhd\", sc, uc);\n" " printf(\"%hd %hu\", si, usi);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhx in format string (no. 1) requires 'unsigned char' but the argument type is 'signed char'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n", errout.str()); check("void foo(long long int lli, unsigned long long int ulli, long int li, unsigned long int uli) {\n" " printf(\"%llo %llx\", lli, ulli);\n" " printf(\"%ld %lu\", li, uli);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld, std::size_t ss, std::ptrdiff_t sp) {\n" " printf(\"%jd %jo\", im, uim);\n" " printf(\"%zx\", s);\n" " printf(\"%ti\", p);\n" " printf(\"%Lf\", ld);\n" " printf(\"%zx\", ss);\n" " printf(\"%ti\", sp);\n" "}"); ASSERT_EQUALS("", errout.str()); // Unrecognized (and non-existent in standard library) specifiers. // Perhaps should emit warnings check("void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld, std::size_t ss, std::ptrdiff_t sp) {\n" " printf(\"%jb %jw\", im, uim);\n" " printf(\"%zr\", s);\n" " printf(\"%tm\", p);\n" " printf(\"%La\", ld);\n" " printf(\"%zv\", ss);\n" " printf(\"%tp\", sp);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(long long l, ptrdiff_t p, std::ptrdiff_t sp) {\n" " printf(\"%td\", p);\n" " printf(\"%td\", sp);\n" " printf(\"%td\", l);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) %td in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'signed long long'.\n", errout.str()); check("void foo(int i, long double ld) {\n" " printf(\"%zx %zu\", i, ld);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %zx in format string (no. 1) requires 'size_t' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %zu in format string (no. 2) requires 'size_t' but the argument type is 'long double'.\n", errout.str()); check("void foo(unsigned int ui, long double ld) {\n" " printf(\"%zu %zx\", ui, ld);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %zu in format string (no. 1) requires 'size_t' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %zx in format string (no. 2) requires 'size_t' but the argument type is 'long double'.\n", errout.str()); check("void foo(int i, long double ld) {\n" " printf(\"%tx %tu\", i, ld);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %tx in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %tu in format string (no. 2) requires 'unsigned ptrdiff_t' but the argument type is 'long double'.\n", errout.str()); // False negative test check("void foo(unsigned int i) {\n" " printf(\"%h\", i);\n" " printf(\"%hh\", i);\n" " printf(\"%l\", i);\n" " printf(\"%ll\", i);\n" " printf(\"%j\", i);\n" " printf(\"%z\", i);\n" " printf(\"%t\", i);\n" " printf(\"%L\", i);\n" " printf(\"%I\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); check("void foo(unsigned int i) {\n" " printf(\"%hd\", i);\n" " printf(\"%hhd\", i);\n" " printf(\"%ld\", i);\n" " printf(\"%lld\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'unsigned int'.\n" "[test.cpp:3]: (warning) %hhd in format string (no. 1) requires 'char' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'unsigned int'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix32); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix32); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long long}'.\n", errout.str()); check("void foo(size_t s, uintmax_t um) {\n" " printf(\"%lu\", s);\n" " printf(\"%lu\", um);\n" " printf(\"%llu\", s);\n" " printf(\"%llu\", um);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:3]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'uintmax_t {aka unsigned long long}'.\n" "[test.cpp:4]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'uintmax_t {aka unsigned long long}'.\n", errout.str()); check("void foo(unsigned int i) {\n" " printf(\"%ld\", i);\n" " printf(\"%lld\", i);\n" " printf(\"%lu\", i);\n" " printf(\"%llu\", i);\n" " printf(\"%lx\", i);\n" " printf(\"%llx\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:3]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'unsigned int'.\n" "[test.cpp:6]: (warning) %lx in format string (no. 1) requires 'unsigned long' but the argument type is 'unsigned int'.\n" "[test.cpp:7]: (warning) %llx in format string (no. 1) requires 'unsigned long long' but the argument type is 'unsigned int'.\n", errout.str()); check("void foo(int i, intmax_t im, ptrdiff_t p) {\n" " printf(\"%lld\", i);\n" " printf(\"%lld\", im);\n" " printf(\"%lld\", p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'signed int'.\n", errout.str()); check("void foo(intmax_t im, ptrdiff_t p) {\n" " printf(\"%lld\", im);\n" " printf(\"%lld\", p);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %lld in format string (no. 1) requires 'long long' but the argument type is 'intmax_t {aka signed long long}'.\n" "[test.cpp:3]: (portability) %lld in format string (no. 1) requires 'long long' but the argument type is 'ptrdiff_t {aka signed long long}'.\n", errout.str()); check("class Foo {\n" " double d;\n" " struct Bar {\n" " int i;\n" " } bar[2];\n" " struct Baz {\n" " int i;\n" " } baz;\n" "};\n" "int a[10];\n" "Foo f[10];\n" "void foo(const Foo* foo) {\n" " printf(\"%d %f %f %d %f %f\",\n" " foo->d, foo->bar[0].i, a[0],\n" " f[0].d, f[0].baz.i, f[0].bar[0].i);\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'double'.\n" "[test.cpp:13]: (warning) %f in format string (no. 2) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:13]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'int'.\n" "[test.cpp:13]: (warning) %d in format string (no. 4) requires 'int' but the argument type is 'double'.\n" "[test.cpp:13]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'int'.\n" "[test.cpp:13]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'int'.\n", errout.str()); check("short f() { return 0; }\n" "void foo() { printf(\"%d %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed short'.\n", errout.str()); check("unsigned short f() { return 0; }\n" "void foo() { printf(\"%u %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned short'.\n", errout.str()); check("int f() { return 0; }\n" "void foo() { printf(\"%d %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed int'.\n", errout.str()); check("unsigned int f() { return 0; }\n" "void foo() { printf(\"%u %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned int'.\n", errout.str()); check("long f() { return 0; }\n" "void foo() { printf(\"%ld %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed long'.\n", errout.str()); check("unsigned long f() { return 0; }\n" "void foo() { printf(\"%lu %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned long'.\n", errout.str()); check("long long f() { return 0; }\n" "void foo() { printf(\"%lld %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed long long'.\n", errout.str()); check("unsigned long long f() { return 0; }\n" "void foo() { printf(\"%llu %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned long long'.\n", errout.str()); check("float f() { return 0; }\n" "void foo() { printf(\"%f %d %ld %u %lu %I64d %I64u %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 8) requires 'long double' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'float'.\n", errout.str()); check("double f() { return 0; }\n" "void foo() { printf(\"%f %d %ld %u %lu %I64d %I64u %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 8) requires 'long double' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'double'.\n", errout.str()); check("long double f() { return 0; }\n" "void foo() { printf(\"%Lf %d %ld %u %lu %I64d %I64u %f %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %f in format string (no. 8) requires 'double' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'long double'.\n", errout.str()); check("int f() { return 0; }\n" "void foo() { printf(\"%I64d %I64u %I64x %d\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %I64d in format string (no. 1) requires '__int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64x in format string (no. 3) requires 'unsigned __int64' but the argument type is 'signed int'.\n", errout.str()); check("long long f() { return 0; }\n" "void foo() { printf(\"%I32d %I32u %I32x %lld\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %I32x in format string (no. 3) requires 'unsigned __int32' but the argument type is 'signed long long'.\n", errout.str()); check("unsigned long long f() { return 0; }\n" "void foo() { printf(\"%I32d %I32u %I32x %llx\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %I32x in format string (no. 3) requires 'unsigned __int32' but the argument type is 'unsigned long long'.\n", errout.str()); check("signed char f() { return 0; }\n" "void foo() { printf(\"%Id %Iu %Ix %hhi\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'signed char'.\n" "[test.cpp:2]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'signed char'.\n" "[test.cpp:2]: (warning) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'signed char'.\n", errout.str()); check("unsigned char f() { return 0; }\n" "void foo() { printf(\"%Id %Iu %Ix %hho\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'unsigned char'.\n", errout.str()); check("namespace bar { int f() { return 0; } }\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar::f(), bar::f(), bar::f(), bar::f(), bar::f(), bar::f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", f.i, f.i, f.i, f.i, f.i, f.i); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { unsigned int u; } f;\n" "void foo() { printf(\"%u %d %ld %f %Lf %p\", f.u, f.u, f.u, f.u, f.u, f.u); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'unsigned int'.\n", errout.str()); check("struct Fred { unsigned int ui() { return 0; } } f;\n" "void foo() { printf(\"%u %d %ld %f %Lf %p\", f.ui(), f.ui(), f.ui(), f.ui(), f.ui(), f.ui()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'unsigned int'.\n", errout.str()); // #4975 check("void f(int len, int newline) {\n" " printf(\"%s\", newline ? a : str + len);\n" " printf(\"%s\", newline + newline);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "struct Fred & bar() { };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "const struct Fred & bar() { };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "static const struct Fred & bar() { };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f[2];\n" "struct Fred * bar() { return f; };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f[2];\n" "const struct Fred * bar() { return f; };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f[2];\n" "static const struct Fred * bar() { return f; };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int32_t i; } f;\n" "struct Fred & bar() { };\n" "void foo() { printf(\"%d %ld %u %lu %f %Lf\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 4) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 6) requires 'long double' but the argument type is 'signed int'.\n", errout.str()); // #4984 check("void f(double *x) {\n" " printf(\"%f\", x[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int array[10];\n" "int * foo() { return array; }\n" "void f() {\n" " printf(\"%f\", foo()[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout.str()); check("struct Base { int length() { } };\n" "struct Derived : public Base { };\n" "void foo(Derived * d) {\n" " printf(\"%f\", d.length());\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout.str()); check("std::vector v;\n" "void foo() {\n" " printf(\"%d %u %f\", v[0], v[0], v[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'signed int'.\n", errout.str()); // #4999 (crash) check("int bar(int a);\n" "void foo() {\n" " printf(\"%d\", bar(0));\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Unix32); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("class Fred : public std::vector {} v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("class Fred : public std::vector {} v;\n" "void foo() {\n" " printf(\"%d %u %f\", v[0], v[0], v[0]);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'int'.\n", errout.str()); check("std::string s;\n" "void foo() {\n" " printf(\"%s %p %u %d %f\", s.c_str(), s.c_str(), s.c_str(), s.c_str(), s.c_str());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %d in format string (no. 4) requires 'int' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'const char *'.\n", errout.str()); check("std::vector array;\n" "char * p = 0;\n" "char q[] = \"abc\";\n" "char r[10] = { 0 };\n" "size_t s;\n" "void foo() {\n" " printf(\"%zu %zu\", array.size(), s);\n" " printf(\"%u %u %u\", p, q, r);\n" " printf(\"%u %u\", array.size(), s);\n" " printf(\"%lu %lu\", array.size(), s);\n" " printf(\"%llu %llu\", array.size(), s);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:8]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n" "[test.cpp:8]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'char *'.\n" "[test.cpp:8]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'char *'.\n" "[test.cpp:9]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:9]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:10]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:10]: (portability) %lu in format string (no. 2) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:11]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:11]: (portability) %llu in format string (no. 2) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%td %zd %d %d %d %d %d %d %d %d %d %d %d\", pt, pt, b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (portability) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %d in format string (no. 11) requires 'int' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %d in format string (no. 12) requires 'int' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %d in format string (no. 13) requires 'int' but the argument type is 'char *'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\", b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'signed short'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 9) requires 'long' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 10) requires 'long' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 11) requires 'long' but the argument type is 'char *'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%td %zd %d %d %d %d %d %d %d %d %d\", ptf(), ptf(), bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (portability) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %d in format string (no. 11) requires 'int' but the argument type is 'char *'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld\", bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'signed short'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 9) requires 'long' but the argument type is 'char *'.\n", errout.str()); check("struct A {};\n" "class B : public std::vector {} b;\n" "class C : public std::vector {} c;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %u\", b.size(), b.size());\n" " printf(\"%p %d\", b[0], b[0]);\n" " printf(\"%p %d\", c[0], c[0]);\n" " printf(\"%p %d\", s.c_str(), s.c_str());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:6]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:7]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const int *'.\n" "[test.cpp:8]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const struct A *'.\n" "[test.cpp:9]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const char *'.\n", errout.str()); check("class A : public std::vector {} a;\n" "class B : public std::string {} b;\n" "std::string s;\n" "void foo() {\n" " printf(\"%p %d\", a[0].c_str(), a[0].c_str());\n" " printf(\"%c %p\", b[0], b[0]);\n" " printf(\"%c %p\", s[0], s[0]);\n" "}\n", false, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const char *'.\n" "[test.cpp:6]: (warning) %p in format string (no. 2) requires an address but the argument type is 'char'.\n" "[test.cpp:7]: (warning) %p in format string (no. 2) requires an address but the argument type is 'char'.\n", errout.str()); check("template \n" "struct buffer {\n" " size_t size();\n" "};\n" "buffer b;\n" "void foo() {\n" " printf(\"%u\", b.size());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:7]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); check("DWORD a;\n" "DWORD_PTR b;\n" "void foo() {\n" " printf(\"%u %u\", a, b);\n" "}\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'DWORD {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'DWORD_PTR {aka unsigned long}'.\n", errout.str()); check("unsigned long a[] = { 1, 2 };\n" "void foo() {\n" " printf(\"%d %d %x \", a[0], a[0], a[0]);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned long'.\n" "[test.cpp:3]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long'.\n" "[test.cpp:3]: (warning) %x in format string (no. 3) requires 'unsigned int' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo (wchar_t c) {\n" // ticket #5051 false positive " printf(\"%c\", c);\n" "}\n", false, false, Settings::Win64); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " printf(\"%f %d\", static_cast(1.0f), reinterpret_cast(0));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const void *'.\n", errout.str()); check("void foo() {\n" " UNKNOWN * u;\n" " printf(\"%d %x %u %f\", u[i], u[i], u[i], u[i]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " long * l;\n" " printf(\"%d %x %u %f\", l[i], l[i], l[i], l[i]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'signed long'.\n" "[test.cpp:3]: (warning) %x in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long'.\n" "[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'signed long'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed long'.\n", errout.str()); check("void f() {\n" // #5104 " myvector v1(1,0);\n" " printf(\"%d\",v1[0]);\n" " myvector v2(1,0);\n" " printf(\"%d\",v2[0]);\n" " myvector v3(1,0);\n" " printf(\"%u\",v3[0]);\n" " myvector v4(1,0);\n" " printf(\"%x\",v4[0]);\n" " myvector v5(1,0);\n" " printf(\"%f\",v5[0]);\n" " myvector v6(1,0);\n" " printf(\"%u\",v6[0]);\n" " myvector v7(1,0);\n" " printf(\"%s\",v7[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector v;\n" // #5151 "void foo() {\n" " printf(\"%c %u %f\", v.at(32), v.at(32), v.at(32));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'char'.\n" "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'char'.\n", errout.str()); // #5195 (segmentation fault) check("void T::a(const std::vector& vx) {\n" " printf(\"%f\", vx.at(0));\n" "}"); ASSERT_EQUALS("", errout.str()); // #5486 check("void foo() {\n" " ssize_t test = 0;\n" " printf(\"%zd\", test);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6009 check("extern std::string StringByReturnValue();\n" "extern int IntByReturnValue();\n" "void MyFunction() {\n" " printf( \"%s - %s\", StringByReturnValue(), IntByReturnValue() );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n" "[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'signed int'.\n", errout.str()); check("template \n" "struct Array {\n" " T data[S];\n" " T & operator [] (size_t i) { return data[i]; }\n" "};\n" "void foo() {\n" " Array array1;\n" " Array array2;\n" " printf(\"%u %u\", array1[0], array2[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'int'.\n" "[test.cpp:9]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'float'.\n", errout.str()); // Ticket #7445 check("struct S { unsigned short x; } s = {0};\n" "void foo() {\n" " printf(\"%d\", s.x);\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #7601 check("void foo(int i, unsigned int ui, long long ll, unsigned long long ull) {\n" " printf(\"%Ld %Lu %Ld %Lu\", i, ui, ll, ull);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lu in format string (no. 2) requires 'unsigned long long' but the argument type is 'unsigned int'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hhd %hhd %hhd %hhd %hhd %hhd %hhd %hhd\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 3) requires 'char' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 4) requires 'char' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 5) requires 'char' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 6) requires 'char' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 7) requires 'char' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 8) requires 'char' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhu in format string (no. 1) requires 'unsigned char' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 3) requires 'unsigned char' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 5) requires 'unsigned char' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 7) requires 'unsigned char' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhx in format string (no. 1) requires 'unsigned char' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 3) requires 'unsigned char' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 5) requires 'unsigned char' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 7) requires 'unsigned char' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hd %hd %hd %hd %hd %hd %hd %hd\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 2) requires 'short' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 4) requires 'short' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 5) requires 'short' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 6) requires 'short' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 7) requires 'short' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 8) requires 'short' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hu %hu %hu %hu %hu %hu %hu %hu\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hu in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 3) requires 'unsigned short' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 5) requires 'unsigned short' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 7) requires 'unsigned short' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hx %hx %hx %hx %hx %hx %hx %hx\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hx in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 3) requires 'unsigned short' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 5) requires 'unsigned short' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 7) requires 'unsigned short' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout.str()); // #7837 - Use ValueType for function call check("struct S {\n" " double (* f)(double);\n" "};\n" "\n" "void foo(struct S x) {\n" " printf(\"%f\", x.f(4.0));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " printf(\"%lu\", sizeof(char));\n" "}\n", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long long}'.\n", errout.str()); } void testPrintfArgumentVariables() { TEST_PRINTF_NOWARN("%u", "unsigned int", "bool"); TEST_PRINTF_WARN("%u", "unsigned int", "char"); TEST_PRINTF_WARN("%u", "unsigned int", "signed char"); TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned char"); TEST_PRINTF_WARN("%u", "unsigned int", "signed short"); TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned short"); TEST_PRINTF_WARN("%u", "unsigned int", "signed int"); TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned int"); TEST_PRINTF_WARN("%u", "unsigned int", "signed long"); TEST_PRINTF_WARN("%u", "unsigned int", "unsigned long"); TEST_PRINTF_WARN("%u", "unsigned int", "signed long long"); TEST_PRINTF_WARN("%u", "unsigned int", "unsigned long long"); TEST_PRINTF_WARN("%u", "unsigned int", "float"); TEST_PRINTF_WARN("%u", "unsigned int", "double"); TEST_PRINTF_WARN("%u", "unsigned int", "long double"); TEST_PRINTF_WARN("%u", "unsigned int", "void *"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%x", "unsigned int", "bool"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "char"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed char"); TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned char"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed short"); TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned short"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed int"); TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned int"); TEST_PRINTF_WARN("%x", "unsigned int", "signed long"); TEST_PRINTF_WARN("%x", "unsigned int", "unsigned long"); TEST_PRINTF_WARN("%x", "unsigned int", "signed long long"); TEST_PRINTF_WARN("%x", "unsigned int", "unsigned long long"); TEST_PRINTF_WARN("%x", "unsigned int", "float"); TEST_PRINTF_WARN("%x", "unsigned int", "double"); TEST_PRINTF_WARN("%x", "unsigned int", "long double"); TEST_PRINTF_WARN("%x", "unsigned int", "void *"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%lu","unsigned long","bool"); TEST_PRINTF_WARN("%lu","unsigned long","char"); TEST_PRINTF_WARN("%lu","unsigned long","signed char"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned char"); TEST_PRINTF_WARN("%lu","unsigned long","signed short"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned short"); TEST_PRINTF_WARN("%lu","unsigned long","signed int"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned int"); TEST_PRINTF_WARN("%lu","unsigned long","signed long"); TEST_PRINTF_NOWARN("%lu","unsigned long","unsigned long"); TEST_PRINTF_WARN("%lu","unsigned long","signed long long"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned long long"); TEST_PRINTF_WARN("%lu","unsigned long","float"); TEST_PRINTF_WARN("%lu","unsigned long","double"); TEST_PRINTF_WARN("%lu","unsigned long","long double"); TEST_PRINTF_WARN("%lu","unsigned long","void *"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","size_t","unsigned long","unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","uintmax_t","unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","uintptr_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::size_t","unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","std::uintmax_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","std::uintptr_t", "unsigned long long"); TEST_PRINTF_WARN("%lx","unsigned long","bool"); TEST_PRINTF_WARN("%lx","unsigned long","char"); TEST_PRINTF_WARN("%lx","unsigned long","signed char"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned char"); TEST_PRINTF_WARN("%lx","unsigned long","signed short"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned short"); TEST_PRINTF_WARN("%lx","unsigned long","signed int"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned int"); //TODO TEST_PRINTF_WARN("%lx","unsigned long","signed long"); TEST_PRINTF_NOWARN("%lx","unsigned long","unsigned long"); TEST_PRINTF_WARN("%lx","unsigned long","signed long long"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned long long"); TEST_PRINTF_WARN("%lx","unsigned long","float"); TEST_PRINTF_WARN("%lx","unsigned long","double"); TEST_PRINTF_WARN("%lx","unsigned long","long double"); TEST_PRINTF_WARN("%lx","unsigned long","void *"); TEST_PRINTF_WARN_AKA("%lx","unsigned long","size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","ssize_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","ptrdiff_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","intmax_t", "signed long long"); TEST_PRINTF_WARN_AKA("%lx","unsigned long","uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","intptr_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","uintptr_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lx","unsigned long","std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::ssize_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::ptrdiff_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::intmax_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::uintmax_t", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::intptr_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::uintptr_t", "unsigned long long"); TEST_PRINTF_WARN("%llu","unsigned long long","bool"); TEST_PRINTF_WARN("%llu","unsigned long long","char"); TEST_PRINTF_WARN("%llu","unsigned long long","signed char"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned char"); TEST_PRINTF_WARN("%llu","unsigned long long","signed short"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned short"); TEST_PRINTF_WARN("%llu","unsigned long long","signed int"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned int"); TEST_PRINTF_WARN("%llu","unsigned long long","signed long"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned long"); TEST_PRINTF_WARN("%llu","unsigned long long","signed long long"); TEST_PRINTF_NOWARN("%llu","unsigned long long","unsigned long long"); TEST_PRINTF_WARN("%llu","unsigned long long","float"); TEST_PRINTF_WARN("%llu","unsigned long long","double"); TEST_PRINTF_WARN("%llu","unsigned long long","long double"); TEST_PRINTF_WARN("%llu","unsigned long long","void *"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%llx","unsigned long long","bool"); TEST_PRINTF_WARN("%llx","unsigned long long","char"); TEST_PRINTF_WARN("%llx","unsigned long long","signed char"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned char"); TEST_PRINTF_WARN("%llx","unsigned long long","signed short"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned short"); TEST_PRINTF_WARN("%llx","unsigned long long","signed int"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned int"); TEST_PRINTF_WARN("%llx","unsigned long long","signed long"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned long"); //TODO TEST_PRINTF_WARN("%llx","unsigned long long","signed long long"); TEST_PRINTF_NOWARN("%llx","unsigned long long","unsigned long long"); TEST_PRINTF_WARN("%llx","unsigned long long","float"); TEST_PRINTF_WARN("%llx","unsigned long long","double"); TEST_PRINTF_WARN("%llx","unsigned long long","long double"); TEST_PRINTF_WARN("%llx","unsigned long long","void *"); TEST_PRINTF_WARN_AKA("%llx","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","ptrdiff_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","intmax_t", "signed long"); TEST_PRINTF_WARN_AKA("%llx","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llx","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::ptrdiff_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%hu", "unsigned short", "bool"); TEST_PRINTF_WARN("%hu", "unsigned short", "char"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed char"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned char"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed short"); TEST_PRINTF_NOWARN("%hu", "unsigned short", "unsigned short"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed int"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned int"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed long"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned long"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed long long"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned long long"); TEST_PRINTF_WARN("%hu", "unsigned short", "float"); TEST_PRINTF_WARN("%hu", "unsigned short", "double"); TEST_PRINTF_WARN("%hu", "unsigned short", "long double"); TEST_PRINTF_WARN("%hu", "unsigned short", "void *"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%hx", "unsigned short", "bool"); TEST_PRINTF_WARN("%hx", "unsigned short", "char"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed char"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned char"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed short"); TEST_PRINTF_NOWARN("%hx", "unsigned short", "unsigned short"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed int"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned int"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed long"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned long"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed long long"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned long long"); TEST_PRINTF_WARN("%hx", "unsigned short", "float"); TEST_PRINTF_WARN("%hx", "unsigned short", "double"); TEST_PRINTF_WARN("%hx", "unsigned short", "long double"); TEST_PRINTF_WARN("%hx", "unsigned short", "void *"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "bool"); TEST_PRINTF_WARN("%hhu", "unsigned char", "char"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed char"); TEST_PRINTF_NOWARN("%hhu", "unsigned char", "unsigned char"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed short"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned short"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed int"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned int"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed long long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned long long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "float"); TEST_PRINTF_WARN("%hhu", "unsigned char", "double"); TEST_PRINTF_WARN("%hhu", "unsigned char", "long double"); TEST_PRINTF_WARN("%hhu", "unsigned char", "void *"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "bool"); TEST_PRINTF_WARN("%hhx", "unsigned char", "char"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed char"); TEST_PRINTF_NOWARN("%hhx", "unsigned char", "unsigned char"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed short"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned short"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed int"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned int"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed long long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned long long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "float"); TEST_PRINTF_WARN("%hhx", "unsigned char", "double"); TEST_PRINTF_WARN("%hhx", "unsigned char", "long double"); TEST_PRINTF_WARN("%hhx", "unsigned char", "void *"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "bool"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "char"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed char"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned char"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed short"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned short"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed int"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned int"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed long long"); TEST_PRINTF_NOWARN("%Lu", "unsigned long long", "unsigned long long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "float"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "double"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "long double"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "void *"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::uintptr_t", "unsigned long"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "bool"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "char"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed char"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned char"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed short"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned short"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed int"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned int"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed long"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned long"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed long long"); //TODO TEST_PRINTF_NOWARN("%Lx", "unsigned long long", "unsigned long long"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "float"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "double"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "long double"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "void *"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "bool"); TEST_PRINTF_WARN("%ju", "uintmax_t", "char"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed char"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned char"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed short"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned short"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed int"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned int"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed long long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned long long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "float"); TEST_PRINTF_WARN("%ju", "uintmax_t", "double"); TEST_PRINTF_WARN("%ju", "uintmax_t", "long double"); TEST_PRINTF_WARN("%ju", "uintmax_t", "void *"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%ju", "uintmax_t", "uintmax_t"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "bool"); TEST_PRINTF_WARN("%jx", "uintmax_t", "char"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed char"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned char"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed short"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned short"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed int"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned int"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed long long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned long long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "float"); TEST_PRINTF_WARN("%jx", "uintmax_t", "double"); TEST_PRINTF_WARN("%jx", "uintmax_t", "long double"); TEST_PRINTF_WARN("%jx", "uintmax_t", "void *"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%jx", "uintmax_t", "uintmax_t"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%zd", "ssize_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%zi", "ssize_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%zu", "size_t", "bool"); TEST_PRINTF_WARN("%zu", "size_t", "char"); TEST_PRINTF_WARN("%zu", "size_t", "signed char"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned char"); TEST_PRINTF_WARN("%zu", "size_t", "signed short"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned short"); TEST_PRINTF_WARN("%zu", "size_t", "signed int"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned int"); TEST_PRINTF_WARN("%zu", "size_t", "signed long"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned long"); TEST_PRINTF_WARN("%zu", "size_t", "signed long long"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%zu", "size_t", "float"); TEST_PRINTF_WARN("%zu", "size_t", "double"); TEST_PRINTF_WARN("%zu", "size_t", "long double"); TEST_PRINTF_WARN("%zu", "size_t", "void *"); TEST_PRINTF_NOWARN("%zu", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%zu", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%zx", "size_t", "bool"); TEST_PRINTF_WARN("%zx", "size_t", "char"); TEST_PRINTF_WARN("%zx", "size_t", "signed char"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned char"); TEST_PRINTF_WARN("%zx", "size_t", "signed short"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned short"); TEST_PRINTF_WARN("%zx", "size_t", "signed int"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned int"); TEST_PRINTF_WARN("%zx", "size_t", "signed long"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned long"); TEST_PRINTF_WARN("%zx", "size_t", "signed long long"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%zx", "size_t", "float"); TEST_PRINTF_WARN("%zx", "size_t", "double"); TEST_PRINTF_WARN("%zx", "size_t", "long double"); TEST_PRINTF_WARN("%zx", "size_t", "void *"); TEST_PRINTF_NOWARN("%zx", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%zx", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "bool"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "char"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed char"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned char"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed short"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned short"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed int"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned int"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed long long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "float"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "double"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "long double"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "void *"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%tu", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "bool"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "char"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed char"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned char"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed short"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned short"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed int"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned int"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed long long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "float"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "double"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "long double"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "void *"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); //TODO TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%tx", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); //TODO TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%Iu", "size_t", "bool"); TEST_PRINTF_WARN("%Iu", "size_t", "char"); TEST_PRINTF_WARN("%Iu", "size_t", "signed char"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned char"); TEST_PRINTF_WARN("%Iu", "size_t", "signed short"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned short"); TEST_PRINTF_WARN("%Iu", "size_t", "signed int"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned int"); TEST_PRINTF_WARN("%Iu", "size_t", "signed long"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned long"); TEST_PRINTF_WARN("%Iu", "size_t", "signed long long"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%Iu", "size_t", "float"); TEST_PRINTF_WARN("%Iu", "size_t", "double"); TEST_PRINTF_WARN("%Iu", "size_t", "long double"); TEST_PRINTF_WARN("%Iu", "size_t", "void *"); TEST_PRINTF_NOWARN("%Iu", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%Iu", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%Ix", "size_t", "bool"); TEST_PRINTF_WARN("%Ix", "size_t", "char"); TEST_PRINTF_WARN("%Ix", "size_t", "signed char"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned char"); TEST_PRINTF_WARN("%Ix", "size_t", "signed short"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned short"); TEST_PRINTF_WARN("%Ix", "size_t", "signed int"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned int"); TEST_PRINTF_WARN("%Ix", "size_t", "signed long"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned long"); TEST_PRINTF_WARN("%Ix", "size_t", "signed long long"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%Ix", "size_t", "float"); TEST_PRINTF_WARN("%Ix", "size_t", "double"); TEST_PRINTF_WARN("%Ix", "size_t", "long double"); TEST_PRINTF_WARN("%Ix", "size_t", "void *"); TEST_PRINTF_NOWARN("%Ix", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%Ix", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "bool"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "char"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed char"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned char"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed short"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned short"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed int"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned int"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed long long"); TEST_PRINTF_NOWARN("%I64u", "unsigned __int64", "unsigned long long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "float"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "double"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "long double"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "void *"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "bool"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "char"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed char"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned char"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed short"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned short"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed int"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned int"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed long"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned long"); //TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed long long"); TEST_PRINTF_NOWARN("%I64x", "unsigned __int64", "unsigned long long"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "float"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "double"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "long double"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "void *"); //TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "size_t"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "ptrdiff_t", "signed long"); //TODO TEST_PRINTF_NOWARN("%I64x", "unsigned __int64", "unsigned __int64"); // TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "__int64"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::size_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::ptrdiff_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%I64d", "__int64", "bool"); TEST_PRINTF_WARN("%I64d", "__int64", "signed char"); TEST_PRINTF_WARN("%I64d", "__int64", "unsigned char"); TEST_PRINTF_WARN("%I64d", "__int64", "void *"); // TODO TEST_PRINTF_WARN("%I64d", "__int64", "size_t"); TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "ptrdiff_t", "signed long"); TEST_PRINTF_NOWARN("%I64d", "__int64", "__int64"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "bool"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "char"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed char"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned char"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed short"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned short"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed int"); TEST_PRINTF_NOWARN("%I32u", "unsigned __int32", "unsigned int"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed long long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned long long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "float"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "double"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "long double"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "void *"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "bool"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "char"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed char"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned char"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed short"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned short"); //TODO TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed int"); TEST_PRINTF_NOWARN("%I32x", "unsigned __int32", "unsigned int"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed long long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned long long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "float"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "double"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "long double"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "void *"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings check("void foo() {" " int bar;" " printf(\"%1$d\", 1);" " printf(\"%1$d, %d, %1$d\", 1, 2);" " scanf(\"%1$d\", &bar);" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " int bar;\n" " printf(\"%1$d\");\n" " printf(\"%1$d, %d, %4$d\", 1, 2, 3);\n" " scanf(\"%2$d\", &bar);\n" " printf(\"%0$f\", 0.0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) printf format string requires 1 parameter but only 0 are given.\n" "[test.cpp:4]: (warning) printf: referencing parameter 4 while 3 arguments given\n" "[test.cpp:5]: (warning) scanf: referencing parameter 2 while 1 arguments given\n" "[test.cpp:6]: (warning) printf: parameter positions start at 1, not 0\n" "", errout.str()); } void testMicrosoftPrintfArgument() { check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " printf(\"%Id %Iu %Ix\", s, s, s);\n" " printf(\"%Id %Iu %Ix\", p, p, p);\n" " printf(\"%I32d %I32u %I32x\", i32, i32, i32);\n" " printf(\"%I32d %I32u %I32x\", u32, u32, u32);\n" " printf(\"%I64d %I64u %I64x\", i64, i64, i64);\n" " printf(\"%I64d %I64u %I64x\", u64, u64, u64);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka signed int}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka signed long long}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64' but the argument type is 'unsigned __int64 {aka unsigned long long}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " printf(\"%Id %Iu %Ix\", s, s, s);\n" " printf(\"%Id %Iu %Ix\", p, p, p);\n" " printf(\"%I32d %I32u %I32x\", i32, i32, i32);\n" " printf(\"%I32d %I32u %I32x\", u32, u32, u32);\n" " printf(\"%I64d %I64u %I64x\", i64, i64, i64);\n" " printf(\"%I64d %I64u %I64x\", u64, u64, u64);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka signed int}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka signed long long}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64' but the argument type is 'unsigned __int64 {aka unsigned long long}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " int i;\n" " printf(\"%I\", s);\n" " printf(\"%I6\", s);\n" " printf(\"%I6x\", s);\n" " printf(\"%I16\", s);\n" " printf(\"%I16x\", s);\n" " printf(\"%I32\", s);\n" " printf(\"%I64\", s);\n" " printf(\"%I%i\", s, i);\n" " printf(\"%I6%i\", s, i);\n" " printf(\"%I6x%i\", s, i);\n" " printf(\"%I16%i\", s, i);\n" " printf(\"%I16x%i\", s, i);\n" " printf(\"%I32%i\", s, i);\n" " printf(\"%I64%i\", s, i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:11]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:12]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:13]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:14]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:15]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:16]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); // ticket #5264 check("void foo(LPARAM lp, WPARAM wp, LRESULT lr) {\n" " printf(\"%Ix %Ix %Ix\", lp, wp, lr);\n" "}\n", false, true, Settings::Win64); ASSERT_EQUALS("", errout.str()); check("void foo(LPARAM lp, WPARAM wp, LRESULT lr) {\n" " printf(\"%Ix %Ix %Ix\", lp, wp, lr);\n" "}\n", false, true, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo(UINT32 a, ::UINT32 b, Fred::UINT32 c) {\n" " printf(\"%d %d %d\", a, b, c);\n" "};\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (portability) %d in format string (no. 1) requires 'int' but the argument type is 'UINT32 {aka unsigned int}'.\n" "[test.cpp:2]: (portability) %d in format string (no. 2) requires 'int' but the argument type is 'UINT32 {aka unsigned int}'.\n", errout.str()); check("void foo(LPCVOID a, ::LPCVOID b, Fred::LPCVOID c) {\n" " printf(\"%d %d %d\", a, b, c);\n" "};\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const void *'.\n" "[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const void *'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s = -2;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s = -2;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long long}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s = -2;\n" // Under Unix, ssize_t has to be written in small letters. Not Cppcheck, but the compiler will report this. " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " typedef SSIZE_T ssize_t;\n" // Test using typedef " ssize_t s = -2;\n" " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:7]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long long}'.\n", errout.str()); } void testMicrosoftScanfArgument() { check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n" " scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n" " scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n" " scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n" " scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n" " scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:10]: (portability) %I32x in format string (no. 3) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:12]: (portability) %I64x in format string (no. 3) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n" " scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n" " scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n" " scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n" " scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n" " scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:10]: (portability) %I32x in format string (no. 3) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:12]: (portability) %I64x in format string (no. 3) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " int i;\n" " scanf(\"%I\", &s);\n" " scanf(\"%I6\", &s);\n" " scanf(\"%I6x\", &s);\n" " scanf(\"%I16\", &s);\n" " scanf(\"%I16x\", &s);\n" " scanf(\"%I32\", &s);\n" " scanf(\"%I64\", &s);\n" " scanf(\"%I%i\", &s, &i);\n" " scanf(\"%I6%i\", &s, &i);\n" " scanf(\"%I6x%i\", &s, &i);\n" " scanf(\"%I16%i\", &s, &i);\n" " scanf(\"%I16x%i\", &s, &i);\n" " scanf(\"%I32%i\", &s, &i);\n" " scanf(\"%I64%i\", &s, &i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:11]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:12]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:13]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:14]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:15]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:16]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); check("void foo() {\n" " SSIZE_T s;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long *}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long long *}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s;\n" // Under Unix, ssize_t has to be written in small letters. Not Cppcheck, but the compiler will report this. " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " typedef SSIZE_T ssize_t;\n" // Test using typedef " ssize_t s;\n" " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:7]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long long *}'.\n", errout.str()); } void testMicrosoftCStringFormatArguments() { // ticket #4920 check("void foo() {\n" " unsigned __int32 u32;\n" " String string;\n" " string.Format(\"%I32d\", u32);\n" " string.AppendFormat(\"%I32d\", u32);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " unsigned __int32 u32;\n" " CString string;\n" " string.Format(\"%I32d\", u32);\n" " string.AppendFormat(\"%I32d\", u32);\n" "}", false, true, Settings::Unix32); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " unsigned __int32 u32;\n" " CString string;\n" " string.Format(\"%I32d\", u32);\n" " string.AppendFormat(\"%I32d\", u32);\n" " CString::Format(\"%I32d\", u32);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:5]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:6]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n", errout.str()); } void testMicrosoftSecurePrintfArgument() { check("void foo() {\n" " int i;\n" " unsigned int u;\n" " _tprintf_s(_T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " _tprintf_s(_T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " printf_s(\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) printf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " wprintf_s(L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) wprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char str[10];\n" " int i;\n" " unsigned int u;\n" " sprintf_s(str, sizeof(str), \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) sprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char str[10];\n" " int i;\n" " unsigned int u;\n" " sprintf_s(str, \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) sprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " wchar_t str[10];\n" " int i;\n" " unsigned int u;\n" " swprintf_s(str, sizeof(str) / sizeof(wchar_t), L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) swprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " wchar_t str[10];\n" " int i;\n" " unsigned int u;\n" " swprintf_s(str, L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) swprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char str[10];\n" " int i;\n" " unsigned int u;\n" " _snprintf_s(str, sizeof(str), _TRUNCATE, \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _snprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " wchar_t str[10];\n" " int i;\n" " unsigned int u;\n" " _snwprintf_s(str, sizeof(str) / sizeof(wchar_t), _TRUNCATE, L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _snwprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " fprintf_s(fp, \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) fprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " fwprintf_s(fp, L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) fwprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char lineBuffer [600];\n" " const char * const format = \"%15s%17s%17s%17s%17s\";\n" " sprintf_s(lineBuffer, 600, format, \"type\", \"sum\", \"avg\", \"min\", \"max\");\n" " sprintf_s(lineBuffer, format, \"type\", \"sum\", \"avg\", \"min\", \"max\");\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " const char * const format1 = \"%15s%17s%17s%17s%17s\";\n" " const char format2[] = \"%15s%17s%17s%17s%17s\";\n" " const char * const format3 = format1;\n" " int i = 0;\n" " sprintf_s(lineBuffer, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf(lineBuffer, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf(lineBuffer, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf(lineBuffer, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " printf(format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " printf(format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " printf(format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, 100, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, 100, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, 100, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" "}\n", true, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:6]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:7]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:7]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:8]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:8]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:9]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:9]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" "[test.cpp:10]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:10]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" "[test.cpp:11]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:11]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" "[test.cpp:12]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:12]: (warning) printf format string requires 5 parameters but 6 are given.\n" "[test.cpp:13]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:13]: (warning) printf format string requires 5 parameters but 6 are given.\n" "[test.cpp:14]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:14]: (warning) printf format string requires 5 parameters but 6 are given.\n" "[test.cpp:15]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:15]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:16]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:16]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:17]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:17]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n", errout.str()); } void testMicrosoftSecureScanfArgument() { check("void foo() {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " char str[10];\n" " scanf_s(\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) scanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " wchar_t str[10];\n" " wscanf_s(L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) wscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void f() {\n" " char str[8];\n" " scanf_s(\"%8c\", str, sizeof(str));\n" " scanf_s(\"%9c\", str, sizeof(str));\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (error) Width 9 given in format string (no. 1) is larger than destination buffer 'str[8]', use %8c to prevent overflowing it.\n", errout.str()); check("void foo() {\n" " TCHAR txt[100];\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " TCHAR txt[100];\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " char txt[100];\n" " int i;\n" " unsigned int u;\n" " char str[10];\n" " sscanf_s(txt, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) sscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " wchar_t txt[100];\n" " int i;\n" " unsigned int u;\n" " wchar_t str[10];\n" " swscanf_s(txt, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) swscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " char str[10];\n" " fscanf_s(fp, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) fscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " wchar_t str[10];\n" " fwscanf_s(fp, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) fwscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " WCHAR msStr1[5] = {0};\n" " wscanf_s(L\"%4[^-]\", msStr1, _countof(msStr1));\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); } void testQStringFormatArguments() { check("void foo(float f) {\n" " QString string;\n" " string.sprintf(\"%d\", f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'float'.\n", errout.str()); check("void foo(float f) {\n" " QString string;\n" " string = QString::asprintf(\"%d\", f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'float'.\n", errout.str()); } void testTernary() { // ticket #6182 check("void test(const std::string &val) {\n" " printf(\"%s\", val.empty() ? \"I like to eat bananas\" : val.c_str());\n" "}"); ASSERT_EQUALS("", errout.str()); } void testUnsignedConst() { // ticket #6321 check("void test() {\n" " unsigned const x = 5;\n" " printf(\"%u\", x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testAstType() { // ticket #7014 check("void test() {\n" " printf(\"%c\", \"hello\"[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " printf(\"%lld\", (long long)1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " printf(\"%i\", (short *)x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'signed short *'.\n", errout.str()); check("int (*fp)();\n" // #7178 - function pointer call "void test() {\n" " printf(\"%i\", fp());\n" "}"); ASSERT_EQUALS("", errout.str()); } void testPrintf0WithSuffix() { // ticket #7069 check("void foo() {\n" " printf(\"%u %lu %llu\", 0U, 0UL, 0ULL);\n" " printf(\"%u %lu %llu\", 0u, 0ul, 0ull);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testReturnValueTypeStdLib() { check("void f() {\n" " const char *s = \"0\";\n" " printf(\"%ld%lld\", atol(s), atoll(s));\n" "}"); ASSERT_EQUALS("", errout.str()); // 8141 check("void f(int i) {\n" " printf(\"%f\", imaxabs(i));\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (portability) %f in format string (no. 1) requires 'double' but the argument type is 'intmax_t {aka signed long}'.\n", errout.str()); } void testPrintfTypeAlias1() { check("using INT = int;\n\n" "using PINT = INT *;\n" "using PCINT = const PINT;\n" "INT i;\n" "PINT pi;\n" "PCINT pci;" "void foo() {\n" " printf(\"%d %p %p\", i, pi, pci);\n" "};"); ASSERT_EQUALS("", errout.str()); check("using INT = int;\n\n" "using PINT = INT *;\n" "using PCINT = const PINT;\n" "INT i;\n" "PINT pi;\n" "PCINT pci;" "void foo() {\n" " printf(\"%f %f %f\", i, pi, pci);\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:8]: (warning) %f in format string (no. 2) requires 'double' but the argument type is 'signed int *'.\n" "[test.cpp:8]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'const signed int *'.\n", errout.str()); } void testPrintfAuto() { // #8992 check("void f() {\n" " auto s = sizeof(int);\n" " printf(\"%zu\", s);\n" " printf(\"%f\", s);\n" "}\n", false, true); ASSERT_EQUALS("[test.cpp:4]: (portability) %f in format string (no. 1) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); } void testPrintfParenthesis() { // #8489 check("void f(int a) {\n" " printf(\"%f\", (a >> 24) & 0xff);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout.str()); check("void f(int a) {\n" " printf(\"%f\", 0xff & (a >> 24));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout.str()); check("void f(int a) {\n" " printf(\"%f\", ((a >> 24) + 1) & 0xff);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout.str()); } void testStdDistance() { // #10304 check("void foo(const std::vector& IO, const int* pio) {\n" "const auto Idx = std::distance(&IO.front(), pio);\n" "printf(\"Idx = %td\", Idx);\n" "}", /*inconclusive*/ false, /*portability*/ true); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestIO) cppcheck-2.7/test/testleakautovar.cpp000066400000000000000000002366101417746362400200710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkleakautovar.h" #include "config.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include #include #include class TestLeakAutoVarStrcpy; class TestLeakAutoVarWindows; class TestLeakAutoVar : public TestFixture { public: TestLeakAutoVar() : TestFixture("TestLeakAutoVar") {} private: Settings settings; void run() OVERRIDE { int id = 0; while (!Library::ismemory(++id)); settings.library.setalloc("malloc", id, -1); settings.library.setrealloc("realloc", id, -1); settings.library.setdealloc("free", id, 1); while (!Library::isresource(++id)); settings.library.setalloc("socket", id, -1); settings.library.setdealloc("close", id, 1); while (!Library::isresource(++id)); settings.library.setalloc("fopen", id, -1); settings.library.setrealloc("freopen", id, -1, 3); settings.library.setdealloc("fclose", id, 1); settings.library.smartPointers["std::shared_ptr"]; settings.library.smartPointers["std::unique_ptr"]; settings.library.smartPointers["std::unique_ptr"].unique = true; const char xmldata[] = "\n" "\n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); // Assign TEST_CASE(assign1); TEST_CASE(assign2); TEST_CASE(assign3); TEST_CASE(assign4); TEST_CASE(assign5); TEST_CASE(assign6); TEST_CASE(assign7); TEST_CASE(assign8); TEST_CASE(assign9); TEST_CASE(assign10); TEST_CASE(assign11); // #3942: x = a(b(p)); TEST_CASE(assign12); // #4236: FP. bar(&x); TEST_CASE(assign13); // #4237: FP. char*&ref=p; p=malloc(10); free(ref); TEST_CASE(assign14); TEST_CASE(assign15); TEST_CASE(assign16); TEST_CASE(assign17); // #9047 TEST_CASE(assign18); TEST_CASE(assign19); TEST_CASE(assign20); // #9187 TEST_CASE(assign21); // #10186 TEST_CASE(assign22); // #9139 TEST_CASE(isAutoDealloc); TEST_CASE(realloc1); TEST_CASE(realloc2); TEST_CASE(realloc3); TEST_CASE(realloc4); TEST_CASE(realloc5); // #9292, #9990 TEST_CASE(freopen1); TEST_CASE(freopen2); TEST_CASE(deallocuse1); TEST_CASE(deallocuse2); TEST_CASE(deallocuse3); TEST_CASE(deallocuse4); TEST_CASE(deallocuse5); // #4018: FP. free(p), p = 0; TEST_CASE(deallocuse6); // #4034: FP. x = p = f(); TEST_CASE(deallocuse7); // #6467, #6469, #6473 TEST_CASE(deallocuse8); // #1765 TEST_CASE(deallocuse9); // #9781 TEST_CASE(doublefree1); TEST_CASE(doublefree2); TEST_CASE(doublefree3); // #4914 TEST_CASE(doublefree4); // #5451 - FP when exit is called TEST_CASE(doublefree5); // #5522 TEST_CASE(doublefree6); // #7685 TEST_CASE(doublefree7); TEST_CASE(doublefree8); TEST_CASE(doublefree9); TEST_CASE(doublefree10); // #8706 TEST_CASE(doublefree11); // exit TEST_CASE(exit1); TEST_CASE(exit2); TEST_CASE(exit3); // handling function calls TEST_CASE(functioncall1); // goto TEST_CASE(goto1); TEST_CASE(goto2); // if/else TEST_CASE(ifelse1); TEST_CASE(ifelse2); TEST_CASE(ifelse3); TEST_CASE(ifelse4); TEST_CASE(ifelse5); TEST_CASE(ifelse6); // #3370 TEST_CASE(ifelse7); // #5576 - if (fd < 0) TEST_CASE(ifelse8); // #5747 - if (fd == -1) TEST_CASE(ifelse9); // #5273 - if (X(p==NULL, 0)) TEST_CASE(ifelse10); // #8794 - if (!(x!=NULL)) TEST_CASE(ifelse11); // #8365 - if (NULL == (p = malloc(4))) TEST_CASE(ifelse12); // #8340 - if ((*p = malloc(4)) == NULL) TEST_CASE(ifelse13); // #8392 TEST_CASE(ifelse14); // #9130 - if (x == (char*)NULL) TEST_CASE(ifelse15); // #9206 - if (global_ptr = malloc(1)) TEST_CASE(ifelse16); // #9635 - if (p = malloc(4), p == NULL) TEST_CASE(ifelse17); // if (!!(!p)) TEST_CASE(ifelse18); TEST_CASE(ifelse19); TEST_CASE(ifelse20); // #10182 TEST_CASE(ifelse21); TEST_CASE(ifelse22); // #10187 // switch TEST_CASE(switch1); // loops TEST_CASE(loop1); // mismatching allocation/deallocation TEST_CASE(mismatchAllocDealloc); TEST_CASE(smartPointerDeleter); TEST_CASE(smartPointerRelease); // Execution reaches a 'return' TEST_CASE(return1); TEST_CASE(return2); TEST_CASE(return3); TEST_CASE(return4); TEST_CASE(return5); TEST_CASE(return6); // #8282 return {p, p} TEST_CASE(return7); // #9343 return (uint8_t*)x TEST_CASE(return8); // General tests: variable type, allocation type, etc TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); // #3954 - reference pointer TEST_CASE(test4); // #5923 - static pointer TEST_CASE(test5); // unknown type // Execution reaches a 'throw' TEST_CASE(throw1); TEST_CASE(throw2); // Possible leak => Further configuration is needed for complete analysis TEST_CASE(configuration1); TEST_CASE(configuration2); TEST_CASE(configuration3); TEST_CASE(configuration4); TEST_CASE(ptrptr); TEST_CASE(nestedAllocation); TEST_CASE(testKeywords); // #6767 TEST_CASE(inlineFunction); // #3989 TEST_CASE(smartPtrInContainer); // #8262 TEST_CASE(functionCallCastConfig); // #9652 } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], bool cpp = false) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, cpp ? "test.cpp" : "test.c"), file, line); // Check for leaks.. CheckLeakAutoVar c; settings.checkLibrary = true; settings.severity.enable(Severity::information); c.runChecks(&tokenizer, &settings, this); } void check_(const char* file, int line, const char code[], Settings & settings_) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings_, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for leaks.. CheckLeakAutoVar c; settings_.checkLibrary = true; settings_.severity.enable(Severity::information); c.runChecks(&tokenizer, &settings_, this); } void assign1() { check("void f() {\n" " char *p = malloc(10);\n" " p = NULL;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void assign2() { check("void f() {\n" " char *p = malloc(10);\n" " char *q = p;\n" " free(q);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign3() { check("void f() {\n" " char *p = malloc(10);\n" " char *q = p + 1;\n" " free(q - 1);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign4() { check("void f() {\n" " char *a = malloc(10);\n" " a += 10;\n" " free(a - 10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign5() { check("void foo()\n" "{\n" " char *p = new char[100];\n" " list += p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign6() { // #2806 - FP when there is redundant assignment check("void foo() {\n" " char *p = malloc(10);\n" " p = strcpy(p,q);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign7() { check("void foo(struct str *d) {\n" " struct str *p = malloc(10);\n" " d->p = p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign8() { // linux list check("void foo(struct str *d) {\n" " struct str *p = malloc(10);\n" " d->p = &p->x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign9() { check("void foo() {\n" " char *p = x();\n" " free(p);\n" " p = NULL;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign10() { check("void foo() {\n" " char *p;\n" " if (x) { p = malloc(10); }\n" " if (!x) { p = NULL; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign11() { // #3942 - FP for x = a(b(p)); check("void f() {\n" " char *p = malloc(10);\n" " x = a(b(p));\n" "}"); ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function b() should have / configuration\n", errout.str()); } void assign12() { // #4236: FP. bar(&x) check("void f() {\n" " char *p = malloc(10);\n" " free(p);\n" " bar(&p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign13() { // #4237: FP. char *&ref=p; p=malloc(10); free(ref); check("void f() {\n" " char *p;\n" " char * &ref = p;\n" " p = malloc(10);\n" " free(ref);\n" "}"); TODO_ASSERT_EQUALS("", "[test.c:6]: (error) Memory leak: p\n", errout.str()); } void assign14() { check("void f(int x) {\n" " char *p;\n" " if (x && (p = malloc(10))) { }" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); check("void f(int x) {\n" " char *p;\n" " if (x && (p = new char[10])) { }" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); } void assign15() { // #8120 check("void f() {\n" " baz *p;\n" " p = malloc(sizeof *p);\n" " free(p);\n" " p = malloc(sizeof *p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign16() { check("void f() {\n" " char *p = malloc(10);\n" " free(p);\n" " if (p=dostuff()) *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign17() { // #9047 check("void f() {\n" " char *p = (char*)malloc(10);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " char *p = (char*)(int*)malloc(10);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void assign18() { check("void f(int x) {\n" " char *p;\n" " if (x && (p = (char*)malloc(10))) { }" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); check("void f(int x) {\n" " char *p;\n" " if (x && (p = (char*)(int*)malloc(10))) { }" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void assign19() { check("void f() {\n" " char *p = malloc(10);\n" " free((void*)p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign20() { // #9187 check("void f() {\n" " char *p = static_cast(malloc(10));\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); } void assign21() { // #10186 check("void f(int **x) {\n" " void *p = malloc(10);\n" " *x = (int*)p;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void assign22() { // #9139 check("void f(char tempFileName[256]) {\n" " const int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Resource leak: fd\n", errout.str()); check("void f() {\n" " const void * const p = malloc(10);\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); } void isAutoDealloc() { check("void f() {\n" " char *p = new char[100];" "}", true); ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " Fred *fred = new Fred;" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::string *str = new std::string;" "}", true); TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: str\n", "", errout.str()); check("class TestType {\n" // #9028 "public:\n" " char ca[12];\n" "};\n" "void f() {\n" " TestType *tt = new TestType();\n" "}", true); ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: tt\n", errout.str()); check("void f(Bar& b) {\n" // #7622 " char* data = new char[10];\n" " b = Bar(*new Foo(data));\n" "}", /*cpp*/ true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function Foo() should have / configuration\n", errout.str()); } void realloc1() { check("void f() {\n" " void *p = malloc(10);\n" " void *q = realloc(p, 20);\n" " free(q)\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc2() { check("void f() {\n" " void *p = malloc(10);\n" " void *q = realloc(p, 20);\n" "}"); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str()); } void realloc3() { check("void f() {\n" " char *p = malloc(10);\n" " char *q = (char*) realloc(p, 20);\n" "}"); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str()); } void realloc4() { check("void f(void *p) {\n" " void * q = realloc(p, 10);\n" " if (q == NULL)\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:5]: (error) Memory leak: q\n", errout.str()); } void realloc5() { // #9292 check("void * f(void * ptr, size_t size) {\n" " void *datap = realloc(ptr, size);\n" " if (size && !datap)\n" " free(ptr);\n" " return datap;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9990 check("void f() {\n" " void * p1 = malloc(10);\n" " if (!p1)\n" " return;\n" " void * p2 = realloc(p1, 42);\n" " if (!p2) {\n" " free(p1);\n" " return;\n" " }\n" " free(p2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void freopen1() { check("void f() {\n" " void *p = fopen(name,a);\n" " void *q = freopen(name, b, p);\n" " fclose(q)\n" "}"); ASSERT_EQUALS("", errout.str()); } void freopen2() { check("void f() {\n" " void *p = fopen(name,a);\n" " void *q = freopen(name, b, p);\n" "}"); ASSERT_EQUALS("[test.c:4]: (error) Resource leak: q\n", errout.str()); } void deallocuse1() { check("void f(char *p) {\n" " free(p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str()); check("void f(char *p) {\n" " free(p);\n" " char c = *p;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str()); } void deallocuse2() { check("void f(char *p) {\n" " free(p);\n" " strcpy(a, p);\n" "}"); TODO_ASSERT_EQUALS("error (free,use)", "[test.c:3]: (information) --check-library: Function strcpy() should have configuration\n", errout.str()); check("void f(char *p) {\n" // #3041 - assigning pointer when it's used " free(p);\n" " strcpy(a, p=b());\n" "}"); TODO_ASSERT_EQUALS("", "[test.c:3]: (information) --check-library: Function strcpy() should have configuration\n", errout.str()); } void deallocuse3() { check("void f(struct str *p) {\n" " free(p);\n" " p = p->next;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str()); } void deallocuse4() { check("void f(char *p) {\n" " free(p);\n" " return p;\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout.str()); check("void f(char *p) {\n" " if (!p) free(p);\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *p) {\n" " if (!p) delete p;\n" " return p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(char *p) {\n" " if (!p) delete [] p;\n" " return p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(void* p) {\n" " if (a) {\n" " free(p);\n" " return;\n" " }\n" " g(p);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse5() { // #4018 check("void f(char *p) {\n" " free(p), p = 0;\n" " *p = 0;\n" // <- Make sure pointer info is reset. It is NOT a freed pointer dereference "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse6() { // #4034 check("void f(char *p) {\n" " free(p);\n" " x = p = foo();\n" // <- p is not dereferenced "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse7() { // #6467, #6469, #6473, #6648 check("struct Foo { int* ptr; };\n" "void f(Foo* foo) {\n" " delete foo->ptr;\n" " foo->ptr = new Foo;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct Foo { int* ptr; };\n" "void f(Foo* foo) {\n" " delete foo->ptr;\n" " x = *foo->ptr;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", errout.str()); check("void parse() {\n" " struct Buf {\n" " Buf(uint32_t len) : m_buf(new uint8_t[len]) {}\n" " ~Buf() { delete[]m_buf; }\n" " uint8_t *m_buf;\n" " };\n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct Foo {\n" " Foo();\n" " Foo* ptr;\n" " void func();\n" "};\n" "void bar(Foo* foo) {\n" " delete foo->ptr;\n" " foo->ptr = new Foo;\n" " foo->ptr->func();\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(void (*conv)(char**)) {\n" " char * ptr=(char*)malloc(42);\n" " free(ptr);\n" " (*conv)(&ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse8() { // #1765 check("void f() {\n" " int *ptr = new int;\n" " delete(ptr);\n" " *ptr = 0;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", errout.str()); } void deallocuse9() { check("void f(Type* p) {\n" // #9781 " std::shared_ptr sp(p);\n" " bool b = p->foo();\n" " return b;\n" "}\n", /*cpp*/ true); ASSERT_EQUALS("", errout.str()); check("struct A {\n" // #8635 " std::vector> array_;\n" " A* foo() {\n" " A* a = new A();\n" " array_.push_back(std::unique_ptr(a));\n" " return a;\n" " }\n" "};\n", /*cpp*/ true); ASSERT_EQUALS("", errout.str()); } void doublefree1() { // #3895 check("void f(char *p) {\n" " if (x)\n" " free(p);\n" " else\n" " p = 0;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:3] -> [test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p, char *r) {\n" " free(p);\n" " free(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " free(p);\n" " free(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " if (x < 3) free(p);\n" " else { if (x > 9) free(p); }\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " getNext(&p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " bar();\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " printf(\"Freed memory at location %x\", p);\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(FILE *p) {\n" " fclose(p);\n" " fclose(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Resource handle 'p' freed twice.\n", errout.str()); check( "void foo(FILE *p, FILE *r) {\n" " fclose(p);\n" " fclose(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(FILE *p) {\n" " if (x < 3) fclose(p);\n" " else { if (x > 9) fclose(p); }\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(FILE *p) {\n" " fclose(p);\n" " gethandle(&p);\n" " fclose(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(FILE *p) {\n" " fclose(p);\n" " gethandle();\n" " fclose(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Resource handle 'p' freed twice.\n", errout.str()); check( "void foo(Data* p) {\n" " free(p->a);\n" " free(p->b);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void f() {\n" " char *p; p = malloc(100);\n" " if (x) {\n" " free(p);\n" " exit();\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void f() {\n" " char *p; p = malloc(100);\n" " if (x) {\n" " free(p);\n" " x = 0;\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:4] -> [test.c:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void f() {\n" " char *p; p = do_something();\n" " free(p);\n" " p = do_something();\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " delete p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p, char *r) {\n" " delete p;\n" " delete r;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(P p) {\n" " delete p.x;\n" " delete p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char **p) {\n" " delete p[0];\n" " delete p[1];\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " getNext(&p);\n" " delete p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " bar();\n" " delete p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " delete[] p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p, char *r) {\n" " delete[] p;\n" " delete[] r;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " getNext(&p);\n" " delete[] p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " bar();\n" " delete[] p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "LineMarker::~LineMarker() {\n" " delete pxpm;\n" "}\n" "LineMarker &LineMarker::operator=(const LineMarker &) {\n" " delete pxpm;\n" " pxpm = NULL;\n" " return *this;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo()\n" "{\n" " int* ptr; ptr = NULL;\n" " try\n" " {\n" " ptr = new int(4);\n" " }\n" " catch(...)\n" " {\n" " delete ptr;\n" " throw;\n" " }\n" " delete ptr;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "int foo()\n" "{\n" " int* a; a = new int;\n" " bool doDelete; doDelete = true;\n" " if (a != 0)\n" " {\n" " doDelete = false;\n" " delete a;\n" " }\n" " if(doDelete)\n" " delete a;\n" " return 0;\n" "}", true); TODO_ASSERT_EQUALS("", "[test.cpp:8] -> [test.cpp:11]: (error) Memory pointed to by 'a' is freed twice.\n", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " while(true) {\n" " x = new char[100];\n" " if (y++ > 100)\n" " break;\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " for (int i = 0; i < 10000; i++) {\n" " x = new char[100];\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " while (isRunning()) {\n" " x = new char[100];\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " while (isRunning()) {\n" " x = malloc(100);\n" " free(x);\n" " }\n" " free(x);\n" "}"); TODO_ASSERT_EQUALS("[test.c:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " for (;;) {\n" " x = new char[100];\n" " if (y++ > 100)\n" " break;\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " do {\n" " x = new char[100];\n" " if (y++ > 100)\n" " break;\n" " delete[] x;\n" " } while (true);\n" " delete[] x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void f()\n" "{\n" " char *p; p = 0;\n" " if (x < 100) {\n" " p = malloc(10);\n" " free(p);\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:6] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void MyFunction()\n" "{\n" " char* data; data = new char[100];\n" " try\n" " {\n" " }\n" " catch(err)\n" " {\n" " delete[] data;\n" " MyThrow(err);\n" " }\n" " delete[] data;\n" "}\n" "void MyThrow(err)\n" "{\n" " throw(err);\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void MyFunction()\n" "{\n" " char* data; data = new char[100];\n" " try\n" " {\n" " }\n" " catch(err)\n" " {\n" " delete[] data;\n" " MyExit(err);\n" " }\n" " delete[] data;\n" "}\n" "void MyExit(err)\n" "{\n" " exit(err);\n" "}", true); ASSERT_EQUALS("", errout.str()); check( // #6252 "struct Wrapper {\n" " Thing* m_thing;\n" " Wrapper() : m_thing(0) {\n" " }\n" " ~Wrapper() {\n" " delete m_thing;\n" " }\n" " void changeThing() {\n" " delete m_thing;\n" " m_thing = new Thing;\n" " }\n" "};", true); ASSERT_EQUALS("", errout.str()); // #7401 check("void pCodeLabelDestruct(pCode *pc) {\n" " free(PCL(pc)->label);\n" " free(pc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree2() { // #3891 check("void *f(int a) {\n" " char *p = malloc(10);\n" " if (a == 2) { free(p); return ((void*)1); }\n" " free(p);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree3() { // #4914 check("void foo() {\n" " bool done = false;\n" " do {\n" " char *bar = malloc(10)\n" " if(condition()) {\n" " free(bar);\n" " continue;\n" " }\n" " done = true;\n" " free(bar)\n" " } while(!done);\n" " return;" "}" ); ASSERT_EQUALS("", errout.str()); } void doublefree4() { // #5451 - exit check("void f(char *p) {\n" " if (x) {\n" " free(p);\n" " exit(1);\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree5() { // #5522 check("void f(char *p) {\n" " free(p);\n" " x = (q == p);\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); } void doublefree6() { // #7685 check("void do_wordexp(FILE *f) {\n" " free(getword(f));\n" " fclose(f);\n" "}", /*cpp=*/ false); ASSERT_EQUALS("", errout.str()); } void doublefree7() { check("void f(char *p, int x) {\n" " free(p);\n" " if (x && (p = malloc(10)))\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *p, int x) {\n" " delete[] p;\n" " if (x && (p = new char[10]))\n" " delete[] p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree8() { check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " delete i;\n" " std::unique_ptr x(i);\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x{i};\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::shared_ptr x(i);\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::shared_ptr x{i};\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); // Check for use-after-free FP check("void f() {\n" " int * i = new int;\n" " std::shared_ptr x{i};\n" " *i = 123;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int[1];\n" " std::unique_ptr x(i);\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); } void doublefree9() { check("struct foo {\n" " int* get(int) { return new int(); }\n" "};\n" "void f(foo* b) {\n" " std::unique_ptr x(b->get(0));\n" " std::unique_ptr y(b->get(1));\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void doublefree10() { check("void f(char* s) {\n" " char *p = malloc(strlen(s));\n" " if (p != NULL) {\n" " strcat(p, s);\n" " if (strlen(s) != 10)\n" " free(p); p = NULL;\n" " }\n" " if (p != NULL)\n" " free(p);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f(char* s) {\n" " char *p = malloc(strlen(s));\n" " if (p != NULL) {\n" " strcat(p, s);\n" " if (strlen(s) != 10)\n" " free(p), p = NULL;\n" " }\n" " if (p != NULL)\n" " free(p);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void doublefree11() { check("void f() {\n" " void * p = malloc(5);\n" " void * q = realloc(p, 10);\n" " if (q == NULL) {\n" " free(p);\n" " return;\n" " }\n" " free(p);\n" " if (q == NULL)\n" " return;\n" " free(q)\n" "}"); ASSERT_EQUALS("[test.c:3] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); } void exit1() { check("void f() {\n" " char *p = malloc(10);\n" " exit(0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void exit2() { check("void f() {\n" " char *p = malloc(10);\n" " fatal_error();\n" "}"); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function fatal_error() should have configuration\n" "[test.c:4]: (information) --check-library: Function fatal_error() should have / configuration\n", errout.str()); } void exit3() { check("void f() {\n" " char *p = malloc(100);\n" " if (x) {\n" " free(p);\n" " ::exit(0);\n" " }" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *p = malloc(100);\n" " if (x) {\n" " free(p);\n" " std::exit(0);\n" " }" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void functioncall1() { check("void f(struct S *p) {\n" " p->x = malloc(10);\n" " free(p->x);\n" " p->x = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void goto1() { check("static void f() {\n" " int err = -ENOMEM;\n" " char *reg = malloc(100);\n" " if (err) {\n" " free(reg);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void goto2() { // #4231 check("static char * f() {\n" "x:\n" " char *p = malloc(100);\n" " if (err) {\n" " free(p);\n" " goto x;\n" " }\n" " return p;\n" // no error since there is a goto "}"); ASSERT_EQUALS("", errout.str()); } void ifelse1() { check("int f() {\n" " char *p = NULL;\n" " if (x) { p = malloc(10); }\n" " else { return 0; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse2() { check("int f() {\n" " char *p = NULL;\n" " if (x) { p = malloc(10); }\n" " else { return 0; }\n" "}"); ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout.str()); } void ifelse3() { check("void f() {\n" " char *p = malloc(10);\n" " if (!p) { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char * f(size_t size) {" " void *p = malloc(1);" " if (!p && size != 0)" " return NULL;" " return p;" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " if (p) { } else { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); // #3866 - UNLIKELY check("void f() {\n" " char *p = malloc(10);\n" " if (UNLIKELY(!p)) { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse4() { check("void f(int x) {\n" " char *p;\n" " if (x) { p = malloc(10); }\n" " if (x) { free(p); }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " char *p;\n" " if (x) { p = malloc(10); }\n" " if (!x) { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse5() { check("void f() {\n" " char *p = malloc(10);\n" " if (!p && x) { p = malloc(10); }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse6() { // #3370 check("void f(int x) {\n" " int *a = malloc(20);\n" " if (x)\n" " free(a);\n" " else\n" " a = 0;\n" "}"); ASSERT_EQUALS("[test.c:6]: (error) Memory leak: a\n", errout.str()); } void ifelse7() { // #5576 check("void f() {\n" " int x = malloc(20);\n" " if (x < 0)\n" // assume negative value indicates its unallocated " return;\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse8() { // #5747 check("int f() {\n" " int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" " if (fd == -1)\n" " return -1;\n" " return fd;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" " if (fd != -1)\n" " return fd;\n" " return -1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse9() { // #5273 check("void f() {\n" " char *p = malloc(100);\n" " if (dostuff(p==NULL,0))\n" " return;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse10() { // #8794 check("void f() {\n" " void *x = malloc(1U);\n" " if (!(x != NULL))\n" " return;\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse11() { // #8365 check("void f() {\n" " void *p;\n" " if (NULL == (p = malloc(4)))\n" " return;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse12() { // #8340 check("void f(char **p) {\n" " if ((*p = malloc(4)) == NULL)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse13() { // #8392 check("int f(int fd, const char *mode) {\n" " char *path;\n" " if (fd == -1 || (path = (char *)malloc(10)) == NULL)\n" " return 1;\n" " free(path);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int fd, const char *mode) {\n" " char *path;\n" " if ((path = (char *)malloc(10)) == NULL || fd == -1)\n" " return 1;\n" // <- memory leak " free(path);\n" " return 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4] memory leak", "", errout.str()); } void ifelse14() { // #9130 check("char* f() {\n" " char* buf = malloc(10);\n" " if (buf == (char*)NULL)\n" " return NULL;\n" " return buf;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse15() { // #9206 check("struct SSS { int a; };\n" "SSS* global_ptr;\n" "void test_alloc() {\n" " if ( global_ptr = new SSS()) {}\n" " return;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("FILE* hFile;\n" "int openFile( void ) {\n" " if ((hFile = fopen(\"1.txt\", \"wb\" )) == NULL) return 0;\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse16() { // #9635 check("void f(void) {\n" " char *p;\n" " if(p = malloc(4), p == NULL)\n" " return;\n" " free(p);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " char *p, q;\n" " if(p = malloc(4), q = 1, p == NULL)\n" " return;\n" " free(p);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse17() { check("int *f() {\n" " int *p = realloc(nullptr, 10);\n" " if (!p)\n" " return NULL;\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int *f() {\n" " int *p = realloc(nullptr, 10);\n" " if (!!(!p))\n" " return NULL;\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse18() { check("void f() {\n" " void * p = malloc(10);\n" " void * q = realloc(p, 20);\n" " if (q == 0)\n" " return;\n" " free(q);\n" "}"); ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " void * p = malloc(10);\n" " void * q = realloc(p, 20);\n" " if (q != 0) {\n" " free(q);\n" " return;\n" " } else\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:8]: (error) Memory leak: p\n", errout.str()); } void ifelse19() { check("void f() {\n" " static char * a;\n" " char * b = realloc(a, 10);\n" " if (!b)\n" " return;\n" " a = b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse20() { check("void f() {\n" " if (x > 0)\n" " void * p1 = malloc(5);\n" " else\n" " void * p2 = malloc(2);\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n" "[test.c:5]: (error) Memory leak: p2\n", errout.str()); check("void f() {\n" " if (x > 0)\n" " void * p1 = malloc(5);\n" " else\n" " void * p2 = malloc(2);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n" "[test.c:5]: (error) Memory leak: p2\n", errout.str()); } void ifelse21() { check("void f() {\n" " if (y) {\n" " void * p;\n" " if (x > 0)\n" " p = malloc(5);\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:6]: (error) Memory leak: p\n", errout.str()); } void ifelse22() { // #10187 check("int f(const char * pathname, int flags) {\n" " int fd = socket(pathname, flags);\n" " if (fd >= 0)\n" " return fd;\n" " return -1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(const char * pathname, int flags) {\n" " int fd = socket(pathname, flags);\n" " if (fd <= -1)\n" " return -1;\n" " return fd;\n" "}"); ASSERT_EQUALS("", errout.str()); } void switch1() { check("void f() {\n" " char *p = 0;\n" " switch (x) {\n" " case 123: p = malloc(100); break;\n" " default: return;\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void loop1() { // test the handling of { } check("void f() {\n" " char *p;\n" " for (i=0;i<5;i++) { }\n" " if (x) { free(p) }\n" " else { a = p; }\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatchAllocDealloc() { check("void f() {\n" " FILE*f=fopen(fname,a);\n" " free(f);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " free((void*)f);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " char *cPtr = new char[100];\n" " delete[] cPtr;\n" " cPtr = new char[100]('x');\n" " delete[] cPtr;\n" " cPtr = new char[100];\n" " delete cPtr;\n" "}", true); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str()); check("void f() {\n" " char *cPtr = new char[100];\n" " free(cPtr);\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str()); check("void f() {\n" " char *cPtr = new (buf) char[100];\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int[1];\n" " std::unique_ptr x(i);\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str()); check("void f() {\n" " void* a = malloc(1);\n" " void* b = freopen(f, p, a);\n" " free(b);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: a\n" "[test.c:3] -> [test.c:4]: (error) Mismatching allocation and deallocation: b\n", errout.str()); check("void f() {\n" " void* a;\n" " void* b = realloc(a, 10);\n" " free(b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int;\n" " int * j = realloc(i, 2 * sizeof(int));\n" " delete[] j;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n" "[test.cpp:3] -> [test.cpp:4]: (error) Mismatching allocation and deallocation: j\n", errout.str()); } void smartPointerDeleter() { check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::unique_ptr fp{f};\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::unique_ptr fp{f, &fclose};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, &fclose};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct deleter { void operator()(FILE* f) { fclose(f); }};\n" "void f() {\n" " FILE*f=fopen(fname,a);\n" " std::unique_ptr fp{f};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int * create();\n" "void destroy(int * x);\n" "void f() {\n" " int x * = create()\n" " std::unique_ptr xp{x, &destroy()};\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("int * create();\n" "void destroy(int * x);\n" "void f() {\n" " int x * = create()\n" " std::unique_ptr xp(x, &destroy());\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) { fclose(x); }};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, +[](FILE* x) { fclose(x); }};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) { free(f); }};\n" "}", true); TODO_ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", "", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) {}};\n" "}", true); TODO_ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", "", errout.str()); check("class C;\n" "void f() {\n" " C* c = new C{};\n" " std::shared_ptr a{c, [](C*) {}};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("class C;\n" "void f() {\n" " C* c = new C{};\n" " std::shared_ptr a{c, [](C* x) { delete x; }};\n" "}", true); ASSERT_EQUALS("", errout.str()); } void smartPointerRelease() { check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" " x.release();\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" " x.release();\n" "}\n", true); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: i\n", errout.str()); } void return1() { check("int f() {\n" " char *p = malloc(100);\n" " return 123;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void return2() { check("char *f() {\n" " char *p = malloc(100);\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void return3() { check("struct dev * f() {\n" " struct ABC *abc = malloc(100);\n" " return &abc->dev;\n" "}"); ASSERT_EQUALS("", errout.str()); } void return4() { // ticket #3862 // avoid false positives check("void f(char *p, int x) {\n" " if (x==12) {\n" " free(p);\n" " throw 1;\n" " }\n" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(char *p, int x) {\n" " if (x==12) {\n" " delete p;\n" " throw 1;\n" " }\n" " delete p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(char *p, int x) {\n" " if (x==12) {\n" " delete [] p;\n" " throw 1;\n" " }\n" " delete [] p;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void return5() { // ticket #6397 - conditional allocation/deallocation and conditional return // avoid false positives check("void f(int *p, int x) {\n" " if (x != 0) {\n" " free(p);\n" " }\n" " if (x != 0) {\n" " return;\n" " }\n" " *p = 0;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void return6() { // #8282 check("std::pair f(size_t n) {\n" " char* p = (char* )malloc(n);\n" " return {p, p};\n" "}", true); ASSERT_EQUALS("", errout.str()); } void return7() { // #9343 check("uint8_t *f() {\n" " void *x = malloc(1);\n" " return (uint8_t *)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("uint8_t f() {\n" " void *x = malloc(1);\n" " return (uint8_t)x;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); check("void** f() {\n" " void *x = malloc(1);\n" " return (void**)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return (long long)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return (void*)(short)x;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return (mytype)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" // Do not crash " void *x = malloc(1);\n" " return (mytype)y;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); } void return8() { check("void* f() {\n" " void *x = malloc(1);\n" " return (x);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return ((x));\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return ((((x))));\n" "}", true); ASSERT_EQUALS("", errout.str()); check("char* f() {\n" " void *x = malloc(1);\n" " return (char*)(x);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void test1() { // 3809 check("void f(double*&p) {\n" " p = malloc(0x100);\n" "}"); ASSERT_EQUALS("", errout.str()); } void test2() { // 3899 check("struct Fred {\n" " char *p;\n" " void f1() { free(p); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void test3() { // 3954 - reference pointer check("void f() {\n" " char *&p = x();\n" " p = malloc(10);\n" "};"); ASSERT_EQUALS("", errout.str()); } void test4() { // 5923 - static pointer check("void f() {\n" " static char *p;\n" " if (!p) p = malloc(10);\n" " if (x) { free(p); p = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void test5() { // unknown type check("void f() { Fred *p = malloc(10); }", true); ASSERT_EQUALS("[test.cpp:1]: (error) Memory leak: p\n", errout.str()); check("void f() { Fred *p = malloc(10); }", false); ASSERT_EQUALS("[test.c:1]: (error) Memory leak: p\n", errout.str()); check("void f() { Fred *p = new Fred; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { Fred fred = malloc(10); }", true); ASSERT_EQUALS("", errout.str()); } void throw1() { // 3987 - Execution reach a 'throw' check("void f() {\n" " char *p = malloc(10);\n" " throw 123;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " char *p;\n" " try {\n" " p = malloc(10);\n" " throw 123;\n" " } catch (...) { }\n" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void throw2() { // do not miss ::NS::Except() check("namespace NS {\n" " class Except {\n" " };\n" "}\n" "void foo(int i)\n" "{\n" " int *pi = new int;\n" " if (i == 42) {\n" " delete pi;\n" " throw ::NS::Except();\n" " }\n" " delete pi;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void configuration1() { // Possible leak => configuration is required for complete analysis // The user should be able to "white list" and "black list" functions. // possible leak. If the function 'x' deallocates the pointer or // takes the address, there is no leak. check("void f() {\n" " char *p = malloc(10);\n" " x(p);\n" "}"); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have configuration\n" "[test.c:4]: (information) --check-library: Function x() should have / configuration\n", errout.str()); } void configuration2() { // possible leak. If the function 'x' deallocates the pointer or // takes the address, there is no leak. check("void f() {\n" " char *p = malloc(10);\n" " x(&p);\n" "}"); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have configuration\n" "[test.c:4]: (information) --check-library: Function x() should have / configuration\n", errout.str()); } void configuration3() { const char * code = "void f() {\n" " char *p = malloc(10);\n" " if (set_data(p)) { }\n" "}"; check(code); ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n", errout.str()); check(code, true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function set_data() should have / configuration\n", errout.str()); code = "void f() {\n" " char *p = malloc(10);\n" " if (set_data(p)) { return; }\n" "}"; check(code); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function set_data() should have / configuration\n" "[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n" , errout.str()); check(code, true); ASSERT_EQUALS("[test.cpp:3]: (information) --check-library: Function set_data() should have / configuration\n" "[test.cpp:4]: (information) --check-library: Function set_data() should have / configuration\n" , errout.str()); } void configuration4() { check("void f() {\n" " char *p = malloc(10);\n" " int ret = set_data(p);\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n", errout.str()); } void ptrptr() { check("void f() {\n" " char **p = malloc(10);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void nestedAllocation() { check("void QueueDSMCCPacket(unsigned char *data, int length) {\n" " unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n" " m_dsmccQueue.enqueue(new DSMCCPacket(dataCopy));\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function DSMCCPacket() should have / configuration\n", errout.str()); check("void QueueDSMCCPacket(unsigned char *data, int length) {\n" " unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n" " m_dsmccQueue.enqueue(new DSMCCPacket(somethingunrelated));\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: dataCopy\n", errout.str()); check("void f() {\n" " char *buf = new char[1000];\n" " clist.push_back(new (std::nothrow) C(buf));\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function C() should have / configuration\n", errout.str()); } void testKeywords() { check("int main(int argc, char **argv) {\n" " double *new = malloc(1*sizeof(double));\n" " free(new);\n" " return 0;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void inlineFunction() { check("int test() {\n" " char *c;\n" " int ret() {\n" " free(c);\n" " return 0;\n" " }\n" " c = malloc(128);\n" " return ret();\n" "}"); ASSERT_EQUALS("", errout.str()); } // #8262 void smartPtrInContainer() { check("std::list< std::shared_ptr > mList;\n" "void test(){\n" " int *pt = new int(1);\n" " mList.push_back(std::shared_ptr(pt));\n" "}\n", true ); ASSERT_EQUALS("", errout.str()); } void functionCallCastConfig() { // #9652 Settings settingsFunctionCall = settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settingsFunctionCall.library.load(doc); check("void test_func()\n" "{\n" " char * buf = malloc(4);\n" " free_func((void *)(1), buf);\n" "}", settingsFunctionCall); ASSERT_EQUALS("[test.cpp:5]: (information) --check-library: Function free_func() should have / configuration\n", errout.str()); } }; REGISTER_TEST(TestLeakAutoVar) class TestLeakAutoVarRecursiveCountLimit : public TestFixture { public: TestLeakAutoVarRecursiveCountLimit() : TestFixture("TestLeakAutoVarRecursiveCountLimit") {} private: Settings settings; void checkP(const char code[], bool cpp = false) { // Clear the error buffer.. errout.str(""); // Raw tokens.. std::vector files(1, cpp?"test.cpp":"test.c"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenizer.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); // Check for leaks.. CheckLeakAutoVar c; settings.checkLibrary = true; settings.addEnabled("information"); c.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097 } void recursiveCountLimit() { // #5872 #6157 #9097 ASSERT_THROW(checkP("#define ONE else if (0) { }\n" "#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n" "#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n" "#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n" "void foo() {\n" " if (0) { }\n" " THOU THOU\n" "}"), InternalError); ASSERT_NO_THROW(checkP("#define ONE if (0) { }\n" "#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n" "#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n" "#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n" "void foo() {\n" " if (0) { }\n" " THOU THOU\n" "}")); } }; REGISTER_TEST(TestLeakAutoVarRecursiveCountLimit) class TestLeakAutoVarStrcpy : public TestFixture { public: TestLeakAutoVarStrcpy() : TestFixture("TestLeakAutoVarStrcpy") {} private: Settings settings; void check_(const char* file, int line, const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for leaks.. CheckLeakAutoVar checkLeak; settings.checkLibrary = true; settings.severity.enable(Severity::information); checkLeak.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(returnedValue); // #9298 TEST_CASE(fclose_false_positive); // #9575 } void returnedValue() { // #9298 check("char *m;\n" "void strcpy_returnedvalue(const char* str)\n" "{\n" " char* ptr = new char[strlen(str)+1];\n" " m = strcpy(ptr, str);\n" "}"); ASSERT_EQUALS("", errout.str()); } void fclose_false_positive() { // #9575 check("int f(FILE *fp) { return fclose(fp); }"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestLeakAutoVarStrcpy) class TestLeakAutoVarWindows : public TestFixture { public: TestLeakAutoVarWindows() : TestFixture("TestLeakAutoVarWindows") {} private: Settings settings; void check_(const char* file, int line, const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.c"), file, line); // Check for leaks.. CheckLeakAutoVar checkLeak; checkLeak.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { LOAD_LIB_2(settings.library, "windows.cfg"); TEST_CASE(heapDoubleFree); } void heapDoubleFree() { check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" " HeapFree(MyHeap, 0, b);" " HeapDestroy(MyHeap);" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {" " int *a = HeapAlloc(GetProcessHeap(), 0, sizeof(int));" " int *b = HeapAlloc(GetProcessHeap(), 0, sizeof(int));" " HeapFree(GetProcessHeap(), 0, a);" " HeapFree(GetProcessHeap(), 0, b);" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" " HeapDestroy(MyHeap);" "}"); ASSERT_EQUALS("[test.c:1]: (error) Memory leak: b\n", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" " HeapFree(MyHeap, 0, b);" "}"); TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap", "", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" "}"); TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: MyHeap\n" "[test.c:1] (error) Memory leak: b", "[test.c:1]: (error) Memory leak: b\n", errout.str()); } }; REGISTER_TEST(TestLeakAutoVarWindows) cppcheck-2.7/test/testlibrary.cpp000066400000000000000000001326311417746362400172150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include #include #include #define ASSERT_EQ(expected, actual) ASSERT(expected == actual) class TestLibrary : public TestFixture { public: TestLibrary() : TestFixture("TestLibrary") {} private: Settings settings; void run() OVERRIDE { TEST_CASE(isCompliantValidationExpression); TEST_CASE(empty); TEST_CASE(function); TEST_CASE(function_match_scope); TEST_CASE(function_match_args); TEST_CASE(function_match_args_default); TEST_CASE(function_match_var); TEST_CASE(function_arg); TEST_CASE(function_arg_any); TEST_CASE(function_arg_variadic); TEST_CASE(function_arg_direction); TEST_CASE(function_arg_valid); TEST_CASE(function_arg_minsize); TEST_CASE(function_namespace); TEST_CASE(function_method); TEST_CASE(function_baseClassMethod); // calling method in base class TEST_CASE(function_warn); TEST_CASE(memory); TEST_CASE(memory2); // define extra "free" allocation functions TEST_CASE(memory3); TEST_CASE(resource); TEST_CASE(podtype); TEST_CASE(container); TEST_CASE(version); TEST_CASE(loadLibErrors); } static Library::Error readLibrary(Library& library, const char* xmldata) { tinyxml2::XMLDocument doc; doc.Parse(xmldata); return library.load(doc); } void isCompliantValidationExpression() { ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1:")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression(":1")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1,42")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1,-42")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1.0:42.0")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1.175494e-38:3.402823e+38")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1.175494e-38,3.402823e+38")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1.175494e-38:")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression(":1.175494e-38")); ASSERT_EQUALS(true, Library::isCompliantValidationExpression(":42.0")); // Robustness tests ASSERT_EQUALS(false, Library::isCompliantValidationExpression(nullptr)); ASSERT_EQUALS(false, Library::isCompliantValidationExpression("x")); } void empty() const { // Reading an empty library file is considered to be OK const char xmldata[] = "\n"; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); } void function() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; TokenList tokenList(nullptr); std::istringstream istr("foo();"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(library.functions.size(), 1U); ASSERT(library.functions.at("foo").argumentChecks.empty()); ASSERT(library.isnotnoreturn(tokenList.front())); } void function_match_scope() const { const char xmldata[] = "\n" "\n" " \n" " " " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); { TokenList tokenList(nullptr); std::istringstream istr("fred.foo(123);"); // <- wrong scope, not library function tokenList.createTokens(istr); ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2))); } { TokenList tokenList(nullptr); std::istringstream istr("Fred::foo(123);"); // <- wrong scope, not library function tokenList.createTokens(istr); ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2))); } } void function_match_args() const { const char xmldata[] = "\n" "\n" " \n" " " " \n" ""; TokenList tokenList(nullptr); std::istringstream istr("foo();"); // <- too few arguments, not library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.isNotLibraryFunction(tokenList.front())); } void function_match_args_default() const { const char xmldata[] = "\n" "\n" " \n" " " " " " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); { TokenList tokenList(nullptr); std::istringstream istr("foo();"); // <- too few arguments, not library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(library.isNotLibraryFunction(tokenList.front())); } { TokenList tokenList(nullptr); std::istringstream istr("foo(a);"); // <- library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(!library.isNotLibraryFunction(tokenList.front())); } { TokenList tokenList(nullptr); std::istringstream istr("foo(a, b);"); // <- library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(!library.isNotLibraryFunction(tokenList.front())); } { TokenList tokenList(nullptr); std::istringstream istr("foo(a, b, c);"); // <- too much arguments, not library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(library.isNotLibraryFunction(tokenList.front())); } } void function_match_var() const { const char xmldata[] = "\n" "\n" " \n" " " " \n" ""; TokenList tokenList(nullptr); std::istringstream istr("Fred foo(123);"); // <- Variable declaration, not library function tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); tokenList.front()->next()->varId(1); Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.isNotLibraryFunction(tokenList.front()->next())); } void function_arg() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[1].notuninit); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[2].notnull); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[3].formatstr); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[4].strz); ASSERT_EQUALS(false, library.functions["foo"].argumentChecks[4].optional); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].notbool); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].optional); } void function_arg_any() const { const char xmldata[] = "\n" "\n" "\n" " \n" "\n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[-1].notuninit); } void function_arg_variadic() const { const char xmldata[] = "\n" "\n" "\n" " \n" " \n" "\n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[-1].notuninit); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d,e);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); ASSERT_EQUALS(false, library.isuninitargbad(tokenList.front(), 1)); ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 2)); ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 3)); ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 4)); } void function_arg_direction() const { const char xmldata[] = "\n" "\n" "\n" " \n" " \n" " \n" " \n" "\n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); ASSERT(Library::ArgumentChecks::Direction::DIR_IN == library.getArgDirection(tokenList.front(), 1)); ASSERT(Library::ArgumentChecks::Direction::DIR_OUT == library.getArgDirection(tokenList.front(), 2)); ASSERT(Library::ArgumentChecks::Direction::DIR_INOUT == library.getArgDirection(tokenList.front(), 3)); ASSERT(Library::ArgumentChecks::Direction::DIR_UNKNOWN == library.getArgDirection(tokenList.front(), 4)); } void function_arg_valid() const { const char xmldata[] = "\n" "\n" " \n" " 1:\n" " -7:0\n" " 1:5,8\n" " -1,5\n" " :1,5\n" " 1.5:\n" " -6.7:-5.5,-3.3:-2.7\n" " 0.0:\n" " :2.0\n" " 0.0\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d,e,f,g,h,i,j);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); // 1- ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, -10)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, -10.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, 0.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 1.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 10)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 10.0)); // -7-0 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, -10)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -10.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.5)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.1)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -7)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -7.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -3)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.5)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, 0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, 0.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 0.5)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, 1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 1.0)); // 1-5,8 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 0.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 1.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 3)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 3.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 5)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 5.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 6)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 6.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 7)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 7.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 8)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 8.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 9)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 9.0)); // -1,5 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 4, -10)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -10.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 4, -1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -1.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.000001)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.5)); // :1,5 ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, -10)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, -10.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, 1.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 5, 2)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 5, 2.0)); // 1.5: ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 0.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 1.499999)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 6, 1.5)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 2)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 10)); // -6.7:-5.5,-3.3:-2.7 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -7)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -7.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -6.7000001)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.7)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -6)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -5.5)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -5.4999999)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -3.3000001)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.3)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -3)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -2.7)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.6999999)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -2)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 0.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 3)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 3.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 6)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 6.0)); // 0.0: ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 8, -1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -1.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -0.00000001)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.000000001)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 1.0)); // :2.0 ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, -1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, -1.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, 2)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, 2.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 2.00000001)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 9, 200)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 200.0)); // 0.0 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 10, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 10, 0.0)); } void function_arg_minsize() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); // arg1: type=strlen arg2 const std::vector *minsizes = library.argminsizes(tokenList.front(),1); ASSERT_EQUALS(true, minsizes != nullptr); ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); if (minsizes && minsizes->size() == 1U) { const Library::ArgumentChecks::MinSize &m = minsizes->front(); ASSERT_EQUALS(true, Library::ArgumentChecks::MinSize::Type::STRLEN == m.type); ASSERT_EQUALS(2, m.arg); } // arg2: type=argvalue arg3 minsizes = library.argminsizes(tokenList.front(), 2); ASSERT_EQUALS(true, minsizes != nullptr); ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); if (minsizes && minsizes->size() == 1U) { const Library::ArgumentChecks::MinSize &m = minsizes->front(); ASSERT_EQUALS(true, Library::ArgumentChecks::MinSize::Type::ARGVALUE == m.type); ASSERT_EQUALS(3, m.arg); } // arg4: type=value minsizes = library.argminsizes(tokenList.front(), 4); ASSERT_EQUALS(true, minsizes != nullptr); ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); if (minsizes && minsizes->size() == 1U) { const Library::ArgumentChecks::MinSize &m = minsizes->front(); ASSERT(Library::ArgumentChecks::MinSize::Type::VALUE == m.type); ASSERT_EQUALS(500, m.value); } } void function_namespace() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(library.functions.size(), 2U); ASSERT(library.functions.at("Foo::foo").argumentChecks.empty()); ASSERT(library.functions.at("bar").argumentChecks.empty()); { TokenList tokenList(nullptr); std::istringstream istr("Foo::foo();"); tokenList.createTokens(istr); ASSERT(library.isnotnoreturn(tokenList.front()->tokAt(2))); } { TokenList tokenList(nullptr); std::istringstream istr("bar();"); tokenList.createTokens(istr); ASSERT(library.isnotnoreturn(tokenList.front())); } } void function_method() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(library.functions.size(), 1U); { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("CString str; str.Format();"); ASSERT(tokenizer.tokenize(istr, "test.cpp")); ASSERT(library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format"))); } { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("HardDrive hd; hd.Format();"); ASSERT(tokenizer.tokenize(istr, "test.cpp")); ASSERT(!library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format"))); } } void function_baseClassMethod() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("struct X : public Base { void dostuff() { f(0); } };"); ASSERT(tokenizer.tokenize(istr, "test.cpp")); ASSERT(library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); } { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("struct X : public Base { void dostuff() { f(1,2); } };"); ASSERT(tokenizer.tokenize(istr, "test.cpp")); ASSERT(!library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); } } void function_warn() const { const char xmldata[] = "\n" "\n" " \n" " Message\n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("a(); b();"); tokenList.createTokens(istr); const Library::WarnInfo* a = library.getWarnInfo(tokenList.front()); const Library::WarnInfo* b = library.getWarnInfo(tokenList.front()->tokAt(4)); ASSERT_EQUALS(2, library.functionwarn.size()); ASSERT(a && b); if (a && b) { ASSERT_EQUALS("Message", a->message); ASSERT_EQUALS(Severity::style, a->severity); ASSERT_EQUALS(Standards::C99, a->standards.c); ASSERT_EQUALS(Standards::CPP03, a->standards.cpp); ASSERT_EQUALS("Obsolescent function 'b' called. It is recommended to use 'c', 'd' or 'e' instead.", b->message); ASSERT_EQUALS(Severity::performance, b->severity); ASSERT_EQUALS(Standards::C89, b->standards.c); ASSERT_EQUALS(Standards::CPP11, b->standards.cpp); } } void memory() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); ASSERT(Library::ismemory(library.getAllocFuncInfo("CreateX"))); ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX"); ASSERT(af && af->arg == -1); const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX"); ASSERT(df && df->arg == 1); } void memory2() const { const char xmldata1[] = "\n" "\n" " \n" " malloc\n" " free\n" " \n" ""; const char xmldata2[] = "\n" "\n" " \n" " foo\n" " free\n" " \n" ""; Library library; ASSERT_EQUALS(true, library.loadxmldata(xmldata1, sizeof(xmldata1))); ASSERT_EQUALS(true, library.loadxmldata(xmldata2, sizeof(xmldata2))); ASSERT_EQUALS(library.deallocId("free"), library.allocId("malloc")); ASSERT_EQUALS(library.deallocId("free"), library.allocId("foo")); } void memory3() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX"); ASSERT(af && af->arg == 5 && !af->initData); const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX"); ASSERT(df && df->arg == 2); } void resource() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); ASSERT(Library::isresource(library.allocId("CreateX"))); ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); } void podtype() const { { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); // s8 { const struct Library::PodType * const type = library.podtype("s8"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(1U, type->size); ASSERT_EQUALS('s', type->sign); } } // u8 { const struct Library::PodType * const type = library.podtype("u8"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(1U, type->size); ASSERT_EQUALS('u', type->sign); } } // u16 { const struct Library::PodType * const type = library.podtype("u16"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(2U, type->size); ASSERT_EQUALS('u', type->sign); } } // s16 { const struct Library::PodType * const type = library.podtype("s16"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(2U, type->size); ASSERT_EQUALS('s', type->sign); } } // robustness test: provide cfg without PodType { const struct Library::PodType * const type = library.podtype("nonExistingPodType"); ASSERT_EQUALS(true, type == nullptr); } } } void container() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" // Inherits all but templateParameter " \n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::ErrorCode::OK == (readLibrary(library, xmldata)).errorcode); Library::Container& A = library.containers["A"]; Library::Container& B = library.containers["B"]; Library::Container& C = library.containers["C"]; ASSERT_EQUALS(A.type_templateArgNo, 1); ASSERT_EQUALS(A.size_templateArgNo, 4); ASSERT_EQUALS(A.startPattern, "std :: A <"); ASSERT_EQUALS(A.endPattern, "> !!::"); ASSERT_EQUALS(A.itEndPattern, "> :: iterator"); ASSERT_EQUALS(A.stdStringLike, false); ASSERT_EQUALS(A.arrayLike_indexOp, false); ASSERT_EQUALS(A.opLessAllowed, true); ASSERT_EQ(Library::Container::Yield::SIZE, A.getYield("size")); ASSERT_EQ(Library::Container::Yield::EMPTY, A.getYield("empty")); ASSERT_EQ(Library::Container::Yield::AT_INDEX, A.getYield("at")); ASSERT_EQ(Library::Container::Yield::START_ITERATOR, A.getYield("begin")); ASSERT_EQ(Library::Container::Yield::END_ITERATOR, A.getYield("end")); ASSERT_EQ(Library::Container::Yield::BUFFER, A.getYield("data")); ASSERT_EQ(Library::Container::Yield::BUFFER_NT, A.getYield("c_str")); ASSERT_EQ(Library::Container::Yield::ITEM, A.getYield("front")); ASSERT_EQ(Library::Container::Yield::NO_YIELD, A.getYield("foo")); ASSERT_EQ(Library::Container::Action::RESIZE, A.getAction("resize")); ASSERT_EQ(Library::Container::Action::CLEAR, A.getAction("clear")); ASSERT_EQ(Library::Container::Action::PUSH, A.getAction("push_back")); ASSERT_EQ(Library::Container::Action::POP, A.getAction("pop_back")); ASSERT_EQ(Library::Container::Action::FIND, A.getAction("find")); ASSERT_EQ(Library::Container::Action::NO_ACTION, A.getAction("foo")); ASSERT_EQUALS(B.type_templateArgNo, 1); ASSERT_EQUALS(B.size_templateArgNo, 3); ASSERT_EQUALS(B.startPattern, "std :: B <"); ASSERT_EQUALS(B.endPattern, "> !!::"); ASSERT_EQUALS(B.itEndPattern, "> :: iterator"); ASSERT_EQUALS(B.functions.size(), A.functions.size()); ASSERT_EQUALS(B.opLessAllowed, false); ASSERT(C.functions.empty()); ASSERT_EQUALS(C.type_templateArgNo, -1); ASSERT_EQUALS(C.size_templateArgNo, -1); ASSERT_EQUALS(C.stdStringLike, true); ASSERT_EQUALS(C.arrayLike_indexOp, true); } void version() const { { const char xmldata[] = "\n" "\n" ""; Library library; const Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(true, err.errorcode == Library::ErrorCode::OK); } { const char xmldata[] = "\n" "\n" ""; Library library; const Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(true, err.errorcode == Library::ErrorCode::OK); } { const char xmldata[] = "\n" "\n" ""; Library library; const Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(true, err.errorcode == Library::ErrorCode::UNSUPPORTED_FORMAT); } } void loadLibError(const char xmldata[], Library::ErrorCode errorcode, const char* file, unsigned line) const { Library library; assertEquals(file, line, true, errorcode == readLibrary(library, xmldata).errorcode); } #define LOADLIBERROR(xmldata, errorcode) loadLibError(xmldata, errorcode, __FILE__, __LINE__) #define LOADLIB_ERROR_INVALID_RANGE(valid) LOADLIBERROR("\n" \ "\n" \ "\n" \ "\n" \ "" valid "\n" \ "\n" \ "\n" \ "", \ Library::ErrorCode::BAD_ATTRIBUTE_VALUE) void loadLibErrors() const { LOADLIBERROR("\n" "\n" " \n" "", Library::ErrorCode::UNKNOWN_ELEMENT); // #define without attributes LOADLIBERROR("\n" "\n" " \n" // no attributes provided at all "", Library::ErrorCode::MISSING_ATTRIBUTE); // #define with name but without value LOADLIBERROR("\n" "\n" " \n" // no value provided "", Library::ErrorCode::MISSING_ATTRIBUTE); LOADLIBERROR("\n" "\n" " \n" // no name provided "", Library::ErrorCode::MISSING_ATTRIBUTE); LOADLIBERROR("\n" "\n" "", Library::ErrorCode::UNSUPPORTED_FORMAT); // empty range LOADLIB_ERROR_INVALID_RANGE(""); // letter as range LOADLIB_ERROR_INVALID_RANGE("a"); // letter and number as range LOADLIB_ERROR_INVALID_RANGE("1a"); // digit followed by dash LOADLIB_ERROR_INVALID_RANGE("0:2-1"); // single dash LOADLIB_ERROR_INVALID_RANGE("-"); // range with multiple colons LOADLIB_ERROR_INVALID_RANGE("1:2:3"); // extra dot LOADLIB_ERROR_INVALID_RANGE("1.0.0:10"); // consecutive dots LOADLIB_ERROR_INVALID_RANGE("1..0:10"); // dot followed by dash LOADLIB_ERROR_INVALID_RANGE("1.-0:10"); // dot without preceding number LOADLIB_ERROR_INVALID_RANGE(".5:10"); // dash followed by dot LOADLIB_ERROR_INVALID_RANGE("-.5:10"); // colon followed by dot without preceding number LOADLIB_ERROR_INVALID_RANGE("0:.5"); // colon followed by dash followed by dot LOADLIB_ERROR_INVALID_RANGE("-10:-.5"); // dot not followed by number LOADLIB_ERROR_INVALID_RANGE("1:5."); // dot not followed by number LOADLIB_ERROR_INVALID_RANGE("1.:5"); // dot followed by comma LOADLIB_ERROR_INVALID_RANGE("1:5.,6:10"); // comma followed by dot LOADLIB_ERROR_INVALID_RANGE("-10:0,.5:"); } }; REGISTER_TEST(TestLibrary) cppcheck-2.7/test/testmathlib.cpp000066400000000000000000002013571417746362400171730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "mathlib.h" #include "testsuite.h" #include #include struct InternalError; class TestMathLib : public TestFixture { public: TestMathLib() : TestFixture("TestMathLib") {} private: void run() OVERRIDE { TEST_CASE(isint); TEST_CASE(isbin); TEST_CASE(isdec); TEST_CASE(isoct); TEST_CASE(isFloatHex); TEST_CASE(isIntHex); TEST_CASE(isValidIntegerSuffix); TEST_CASE(isnegative); TEST_CASE(ispositive); TEST_CASE(isFloat); TEST_CASE(isDecimalFloat); TEST_CASE(isGreater); TEST_CASE(isGreaterEqual); TEST_CASE(isEqual); TEST_CASE(isNotEqual); TEST_CASE(isLess); TEST_CASE(isLessEqual); TEST_CASE(calculate); TEST_CASE(calculate1); TEST_CASE(typesuffix); TEST_CASE(toLongNumber); TEST_CASE(toDoubleNumber); TEST_CASE(naninf); TEST_CASE(isNullValue); TEST_CASE(incdec); TEST_CASE(sin); TEST_CASE(cos); TEST_CASE(tan); TEST_CASE(abs); TEST_CASE(toString); TEST_CASE(CPP14DigitSeparators); } void isGreater() const { ASSERT_EQUALS(true, MathLib::isGreater("1.0", "0.001")); ASSERT_EQUALS(false, MathLib::isGreater("-1.0", "0.001")); } void isGreaterEqual() const { ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.00", "1.0")); ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.001", "1.0")); ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.0", "0.001")); ASSERT_EQUALS(false, MathLib::isGreaterEqual("-1.0", "0.001")); } void isEqual() const { ASSERT_EQUALS(true, MathLib::isEqual("1.0", "1.0")); ASSERT_EQUALS(false, MathLib::isEqual("1.", "1.01")); ASSERT_EQUALS(true, MathLib::isEqual("0.1","1.0E-1")); } void isNotEqual() const { ASSERT_EQUALS(false, MathLib::isNotEqual("1.0", "1.0")); ASSERT_EQUALS(true, MathLib::isNotEqual("1.", "1.01")); } void isLess() const { ASSERT_EQUALS(false, MathLib::isLess("1.0", "0.001")); ASSERT_EQUALS(true, MathLib::isLess("-1.0", "0.001")); } void isLessEqual() const { ASSERT_EQUALS(true, MathLib::isLessEqual("1.00", "1.0")); ASSERT_EQUALS(false, MathLib::isLessEqual("1.001", "1.0")); ASSERT_EQUALS(false, MathLib::isLessEqual("1.0", "0.001")); ASSERT_EQUALS(true, MathLib::isLessEqual("-1.0", "0.001")); } void calculate() const { // addition ASSERT_EQUALS("256", MathLib::add("0xff", "1")); ASSERT_EQUALS("249", MathLib::add("250", "-1")); ASSERT_EQUALS("251", MathLib::add("250", "1")); ASSERT_EQUALS("-2.0", MathLib::add("-1.", "-1")); ASSERT_EQUALS("-1", MathLib::add("0", "-1")); ASSERT_EQUALS("1", MathLib::add("1", "0")); ASSERT_EQUALS("0.0", MathLib::add("0", "0.")); ASSERT_EQUALS("1.00000001", MathLib::add("1", "0.00000001")); // #4016 ASSERT_EQUALS("30666.22", MathLib::add("30666.22", "0.0")); // #4068 // subtraction ASSERT_EQUALS("254", MathLib::subtract("0xff", "1")); ASSERT_EQUALS("251", MathLib::subtract("250", "-1")); ASSERT_EQUALS("249", MathLib::subtract("250", "1")); ASSERT_EQUALS("0.0", MathLib::subtract("-1.", "-1")); ASSERT_EQUALS("1", MathLib::subtract("0", "-1")); ASSERT_EQUALS("1", MathLib::subtract("1", "0")); ASSERT_EQUALS("0.0", MathLib::subtract("0", "0.")); ASSERT_EQUALS("0.99999999", MathLib::subtract("1", "0.00000001")); // #4016 ASSERT_EQUALS("30666.22", MathLib::subtract("30666.22", "0.0")); // #4068 ASSERT_EQUALS("0.0", MathLib::subtract("0.0", "0.0")); // multiply ASSERT_EQUALS("-0.003", MathLib::multiply("-1e-3", "3")); ASSERT_EQUALS("-11.96", MathLib::multiply("-2.3", "5.2")); ASSERT_EQUALS("3000.0", MathLib::multiply("1E3", "3")); ASSERT_EQUALS("3000.0", MathLib::multiply("1E+3", "3")); ASSERT_EQUALS("3000.0", MathLib::multiply("1.0E3", "3")); ASSERT_EQUALS("-3000.0", MathLib::multiply("-1.0E3", "3")); ASSERT_EQUALS("-3000.0", MathLib::multiply("-1.0E+3", "3")); ASSERT_EQUALS("0.0", MathLib::multiply("+1.0E+3", "0")); ASSERT_EQUALS("2147483648", MathLib::multiply("2","1073741824")); ASSERT_EQUALS("536870912", MathLib::multiply("512","1048576")); // divide ASSERT_EQUALS("1", MathLib::divide("1", "1")); ASSERT_EQUALS("0", MathLib::divide("0", "1")); ASSERT_EQUALS("5", MathLib::divide("-10", "-2")); ASSERT_EQUALS("-2.5", MathLib::divide("-10.", "4")); ASSERT_EQUALS("2.5", MathLib::divide("-10.", "-4")); ASSERT_EQUALS("5.0", MathLib::divide("25.5", "5.1")); ASSERT_EQUALS("7.0", MathLib::divide("21.", "3")); ASSERT_EQUALS("1", MathLib::divide("3", "2")); ASSERT_THROW(MathLib::divide("123", "0"), InternalError); // decimal zero: throw ASSERT_THROW(MathLib::divide("123", "00"), InternalError); // octal zero: throw ASSERT_THROW(MathLib::divide("123", "0x0"), InternalError); // hex zero: throw MathLib::divide("123", "0.0f"); // float zero: don't throw MathLib::divide("123", "0.0"); // double zero: don't throw MathLib::divide("123", "0.0L"); // long double zero: don't throw ASSERT_THROW(MathLib::divide("-9223372036854775808", "-1"), InternalError); // #4520 - out of range => throw ASSERT_EQUALS("4611686018427387904", MathLib::divide("-9223372036854775808", "-2")); // #6679 // invoke for each supported action ASSERT_EQUALS("3", MathLib::calculate("2", "1", '+')); ASSERT_EQUALS("1", MathLib::calculate("2", "1", '-')); ASSERT_EQUALS("2", MathLib::calculate("2", "1", '*')); ASSERT_EQUALS("2", MathLib::calculate("2", "1", '/')); ASSERT_EQUALS("0", MathLib::calculate("2", "1", '%')); ASSERT_EQUALS("0", MathLib::calculate("1", "2", '&')); ASSERT_EQUALS("1", MathLib::calculate("1", "1", '|')); ASSERT_EQUALS("1", MathLib::calculate("0", "1", '^')); // Unknown action should throw exception ASSERT_THROW(MathLib::calculate("1","2",'j'),InternalError); } void calculate1() const { ASSERT_EQUALS("0", MathLib::calculate("2", "1", '%')); ASSERT_EQUALS("2", MathLib::calculate("12", "5", '%')); ASSERT_EQUALS("1", MathLib::calculate("100", "3", '%')); #ifndef TEST_MATHLIB_VALUE // floating point modulo is not defined in C/C++ ASSERT_EQUALS("0.0", MathLib::calculate("2.0", "1.0", '%')); ASSERT_EQUALS("12.0", MathLib::calculate("12.0", "13.0", '%')); ASSERT_EQUALS("1.3", MathLib::calculate("5.3", "2.0", '%')); ASSERT_EQUALS("1.7", MathLib::calculate("18.5", "4.2", '%')); MathLib::calculate("123", "0.0", '%'); // don't throw #endif ASSERT_THROW(MathLib::calculate("123", "0", '%'), InternalError); // throw ASSERT_EQUALS("0", MathLib::calculate("1", "1", '^')); ASSERT_EQUALS("3", MathLib::calculate("2", "1", '^')); } void typesuffix() const { ASSERT_EQUALS("2", MathLib::add("1", "1")); ASSERT_EQUALS("2U", MathLib::add("1U", "1")); ASSERT_EQUALS("2L", MathLib::add("1L", "1")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1")); ASSERT_EQUALS("2LL", MathLib::add("1i64", "1")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1")); ASSERT_EQUALS("2ULL", MathLib::add("1ui64","1")); ASSERT_EQUALS("2U", MathLib::add("1", "1U")); ASSERT_EQUALS("2U", MathLib::add("1U", "1U")); ASSERT_EQUALS("2L", MathLib::add("1L", "1U")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1U")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1U")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1U")); ASSERT_EQUALS("2L", MathLib::add("1", "1L")); ASSERT_EQUALS("2L", MathLib::add("1U", "1L")); ASSERT_EQUALS("2L", MathLib::add("1L", "1L")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1L")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1L")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1L")); ASSERT_EQUALS("2UL", MathLib::add("1", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1U", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1L", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1UL")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1UL")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1", "1LU")); ASSERT_EQUALS("2UL", MathLib::add("1U", "1LU")); ASSERT_EQUALS("2UL", MathLib::add("1L", "1LU")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1LU")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1LU")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LU")); ASSERT_EQUALS("2LL", MathLib::add("1", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1U", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1L", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1UL", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1LL")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LL")); ASSERT_EQUALS("2ULL", MathLib::add("1", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1U", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1L", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1UL", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1LL", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1U", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1L", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1UL", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1LL", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LLU")); } void toLongNumber() const { // from hex ASSERT_EQUALS(0, MathLib::toLongNumber("0x0")); ASSERT_EQUALS(0, MathLib::toLongNumber("-0x0")); ASSERT_EQUALS(0, MathLib::toLongNumber("+0x0")); ASSERT_EQUALS(10, MathLib::toLongNumber("0xa")); ASSERT_EQUALS(10995, MathLib::toLongNumber("0x2AF3")); ASSERT_EQUALS(-10, MathLib::toLongNumber("-0xa")); ASSERT_EQUALS(-10995, MathLib::toLongNumber("-0x2AF3")); ASSERT_EQUALS(10, MathLib::toLongNumber("+0xa")); ASSERT_EQUALS(10995, MathLib::toLongNumber("+0x2AF3")); // from octal ASSERT_EQUALS(8, MathLib::toLongNumber("010")); ASSERT_EQUALS(8, MathLib::toLongNumber("+010")); ASSERT_EQUALS(-8, MathLib::toLongNumber("-010")); ASSERT_EQUALS(125, MathLib::toLongNumber("0175")); ASSERT_EQUALS(125, MathLib::toLongNumber("+0175")); ASSERT_EQUALS(-125, MathLib::toLongNumber("-0175")); // from binary ASSERT_EQUALS(0, MathLib::toLongNumber("0b0")); ASSERT_EQUALS(1, MathLib::toLongNumber("0b1")); ASSERT_EQUALS(1, MathLib::toLongNumber("0b1U")); ASSERT_EQUALS(1, MathLib::toLongNumber("0b1L")); ASSERT_EQUALS(1, MathLib::toLongNumber("0b1LU")); ASSERT_EQUALS(1, MathLib::toLongNumber("0b1LL")); ASSERT_EQUALS(1, MathLib::toLongNumber("0b1LLU")); ASSERT_EQUALS(1, MathLib::toLongNumber("+0b1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-0b1")); ASSERT_EQUALS(215, MathLib::toLongNumber("0b11010111")); ASSERT_EQUALS(-215, MathLib::toLongNumber("-0b11010111")); ASSERT_EQUALS(215, MathLib::toLongNumber("0B11010111")); // from base 10 ASSERT_EQUALS(10, MathLib::toLongNumber("10")); ASSERT_EQUALS(10, MathLib::toLongNumber("10.")); ASSERT_EQUALS(10, MathLib::toLongNumber("10.0")); ASSERT_EQUALS(100, MathLib::toLongNumber("10E+1")); ASSERT_EQUALS(1, MathLib::toLongNumber("10E-1")); ASSERT_EQUALS(100, MathLib::toLongNumber("+10E+1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-10E-1")); ASSERT_EQUALS(100, MathLib::toLongNumber("+10.E+1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-10.E-1")); ASSERT_EQUALS(100, MathLib::toLongNumber("+10.0E+1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-10.0E-1")); // from char ASSERT_EQUALS((int)('A'), MathLib::toLongNumber("'A'")); ASSERT_EQUALS((int)('\x10'), MathLib::toLongNumber("'\\x10'")); ASSERT_EQUALS((int)('\100'), MathLib::toLongNumber("'\\100'")); ASSERT_EQUALS((int)('\200'), MathLib::toLongNumber("'\\200'")); ASSERT_EQUALS((int)(L'A'), MathLib::toLongNumber("L'A'")); ASSERT_EQUALS(-8552249625308161526, MathLib::toLongNumber("0x89504e470d0a1a0a")); ASSERT_EQUALS(-8481036456200365558, MathLib::toLongNumber("0x8a4d4e470d0a1a0a")); ASSERT_EQUALS(9894494448401390090ULL, MathLib::toULongNumber("0x89504e470d0a1a0a")); ASSERT_EQUALS(9965707617509186058ULL, MathLib::toULongNumber("0x8a4d4e470d0a1a0a")); // zero input ASSERT_EQUALS(0, MathLib::toULongNumber("0")); ASSERT_EQUALS(0, MathLib::toULongNumber("-0")); ASSERT_EQUALS(0, MathLib::toULongNumber("+0")); ASSERT_EQUALS(0U, MathLib::toULongNumber("0U")); ASSERT_EQUALS(0, MathLib::toULongNumber("-0x0")); ASSERT_EQUALS(1U, MathLib::toULongNumber("1U")); ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); ASSERT_EQUALS(0xFF00000000000000UL, MathLib::toULongNumber("0xFF00000000000000UL")); ASSERT_EQUALS(0x0A00000000000000UL, MathLib::toULongNumber("0x0A00000000000000UL")); ASSERT_EQUALS(0, MathLib::toULongNumber("0b0")); ASSERT_EQUALS(1, MathLib::toULongNumber("0b1")); ASSERT_EQUALS(1, MathLib::toULongNumber("0b1U")); ASSERT_EQUALS(1, MathLib::toULongNumber("0b1L")); ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LU")); ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LL")); ASSERT_EQUALS(1, MathLib::toULongNumber("0b1LLU")); ASSERT_EQUALS(9U, MathLib::toULongNumber("011")); ASSERT_EQUALS(5U, MathLib::toULongNumber("0b101")); // from long long /* * ASSERT_EQUALS(0xFF00000000000000LL, MathLib::toLongNumber("0xFF00000000000000LL")); * This does not work in a portable way! * While it succeeds on 32bit Visual Studio it fails on Linux 64bit because it is greater than 0x7FFFFFFFFFFFFFFF (=LLONG_MAX) */ ASSERT_EQUALS(0x0A00000000000000LL, MathLib::toLongNumber("0x0A00000000000000LL")); // min/max numeric limits ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toLongNumber(std::to_string(std::numeric_limits::min()))); ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toLongNumber(std::to_string(std::numeric_limits::max()))); ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toLongNumber(std::to_string(std::numeric_limits::min()))); ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toLongNumber(std::to_string(std::numeric_limits::max()))); ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits::min()))); ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits::max()))); ASSERT_EQUALS(std::numeric_limits::min(), MathLib::toULongNumber(std::to_string(std::numeric_limits::min()))); ASSERT_EQUALS(std::numeric_limits::max(), MathLib::toULongNumber(std::to_string(std::numeric_limits::max()))); // min/max and out-of-bounds - hex { const MathLib::bigint i = 0xFFFFFFFFFFFFFFFF; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("0xFFFFFFFFFFFFFFFF")); } { const MathLib::biguint u = 0xFFFFFFFFFFFFFFFF; ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); ASSERT_EQUALS(u, MathLib::toULongNumber("0xFFFFFFFFFFFFFFFF")); } { const MathLib::bigint i = -0xFFFFFFFFFFFFFFFF; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("-0xFFFFFFFFFFFFFFFF")); } { const MathLib::biguint u = -0xFFFFFFFFFFFFFFFF; ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); ASSERT_EQUALS(u, MathLib::toULongNumber("-0xFFFFFFFFFFFFFFFF")); } ASSERT_THROW(MathLib::toLongNumber("0x10000000000000000"), InternalError); ASSERT_THROW(MathLib::toULongNumber("0x10000000000000000"), InternalError); ASSERT_THROW(MathLib::toLongNumber("-0x10000000000000000"), InternalError); ASSERT_THROW(MathLib::toULongNumber("-0x10000000000000000"), InternalError); // min/max and out-of-bounds - octal { const MathLib::bigint i = 01777777777777777777777; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("01777777777777777777777")); } { const MathLib::biguint u = 01777777777777777777777; ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); ASSERT_EQUALS(u, MathLib::toULongNumber("01777777777777777777777")); } { const MathLib::bigint i = -01777777777777777777777; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("-01777777777777777777777")); } { const MathLib::biguint u = -01777777777777777777777; ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); ASSERT_EQUALS(u, MathLib::toULongNumber("-01777777777777777777777")); } ASSERT_THROW(MathLib::toLongNumber("02000000000000000000000"), InternalError); ASSERT_THROW(MathLib::toULongNumber("02000000000000000000000"), InternalError); ASSERT_THROW(MathLib::toLongNumber("-02000000000000000000000"), InternalError); ASSERT_THROW(MathLib::toULongNumber("-02000000000000000000000"), InternalError); // min/max and out-of-bounds - decimal { const MathLib::bigint i = 18446744073709551615; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("18446744073709551615")); } { const MathLib::biguint u = 18446744073709551615; ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); ASSERT_EQUALS(u, MathLib::toULongNumber("18446744073709551615")); } { const MathLib::bigint i = -18446744073709551615; ASSERT_EQUALS(i, MathLib::toLongNumber(std::to_string(i))); ASSERT_EQUALS(i, MathLib::toLongNumber("-18446744073709551615")); } { const MathLib::biguint u = -18446744073709551615; ASSERT_EQUALS(u, MathLib::toULongNumber(std::to_string(u))); ASSERT_EQUALS(u, MathLib::toULongNumber("-18446744073709551615")); } ASSERT_THROW(MathLib::toLongNumber("18446744073709551616"), InternalError); ASSERT_THROW(MathLib::toULongNumber("18446744073709551616"), InternalError); ASSERT_THROW(MathLib::toLongNumber("-18446744073709551616"), InternalError); ASSERT_THROW(MathLib::toULongNumber("-18446744073709551616"), InternalError); // TODO: test binary // TODO: test floating point // TODO: test with 128-bit values } void toDoubleNumber() const { ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1"), 0.001); ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("0x1"), 0.001); ASSERT_EQUALS_DOUBLE(10.0, MathLib::toDoubleNumber("10"), 0.001); ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2"), 0.001); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(-100.0, MathLib::toDoubleNumber("-1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(-1e+10, MathLib::toDoubleNumber("-1.0E+10"), 1); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("+1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("+1.0E+10"), 1); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("1.0E+10"), 1); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E-0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E-00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0E+00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0E-00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0."), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0.0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0."), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0."), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0.0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0.0"), 0.000001); ASSERT_EQUALS_DOUBLE('0', MathLib::toDoubleNumber("'0'"), 0.000001); ASSERT_EQUALS_DOUBLE(L'0', MathLib::toDoubleNumber("L'0'"), 0.000001); ASSERT_EQUALS_DOUBLE(192, MathLib::toDoubleNumber("0x0.3p10"), 0.000001); ASSERT_EQUALS_DOUBLE(5.42101e-20, MathLib::toDoubleNumber("0x1p-64"), 1e-20); ASSERT_EQUALS_DOUBLE(3.14159, MathLib::toDoubleNumber("0x1.921fb5p+1"), 0.000001); ASSERT_EQUALS_DOUBLE(2006, MathLib::toDoubleNumber("0x1.f58000p+10"), 0.000001); ASSERT_EQUALS_DOUBLE(1e-010, MathLib::toDoubleNumber("0x1.b7cdfep-34"), 0.000001); ASSERT_EQUALS_DOUBLE(.484375, MathLib::toDoubleNumber("0x1.fp-2"), 0.000001); ASSERT_EQUALS_DOUBLE(9.0, MathLib::toDoubleNumber("0x1.2P3"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0625, MathLib::toDoubleNumber("0x.1P0"), 0.000001); // verify: string --> double --> string conversion ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0f"))); ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("0.0f"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("0.0"))); ASSERT_EQUALS("-1.0", MathLib::toString(MathLib::toDoubleNumber("-1.0f"))); ASSERT_EQUALS("-1.0", MathLib::toString(MathLib::toDoubleNumber("-1.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0f"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0"))); ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("+1.0f"))); ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("+1.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("+0.0f"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("+0.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0."))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0"))); } void isint() const { // zero tests ASSERT_EQUALS(true, MathLib::isInt("0")); ASSERT_EQUALS(false, MathLib::isInt("0.")); ASSERT_EQUALS(false, MathLib::isInt("0.0")); ASSERT_EQUALS(false, MathLib::isInt("-0.")); ASSERT_EQUALS(false, MathLib::isInt("+0.")); ASSERT_EQUALS(false, MathLib::isInt("-0.0")); ASSERT_EQUALS(false, MathLib::isInt("+0.0")); ASSERT_EQUALS(false, MathLib::isInt("+0.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("+0.0E-1")); ASSERT_EQUALS(false, MathLib::isInt("-0.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("-0.0E-1")); ASSERT_EQUALS(true, MathLib::isInt("1")); ASSERT_EQUALS(true, MathLib::isInt("-1")); ASSERT_EQUALS(true, MathLib::isInt("+1")); ASSERT_EQUALS(false, MathLib::isInt("+1E+1")); ASSERT_EQUALS(false, MathLib::isInt("+1E+10000")); ASSERT_EQUALS(false, MathLib::isInt("-1E+1")); ASSERT_EQUALS(false, MathLib::isInt("-1E+10000")); ASSERT_EQUALS(false, MathLib::isInt("-1E-1")); ASSERT_EQUALS(false, MathLib::isInt("-1E-10000")); ASSERT_EQUALS(true, MathLib::isInt("0xff")); ASSERT_EQUALS(true, MathLib::isInt("0xa")); ASSERT_EQUALS(true, MathLib::isInt("0b1000")); ASSERT_EQUALS(true, MathLib::isInt("0B1000")); ASSERT_EQUALS(true, MathLib::isInt("0l")); ASSERT_EQUALS(true, MathLib::isInt("0L")); ASSERT_EQUALS(true, MathLib::isInt("0ul")); ASSERT_EQUALS(true, MathLib::isInt("0ull")); ASSERT_EQUALS(true, MathLib::isInt("0llu")); ASSERT_EQUALS(true, MathLib::isInt("333L")); ASSERT_EQUALS(true, MathLib::isInt("330L")); ASSERT_EQUALS(true, MathLib::isInt("330llu")); ASSERT_EQUALS(true, MathLib::isInt("07")); ASSERT_EQUALS(true, MathLib::isInt("0123")); ASSERT_EQUALS(false, MathLib::isInt("0.4")); ASSERT_EQUALS(false, MathLib::isInt("2352.3f")); ASSERT_EQUALS(false, MathLib::isInt("0.00004")); ASSERT_EQUALS(false, MathLib::isInt("2352.00001f")); ASSERT_EQUALS(false, MathLib::isInt(".4")); ASSERT_EQUALS(false, MathLib::isInt("1.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt("-1.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("+1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt("-1.E+1")); ASSERT_EQUALS(false, MathLib::isInt("+1.E-1")); ASSERT_EQUALS(false, MathLib::isInt(" 1.0E+1")); // with whitespace in front ASSERT_EQUALS(false, MathLib::isInt(" 1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt(" -1.0E+1")); ASSERT_EQUALS(false, MathLib::isInt(" +1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt(" -1.E+1")); ASSERT_EQUALS(false, MathLib::isInt(" +1.E-1")); // with whitespace in front and end ASSERT_EQUALS(false, MathLib::isInt(" 1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt(" -1.0E+1 ")); ASSERT_EQUALS(false, MathLib::isInt(" +1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt(" -1.E+1 ")); ASSERT_EQUALS(false, MathLib::isInt(" +1.E-1 ")); // with whitespace in front and end ASSERT_EQUALS(false, MathLib::isInt("1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt("-1.0E+1 ")); ASSERT_EQUALS(false, MathLib::isInt("+1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt("-1.E+1 ")); ASSERT_EQUALS(false, MathLib::isInt("+1.E-1 ")); // test some garbage ASSERT_EQUALS(false, MathLib::isInt("u")); ASSERT_EQUALS(false, MathLib::isInt("l")); ASSERT_EQUALS(false, MathLib::isInt("ul")); ASSERT_EQUALS(false, MathLib::isInt("ll")); ASSERT_EQUALS(false, MathLib::isInt("U")); ASSERT_EQUALS(false, MathLib::isInt("L")); ASSERT_EQUALS(false, MathLib::isInt("uL")); ASSERT_EQUALS(false, MathLib::isInt("LL")); ASSERT_EQUALS(false, MathLib::isInt("e2")); ASSERT_EQUALS(false, MathLib::isInt("E2")); ASSERT_EQUALS(false, MathLib::isInt(".e2")); ASSERT_EQUALS(false, MathLib::isInt(".E2")); ASSERT_EQUALS(false, MathLib::isInt("0x")); ASSERT_EQUALS(false, MathLib::isInt("0xu")); ASSERT_EQUALS(false, MathLib::isInt("0xl")); ASSERT_EQUALS(false, MathLib::isInt("0xul")); // test empty string ASSERT_EQUALS(false, MathLib::isInt("")); } void isbin() const { // positive testing ASSERT_EQUALS(true, MathLib::isBin("0b0")); ASSERT_EQUALS(true, MathLib::isBin("0b1")); ASSERT_EQUALS(true, MathLib::isBin("+0b1")); ASSERT_EQUALS(true, MathLib::isBin("-0b1")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111")); ASSERT_EQUALS(true, MathLib::isBin("-0b11010111")); ASSERT_EQUALS(true, MathLib::isBin("0B11010111")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111u")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111ul")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111ull")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111l")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111ll")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111llu")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111l")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111lu")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lul")); // Suffix LUL not allowed // negative testing ASSERT_EQUALS(false, MathLib::isBin("100101bx")); ASSERT_EQUALS(false, MathLib::isBin("0")); ASSERT_EQUALS(false, MathLib::isBin("0B")); ASSERT_EQUALS(false, MathLib::isBin("0C")); ASSERT_EQUALS(false, MathLib::isBin("+0B")); ASSERT_EQUALS(false, MathLib::isBin("-0B")); ASSERT_EQUALS(false, MathLib::isBin("-0Bx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111x")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111ux")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lux")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111ulx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lulx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111ullx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lll")); // test empty string ASSERT_EQUALS(false, MathLib::isBin("")); } void isnegative() const { ASSERT_EQUALS(true, MathLib::isNegative("-1")); ASSERT_EQUALS(true, MathLib::isNegative("-1.")); ASSERT_EQUALS(true, MathLib::isNegative("-1.0")); ASSERT_EQUALS(true, MathLib::isNegative("-1.0E+2")); ASSERT_EQUALS(true, MathLib::isNegative("-1.0E-2")); ASSERT_EQUALS(false, MathLib::isNegative("+1")); ASSERT_EQUALS(false, MathLib::isNegative("+1.")); ASSERT_EQUALS(false, MathLib::isNegative("+1.0")); ASSERT_EQUALS(false, MathLib::isNegative("+1.0E+2")); ASSERT_EQUALS(false, MathLib::isNegative("+1.0E-2")); // test empty string ASSERT_EQUALS(false, MathLib::isNegative("")); } void isoct() const { // octal number format: [+|-]0[0-7][suffix] // positive testing ASSERT_EQUALS(true, MathLib::isOct("010")); ASSERT_EQUALS(true, MathLib::isOct("+010")); ASSERT_EQUALS(true, MathLib::isOct("-010")); ASSERT_EQUALS(true, MathLib::isOct("0175")); ASSERT_EQUALS(true, MathLib::isOct("+0175")); ASSERT_EQUALS(true, MathLib::isOct("-0175")); ASSERT_EQUALS(true, MathLib::isOct("00")); ASSERT_EQUALS(true, MathLib::isOct("02")); ASSERT_EQUALS(true, MathLib::isOct("+042")); ASSERT_EQUALS(true, MathLib::isOct("-042")); ASSERT_EQUALS(true, MathLib::isOct("+042U")); ASSERT_EQUALS(true, MathLib::isOct("-042U")); ASSERT_EQUALS(true, MathLib::isOct("+042L")); ASSERT_EQUALS(true, MathLib::isOct("-042L")); ASSERT_EQUALS(true, MathLib::isOct("+042LU")); ASSERT_EQUALS(true, MathLib::isOct("-042LU")); ASSERT_EQUALS(true, MathLib::isOct("+042UL")); ASSERT_EQUALS(true, MathLib::isOct("-042UL")); ASSERT_EQUALS(true, MathLib::isOct("+042ULL")); ASSERT_EQUALS(true, MathLib::isOct("-042ULL")); ASSERT_EQUALS(true, MathLib::isOct("+042LLU")); ASSERT_EQUALS(true, MathLib::isOct("-042LLU")); // test empty string ASSERT_EQUALS(false, MathLib::isOct("")); // negative testing ASSERT_EQUALS(false, MathLib::isOct("0")); ASSERT_EQUALS(false, MathLib::isOct("-0x175")); ASSERT_EQUALS(false, MathLib::isOct("-0_garbage_")); ASSERT_EQUALS(false, MathLib::isOct(" ")); ASSERT_EQUALS(false, MathLib::isOct(" ")); ASSERT_EQUALS(false, MathLib::isOct("02.")); ASSERT_EQUALS(false, MathLib::isOct("02E2")); ASSERT_EQUALS(false, MathLib::isOct("+042x")); ASSERT_EQUALS(false, MathLib::isOct("-042x")); ASSERT_EQUALS(false, MathLib::isOct("+042Ux")); ASSERT_EQUALS(false, MathLib::isOct("-042Ux")); ASSERT_EQUALS(false, MathLib::isOct("+042Lx")); ASSERT_EQUALS(false, MathLib::isOct("-042Lx")); ASSERT_EQUALS(false, MathLib::isOct("+042ULx")); ASSERT_EQUALS(false, MathLib::isOct("-042ULx")); ASSERT_EQUALS(false, MathLib::isOct("+042LLx")); ASSERT_EQUALS(false, MathLib::isOct("-042LLx")); ASSERT_EQUALS(false, MathLib::isOct("+042ULLx")); ASSERT_EQUALS(false, MathLib::isOct("-042ULLx")); ASSERT_EQUALS(false, MathLib::isOct("+042LLUx")); ASSERT_EQUALS(false, MathLib::isOct("-042LLUx")); ASSERT_EQUALS(false, MathLib::isOct("+042LUL")); ASSERT_EQUALS(false, MathLib::isOct("-042LUL")); // white space in front ASSERT_EQUALS(false, MathLib::isOct(" -042ULL")); // trailing white space ASSERT_EQUALS(false, MathLib::isOct("-042ULL ")); // front and trailing white space ASSERT_EQUALS(false, MathLib::isOct(" -042ULL ")); ASSERT_EQUALS(false, MathLib::isOct("+042LUL+0")); } void isFloatHex() const { // hex number syntax: [sign]0x[hexnumbers][suffix] ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.999999999999ap-4")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x0.3p10")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.fp3")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1P-1")); ASSERT_EQUALS(true, MathLib::isFloatHex("0xcc.ccccccccccdp-11")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x3.243F6A88p+03")); ASSERT_EQUALS(true, MathLib::isFloatHex("0xA.Fp-10")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p-10f")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10F")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10l")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10L")); ASSERT_EQUALS(true, MathLib::isFloatHex("0X.2p-0")); ASSERT_EQUALS(false, MathLib::isFloatHex("")); ASSERT_EQUALS(false, MathLib::isFloatHex("0")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("0xa")); ASSERT_EQUALS(false, MathLib::isFloatHex("+0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("-0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x.")); ASSERT_EQUALS(false, MathLib::isFloatHex("0XP")); ASSERT_EQUALS(false, MathLib::isFloatHex("0xx")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1P+-1")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10e")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+1af")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10LL")); } void isIntHex() const { // hex number syntax: [sign]0x[hexnumbers][suffix] // positive testing ASSERT_EQUALS(true, MathLib::isIntHex("0xa")); ASSERT_EQUALS(true, MathLib::isIntHex("0x2AF3")); ASSERT_EQUALS(true, MathLib::isIntHex("-0xa")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x2AF3")); ASSERT_EQUALS(true, MathLib::isIntHex("+0xa")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x2AF3")); ASSERT_EQUALS(true, MathLib::isIntHex("0x0")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0U")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0U")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0L")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0L")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LU")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LU")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0UL")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0UL")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LL")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LL")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0ULL")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0ULL")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LLU")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LLU")); // negative testing ASSERT_EQUALS(false, MathLib::isIntHex("+0x")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x")); ASSERT_EQUALS(false, MathLib::isIntHex("0x")); ASSERT_EQUALS(false, MathLib::isIntHex("0xl")); ASSERT_EQUALS(false, MathLib::isIntHex("0xx")); ASSERT_EQUALS(false, MathLib::isIntHex("-0175")); ASSERT_EQUALS(false, MathLib::isIntHex("-0_garbage_")); ASSERT_EQUALS(false, MathLib::isIntHex(" ")); ASSERT_EQUALS(false, MathLib::isIntHex(" ")); ASSERT_EQUALS(false, MathLib::isIntHex("0")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0Z")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0Z")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0Uz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0Uz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0Lz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0Lz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LUz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LUz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0ULz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0ULz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LLz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LLz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0ULLz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0ULLz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LLUz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LLUz")); ASSERT_EQUALS(false, MathLib::isIntHex("0x0+0")); ASSERT_EQUALS(false, MathLib::isIntHex("e2")); ASSERT_EQUALS(false, MathLib::isIntHex("+E2")); // test empty string ASSERT_EQUALS(false, MathLib::isIntHex("")); } void isValidIntegerSuffix() const { // negative testing ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ux")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ulx")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lx")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lux")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lll")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("garbage")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("llu ")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("iX")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i6X")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64X")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64 ")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i66")); // positive testing ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("u")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ul")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ull")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("l")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("lu")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ll")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("llu")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("llU")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("LLU")); // Microsoft extensions: ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("i64")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("I64")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ui64")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("UI64")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64", false)); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("I64", false)); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ui64", false)); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("UI64", false)); } void ispositive() const { ASSERT_EQUALS(false, MathLib::isPositive("-1")); ASSERT_EQUALS(false, MathLib::isPositive("-1.")); ASSERT_EQUALS(false, MathLib::isPositive("-1.0")); ASSERT_EQUALS(false, MathLib::isPositive("-1.0E+2")); ASSERT_EQUALS(false, MathLib::isPositive("-1.0E-2")); ASSERT_EQUALS(true, MathLib::isPositive("+1")); ASSERT_EQUALS(true, MathLib::isPositive("+1.")); ASSERT_EQUALS(true, MathLib::isPositive("+1.0")); ASSERT_EQUALS(true, MathLib::isPositive("+1.0E+2")); ASSERT_EQUALS(true, MathLib::isPositive("+1.0E-2")); // test empty string ASSERT_EQUALS(false, MathLib::isPositive("")); // "" is neither positive nor negative } void isFloat() const { ASSERT_EQUALS(false, MathLib::isFloat("")); ASSERT_EQUALS(true, MathLib::isFloat("0.f")); ASSERT_EQUALS(true, MathLib::isFloat("0.f")); ASSERT_EQUALS(true, MathLib::isFloat("0xA.Fp-10")); } void isDecimalFloat() const { ASSERT_EQUALS(false, MathLib::isDecimalFloat("")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("...")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".e")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".e2")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".E")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+E.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+e.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-E.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-e.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-X")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+X")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0 ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0 ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.L")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0. ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0. ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0..")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("..0..")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("..0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0E0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E+0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E-0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0E+0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0E-0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0E-1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0E-1")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("1")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-1")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1e+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+007")); // to be sure about #5485 ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+10000")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E+10000")); ASSERT_EQUALS(true, MathLib::isDecimalFloat(".1250E+04")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E-1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E-10000")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1.23e+01")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1.23E+01")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1e+x")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+X")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001lX")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001LX")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001f2")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001F2")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1e+003x")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+003X")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.4")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.00004")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat(".4")); ASSERT_EQUALS(true, MathLib::isDecimalFloat(".3e2")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("1.0E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("1.0E-1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1.0E+1")); } void naninf() const { ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "0.0")); // nan ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-0.0", "0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-0.f", "0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-0.0", "-0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-.0", "-0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "-0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("0.f", "-0.f")); // nan (#5875) ASSERT_EQUALS("inf.0", MathLib::divide("3.0", "0.0")); // inf ASSERT_EQUALS("inf.0", MathLib::divide("3.0", "0.f")); // inf (#5875) ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "0.0")); // -inf (#5142) ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "0.0f")); // -inf (#5142) ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "-0.0f")); // inf (#5142) } void isdec() const { // positive testing ASSERT_EQUALS(true, MathLib::isDec("1")); ASSERT_EQUALS(true, MathLib::isDec("+1")); ASSERT_EQUALS(true, MathLib::isDec("-1")); ASSERT_EQUALS(true, MathLib::isDec("-100")); ASSERT_EQUALS(true, MathLib::isDec("-1L")); ASSERT_EQUALS(true, MathLib::isDec("1UL")); // negative testing ASSERT_EQUALS(false, MathLib::isDec("-1.")); ASSERT_EQUALS(false, MathLib::isDec("+1.")); ASSERT_EQUALS(false, MathLib::isDec("-x")); ASSERT_EQUALS(false, MathLib::isDec("+x")); ASSERT_EQUALS(false, MathLib::isDec("x")); ASSERT_EQUALS(false, MathLib::isDec("")); } void isNullValue() const { // inter zero value ASSERT_EQUALS(true, MathLib::isNullValue("0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0")); // inter zero value (octal) ASSERT_EQUALS(true, MathLib::isNullValue("00")); ASSERT_EQUALS(true, MathLib::isNullValue("+00")); ASSERT_EQUALS(true, MathLib::isNullValue("-00")); // inter zero value (hex) ASSERT_EQUALS(true, MathLib::isNullValue("0x0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0x0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0x0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0X0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0X0")); // unsigned integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0U")); ASSERT_EQUALS(true, MathLib::isNullValue("+0U")); ASSERT_EQUALS(true, MathLib::isNullValue("-0U")); // long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0L")); ASSERT_EQUALS(true, MathLib::isNullValue("+0L")); ASSERT_EQUALS(true, MathLib::isNullValue("-0L")); // unsigned long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0UL")); ASSERT_EQUALS(true, MathLib::isNullValue("+0UL")); ASSERT_EQUALS(true, MathLib::isNullValue("-0UL")); // unsigned long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0LU")); ASSERT_EQUALS(true, MathLib::isNullValue("+0LU")); ASSERT_EQUALS(true, MathLib::isNullValue("-0LU")); // long long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0LL")); ASSERT_EQUALS(true, MathLib::isNullValue("+0LL")); ASSERT_EQUALS(true, MathLib::isNullValue("-0LL")); // long long unsigned zero value ASSERT_EQUALS(true, MathLib::isNullValue("0LLU")); ASSERT_EQUALS(true, MathLib::isNullValue("+0LLU")); ASSERT_EQUALS(true, MathLib::isNullValue("-0LLU")); // unsigned long long zero value ASSERT_EQUALS(true, MathLib::isNullValue("0ULL")); ASSERT_EQUALS(true, MathLib::isNullValue("+0ULL")); ASSERT_EQUALS(true, MathLib::isNullValue("-0ULL")); // floating pointer zero value (no trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("0.")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.")); // floating pointer zero value (1 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("0.0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.0")); // floating pointer zero value (3 trailing zeros after dot) ASSERT_EQUALS(true, MathLib::isNullValue("0.000")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.000")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.000")); // floating pointer zero value (no trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("00.")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.")); // floating pointer zero value (1 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("00.0")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.0")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.0")); // floating pointer zero value (3 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("00.000")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.000")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.000")); // floating pointer zero value (3 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue(".000")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0e0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0e0")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E1")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E1")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E1")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E42")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E42")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E42")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E429999")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E429999")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E429999")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E+1")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E+1")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E+1")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E+42")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E+42")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E+42")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E+429999")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E+429999")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E+429999")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E-1")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E-1")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E-1")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E-42")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E-42")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E-42")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E-429999")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E-429999")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E-429999")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.E0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.E0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.E0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.E+0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.E+0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.E+0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.E+0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.00E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.00E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.00E-0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("00000.00E-000000000")); ASSERT_EQUALS(true, MathLib::isNullValue("+00000.00E-000000000")); ASSERT_EQUALS(true, MathLib::isNullValue("-00000.00E-000000000")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("0.f")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.f")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("0.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("0.0F")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.0f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("00.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.0f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("00.00f")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.00f")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.00f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("00.00E+1f")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.00E+1f")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.00E+1f")); // hex float ASSERT_EQUALS(true, MathLib::isNullValue("0x0p3")); ASSERT_EQUALS(true, MathLib::isNullValue("0X0P3")); ASSERT_EQUALS(true, MathLib::isNullValue("0X0p-3")); ASSERT_EQUALS(true, MathLib::isNullValue("-0x0p3")); ASSERT_EQUALS(true, MathLib::isNullValue("+0x0p3")); // binary numbers ASSERT_EQUALS(true, MathLib::isNullValue("0b00")); ASSERT_EQUALS(true, MathLib::isNullValue("+0b00")); ASSERT_EQUALS(true, MathLib::isNullValue("-0b00")); // binary numbers (long) ASSERT_EQUALS(true, MathLib::isNullValue("0b00L")); ASSERT_EQUALS(true, MathLib::isNullValue("+0b00L")); ASSERT_EQUALS(true, MathLib::isNullValue("-0b00L")); // negative testing ASSERT_EQUALS(false, MathLib::isNullValue("0.1")); ASSERT_EQUALS(false, MathLib::isNullValue("1.0")); ASSERT_EQUALS(false, MathLib::isNullValue("0.01")); ASSERT_EQUALS(false, MathLib::isNullValue("0xF")); ASSERT_EQUALS(false, MathLib::isNullValue("0XFF")); ASSERT_EQUALS(false, MathLib::isNullValue("0b01")); ASSERT_EQUALS(false, MathLib::isNullValue("0B01")); ASSERT_EQUALS(false, MathLib::isNullValue("0x1p0")); ASSERT_EQUALS(false, MathLib::isNullValue("0x1P0")); ASSERT_EQUALS(false, MathLib::isNullValue("0xap0")); ASSERT_EQUALS(false, MathLib::isNullValue("0xbp0")); ASSERT_EQUALS(false, MathLib::isNullValue("0xcp0")); ASSERT_EQUALS(false, MathLib::isNullValue("0xdp0")); ASSERT_EQUALS(false, MathLib::isNullValue("0xep0")); ASSERT_EQUALS(false, MathLib::isNullValue("0xfp0")); ASSERT_EQUALS(false, MathLib::isNullValue("-00.01e-12")); ASSERT_EQUALS(false, MathLib::isNullValue("-00.01e+12")); ASSERT_EQUALS(false, MathLib::isNullValue("")); ASSERT_EQUALS(false, MathLib::isNullValue(" ")); ASSERT_EQUALS(false, MathLib::isNullValue("x")); ASSERT_EQUALS(false, MathLib::isNullValue("garbage")); ASSERT_EQUALS(false, MathLib::isNullValue("UL")); ASSERT_EQUALS(false, MathLib::isNullValue("-ENOMEM")); } void incdec() const { // increment { const MathLib::biguint num = ~10U; const std::string op = "++"; const std::string strNum = MathLib::incdec(MathLib::toString(num), op); const MathLib::biguint incrementedNum = MathLib::toULongNumber(strNum); ASSERT_EQUALS(num + 1U, incrementedNum); } // decrement { const MathLib::biguint num = ~10U; const std::string op = "--"; const std::string strNum = MathLib::incdec(MathLib::toString(num), op); const MathLib::biguint decrementedNum = MathLib::toULongNumber(strNum); ASSERT_EQUALS(num - 1U, decrementedNum); } // invalid operation ASSERT_THROW(MathLib::incdec("1", "x"), InternalError); // throw } void sin() const { ASSERT_EQUALS("0.0", MathLib::sin("0")); } void cos() const { ASSERT_EQUALS("1.0", MathLib::cos("0")); } void tan() const { ASSERT_EQUALS("0.0", MathLib::tan("0")); } void abs() const { ASSERT_EQUALS("", MathLib::abs("")); ASSERT_EQUALS("0", MathLib::abs("0")); ASSERT_EQUALS("+0", MathLib::abs("+0")); ASSERT_EQUALS("0", MathLib::abs("-0")); ASSERT_EQUALS("+1", MathLib::abs("+1")); ASSERT_EQUALS("+1.0", MathLib::abs("+1.0")); ASSERT_EQUALS("1", MathLib::abs("-1")); ASSERT_EQUALS("1.0", MathLib::abs("-1.0")); ASSERT_EQUALS("9007199254740991", MathLib::abs("9007199254740991")); } void toString() const { ASSERT_EQUALS("0.0", MathLib::toString(0.0)); ASSERT_EQUALS("0.0", MathLib::toString(+0.0)); ASSERT_EQUALS("0.0", MathLib::toString(-0.0)); // float (trailing f or F) ASSERT_EQUALS("0", MathLib::toString(+0.0f)); ASSERT_EQUALS("-0", MathLib::toString(-0.0F)); // double (tailing l or L) ASSERT_EQUALS("0", MathLib::toString(+0.0l)); ASSERT_EQUALS("-0", MathLib::toString(-0.0L)); } void CPP14DigitSeparators() const { // Ticket #7137, #7565 ASSERT(MathLib::isDigitSeparator("'", 0) == false); ASSERT(MathLib::isDigitSeparator("123'0;", 3)); ASSERT(MathLib::isDigitSeparator("foo(1'2);", 5)); ASSERT(MathLib::isDigitSeparator("foo(1,1'2);", 7)); ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 7)); ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 13)); ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 16) == false); ASSERT(MathLib::isDigitSeparator("int b=1+9'8;", 9)); ASSERT(MathLib::isDigitSeparator("if (1'2) { char c = 'c'; }", 5)); ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 9)); ASSERT(MathLib::isDigitSeparator("if (120&1'2) { char c = 'c'; }", 9)); ASSERT(MathLib::isDigitSeparator("if (120|1'2) { char c = 'c'; }", 9)); ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 24) == false); ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 26) == false); ASSERT(MathLib::isDigitSeparator("0b0000001'0010'01110", 14)); } }; REGISTER_TEST(TestMathLib) cppcheck-2.7/test/testmemleak.cpp000066400000000000000000002442031417746362400171630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkmemoryleak.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include #include #include #include class TestMemleakInClass; class TestMemleakNoVar; class TestMemleakStructMember; class TestMemleak : private TestFixture { public: TestMemleak() : TestFixture("TestMemleak") {} private: Settings settings; void run() OVERRIDE { TEST_CASE(testFunctionReturnType); TEST_CASE(open); } #define functionReturnType(code) functionReturnType_(code, __FILE__, __LINE__) CheckMemoryLeak::AllocType functionReturnType_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); const CheckMemoryLeak c(&tokenizer, this, &settings); return (c.functionReturnType)(&tokenizer.getSymbolDatabase()->scopeList.front().functionList.front()); } void testFunctionReturnType() { { const char code[] = "const char *foo()\n" "{ return 0; }"; ASSERT_EQUALS(CheckMemoryLeak::No, functionReturnType(code)); } { const char code[] = "Fred *newFred()\n" "{ return new Fred; }"; ASSERT_EQUALS(CheckMemoryLeak::New, functionReturnType(code)); } { const char code[] = "char *foo()\n" "{ return new char[100]; }"; ASSERT_EQUALS(CheckMemoryLeak::NewArray, functionReturnType(code)); } { const char code[] = "char *foo()\n" "{\n" " char *p = new char[100];\n" " return p;\n" "}"; ASSERT_EQUALS(CheckMemoryLeak::NewArray, functionReturnType(code)); } } void open() { const char code[] = "class A {\n" " static int open() {\n" " return 1;\n" " }\n" "\n" " A() {\n" " int ret = open();\n" " }\n" "};\n"; // Clear the error buffer.. errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT(tokenizer.tokenize(istr, "test.cpp")); // there is no allocation const Token *tok = Token::findsimplematch(tokenizer.tokens(), "ret ="); const CheckMemoryLeak check(&tokenizer, nullptr, &settings); ASSERT_EQUALS(CheckMemoryLeak::No, check.getAllocationType(tok->tokAt(2), 1)); } }; REGISTER_TEST(TestMemleak) class TestMemleakInFunction : public TestFixture { public: TestMemleakInFunction() : TestFixture("TestMemleakInFunction") {} private: Settings settings0; Settings settings1; Settings settings2; #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[]) { // Clear the error buffer.. errout.str(""); Settings *settings = &settings1; // Tokenize.. Tokenizer tokenizer(settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, settings, this); checkMemoryLeak.checkReallocUsage(); } void run() OVERRIDE { LOAD_LIB_2(settings1.library, "std.cfg"); LOAD_LIB_2(settings1.library, "posix.cfg"); LOAD_LIB_2(settings2.library, "std.cfg"); TEST_CASE(realloc1); TEST_CASE(realloc2); TEST_CASE(realloc3); TEST_CASE(realloc4); TEST_CASE(realloc5); TEST_CASE(realloc7); TEST_CASE(realloc8); TEST_CASE(realloc9); TEST_CASE(realloc10); TEST_CASE(realloc11); TEST_CASE(realloc12); TEST_CASE(realloc13); TEST_CASE(realloc14); TEST_CASE(realloc15); TEST_CASE(realloc16); TEST_CASE(realloc17); TEST_CASE(realloc18); TEST_CASE(realloc19); TEST_CASE(realloc20); TEST_CASE(realloc21); TEST_CASE(realloc22); TEST_CASE(realloc23); TEST_CASE(realloc24); // #9228 TEST_CASE(reallocarray1); } void realloc1() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = realloc(a, 100);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc2() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = (char *)realloc(a, 100);\n" " free(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc3() { check("void foo()\n" "{\n" " char *a = 0;\n" " if ((a = realloc(a, 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc4() { check("void foo()\n" "{\n" " static char *a = 0;\n" " if ((a = realloc(a, 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: a\n", "[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc5() { check("void foo()\n" "{\n" " char *buf;\n" " char *new_buf;\n" " buf = calloc( 10 );\n" " new_buf = realloc ( buf, 20);\n" " if ( !new_buf )\n" " free(buf);\n" " else\n" " free(new_buf);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc7() { check("bool foo(size_t nLen, char* pData)\n" "{\n" " pData = (char*) realloc(pData, sizeof(char) + (nLen + 1)*sizeof(char));\n" " if ( pData == NULL )\n" " {\n" " return false;\n" " }\n" " free(pData);\n" " return true;\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc8() { check("void foo()\n" "{\n" " char *origBuf = m_buf;\n" " m_buf = (char *) realloc (m_buf, m_capacity + growBy);\n" " if (!m_buf) {\n" " m_buf = origBuf;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc9() { check("void foo()\n" "{\n" " x = realloc(x,100);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc10() { check("void foo() {\n" " char *pa, *pb;\n" " pa = pb = malloc(10);\n" " pa = realloc(pa, 20);" " exit();\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc11() { check("void foo() {\n" " char *p;\n" " p = realloc(p, size);\n" " if (!p)\n" " error();\n" " usep(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc12() { check("void foo(int x)\n" "{\n" " char *a = 0;\n" " if ((a = realloc(a, x + 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc13() { check("void foo()\n" "{\n" " char **str;\n" " *str = realloc(*str,100);\n" " free (*str);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'str\' nulled but not freed upon failure\n", errout.str()); } void realloc14() { check("void foo() {\n" " char *p;\n" " p = realloc(p, size + 1);\n" " if (!p)\n" " error();\n" " usep(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc15() { check("bool foo() {\n" " char ** m_options;\n" " m_options = (char**)realloc( m_options, 2 * sizeof(char*));\n" " if( m_options == NULL )\n" " return false;\n" " return true;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Common realloc mistake: \'m_options\' nulled but not freed upon failure\n", errout.str()); } void realloc16() { check("void f(char *zLine) {\n" " zLine = realloc(zLine, 42);\n" " if (zLine) {\n" " free(zLine);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc17() { check("void foo()\n" "{\n" " void ***a = malloc(sizeof(a));\n" " ***a = realloc(***(a), sizeof(a) * 2);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc18() { check("void foo()\n" "{\n" " void *a = malloc(sizeof(a));\n" " a = realloc((void*)a, sizeof(a) * 2);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc19() { check("void foo()\n" "{\n" " void *a = malloc(sizeof(a));\n" " a = (realloc((void*)((a)), sizeof(a) * 2));\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc20() { check("void foo()\n" "{\n" " void *a = malloc(sizeof(a));\n" " a = realloc((a) + 1, sizeof(a) * 2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc21() { check("char *foo(char *bs0)\n" "{\n" " char *bs = bs0;\n" " bs = realloc(bs, 100);\n" " if (bs == NULL) return bs0;\n" " return bs;\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc22() { check("void foo(char **bsp)\n" "{\n" " char *bs = *bsp;\n" " bs = realloc(bs, 100);\n" " if (bs == NULL) return;\n" " *bsp = bs;\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc23() { check("void foo(struct ABC *s)\n" "{\n" " uint32_t *cigar = s->cigar;\n" " if (!(cigar = realloc(cigar, 100 * sizeof(*cigar))))\n" " return;\n" " s->cigar = cigar;\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc24() { // #9228 check("void f() {\n" "void *a = NULL;\n" "a = realloc(a, 20);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" "void *a = NULL;\n" "a = malloc(10);\n" "a = realloc(a, 20);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); check("void f() {\n" "void *a = std::nullptr;\n" "a = malloc(10);\n" "a = realloc(a, 20);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); check("void f(char *b) {\n" "void *a = NULL;\n" "a = b;\n" "a = realloc(a, 20);\n" "}"); ASSERT_EQUALS("", errout.str()); } void reallocarray1() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = reallocarray(a, 100, 2);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common reallocarray mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } }; REGISTER_TEST(TestMemleakInFunction) class TestMemleakInClass : public TestFixture { public: TestMemleakInClass() : TestFixture("TestMemleakInClass") {} private: Settings settings; /** * Tokenize and execute leak check for given code * @param code Source code */ void check_(const char* file, int line, const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for memory leaks.. CheckMemoryLeakInClass checkMemoryLeak(&tokenizer, &settings, this); (checkMemoryLeak.check)(); } void run() OVERRIDE { settings.severity.enable(Severity::warning); settings.severity.enable(Severity::style); LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(class1); TEST_CASE(class2); TEST_CASE(class3); TEST_CASE(class4); TEST_CASE(class6); TEST_CASE(class7); TEST_CASE(class8); TEST_CASE(class9); TEST_CASE(class10); TEST_CASE(class11); TEST_CASE(class12); TEST_CASE(class13); TEST_CASE(class14); TEST_CASE(class15); TEST_CASE(class16); TEST_CASE(class17); TEST_CASE(class18); TEST_CASE(class19); // ticket #2219 TEST_CASE(class20); TEST_CASE(class21); // ticket #2517 TEST_CASE(class22); // ticket #3012 TEST_CASE(class23); // ticket #3303 TEST_CASE(class24); // ticket #3806 - false positive in copy constructor TEST_CASE(class25); // ticket #4367 - false positive implementation for destructor is not seen TEST_CASE(class26); // ticket #10789 TEST_CASE(staticvar); TEST_CASE(free_member_in_sub_func); TEST_CASE(mismatch1); TEST_CASE(mismatch2); // #5659 // allocating member variable in public function TEST_CASE(func1); TEST_CASE(func2); } void class1() { check("class Fred\n" "{\n" "private:\n" " char *str1;\n" " char *str2;\n" "public:\n" " Fred();\n" " ~Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " char *str1;\n" " char *str2;\n" "public:\n" " Fred()\n" " {\n" " str1 = new char[10];\n" " str2 = new char[10];\n" " }\n" " ~Fred()\n" " {\n" " delete [] str2;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); } void class2() { check("class Fred\n" "{\n" "private:\n" " char *str1;\n" "public:\n" " Fred();\n" " ~Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{\n" " str1 = new char[10];\n" "}\n" "\n" "Fred::~Fred()\n" "{\n" " free(str1);\n" "}"); ASSERT_EQUALS("[test.cpp:17]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " char *str1;\n" "public:\n" " Fred()\n" " {\n" " str1 = new char[10];\n" " }\n" " ~Fred()\n" " {\n" " free(str1);\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:12]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout.str()); } void class3() { check("class Token;\n" "\n" "class Tokenizer\n" "{\n" "private:\n" " Token *_tokens;\n" "\n" "public:\n" " Tokenizer();\n" " ~Tokenizer();\n" " void deleteTokens(Token *tok);\n" "};\n" "\n" "Tokenizer::Tokenizer()\n" "{\n" " _tokens = new Token;\n" "}\n" "\n" "Tokenizer::~Tokenizer()\n" "{\n" " deleteTokens(_tokens);\n" "}\n" "\n" "void Tokenizer::deleteTokens(Token *tok)\n" "{\n" " while (tok)\n" " {\n" " Token *next = tok->next();\n" " delete tok;\n" " tok = next;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Token;\n" "\n" "class Tokenizer\n" "{\n" "private:\n" " Token *_tokens;\n" "\n" "public:\n" " Tokenizer()\n" " {\n" " _tokens = new Token;\n" " }\n" " ~Tokenizer()\n" " {\n" " deleteTokens(_tokens);\n" " }\n" " void deleteTokens(Token *tok)\n" " {\n" " while (tok)\n" " {\n" " Token *next = tok->next();\n" " delete tok;\n" " tok = next;\n" " }\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class4() { check("struct ABC;\n" "class Fred\n" "{\n" "private:\n" " void addAbc(ABC *abc);\n" "public:\n" " void click();\n" "};\n" "\n" "void Fred::addAbc(ABC* abc)\n" "{\n" " AbcPosts->Add(abc);\n" "}\n" "\n" "void Fred::click()\n" "{\n" " ABC *p = new ABC;\n" " addAbc( p );\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct ABC;\n" "class Fred\n" "{\n" "private:\n" " void addAbc(ABC* abc)\n" " {\n" " AbcPosts->Add(abc);\n" " }\n" "public:\n" " void click()\n" " {\n" " ABC *p = new ABC;\n" " addAbc( p );\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class6() { check("class Fred\n" "{\n" "public:\n" " void foo();\n" "};\n" "\n" "void Fred::foo()\n" "{\n" " char *str = new char[100];\n" " delete [] str;\n" " hello();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " void foo()\n" " {\n" " char *str = new char[100];\n" " delete [] str;\n" " hello();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class7() { check("class Fred\n" "{\n" "public:\n" " int *i;\n" " Fred();\n" " ~Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{\n" " this->i = new int;\n" "}\n" "Fred::~Fred()\n" "{\n" " delete this->i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " int *i;\n" " Fred()\n" " {\n" " this->i = new int;\n" " }\n" " ~Fred()\n" " {\n" " delete this->i;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class8() { check("class A\n" "{\n" "public:\n" " void a();\n" " void doNothing() { }\n" "};\n" "\n" "void A::a()\n" "{\n" " int* c = new int(1);\n" " delete c;\n" " doNothing(c);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "public:\n" " void a()\n" " {\n" " int* c = new int(1);\n" " delete c;\n" " doNothing(c);\n" " }\n" " void doNothing() { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class9() { check("class A\n" "{\n" "public:\n" " int * p;\n" " A();\n" " ~A();\n" "};\n" "\n" "A::A()\n" "{ p = new int; }\n" "\n" "A::~A()\n" "{ delete (p); }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "public:\n" " int * p;\n" " A()\n" " { p = new int; }\n" " ~A()\n" " { delete (p); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class10() { check("class A\n" "{\n" "public:\n" " int * p;\n" " A();\n" "};\n" "A::A()\n" "{ p = new int; }"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" "public:\n" " int * p;\n" " A() { p = new int; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class11() { check("class A\n" "{\n" "public:\n" " int * p;\n" " A() : p(new int[10])\n" " { }" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" "public:\n" " int * p;\n" " A();\n" "};\n" "A::A() : p(new int[10])\n" "{ }"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class12() { check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A();\n" " ~A();\n" " void cleanup();" "};\n" "\n" "A::A()\n" "{ p = new int[10]; }\n" "\n" "A::~A()\n" "{ }\n" "\n" "void A::cleanup()\n" "{ delete [] p; }"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A()\n" " { p = new int[10]; }\n" " ~A()\n" " { }\n" " void cleanup()\n" " { delete [] p; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class13() { check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A();\n" " ~A();\n" " void foo();" "};\n" "\n" "A::A()\n" "{ }\n" "\n" "A::~A()\n" "{ }\n" "\n" "void A::foo()\n" "{ p = new int[10]; delete [] p; }"); ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n", errout.str()); check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A()\n" " { }\n" " ~A()\n" " { }\n" " void foo()\n" " { p = new int[10]; delete [] p; }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n", errout.str()); } void class14() { check("class A\n" "{\n" " int *p;\n" "public:\n" " void init();\n" "};\n" "\n" "void A::init()\n" "{ p = new int[10]; }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init()\n" " { p = new int[10]; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init();\n" "};\n" "\n" "void A::init()\n" "{ p = new int; }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init()\n" " { p = new int; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init();\n" "};\n" "\n" "void A::init()\n" "{ p = malloc(sizeof(int)*10); }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init()\n" " { p = malloc(sizeof(int)*10); }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class15() { check("class A\n" "{\n" " int *p;\n" "public:\n" " A();\n" " ~A() { delete [] p; }\n" "};\n" "A::A()\n" "{ p = new int[10]; }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A()\n" " { p = new int[10]; }\n" " ~A() { delete [] p; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A();\n" " ~A() { delete p; }\n" "};\n" "A::A()\n" "{ p = new int; }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A()\n" " { p = new int; }\n" " ~A() { delete p; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A();\n" " ~A() { free(p); }\n" "};\n" "A::A()\n" "{ p = malloc(sizeof(int)*10); }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A()\n" " { p = malloc(sizeof(int)*10); }\n" " ~A() { free(p); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class16() { // Ticket #1510 check("class A\n" "{\n" " int *a;\n" " int *b;\n" "public:\n" " A() { a = b = new int[10]; }\n" " ~A() { delete [] a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class17() { // Ticket #1557 check("class A {\n" "private:\n" " char *pd;\n" "public:\n" " void foo();\n" "};\n" "\n" "void A::foo()\n" "{\n" " A::pd = new char[12];\n" " delete [] A::pd;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout.str()); check("class A {\n" "private:\n" " char *pd;\n" "public:\n" " void foo()\n" " {\n" " pd = new char[12];\n" " delete [] pd;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout.str()); check("class A {\n" "private:\n" " char *pd;\n" "public:\n" " void foo();\n" "};\n" "\n" "void A::foo()\n" "{\n" " pd = new char[12];\n" " delete [] pd;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout.str()); } void class18() { // Ticket #853 check("class A : public x\n" "{\n" "public:\n" " A()\n" " {\n" " a = new char[10];\n" " foo(a);\n" " }\n" "private:\n" " char *a;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A : public x\n" "{\n" "public:\n" " A();\n" "private:\n" " char *a;\n" "};\n" "A::A()\n" "{\n" " a = new char[10];\n" " foo(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void class19() { // Ticket #2219 check("class Foo\n" "{\n" "private:\n" " TRadioButton* rp1;\n" " TRadioButton* rp2;\n" "public:\n" " Foo();\n" "};\n" "Foo::Foo()\n" "{\n" " rp1 = new TRadioButton(this);\n" " rp2 = new TRadioButton(this);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class TRadioButton { };\n" "class Foo\n" "{\n" "private:\n" " TRadioButton* rp1;\n" " TRadioButton* rp2;\n" "public:\n" " Foo();\n" "};\n" "Foo::Foo()\n" "{\n" " rp1 = new TRadioButton;\n" " rp2 = new TRadioButton;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Foo' is unsafe, 'Foo::rp1' can leak by wrong usage.\n" "[test.cpp:6]: (style) Class 'Foo' is unsafe, 'Foo::rp2' can leak by wrong usage.\n", errout.str()); check("class TRadioButton { };\n" "class Foo\n" "{\n" "private:\n" " TRadioButton* rp1;\n" " TRadioButton* rp2;\n" "public:\n" " Foo();\n" " ~Foo();\n" "};\n" "Foo::Foo()\n" "{\n" " rp1 = new TRadioButton;\n" " rp2 = new TRadioButton;\n" "}\n" "Foo::~Foo()\n" "{\n" " delete rp1;\n" " delete rp2;\n" "}"); ASSERT_EQUALS("", errout.str()); } void class20() { check("namespace ns1 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred()\n" " {\n" " str1 = new char[10];\n" " str2 = new char[10];\n" " }\n" " ~Fred()\n" " {\n" " delete [] str2;\n" " }\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" "\n" " Fred::Fred()\n" " {\n" " str1 = new char[10];\n" " str2 = new char[10];\n" " }\n" "\n" " Fred::~Fred()\n" " {\n" " delete [] str2;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" "}\n" "ns1::Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "ns1::Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " namespace ns2 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" " }\n" "}\n" "ns1::ns2::Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "ns1::ns2::Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " namespace ns2 {\n" " namespace ns3 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" " }\n" " }\n" "}\n" "ns1::ns2::ns3::Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "ns1::ns2::ns3::Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); } void class21() { // ticket #2517 check("struct B { };\n" "struct C\n" "{\n" " B * b;\n" " C(B * x) : b(x) { }\n" "};\n" "class A\n" "{\n" " B *b;\n" " C *c;\n" "public:\n" " A() : b(new B()), c(new C(b)) { }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' can leak by wrong usage.\n" "[test.cpp:10]: (style) Class 'A' is unsafe, 'A::c' can leak by wrong usage.\n", "[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' can leak by wrong usage.\n", errout.str()); check("struct B { };\n" "struct C\n" "{\n" " B * b;\n" " C(B * x) : b(x) { }\n" "};\n" "class A\n" "{\n" " B *b;\n" " C *c;\n" "public:\n" " A()\n" " {\n" " b = new B();\n" " c = new C(b);\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' can leak by wrong usage.\n" "[test.cpp:10]: (style) Class 'A' is unsafe, 'A::c' can leak by wrong usage.\n", "[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' can leak by wrong usage.\n", errout.str()); } void class22() { // ticket #3012 - false positive check("class Fred {\n" "private:\n" " int * a;\n" "private:\n" " Fred() { a = new int; }\n" " ~Fred() { (delete(a), (a)=NULL); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class23() { // ticket #3303 - false positive check("class CDataImpl {\n" "public:\n" " CDataImpl() { m_refcount = 1; }\n" " void Release() { if (--m_refcount == 0) delete this; }\n" "private:\n" " int m_refcount;\n" "};\n" "\n" "class CData {\n" "public:\n" " CData() : m_impl(new CDataImpl()) { }\n" " ~CData() { if (m_impl) m_impl->Release(); }\n" "private:\n" " CDataImpl *m_impl;\n" "};"); ASSERT_EQUALS("", errout.str()); } void class24() { // ticket #3806 - false positive in copy constructor check("class Fred {\n" "private:\n" " int * a;\n" "public:\n" " Fred(const Fred &fred) { a = new int; }\n" " ~Fred() { delete a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class25() { // ticket #4367 - false positive when implementation for destructor is not seen check("class Fred {\n" "private:\n" " int * a;\n" "public:\n" " Fred() { a = new int; }\n" " ~Fred();\n" "};"); ASSERT_EQUALS("", errout.str()); } void class26() { // ticket #10789 - crash check("class C;\n" "struct S {\n" " S() { p = new C; }\n" " ~S();\n" " C* p;\n" "};\n" "S::~S() = default;\n"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'S' is unsafe, 'S::p' can leak by wrong usage.\n", errout.str()); } void staticvar() { check("class A\n" "{\n" "private:\n" " static int * p;\n" "public:" " A()\n" " {\n" " if (!p)\n" " p = new int[100];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void free_member_in_sub_func() { // Member function check("class Tokenizer\n" "{\n" "public:\n" " Tokenizer();\n" " ~Tokenizer();\n" "\n" "private:\n" " int *_tokens;\n" " static void deleteTokens(int *tok);\n" "};\n" "\n" "Tokenizer::Tokenizer()\n" "{\n" " _tokens = new int;\n" "}\n" "\n" "Tokenizer::~Tokenizer()\n" "{\n" " deleteTokens(_tokens);\n" " _tokens = 0;\n" "}\n" "\n" "void Tokenizer::deleteTokens(int *tok)\n" "{\n" " delete tok;\n" "}"); ASSERT_EQUALS("", errout.str()); // Global function check("void deleteTokens(int *tok)\n" "{\n" " delete tok;\n" "}\n" "class Tokenizer\n" "{\n" "public:\n" " Tokenizer();\n" " ~Tokenizer();\n" "\n" "private:\n" " int *_tokens;\n" "};\n" "\n" "Tokenizer::Tokenizer()\n" "{\n" " _tokens = new int;\n" "}\n" "\n" "Tokenizer::~Tokenizer()\n" "{\n" " deleteTokens(_tokens);\n" " _tokens = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatch1() { check("class A\n" "{\n" "public:\n" " A(int i);\n" " ~A();\n" "private:\n" " char* pkt_buffer;\n" "};\n" "\n" "A::A(int i)\n" "{\n" " pkt_buffer = new char[8192];\n" " if (i != 1) {\n" " delete pkt_buffer;\n" " pkt_buffer = 0;\n" " }\n" "}\n" "\n" "A::~A() {\n" " delete [] pkt_buffer;\n" "}"); ASSERT_EQUALS("[test.cpp:14]: (error) Mismatching allocation and deallocation: A::pkt_buffer\n", errout.str()); } void mismatch2() { // #5659 check("namespace NS\n" "{\n" "class Foo\n" "{\n" "public:\n" " void fct();\n" "\n" "private:\n" " char* data_;\n" "};\n" "}\n" "\n" "using namespace NS;\n" "\n" "void Foo::fct()\n" "{\n" " data_ = new char[42];\n" " delete data_;\n" " data_ = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'data_' is not deallocated before it is allocated.\n" "[test.cpp:18]: (error) Mismatching allocation and deallocation: Foo::data_\n", errout.str()); check("namespace NS\n" "{\n" "class Foo\n" "{\n" "public:\n" " void fct(int i);\n" "\n" "private:\n" " char* data_;\n" "};\n" "}\n" "\n" "using namespace NS;\n" "\n" "void Foo::fct(int i)\n" "{\n" " data_ = new char[42];\n" " delete data_;\n" " data_ = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'data_' is not deallocated before it is allocated.\n" "[test.cpp:18]: (error) Mismatching allocation and deallocation: Foo::data_\n", errout.str()); } void func1() { check("class Fred\n" "{\n" "private:\n" " char *s;\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { free(s); }\n" " void xy()\n" " { s = malloc(100); }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { free(s); }\n" " void xy()\n" " { s = malloc(100); }\n" "private:\n" " char *s;\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout.str()); } void func2() { check("class Fred\n" "{\n" "private:\n" " char *s;\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { free(s); }\n" " const Fred & operator = (const Fred &f)\n" " { s = malloc(100); }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout.str()); } }; REGISTER_TEST(TestMemleakInClass) class TestMemleakStructMember : public TestFixture { public: TestMemleakStructMember() : TestFixture("TestMemleakStructMember") {} private: Settings settings; void check_(const char* file, int line, const char code[], bool isCPP = true) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, isCPP ? "test.cpp" : "test.c"), file, line); // Check for memory leaks.. CheckMemoryLeakStructMember checkMemoryLeakStructMember(&tokenizer, &settings, this); (checkMemoryLeakStructMember.check)(); } void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "posix.cfg"); // testing that errors are detected TEST_CASE(err); // handle / bail out when "goto" is found TEST_CASE(goto_); // Don't report errors if the struct is returned TEST_CASE(ret1); TEST_CASE(ret2); // assignments TEST_CASE(assign1); TEST_CASE(assign2); TEST_CASE(assign3); // Failed allocation TEST_CASE(failedAllocation); TEST_CASE(function1); // Deallocating in function TEST_CASE(function2); // #2848: Taking address in function TEST_CASE(function3); // #3024: kernel list TEST_CASE(function4); // #3038: Deallocating in function TEST_CASE(function5); // #10381, #10382, #10158 // Handle if-else TEST_CASE(ifelse); // Linked list TEST_CASE(linkedlist); // struct variable is a global variable TEST_CASE(globalvar); // local struct variable TEST_CASE(localvars); // struct variable is a reference variable TEST_CASE(refvar); // Segmentation fault in CheckMemoryLeakStructMember TEST_CASE(trac5030); TEST_CASE(varid); // #5201: Analysis confused by (variable).attribute notation TEST_CASE(varid_2); // #5315: Analysis confused by ((variable).attribute) notation TEST_CASE(customAllocation); TEST_CASE(lambdaInForLoop); // #9793 } void err() { check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " free(abc);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: abc.a\n", errout.str()); check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: abc.a\n", errout.str()); check("static ABC * foo()\n" "{\n" " ABC *abc = malloc(sizeof(ABC));\n" " abc->a = malloc(10);\n" " abc->b = malloc(10);\n" " if (abc->b == 0)\n" " {\n" " return 0;\n" " }\n" " return abc;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: abc.a\n", errout.str()); check("static void foo(int a)\n" "{\n" " ABC *abc = malloc(sizeof(ABC));\n" " abc->a = malloc(10);\n" " if (a == 1)\n" " {\n" " free(abc->a);\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: abc.a\n", errout.str()); } void goto_() { check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " if (abc->a)\n" " { goto out; }\n" " free(abc);\n" " return;\n" "out:\n" " free(abc->a);\n" " free(abc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ret1() { check("static ABC * foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " return abc;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo(struct ABC *abc)\n" "{\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7302 check("void* foo() {\n" " struct ABC abc;\n" " abc.a = malloc(10);\n" " return abc.a;\n" "}", false); ASSERT_EQUALS("", errout.str()); check("void* foo() {\n" " struct ABC abc;\n" " abc.a = malloc(10);\n" " return abc.b;\n" "}", false); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: abc.a\n", errout.str()); } void ret2() { check("static ABC * foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " return &abc->self;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign1() { check("static void foo()\n" "{\n" " struct ABC *abc = abc1;\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo()\n" "{\n" " struct ABC *abc;\n" " abc1 = abc = malloc(sizeof(ABC));\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo()\n" "{\n" " struct msn_entry *ptr;\n" " ptr = malloc(sizeof(struct msn_entry));\n" " ptr->msn = malloc(100);\n" " back = ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign2() { check("static void foo() {\n" " struct ABC *abc = malloc(123);\n" " abc->a = abc->b = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign3() { check("void f(struct s *f1) {\n" " struct s f2;\n" " f2.a = malloc(100);\n" " *f1 = f2;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void failedAllocation() { check("static struct ABC * foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " if (!abc->a)\n" " {\n" " free(abc);\n" " return 0;\n" " }\n" " return abc;\n" "}"); ASSERT_EQUALS("", errout.str()); } void function1() { // Not found function => assume that the function may deallocate check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " func(abc);\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abclist.push_back(abc);\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); } // #2848: Taking address in function 'assign' void function2() { check("void f() {\n" " A a = { 0 };\n" " a.foo = (char *) malloc(10);\n" " assign(&a);\n" "}", false); ASSERT_EQUALS("", errout.str()); } // #3024: kernel list void function3() { check("void f() {\n" " struct ABC *abc = malloc(100);\n" " abc.a = (char *) malloc(10);\n" " list_add_tail(&abc->list, head);\n" "}", false); ASSERT_EQUALS("", errout.str()); } // #3038: deallocating in function void function4() { check("void a(char *p) { char *x = p; free(x); }\n" "void b() {\n" " struct ABC abc;\n" " abc.a = (char *) malloc(10);\n" " a(abc.a);\n" "}", false); ASSERT_EQUALS("", errout.str()); } void function5() { check("struct s f() {\n" // #10381 " struct s s1;\n" " s1->x = malloc(1);\n" " return (s1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct nc_rpc nc_rpc_getconfig() {\n" // #10382 " struct nc_rpc rpc;\n" " rpc->filter = malloc(1);\n" " return (nc_rpc)rpc;\n" "}"); ASSERT_EQUALS("", errout.str()); check("T* f(const char *str) {\n" // #10158 " S* s = malloc(sizeof(S));\n" " s->str = strdup(str);\n" " return NewT(s);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void ifelse() { check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " if (x)" " {\n" " abc->a = malloc(10);\n" " }\n" " else\n" " {\n" " free(abc);\n" " return;\n" " }\n" " free(abc->a);\n" " free(abc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void linkedlist() { // #3904 - false positive when linked list is used check("static void foo() {\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->next = malloc(sizeof(struct ABC));\n" " abc->next->next = NULL;\n" "\n" " while (abc) {\n" " struct ABC *next = abc->next;\n" " free(abc);\n" " abc = next;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void globalvar() { check("struct ABC *abc;\n" "\n" "static void foo()\n" "{\n" " abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Ticket #933 Leaks with struct members not detected void localvars() { // Test error case const char code1[] = "struct A {\n" " FILE* f;\n" " char* c;\n" " void* m;\n" "};\n" "\n" "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" " a.c = new char[12];\n" " a.m = malloc(12);\n" "}"; check(code1, true); ASSERT_EQUALS("[test.cpp:12]: (error) Memory leak: a.f\n" "[test.cpp:12]: (error) Memory leak: a.c\n" "[test.cpp:12]: (error) Memory leak: a.m\n", errout.str()); check(code1, false); ASSERT_EQUALS("[test.c:12]: (error) Memory leak: a.f\n" "[test.c:12]: (error) Memory leak: a.m\n", errout.str()); // Test OK case const char code2[] = "struct A {\n" " FILE* f;\n" " char* c;\n" " void* m;\n" "};\n" "\n" "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" " a.c = new char[12];\n" " a.m = malloc(12);\n" " fclose(a.f);\n" " delete [] a.c;\n" " free(a.m);\n" "}"; check(code2, true); ASSERT_EQUALS("", errout.str()); check(code2, false); ASSERT_EQUALS("", errout.str()); // Test unknown struct. In C++, it might have a destructor const char code3[] = "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" "}"; check(code3, true); ASSERT_EQUALS("", errout.str()); check(code3, false); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: a.f\n", errout.str()); // Test struct with destructor const char code4[] = "struct A {\n" " FILE* f;\n" " ~A();\n" "};\n" "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" "}"; check(code4, true); ASSERT_EQUALS("", errout.str()); } void refvar() { // #8116 check("struct Test\n" "{\n" " int* data;\n" "};\n" "\n" "void foo(Test* x)\n" "{\n" " Test& y = *x;\n" " y.data = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); } // don't crash void trac5030() { check("bool bob( char const **column_ptrs ) {\n" "unique_ptrotherbuffer{new char[otherbufsize+1]};\n" "char *const oldbuffer = otherbuffer.get();\n" "int const oldbufsize = otherbufsize;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varid() { // #5201 check("struct S {\n" " void *state_check_buff;\n" "};\n" "void f() {\n" " S s;\n" " (s).state_check_buff = (void* )malloc(1);\n" " if (s.state_check_buff == 0)\n" " return;\n" "}", false); ASSERT_EQUALS("[test.c:9]: (error) Memory leak: s.state_check_buff\n", errout.str()); } void varid_2() { // #5315 check("typedef struct foo { char *realm; } foo;\n" "void build_principal() {\n" " foo f;\n" " ((f)->realm) = strdup(realm);\n" " if(f->realm == NULL) {}\n" "}", false); TODO_ASSERT_EQUALS("[test.c:6]: (error) Memory leak: f.realm\n", "", errout.str()); } void customAllocation() { // #4770 check("char *myalloc(void) {\n" " return malloc(100);\n" "}\n" "void func() {\n" " struct ABC abc;\n" " abc.a = myalloc();\n" "}", false); ASSERT_EQUALS("[test.c:7]: (error) Memory leak: abc.a\n", errout.str()); } void lambdaInForLoop() { // #9793 check( "struct S { int * p{nullptr}; };\n" "int main()\n" "{\n" " S s;\n" " s.p = new int[10];\n" " for (int i = 0; i < 10; ++i) {\n" " s.p[i] = []() { return 1; }();\n" " }\n" " delete[] s.p;\n" " return 0;\n" "}", true); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestMemleakStructMember) class TestMemleakNoVar : public TestFixture { public: TestMemleakNoVar() : TestFixture("TestMemleakNoVar") {} private: Settings settings; void check_(const char* file, int line, const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for memory leaks.. CheckMemoryLeakNoVar checkMemoryLeakNoVar(&tokenizer, &settings, this); (checkMemoryLeakNoVar.check)(); } void run() OVERRIDE { settings.certainty.setEnabled(Certainty::inconclusive, true); settings.libraries.emplace_back("posix"); settings.severity.enable(Severity::warning); LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "posix.cfg"); // pass allocated memory to function.. TEST_CASE(functionParameter); // never use leakable resource TEST_CASE(missingAssignment); // pass allocated memory to function using a smart pointer TEST_CASE(smartPointerFunctionParam); TEST_CASE(resourceLeak); // Test getAllocationType for subfunction TEST_CASE(getAllocationType); TEST_CASE(crash1); // #10729 } void functionParameter() { // standard function.. check("void x() {\n" " strcpy(a, strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str()); check("char *x() {\n" " char *ret = strcpy(malloc(10), \"abc\");\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *x() {\n" " return strcpy(malloc(10), \"abc\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x() {\n" " free(malloc(10));\n" "}"); ASSERT_EQUALS("", errout.str()); // user function.. check("void set_error(const char *msg) {\n" "}\n" "\n" "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()); check("void f()\n" "{\n" " int fd;\n" " fd = mkstemp(strdup(\"/tmp/file.XXXXXXXX\"));\n" " close(fd);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Allocation with strdup, mkstemp doesn't release it.\n", "", errout.str()); check("void f()\n" "{\n" " if(TRUE || strcmp(strdup(a), b));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout.str()); check("void f()\n" "{\n" " if(!strcmp(strdup(a), b) == 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout.str()); check("void f()\n" "{\n" " 42, strcmp(strdup(a), b);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout.str()); check("void f() {\n" " assert(freopen(\"/dev/null\", \"r\", stdin));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x() {\n" " strcpy(a, (void*)strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str()); check("void* malloc1() {\n" " return (malloc(1));\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *x() {\n" " char *ret = (char*)strcpy(malloc(10), \"abc\");\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " free(malloc(1));\n" " strcpy(a, strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str()); check("void f() {\n" " memcmp(calloc(10, 10), strdup(q), 100);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with calloc, memcmp doesn't release it.\n" "[test.cpp:2]: (error) Allocation with strdup, memcmp doesn't release it.\n", errout.str()); check("void* f(int size) {\n" " return (void*) malloc(size);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(int size) {\n" " return static_cast(malloc(size));\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingAssignment() { check("void x()\n" "{\n" " malloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("void x()\n" "{\n" " calloc(10, 1);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'calloc' is not stored.\n", errout.str()); check("void x()\n" "{\n" " strdup(\"Test\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'strdup' is not stored.\n", errout.str()); check("void x()\n" "{\n" " reallocarray(NULL, 10, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'reallocarray' is not stored.\n", errout.str()); check("void x()\n" "{\n" " (char*) malloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("void x()\n" "{\n" " char* ptr = malloc(10);\n" " foo(ptr);\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char** x(const char* str) {\n" " char* ptr[] = { malloc(10), malloc(5), strdup(str) };\n" " return ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x()\n" "{\n" " 42,malloc(42);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", "", errout.str()); check("void *f()\n" "{\n" " return malloc(10);\n" "}\n" "void x()\n" "{\n" " f();\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Return value of allocation function 'f' is not stored.\n", errout.str()); check("void f()\n" // #8100 "{\n" " auto lambda = [](){return malloc(10);};\n" "}\n" "void x()\n" "{\n" " f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void *f() {\n" // #8848 " struct S { void *alloc() { return malloc(10); } };\n" "}\n" "void x()\n" "{\n" " f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x()\n" "{\n" " if(!malloc(5)) fail();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("FOO* factory() {\n" " FOO* foo = new (std::nothrow) FOO;\n" " return foo;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #6536 check("struct S { S(int) {} };\n" "void foo(int i) {\n" " S socket(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #6693 check("struct CTest {\n" " void Initialise();\n" " void malloc();\n" "};\n" "void CTest::Initialise() {\n" " malloc();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // #7348 - cast " p = (::X*)malloc(42);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7182 "crash: CheckMemoryLeak::functionReturnType()" check("template auto unary_right_comma (Ts... ts) { return (ts , ...); }\n" "template auto binary_left_comma (T x, Ts... ts) { return (x , ... , ts); }\n" "int main() {\n" " unary_right_comma (a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void smartPointerFunctionParam() { check("void x() {\n" " f(shared_ptr(new int(42)), g());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " h(12, f(shared_ptr(new int(42)), g()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(unique_ptr(new int(42)), g());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique() instead.\n", errout.str()); check("void x() {\n" " f(g(), shared_ptr(new int(42)));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(g(), unique_ptr(new int(42)));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique() instead.\n", errout.str()); check("void x() {\n" " f(shared_ptr(new char), make_unique(32));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If make_unique() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(g(124), h(\"test\", 234), shared_ptr(new char));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If h() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(shared_ptr(new std::string(\"\")), g());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void g(int x) throw() { }\n" "void x() {\n" " f(g(124), shared_ptr(new char));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void __declspec(nothrow) g(int x) { }\n" "void x() {\n" " f(g(124), shared_ptr(new char));\n" "}"); ASSERT_EQUALS("", errout.str()); } void resourceLeak() { check("void foo() {\n" " fopen(\"file.txt\", \"r\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Return value of allocation function 'fopen' is not stored.\n", errout.str()); check("void foo() {\n" " FILE f* = fopen(\"file.txt\", \"r\");\n" " freopen(\"file.txt\", \"r\", f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'freopen' is not stored.\n", errout.str()); check("void foo() {\n" " freopen(\"file.txt\", \"r\", stdin);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder h ( fopen(\"file.txt\", \"r\"));\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder ( fopen(\"file.txt\", \"r\"));\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder h { fopen(\"file.txt\", \"r\")};\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder h = fopen(\"file.txt\", \"r\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder { fopen(\"file.txt\", \"r\")};\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(int i, FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder { 0, fopen(\"file.txt\", \"r\")};\n" "}"); ASSERT_EQUALS("", errout.str()); } void getAllocationType() { // #7845 check("class Thing { Thing(); };\n" "Thing * makeThing() { Thing *thing = new Thing; return thing; }\n" "\n" "void f() {\n" " makeThing();\n" "}"); ASSERT_EQUALS("", errout.str()); // #10631 check("struct Thing {\n" " Thing();\n" "};\n" "std::vector g_things;\n" "Thing* makeThing() {\n" " Thing* n = new Thing();\n" " return n;\n" "}\n" "Thing::Thing() {\n" " g_things.push_back(this);\n" "}\n" "void f() {\n" " makeThing();\n" " for(Thing* t : g_things) {\n" " delete t;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void crash1() { // #10729 check("void foo() {\n" " extern void *realloc (void *ptr, size_t size);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " extern void *malloc (size_t size);\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestMemleakNoVar) cppcheck-2.7/test/testnullpointer.cpp000066400000000000000000004405551417746362400201330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check.h" #include "checknullpointer.h" #include "config.h" #include "ctu.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include #include #include #include #include #include #include #include class TestNullPointer : public TestFixture { public: TestNullPointer() : TestFixture("TestNullPointer") {} private: Settings settings; void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); settings.severity.enable(Severity::warning); TEST_CASE(nullpointerAfterLoop); TEST_CASE(nullpointer1); TEST_CASE(nullpointer2); TEST_CASE(structDerefAndCheck); // dereferencing struct and then checking if it's null TEST_CASE(pointerDerefAndCheck); TEST_CASE(nullpointer5); // References should not be checked TEST_CASE(nullpointerExecutionPaths); TEST_CASE(nullpointerExecutionPathsLoop); TEST_CASE(nullpointer7); TEST_CASE(nullpointer9); TEST_CASE(nullpointer10); TEST_CASE(nullpointer11); // ticket #2812 TEST_CASE(nullpointer12); // ticket #2470 TEST_CASE(nullpointer15); // #3560 (fp: return p ? f(*p) : f(0)) TEST_CASE(nullpointer16); // #3591 TEST_CASE(nullpointer17); // #3567 TEST_CASE(nullpointer18); // #1927 TEST_CASE(nullpointer19); // #3811 TEST_CASE(nullpointer20); // #3807 (fp: return p ? (p->x() || p->y()) : z) TEST_CASE(nullpointer21); // #4038 (fp: if (x) p=q; else return;) TEST_CASE(nullpointer23); // #4665 (false positive) TEST_CASE(nullpointer24); // #5082 fp: chained assignment TEST_CASE(nullpointer25); // #5061 TEST_CASE(nullpointer26); // #3589 TEST_CASE(nullpointer27); // #6568 TEST_CASE(nullpointer28); // #6491 TEST_CASE(nullpointer30); // #6392 TEST_CASE(nullpointer31); // #8482 TEST_CASE(nullpointer32); // #8460 TEST_CASE(nullpointer33); TEST_CASE(nullpointer34); TEST_CASE(nullpointer35); TEST_CASE(nullpointer36); // #9264 TEST_CASE(nullpointer37); // #9315 TEST_CASE(nullpointer38); TEST_CASE(nullpointer39); // #2153 TEST_CASE(nullpointer40); TEST_CASE(nullpointer41); TEST_CASE(nullpointer42); TEST_CASE(nullpointer43); // #9404 TEST_CASE(nullpointer44); // #9395, #9423 TEST_CASE(nullpointer45); TEST_CASE(nullpointer46); // #9441 TEST_CASE(nullpointer47); // #6850 TEST_CASE(nullpointer48); // #9196 TEST_CASE(nullpointer49); // #7804 TEST_CASE(nullpointer50); // #6462 TEST_CASE(nullpointer51); TEST_CASE(nullpointer52); TEST_CASE(nullpointer53); // #8005 TEST_CASE(nullpointer54); // #9573 TEST_CASE(nullpointer55); // #8144 TEST_CASE(nullpointer56); // #9701 TEST_CASE(nullpointer57); // #9751 TEST_CASE(nullpointer58); // #9807 TEST_CASE(nullpointer59); // #9897 TEST_CASE(nullpointer60); // #9842 TEST_CASE(nullpointer61); TEST_CASE(nullpointer62); TEST_CASE(nullpointer63); TEST_CASE(nullpointer64); TEST_CASE(nullpointer65); // #9980 TEST_CASE(nullpointer66); // #10024 TEST_CASE(nullpointer67); // #10062 TEST_CASE(nullpointer68); TEST_CASE(nullpointer69); // #8143 TEST_CASE(nullpointer70); TEST_CASE(nullpointer71); // #10178 TEST_CASE(nullpointer72); // #10215 TEST_CASE(nullpointer73); // #10321 TEST_CASE(nullpointer74); TEST_CASE(nullpointer75); TEST_CASE(nullpointer76); // #10408 TEST_CASE(nullpointer77); TEST_CASE(nullpointer78); // #7802 TEST_CASE(nullpointer79); // #10400 TEST_CASE(nullpointer80); // #10410 TEST_CASE(nullpointer81); // #8724 TEST_CASE(nullpointer82); // #10331 TEST_CASE(nullpointer83); // #9870 TEST_CASE(nullpointer84); // #9873 TEST_CASE(nullpointer85); // #10210 TEST_CASE(nullpointer86); TEST_CASE(nullpointer87); // #9291 TEST_CASE(nullpointer88); // #9949 TEST_CASE(nullpointer89); // #10640 TEST_CASE(nullpointer90); // #6098 TEST_CASE(nullpointer91); // #10678 TEST_CASE(nullpointer_addressOf); // address of TEST_CASE(nullpointerSwitch); // #2626 TEST_CASE(nullpointer_cast); // #4692 TEST_CASE(nullpointer_castToVoid); // #3771 TEST_CASE(nullpointer_subfunction); TEST_CASE(pointerCheckAndDeRef); // check if pointer is null and then dereference it TEST_CASE(nullConstantDereference); // Dereference NULL constant TEST_CASE(gcc_statement_expression); // Don't crash TEST_CASE(snprintf_with_zero_size); TEST_CASE(snprintf_with_non_zero_size); TEST_CASE(printf_with_invalid_va_argument); TEST_CASE(scanf_with_invalid_va_argument); TEST_CASE(nullpointer_in_return); TEST_CASE(nullpointer_in_typeid); TEST_CASE(nullpointer_in_for_loop); TEST_CASE(nullpointerDelete); TEST_CASE(nullpointerSubFunction); TEST_CASE(nullpointerExit); TEST_CASE(nullpointerStdString); TEST_CASE(nullpointerStdStream); TEST_CASE(nullpointerSmartPointer); TEST_CASE(functioncall); TEST_CASE(functioncalllibrary); // use Library to parse function call TEST_CASE(functioncallDefaultArguments); TEST_CASE(nullpointer_internal_error); // #5080 TEST_CASE(ticket6505); TEST_CASE(subtract); TEST_CASE(addNull); TEST_CASE(isPointerDeRefFunctionDecl); TEST_CASE(ctuTest); } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], bool inconclusive = false, const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); settings.certainty.setEnabled(Certainty::inconclusive, inconclusive); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename), file, line); // Check for null pointer dereferences.. CheckNullPointer checkNullPointer; checkNullPointer.runChecks(&tokenizer, &settings, this); } void checkP(const char code[]) { // Clear the error buffer.. errout.str(""); settings.certainty.setEnabled(Certainty::inconclusive, false); // Raw tokens.. std::vector files(1, "test.cpp"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenizer.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); // Check for null pointer dereferences.. CheckNullPointer checkNullPointer; checkNullPointer.runChecks(&tokenizer, &settings, this); } void nullpointerAfterLoop() { // extracttests.start: struct Token { const Token *next() const; std::string str() const; }; check("void foo(const Token *tok)\n" "{\n" " while (tok);\n" " tok = tok->next();\n" "}", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout.str()); // #2681 { const char code[] = "void foo(const Token *tok)\n" "{\n" " while (tok && tok->str() == \"=\")\n" " tok = tok->next();\n" "\n" " if (tok->str() != \";\")\n" " ;\n" "}\n"; check(code); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout.str()); } check("void foo()\n" "{\n" " for (const Token *tok = tokens; tok; tok = tok->next())\n" " {\n" " while (tok && tok->str() != \";\")\n" " tok = tok->next();\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'while' is redundant or there is possible null pointer dereference: tok.\n", "", errout.str()); check("void foo(Token &tok)\n" "{\n" " for (int i = 0; i < tok.size(); i++ )\n" " {\n" " while (!tok)\n" " char c = tok.read();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " for (const Token *tok = tokens; tok; tok = tok->next())\n" " {\n" " while (tok && tok->str() != \";\")\n" " tok = tok->next();\n" " if( !tok ) break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " for (const Token *tok = tokens; tok; tok = tok ? tok->next() : NULL)\n" " {\n" " while (tok && tok->str() != \";\")\n" " tok = tok->next();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(A*a)\n" "{\n" " switch (a->b()) {\n" " case 1:\n" " while( a ){\n" " a = a->next;\n" " }\n" " break;\n" " case 2:\n" " a->b();\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // dereference in outer scope.. check("void foo(int x, const Token *tok) {\n" " if (x == 123) {\n" " while (tok) tok = tok->next();\n" " }\n" " tok->str();\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout.str()); check("int foo(const Token *tok)\n" "{\n" " while (tok){;}\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("int foo(const Token *tok)\n" "{\n" " while (tok){;}\n" " char a[2] = {0,0};\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("struct b {\n" " b * c;\n" " int i;\n" "}\n" "void a(b * e) {\n" " for (b *d = e;d; d = d->c)\n" " while (d && d->i == 0)\n" " d = d->c;\n" " if (!d) throw;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct b {\n" " b * c;\n" " int i;\n" "};\n" "void f(b* e1, b* e2) {\n" " for (const b* d = e1; d != e2; d = d->c) {\n" " if (d && d->i != 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6]: (warning) Either the condition 'd' is redundant or there is possible null pointer dereference: d.\n", errout.str()); } void nullpointer1() { // ticket #1923 - no false positive when using else if check("void f(A *a)\n" "{\n" " if (a->x == 1)\n" " {\n" " a = a->next;\n" " }\n" " else if (a->x == 2) { }\n" " if (a) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #2134 - sizeof doesn't dereference check("void f() {\n" " int c = 1;\n" " int *list = NULL;\n" " sizeof(*list);\n" " if (!list)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); // ticket #2245 - sizeof doesn't dereference check("void f(Bar *p) {\n" " if (!p) {\n" " int sz = sizeof(p->x);\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer2() { // Null pointer dereference can only happen with pointers check("void foo()\n" "{\n" " Fred fred;\n" " while (fred);\n" " fred.hello();\n" "}", true); ASSERT_EQUALS("", errout.str()); } // Dereferencing a struct and then checking if it is null // This is checked by this function: // CheckOther::nullPointerStructByDeRefAndChec void structDerefAndCheck() { // extracttests.start: struct ABC { int a; int b; int x; }; // errors.. check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); check("void foo(struct ABC *abc) {\n" " bar(abc->a);\n" " bar(x, abc->a);\n" " bar(x, y, abc->a);\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n" "[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n" "[test.cpp:5] -> [test.cpp:4]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); check("void foo(ABC *abc) {\n" " if (abc->a == 3) {\n" " return;\n" " }\n" " if (abc) {}\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); check("void f(ABC *abc) {\n" " if (abc->x == 0) {\n" " return;\n" " }\n" " if (!abc);\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // TODO: False negative if member of member is dereferenced check("void foo(ABC *abc) {\n" " abc->next->a = 0;\n" " if (abc->next)\n" " ;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n", "", errout.str()); check("void foo(ABC *abc) {\n" " abc->a = 0;\n" " if (abc && abc->b == 0)\n" " ;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // ok dereferencing in a condition check("void foo(struct ABC *abc)\n" "{\n" " if (abc && abc->a);\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" " int x = abc && a(abc->x);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok to use a linked list.. check("void foo(struct ABC *abc)\n" "{\n" " abc = abc->next;\n" " if (!abc)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" " abc = (ABC *)(abc->_next);\n" " if (abc) { }" "}", true); ASSERT_EQUALS("", errout.str()); // reassign struct.. check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;\n" " abc = abc->next;\n" " if (!abc)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;\n" " f(&abc);\n" " if (!abc)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); // goto.. check("void foo(struct ABC *abc)\n" "{\n" " int a;\n" " if (!abc)\n" " goto out;" " a = abc->a;\n" " return;\n" "out:\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); // loops.. check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;" " do\n" " {\n" " if (abc)\n" " abc = abc->next;\n" " --a;\n" " }\n" " while (a > 0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())\n" " {\n" " while (tok && tok->str() != \"{\")\n" " tok = tok->next();\n" " if (!tok)\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // dynamic_cast.. check("void foo(ABC *abc)\n" "{\n" " int a = abc->a;\n" " if (!dynamic_cast(abc))\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); // #2641 - global pointer, function call check("ABC *abc;\n" "void f() {\n" " abc->a = 0;\n" " do_stuff();\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("",errout.str()); check("Fred *fred;\n" "void f() {\n" " fred->foo();\n" " if (fred) { }\n" "}"); ASSERT_EQUALS("",errout.str()); // #2641 - local pointer, function call check("void f() {\n" " ABC *abc = abc1;\n" " abc->a = 0;\n" " do_stuff();\n" " if (abc) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // #2641 - local pointer, function call check("void f(ABC *abc) {\n" " abc->a = 0;\n" " do_stuff();\n" " if (abc) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // #2691 - switch/break check("void f(ABC *abc) {\n" " switch ( x ) {\n" " case 14:\n" " sprintf(buf, \"%d\", abc->a);\n" " break;\n" " case 15:\n" " if ( abc ) {}\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3128 check("void f(ABC *abc) {\n" " x(!abc || y(abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(ABC *abc) {\n" " x(def || !abc || y(def, abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(ABC *abc) {\n" " x(abc && y(def, abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(ABC *abc) {\n" " x(def && abc && y(def, abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #3228 - calling function with null object { const char code[] = "void f(Fred *fred) {\n" " fred->x();\n" " if (fred) { }\n" "}"; check(code); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'fred' is redundant or there is possible null pointer dereference: fred.\n", errout.str()); } // #3425 - false positives when there are macros checkP("#define IF if\n" "void f(struct FRED *fred) {\n" " fred->x = 0;\n" " IF(!fred){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " BUFFER *buffer = get_buffer();\n" " if (!buffer)\n" " uv_fatal_error();\n" " buffer->x = 11;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Dereferencing a pointer and then checking if it is null void pointerDerefAndCheck() { // extracttests.start: void bar(int); // errors.. check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (p) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (p || q) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(int *p)\n" "{\n" " bar(*p);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p)\n" "{\n" " strcpy(p, \"abc\");\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p)\n" "{\n" " if (*p == 0) { }\n" " if (!p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // no error check("void foo()\n" "{\n" " int *p;\n" " f(&p);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int **p = f();\n" " if (!p)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(int *p)\n" "{\n" " if (x)\n" " p = 0;\n" " else\n" " *p = 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x)\n" "{\n" " int a = 2 * x;" " if (x == 0)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(int *p)\n" "{\n" " int var1 = p ? *p : 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p, bool x)\n" "{\n" " int var1 = x ? *p : 5;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // while check("void f(int *p) {\n" " *p = 0;\n" " while (p) { p = 0; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " *p = 0;\n" " while (p) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // Ticket #3125 check("void foo(ABC *p)\n" "{\n" " int var1 = p ? (p->a) : 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(ABC *p)\n" "{\n" " int var1 = p ? (1 + p->a) : 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * a=0;\n" " if (!a) {};\n" " int c = a ? 0 : 1;\n" "}\n",true); ASSERT_EQUALS("", errout.str()); // #3686 check("void f() {\n" " int * a=0;\n" " if (!a) {};\n" " int c = a ? b : b+1;\n" "}\n",true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * a=0;\n" " if (!a) {};\n" " int c = (a) ? b : b+1;\n" "}\n",true); ASSERT_EQUALS("", errout.str()); check("void foo(P *p)\n" "{\n" " while (p)\n" " if (p->check())\n" " break;\n" " else\n" " p = p->next();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(Document *doc) {\n" " int x = doc && doc->x;\n" " if (!doc) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3128 - false positive check("void f(int *p) {\n" " assert(!p || (*p<=6));\n" " if (p) { *p = 0; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " assert(p && (*p<=6));\n" " if (p) { *p = 0; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " *p = 12;\n" " assert(p && (*p<=6));\n" " if (p) { *p = 0; }\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(x *p)\n" "{\n" " p = p->next;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(x *p)\n" "{\n" " p = bar(p->next);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(x *p)\n" "{\n" " p = aa->bar(p->next);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(x *p)\n" "{\n" " p = *p2 = p->next;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(struct ABC *abc)\n" "{\n" " abc = abc ? abc->next : 0;\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" // #4523 " abc = (*abc).next;\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" // #4523 " abc = (*abc->ptr);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(Item *item) {\n" " x = item ? ab(item->x) : 0;\n" " if (item) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(Item *item) {\n" " item->x = 0;\n" " a = b ? c : d;\n" " if (item) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition 'item' is redundant or there is possible null pointer dereference: item.\n", errout.str()); check("BOOL GotoFlyAnchor()\n" // #2243 "{\n" " const SwFrm* pFrm = GetCurrFrm();\n" " do {\n" " pFrm = pFrm->GetUpper();\n" " } while( pFrm && !pFrm->IsFlyFrm() );\n" "\n" " if( !pFrm )\n" " return FALSE;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #2463 check("struct A\n" "{\n" " B* W;\n" "\n" " void f() {\n" " switch (InData) {\n" " case 2:\n" " if (!W) return;\n" " W->foo();\n" " break;\n" " case 3:\n" " f();\n" " if (!W) return;\n" " break;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #2525 - sizeof check("void f() {\n" " int *test = NULL;\n" " int c = sizeof(test[0]);\n" " if (!test)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(type* p) {\n" // #4983 " x(sizeof p[0]);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3023 - checked deref check("void f(struct ABC *abc) {\n" " WARN_ON(!abc || abc->x == 0);\n" " if (!abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" " WARN_ON(!abc || abc->x == 7);\n" " if (!abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3425 - false positives when there are macros checkP("#define IF if\n" "void f(int *p) {\n" " *p = 0;\n" " IF(!p){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #3914 - false positive " int *p;\n" " ((p=ret()) && (x=*p));\n" " if (p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer5() { // errors.. check("void foo(A &a)\n" "{\n" " char c = a.c();\n" " if (!a)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Execution paths.. void nullpointerExecutionPaths() { // errors.. check("static void foo()\n" "{\n" " Foo *p = 0;\n" " if (a == 1) {\n" " p = new FooBar;\n" " } else { if (a == 2) {\n" " p = new FooCar; } }\n" " p->abcd();\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Possible null pointer dereference: p\n", "", errout.str()); check("static void foo() {\n" " int &r = *(int*)0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout.str()); check("static void foo(int x) {\n" " int y = 5 + *(int*)0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout.str()); { const char code[] = "static void foo() {\n" " Foo *abc = 0;\n" " abc->a();\n" "}\n"; check(code); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: abc\n", errout.str()); } check("static void foo() {\n" " std::cout << *(int*)0;" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout.str()); check("void f()\n" "{\n" " char *c = 0;\n" " {\n" " delete c;\n" " }\n" " c[0] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Null pointer dereference: c\n", errout.str()); check("static void foo() {\n" " if (3 > *(int*)0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout.str()); // no false positive.. check("static void foo()\n" "{\n" " Foo *p = 0;\n" " p = new Foo;\n" " p->abcd();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int sz = sizeof((*(struct dummy *)0).x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void get_offset(long &offset)\n" "{\n" " mystruct * temp; temp = 0;\n" " offset = (long)(&(temp->z));\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #1893 - try/catch inside else check("int *test(int *Z)\n" "{\n" " int *Q=NULL;\n" " if (Z) {\n" " Q = Z;\n" " }\n" " else {\n" " Z = new int;\n" " try {\n" " } catch(...) {\n" " }\n" " Q = Z;\n" " }\n" " *Q=1;\n" " return Q;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int *test(int *Z)\n" "{\n" " int *Q=NULL;\n" " if (Z) {\n" " Q = Z;\n" " }\n" " else {\n" " try {\n" " } catch(...) {\n" " }\n" " }\n" " *Q=1;\n" " return Q;\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (warning) Possible null pointer dereference: Q\n", errout.str()); // Ticket #2052 (false positive for 'else continue;') check("void f() {\n" " for (int x = 0; x < 5; ++x) {" " int *p = 0;\n" " if (a(x)) p=b(x);\n" " else continue;\n" " *p = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // function pointer.. check("void foo()\n" "{\n" " void (*f)();\n" " f = 0;\n" " f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f\n", errout.str()); // loops.. check("void f() {\n" " int *p = 0;\n" " for (int i = 0; i < 10; ++i) {\n" " int x = *p + 1;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("void f(int a) {\n" " const char *p = 0;\n" " if (a) {\n" " p = \"abcd\";\n" " }\n" " for (int i = 0; i < 3; i++) {\n" " if (a && (p[i] == '1'));\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); // ticket #2251: taking the address of member check("void f() {\n" " Fred *fred = 0;\n" " int x = &fred->x;\n" "}", true); ASSERT_EQUALS("", errout.str()); // ticket #3220: dereferencing a null pointer is UB check("void f() {\n" " Fred *fred = NULL;\n" " fred->do_something();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: fred\n", errout.str()); // ticket #3570 - parsing of conditions { check("void f() {\n" " int *p = NULL;\n" " if (x)\n" " p = q;\n" " if (p && *p) { }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *p = NULL;\n" " if (x)\n" " p = q;\n" " if (!p || *p) { }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *p = NULL;\n" " if (x)\n" " p = q;\n" " if (p || *p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Possible null pointer dereference: p\n", errout.str()); } // ticket #8831 - FP triggered by if/return/else sequence { check("void f(int *p, int *q) {\n" " if (p == NULL)\n" " return;\n" " else if (q == NULL)\n" " return;\n" " *q = 0;\n" "}\n" "\n" "void g() {\n" " f(NULL, NULL);\n" "}", true); ASSERT_EQUALS("", errout.str()); } check("void f() {\n" // #5979 " int* const crash = 0;\n" " *crash = 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: crash\n", errout.str()); } // Ticket #2350 void nullpointerExecutionPathsLoop() { // No false positive: check("void foo() {\n" " int n;\n" " int *argv32 = p;\n" " if (x) {\n" " n = 0;\n" " argv32 = 0;\n" " }\n" "\n" " for (int i = 0; i < n; i++) {\n" " argv32[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // No false negative: check("void foo() {\n" " int n;\n" " int *argv32;\n" " if (x) {\n" " n = 10;\n" " argv32 = 0;\n" " }\n" "\n" " for (int i = 0; i < n; i++) {\n" " argv32[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (warning) Possible null pointer dereference: argv32\n", errout.str()); // #2231 - error if assignment in loop is not used // extracttests.start: int y[20]; check("void f() {\n" " char *p = 0;\n" "\n" " for (int x = 0; x < 3; ++x) {\n" " if (y[x] == 0) {\n" " p = (char *)malloc(10);\n" " break;\n" " }\n" " }\n" "\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (warning) Possible null pointer dereference: p\n", errout.str()); } void nullpointer7() { check("void foo()\n" "{\n" " wxLongLong x = 0;\n" " int y = x.GetValue();\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer9() { //#ticket 1778 check("void foo()\n" "{\n" " std::string * x = 0;\n" " *x = \"test\";\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: x\n", errout.str()); } void nullpointer10() { // extracttests.start: struct my_type { int x; }; check("void foo()\n" "{\n" " struct my_type* p = 0;\n" " p->x = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); } void nullpointer11() { // ticket #2812 // extracttests.start: struct my_type { int x; }; check("int foo()\n" "{\n" " struct my_type* p;\n" " p = 0;\n" " return p->x;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: p\n", errout.str()); } void nullpointer12() { // ticket #2470, #4035 const char code[] = "int foo()\n" "{\n" " int* i = nullptr;\n" " return *i;\n" "}\n"; check(code, false, "test.cpp"); // C++ file => nullptr means NULL ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: i\n", errout.str()); check(code, false, "test.c"); // C file => nullptr does not mean NULL ASSERT_EQUALS("", errout.str()); } void nullpointer15() { // #3560 check("void f() {\n" " char *p = 0;\n" " if (x) p = \"abcd\";\n" " return p ? f(*p) : f(0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer16() { // #3591 check("void foo() {\n" " int *p = 0;\n" " bar(&p);\n" " *p = 0;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer17() { // #3567 check("int foo() {\n" " int *p = 0;\n" " if (x) { return 0; }\n" " return !p || *p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " int *p = 0;\n" " if (x) { return 0; }\n" " return p && *p;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer18() { // #1927 check("void f ()\n" "{\n" " int i=0;\n" " char *str=NULL;\n" " while (str[i])\n" " {\n" " i++;\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: str\n", errout.str()); } void nullpointer19() { // #3811 check("int foo() {\n" " perror(0);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer20() { // #3807 check("void f(int x) {\n" " struct xy *p = 0;\n" " if (x) p = q;\n" " if (p ? p->x || p->y : 0) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" // false negative " struct xy *p = 0;\n" " if (x) p = q;\n" " if (y ? p->x : p->y) { }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: p\n", "", errout.str()); } void nullpointer21() { // #4038 - fp: if (x) p=q; else return; check("void f(int x) {\n" " int *p = 0;\n" " if (x) p = q;\n" " else return;\n" " *p = 0;\n" // <- p is not NULL "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer23() { // #4665 check("void f(){\n" " char *c = NULL;\n" " char cBuf[10];\n" " sprintf(cBuf, \"%s\", c ? c : \"0\" );\n" "}"); ASSERT_EQUALS("",errout.str()); } void nullpointer24() { // #5083 - fp: chained assignment check("void f(){\n" " char *c = NULL;\n" " x = c = new char[10];\n" " *c = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer25() { // #5061 check("void f(int *data, int i)\n" "{\n" " int *array = NULL;\n" " if (data == 1 && array[i] == 0)\n" " std::cout << \"test\";\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: array\n", errout.str()); } void nullpointer26() { // #3589 check("double foo() {\n" " sk *t1 = foo();\n" " sk *t2 = foo();\n" " if ((!t1) && (!t2))\n" " return 0.0;\n" " if (t1 && (!t2))\n" " return t1->Inter();\n" " if (t2->GetT() == t)\n" " return t2->Inter();\n" " if (t2 && (!t1))\n" " return 0.0;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer27() { // #6568 check("template\n" "class Foo {\n" " Foo& operator = ( Type* );\n" "};\n" "template\n" "Foo& Foo::operator = ( Type* pointer_ ) {\n" " pointer_=NULL;\n" " *pointer_=0;\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Null pointer dereference: pointer_\n", errout.str()); } void nullpointer28() { // #6491 check("typedef struct { int value; } S;\n" "int f(const S *s) {\n" " int i = s ? s->value + 1\n" " : s->value - 1; // <-- null ptr dereference\n" " return i;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: s\n", "", errout.str()); } void nullpointer30() { // #6392 check("void f(std::vector *values)\n" "{\n" " values->clear();\n" " if (values)\n" " {\n" " for (int i = 0; i < values->size(); ++i)\n" " {\n" " values->push_back(\"test\");\n" " }\n" " }\n" "}\n", true); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'values' is redundant or there is possible null pointer dereference: values.\n", errout.str()); } void nullpointer31() { // #8482 check("struct F\n" "{\n" " int x;\n" "};\n" "\n" "static void foo(F* f)\n" "{\n" " if( f ) {}\n" " else { return; }\n" " (void)f->x;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("typedef struct\n" "{\n" " int x;\n" "} F;\n" "\n" "static void foo(F* f)\n" "{\n" " if( !f || f->x == 0 )\n" " {\n" " if( !f )\n" " return;\n" " }\n" "\n" " (void)f->x;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer32() { // #8460 check("int f(int * ptr) {\n" " if(ptr)\n" " { return 0;}\n" " else{\n" " int *p1 = ptr;\n" " return *p1;\n" " }\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition 'ptr' is redundant or there is possible null pointer dereference: p1.\n", errout.str()); } void nullpointer33() { check("void f(int * x) {\n" " if (x != nullptr)\n" " *x = 2;\n" " else\n" " *x = 3;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'x!=nullptr' is redundant or there is possible null pointer dereference: x.\n", errout.str()); } void nullpointer34() { check("void g() {\n" " throw " ";\n" "}\n" "bool f(int * x) {\n" " if (x) *x += 1;\n" " if (!x) g();\n" " return *x;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer35() { check("bool f(int*);\n" "void g(int* x) {\n" " if (f(x)) {\n" " *x = 1;\n" " }\n" "}\n" "void h() {\n" " g(0);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("bool f(int*);\n" "void g(int* x) {\n" " bool b = f(x);\n" " if (b) {\n" " *x = 1;\n" " }\n" "}\n" "void h() {\n" " g(0);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer36() { check("char* f(char* s) {\n" " char* start = s;\n" " if (!s)\n" " return (s);\n" " while (isspace(*start))\n" " start++;\n" " return (start);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer37() { check("void f(int value, char *string) {\n" " char *ptr1 = NULL, *ptr2 = NULL;\n" " unsigned long count = 0;\n" " if(!string)\n" " return;\n" " ptr1 = string;\n" " ptr2 = strrchr(string, 'a');\n" " if(ptr2 == NULL)\n" " return;\n" " while(ptr1 < ptr2) {\n" " count++;\n" " ptr1++;\n" " }\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer38() { check("void f(int * x) {\n" " std::vector v;\n" " if (x) {\n" " v.push_back(x);\n" " *x;\n" " }\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer39() { check("struct A { int * x; };\n" "void f(struct A *a) {\n" " if (a->x == NULL) {}\n" " *(a->x);\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->x==NULL' is redundant or there is possible null pointer dereference: a->x.\n", errout.str()); } void nullpointer40() { check("struct A { std::unique_ptr x; };\n" "void f(struct A *a) {\n" " if (a->x == nullptr) {}\n" " *(a->x);\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->x==nullptr' is redundant or there is possible null pointer dereference: a->x.\n", errout.str()); } void nullpointer41() { check("struct A { int * g() const; };\n" "void f(struct A *a) {\n" " if (a->g() == nullptr) {}\n" " *(a->g());\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->g()==nullptr' is redundant or there is possible null pointer dereference: a->g().\n", errout.str()); check("struct A { int * g(); };\n" "void f(struct A *a) {\n" " if (a->g() == nullptr) {}\n" " *(a->g());\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer42() { check("struct A { std::unique_ptr g() const; };\n" "void f(struct A *a) {\n" " if (a->g() == nullptr) {}\n" " *(a->g());\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->g()==nullptr' is redundant or there is possible null pointer dereference: a->g().\n", errout.str()); } void nullpointer43() { check("struct A { int* x; };\n" "void f(A* a) {\n" " int * x = a->x;\n" " if (x) {\n" " (void)*a->x;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer44() { // #9395 check("int foo( ) {\n" " const B* b = getB();\n" " const double w = ( nullptr != b) ? 42. : 0.0;\n" " if ( w == 0.0 )\n" " return 0;\n" " return b->get();\n" "}"); ASSERT_EQUALS("", errout.str()); // #9423 check("extern F* GetF();\n" "extern L* GetL();\n" "void Foo() {\n" " const F* const fPtr = GetF();\n" " const bool fPtrOk = fPtr != NULL;\n" " assert(fPtrOk);\n" " if (!fPtrOk)\n" " return;\n" " L* const lPtr = fPtr->l;\n" " const bool lPtrOk = lPtr != NULL;\n" " assert(lPtrOk);\n" " if (!lPtrOk)\n" " return;\n" " lPtr->Clear();\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer45() { check("struct a {\n" " a *b() const;\n" "};\n" "void g() { throw 0; }\n" "a h(a * c) {\n" " if (c && c->b()) {}\n" " if (!c)\n" " g();\n" " if (!c->b())\n" " g();\n" " a d = *c->b();\n" " return d;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " a *b() const;\n" "};\n" "void e() { throw 0; }\n" "a f() {\n" " a *c = 0;\n" " if (0 && c->b()) {}\n" " if (!c)\n" " e();\n" " if (!c->b())\n" " e();\n" " a d = *c->b();\n" " return d;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer46() { check("void f() {\n" " char* p = new(std::nothrow) char[1];\n" " if( p ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer47() { check("void f(int *p) {\n" " if(!p[0]) {}\n" " const int *const a = p;\n" " if(!a){}\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition '!a' is redundant or there is possible null pointer dereference: p.\n", errout.str()); } void nullpointer48() { check("template\n" "auto f(T& x) -> decltype(x);\n" "int& g(int* x) {\n" " return f(*x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer49() { check("void f(int *p, int n) {\n" " int *q = 0;\n" " if(n > 10) q = p;\n" " *p +=2;\n" " if(n < 120) *q+=12;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Possible null pointer dereference: q\n", errout.str()); check("void f(int *p, int n) {\n" " int *q = 0;\n" " if(n > 10) q = p;\n" " *p +=2;\n" " if(n > 10) *q+=12;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer50() { check("void f(int *p, int a) {\n" " if(!p) {\n" " if(a > 0) {\n" " if(a > 10){}\n" " else {\n" " *p = 0;\n" " }\n" " }\n" " }\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); } void nullpointer51() { check("struct a {\n" " a *b();\n" "};\n" "bool c(a *, const char *);\n" "a *d(a *e) {\n" " if (e) {}\n" " if (c(e, \"\"))\n" " return nullptr;\n" " return e->b();\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer52() { check("int f(int a, int* b) {\n" " int* c = nullptr;\n" " if(b) c = b;\n" " if (!c) c = &a;\n" " return *c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a, int* b) {\n" " int* c = nullptr;\n" " if(b) c = b;\n" " bool d = !c;\n" " if (d) c = &a;\n" " return *c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int* x; };\n" "int f(int a, int* b) {\n" " A c;\n" " c.x = nullptr;\n" " if(b) c.x = b;\n" " if (!c.x) c.x = &a;\n" " return *c.x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int* x; };\n" "int f(int a, int* b) {\n" " A c;\n" " c.x = nullptr;\n" " if(b) c.x = b;\n" " bool d = !c.x;\n" " if (d) c.x = &a;\n" " return *c.x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int* x; };\n" "int f(int a, int* b) {\n" " A c;\n" " c.x = nullptr;\n" " if(b) c.x = b;\n" " bool d = !c.x;\n" " if (!d) c.x = &a;\n" " return *c.x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (warning) Possible null pointer dereference: c.x\n", errout.str()); } void nullpointer53() { check("void f(int nParams, int* params) {\n" " for (int n=1; nastParent() && tok3->str() == \",\")\n" " tok3 = tok3->astParent();\n" " if (tok3 && tok3->str() == \"(\") {}\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'tok3' is redundant or there is possible null pointer dereference: tok3.\n", errout.str()); check("void f(int* t1, int* t2) {\n" " while (t1 && t2 &&\n" " *t1 == *t2) {\n" " t1 = nullptr;\n" " t2 = nullptr;\n" " }\n" " if (!t1 || !t2)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int* i);\n" "void g(int* i) {\n" " while(f(i) && *i == 0)\n" " i++;\n" " if (!i) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer56() { check("struct ListEntry {\n" " struct ListEntry *next;\n" "};\n" "static void dostuff(ListEntry * listHead) {\n" " ListEntry *prev = NULL;\n" " for (ListEntry *cursor = listHead; cursor != NULL; prev = cursor, cursor = cursor->next) {}\n" " if (prev) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer57() { check("void f() {\n" " FILE* fptr = fopen(\"test\", \"r\");\n" " if (fptr != nullptr) {\n" " std::function fn([&] {\n" " fclose(fptr);\n" " fptr = NULL;\n" " });\n" " fgetc(fptr);\n" " fn();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer58() { check("struct myStruct { char entry[0]; };\n" "void f() {\n" " struct myStruct* sPtr = NULL;\n" " int sz = (!*(&sPtr) || ((*(&sPtr))->entry[0] > 15)) ?\n" " sizeof((*(&sPtr))->entry[0]) : 123456789;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer59() { check("struct Box {\n" " struct Box* prev;\n" " struct Box* next;\n" "};\n" "void foo(Box** pfreeboxes) {\n" " Box *b = *pfreeboxes;\n" " *pfreeboxes = b->next;\n" " if( *pfreeboxes )\n" " (*pfreeboxes)->prev = nullptr;\n" " b->next = nullptr;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer60() { check("void f(){\n" " char uuid[128];\n" " char *s1;\n" " memset(uuid, 0, sizeof(uuid));\n" " s1 = strchr(uuid, '=');\n" " s1 = s1 ? s1 + 1 : &uuid[5];\n" " if (!strcmp(\"00000000000000000000000000000000\", s1) )\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer61() { check("struct a {\n" " int *e;\n" "};\n" "struct f {\n" " a *g() const;\n" "};\n" "void h() {\n" " for (f b;;) {\n" " a *c = b.g();\n" " int *d = c->e;\n" " if (d)\n" " ;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " A* g() const;\n" " A* h() const;\n" "};\n" "void f(A* a) {\n" " if (!a->h())\n" " return;\n" " const A *b = a;\n" " while (b && !b->h())\n" " b = b->g();\n" " if (!b || b == b->g()->h())\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer62() { check("struct A {\n" " bool f()() const;\n" "};\n" "void a(A *x) {\n" " std::string b = x && x->f() ? \"\" : \"\";\n" " if (x) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " bool f()() const;\n" "};\n" "void a(A *x) {\n" " std::string b = (!x || x->f()) ? \"\" : \"\";\n" " if (x) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " A * aa;\n" "};\n" "void b(A*);\n" "void a(A *x) {\n" " b(x ? x->aa : nullptr);\n" " if (!x) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer63() { check("struct A {\n" " A* a() const;\n" " A* b() const;\n" "};\n" "A* f(A*);\n" "void g(const A* x) {\n" " A *d = x->a();\n" " d = f(d->b()) ? d->a() : nullptr;\n" " if (d && f(d->b())) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer64() { check("struct A {\n" " A* f() const;\n" " int g() const;\n" "};\n" "bool a;\n" "bool b(A* c) {\n" " if (c->g() == 0)\n" " ;\n" " A *aq = c;\n" " if (c->g() == 0)\n" " c = c->f();\n" " if (c)\n" " for (A *d = c; d != aq; d = d->f()) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " A* g() const;\n" " A* h() const;\n" "};\n" "bool i(A*);\n" "void f(A* x) {\n" " if (i(x->g())) {\n" " A *y = x->g();\n" " x = x->g()->h();\n" " if (x && x->g()) {\n" " y = x->g()->h();\n" " }\n" " if (!y) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer65() { check("struct A {\n" " double get();\n" "};\n" "double x;\n" "double run(A** begin, A** end) {\n" " A* a = nullptr;\n" " while (begin != end) {\n" " a = *begin;\n" " x = a->get();\n" " ++begin;\n" " }\n" " x = 0;\n" " if (a)\n" " return a->get();\n" " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer66() { check("int f() {\n" " int ret = 0;\n" " int *v = nullptr;\n" " if (!MyAlloc(&v)) {\n" " ret = -1;\n" " goto done;\n" " }\n" " DoSomething(*v);\n" "done:\n" " if (v)\n" " MyFree(&v);\n" " return ret;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer67() { check("int result;\n" "\n" "int test_b(void) {\n" " char **string = NULL;\n" "\n" " /* The bug disappears if \"result =\" is omitted. */\n" " result = some_other_call(&string);\n" " if (string && string[0])\n" " return 0;\n" " return -1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int result;\n" "\n" "int test_b(void) {\n" " char **string = NULL;\n" "\n" " some_other_call(&string);\n" " if (string && string[0])\n" " return 0;\n" " return -1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer68() { check("struct A {\n" " A* b;\n" "};\n" "void f(A* c) {\n" " c = c->b;\n" " if (c->b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " A* b;\n" "};\n" "void f(A* c) {\n" " A* d = c->b;\n" " A *e = c;\n" " while (nullptr != (e = e->b)) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer69() { check("void f(const Scope *scope) {\n" " if (scope->definedType) {}\n" " while (scope) {\n" " scope = scope->nestedIn;\n" " enumerator = scope->findEnumerator();\n" " }\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:5]: (warning) Either the condition 'scope' is redundant or there is possible null pointer dereference: scope.\n", errout.str()); check("void f(const Scope *scope) {\n" " if (scope->definedType) {}\n" " while (scope && scope->nestedIn) {\n" " if (scope->type == Scope::eFunction && scope->functionOf)\n" " scope = scope->functionOf;\n" " else\n" " scope = scope->nestedIn;\n" " enumerator = scope->findEnumerator();\n" " }\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:8]: (warning) Either the condition 'scope' is redundant or there is possible null pointer dereference: scope.\n", errout.str()); check("struct a {\n" " a *b() const;\n" " void c();\n" "};\n" "void d() {\n" " for (a *e;;) {\n" " e->b()->c();\n" " while (e)\n" " e = e->b();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer70() { check("struct Token {\n" " const Token* nextArgument() const;\n" " const Token* next() const;\n" " int varId() const;\n" "};\n" "int f(const Token *first, const Token* second) {\n" " first = first->nextArgument();\n" " if (first)\n" " first = first->next();\n" " if (second->next()->varId() == 0) {\n" " second = second->nextArgument();\n" " if (!first || !second)\n" " return 0;\n" " } else if (!first) {\n" " return 0;\n" " }\n" " return first->varId();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Token {\n" " const Token* nextArgument() const;\n" " const Token* next() const;\n" " int varId() const;\n" " void str() const;" "};\n" "void f(const Token *first) {\n" " first = first->nextArgument();\n" " if (first)\n" " first = first->next();\n" " first->str();\n" "}\n"); TODO_ASSERT_EQUALS( "[test.cpp:8] -> [test.cpp:10]: (warning) Either the condition 'first' is redundant or there is possible null pointer dereference: first.\n", "", errout.str()); } void nullpointer71() { check("void f() {\n" " Device* dev = Get();\n" " SetCount(dev == nullptr ? 0 : dev->size());\n" " if (dev)\n" " DoSomething(dev);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " Device* dev = Get();\n" " SetCount(dev != nullptr ? dev->size() : 0);\n" " if (dev)\n" " DoSomething(dev);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer72() { // #10215 check("int test() {\n" " int* p0 = nullptr, *p1 = nullptr;\n" " getFoo(p0);\n" " getBar(p1);\n" " if (!(p0 != nullptr && p1 != nullptr))\n" " return {};\n" " return *p0 + *p1;\n" "}\n", true /*inconclusive*/); ASSERT_EQUALS("", errout.str()); check("int test2() {\n" " int* p0 = nullptr;\n" " if (!(getBaz(p0) && p0 != nullptr))\n" " return 0;\n" " return *p0;\n" "}\n", true /*inconclusive*/); ASSERT_EQUALS("", errout.str()); check("int test3() {\n" " Obj* PObj = nullptr;\n" " if (!(GetObj(PObj) && PObj != nullptr))\n" " return 1;\n" " if (!PObj->foo())\n" " test();\n" " PObj->bar();\n" "}\n", true /*inconclusive*/); ASSERT_EQUALS("", errout.str()); } void nullpointer73() { check("void f(bool flag2, int* ptr) {\n" " bool flag1 = true;\n" " if (flag2) {\n" " if (ptr != nullptr)\n" " (*ptr)++;\n" " else\n" " flag1 = false;\n" " }\n" " if (flag1 && flag2)\n" " (*ptr)++;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool flag2, int* ptr) {\n" " bool flag1 = true;\n" " if (flag2) {\n" " if (ptr != nullptr)\n" " (*ptr)++;\n" " else\n" " flag1 = false;\n" " }\n" " if (!flag1 && flag2)\n" " (*ptr)++;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:10]: (warning) Either the condition 'ptr!=nullptr' is redundant or there is possible null pointer dereference: ptr.\n", errout.str()); } void nullpointer74() { check("struct d {\n" " d* e();\n" "};\n" "void g(d* f) {\n" " do {\n" " f = f->e();\n" " if (f) {}\n" " } while (0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct d {\n" " d* e();\n" "};\n" "void g(d* f, int i) {\n" " do {\n" " i--;\n" " f = f->e();\n" " if (f) {}\n" " } while (i > 0);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:8] -> [test.cpp:7]: (warning) Either the condition 'f' is redundant or there is possible null pointer dereference: f.\n", errout.str()); check("struct d {\n" " d* e();\n" "};\n" "void g(d* f, int i) {\n" " do {\n" " i--;\n" " f = f->e();\n" " if (f) {}\n" " } while (f && i > 0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer75() { check("struct a {\n" " a *b() const;\n" " void c();\n" " int d() const;\n" "};\n" "void e(a *x) {\n" " while (x->b()->d() == 0)\n" " x->c();\n" " x->c();\n" " if (x->b()) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer76() { check("int* foo(int y) {\n" " std::unique_ptr x = std::make_unique(0);\n" " if( y == 0 )\n" " return x.release();\n" " (*x) ++;\n" " return x.release();\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer77() { check("bool h(int*);\n" "void f(int* i) {\n" " int* i = nullptr;\n" " if (h(i) && *i == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool h(int*);\n" "void f(int* i) {\n" " int* i = nullptr;\n" " if (h(i))\n" " if (*i == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool h(int*);\n" "void f(int* x) {\n" " int* i = x;\n" " if (h(i))\n" " i = nullptr;\n" " if (h(i) && *i == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer78() // #7802 { check("void f()\n" "{\n" " int **pp;\n" " int *p = 0;\n" " pp = &p;\n" " **pp = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Null pointer dereference: *pp\n", errout.str()); } void nullpointer79() // #10400 { check("void resize(size_t nF, size_t nT) {\n" " double* pValues = nullptr;\n" " if (nF > 0 && nT > 0)\n" " pValues = new double[nF * nT];\n" " for (size_t cc = 0; cc < nF * nT; ++cc)\n" " pValues[cc] = 42;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer80() // #10410 { check("int f(int* a, int* b) {\n" " if( a || b ) {\n" " int n = a ? *a : *b;\n" " if( b )\n" " n++;\n" " return n;\n" " }\n" " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer81() // #8724 { check("void f(A **list) {\n" " A *tmp_List = NULL;\n" " *list = NULL;\n" " while (1) {\n" " if (*list == NULL) {\n" " tmp_List = malloc (sizeof (ArchiveList_struct));\n" " *list = tmp_List;\n" " } else {\n" " tmp_List->next = malloc (sizeof (ArchiveList_struct));\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer82() // #10331 { check("bool g();\n" "int* h();\n" "void f(int* ptr) {\n" " if (!ptr) {\n" " if (g())\n" " goto done;\n" " ptr = h();\n" " if (!ptr)\n" " return;\n" " }\n" " if (*ptr == 1)\n" " return;\n" "\n" "done:\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer83() // #9870 { check("int* qux();\n" "int* f7c2(int *x) {\n" " int* p = 0;\n" " if (nullptr == x)\n" " p = qux();\n" " if (nullptr == x)\n" " return x;\n" " *p = 1;\n" " return x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (warning) Possible null pointer dereference: p\n", errout.str()); } void nullpointer84() // #9873 { check("void f(std::unique_ptr P) {\n" " A *RP = P.get();\n" " if (!RP) {\n" " P->foo();\n" " }\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition '!RP' is redundant or there is possible null pointer dereference: P.\n", errout.str()); } void nullpointer85() // #10210 { check("struct MyStruct {\n" " int GetId() const {\n" " int id = 0;\n" " int page = m_notebook->GetSelection();\n" " if (m_notebook && (m_notebook->GetPageCount() > 0))\n" " id = page;\n" " return id;\n" " }\n" " wxNoteBook *m_notebook = nullptr;\n" "};\n" "int f() {\n" " const MyStruct &s = Get();\n" " return s.GetId();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:4]: (warning) Either the condition 'm_notebook' is redundant or there is possible null pointer dereference: m_notebook.\n", errout.str()); } void nullpointer86() { check("struct A {\n" " A* a() const;\n" " int b() const;\n" "};\n" "A* f(A* t) {\n" " if (t->b() == 0) {\n" " return t;\n" " }\n" " return t->a();\n" "}\n" "void g(A* t) {\n" " t = f(t->a());\n" " if (!t->a()) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer87() // #9291 { check("int f(bool b, int* x) {\n" " if (b && x == nullptr)\n" " return 0;\n" " else if (!b && x == nullptr)\n" " return 1;\n" " else if (!b && x != nullptr)\n" " return *x;\n" " else\n" " return *x + 1;\n" "}\n"); TODO_ASSERT_EQUALS("", "[test.cpp:6] -> [test.cpp:9]: (warning) Either the condition 'x!=nullptr' is redundant or there is possible null pointer dereference: x.\n", errout.str()); check("void f(int n, int* p) {\n" " int* r = nullptr;\n" " if (n < 0)\n" " return;\n" " if (n == 0)\n" " r = p;\n" " else if (n > 0)\n" " r = p + 1;\n" " *r;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer88() // #9949 { check("struct S { char **ppc; };\n" "int alloc(struct S* s) {\n" " char** ppc = malloc(4096);\n" " if (ppc != NULL) {\n" " s->ppc = ppc;\n" " return 1;\n" " }\n" " return 0;\n" "}\n" "void f() {\n" " struct S* s = malloc(sizeof(struct S));\n" " s->ppc = NULL;\n" " if (alloc(s))\n" " s->ppc[0] = \"\";\n" "}\n", /*inconclusive*/ false, "test.c"); ASSERT_EQUALS("", errout.str()); } void nullpointer89() // #10640 { check("typedef struct {\n" " int x;\n" "} foo_t;\n" "typedef struct {\n" " foo_t *y;\n" "} bar_t;\n" "void f(bar_t *ptr) {\n" " if(ptr->y->x)\n" " if(ptr->y != nullptr) {}\n" "}\n"); ASSERT_EQUALS( "[test.cpp:9] -> [test.cpp:8]: (warning) Either the condition 'ptr->y!=nullptr' is redundant or there is possible null pointer dereference: ptr->y.\n", errout.str()); } void nullpointer90() // #6098 { check("std::string definitionToName(Definition *ctx)\n" "{\n" " if (ctx->definitionType()==Definition::TypeMember)\n" // possible null pointer dereference " {\n" " return \"y\";\n" " }\n" " else if (ctx)\n" // ctx is checked against null " {\n" " if(ctx->definitionType()!=Definition::TypeMember)\n" " {\n" " return \"x\";\n" " }\n" " }\n" " return \"unknown\";\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:3]: (warning) Either the condition 'ctx' is redundant or there is possible null pointer dereference: ctx.\n", errout.str()); } void nullpointer91() // #10678 { check("void f(const char* PBeg, const char* PEnd) {\n" " while (PEnd != nullptr) {\n" " const int N = h(PEnd);\n" " PEnd = g();\n" " const int Length = PEnd == nullptr ? 0 : PEnd - PBeg;\n" " };\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer_addressOf() { // address of check("void f() {\n" " struct X *x = 0;\n" " if (addr == &x->y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct X *x = 0;\n" " if (addr == &x->y.z[0]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); checkP("typedef int Count;\n" // #10018 "#define offsetof(TYPE, MEMBER) ((Count) & ((TYPE*)0)->MEMBER)\n" "struct S {\n" " int a[20];\n" "};\n" "int g(int i) {\n" " return offsetof(S, a[i]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointerSwitch() { // #2626 // extracttests.start: char *do_something(); check("char *f(int x) {\n" " char *p = do_something();\n" " switch (x) {\n" " case 1:\n" " p = 0;\n" " case 2:\n" " *p = 0;\n" " break;\n" " }\n" " return p;\n" "}", true); ASSERT_EQUALS("[test.cpp:7]: (warning) Possible null pointer dereference: p\n", errout.str()); } void nullpointer_cast() { // #4692 check("char *nasm_skip_spaces(const char *p) {\n" " if (p)\n" " while (*p && nasm_isspace(*p))\n" " p++;\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer_castToVoid() { // #3771 check("void f () {\n" " int *buf; buf = NULL;\n" " buf;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer_subfunction() { check("int f(int* x, int* y) {\n" " if (!x)\n" " return;\n" " return *x + *y;\n" "}\n" "void g() {\n" " f(nullptr, nullptr);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } // Check if pointer is null and the dereference it void pointerCheckAndDeRef() { check("void foo(char *p) {\n" " if (!p) {\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p && *p == 0) {\n" " }\n" " printf(\"%c\", *p);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p && *p == 0) {\n" " } else { *p = 0; }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p) {\n" " }\n" " strcpy(p, \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p) {\n" " }\n" " bar();\n" " strcpy(p, \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(abc *p) {\n" " if (!p) {\n" " }\n" " else { if (!p->x) {\n" " } }\n" "}"); ASSERT_EQUALS("", errout.str()); { static const char code[] = "void foo(char *p) {\n" " if (!p) {\n" " abort();\n" " }\n" " *p = 0;\n" "}"; check(code, false); ASSERT_EQUALS("", errout.str()); check(code, true); ASSERT_EQUALS("", errout.str()); } check("void foo(char *p) {\n" " if (!p) {\n" " (*bail)();\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " throw x;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " ab.abort();\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " switch (x) { }\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " }\n" " return *x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int foo(int *p) {\n" " if (!p) {\n" " x = *p;\n" " return 5+*p;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // operator! check("void f() {\n" " A a;\n" " if (!a) {\n" " a.x();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // This is why this check can't be used on the simplified token list check("void f(Foo *foo) {\n" " if (!dynamic_cast(foo)) {\n" " *foo = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket: #2300 - calling unknown function that may initialize the pointer check("Fred *fred;\n" "void a() {\n" " if (!fred) {\n" " initfred();\n" " fred->x = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #1219 check("void foo(char *p) {\n" " if (p) {\n" " return;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // #2467 - unknown macro may terminate the application check("void f(Fred *fred) {\n" " if (fred == NULL) {\n" " MACRO;\n" " }\n" " fred->a();\n" "}"); ASSERT_EQUALS("", errout.str()); // #2493 - switch check("void f(Fred *fred) {\n" " if (fred == NULL) {\n" " x = 0;\n" " }\n" " switch (x) {\n" " case 1:\n" " fred->a();\n" " break;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); // #4118 - second if check("void f(char *p) {\n" " int x = 1;\n" " if (!p) x = 0;\n" " if (x) *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #2674 - different functions check("class Fred {\n" "public:\n" " Wilma *wilma;\n" " void a();\n" " void b();\n" "};\n" "\n" "void Fred::a() {\n" " if ( wilma ) { }\n" "}\n" "\n" "void Fred::b() {\n" " wilma->Reload();\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void test(int *i) {\n" " if(i == NULL) { }\n" " else {\n" " int b = *i;\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); // #2696 - false positives nr 1 check("void f()\n" "{\n" " struct foo *pFoo = NULL;\n" " size_t len;\n" "\n" " len = sizeof(*pFoo) - sizeof(pFoo->data);\n" "\n" " if (pFoo)\n" " bar();\n" "}", true); ASSERT_EQUALS("", errout.str()); // #2696 - false positives nr 2 check("void f()\n" "{\n" " struct foo *pFoo = NULL;\n" " size_t len;\n" "\n" " while (pFoo)\n" " pFoo = pFoo->next;\n" "\n" " len = sizeof(pFoo->data);\n" "}", true); ASSERT_EQUALS("", errout.str()); // #2696 - false positives nr 3 check("void f()\n" "{\n" " struct foo *pFoo = NULL;\n" " size_t len;\n" "\n" " while (pFoo)\n" " pFoo = pFoo->next;\n" "\n" " len = decltype(*pFoo);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int foo(struct Fred *fred) {\n" " if (fred) { }\n" " return fred->a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'fred' is redundant or there is possible null pointer dereference: fred.\n", errout.str()); // #2789 - assign and check pointer check("void f() {\n" " char *p; p = x();\n" " if (!p) { }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, assign and use check("void f() {\n" " char *p;\n" " if (p == 0 && (p = malloc(10)) != 0) {\n" " *p = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // check, assign and use check("void f() {\n" " char *p;\n" " if (p == 0 && (p = malloc(10)) != a && (*p = a)) {\n" " *p = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // check, and use check("void f() {\n" " char *p;\n" " if (p == 0 && (*p = 0)) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, and use check("void f() {\n" " struct foo *p;\n" " if (p == 0 && p->x == 10) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, and use check("void f() {\n" " struct foo *p;\n" " if (p == 0 || p->x == 10) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // check, and use check("void f() {\n" " char *p; p = malloc(10);\n" " if (p == NULL && (*p = a)) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==NULL' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, and use check("void f(struct X *p, int x) {\n" " if (!p && x==1 || p && p->x==0) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); { const char code[] = "void f(Fred *fred) {\n" " if (fred == NULL) { }\n" " fred->x();\n" "}"; check(code); // inconclusive ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'fred==NULL' is redundant or there is possible null pointer dereference: fred.\n", errout.str()); } check("void f(char *s) {\n" // #3358 " if (s==0);\n" " strcpy(a, s?b:c);\n" "}"); ASSERT_EQUALS("", errout.str()); // sizeof check("void f(struct fred_t *fred) {\n" " if (!fred)\n" " int sz = sizeof(fred->x);\n" "}", true); ASSERT_EQUALS("", errout.str()); // check in macro check("void f(int *x) {\n" " $if (!x) {}\n" " *x = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // return ?: check("int f(ABC *p) {\n" // FP : return ?: " if (!p) {}\n" " return p ? p->x : 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(ABC *p) {\n" // no fn " if (!p) {}\n" " return q ? p->x : 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", "", errout.str()); check("int f(ABC *p) {\n" // FP : return && " if (!p) {}\n" " return p && p->x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int *p) {\n" " if (x || !p) {}\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // sizeof check("void f() {\n" " int *pointer = NULL;\n" " pointer = func(sizeof pointer[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Test CheckNullPointer::nullConstantDereference void nullConstantDereference() { check("int f() {\n" " int* p = 0;\n" " return p[4];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: p\n", errout.str()); check("void f() {\n" " typeof(*NULL) y;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int * f() {\n" " return NULL;\n" "}\n" "int main() {\n" " return *f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f()\n", errout.str()); } void gcc_statement_expression() { // Ticket #2621 check("void f(struct ABC *abc) {\n" " ({ if (abc) dbg(); })\n" "}"); ASSERT_EQUALS("", errout.str()); } void snprintf_with_zero_size() { // Ticket #2840 check("void f() {\n" " int bytes = snprintf(0, 0, \"%u\", 1);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void snprintf_with_non_zero_size() { // Ticket #2840 check("void f() {\n" " int bytes = snprintf(0, 10, \"%u\", 1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); } void printf_with_invalid_va_argument() { check("void f() {\n" " printf(\"%s\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"%s\", s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char* s = 0;\n" " printf(\"%s\", s);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: s\n", errout.str()); check("void f() {\n" " char *s = 0;\n" " printf(\"%s\", s == 0 ? a : s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " printf(\"%u%s\", 0, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"%u%s\", 0, s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char* s = 0;\n" " printf(\"%u%s\", 123, s);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: s\n", errout.str()); check("void f() {\n" " printf(\"%%%s%%\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"text: %s, %s\", s, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f() {\n" " char* s = \"blabla\";\n" " printf(\"%s\", s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char* s) {\n" " printf(\"text: %m%s, %s\", s, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"text: %*s, %s\", s, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); // Ticket #3364 check("void f() {\n" " printf(\"%-*.*s\", s, 0);\n" " sprintf(\"%*\", s);\n" "}"); ASSERT_EQUALS("", errout.str()); } void scanf_with_invalid_va_argument() { check("void f(char* s) {\n" " sscanf(s, \"%s\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f() {\n" " scanf(\"%d\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* foo) {\n" " char location[200];\n" " int width, height;\n" " sscanf(imgInfo, \"%s %d %d\", location, &width, &height);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3207 check("void f(char *dummy) {\n" " int iVal;\n" " sscanf(dummy, \"%d%c\", &iVal);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3211 check("void f(char *dummy) {\n" " int* iVal = 0;\n" " sscanf(dummy, \"%d\", iVal);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: iVal\n", errout.str()); check("void f(char *dummy) {\n" " int* iVal;\n" " sscanf(dummy, \"%d\", foo(iVal));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *dummy) {\n" " int* iVal = 0;\n" " sscanf(dummy, \"%d%d\", foo(iVal), iVal);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char* dummy) {\n" " sscanf(dummy, \"%*d%u\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); } void nullpointer_in_return() { // extracttests.start: int maybe(); int *g(); check("int foo() {\n" " int* iVal = 0;\n" " if(maybe()) iVal = g();\n" " return iVal[0];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: iVal\n", errout.str()); check("int foo(int* iVal) {\n" " return iVal[0];\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer_in_typeid() { // Should throw std::bad_typeid check("struct PolymorphicA { virtual ~A() {} };\n" "bool foo() {\n" " PolymorphicA* a = 0;\n" " return typeid(*a) == typeid(*a);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct NonPolymorphicA { ~A() {} };\n" "bool foo() {\n" " NonPolymorphicA* a = 0;\n" " return typeid(*a) == typeid(*a);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("bool foo() {\n" " char* c = 0;\n" " return typeid(*c) == typeid(*c);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer_in_for_loop() { // Ticket #3278 check("void f(int* ptr, int cnt){\n" " if (!ptr)\n" " cnt = 0;\n" " for (int i = 0; i < cnt; ++i)\n" " *ptr++ = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointerDelete() { check("void f() {\n" " K *k = getK();\n" " if (k)\n" " k->doStuff();\n" " delete k;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " K *k = getK();\n" " if (k)\n" " k[0] = ptr;\n" " delete [] k;\n" " k = new K[10];\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointerSubFunction() { check("void g(int* x) { *x; }\n" "void f(int* x) {\n" " if (x)\n" " g(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointerExit() { check("void f() {\n" " K *k = getK();\n" " if (!k)\n" " exit(1);\n" " k->f();\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointerStdString() { check("void f(std::string s1) {\n" " void* p = 0;\n" " s1 = 0;\n" " s1 = '\\0';\n" " std::string s2 = 0;\n" " std::string s2 = '\\0';\n" " std::string s3(0);\n" " foo(std::string(0));\n" " s1 = p;\n" " std::string s4 = p;\n" " std::string s5(p);\n" " foo(std::string(p));\n" "}", true); ASSERT_EQUALS("[test.cpp:9]: (error) Null pointer dereference: p\n" "[test.cpp:10]: (error) Null pointer dereference: p\n" "[test.cpp:11]: (error) Null pointer dereference: p\n" "[test.cpp:12]: (warning, inconclusive) Possible null pointer dereference: p\n" "[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" "[test.cpp:7]: (error) Null pointer dereference\n" "[test.cpp:8]: (error) Null pointer dereference\n" , errout.str()); check("void f(std::string s1) {\n" " s1 = nullptr;\n" " std::string s2 = nullptr;\n" " std::string s3(nullptr);\n" " foo(std::string(nullptr));\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n" "[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:4]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" , errout.str()); check("void f(std::string s1) {\n" " s1 = NULL;\n" " std::string s2 = NULL;\n" " std::string s3(NULL);\n" " foo(std::string(NULL));\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n" "[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:4]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" , errout.str()); check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n" " void* p = 0;\n" " if (x) { return; }\n" " foo(s1 == p);\n" " foo(s2 == p);\n" " foo(s3 == p);\n" " foo(p == s1);\n" " foo(p == s2);\n" " foo(p == s3);\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n" "[test.cpp:5]: (error) Null pointer dereference: p\n" "[test.cpp:7]: (error) Null pointer dereference: p\n" "[test.cpp:8]: (error) Null pointer dereference: p\n", errout.str()); check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n" " void* p = 0;\n" " if (x) { return; }\n" " foo(0 == s1.size());\n" " foo(0 == s2.size());\n" " foo(0 == s3->size());\n" " foo(s1.size() == 0);\n" " foo(s2.size() == 0);\n" " foo(s3->size() == 0);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(std::string s1, const std::string& s2) {\n" " if (x) { return; }\n" " foo(0 == s1[0]);\n" " foo(0 == s2[0]);\n" " foo(s1[0] == 0);\n" " foo(s2[0] == 0);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(std::string s1, const std::string& s2) {\n" " if (x) { return; }\n" " foo(s1 == '\\0');\n" " foo(s2 == '\\0');\n" " foo('\\0' == s1);\n" " foo('\\0' == s2);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("class Bar {\n" " std::string s;\n" " Bar() : s(0) {}\n" "};\n" "class Foo {\n" " std::string s;\n" " Foo();\n" "};\n" "Foo::Foo() : s(0) {}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:9]: (error) Null pointer dereference\n", errout.str()); check("void f() {\n" " std::string s = 0 == x ? \"a\" : \"b\";\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const std::string s = g();\n" " ASSERT_MESSAGE(\"Error on s\", 0 == s.compare(\"Some text\"));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int i, std::string s);\n" "void bar() {\n" " foo(0, \"\");\n" " foo(0, 0);\n" " foo(var, 0);\n" " foo(var, NULL);\n" " foo(var, nullptr);\n" " foo(0, var);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" "[test.cpp:6]: (error) Null pointer dereference\n" "[test.cpp:7]: (error) Null pointer dereference\n", errout.str()); check("std::string f() {\n" // #9827 " char* p = NULL;\n" " int r = g(p);\n" " if (!r)\n" " return \"\";\n" " std::string s(p);\n" " return s;\n" "}\n", /*inconclusive*/ true); ASSERT_EQUALS("", errout.str()); } void nullpointerStdStream() { check("void f(std::ifstream& is) {\n" " char* p = 0;\n" " is >> p;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: p\n", "", errout.str()); check("void f(const std::ostringstream& oss, char* q) {\n" " char const* p = 0;\n" // Simplification makes detection of bug difficult " oss << p;\n" " oss << foo << p;\n" " if(q == 0)\n" " oss << foo << q;\n" "}", false); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: p\n" "[test.cpp:4]: (error) Null pointer dereference: p\n" "[test.cpp:5] -> [test.cpp:6]: (warning) Either the condition 'q==0' is redundant or there is possible null pointer dereference: q.\n", errout.str()); check("void f(const char* p) {\n" " if(p == 0) {\n" " std::cout << p;\n" " std::cerr << p;\n" " std::cin >> p;\n" " std::cout << abc << p;\n" " }\n" "}", false); TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", "[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void f() {\n" " void* p1 = 0;\n" " std::cout << p1;\n" // No char* " char* p2 = 0;\n" " std::cin >> (int)p;\n" // result casted " std::cout << (int)p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(const std::string& str) {\n" " long long ret = 0;\n" " std::istringstream istr(str);\n" " istr >> std::hex >> ret;\n" // Read integer " return ret;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(int* i) {\n" " if(i) return;\n" " std::cout << i;\n" // Its no char* (#4240) "}", true); ASSERT_EQUALS("", errout.str()); // #5811 false positive: (error) Null pointer dereference check("using namespace std;\n" "std::string itoip(int ip) {\n" " stringstream out;\n" " out << ((ip >> 0) & 0xFF);\n" " return out.str();\n" "}n", true); ASSERT_EQUALS("", errout.str()); // avoid regression from first fix attempt for #5811... check("void deserialize(const std::string &data) {\n" "std::istringstream iss(data);\n" "unsigned int len = 0;\n" "if (!(iss >> len))\n" " return;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointerSmartPointer() { // extracttests.start: void dostuff(int); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " if (p) {}\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " p = nullptr;\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::unique_ptr p) {\n" " if (p) {}\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::unique_ptr p) {\n" " p = nullptr;\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f() {\n" " std::shared_ptr p;\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " p.reset();\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " Fred * pp = nullptr;\n" " p.reset(pp);\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(Fred& f) {\n" " std::shared_ptr p;\n" " p.reset(&f);\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " p.reset();\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f() {\n" " std::shared_ptr p(nullptr);\n" " dostuff(p->x);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct A {};\n" "void f(int n) {\n" " std::unique_ptr p;\n" " p.reset(new const A*[n]);\n" "}"); ASSERT_EQUALS("", errout.str()); // #9216 check("struct A {\n" " void reset();\n" " void f();\n" "};\n" "void g(std::unique_ptr var) {\n" " var->reset();\n" " var->f();\n" "}"); ASSERT_EQUALS("", errout.str()); // #9439 check("char* g();\n" "char* f() {\n" " std::unique_ptr x(g());\n" " if( x ) {}\n" " return x.release();\n" "}\n", true); ASSERT_EQUALS("", errout.str()); // #9496 check("std::shared_ptr f() {\n" " return std::shared_ptr(nullptr);\n" "}\n" "void g() {\n" " int a = *f();\n" "}\n", true); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f()\n", errout.str()); } void functioncall() { // #3443 - function calls // dereference pointer and then check if it's null { // function not seen check("void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // function seen (taking pointer parameter) check("void foo(int *p) { }\n" "\n" "void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // function seen (taking reference parameter) check("void foo(int *&p) { }\n" "\n" "void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}", true); ASSERT_EQUALS("", errout.str()); // function implementation not seen check("void foo(int *p);\n" "\n" "void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // inconclusive check("void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}", true); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:2]: (warning, inconclusive) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); } // dereference struct pointer and then check if it's null { // function not seen check("void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // function seen (taking pointer parameter) check("void foo(struct ABC *abc) { }\n" "\n" "void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // function implementation not seen check("void foo(struct ABC *abc);\n" "\n" "void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // inconclusive check("void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}", true); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:2]: (warning, inconclusive) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); } } void functioncalllibrary() { Settings settings1; Tokenizer tokenizer(&settings1,this); std::istringstream code("void f() { int a,b,c; x(a,b,c); }"); ASSERT_EQUALS(true, tokenizer.tokenize(code, "test.c")); const Token *xtok = Token::findsimplematch(tokenizer.tokens(), "x"); // nothing bad.. { Library library; Library::ArgumentChecks arg; library.functions["x"].argumentChecks[1] = arg; library.functions["x"].argumentChecks[2] = arg; library.functions["x"].argumentChecks[3] = arg; std::list null; CheckNullPointer::parseFunctionCall(*xtok, null, &library); ASSERT_EQUALS(0U, null.size()); } // for 1st parameter null pointer is not ok.. { Library library; Library::ArgumentChecks arg; library.functions["x"].argumentChecks[1] = arg; library.functions["x"].argumentChecks[2] = arg; library.functions["x"].argumentChecks[3] = arg; library.functions["x"].argumentChecks[1].notnull = true; std::list null; CheckNullPointer::parseFunctionCall(*xtok, null, &library); ASSERT_EQUALS(1U, null.size()); ASSERT_EQUALS("a", null.front()->str()); } } void functioncallDefaultArguments() { check("void f(int *p = 0) {\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " if (!p)\n" " return;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char a, int *p = 0) {\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " printf(\"p = %d\", *p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " printf(\"p[1] = %d\", p[1]);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " buf[p] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p != 0 && bar())\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p != 0)\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " int y;\n" " if (p == 0)\n" " p = &y;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int *p = 0) {\n" " if (a != 0)\n" " *p = 0;\n" "}", true); ASSERT_EQUALS( "[test.cpp:3]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " p = a;\n" " *p = 0;\n" // <- don't simplify and verify "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " p += a;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int *p = 0) {\n" " if (p == 0) {\n" " return 0;\n" " }\n" " return *p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0; "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); // Check the first branch of ternary check("void f(char *p = 0) {\n" " std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0; "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " std::cout << (p ? *p : 0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " std::cout << p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " std::cout << (p && p[0] ? *p : 42);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void isEmpty(int *p = 0) {\n" " return p && *p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int *p = 0) {\n" " return !p || *p;\n" "}"); ASSERT_EQUALS("", errout.str()); // bar may initialize p but be can't know for sure without knowing // if p is passed in by reference and is modified by bar() check("void f(int *p = 0) {\n" " bar(p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " printf(\"%p\", p);\n" " *p = 0;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); // The init() function may or may not initialize p, but since the address // of p is passed in, it's a good bet that p may be modified and // so we should not report an error. check("void f(int *p = 0) {\n" " init(&p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void init(int* &g);\n" "void f(int *p = 0) {\n" " init(p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p == 0) {\n" " init(&p);\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p == 0) {\n" " throw SomeException;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x, int *p = 0) {\n" " int var1 = x ? *p : 5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); } void nullpointer_internal_error() { // ticket #5080 check("struct A { unsigned int size; };\n" "struct B { struct A *a; };\n" "void f(struct B *b) {\n" " unsigned int j;\n" " for (j = 0; j < b[0].a->size; ++j) {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void ticket6505() { check("void foo(MythSocket *socket) {\n" " bool do_write=0;\n" " if (socket) {\n" " do_write=something();\n" " }\n" " if (do_write) {\n" " socket->func();\n" " }\n" "}\n" "void bar() {\n" " foo(0);\n" "}\n", true, "test.c"); ASSERT_EQUALS("", errout.str()); } void subtract() { check("void foo(char *s) {\n" " char *p = s - 20;\n" "}\n" "void bar() { foo(0); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " char *p = s - 20;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout.str()); check("void foo(char *s) {\n" " s -= 20;\n" "}\n" "void bar() { foo(0); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " s -= 20;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout.str()); check("int* f8() { int *x = NULL; return --x; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); check("int* f9() { int *x = NULL; return x--; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); } void addNull() { check("void foo(char *s) {\n" " char * p = s + 20;\n" "}\n" "void bar() { foo(0); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " char * p = s + 20;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " char * p = 20 + s;\n" "}\n" "void bar() { foo(0); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " char * p = 20 + s;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " s += 20;\n" "}\n" "void bar() { foo(0); }"); ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " s += 20;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout.str()); check("int* f7() { int *x = NULL; return ++x; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("int* f10() { int *x = NULL; return x++; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("class foo {};\n" "const char* get() const { return 0; }\n" "void f(foo x) { if (get()) x += get(); }"); ASSERT_EQUALS("", errout.str()); } void isPointerDeRefFunctionDecl() { check("const char** get() { return 0; }"); ASSERT_EQUALS("", errout.str()); } #define ctu(code) ctu_(code, __FILE__, __LINE__) void ctu_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CTU::FileInfo *ctu = CTU::getFileInfo(&tokenizer); // Check code.. std::list fileInfo; CheckNullPointer checkNullPointer(&tokenizer, &settings, this); fileInfo.push_back(checkNullPointer.getFileInfo(&tokenizer, &settings)); checkNullPointer.analyseWholeProgram(ctu, fileInfo, settings, *this); while (!fileInfo.empty()) { delete fileInfo.back(); fileInfo.pop_back(); } delete ctu; } void ctuTest() { setMultiline(); ctu("void f(int *fp) {\n" " a = *fp;\n" "}\n" "int main() {\n" " int *p = 0;\n" " f(p);\n" "}"); ASSERT_EQUALS("test.cpp:2:error:Null pointer dereference: fp\n" "test.cpp:5:note:Assignment 'p=0', assigned value is 0\n" "test.cpp:6:note:Calling function f, 1st argument is null\n" "test.cpp:2:note:Dereferencing argument fp that is null\n", errout.str()); ctu("void use(int *p) { a = *p + 3; }\n" "void call(int x, int *p) { x++; use(p); }\n" "int main() {\n" " call(4,0);\n" "}"); ASSERT_EQUALS("test.cpp:1:error:Null pointer dereference: p\n" "test.cpp:4:note:Calling function call, 2nd argument is null\n" "test.cpp:2:note:Calling function use, 1st argument is null\n" "test.cpp:1:note:Dereferencing argument p that is null\n", errout.str()); ctu("void dostuff(int *x, int *y) {\n" " if (!var)\n" " return -1;\n" // <- early return " *x = *y;\n" "}\n" "\n" "void f() {\n" " dostuff(a, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); ctu("void dostuff(int *x, int *y) {\n" " if (cond)\n" " *y = -1;\n" // <- conditionally written " *x = *y;\n" "}\n" "\n" "void f() {\n" " dostuff(a, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // else ctu("void dostuff(int mask, int *p) {\n" " if (mask == 13) ;\n" " else *p = 45;\n" "}\n" "\n" "void f() {\n" " dostuff(0, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // ?, &&, || ctu("void dostuff(int mask, int *p) {\n" " x = (mask & 1) ? *p : 0;\n" "}\n" "\n" "void f() {\n" " dostuff(0, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); ctu("void g(int* x) { *x; }\n" "void f(int* x) {\n" " if (x)\n" " g(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestNullPointer) cppcheck-2.7/test/testoptions.cpp000066400000000000000000000101421417746362400172340ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2022 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "config.h" #include "options.h" #include "testsuite.h" #include #include class TestOptions : public TestFixture { public: TestOptions() : TestFixture("TestOptions") {} private: void run() OVERRIDE { TEST_CASE(which_test); TEST_CASE(which_test_method); TEST_CASE(no_test_method); TEST_CASE(not_quiet); TEST_CASE(quiet); TEST_CASE(not_help); TEST_CASE(help); TEST_CASE(help_long); TEST_CASE(multiple_testcases); TEST_CASE(multiple_testcases_ignore_duplicates); TEST_CASE(invalid_switches); } void which_test() const { const char* argv[] = {"./test_runner", "TestClass"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT(std::set {"TestClass"} == args.which_test()); } void which_test_method() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT(std::set {"TestClass::TestMethod"} == args.which_test()); } void no_test_method() const { const char* argv[] = {"./test_runner"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT(std::set {""} == args.which_test()); } void not_quiet() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-v"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(false, args.quiet()); } void quiet() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-q"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(true, args.quiet()); } void not_help() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-v"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(false, args.help()); } void help() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-h"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(true, args.help()); } void help_long() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "--help"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(true, args.help()); } void multiple_testcases() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "TestClass::AnotherTestMethod"}; options args(sizeof argv / sizeof argv[0], argv); std::set expected {"TestClass::TestMethod", "TestClass::AnotherTestMethod"}; ASSERT(expected == args.which_test()); } void multiple_testcases_ignore_duplicates() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "TestClass"}; options args(sizeof argv / sizeof argv[0], argv); std::set expected {"TestClass"}; ASSERT(expected == args.which_test()); } void invalid_switches() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-a", "-v", "-q"}; options args(sizeof argv / sizeof argv[0], argv); std::set expected {"TestClass::TestMethod"}; ASSERT(expected == args.which_test()); ASSERT_EQUALS(true, args.quiet()); } }; REGISTER_TEST(TestOptions) cppcheck-2.7/test/testother.cpp000066400000000000000000013101651417746362400166730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkother.h" #include "config.h" #include "errortypes.h" #include "library.h" #include "platform.h" #include "preprocessor.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include #include #include #include #include class TestOther : public TestFixture { public: TestOther() : TestFixture("TestOther") {} private: Settings _settings; void run() OVERRIDE { LOAD_LIB_2(_settings.library, "std.cfg"); TEST_CASE(emptyBrackets); TEST_CASE(zeroDiv1); TEST_CASE(zeroDiv2); TEST_CASE(zeroDiv3); TEST_CASE(zeroDiv4); TEST_CASE(zeroDiv5); TEST_CASE(zeroDiv6); TEST_CASE(zeroDiv7); // #4930 TEST_CASE(zeroDiv8); TEST_CASE(zeroDiv9); TEST_CASE(zeroDiv10); TEST_CASE(zeroDiv11); TEST_CASE(zeroDiv12); TEST_CASE(zeroDiv13); TEST_CASE(zeroDivCond); // division by zero / useless condition TEST_CASE(nanInArithmeticExpression); TEST_CASE(varScope1); TEST_CASE(varScope2); TEST_CASE(varScope3); TEST_CASE(varScope4); TEST_CASE(varScope5); TEST_CASE(varScope6); TEST_CASE(varScope7); TEST_CASE(varScope8); TEST_CASE(varScope9); // classes may have extra side-effects TEST_CASE(varScope10); // Undefined macro FOR TEST_CASE(varScope11); // #2475 - struct initialization is not inner scope TEST_CASE(varScope12); TEST_CASE(varScope13); // variable usage in inner loop TEST_CASE(varScope14); TEST_CASE(varScope15); // #4573 if-else-if TEST_CASE(varScope16); TEST_CASE(varScope17); TEST_CASE(varScope18); TEST_CASE(varScope20); // Ticket #5103 TEST_CASE(varScope21); // Ticket #5382 TEST_CASE(varScope22); // Ticket #5684 TEST_CASE(varScope23); // Ticket #6154 TEST_CASE(varScope24); // pointer / reference TEST_CASE(varScope25); // time_t TEST_CASE(varScope26); // range for loop, map TEST_CASE(varScope27); // #7733 - #if TEST_CASE(oldStylePointerCast); TEST_CASE(invalidPointerCast); TEST_CASE(passedByValue); TEST_CASE(passedByValue_nonConst); TEST_CASE(passedByValue_externC); TEST_CASE(constVariable); TEST_CASE(constParameterCallback); TEST_CASE(constPointer); TEST_CASE(switchRedundantAssignmentTest); TEST_CASE(switchRedundantOperationTest); TEST_CASE(switchRedundantBitwiseOperationTest); TEST_CASE(unreachableCode); TEST_CASE(suspiciousCase); TEST_CASE(suspiciousEqualityComparison); TEST_CASE(selfAssignment); TEST_CASE(trac1132); TEST_CASE(testMisusedScopeObjectDoesNotPickFunction1); TEST_CASE(testMisusedScopeObjectDoesNotPickFunction2); TEST_CASE(testMisusedScopeObjectPicksClass); TEST_CASE(testMisusedScopeObjectPicksStruct); TEST_CASE(testMisusedScopeObjectDoesNotPickIf); TEST_CASE(testMisusedScopeObjectDoesNotPickConstructorDeclaration); TEST_CASE(testMisusedScopeObjectDoesNotPickFunctor); TEST_CASE(testMisusedScopeObjectDoesNotPickLocalClassConstructors); TEST_CASE(testMisusedScopeObjectDoesNotPickUsedObject); TEST_CASE(testMisusedScopeObjectDoesNotPickPureC); TEST_CASE(testMisusedScopeObjectDoesNotPickNestedClass); TEST_CASE(testMisusedScopeObjectInConstructor); TEST_CASE(testMisusedScopeObjectNoCodeAfter); TEST_CASE(trac2071); TEST_CASE(trac2084); TEST_CASE(trac3693); TEST_CASE(clarifyCalculation); TEST_CASE(clarifyStatement); TEST_CASE(duplicateBranch); TEST_CASE(duplicateBranch1); // tests extracted by http://www.viva64.com/en/b/0149/ ( Comparison between PVS-Studio and cppcheck ): Errors detected in Quake 3: Arena by PVS-Studio: Fragment 2 TEST_CASE(duplicateBranch2); // empty macro TEST_CASE(duplicateBranch3); TEST_CASE(duplicateBranch4); TEST_CASE(duplicateExpression1); TEST_CASE(duplicateExpression2); // ticket #2730 TEST_CASE(duplicateExpression3); // ticket #3317 TEST_CASE(duplicateExpression4); // ticket #3354 (++) TEST_CASE(duplicateExpression5); // ticket #3749 (macros with same values) TEST_CASE(duplicateExpression6); // ticket #4639 TEST_CASE(duplicateExpression7); TEST_CASE(duplicateExpression8); TEST_CASE(duplicateExpression9); // #9320 TEST_CASE(duplicateExpression10); // #9485 TEST_CASE(duplicateExpression11); // #8916 (function call) TEST_CASE(duplicateExpression12); // #10026 TEST_CASE(duplicateExpression13); // #7899 TEST_CASE(duplicateExpressionLoop); TEST_CASE(duplicateValueTernary); TEST_CASE(duplicateExpressionTernary); // #6391 TEST_CASE(duplicateExpressionTemplate); // #6930 TEST_CASE(duplicateExpressionCompareWithZero); TEST_CASE(oppositeExpression); TEST_CASE(duplicateVarExpression); TEST_CASE(duplicateVarExpressionUnique); TEST_CASE(duplicateVarExpressionAssign); TEST_CASE(duplicateVarExpressionCrash); TEST_CASE(multiConditionSameExpression); TEST_CASE(checkSignOfUnsignedVariable); TEST_CASE(checkSignOfPointer); TEST_CASE(checkSuspiciousSemicolon1); TEST_CASE(checkSuspiciousSemicolon2); TEST_CASE(checkSuspiciousSemicolon3); TEST_CASE(checkSuspiciousComparison); TEST_CASE(checkInvalidFree); TEST_CASE(checkRedundantCopy); TEST_CASE(checkNegativeShift); TEST_CASE(incompleteArrayFill); TEST_CASE(redundantVarAssignment); TEST_CASE(redundantVarAssignment_trivial); TEST_CASE(redundantVarAssignment_struct); TEST_CASE(redundantVarAssignment_7133); TEST_CASE(redundantVarAssignment_stackoverflow); TEST_CASE(redundantVarAssignment_lambda); TEST_CASE(redundantVarAssignment_loop); TEST_CASE(redundantVarAssignment_after_switch); TEST_CASE(redundantVarAssignment_pointer); TEST_CASE(redundantVarAssignment_pointer_parameter); TEST_CASE(redundantVarAssignment_array); TEST_CASE(redundantVarAssignment_switch_break); TEST_CASE(redundantInitialization); TEST_CASE(redundantMemWrite); TEST_CASE(varFuncNullUB); TEST_CASE(checkPipeParameterSize); // ticket #3521 TEST_CASE(checkCastIntToCharAndBack); // ticket #160 TEST_CASE(checkCommaSeparatedReturn); TEST_CASE(checkPassByReference); TEST_CASE(checkComparisonFunctionIsAlwaysTrueOrFalse); TEST_CASE(integerOverflow); // #5895 TEST_CASE(redundantPointerOp); TEST_CASE(test_isSameExpression); TEST_CASE(raceAfterInterlockedDecrement); TEST_CASE(testUnusedLabel); TEST_CASE(testEvaluationOrder); TEST_CASE(testEvaluationOrderSelfAssignment); TEST_CASE(testEvaluationOrderMacro); TEST_CASE(testEvaluationOrderSequencePointsFunctionCall); TEST_CASE(testEvaluationOrderSequencePointsComma); TEST_CASE(testEvaluationOrderSizeof); TEST_CASE(testUnsignedLessThanZero); TEST_CASE(doubleMove1); TEST_CASE(doubleMoveMemberInitialization1); TEST_CASE(doubleMoveMemberInitialization2); TEST_CASE(moveAndAssign1); TEST_CASE(moveAndAssign2); TEST_CASE(moveAssignMoveAssign); TEST_CASE(moveAndReset1); TEST_CASE(moveAndReset2); TEST_CASE(moveResetMoveReset); TEST_CASE(moveAndFunctionParameter); TEST_CASE(moveAndFunctionParameterReference); TEST_CASE(moveAndFunctionParameterConstReference); TEST_CASE(moveAndFunctionParameterUnknown); TEST_CASE(moveAndReturn); TEST_CASE(moveAndClear); TEST_CASE(movedPointer); TEST_CASE(moveAndAddressOf); TEST_CASE(partiallyMoved); TEST_CASE(moveAndLambda); TEST_CASE(forwardAndUsed); TEST_CASE(funcArgNamesDifferent); TEST_CASE(funcArgOrderDifferent); TEST_CASE(cpp11FunctionArgInit); // #7846 - "void foo(int declaration = {}) {" TEST_CASE(shadowVariables); TEST_CASE(knownArgument); TEST_CASE(knownArgumentHiddenVariableExpression); TEST_CASE(knownArgumentTernaryOperator); TEST_CASE(checkComparePointers); TEST_CASE(unusedVariableValueTemplate); // #8994 TEST_CASE(moduloOfOne); TEST_CASE(sameExpressionPointers); TEST_CASE(checkOverlappingWrite); TEST_CASE(constVariableArrayMember); // #10371 } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool runSimpleChecks=true, bool verbose=false, Settings* settings = nullptr) { // Clear the error buffer.. errout.str(""); if (!settings) { settings = &_settings; } settings->severity.enable(Severity::style); settings->severity.enable(Severity::warning); settings->severity.enable(Severity::portability); settings->severity.enable(Severity::performance); settings->standards.c = Standards::CLatest; settings->standards.cpp = Standards::CPPLatest; settings->certainty.setEnabled(Certainty::inconclusive, inconclusive); settings->certainty.setEnabled(Certainty::experimental, experimental); settings->verbose = verbose; // Tokenize.. Tokenizer tokenizer(settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, filename ? filename : "test.cpp"), file, line); // Check.. CheckOther checkOther(&tokenizer, settings, this); checkOther.runChecks(&tokenizer, settings, this); (void)runSimpleChecks; // TODO Remove this } void check_(const char* file, int line, const char code[], Settings *s) { check_(file, line, code, "test.cpp", false, true, true, false, s); } void checkP(const char code[], const char *filename = "test.cpp") { // Clear the error buffer.. errout.str(""); Settings* settings = &_settings; settings->severity.enable(Severity::style); settings->severity.enable(Severity::warning); settings->severity.enable(Severity::portability); settings->severity.enable(Severity::performance); settings->standards.c = Standards::CLatest; settings->standards.cpp = Standards::CPPLatest; settings->certainty.enable(Certainty::inconclusive); settings->certainty.disable(Certainty::experimental); // Raw tokens.. std::vector files(1, filename); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); Preprocessor preprocessor(*settings, nullptr); preprocessor.setDirectives(tokens1); // Tokenizer.. Tokenizer tokenizer(settings, this); tokenizer.createTokens(std::move(tokens2)); tokenizer.simplifyTokens1(""); tokenizer.setPreprocessor(&preprocessor); // Check.. CheckOther checkOther(&tokenizer, settings, this); checkOther.runChecks(&tokenizer, settings, this); } void checkposix(const char code[]) { static Settings settings; settings.severity.enable(Severity::warning); settings.libraries.emplace_back("posix"); check(code, nullptr, // filename false, // experimental false, // inconclusive true, // runSimpleChecks false, // verbose &settings); } void checkInterlockedDecrement(const char code[]) { static Settings settings; settings.platformType = Settings::Win32A; check(code, nullptr, false, false, true, false, &settings); } void emptyBrackets() { check("{\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv1() { // floating point division by zero => no error check("void foo() {\n" " cout << 1. / 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " cout << 42 / (double)0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " cout << 42 / (float)0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " cout << 42 / (int)0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n", errout.str()); } void zeroDiv2() { check("void foo()\n" "{\n" " int sum = 0;\n" " for(int i = 0; i < n; i ++)\n" " {\n" " sum += i;\n" " }\n" " cout< do not warn check("void f() {\n" " int a = x/2*3/0;\n" " int b = y/2*3%0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " int a = x/2*3/0;\n" " int b = y/2*3%0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n" "[test.cpp:3]: (error) Division by zero.\n", errout.str()); } void zeroDiv8() { // #5584 - FP when function is unknown check("void f() {\n" " int a = 0;\n" " do_something(a);\n" " return 4 / a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Division by zero.\n", errout.str()); } void zeroDiv9() { // #6403 FP zerodiv - inside protecting if-clause check("void foo() {\n" " double fStepHelp = 0;\n" " if( (rOuterValue >>= fStepHelp) ) {\n" " if( fStepHelp != 0.0) {\n" " double fStepMain = 1;\n" " sal_Int32 nIntervalCount = static_cast< sal_Int32 >(fStepMain / fStepHelp);\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv10() { // #5402 false positive: (error) Division by zero -- with boost::format check("int main() {\n" " std::cout\n" " << boost::format(\" %d :: %s <> %s\") % 0 % \"a\" % \"b\"\n" " << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv11() { check("void f(int a) {\n" " int res = (a+2)/0;\n" " int res = (a*2)/0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n" "[test.cpp:3]: (error) Division by zero.\n", errout.str()); check("void f() {\n" " int res = (a+2)/0;\n" " int res = (a*2)/0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv12() { // #8141 check("intmax_t f() {\n" " return 1 / imaxabs(0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n", errout.str()); } void zeroDiv13() { // #7324 check("int f () {\n" " int dividend = 10;\n" " int divisor = 1;\n" " dividend = dividend / (--divisor);\n" " return dividend;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Division by zero.\n", errout.str()); } void zeroDivCond() { check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x > 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>0' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x >= 1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>=1' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(int x) {\n" " int y = 17 / x;\n" " if (x == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x==0' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 2.\n", errout.str()); // function call check("void f1(int x, int y) { c=x/y; }\n" "void f2(unsigned int y) {\n" " f1(123,y);\n" " if (y>0){}\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:1]: (warning) Either the condition 'y>0' is redundant or there is division by zero at line 1.\n", errout.str()); // avoid false positives when variable is changed after division check("void f() {\n" " unsigned int x = do_something();\n" " int y = 17 / x;\n" " x = some+calculation;\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); { // function is called that might modify global variable check("void do_something();\n" "int x;\n" "void f() {\n" " int y = 17 / x;\n" " do_something();\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // function is called. but don't care, variable is local check("void do_something();\n" "void f() {\n" " int x = some + calculation;\n" " int y = 17 / x;\n" " do_something();\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 4.\n", errout.str()); } check("void do_something(int value);\n" "void f(int x) {\n" " int y = 17 / x;\n" " do_something(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int x;\n" "void f() {\n" " int y = 17 / x;\n" " while (y || x == 0) { x--; }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket 5033 segmentation fault (valid code) in CheckOther::checkZeroDivisionOrUselessCondition check("void f() {\n" "double* p1= new double[1];\n" "double* p2= new double[1];\n" "double* p3= new double[1];\n" "double* pp[3] = {p1,p2,p3};\n" "}"); ASSERT_EQUALS("", errout.str()); // #5105 - FP check("int f(int a, int b) {\n" " int r = a / b;\n" " if (func(b)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Unknown types for b and c --> do not warn check("int f(int d) {\n" " int r = (a?b:c) / d;\n" " if (d == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a) {\n" " int r = a ? 1 / a : 0;\n" " if (a == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a) {\n" " int r = (a == 0) ? 0 : 1 / a;\n" " if (a == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int g();\n" "void f(int b) {\n" " int x = g();\n" " if (x == 0) {}\n" " else if (x > 0) {}\n" " else\n" " a = b / -x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int x;\n" "};\n" "int f(A* a) {\n" " if (a->x == 0) \n" " a->x = 1;\n" " return 1/a->x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10049 check("int f(int argc) {\n" " int quotient, remainder;\n" " remainder = argc % 2;\n" " argc = 2;\n" " quotient = argc;\n" " if (quotient != 0) \n" " return quotient;\n" " return remainder;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nanInArithmeticExpression() { check("void f()\n" "{\n" " double x = 3.0 / 0.0 + 1.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 3.0 / 0.0 - 1.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 1.0 + 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 1.0 - 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope1() { check("unsigned short foo()\n" "{\n" " test_client CClient;\n" " try\n" " {\n" " if (CClient.Open())\n" " {\n" " return 0;\n" " }\n" " }\n" " catch (...)\n" " {\n" " return 2;\n" " }\n" "\n" " try\n" " {\n" " CClient.Close();\n" " }\n" " catch (...)\n" " {\n" " return 2;\n" " }\n" "\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope2() { check("int foo()\n" "{\n" " Error e;\n" " e.SetValue(12);\n" " throw e;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope3() { check("void foo()\n" "{\n" " int i;\n" " int *p = 0;\n" " if (abc)\n" " {\n" " p = &i;\n" " }\n" " *p = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope4() { check("void foo()\n" "{\n" " int i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope5() { check("void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'i' can be reduced.\n", errout.str()); check("void f(int x) {\n" " const unsigned char i = 0;\n" " if (x) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {b()}\n" " else {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'i' can be reduced.\n", errout.str()); } void varScope6() { check("void f(int x)\n" "{\n" " int i = x;\n" " if (a) {\n" " x++;\n" " }\n" " if (b) {\n" " c(i);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #5398 " bool success = false;\n" " int notReducable(someClass.getX(&success));\n" " if (success) {\n" " foo(notReducable);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Test &test) {\n" " int& x = test.getData();\n" " if (test.process())\n" " x = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" "int foo = 0;\n" "std::vector vec(10);\n" "BOOST_FOREACH(int& i, vec)\n" "{\n" " foo += 1;\n" " if(foo == 10)\n" " {\n" " return 0;\n" " }\n" "}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int &x)\n" "{\n" " int n = 1;\n" " do\n" " {\n" " ++n;\n" " ++x;\n" " } while (x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope7() { check("void f(int x)\n" "{\n" " int y = 0;\n" " b(y);\n" " if (x) {\n" " y++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope8() { check("void test() {\n" " float edgeResistance=1;\n" " std::vector edges;\n" " BOOST_FOREACH(int edge, edges) {\n" " edgeResistance = (edge+1) / 2.0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'edgeResistance' can be reduced.\n", errout.str()); } void varScope9() { // classes may have extra side effects check("class fred {\n" "public:\n" " void x();\n" "};\n" "void test(int a) {\n" " fred f;\n" " if (a == 2) {\n" " f.x();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope10() { check("int f()\n" "{\n" " int x = 0;\n" " FOR {\n" " foo(x++);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope11() { check("int f() {\n" " int x = 0;\n" " AB ab = { x, 0 };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int x = 0;\n" " if (a == 0) { ++x; }\n" " AB ab = { x, 0 };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int x = 0;\n" " if (a == 0) { ++x; }\n" " if (a == 1) { AB ab = { x, 0 }; }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope12() { check("void f(int x) {\n" " int i[5];\n" " int* j = y;\n" " if (x)\n" " foo(i);\n" " foo(j);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'i' can be reduced.\n", errout.str()); check("void f(int x) {\n" " int i[5];\n" " int* j;\n" " if (x)\n" " j = i;\n" " foo(j);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " const bool b = true;\n" " x++;\n" " if (x == 5)\n" " foo(b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " const bool b = x;\n" " x++;\n" " if (x == 5)\n" " foo(b);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope13() { // #2770 check("void f() {\n" " int i = 0;\n" " forever {\n" " if (i++ == 42) { break; }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope14() { // #3941 check("void f() {\n" " const int i( foo());\n" " if(a) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope15() { // #4573 check("void f() {\n" " int a,b,c;\n" " if (a);\n" " else if(b);\n" " else if(c);\n" " else;\n" "}", nullptr, false, false); ASSERT_EQUALS("", errout.str()); } void varScope16() { check("void f() {\n" " int a = 0;\n" " while((++a) < 56) {\n" " foo();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " do {\n" " foo();\n" " } while((++a) < 56);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " do {\n" " a = 64;\n" " foo(a);\n" " } while((++a) < 56);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " do {\n" " a = 64;\n" " foo(a);\n" " } while(z());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'a' can be reduced.\n", errout.str()); } void varScope17() { check("void f() {\n" " int x;\n" " if (a) {\n" " x = stuff(x);\n" " morestuff(x);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); check("void f() {\n" " int x;\n" " if (a) {\n" " x = stuff(x);\n" " morestuff(x);\n" " }\n" " if (b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); } void varScope18() { check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " break;\n" " case B:\n" " default:\n" " break;\n" " }\n" "\n" " if (c) {\n" " x = foo();\n" " do_something(x);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " x = 10;\n" " break;\n" " case B:\n" " default:\n" " break;\n" " }\n" "\n" " if (c) {\n" " x = foo();\n" " do_something(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " if(c)\n" " do_something(x);\n" " break;\n" " case B:\n" " default:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " if(c)\n" " do_something(x);\n" " break;\n" " case B:\n" " default:\n" " if(d)\n" " do_something(x);\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope20() { // Ticket #5103 - constant variable only used in inner scope check("int f(int a) {\n" " const int x = 234;\n" " int b = a;\n" " if (b > 32) b = x;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope21() { // Ticket #5382 - initializing two-dimensional array check("int test() {\n" " int test_value = 3;\n" " int test_array[1][1] = { { test_value } };\n" " return sizeof(test_array);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope22() { // Ticket #5684 - "The scope of the variable 'p' can be reduced" - But it can not. check("void foo() {\n" " int* p( 42 );\n" " int i = 0;\n" " while ( i != 100 ) {\n" " *p = i;\n" " ++p;\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // try to avoid an obvious false negative after applying the fix for the example above: check("void foo() {\n" " int* p( 42 );\n" " int i = 0;\n" " int dummy = 0;\n" " while ( i != 100 ) {\n" " p = & dummy;\n" " *p = i;\n" " ++p;\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'p' can be reduced.\n", errout.str()); } void varScope23() { // #6154: Don't suggest to reduce scope if inner scope is a lambda check("int main() {\n" " size_t myCounter = 0;\n" " Test myTest([&](size_t aX){\n" " std::cout << myCounter += aX << std::endl;\n" " });\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope24() { check("void f(Foo x) {\n" " Foo &r = x;\n" " if (cond) {\n" " r.dostuff();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'r' can be reduced.\n", errout.str()); check("void f(Foo x) {\n" " Foo foo = x;\n" " if (cond) {\n" " foo.dostuff();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope25() { check("void f() {\n" " time_t currtime;\n" " if (a) {\n" " currtime = time(&dummy);\n" " if (currtime > t) {}\n" " }\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (style) The scope of the variable 'currtime' can be reduced.\n", errout.str()); } void varScope26() { check("void f(const std::map &m) {\n" " for (auto it : m) {\n" " if (cond1) {\n" " int& key = it.first;\n" " if (cond2) { dostuff(key); }\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope27() { checkP("void f() {\n" " int x = 0;\n" "#ifdef X\n" "#endif\n" " if (id == ABC) { return x; }\n" "}"); ASSERT_EQUALS("", errout.str()); checkP("void f() {\n" "#ifdef X\n" "#endif\n" " int x = 0;\n" " if (id == ABC) { return x; }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); } #define checkOldStylePointerCast(code) checkOldStylePointerCast_(code, __FILE__, __LINE__) void checkOldStylePointerCast_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); static Settings settings; settings.severity.enable(Severity::style); settings.standards.cpp = Standards::CPP03; // #5560 // Tokenize.. Tokenizer tokenizerCpp(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizerCpp.tokenize(istr, "test.cpp"), file, line); CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); checkOtherCpp.warningOldStylePointerCast(); } void oldStylePointerCast() { checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base * const) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (volatile Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (volatile Base * const) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const volatile Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const volatile Base * const) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) ( new Derived() );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) new Derived();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) new short[10];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class B;\n" "class A\n" "{\n" " virtual void abc(B *) const = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkOldStylePointerCast("class B;\n" "class A\n" "{\n" " virtual void abc(const B *) const = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3630 checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" " X() : Base((SomeType*)7) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" " X() : Base((SomeType*)var) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" " X() : Base((SomeType*)0) {}\n" "};"); ASSERT_EQUALS("", errout.str()); // #5560 checkOldStylePointerCast("class C;\n" "\n" "class B\n" "{ virtual G* createGui(S*, C*) const = 0; };\n" "\n" "class MS : public M\n" "{ virtual void addController(C*) override {} };"); ASSERT_EQUALS("", errout.str()); // #6164 checkOldStylePointerCast("class Base {};\n" "class Derived: public Base {};\n" "void testCC() {\n" " std::vector v;\n" " v.push_back((Base*)new Derived);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) C-style pointer casting\n", errout.str()); } #define checkInvalidPointerCast(...) checkInvalidPointerCast_(__FILE__, __LINE__, __VA_ARGS__) void checkInvalidPointerCast_(const char* file, int line, const char code[], bool portability = true, bool inconclusive = false) { // Clear the error buffer.. errout.str(""); Settings settings; settings.severity.enable(Severity::warning); if (portability) settings.severity.enable(Severity::portability); settings.certainty.setEnabled(Certainty::inconclusive, inconclusive); settings.defaultSign = 's'; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); CheckOther checkOtherCpp(&tokenizer, &settings, this); checkOtherCpp.invalidPointerCast(); } void invalidPointerCast() { checkInvalidPointerCast("void test() {\n" " float *f = new float[10];\n" " delete [] (double*)f;\n" " delete [] (long double const*)(new float[10]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Casting between float * and double * which have an incompatible binary data representation.\n" "[test.cpp:4]: (portability) Casting between float * and const long double * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("void test(const float* f) {\n" " double *d = (double*)f;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between const float * and double * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("void test(double* d1) {\n" " long double *ld = (long double*)d1;\n" " double *d2 = (double*)ld;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between double * and long double * which have an incompatible binary data representation.\n" "[test.cpp:3]: (portability) Casting between long double * and double * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("char* test(int* i) {\n" " long double *d = (long double*)(i);\n" " double *d = (double*)(i);\n" " float *f = reinterpret_cast(i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between signed int * and long double * which have an incompatible binary data representation.\n" "[test.cpp:3]: (portability) Casting between signed int * and double * which have an incompatible binary data representation.\n" "[test.cpp:4]: (portability) Casting between signed int * and float * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("float* test(unsigned int* i) {\n" " return (float*)i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between unsigned int * and float * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("float* test(unsigned int* i) {\n" " return (float*)i[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkInvalidPointerCast("float* test(double& d) {\n" " return (float*)&d;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between double * and float * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("void test(float* data) {\n" " f.write((char*)data,sizeof(float));\n" "}", true, false); ASSERT_EQUALS("", errout.str()); checkInvalidPointerCast("void test(float* data) {\n" " f.write((char*)data,sizeof(float));\n" "}", true, true); // #3639 ASSERT_EQUALS("[test.cpp:2]: (portability, inconclusive) Casting from float * to signed char * is not portable due to different binary data representations on different platforms.\n", errout.str()); checkInvalidPointerCast("long long* test(float* f) {\n" " return (long long*)f;\n" "}", false); ASSERT_EQUALS("", errout.str()); checkInvalidPointerCast("long long* test(float* f, char* c) {\n" " foo((long long*)f);\n" " return reinterpret_cast(c);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting from float * to signed long long * is not portable due to different binary data representations on different platforms.\n", errout.str()); checkInvalidPointerCast("Q_DECLARE_METATYPE(int*)"); // #4135 - don't crash } void passedByValue() { check("void f(const std::string str) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::unique_ptr ptr) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::shared_ptr ptr) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::function ptr) {}"); ASSERT_EQUALS("", errout.str()); { check("void f(const std::pair x) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::pair x) {}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } check("void f(const std::string::size_type x) {}"); ASSERT_EQUALS("", errout.str()); check("class Foo;\nvoid f(const Foo foo) {}"); // Unknown class ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class Foo { std::vector v; };\nvoid f(const Foo foo) {}"); // Large class (STL member) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class Foo { int i; };\nvoid f(const Foo foo) {}"); // Small class ASSERT_EQUALS("", errout.str()); check("class Foo { int i[6]; };\nvoid f(const Foo foo) {}"); // Large class (array) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class Foo { std::string* s; };\nvoid f(const Foo foo) {}"); // Small class (pointer) ASSERT_EQUALS("", errout.str()); check("class Foo { static std::string s; };\nvoid f(const Foo foo) {}"); // Small class (static member) ASSERT_EQUALS("", errout.str()); check("class X { std::string s; }; class Foo : X { };\nvoid f(const Foo foo) {}"); // Large class (inherited) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class X { std::string s; }; class Foo { X x; };\nvoid f(const Foo foo) {}"); // Large class (inherited) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("void f(const std::string &str) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::vector v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::vector v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::vector::size_type s) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::vector &v) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::map &v) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::streamoff pos) {}"); ASSERT_EQUALS("", errout.str()); check("void f(std::initializer_list i) {}"); ASSERT_EQUALS("", errout.str()); // #5824 check("void log(const std::string& file, int line, const std::string& function, const std::string str, ...) {}"); ASSERT_EQUALS("", errout.str()); // #5534 check("struct float3 { };\n" "typedef float3 vec;\n" "class Plane {\n" " vec Refract(vec &vec) const;\n" " bool IntersectLinePlane(const vec &planeNormal);\n" "};"); ASSERT_EQUALS("", errout.str()); check("class X {\n" " virtual void func(const std::string str) {}\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("enum X;\n" "void foo(X x1){}\n"); ASSERT_EQUALS("", errout.str()); check("enum X { a, b, c };\n" "void foo(X x2){}\n"); ASSERT_EQUALS("", errout.str()); check("enum X { a, b, c };\n" "enum X;" "void foo(X x3){}\n"); ASSERT_EQUALS("", errout.str()); check("enum X;\n" "enum X { a, b, c };" "void foo(X x4){}\n"); ASSERT_EQUALS("", errout.str()); check("union U {\n" " char* pc;\n" " short* ps;\n" " int* pi;\n" "};\n" "void f(U u) {}\n"); ASSERT_EQUALS("", errout.str()); check("struct S { char A[8][8]; };\n" "void f(S s) {}\n"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 's' should be passed by const reference.\n", errout.str()); Settings settings1; settings1.platform(Settings::Win64); check("using ui64 = unsigned __int64;\n" "ui64 Test(ui64 one, ui64 two) { return one + two; }\n", /*filename*/ nullptr, /*experimental*/ false, /*inconclusive*/ true, /*runSimpleChecks*/ true, /*verbose*/ false, &settings1); ASSERT_EQUALS("", errout.str()); } void passedByValue_nonConst() { check("void f(std::string str) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " return str + x;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " std::cout << str;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " std::cin >> str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::string str) {\n" " std::string s2 = str;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " std::string& s2 = str;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 's2' can be declared with const\n", errout.str()); check("void f(std::string str) {\n" " const std::string& s2 = str;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " str = \"\";\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::string str) {\n" " foo(str);\n" // It could be that foo takes str as non-const-reference "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const std::string& str);\n" "void f(std::string str) {\n" " foo(str);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void foo(std::string str);\n" "void f(std::string str) {\n" " foo(str);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void foo(std::string& str);\n" "void f(std::string str) {\n" " foo(str);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(std::string* str);\n" "void f(std::string str) {\n" " foo(&str);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i1, const std::string& str, int& i2);\n" "void f(std::string str) {\n" " foo((a+b)*c, str, x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("std::string f(std::string str) {\n" " str += x;\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class X {\n" " std::string s;\n" " void func() const;\n" "};\n" "Y f(X x) {\n" " x.func();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance) Function parameter 'x' should be passed by const reference.\n", errout.str()); check("class X {\n" " void func();\n" "};\n" "Y f(X x) {\n" " x.func();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class X {\n" " void func(std::string str) {}\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("class X {\n" " virtual void func(std::string str) {}\n" // Do not warn about virtual functions, if 'str' is not declared as const "};"); ASSERT_EQUALS("", errout.str()); check("class X {\n" " char a[1024];\n" "};\n" "class Y : X {\n" " char b;\n" "};\n" "void f(Y y) {\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (performance) Function parameter 'y' should be passed by const reference.\n", errout.str()); check("class X {\n" " void* a;\n" " void* b;\n" "};\n" "class Y {\n" " void* a;\n" " void* b;\n" " char c;\n" "};\n" "void f(X x, Y y) {\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (performance) Function parameter 'y' should be passed by const reference.\n", errout.str()); { // 8-byte data should be passed by const reference on 32-bit platform but not on 64-bit platform const char code[] = "class X {\n" " uint64_t a;\n" " uint64_t b;\n" "};\n" "void f(X x) {}"; Settings s32(_settings); s32.platform(cppcheck::Platform::Unix32); check(code, &s32); ASSERT_EQUALS("[test.cpp:5]: (performance) Function parameter 'x' should be passed by const reference.\n", errout.str()); Settings s64(_settings); s64.platform(cppcheck::Platform::Unix64); check(code, &s64); ASSERT_EQUALS("", errout.str()); } check("Writer* getWriter();\n" "\n" "void foo(Buffer& buffer) {\n" " getWriter()->operator<<(buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); } void passedByValue_externC() { check("struct X { int a[5]; }; void f(X v) { }"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("extern \"C\" { struct X { int a[5]; }; void f(X v) { } }"); ASSERT_EQUALS("", errout.str()); check("struct X { int a[5]; }; extern \"C\" void f(X v) { }"); ASSERT_EQUALS("", errout.str()); check("struct X { int a[5]; }; void f(const X v);"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("extern \"C\" { struct X { int a[5]; }; void f(const X v); }"); ASSERT_EQUALS("", errout.str()); check("struct X { int a[5]; }; extern \"C\" void f(const X v) { }"); ASSERT_EQUALS("", errout.str()); } void constVariable() { check("int f(std::vector x) {\n" " int& i = x[0];\n" " return i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared with const\n", errout.str()); check("int f(std::vector& x) {\n" " return x[0];\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("int f(std::vector x) {\n" " const int& i = x[0];\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector x) {\n" " static int& i = x[0];\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector x) {\n" " int& i = x[0];\n" " i++;\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& f(std::vector& x) {\n" " x.push_back(1);\n" " int& i = x[0];\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(const std::vector& x) {\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& f(std::vector& x) {\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("const int& f(std::vector& x) {\n" " return x[0];\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("int f(std::vector& x) {\n" " x[0]++;\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int a; };\n" "A f(std::vector& x) {\n" " x[0].a = 1;\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int a(); };\n" "A f(std::vector& x) {\n" " x[0].a();\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("int g(int& x);\n" "int f(std::vector& x) {\n" " g(x[0]);\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("template\n" "T f(T& x) {\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("template\n" "T f(T&& x) {\n" " return x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("template\n" "T f(T& x) {\n" " return x[0];\n" "}\n" "void h() { std::vector v; h(v); }"); ASSERT_EQUALS("", errout.str()); check("int f(int& x) {\n" " return std::move(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::ostream& os) {\n" " os << \"Hello\";\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int*);\n" "void f(int& x) {\n" " g(&x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { A(int*); };\n" "A f(int& x) {\n" " return A(&x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { A(int*); };\n" "A f(int& x) {\n" " return A{&x};\n" "}"); ASSERT_EQUALS("", errout.str()); // Perhaps unused variable should be checked as well. check("void f(int& x, int& y) {\n" " y++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " explicit A(int& y) : x(&y) {}\n" " int * x = nullptr;\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " void swap(A& a) {\n" " v.swap(a.v);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " template\n" " void f();\n" " template\n" " void f() const;\n" "};\n" "void g(A& a) {\n" " a.f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" " for(auto&& x:v)\n" " x = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" " for(auto x:v)\n" " x = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); check("void f(std::vector& v) {\n" " for(auto& x:v) {}\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); check("void f(std::vector& v) {\n" " for(const auto& x:v) {}\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); check("void f(int& i) {\n" " int& j = i;\n" " j++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" " int& i = v[0];\n" " i++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::map >& m, unsigned int i) {\n" " std::map& members = m[i];\n" " members.clear();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int& x;\n" " A(int& y) : x(y)\n" " {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " A(int& x);\n" "};\n" "struct B : A {\n" " B(int& x) : A(x)\n" " {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f(bool b, int& x, int& y) {\n" " auto& z = x;\n" " auto& w = b ? y : z;\n" " w = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int i;\n" "};\n" "int& f(S& s) {\n" " return s.i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(std::list& x, unsigned int y) {\n" " for (int& m : x) {\n" " if (m == y)\n" " return &m;\n" " }\n" " return nullptr;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& f(std::list& x, int& y) {\n" " for (int& m : x) {\n" " if (m == y)\n" " return m;\n" " }\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool from_string(int& t, const std::string& s) {\n" " std::istringstream iss(s);\n" " return !(iss >> t).fail();\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9710 check("class a {\n" " void operator()(int& i) const {\n" " i++;\n" " }\n" "};\n" "void f(int& i) {\n" " a()(i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("class a {\n" " void operator()(int& i) const {\n" " i++;\n" " }\n" "};\n" "void f(int& i) {\n" " a x;\n" " x(i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("class a {\n" " void operator()(const int& i) const;\n" "};\n" "void f(int& i) {\n" " a x;\n" " x(i);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared with const\n", errout.str()); //cast or assignment to a non-const reference should prevent the warning check("struct T { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const T& z = x;\n" //Make sure we find all assignments " T& y = x\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = x\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U& y = x\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " my::type& y = x\n" //we don't know if y is const or not " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = static_cast(x)\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U& y = static_cast(x)\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = dynamic_cast(x)\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check( "struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = dynamic_cast(x)\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = dynamic_cast(x)\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U& y = dynamic_cast(x)\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = dynamic_cast(x)\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U& y = dynamic_cast(x)\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U* y = dynamic_cast(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U * y = dynamic_cast(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); TODO_ASSERT_EQUALS("can be const", errout.str(), ""); //Currently taking the address is treated as a non-const operation when it should depend on what we do with it check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U const * y = dynamic_cast(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); TODO_ASSERT_EQUALS("can be const", errout.str(), ""); //Currently taking the address is treated as a non-const operation when it should depend on what we do with it check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U * const y = dynamic_cast(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U const * const * const * const y = dynamic_cast(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); TODO_ASSERT_EQUALS("can be const", errout.str(), ""); //Currently taking the address is treated as a non-const operation when it should depend on what we do with it check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U const * const * * const y = dynamic_cast(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " my::fancy const * * const y = dynamic_cast const * * const>(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " my::fancy const * const * const y = dynamic_cast const * const * const>(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = (const U&)(x)\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U& y = (U&)(x)\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " const U& y = (typename const U&)(x)\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U& y = (typename U&)(x)\n" " y.mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct T : public U { void dostuff() const {}};\n" "void a(T& x) {\n" " x.dostuff();\n" " U* y = (U*)(&x)\n" " y->mutate();\n" //to avoid warnings that y can be const "}"); ASSERT_EQUALS("", errout.str()); check("struct C { void f() const; };\n" // #9875 - crash "\n" "void foo(C& x) {\n" " x.f();\n" " foo( static_cast(0) );\n" "}"); ASSERT_EQUALS("", errout.str()); check("class a {\n" " void foo(const int& i) const;\n" " void operator()(int& i) const;\n" "};\n" "void f(int& i) {\n" " a()(i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("class a {\n" " void operator()(const int& i) const;\n" "};\n" "void f(int& i) {\n" " a()(i);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared with const\n", errout.str()); // #9767 check("void fct1(MyClass& object) {\n" " fct2([&](void){}, object);\n" "}\n" "bool fct2(std::function lambdaExpression, MyClass& object) {\n" " object.modify();\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9778 check("struct A {};\n" "struct B : A {};\n" "B& f(A& x) {\n" " return static_cast(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10002 check("using A = int*;\n" "void f(const A& x) {\n" " ++(*x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #10086 check("struct V {\n" " V& get(typename std::vector::size_type i) {\n" " std::vector& arr = v;\n" " return arr[i];\n" " }\n" " std::vector v;\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("void e();\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void ai(void);\n" "void j(void);\n" "void e(void);\n" "void k(void);\n" "void l(void);\n" "void m(void);\n" "void n(void);\n" "void o(void);\n" "void q(void);\n" "void r(void);\n" "void t(void);\n" "void u(void);\n" "void v(void);\n" "void w(void);\n" "void z(void);\n" "void aj(void);\n" "void am(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void ao(wchar_t *d);\n" "void ah(void);\n" "void e(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void e(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void ah(void);\n" "void k(void);\n" "void an(void);\n" "void e(void);\n" "void e(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void k(void);\n" "void an(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void ah(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void an(void);\n" "void k(void);\n" "void e(void);\n" "void e(void);\n" "void e(void);\n" "void g(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void k(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void g(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void e(void);\n" "void ap(wchar_t *c, int d);\n" "void ah(void);\n" "void an(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void aq(char *b, size_t d, char *c, int a);\n" "void ar(char *b, size_t d, char *c, va_list a);\n" "void k(void);\n" "void g(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void k(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void g(void);\n" "void as(std::string s);\n" "void at(std::ifstream &f);\n" "void au(std::istream &f);\n" "void av(std::string &aa, std::wstring &ab);\n" "void aw(bool b, double x, double y);\n" "void ax(int i);\n" "void ay(std::string c, std::wstring a);\n" "void az(const std::locale &ac);\n" "void an();\n" "void ba(std::ifstream &f);\n" "void bb(std::istream &f) {\n" "f.read(NULL, 0);\n" "}\n" "void h(void) {\n" "struct tm *tm = 0;\n" "(void)std::asctime(tm);\n" "(void)std::asctime(0);\n" "}\n" "void bc(size_t ae) {\n" "wchar_t *ad = 0, *af = 0;\n" "struct tm *ag = 0;\n" "(void)std::wcsftime(ad, ae, af, ag);\n" "(void)std::wcsftime(0, ae, 0, 0);\n" "}\n" "void k(void) {}\n" "void bd(void);\n" "void be(void);\n" "void bf(int b);\n" "void e(void);\n" "void e(void);\n" "void bg(wchar_t *p);\n" "void bh(const std::list &ak, const std::list &al);\n" "void ah();\n" "void an();\n" "void h();"); ASSERT_EQUALS("", errout.str()); check("class C\n" "{\n" "public:\n" " explicit C(int&);\n" "};\n" "\n" "class D\n" "{\n" "public:\n" " explicit D(int&);\n" "\n" "private:\n" " C c;\n" "};\n" "\n" "D::D(int& i)\n" " : c(i)\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); check("class C\n" "{\n" "public:\n" " explicit C(int&);\n" "};\n" "\n" "class D\n" "{\n" "public:\n" " explicit D(int&) noexcept;\n" "\n" "private:\n" " C c;\n" "};\n" "\n" "D::D(int& i) noexcept\n" " : c(i)\n" "{}"); ASSERT_EQUALS("", errout.str()); check("class C\n" "{\n" "public:\n" " explicit C(const int&);\n" "};\n" "\n" "class D\n" "{\n" "public:\n" " explicit D(int&);\n" "\n" "private:\n" " C c;\n" "};\n" "\n" "D::D(int& i)\n" " : c(i)\n" "{\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared with const\n", "", errout.str()); check("class C\n" "{\n" "public:\n" " explicit C(int);\n" "};\n" "\n" "class D\n" "{\n" "public:\n" " explicit D(int&);\n" "\n" "private:\n" " C c;\n" "};\n" "\n" "D::D(int& i)\n" " : c(i)\n" "{\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared with const\n", "", errout.str()); check("class C\n" "{\n" "public:\n" " explicit C(int, int);\n" "};\n" "\n" "class D\n" "{\n" "public:\n" " explicit D(int&);\n" "\n" "private:\n" " C c;\n" "};\n" "\n" "D::D(int& i)\n" " : c(0, i)\n" "{\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared with const\n", "", errout.str()); check("void f(std::map> &map) {\n" // #10266 " for (auto &[slave, panels] : map)\n" " panels.erase(it);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S { void f(); int i; };\n" "void call_f(S& s) { (s.*(&S::f))(); }\n"); ASSERT_EQUALS("", errout.str()); check("struct S { int a[1]; };\n" "void f(S& s) { int* p = s.a; *p = 0; }\n"); ASSERT_EQUALS("", errout.str()); check("struct Foo {\n" // #9910 " int* p{};\n" " int* get() { return p; }\n" " const int* get() const { return p; }\n" "};\n" "struct Bar {\n" " int j{};\n" " void f(Foo& foo) const { int* q = foo.get(); *q = j; }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" // #10679 " void g(long L, const C*& PC) const;\n" " void g(long L, C*& PC);\n" "};\n" "void f(S& s) {\n" " C* PC{};\n" " s.g(0, PC);\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void constParameterCallback() { check("int callback(std::vector& x) { return x[0]; }\n" "void f() { dostuff(callback); }"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style) Parameter 'x' can be declared with const. However it seems that 'callback' is a callback function, if 'x' is declared with const you might also need to cast function pointer(s).\n", errout.str()); // #9906 check("class EventEngine : public IEventEngine {\n" "public:\n" " EventEngine();\n" "\n" "private:\n" " void signalEvent(ev::sig& signal, int revents);\n" "};\n" "\n" "EventEngine::EventEngine() {\n" " mSigWatcher.set(this);\n" "}\n" "\n" "void EventEngine::signalEvent(ev::sig& signal, int revents) {\n" " switch (signal.signum) {}\n" "}"); ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:13]: (style) Parameter 'signal' can be declared with const. However it seems that 'signalEvent' is a callback function, if 'signal' is declared with const you might also need to cast function pointer(s).\n", errout.str()); } void constPointer() { check("void foo(int *p) { return *p; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { x = *p; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { int &ref = *p; ref = 12; }"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p) { x = *p + 10; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { return p[10]; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { int &ref = p[0]; ref = 12; }"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p) { x[*p] = 12; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { if (p) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { if (p || x) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { if (p == 0) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { if (!p) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { if (*p > 123) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { return *p + 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(int *p) { return *p > 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared with const\n", errout.str()); check("void foo(const int* c) { if (c == 0) {}; }"); ASSERT_EQUALS("", errout.str()); check("struct a { void b(); };\n" "struct c {\n" " a* d;\n" " a& g() { return *d; }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct a { void b(); };\n" "struct c { a* d; };\n" "void e(c);\n"); ASSERT_EQUALS("", errout.str()); check("struct V {\n" " V& get(typename std::vector::size_type i, std::vector* arr) {\n" " return arr->at(i);\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct A {};\n" "struct B : A {};\n" "B* f(A* x) {\n" " return static_cast(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector* x) {\n" " int& i = (*x)[0];\n" " i++;\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int a; };\n" "A f(std::vector* x) {\n" " x->front().a = 1;\n" " return x->front();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector* v) {\n" " for(auto&& x:*v)\n" " x = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int* x;\n" " A(int* y) : x(y)\n" " {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f(bool b, int* x, int* y) {\n" " int* z = x;\n" " int* w = b ? y : z;\n" " *w = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool b, int* x, int* y) {\n" " int& z = *x;\n" " int& w = b ? *y : z;\n" " w = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Base { virtual void dostuff(int *p) = 0; };\n" // #10397 "class Derived: public Base { int x; void dostuff(int *p) override { x = *p; } };"); ASSERT_EQUALS("", errout.str()); check("struct Data { char buf[128]; };\n" // #10483 "void encrypt(Data& data) {\n" " const char a[] = \"asfasd\";\n" " memcpy(data.buf, &a, sizeof(a));\n" "}"); ASSERT_EQUALS("", errout.str()); // #10547 check("void foo(std::istream &istr) {\n" " unsigned char x[2];\n" " istr >> x[0];\n" "}"); ASSERT_EQUALS("", errout.str()); } void switchRedundantAssignmentTest() { check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y = 2;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " if (x)\n" " {\n" " y = 3;\n" " }\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y = 2;\n" " if (z)\n" " printf(\"%d\", y);\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int x = a;\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " x = 2;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " break;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " continue;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " throw e;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " printf(\"%d\", y);\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " bar();\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void bar() {}\n" // bar isn't noreturn "void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " bar();\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " case 3:\n" " strcpy(str, \"b'\");\n" " }\n" "}", nullptr, false, false, false); TODO_ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (style) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", "", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strncpy(str, \"a'\");\n" " case 3:\n" " strncpy(str, \"b'\");\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (style) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", "", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " int z = 0;\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " z++;\n" " case 3:\n" " strcpy(str, \"b'\");\n" " z++;\n" " }\n" "}", nullptr, false, false, false); TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (style) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", "", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " break;\n" " case 3:\n" " strcpy(str, \"b'\");\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " printf(str);\n" " case 3:\n" " strcpy(str, \"b'\");\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // Ticket #5158 "segmentation fault (valid code)" check("typedef struct ct_data_s {\n" " union {\n" " char freq;\n" " } fc;\n" "} ct_data;\n" "typedef struct internal_state {\n" " struct ct_data_s dyn_ltree[10];\n" "} deflate_state;\n" "void f(deflate_state *s) {\n" " s->dyn_ltree[0].fc.freq++;\n" "}\n", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // Ticket #6132 "crash: daca: kvirc CheckOther::checkRedundantAssignment()" check("void HttpFileTransfer :: transferTerminated ( bool bSuccess@1 ) {\n" "if ( m_szCompletionCallback . isNull ( ) ) {\n" "KVS_TRIGGER_EVENT ( KviEvent_OnHTTPGetTerminated , out ? out : ( g_pApp . activeConsole ( ) ) , & vParams )\n" "} else {\n" "KviKvsScript :: run ( m_szCompletionCallback , out ? out : ( g_pApp . activeConsole ( ) ) , & vParams ) ;\n" "}\n" "}\n", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x;\n" " switch (state) {\n" " case 1: x = 3; goto a;\n" " case 1: x = 6; goto a;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void switchRedundantOperationTest() { check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " ++y;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " ++y;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " (void)y;\n" " case 3:\n" " ++y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " ++y;\n" " case 3:\n" " ++y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " --y;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " --y;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " (void)y;\n" " case 3:\n" " --y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " --y;\n" " case 3:\n" " --y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y++;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y++;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " y++;\n" " }\n" " bar(y);\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y++;\n" " case 3:\n" " y++;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y--;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y--;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " y--;\n" " }\n" " bar(y);\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y--;\n" " case 3:\n" " y--;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y++;\n" " case 3:\n" " if (x)\n" " {\n" " y = 3;\n" " }\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y++;\n" " if (y)\n" " printf(\"%d\", y);\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int x = a;\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " x++;\n" " case 3:\n" " y++;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " break;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " continue;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " throw e;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " printf(\"%d\", y);\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " bar();\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("bool f() {\n" " bool ret = false;\n" " switch (switchCond) {\n" " case 1:\n" " ret = true;\n" " break;\n" " case 31:\n" " ret = true;\n" " break;\n" " case 54:\n" " ret = true;\n" " break;\n" " };\n" " ret = true;\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n" "[test.cpp:8] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n" "[test.cpp:11] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout.str()); } void switchRedundantBitwiseOperationTest() { check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = y | 3;\n" " case 3:\n" " y = y | 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " default:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " default:\n" " if (z)\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= z;\n" " z++\n" " default:\n" " y |= z;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " bar(y);\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " y = 4;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'y' is reassigned a value before the old one has been used.\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y &= 3;\n" " case 3:\n" " y &= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " break;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y ^= 3;\n" " case 3:\n" " y ^= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 2;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y &= 2;\n" " case 3:\n" " y &= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 2;\n" " case 3:\n" " y &= 2;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void unreachableCode() { check("void foo(int a) {\n" " while(1) {\n" " if (a++ >= 100) {\n" " break;\n" " continue;\n" " }\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:5]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo(int a) {\n" " return 0;\n" " return(a-1);\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo(int a) {\n" " A:" " return(0);\n" " goto A;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); Settings settings; settings.library.setnoreturn("exit", true); settings.library.functions["exit"].argumentChecks[1] = Library::ArgumentChecks(); check("void foo() {\n" " exit(0);\n" " break;\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("class NeonSession {\n" " void exit();\n" "};\n" "void NeonSession::exit()\n" "{\n" " SAL_INFO(\"ucb.ucp.webdav\", \"neon commands cannot be aborted\");\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void NeonSession::exit()\n" "{\n" " SAL_INFO(\"ucb.ucp.webdav\", \"neon commands cannot be aborted\");\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo() { xResAccess->exit(); }", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " switch(a) {\n" " case 0:\n" " printf(\"case 0\");\n" " break;\n" " break;\n" " case 1:\n" " c++;\n" " break;\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:7]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo(int a)\n" "{\n" " switch(a) {\n" " case 0:\n" " printf(\"case 0\");\n" " break;\n" " case 1:\n" " c++;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " while(true) {\n" " if (a++ >= 100) {\n" " break;\n" " break;\n" " }\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:6]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo(int a)\n" "{\n" " while(true) {\n" " if (a++ >= 100) {\n" " continue;\n" " continue;\n" " }\n" " a+=2;\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:6]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo(int a)\n" "{\n" " while(true) {\n" " if (a++ >= 100) {\n" " continue;\n" " }\n" " a+=2;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " throw 0;\n" " return 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo() {\n" " throw 0;\n" " return;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo() {\n" " throw = 0;\n" " return 1;\n" "}", "test.c", false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " return 0;\n" " return 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo() {\n" " return 0;\n" " foo();\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Statements following return, break, continue, goto or throw will never be executed.\n", errout.str()); check("int foo(int unused) {\n" " return 0;\n" " (void)unused;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo(int unused1, int unused2) {\n" " return 0;\n" " (void)unused1;\n" " (void)unused2;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo(int unused1, int unused2) {\n" " return 0;\n" " (void)unused1;\n" " (void)unused2;\n" " foo();\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:5]: (style) Statements following return, break, continue, goto or throw will never be executed.\n", errout.str()); check("int foo() {\n" " if(bar)\n" " return 0;\n" " return 124;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " while(bar) {\n" " return 0;\n" " return 0;\n" " return 0;\n" " return 0;\n" " }\n" " return 124;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:4]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo() {\n" " while(bar) {\n" " return;\n" " break;\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:4]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); // #5707 check("extern int i,j;\n" "int foo() {\n" " switch(i) {\n" " default: j=1; break;\n" " }\n" " return 0;\n" " j=2;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:7]: (style) Statements following return, break, continue, goto or throw will never be executed.\n", errout.str()); check("int foo() {\n" " return 0;\n" " label:\n" " throw 0;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Label 'label' is not used.\n", errout.str()); check("struct A {\n" " virtual void foo (P & Val) throw ();\n" " virtual void foo1 (P & Val) throw ();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " goto label;\n" " while (true) {\n" " bar();\n" " label:\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3457 check("int foo() {\n" " goto label;\n" " do {\n" " bar();\n" " label:\n" " } while (true);\n" "}"); ASSERT_EQUALS("", errout.str()); // #3457 check("int foo() {\n" " goto label;\n" " for (;;) {\n" " bar();\n" " label:\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3457 // #3383. TODO: Use preprocessor check("int foo() {\n" "\n" // #ifdef A " return 0;\n" "\n" // #endif " return 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" "\n" // #ifdef A " return 0;\n" "\n" // #endif " return 1;\n" "}", nullptr, false, true, false); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); // #4711 lambda functions check("int f() {\n" " return g([](int x){(void)x+1; return x;});\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #4756 check("template <>\n" "inline uint16_t htobe(uint16_t value) {\n" " return ( __extension__ ({\n" " register unsigned short int __v, __x = (unsigned short int) (value);\n" " if (__builtin_constant_p (__x))\n" " __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff) << 8)));\n" " else\n" " __asm__ (\"rorw $8, %w0\" : \"=r\" (__v) : \"0\" (__x) : \"cc\");\n" " (void)__v;\n" " }));\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #6008 check("static std::function< int ( int, int ) > GetFunctor() {\n" " return [](int a_, int b_) -> int {\n" " int sum = a_ + b_;\n" " return sum;\n" " };\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #5789 check("struct per_state_info {\n" " uint64_t enter, exit;\n" " uint64_t events;\n" " per_state_info() : enter(0), exit(0), events(0) {}\n" "};", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #6664 check("void foo() {\n" " (beat < 100) ? (void)0 : exit(0);\n" " bar();\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " (beat < 100) ? exit(0) : (void)0;\n" " bar();\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); // #8261 // TODO Do not throw AST validation exception TODO_ASSERT_THROW(check("void foo() {\n" " (beat < 100) ? (void)0 : throw(0);\n" " bar();\n" "}", nullptr, false, false, false, false, &settings), InternalError); //ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " exit(0);\n" " return 1;\n" // <- clarify for tools that function does not continue.. "}"); ASSERT_EQUALS("", errout.str()); } void suspiciousCase() { check("void foo() {\n" " switch(a) {\n" " case A&&B:\n" " foo();\n" " case (A||B):\n" " foo();\n" " case A||B:\n" " foo();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Found suspicious case label in switch(). Operator '&&' probably doesn't work as intended.\n" "[test.cpp:5]: (warning, inconclusive) Found suspicious case label in switch(). Operator '||' probably doesn't work as intended.\n" "[test.cpp:7]: (warning, inconclusive) Found suspicious case label in switch(). Operator '||' probably doesn't work as intended.\n", errout.str()); check("void foo() {\n" " switch(a) {\n" " case 1:\n" " a=A&&B;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // TODO Do not throw AST validation exception TODO_ASSERT_THROW(check("void foo() {\n" " switch(a) {\n" " case A&&B?B:A:\n" " foo();\n" " }\n" "}"), InternalError); //ASSERT_EQUALS("", errout.str()); } void suspiciousEqualityComparison() { check("void foo(int c) {\n" " if (x) c == 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(const int* c) {\n" " if (x) *c == 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " if (c == 1) {\n" " c = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int c) {\n" " c == 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (int i = 0; i == 10; i ++) {\n" " a ++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int c) {\n" " for (i == 0; i < 10; i ++) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (i == 1; i < 10; i ++) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (i == 2; i < 10; i ++) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (int i = 0; i < 10; i == c) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (; running == 1;) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int c) {\n" " printf(\"%i\", ({x==0;}));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int arg) {\n" " printf(\"%i\", ({int x = do_something(); x == 0;}));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " printf(\"%i\", ({x == 0; x > 0 ? 10 : 20}));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int x) {\n" " for (const Token* end = tok->link(); tok != end; tok = (tok == end) ? end : tok->next()) {\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " for (int i = (x == 0) ? 0 : 5; i < 10; i ++) {\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " for (int i = 0; i < 10; i += (x == 5) ? 1 : 2) {\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void selfAssignment() { check("void foo()\n" "{\n" " int x = 1;\n" " x = x;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); check("void foo()\n" "{\n" " int x = x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); check("struct A { int b; };\n" "void foo(A* a1, A* a2) {\n" " a1->b = a1->b;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'a1->b' to itself.\n", errout.str()); check("int x;\n" "void f()\n" "{\n" " x = x = 3;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); // #4073 (segmentation fault) check("void Foo::myFunc( int a )\n" "{\n" " if (a == 42)\n" " a = a;\n" "}"); check("void foo()\n" "{\n" " int x = 1;\n" " x = x + 1;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int *x = getx();\n" " *x = x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " BAR *x = getx();\n" " x = x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); // #2502 - non-primitive type -> there might be some side effects check("void foo()\n" "{\n" " Fred fred; fred = fred;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " x = (x == 0);" " func(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " x = (x != 0);" " func(x);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3001 - false positive check("void foo(int x) {\n" " x = x ? x : 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3800 - false negative when variable is extern check("extern int i;\n" "void f() {\n" " i = i;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'i' to itself.\n", errout.str()); // #4291 - id for variables accessed through 'this' check("class Foo {\n" " int var;\n" " void func();\n" "};\n" "void Foo::func() {\n" " this->var = var;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant assignment of 'this->var' to itself.\n", errout.str()); check("class Foo {\n" " int var;\n" " void func(int var);\n" "};\n" "void Foo::func(int var) {\n" " this->var = var;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6406 - designated initializer doing bogus self assignment check("struct callbacks {\n" " void (*s)(void);\n" "};\n" "void something(void) {}\n" "void f() {\n" " struct callbacks ops = { .s = ops.s };\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant assignment of 'something' to itself.\n", "", errout.str()); check("class V\n" "{\n" "public:\n" " V()\n" " {\n" " x = y = z = 0.0;\n" " }\n" " V( double x, const double y, const double &z )\n" " {\n" " x = x; y = y; z = z;\n" " }\n" " double x, y, z;\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Redundant assignment of 'x' to itself.\n" "[test.cpp:10]: (warning) Redundant assignment of 'y' to itself.\n" "[test.cpp:10]: (warning) Redundant assignment of 'z' to itself.\n", errout.str()); check("void f(int i) { i = !!i; }"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " int x = 1;\n" " int &ref = x;\n" " ref = x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (warning) Redundant assignment of 'ref' to itself.\n", errout.str()); check("class Foo {\n" // #9850 " int i{};\n" " void modify();\n" " void method() {\n" " Foo copy = *this;\n" " modify();\n" " *this = copy;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void trac1132() { check("class Lock\n" "{\n" "public:\n" " Lock(int i)\n" " {\n" " std::cout << \"Lock \" << i << std::endl;\n" " }\n" " ~Lock()\n" " {\n" " std::cout << \"~Lock\" << std::endl;\n" " }\n" "};\n" "int main()\n" "{\n" " Lock(123);\n" " std::cout << \"hello\" << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:15]: (style) Instance of 'Lock' object is destroyed immediately.\n", errout.str()); } void trac3693() { check("struct A{\n" " enum {\n" " b = 300\n" " };\n" "};\n" "const int DFLT_TIMEOUT = A::b % 1000000 ;\n", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickFunction1() { check("int main ( )\n" "{\n" " CouldBeFunction ( 123 ) ;\n" " return 0 ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickFunction2() { check("struct error {\n" " error() {}\n" "};\n" "\n" "class parser {\n" "public:\n" " void error() const {}\n" "\n" " void foo() const {\n" " error();\n" " do_something();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectPicksClass() { check("class NotAFunction ;\n" "int function ( )\n" "{\n" " NotAFunction ( 123 );\n" " return 0 ;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'NotAFunction' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectPicksStruct() { check("struct NotAClass;\n" "bool func ( )\n" "{\n" " NotAClass ( 123 ) ;\n" " return true ;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'NotAClass' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectDoesNotPickIf() { check("bool func( int a , int b , int c )\n" "{\n" " if ( a > b ) return c == a ;\n" " return b == a ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickConstructorDeclaration() { check("class Something : public SomethingElse\n" "{\n" "public:\n" "~Something ( ) ;\n" "Something ( ) ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickFunctor() { check("class IncrementFunctor\n" "{\n" "public:\n" " void operator()(int &i)\n" " {\n" " ++i;\n" " }\n" "};\n" "\n" "int main()\n" "{\n" " int a = 1;\n" " IncrementFunctor()(a);\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickLocalClassConstructors() { check("void f() {\n" " class Foo {\n" " Foo() { }\n" " Foo(int a) { }\n" " Foo(int a, int b) { }\n" " };\n" " Foo();\n" " do_something();\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'Foo' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectDoesNotPickUsedObject() { check("struct Foo {\n" " void bar() {\n" " }\n" "};\n" "\n" "void fn() {\n" " Foo().bar();\n" "}"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickPureC() { // Ticket #2352 const char code[] = "struct cb_watch_bool {\n" " int a;\n" "};\n" "\n" "void f()\n" "{\n" " cb_watch_bool();\n" " do_something();\n" "}\n"; check(code, "test.cpp"); ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'cb_watch_bool' object is destroyed immediately.\n", errout.str()); check(code, "test.c"); ASSERT_EQUALS("", errout.str()); // Ticket #2639 check("struct stat { int a; int b; };\n" "void stat(const char *fn, struct stat *);\n" "\n" "void foo() {\n" " stat(\"file.txt\", &st);\n" " do_something();\n" "}"); ASSERT_EQUALS("",errout.str()); } void testMisusedScopeObjectDoesNotPickNestedClass() { const char code[] = "class ios_base {\n" "public:\n" " class Init {\n" " public:\n" " };\n" "};\n" "class foo {\n" "public:\n" " foo();\n" " void Init(int);\n" "};\n" "foo::foo() {\n" " Init(0);\n" " do_something();\n" "}\n"; check(code, "test.cpp"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectInConstructor() { const char code[] = "class Foo {\n" "public:\n" " Foo(char x) {\n" " Foo(x, 0);\n" " do_something();\n" " }\n" " Foo(char x, int y) { }\n" "};\n"; check(code, "test.cpp"); ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'Foo' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectNoCodeAfter() { check("class Foo {};\n" "void f() {\n" " Foo();\n" // No code after class => don't warn "}", "test.cpp"); ASSERT_EQUALS("", errout.str()); } void trac2084() { check("void f()\n" "{\n" " struct sigaction sa;\n" "\n" " { sigaction(SIGHUP, &sa, 0); };\n" " { sigaction(SIGINT, &sa, 0); };\n" "}"); ASSERT_EQUALS("", errout.str()); } void trac2071() { check("void f() {\n" " struct AB {\n" " AB(int a) { }\n" " };\n" "\n" " const AB ab[3] = { AB(0), AB(1), AB(2) };\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCalculation() { check("int f(char c) {\n" " return 10 * (c == 0) ? 1 : 2;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '*' and '?'.\n", errout.str()); check("void f(char c) {\n" " printf(\"%i\", 10 * (c == 0) ? 1 : 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '*' and '?'.\n", errout.str()); check("void f() {\n" " return (2*a)?b:c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char c) {\n" " printf(\"%i\", a + b ? 1 : 2);\n" "}",nullptr,false,false,false); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '+' and '?'.\n", errout.str()); check("void f() {\n" " std::cout << x << y ? 2 : 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '<<' and '?'.\n", errout.str()); check("void f() {\n" " int ab = a - b ? 2 : 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '-' and '?'.\n", errout.str()); check("void f() {\n" " int ab = a | b ? 2 : 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '|' and '?'.\n", errout.str()); // ticket #195 check("int f(int x, int y) {\n" " return x >> ! y ? 8 : 2;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '>>' and '?'.\n", errout.str()); check("int f() {\n" " return shift < sizeof(int64_t)*8 ? 1 : 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() { a = *p ? 1 : 2; }"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { const char *p = x & 1 ? \"1\" : \"0\"; }"); ASSERT_EQUALS("", errout.str()); check("void foo() { x = a % b ? \"1\" : \"0\"; }"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { return x & 1 ? '1' : '0'; }"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { return x & 16 ? 1 : 0; }"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { return x % 16 ? 1 : 0; }"); ASSERT_EQUALS("", errout.str()); check("enum {X,Y}; void f(int x) { return x & Y ? 1 : 0; }"); ASSERT_EQUALS("", errout.str()); } void clarifyStatement() { check("char* f(char* c) {\n" " *c++;\n" " return c;\n" "}"); ASSERT_EQUALS( "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*'\n" "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char* f(char** c) {\n" " *c[5]--;\n" " return *c;\n" "}"); ASSERT_EQUALS( "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*'\n" "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("void f(Foo f) {\n" " *f.a++;\n" "}"); ASSERT_EQUALS( "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*'\n" "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("void f(Foo f) {\n" " *f.a[5].v[3]++;\n" "}"); ASSERT_EQUALS( "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*'\n" "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("void f(Foo f) {\n" " *f.a(1, 5).v[x + y]++;\n" "}"); ASSERT_EQUALS( "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*'\n" "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char* f(char* c) {\n" " (*c)++;\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char* c) {\n" " bar(*c++);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char*** f(char*** c) {\n" " ***c++;\n" " return c;\n" "}"); ASSERT_EQUALS( "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*'\n" "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char** f(char*** c) {\n" " **c[5]--;\n" " return **c;\n" "}"); ASSERT_EQUALS( "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*'\n" "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char*** f(char*** c) {\n" " (***c)++;\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void *f(char** c) {\n" " bar(**c++);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void *f(char* p) {\n" " for (p = path; *p++;) ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::array,3> array;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateBranch() { check("void f(int a, int &b) {\n" " if (a)\n" " b = 1;\n" " else\n" " b = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a) {\n" " if (a == 1)\n" " b = 2;\n" " else\n" " b = 2;\n" " } else\n" " b = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1)\n" " b = 1;\n" " else {\n" " if (a)\n" " b = 2;\n" " else\n" " b = 2;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("int f(int signed, unsigned char value) {\n" " int ret;\n" " if (signed)\n" " ret = (signed char)value;\n" // cast must be kept so the simplifications and verification is skipped " else\n" " ret = (unsigned char)value;\n" " return ret;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (b)\n" " __asm__(\"mov ax, bx\");\n" " else\n" " __asm__(\"mov bx, bx\");\n" "}"); ASSERT_EQUALS("", errout.str()); // #3407 check("void f() {\n" " if (b)\n" " __asm__(\"mov ax, bx\");\n" " else\n" " __asm__(\"mov ax, bx\");\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); } void duplicateBranch1() { // tests inspired by http://www.viva64.com/en/b/0149/ ( Comparison between PVS-Studio and cppcheck ) // Errors detected in Quake 3: Arena by PVS-Studio: Fragment 2 check("void f()\n" "{\n" " if (front < 0)\n" " frac = front/(front-back);\n" " else\n" " frac = front/(front-back);\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f()\n" "{\n" " if (front < 0)\n" " { frac = front/(front-back);}\n" " else\n" " frac = front/((front-back));\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); // No message about empty branches (#5354) check("void f()\n" "{\n" " if (front < 0)\n" " {}\n" " else\n" " {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateBranch2() { checkP("#define DOSTUFF1 ;\n" "#define DOSTUFF2 ;\n" "void f(int x) {\n" // #4329 " if (x)\n" " DOSTUFF1\n" " else\n" " DOSTUFF2\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateBranch3() { check("void f(bool b, int i) {\n" " int j = i;\n" " if (b) {\n" " x = i;\n" " } else {\n" " x = j;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f(bool b, int i) {\n" " int j = i;\n" " i++;\n" " if (b) {\n" " x = i;\n" " } else {\n" " x = j;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateBranch4() { check("void* f(bool b) {\n" " if (b) {\n" " return new A::Y(true);\n" " } else {\n" " return new A::Z(true);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression1() { check("void foo(int a) {\n" " if (a == a) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void fun(int b) {\n" " return a && a ||\n" " b == b &&\n" " d > d &&\n" " e < e &&\n" " f ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n" "[test.cpp:3]: (style) Same expression on both sides of '=='.\n" "[test.cpp:4]: (style) Same expression on both sides of '>'.\n" "[test.cpp:5]: (style) Same expression on both sides of '<'.\n", errout.str()); check("void foo() {\n" " return a && a;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo() {\n" " a = b && b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo(int b) {\n" " f(a,b == b);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void foo(int b) {\n" " f(b == b, a);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void foo() {\n" " if (x!=2 || x!=2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((a < b) && (b > a)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'aa' represent the same value.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((a <= b) && (b >= a)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'a<=b' and 'b>=a' represent the same value.\n", errout.str()); check("void foo() {\n" " if (x!=2 || y!=3 || x!=2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (x!=2 && (x=y) && x!=2) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (a && b || a && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (a && b || b && c) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (a && b | b && c) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|'.\n", errout.str()); check("void foo() {\n" " if ((a + b) | (a + b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|'.\n", errout.str()); check("void foo() {\n" " if ((a | b) & (a | b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&'.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((a | b) == (a | b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void foo() {\n" " if (a1[a2[c & 0xff] & 0xff]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void d(const char f, int o, int v)\n" "{\n" " if (((f=='R') && (o == 1) && ((v < 2) || (v > 99))) ||\n" " ((f=='R') && (o == 2) && ((v < 2) || (v > 99))) ||\n" " ((f=='T') && (o == 2) && ((v < 200) || (v > 9999)))) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int x) { return x+x; }"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { while (x+=x) ; }"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (a && b && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo() {\n" " if (a || b || b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (a / 1000 / 1000) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(int i) {\n" " return i/i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '/'.\n", errout.str()); check("void foo() {\n" " if (a << 1 << 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() { return !!y; }"); // No FP ASSERT_EQUALS("", errout.str()); // make sure there are not "same expression" fp when there are different casts check("void f(long x) { if ((int32_t)x == (int64_t)x) {} }", nullptr, // filename false, // experimental false, // inconclusive false, // runSimpleChecks false, // verbose nullptr // settings ); ASSERT_EQUALS("", errout.str()); // make sure there are not "same expression" fp when there are different ({}) expressions check("void f(long x) { if (({ 1+2; }) == ({3+4;})) {} }"); ASSERT_EQUALS("", errout.str()); // #5535: Reference named like its type check("void foo() { UMSConfig& UMSConfig = GetUMSConfiguration(); }"); ASSERT_EQUALS("[test.cpp:1]: (style) Variable 'UMSConfig' can be declared with const\n", errout.str()); // #3868 - false positive (same expression on both sides of |) check("void f(int x) {\n" " a = x ? A | B | C\n" " : A | B;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Bar &bar) {\n" " bool a = bar.isSet() && bar->isSet();\n" " bool b = bar.isSet() && bar.isSet();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((b + a) | (a + b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|' because 'b+a' and 'a+b' represent the same value.\n", errout.str()); check("void foo(const std::string& a, const std::string& b) {\n" " return a.find(b+\"&\") || a.find(\"&\"+b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a, int b) {\n" " if ((b > a) | (a > b)) {}\n" // > is not commutative "}"); ASSERT_EQUALS("", errout.str()); check("void foo(double a, double b) {\n" " if ((b + a) > (a + b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The comparison 'b+a > a+b' is always false because 'b+a' and 'a+b' represent the same value.\n", errout.str()); check("void f(int x) {\n" " if ((x == 1) && (x == 0x00000001))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'x==1' and 'x==0x00000001' represent the same value.\n", errout.str()); check("void f() {\n" " enum { Four = 4 };\n" " if (Four == 4) {}" "}", nullptr, false, true, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { Four = 4 };\n" " static_assert(Four == 4, \"\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { Four = 4 };\n" " static_assert(4 == Four, \"\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { FourInEnumOne = 4 };\n" " enum { FourInEnumTwo = 4 };\n" " if (FourInEnumOne == FourInEnumTwo) {}\n" "}", nullptr, false, true, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { FourInEnumOne = 4 };\n" " enum { FourInEnumTwo = 4 };\n" " static_assert(FourInEnumOne == FourInEnumTwo, \"\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a, int b) {\n" " if (sizeof(a) == sizeof(a)) { }\n" " if (sizeof(a) == sizeof(b)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("float bar(int) __attribute__((pure));\n" "char foo(int) __attribute__((pure));\n" "int test(int a, int b) {\n" " if (bar(a) == bar(a)) { }\n" " if (unknown(a) == unknown(a)) { }\n" " if (foo(a) == foo(a)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Same expression on both sides of '=='.\n", errout.str()); } void duplicateExpression2() { // check if float is NaN or Inf check("int f(long double ldbl, double dbl, float flt) {\n" // ticket #2730 " if (ldbl != ldbl) have_nan = 1;\n" " if (!(dbl == dbl)) have_nan = 1;\n" " if (flt != flt) have_nan = 1;\n" " return have_nan;\n" "}"); ASSERT_EQUALS("", errout.str()); check("float f(float x) { return x-x; }"); // ticket #4485 (Inf) ASSERT_EQUALS("", errout.str()); check("float f(float x) { return (X double)x == (X double)x; }", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("struct X { float f; };\n" "float f(struct X x) { return x.f == x.f; }"); ASSERT_EQUALS("", errout.str()); check("struct X { int i; };\n" "int f(struct X x) { return x.i == x.i; }"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); // #5284 - when type is unknown, assume it's float check("int f() { return x==x; }"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression3() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); check("void foo() {\n" " if (x() || x()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " void foo() const;\n" " bool bar() const;\n" "};\n" "void A::foo() const {\n" " if (bar() && bar()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("struct A {\n" " void foo();\n" " bool bar();\n" " bool bar() const;\n" "};\n" "void A::foo() {\n" " if (bar() && bar()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("class B {\n" " void bar(int i);\n" "};\n" "class A {\n" " void bar(int i) const;\n" "};\n" "void foo() {\n" " B b;\n" " A a;\n" " if (b.bar(1) && b.bar(1)) {}\n" " if (a.bar(1) && a.bar(1)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("class D { void strcmp(); };\n" "void foo() {\n" " D d;\n" " if (d.strcmp() && d.strcmp()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if ((mystrcmp(a, b) == 0) || (mystrcmp(a, b) == 0)) {}\n" "}", "test.cpp", false, false, true, false, &settings); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void GetValue() { return rand(); }\n" "void foo() {\n" " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void __attribute__((const)) GetValue() { return X; }\n" "void foo() {\n" " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void GetValue() __attribute__((const));\n" "void GetValue() { return X; }\n" "void foo() {\n" " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (str == \"(\" || str == \"(\") {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (bar(a) && !strcmp(a, b) && bar(a) && !strcmp(a, b)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #5334 check("void f(C *src) {\n" " if (x(src) || x(src))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(A *src) {\n" " if (dynamic_cast(src) || dynamic_cast(src)) {}\n" "}\n", "test.cpp", false, false, false); // don't run simplifications ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); // #5819 check("Vector func(Vector vec1) {\n" " return fabs(vec1 & vec1 & vec1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("Vector func(int vec1) {\n" " return fabs(vec1 & vec1 & vec1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&'.\n", errout.str()); } void duplicateExpression4() { check("void foo() {\n" " if (*a++ != b || *a++ != b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (*a-- != b || *a-- != b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // assignment check("void f() {\n" " while (*(a+=2)==*(b+=2) && *(a+=2)==*(b+=2)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression5() { // #3749 - macros with same values check("void f() {\n" " if ($a == $a) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression6() { // #4639 check("float IsNan(float value) { return !(value == value); }\n" "double IsNan(double value) { return !(value == value); }\n" "long double IsNan(long double value) { return !(value == value); }"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression7() { check("void f() {\n" " const int i = sizeof(int);\n" " if ( i != sizeof (int)){}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'i != sizeof(int)' is always false because 'i' and 'sizeof(int)' represent the same value.\n", errout.str()); check("void f() {\n" " const int i = sizeof(int);\n" " if ( sizeof (int) != i){}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'sizeof(int) != i' is always false because 'sizeof(int)' and 'i' represent the same value.\n", errout.str()); check("void f(int a = 1) { if ( a != 1){}}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 1;\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout.str()); check("void f() {\n" " int a = 1;\n" " int b = 1;\n" " if ( a != b){}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:4]: (style) The comparison 'a != b' is always false because 'a' and 'b' represent the same value.\n", errout.str()); check("void f() {\n" " int a = 1;\n" " int b = a;\n" " if ( a != b){}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The comparison 'a != b' is always false because 'a' and 'b' represent the same value.\n", errout.str()); check("void use(int);\n" "void f() {\n" " int a = 1;\n" " int b = 1;\n" " use(b);\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) The comparison 'a != 1' is always false.\n", errout.str()); check("void use(int);\n" "void f() {\n" " int a = 1;\n" " use(a);\n" " a = 2;\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void use(int);\n" "void f() {\n" " int a = 2;\n" " use(a);\n" " a = 1;\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("const int a = 1;\n" "void f() {\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout.str()); check("int a = 1;\n" " void f() {\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static const int a = 1;\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout.str()); check("void f() {\n" " static int a = 1;\n" " if ( a != 1){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 1;\n" " if ( a != 1){\n" " a++;\n" " }}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout.str()); check("void f(int b) {\n" " int a = 1;\n" " while (b) {\n" " if ( a != 1){}\n" " a++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool a, bool b) {\n" " const bool c = a;\n" " return a && b && c;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '&&' because 'a' and 'c' represent the same value.\n", errout.str()); // 6906 check("void f(const bool b) {\n" " const bool b1 = !b;\n" " if(!b && b1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '&&' because '!b' and 'b1' represent the same value.\n", errout.str()); // 7284 check("void f(void) {\n" " if (a || !!a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'a' and '!!a' represent the same value.\n", errout.str()); // 8205 check("void f(int x) {\n" " int Diag = 0;\n" " switch (x) {\n" " case 12:\n" " if (Diag==0) {}\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The comparison 'Diag == 0' is always true.\n", errout.str()); } void duplicateExpression8() { check("void f() {\n" " int a = 1;\n" " int b = a;\n" " a = 2;\n" " if ( b != a){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int * a, int i) { int b = a[i]; a[i] = 2; if ( b != a[i]){}}"); ASSERT_EQUALS("", errout.str()); check("void f(int * a, int i) { int b = *a; *a = 2; if ( b != *a){}}"); ASSERT_EQUALS("", errout.str()); check("struct A { int f() const; };\n" "A g();\n" "void foo() {\n" " for (A x = A();;) {\n" " const int a = x.f();\n" " x = g();\n" " if (x.f() == a) break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int i);\n" "struct A {\n" " enum E { B, C };\n" " bool f(E);\n" "};\n" "void foo() {\n" " A a;\n" " const bool x = a.f(A::B);\n" " const bool y = a.f(A::C);\n" " if(!x && !y) return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " const bool x = a.f(A::B);\n" " const bool y = a.f(A::C);\n" " if (!x && !y) return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool * const b);\n" "void foo() {\n" " bool x = true;\n" " bool y = true;\n" " f(&x);\n" " if (!x && !y) return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const int a = {};\n" " if(a == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("volatile const int var = 42;\n" "void f() { if(var == 42) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " struct b c;\n" " c.a = &a;\n" " g(&c);\n" " if (a == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression9() { // #9320 check("void f() {\n" " uint16_t x = 1000;\n" " uint8_t y = x;\n" " if (x != y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression10() { // #9485 check("int f() {\n" " const int a = 1;\n" " const int b = a-1;\n" " const int c = a+1;\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression11() { check("class Fred {\n" "public:\n" " double getScale() const { return m_range * m_zoom; }\n" " void setZoom(double z) { m_zoom = z; }\n" " void dostuff(int);\n" "private:\n" " double m_zoom;\n" " double m_range;\n" "};\n" "\n" "void Fred::dostuff(int x) {\n" " if (x == 43) {\n" " double old_scale = getScale();\n" " setZoom(m_zoom + 1);\n" " double scale_ratio = getScale() / old_scale;\n" // <- FP " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression12() { //#10026 check("int f(const std::vector &buffer, const uint8_t index)\n" "{\n" " int var = buffer[index - 1];\n" " return buffer[index - 1] - var;\n" // << "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Same expression on both sides of '-'.\n", errout.str()); } void duplicateExpression13() { //#7899 check("void f() {\n" " if (sizeof(long) == sizeof(long long)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpressionLoop() { check("void f() {\n" " int a = 1;\n" " while ( a != 1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout.str()); check("void f() { int a = 1; while ( a != 1){ a++; }}"); ASSERT_EQUALS("", errout.str()); check("void f() { int a = 1; for ( int i=0; i < 3 && a != 1; i++){ a++; }}"); ASSERT_EQUALS("", errout.str()); check("void f(int b) { int a = 1; while (b) { if ( a != 1){} b++; } a++; }"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'i != 0' is always false.\n", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) {}\n" " i++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) { i++; }\n" " i++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) { i++; }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i = 0;\n" " while(i < 10) {\n" " if( i != 0 ) {}\n" " i++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int b) {\n" " while (b) {\n" " int a = 1;\n" " if ( a != 1){}\n" " b++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The comparison 'a != 1' is always false.\n", errout.str()); } void duplicateExpressionTernary() { // #6391 check("void f() {\n" " return A ? x : x;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression in both branches of ternary operator.\n", errout.str()); check("int f(bool b, int a) {\n" " const int c = a;\n" " return b ? a : c;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression in both branches of ternary operator.\n", errout.str()); check("void f() {\n" " return A ? x : z;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(unsigned char c) {\n" " x = y ? (signed char)c : (unsigned char)c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string stringMerge(std::string const& x, std::string const& y) {\n" // #7938 " return ((x > y) ? (y + x) : (x + y));\n" "}"); ASSERT_EQUALS("", errout.str()); // #6426 { const char code[] = "void foo(bool flag) {\n" " bar( (flag) ? ~0u : ~0ul);\n" "}"; Settings settings = _settings; settings.sizeof_int = 4; settings.int_bit = 32; settings.sizeof_long = 4; settings.long_bit = 32; check(code, &settings); ASSERT_EQUALS("[test.cpp:2]: (style) Same value in both branches of ternary operator.\n", errout.str()); settings.sizeof_long = 8; settings.long_bit = 64; check(code, &settings); ASSERT_EQUALS("", errout.str()); } } void duplicateValueTernary() { check("void f() {\n" " if( a ? (b ? false:false): false ) ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f1(int a) {return (a == 1) ? (int)1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f2(int a) {return (a == 1) ? (int)1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f3(int a) {return (a == 1) ? 1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f4(int a) {return (a == 1) ? 1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f5(int a) {return (a == (int)1) ? (int)1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f6(int a) {return (a == (int)1) ? (int)1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f7(int a) {return (a == (int)1) ? 1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f8(int a) {return (a == (int)1) ? 1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("struct Foo {\n" " std::vector bar{1,2,3};\n" " std::vector baz{4,5,6};\n" "};\n" "void f() {\n" " Foo foo;\n" " it = true ? foo.bar.begin() : foo.baz.begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " std::vector bar{1,2,3};\n" " std::vector baz{4,5,6};\n" " std::vector v = b ? bar : baz;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool q) {\n" // #9570 " static int a = 0;\n" " static int b = 0;\n" " int& x = q ? a : b;\n" " ++x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateExpressionTemplate() { check("template void f() {\n" // #6930 " if (I >= 0 && I < 3) {}\n" "}\n" "\n" "static auto a = f<0>();"); ASSERT_EQUALS("", errout.str()); check("template\n" // #7754 "void f() {\n" " if (std::is_same_v || std::is_same_v) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("typedef long long int64_t;" "template\n" "void f() {\n" " if (std::is_same_v || std::is_same_v) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkP("#define int32_t int" "template\n" "void f() {\n" " if (std::is_same_v || std::is_same_v) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateExpressionCompareWithZero() { check("void f(const int* x, bool b) {\n" " if ((x && b) || (x != 0 && b)) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x&&b' and 'x!=0&&b' represent the same value.\n", errout.str()); check("void f(const int* x, bool b) {\n" " if ((x != 0 && b) || (x && b)) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x!=0&&b' and 'x&&b' represent the same value.\n", errout.str()); check("void f(const int* x, bool b) {\n" " if ((x && b) || (b && x != 0)) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x&&b' and 'b&&x!=0' represent the same value.\n", errout.str()); check("void f(const int* x, bool b) {\n" " if ((!x && b) || (x == 0 && b)) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because '!x&&b' and 'x==0&&b' represent the same value.\n", errout.str()); check("void f(const int* x, bool b) {\n" " if ((x == 0 && b) || (!x && b)) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x==0&&b' and '!x&&b' represent the same value.\n", errout.str()); check("void f(const int* x, bool b) {\n" " if ((!x && b) || (b && x == 0)) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because '!x&&b' and 'b&&x==0' represent the same value.\n", errout.str()); check("struct A {\n" " int* getX() const;\n" " bool getB() const;\n" " void f() {\n" " if ((getX() && getB()) || (getX() != 0 && getB())) {}\n" " }\n" "};\n"); ASSERT_EQUALS("[test.cpp:5]: (style) Same expression on both sides of '||' because 'getX()&&getB()' and 'getX()!=0&&getB()' represent the same value.\n", errout.str()); check("void f(const int* x, bool b) {\n" " if ((x && b) || (x == 0 && b)) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(const int* x, bool b) {\n" " if ((!x && b) || (x != 0 && b)) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void oppositeExpression() { check("void f(bool a) { if(a && !a) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '&&'.\n", errout.str()); check("void f(bool a) { if(a != !a) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f(bool a) { if( a == !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { if( a != !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f(bool a) { if( !(a) == a ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { if( !(a) != a ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f(bool a) { if( !(!a) == !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { if( !(!a) != !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f1(bool a) {\n" " const bool b = a;\n" " if( a == !(b) ) {}\n" " if( b == !(a) ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n" "[test.cpp:2] -> [test.cpp:4]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f2(const bool *a) {\n" " const bool b = *a;\n" " if( *a == !(b) ) {}\n" " if( b == !(*a) ) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n" "[test.cpp:2] -> [test.cpp:4]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { a = !a; }"); ASSERT_EQUALS("", errout.str()); check("void f(int a) { if( a < -a ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '<'.\n", errout.str()); check("void f(int a) { a -= -a; }"); ASSERT_EQUALS("", errout.str()); check("void f(int a) { a = a / (-a); }"); ASSERT_EQUALS("", errout.str()); check("bool f(int i){ return !((i - 1) & i); }"); ASSERT_EQUALS("", errout.str()); check("bool f(unsigned i){ return (x > 0) && (x & (x-1)) == 0; }"); ASSERT_EQUALS("", errout.str()); check("void A::f(bool a, bool c)\n" "{\n" " const bool b = a;\n" " if(c) { a = false; }\n" " if(b && !a) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool c) {\n" " const bool b = a;\n" " if(c) { a = false; }\n" " if(b && !a) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " bool x = a;\n" " dostuff();\n" " if (x && a) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const bool b = g();\n" " if (!b && g()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const bool *a) {\n" " const bool b = a[42];\n" " if( b == !(a[42]) ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(const bool *a) {\n" " const bool b = a[42];\n" " if( a[42] == !(b) ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(const bool *a) {\n" " const bool b = *a;\n" " if( b == !(*a) ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(const bool *a) {\n" " const bool b = *a;\n" " if( *a == !(b) ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(uint16_t u) {\n" // #9342 " if (u != (u & -u))\n" " return false;\n" " if (u != (-u & u))\n" " return false;\n" " return true;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateVarExpression() { check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = f();\n" " int j = f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; int g() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; int g() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " Foo f2 = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = 1 + f();\n" " int j = 1 + f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = f() + 1;\n" " int j = 1 + f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int x = f();\n" " int i = x + 1;\n" " int j = f() + 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = f() + f();\n" " int j = f() + f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f(int) __attribute__((pure));\n" "int g(int) __attribute__((pure));\n" "void test() {\n" " int i = f(0);\n" " int j = f(0);\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f(int) __attribute__((pure));\n" "int g(int) __attribute__((pure));\n" "void test() {\n" " const int x = 0;\n" " int i = f(0);\n" " int j = f(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(const int * p, const int * q) {\n" " int i = *p;\n" " int j = *p;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("void test() {\n" " int i = 0;\n" " int j = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " int i = -1;\n" " int j = -1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int);\n" "void test() {\n" " int i = f(0);\n" " int j = f(1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f();\n" "int g();\n" "void test() {\n" " int i = f() || f();\n" " int j = f() && f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo {};\n" "void test() {\n" " Foo i = Foo();\n" " Foo j = Foo();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo {};\n" "void test() {\n" " Foo i = Foo{};\n" " Foo j = Foo{};\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo { int f() const; float g() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f(); int g(); };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " int i = f();\n" " int j = f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = ++x;\n" " int j = ++x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = x++;\n" " int j = x++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = --x;\n" " int j = --x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = x--;\n" " int j = x--;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = x + 1;\n" " int j = 1 + x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateVarExpressionUnique() { check("struct SW { int first; };\n" "void foo(SW* x) {\n" " int start = x->first;\n" " int end = x->first;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n", errout.str()); check("struct SW { int first; };\n" "void foo(SW* x, int i, int j) {\n" " int start = x->first;\n" " int end = x->first;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n", errout.str()); check("struct Foo { int f() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("void test(const int * p) {\n" " int i = *p;\n" " int j = *p;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; int g(int) const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); } void duplicateVarExpressionAssign() { check("struct A { int x; int y; };" "void use(int);\n" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" " use(i);\n" " i = j;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void use(int);\n" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" " use(j);\n" " j = i;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void use(int);\n" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" " use(j);\n" " if (i == j) {}\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void use(int);\n" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" " use(j);\n" " if (i == a.x) {}\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void use(int);\n" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" " use(i);\n" " if (j == a.x) {}\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); // Issue #8612 check("struct P\n" "{\n" " void func();\n" " bool operator==(const P&) const;\n" "};\n" "struct X\n" "{\n" " P first;\n" " P second;\n" "};\n" "bool bar();\n" "void baz(const P&);\n" "void foo(const X& x)\n" "{\n" " P current = x.first;\n" " P previous = x.first;\n" " while (true)\n" " {\n" " baz(current);\n" " if (bar() && previous == current)\n" " {\n" " current.func();\n" " }\n" " previous = current;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:16] -> [test.cpp:15]: (style, inconclusive) Same expression used in consecutive assignments of 'current' and 'previous'.\n", errout.str()); } void duplicateVarExpressionCrash() { // Issue #8624 check("struct X {\n" " X();\n" " int f() const;\n" "};\n" "void run() {\n" " X x;\n" " int a = x.f();\n" " int b = x.f();\n" " (void)a;\n" " (void)b;\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:7]: (style, inconclusive) Same expression used in consecutive assignments of 'a' and 'b'.\n", errout.str()); // Issue #8712 check("void f() {\n" " unsigned char d;\n" " d = d % 5;\n" "}"); ASSERT_EQUALS("", errout.str()); check("template \n" "T f() {\n" " T x = T();\n" "}\n" "int &a = f();"); ASSERT_EQUALS("", errout.str()); // Issue #8713 check("class A {\n" " int64_t B = 32768;\n" " P m = MakeP(B);\n" "};\n" "void f() {\n" " uint32_t a = 42;\n" " uint32_t b = uint32_t(A ::B / 1024);\n" " int32_t c = int32_t(a / b);\n" "}"); ASSERT_EQUALS("", errout.str()); // Issue #8709 check("a b;\n" "void c() {\n" " switch (d) { case b:; }\n" " double e(b);\n" " if(e <= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #10718 // Should probably not be inconclusive check("struct a {\n" " int b() const;\n" " auto c() -> decltype(0) {\n" " a d;\n" " int e = d.b(), f = d.b();\n" " return e + f;\n" " }\n" "};\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (style, inconclusive) Same expression used in consecutive assignments of 'e' and 'f'.\n", errout.str()); } void multiConditionSameExpression() { check("void f() {\n" " int val = 0;\n" " if (val < 0) continue;\n" " if ((val > 0)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'val < 0' is always false.\n" "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'val > 0' is always false.\n", errout.str()); check("void f() {\n" " int val = 0;\n" " int *p = &val;n" " val = 1;\n" " if (*p < 0) continue;\n" " if ((*p > 0)) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int val = 0;\n" " int *p = &val;\n" " if (*p < 0) continue;\n" " if ((*p > 0)) {}\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison '*p < 0' is always false.\n" "[test.cpp:2] -> [test.cpp:4]: (style) The comparison '*p > 0' is always false.\n", "", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if ((val > 0)) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'val < 0' is always false.\n" "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'val > 0' is always false.\n", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if ((val < 0)) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'val < 0' is always false.\n" "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'val < 0' is always false.\n", errout.str()); check("void f() {\n" " int activate = 0;\n" " int foo = 0;\n" " if (activate) {}\n" " else if (foo) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkSignOfUnsignedVariable() { check("void foo() {\n" " for(unsigned char i = 10; i >= 0; i--) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'i' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(bool b) {\n" " for(unsigned int i = 10; b || i >= 0; i--) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'i' can't be negative so it is unnecessary to test it.\n", errout.str()); { const char code[] = "void foo(unsigned int x) {\n" " if (x < 0) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); } check("void foo(unsigned int x) {\n" " if (x < 0u) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x) {\n" " if (x < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); { const char code[] = "void foo(unsigned x) {\n" " int y = 0;\n" " if (x < y) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); } check("void foo(unsigned x) {\n" " int y = 0;\n" " if (b)\n" " y = 1;\n" " if (x < y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x) {\n" " if (0 > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(unsigned int x) {\n" " if (0UL > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x) {\n" " if (0 > x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x) {\n" " if (x >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(unsigned int x, unsigned y) {\n" " if (x - y >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x-y' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(unsigned int x) {\n" " if (x >= 0ull) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x) {\n" " if (x >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x) {\n" " if (0 <= x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(unsigned int x) {\n" " if (0ll <= x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x) {\n" " if (0 <= x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x < 0 && y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x < 0 && y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (0 > x && y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (0 > x && y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x >= 0 && y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x >= 0 && y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (y && x < 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (y && x < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (y && 0 > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (y && 0 > x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (y && x >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (y && x >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x < 0 || y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x < 0 || y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (0 > x || y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (0 > x || y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x >= 0 || y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x >= 0 || y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #3233 - FP when template is used (template parameter is numeric constant) { const char code[] = "template void foo(unsigned int x) {\n" " if (x <= n);\n" "}\n" "foo<0>();"; check(code, nullptr, false, false); ASSERT_EQUALS("", errout.str()); check(code, nullptr, false, true); ASSERT_EQUALS("", errout.str()); } { Settings keepTemplates; keepTemplates.checkUnusedTemplates = true; check("template void foo(unsigned int x) {\n" "if (x <= 0);\n" "}", &keepTemplates); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); } // #8836 check("uint32_t value = 0xFUL;\n" "void f() {\n" " if (value < 0u)\n" " {\n" " value = 0u;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'value' is less than zero.\n", errout.str()); // #9040 Settings settings1; settings1.platform(Settings::Win64); check("using BOOL = unsigned;\n" "int i;\n" "bool f() {\n" " return i >= 0;\n" "}\n", &settings1); ASSERT_EQUALS("", errout.str()); // #10612 check("void f(void) {\n" " const uint32_t x = 0;\n" " constexpr const auto y = 0xFFFFU;\n" " if (y < x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Checking if unsigned expression 'y' is less than zero.\n", errout.str()); } void checkSignOfPointer() { check("void foo(const int* x) {\n" " if (x >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); { const char code[] = "void foo(const int* x) {\n" " int y = 0;\n" " if (x >= y) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); } check("void foo(const int* x) {\n" " if (*x >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const int* x) {\n" " if (x < 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); { const char code[] = "void foo(const int* x) {\n" " unsigned y = 0u;\n" " if (x < y) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); } check("void foo(const int* x) {\n" " if (*x < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const int* x, const int* y) {\n" " if (x - y < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const int* x, const int* y) {\n" " if (x - y <= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const int* x, const int* y) {\n" " if (x - y > 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const int* x, const int* y) {\n" " if (x - y >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const Bar* x) {\n" " if (0 <= x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first) {\n" " if (first.ptr >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if((first.ptr - second.ptr) >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first) {\n" " if((first.ptr) >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 <= first.ptr - second.ptr) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 <= (first.ptr - second.ptr)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(first.ptr - second.ptr < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if((first.ptr - second.ptr) < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 > first.ptr - second.ptr) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 > (first.ptr - second.ptr)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const int* x) {\n" " if (0 <= x[0]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 <= x.y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 <= x->y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x, Bar* y) {\n" " if (0 <= x->y - y->y ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const Bar* x) {\n" " if (0 > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check("void foo(const int* x) {\n" " if (0 > x[0]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 > x.y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 > x->y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " int (*t)(void *a, void *b);\n" " if (t(a, b) < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " int (*t)(void *a, void *b);\n" " if (0 > t(a, b)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct object_info { int *typep; };\n" "void packed_object_info(struct object_info *oi) {\n" " if (oi->typep < 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check("struct object_info { int typep[10]; };\n" "void packed_object_info(struct object_info *oi) {\n" " if (oi->typep < 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check("struct object_info { int *typep; };\n" "void packed_object_info(struct object_info *oi) {\n" " if (*oi->typep < 0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkSuspiciousSemicolon1() { check("void foo() {\n" " for(int i = 0; i < 10; ++i);\n" "}"); ASSERT_EQUALS("", errout.str()); // Empty block check("void foo() {\n" " for(int i = 0; i < 10; ++i); {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious use of ; at the end of 'for' statement.\n", errout.str()); check("void foo() {\n" " while (!quit); {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious use of ; at the end of 'while' statement.\n", errout.str()); } void checkSuspiciousSemicolon2() { check("void foo() {\n" " if (i == 1); {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious use of ; at the end of 'if' statement.\n", errout.str()); // Seen this in the wild check("void foo() {\n" " if (Match());\n" " do_something();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (Match());\n" " else\n" " do_something();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (i == 1)\n" " ;\n" " {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (i == 1);\n" "\n" " {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkSuspiciousSemicolon3() { checkP("#define REQUIRE(code) {code}\n" "void foo() {\n" " if (x == 123);\n" " REQUIRE(y=z);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkSuspiciousComparison() { checkP("void f(int a, int b) {\n" " a > b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '>'\n", errout.str()); checkP("void f() {\n" // #10607 " for (auto p : m)\n" " std::vector> k;\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkInvalidFree() { check("void foo(char *p) {\n" " char *a; a = malloc(1024);\n" " free(a + 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = malloc(1024);\n" " free(a - 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = malloc(1024);\n" " free(10 + a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char[1024];\n" " delete[] (a + 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " delete a + 10;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " bar(a);\n" " delete a + 10;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " char *b; b = new char;\n" " bar(a);\n" " delete a + 10;\n" " delete b + 10;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " char *b; b = new char;\n" " bar(a, b);\n" " delete a + 10;\n" " delete b + 10;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " bar()\n" " delete a + 10;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(size_t xx) {\n" " char *ptr; ptr = malloc(42);\n" " ptr += xx;\n" " free(ptr + 1 - xx);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(size_t xx) {\n" " char *ptr; ptr = malloc(42);\n" " std::cout << ptr;\n" " ptr = otherPtr;\n" " free(otherPtr - xx - 1);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkRedundantCopy() { check("const std::string& getA(){static std::string a;return a;}\n" "void foo() {\n" " const std::string a = getA();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout.str()); check("class A{public:A(){}};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a = getA();\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout.str()); check("const int& getA(){static int a;return a;}\n" "int main()\n" "{\n" " const int a = getA();\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("const int& getA(){static int a;return a;}\n" "int main()\n" "{\n" " int getA = 0;\n" " const int a = getA + 3;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Local variable \'getA\' shadows outer function\n", errout.str()); check("class A{public:A(){}};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a(getA());\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout.str()); check("const int& getA(){static int a;return a;}\n" "int main()\n" "{\n" " const int a(getA());\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A{\n" "public:A(int a=0){_a = a;}\n" "A operator+(const A & a){return A(_a+a._a);}\n" "private:int _a;};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a = getA() + 1;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A{\n" "public:A(int a=0){_a = a;}\n" "A operator+(const A & a){return A(_a+a._a);}\n" "private:int _a;};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a(getA()+1);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #5190 - FP when creating object with constructor that takes a reference check("class A {};\n" "class B { B(const A &a); };\n" "const A &getA();\n" "void f() {\n" " const B b(getA());\n" "}"); ASSERT_EQUALS("", errout.str()); // #5618 const char* code5618 = "class Token {\n" "public:\n" " const std::string& str();\n" "};\n" "void simplifyArrayAccessSyntax() {\n" " for (Token *tok = list.front(); tok; tok = tok->next()) {\n" " const std::string temp = tok->str();\n" " tok->str(tok->strAt(2));\n" " }\n" "}"; check(code5618, nullptr, false, true); TODO_ASSERT_EQUALS("", "[test.cpp:7]: (performance, inconclusive) Use const reference for 'temp' to avoid unnecessary data copying.\n", errout.str()); check(code5618, nullptr, false, false); ASSERT_EQUALS("", errout.str()); // #5890 - crash: wesnoth desktop_util.cpp / unicode.hpp check("typedef std::vector X;\n" "X f(const X &in) {\n" " const X s = f(in);\n" " return f(s);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7981 - False positive redundantCopyLocalConst - const ref argument to ctor check("class CD {\n" " public:\n" " CD(const CD&);\n" " static const CD& getOne();\n" "};\n" " \n" "void foo() {\n" " const CD cd(CD::getOne());\n" "}", nullptr, false, true); ASSERT_EQUALS("", errout.str()); } void checkNegativeShift() { check("void foo()\n" "{\n" " int a; a = 123;\n" " (void)(a << -1);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " int a; a = 123;\n" " (void)(a >> -1);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " int a; a = 123;\n" " a <<= -1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " int a; a = 123;\n" " a >>= -1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " std::cout << -1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << a << -1 ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << 3 << -1 ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " x = (-10+2) << 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Shifting a negative value is technically undefined behaviour\n", errout.str()); check("x = y ? z << $-1 : 0;"); ASSERT_EQUALS("", errout.str()); // Negative LHS check("const int x = -1 >> 2;"); ASSERT_EQUALS("[test.cpp:1]: (portability) Shifting a negative value is technically undefined behaviour\n", errout.str()); // #6383 - unsigned type check("const int x = (unsigned int)(-1) >> 2;"); ASSERT_EQUALS("", errout.str()); // #7814 - UB happening in valueflowcode when it tried to compute shifts. check("int shift1() { return 1 >> -1 ;}\n" "int shift2() { return 1 << -1 ;}\n" "int shift3() { return -1 >> 1 ;}\n" "int shift4() { return -1 << 1 ;}"); ASSERT_EQUALS("[test.cpp:1]: (error) Shifting by a negative value is undefined behaviour\n" "[test.cpp:2]: (error) Shifting by a negative value is undefined behaviour\n" "[test.cpp:3]: (portability) Shifting a negative value is technically undefined behaviour\n" "[test.cpp:4]: (portability) Shifting a negative value is technically undefined behaviour\n", errout.str()); } void incompleteArrayFill() { check("void f() {\n" " int a[5];\n" " memset(a, 123, 5);\n" " memcpy(a, b, 5);\n" " memmove(a, b, 5);\n" "}"); ASSERT_EQUALS(// TODO "[test.cpp:4] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n" "[test.cpp:3]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n" "[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memcpy()' with 'sizeof(*a)'?\n" "[test.cpp:5]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memmove()' with 'sizeof(*a)'?\n", errout.str()); check("void f() {\n" " Foo* a[5];\n" " memset(a, 'a', 5);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", errout.str()); check("class Foo {int a; int b;};\n" "void f() {\n" " Foo a[5];\n" " memset(a, 'a', 5);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", errout.str()); check("void f() {\n" " Foo a[5];\n" // Size of foo is unknown " memset(a, 'a', 5);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char a[5];\n" " memset(a, 'a', 5);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[5];\n" " memset(a+15, 'a', 5);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " bool a[5];\n" " memset(a, false, 5);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability, inconclusive) Array 'a' might be filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", errout.str()); } void redundantVarAssignment() { setMultiline(); // Simple tests check("void f(int i) {\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:2:note:i is assigned\n" "test.cpp:3:note:i is overwritten\n", errout.str()); // non-local variable => only show warning when inconclusive is used check("int i;\n" "void f() {\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i is assigned\n" "test.cpp:4:note:i is overwritten\n", errout.str()); check("void f() {\n" " int i;\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i is assigned\n" "test.cpp:4:note:i is overwritten\n", errout.str()); check("void f() {\n" " static int i;\n" " i = 1;\n" " i = 1;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("void f() {\n" " int i[10];\n" " i[2] = 1;\n" " i[2] = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i[2]' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i[2] is assigned\n" "test.cpp:4:note:i[2] is overwritten\n", errout.str()); check("void f(int x) {\n" " int i[10];\n" " i[x] = 1;\n" " x=1;\n" " i[x] = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int x) {\n" " int i[10];\n" " i[x] = 1;\n" " i[x] = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i[x]' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i[x] is assigned\n" "test.cpp:4:note:i[x] is overwritten\n", errout.str()); // Testing different types check("void f() {\n" " Foo& bar = foo();\n" " bar = x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " Foo& bar = foo();\n" " bar = x;\n" " bar = y;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("void f() {\n" " Foo& bar = foo();\n" // #4425. bar might refer to something global, etc. " bar = y();\n" " foo();\n" " bar = y();\n" "}"); ASSERT_EQUALS("", errout.str()); // Tests with function call between assignment check("void f(int i) {\n" " i = 1;\n" " bar();\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:2:note:i is assigned\n" "test.cpp:4:note:i is overwritten\n", errout.str()); check("int i;\n" "void f() {\n" " i = 1;\n" " bar();\n" // Global variable might be accessed in bar() " i = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int i;\n" " i = 1;\n" " bar();\n" // bar() might call f() recursively. This could be a false positive in more complex examples (when value of i is used somewhere. See #4229) " i = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i;\n" " i = 1;\n" " bar();\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:5:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i is assigned\n" "test.cpp:5:note:i is overwritten\n", errout.str()); check("void bar(int i) {}\n" "void f(int i) {\n" " i = 1;\n" " bar(i);\n" // Passed as argument " i = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " Foo bar = foo();\n" " bar();\n" // #5568. operator() called " bar = y();\n" "}"); ASSERT_EQUALS("", errout.str()); // Branch tests check("void f(int i) {\n" " i = 1;\n" " if(x)\n" " i = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " if(x)\n" " i = 0;\n" " i = 1;\n" " i = 2;\n" "}"); ASSERT_EQUALS("test.cpp:5:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:4:note:i is assigned\n" "test.cpp:5:note:i is overwritten\n", errout.str()); // #4513 check("int x;\n" "int g() {\n" " return x*x;\n" "}\n" "void f() {\n" " x = 2;\n" " x = g();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int g() {\n" " return x*x;\n" "}\n" "void f(int x) {\n" " x = 2;\n" " x = g();\n" "}"); ASSERT_EQUALS("test.cpp:6:style:Variable 'x' is reassigned a value before the old one has been used.\n" "test.cpp:5:note:x is assigned\n" "test.cpp:6:note:x is overwritten\n", errout.str()); check("void f() {\n" " Foo& bar = foo();\n" " bar = x;\n" " bar = y();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class C {\n" " int x;\n" " void g() { return x * x; }\n" " void f();\n" "};\n" "\n" "void C::f() {\n" " x = 2;\n" " x = g();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class C {\n" " int x;\n" " void g() { return x*x; }\n" " void f(Foo z);\n" "};\n" "\n" "void C::f(Foo z) {\n" " x = 2;\n" " x = z.g();\n" "}"); ASSERT_EQUALS("", errout.str()); // ({ }) check("void f() {\n" " int x;\n" " x = 321;\n" " x = ({ asm(123); })\n" "}"); ASSERT_EQUALS("", errout.str()); // from #3103 (avoid a false negative) check("int foo(){\n" " int x;\n" " x = 1;\n" " x = 1;\n" " return x + 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'x' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:x is assigned\n" "test.cpp:4:note:x is overwritten\n", errout.str()); // from #3103 (avoid a false positive) check("int foo(){\n" " int x;\n" " x = 1;\n" " if (y)\n" // <-- cppcheck does not know anything about 'y' " x = 2;\n" " return x + 1;\n" "}"); ASSERT_EQUALS("", errout.str()); // initialization, assignment with 0 check("void f() {\n" // Ticket #4356 " int x = 0;\n" // <- ignore initialization with 0 " x = 3;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " state_t *x = NULL;\n" " x = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " state_t *x;\n" " x = NULL;\n" " x = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" // #4420 " int x;\n" " bar(++x);\n" " x = 5;\n" " return bar(x);\n" "}"); ASSERT_EQUALS("", errout.str()); // struct member.. check("struct AB { int a; int b; };\n" "\n" "int f() {\n" " struct AB ab;\n" " ab.a = 1;\n" " ab.a = 2;\n" " return ab.a;\n" "}"); ASSERT_EQUALS("test.cpp:6:style:Variable 'ab.a' is reassigned a value before the old one has been used.\n" "test.cpp:5:note:ab.a is assigned\n" "test.cpp:6:note:ab.a is overwritten\n", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f() {\n" " struct AB ab;\n" " ab.a = 1;\n" " ab = do_something();\n" " return ab.a;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f() {\n" " struct AB ab;\n" " ab.a = 1;\n" " do_something(&ab);\n" " ab.a = 2;\n" " return ab.a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f(DO_SOMETHING do_something) {\n" " struct AB ab;\n" " ab.a = 1;\n" " do_something(&ab);\n" " ab.a = 2;\n" " return ab.a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f(struct AB *ab) {\n" " ab->a = 1;\n" " ab->b = 2;\n" " ab++;\n" " ab->a = 1;\n" " ab->b = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f(struct AB *ab) {\n" " ab->a = 1;\n" " ab->b = 2;\n" " ab = x;\n" " ab->a = 1;\n" " ab->b = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct AB *ab) {\n" // # " ab->data->x = 1;\n" " ab = &ab1;\n" " ab->data->x = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // #5964 check("void func(char *buffer, const char *format, int precision, unsigned value) {\n" " (precision < 0) ? sprintf(buffer, format, value) : sprintf(buffer, format, precision, value);\n" "}"); ASSERT_EQUALS("", errout.str()); // don't crash check("struct data {\n" " struct { int i; } fc;\n" "};\n" "struct state {\n" " struct data d[123];\n" "};\n" "void func(struct state *s) {\n" " s->foo[s->x++] = 2;\n" " s->d[1].fc.i++;\n" "}"); // #6525 - inline assembly check("void f(int i) {\n" " i = 1;\n" " asm(\"foo\");\n" " i = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6555 check("void foo() {\n" " char *p = 0;\n" " try {\n" " p = fred();\n" " p = wilma();\n" " }\n" " catch (...) {\n" " barney(p);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char *p = 0;\n" " try {\n" " p = fred();\n" " p = wilma();\n" " }\n" " catch (...) {\n" " barney(x);\n" " }\n" "}"); ASSERT_EQUALS("test.cpp:2:style:The scope of the variable 'p' can be reduced.\n", errout.str()); check("void foo() {\n" " char *p = 0;\n" " try {\n" " if(z) {\n" " p = fred();\n" " p = wilma();\n" " }\n" " }\n" " catch (...) {\n" " barney(p);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Member variable pointers check("void podMemPtrs() {\n" " int POD::*memptr;\n" " memptr = &POD::a;\n" " memptr = &POD::b;\n" " if (memptr)\n" " memptr = 0;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'memptr' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:memptr is assigned\n" "test.cpp:4:note:memptr is overwritten\n", errout.str()); // Pointer function argument (#3857) check("void f(float * var)\n" "{\n" " var[0] = 0.2f;\n" " var[0] = 0.2f;\n" // <-- is initialized twice "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'var[0]' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:var[0] is assigned\n" "test.cpp:4:note:var[0] is overwritten\n", errout.str()); check("void f(float * var)\n" "{\n" " *var = 0.2f;\n" " *var = 0.2f;\n" // <-- is initialized twice "}"); ASSERT_EQUALS("test.cpp:4:style:Variable '*var' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:*var is assigned\n" "test.cpp:4:note:*var is overwritten\n", errout.str()); // Volatile variables check("void f() {\n" " volatile char *reg = (volatile char *)0x12345;\n" " *reg = 12;\n" " *reg = 34;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_trivial() { check("void f() {\n" " int a = 0;\n" " a = 4;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a;\n" " a = 0;\n" " a = 4;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " unsigned a;\n" " a = 0u;\n" " a = 2u;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " void* a;\n" " a = (void*)0;\n" " a = p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " void* a;\n" " a = (void*)0U;\n" " a = p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_struct() { check("struct foo {\n" " int a,b;\n" "};\n" "\n" "int main() {\n" " struct foo x;\n" " x.a = _mm_set1_ps(1.0);\n" " x.a = _mm_set1_ps(2.0);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'x.a' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" " struct AB ab;\n" " ab.x = 23;\n" " ab.y = 41;\n" " ab.x = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'ab.x' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" " struct AB ab = {0};\n" " ab = foo();\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_7133() { // #7133 check("sal_Int32 impl_Export() {\n" " try {\n" " try {\n" " uno::Sequence< uno::Any > aArgs(2);\n" " beans::NamedValue aValue;\n" " aValue.Name = \"DocumentHandler\";\n" " aValue.Value <<= xDocHandler;\n" " aArgs[0] <<= aValue;\n" " aValue.Name = \"Model\";\n" " aValue.Value <<= xDocumentComp;\n" " aArgs[1] <<= aValue;\n" " }\n" " catch (const uno::Exception&) {\n" " }\n" " }\n" " catch (const uno::Exception&) {\n" " }\n" "}", "test.cpp", false, true); ASSERT_EQUALS("", errout.str()); check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n" " BitmapBuffer aSrcBuf;\n" " aSrcBuf.mnBitCount = nSrcBits;\n" " BitmapBuffer aDstBuf;\n" " aSrcBuf.mnBitCount = nDestBits;\n" " bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3] -> [test.c:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", errout.str()); check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n" " BitmapBuffer aSrcBuf;\n" " aSrcBuf.mnBitCount = nSrcBits;\n" " BitmapBuffer aDstBuf;\n" " aSrcBuf.mnBitCount = nDestBits;\n" " bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", errout.str()); check("class C { void operator=(int x); };\n" // #8368 - assignment operator might have side effects => inconclusive "void f() {\n" " C c;\n" " c = x;\n" " c = x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Variable 'c' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); } void redundantVarAssignment_stackoverflow() { check("typedef struct message_node {\n" " char code;\n" " size_t size;\n" " struct message_node *next, *prev;\n" "} *message_list;\n" "static message_list remove_message_from_list(message_list m) {\n" " m->prev->next = m->next;\n" " m->next->prev = m->prev;\n" " return m->next;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_lambda() { // #7152 check("int foo() {\n" " int x = 0, y = 0;\n" " auto f = [&]() { if (x < 5) ++y; };\n" " x = 2;\n" " f();\n" " x = 6;\n" " f();\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_loop() { check("void f() {\n" " char buf[10];\n" " int i;\n" " for (i = 0; i < 4; i++)\n" " buf[i] = 131;\n" " buf[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void bar() {\n" // #9262 do-while with break " int x = 0;\n" " x = 432;\n" " do {\n" " if (foo()) break;\n" " x = 1;\n" " } while (false);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int num) {\n" // #9420 FP " int a = num;\n" " for (int b = 0; b < num; a = b++)\n" " dostuff(a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int num) {\n" // #9420 FN " int a = num;\n" " for (int b = 0; b < num; a = b++);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void redundantVarAssignment_after_switch() { check("void f(int x) {\n" // #7907 " int ret;\n" " switch (x) {\n" " case 123:\n" " ret = 1;\n" // redundant assignment " break;\n" " }\n" " ret = 3;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:8]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout.str()); } void redundantVarAssignment_pointer() { check("void f(int *ptr) {\n" " int *x = ptr + 1;\n" " *x = 23;\n" " foo(ptr);\n" " *x = 32;\n" "}"); ASSERT_EQUALS("", errout.str()); // #8997 check("void f() {\n" " char x[2];\n" " char* p = x;\n" " *p = 1;\n" " p += 1;\n" " *p = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_pointer_parameter() { check("void f(int *p) {\n" " *p = 1;\n" " if (condition) return;\n" " *p = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_array() { check("void f() {\n" " int arr[10];\n" " int i = 0;\n" " arr[i] = 1;\n" " i += 2;\n" " arr[i] = 3;\n" " dostuff(arr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_switch_break() { // #10058 check("void f(int a, int b) {\n" " int ret = 0;\n" " switch (a) {\n" " case 1:\n" " ret = 543;\n" " if (b) break;\n" " ret = 1;\n" " break;\n" " }" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int b) {\n" " int ret = 0;\n" " switch (a) {\n" " case 1:\n" " ret = 543;\n" " if (b) break;\n" " ret = 1;\n" " break;\n" " }" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:7]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout.str()); } void redundantInitialization() { setMultiline(); check("void f() {\n" " int err = -ENOMEM;\n" " err = dostuff();\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Redundant initialization for 'err'. The initialized value is overwritten before it is read.\n" "test.cpp:2:note:err is initialized\n" "test.cpp:3:note:err is overwritten\n", errout.str()); check("void f() {\n" " struct S s = {1,2,3};\n" " s = dostuff();\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Redundant initialization for 's'. The initialized value is overwritten before it is read.\n" "test.cpp:2:note:s is initialized\n" "test.cpp:3:note:s is overwritten\n", errout.str()); check("void f() {\n" " int *p = NULL;\n" " p = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); // "trivial" initialization => do not warn check("void f() {\n" " struct S s = {0};\n" " s = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace N { enum E {e0,e1}; }\n" "void f() {\n" " N::E e = N::e0;\n" // #9261 " e = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #10143 " std::shared_ptr i = g();\n" " h();\n" " i = nullptr;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void redundantMemWrite() { return; // FIXME: temporary hack // Simple tests // cppcheck-suppress unreachableCode - remove when code is enabled again check("void f() {\n" " char a[10];\n" " memcpy(a, foo, bar);\n" " memset(a, 0, bar);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void f() {\n" " char a[10];\n" " strcpy(a, foo);\n" " strncpy(a, 0, bar);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void f() {\n" " char a[10];\n" " sprintf(a, \"foo\");\n" " memmove(a, 0, bar);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void f(char *filename) {\n" " char *p = strrchr(filename,'.');\n" " strcpy(p, \"foo\");\n" " dostuff(filename);\n" " strcpy(p, \"foo\");\n" "}"); ASSERT_EQUALS("", errout.str()); // Writing to different parts of a buffer check("void f(void* a) {\n" " memcpy(a, foo, bar);\n" " memset(a+5, 0, bar);\n" "}"); ASSERT_EQUALS("", errout.str()); // Use variable as second argument check("void f(void* a, void* b) {\n" " memset(a, 0, 5);\n" " memcpy(b, a, 5);\n" " memset(a, 1, 5);\n" "}"); ASSERT_EQUALS("", errout.str()); // strcat is special check("void f() {\n" " char a[10];\n" " strcpy(a, foo);\n" " strcat(a, bar);\n" // Not redundant " strcpy(a, x);\n" // Redundant "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); // Tests with function call between copy check("void f() {\n" " char a[10];\n" " snprintf(a, foo, bar);\n" " bar();\n" " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void* a;\n" "void f() {\n" " memset(a, 0, size);\n" " bar();\n" // Global variable might be accessed in bar() " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char a[10];\n" " memset(a, 0, size);\n" " bar();\n" " memset(a, 0, size);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", "", errout.str()); check("void bar(void* a) {}\n" "void f(void* a) {\n" " memset(a, 0, size);\n" " bar(a);\n" // Passed as argument " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("", errout.str()); // Branch tests check("void f(void* a) {\n" " memset(a, 0, size);\n" " if(x)\n" " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4455 - initialization of local buffer check("void f(void) {" " char buf[10];\n" " memset(buf, 0, 10);\n" " strcpy(buf, string);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " char buf[10] = {0};\n" " memset(buf, 0, 10);\n" " strcpy(buf, string);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'buf' is being written before its old content has been used.\n", errout.str()); // #5689 - use return value of strcpy check("int f(void* a) {\n" " int i = atoi(strcpy(a, foo));\n" " strncpy(a, 0, bar);\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); // #7175 - read+write check("void f() {\n" " char buf[100];\n" " strcpy(buf, x);\n" " strcpy(buf, dostuff(buf));\n" // <- read + write " strcpy(buf, x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char buf[100];\n" " strcpy(buf, x);\n" " strcpy(buf, dostuff(buf));\n" " strcpy(buf, x);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void varFuncNullUB() { // #4482 check("void a(...);\n" "void b() { a(NULL); }"); ASSERT_EQUALS("[test.cpp:2]: (portability) Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n", errout.str()); check("void a(char *p, ...);\n" "void b() { a(NULL, 2); }"); ASSERT_EQUALS("", errout.str()); } void checkPipeParameterSize() { // #3521 checkposix("void f(){\n" "int pipefd[1];\n" // <-- array of two integers is needed "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer 'pipefd' must have size of 2 integers if used as parameter of pipe().\n", errout.str()); checkposix("void f(){\n" "int pipefd[2];\n" "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(){\n" "int pipefd[20];\n" "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(){\n" "int pipefd[1];\n" // <-- array of two integers is needed "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer 'pipefd' must have size of 2 integers if used as parameter of pipe().\n", errout.str()); checkposix("void f(){\n" "int pipefd[2];\n" "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(){\n" "int pipefd[20];\n" "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid crash with pointer variable check("void foo (int* arrayPtr)\n" "{\n" " if (pipe (arrayPtr) < 0)\n" " {}\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid crash with pointer variable - for local variable on stack as well - see #4801 check("void foo() {\n" " int *cp;\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // test with unknown variable check("void foo() {\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid crash with pointer variable - for local variable on stack as well - see #4801 check("void foo() {\n" " int *cp;\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // test with unknown variable check("void foo() {\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkCastIntToCharAndBack() { // #160 // check getchar check("void f() {\n" "unsigned char c; c = getchar();\n" " while( c != EOF)\n" " {\n" " bar(c);\n" " c = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" "unsigned char c = getchar();\n" " while( EOF != c)\n" " {\n" " bar(c);\n" " } ;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" " unsigned char c; c = getchar();\n" " while( EOF != c )\n" " {\n" " bar(c);\n" " c = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" " unsigned char c;\n" " while( EOF != ( c = getchar() ) )\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" " int i; i = getchar();\n" " while( i != EOF)\n" " {\n" " bar(i);\n" " i = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i; i = getchar();\n" " while( EOF != i )\n" " {\n" " bar(i);\n" " i = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("", errout.str()); // check getc check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = getc (pFile);\n" "} while (c != EOF)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = getc (pFile);\n" "} while (EOF != c)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" "} while (i != EOF)" "}"); ASSERT_EQUALS("", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" "} while (EOF != i)" "}"); ASSERT_EQUALS("", errout.str()); // check fgetc check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = fgetc (pFile);\n" "} while (c != EOF)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "char c;\n" "do {\n" " c = fgetc (pFile);\n" "} while (EOF != c)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "signed char c;\n" "do {\n" " c = fgetc (pFile);\n" "} while (EOF != c)" "}"); ASSERT_EQUALS("", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = fgetc (pFile);\n" "} while (i != EOF)" "}"); ASSERT_EQUALS("", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = fgetc (pFile);\n" "} while (EOF != i)" "}"); ASSERT_EQUALS("", errout.str()); // cin.get() check("void f(){\n" " char ch; ch = std::cin.get();\n" " while (EOF != ch) {\n" " std::cout << ch;\n" " ch = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing cin.get() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f(){\n" " char ch; ch = std::cin.get();\n" " while (ch != EOF) {\n" " std::cout << ch;\n" " ch = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing cin.get() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f(){\n" " int i; i = std::cin.get();\n" " while ( EOF != i ) {\n" " std::cout << i;\n" " i = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(){\n" " int i; i = std::cin.get();\n" " while ( i != EOF ) {\n" " std::cout << i;\n" " i = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkCommaSeparatedReturn() { check("int fun(int a) {\n" " if (a < 0)\n" " return a++,\n" " do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Comma is used in return statement. The comma can easily be misread as a ';'.\n", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return a++, do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return a+5,\n" " do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Comma is used in return statement. The comma can easily be misread as a ';'.\n", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return a+5, do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return c::b;\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); // #4943 take care of C++11 initializer lists check("std::vector Bar() {\n" " return\n" " {\n" " { \"1\" },\n" " { \"2\" },\n" " { \"3\" }\n" " };\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); } void checkPassByReference() { // #8570 passByValue when std::move is used check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " explicit B(A a) : a(std::move(a)) {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" "};", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " explicit B(A a) : a{std::move(a)} {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" "};", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " B(A a, A a2) : a{std::move(a)}, a2{std::move(a2)} {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" " A a2;" "};", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " B(A a, A a2) : a{std::move(a)}, a2{a2} {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" " A a2;" "};", nullptr, false, false, true); ASSERT_EQUALS("[test.cpp:8]: (performance) Function parameter 'a2' should be passed by const reference.\n", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " B(A a, A a2) : a{std::move(a)}, a2(a2) {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" " A a2;" "};", nullptr, false, false, true); ASSERT_EQUALS("[test.cpp:8]: (performance) Function parameter 'a2' should be passed by const reference.\n", errout.str()); } void checkComparisonFunctionIsAlwaysTrueOrFalse() { // positive test check("bool f(int x){\n" " return isless(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isless(x,x) always evaluates to false.\n", errout.str()); check("bool f(int x){\n" " return isgreater(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isgreater(x,x) always evaluates to false.\n", errout.str()); check("bool f(int x){\n" " return islessgreater(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with islessgreater(x,x) always evaluates to false.\n", errout.str()); check("bool f(int x){\n" " return islessequal(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with islessequal(x,x) always evaluates to true.\n", errout.str()); check("bool f(int x){\n" " return isgreaterequal(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isgreaterequal(x,x) always evaluates to true.\n", errout.str()); // no warning should be reported for check("bool f(int x, int y){\n" " return isgreaterequal(x,y) && islessequal(x,y) && islessgreater(x,y) && isgreater(x,y) && isless(x,y);\n" "}"); ASSERT_EQUALS("", errout.str()); } void integerOverflow() { // 5895 // no signed integer overflow should happen check("void f(unsigned long long ull) {\n" " if (ull == 0x89504e470d0a1a0a || ull == 0x8a4d4e470d0a1a0a) ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantPointerOp() { check("int *f(int *x) {\n" " return &*x;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'x' - it's already a pointer.\n", errout.str()); check("int *f(int *y) {\n" " return &(*y);\n" "}\n", nullptr, false, true); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'y' - it's already a pointer.\n", errout.str()); // no warning for bitwise AND check("void f(const int *b) {\n" " int x = 0x20 & *b;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // No message for double pointers to structs check("void f(struct foo **my_struct) {\n" " char **pass_to_func = &(*my_struct)->buf;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // another double pointer to struct - with an array check("void f(struct foo **my_struct) {\n" " char **pass_to_func = &(*my_struct)->buf[10];\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // double pointer to array check("void f(char **ptr) {\n" " int *x = &(*ptr)[10];\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // function calls check("void f(Mutex *mut) {\n" " pthread_mutex_lock(&*mut);\n" "}\n", nullptr, false, false); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'mut' - it's already a pointer.\n", errout.str()); // make sure we got the AST match for "(" right check("void f(char *ptr) {\n" " if (&*ptr == NULL)\n" " return;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'ptr' - it's already a pointer.\n", errout.str()); // no warning for macros checkP("#define MUTEX_LOCK(m) pthread_mutex_lock(&(m))\n" "void f(struct mutex *mut) {\n" " MUTEX_LOCK(*mut);\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkP("#define B(op) bar(op)\n" "#define C(orf) B(&orf)\n" "void foo(const int * pkey) {\n" " C(*pkey);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void test_isSameExpression() { // see #5738 check("bool isInUnoIncludeFile(StringRef name) {" " return name.startswith(SRCDIR \"/com/\") || name.startswith(SRCDIR \"/uno/\");\n" "};", "test.cpp", false, false); ASSERT_EQUALS("", errout.str()); } void raceAfterInterlockedDecrement() { checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " whatever();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter)\n" " return;\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (!counter)\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter > 0)\n" " return;\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 < counter)\n" " return;\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter == 0)\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 == counter)\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 != counter)\n" " return;\n" " destroy()\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter != 0)\n" " return;\n" " destroy()\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter <= 0)\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 >= counter)\n" " destroy();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount)\n" " return;\n" " destroy();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (!newCount)\n" " destroy();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount > 0)\n" " return;\n" " destroy();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 < newCount)\n" " return;\n" " destroy();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount == 0)\n" " destroy();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 == newCount)\n" " destroy();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 != newCount)\n" " return;\n" " destroy()\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount != 0)\n" " return;\n" " destroy()\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount <= 0)\n" " destroy();\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 >= newCount)\n" " destroy;\n" "}"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else {\n" " return counter;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (::InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else {\n" " return counter;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " }\n" " return counter;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (::InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " }\n" " return counter;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else\n" " return counter;\n" " \n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (::InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else\n" " return counter;\n" " \n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); } void testUnusedLabel() { check("void f() {\n" " label:\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Label 'label' is not used.\n", errout.str()); check("void f() {\n" " label:\n" " foo();\n" " goto label;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " label:\n" " foo();\n" " goto label;\n" "}\n" "void g() {\n" " label:\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Label 'label' is not used.\n", errout.str()); check("void f() {\n" " switch(a) {\n" " default:\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " class X {\n" " protected:\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " class X {\n" " my_protected:\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int test(char art) {\n" " switch (art) {\n" " caseZERO:\n" " return 0;\n" " case1:\n" " return 1;\n" " case 2:\n" " return 2;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Label 'caseZERO' is not used. Should this be a 'case' of the enclosing switch()?\n" "[test.cpp:5]: (warning) Label 'case1' is not used. Should this be a 'case' of the enclosing switch()?\n", errout.str()); check("int test(char art) {\n" " switch (art) {\n" " case 2:\n" " return 2;\n" " }\n" " label:\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Label 'label' is not used.\n", errout.str()); } void testEvaluationOrder() { check("void f() {\n" " int x = dostuff();\n" " return x + x++;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 'x+x++' depends on order of evaluation of side effects\n", errout.str()); // #7226 check("long int f1(const char *exp) {\n" " return strtol(++exp, (char **)&exp, 10);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); check("long int f1(const char *exp) {\n" " return dostuff(++exp, exp, 10);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (error) Expression '++exp,exp' depends on order of evaluation of side effects\n", errout.str()); check("void f() {\n" " int a;\n" " while (a=x(), a==123) {}\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); // # 8717 check("void f(int argc, char *const argv[]) {\n" " char **local_argv = safe_malloc(sizeof (*local_argv));\n" " int local_argc = 0;\n" " local_argv[local_argc++] = argv[0];\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 0;\n" " return 0 + x++;\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " int a[10];\n" " a[x+y] = a[y+x]++;;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 'a[x+y]=a[y+x]++' depends on order of evaluation of side effects\n", errout.str()); } void testEvaluationOrderSelfAssignment() { // self assignment check("void f() {\n" " int x = x = y + 1;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); } void testEvaluationOrderMacro() { // macro, don't bailout (#7233) checkP("#define X x\n" "void f(int x) {\n" " return x + X++;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 'x+x++' depends on order of evaluation of side effects\n", errout.str()); } void testEvaluationOrderSequencePointsFunctionCall() { // FP check("void f(int id) {\n" " id = dostuff(id += 42);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); // FN check("void f(int id) {\n" " id = id + dostuff(id += 42);\n" "}", "test.c"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void testEvaluationOrderSequencePointsComma() { check("int f(void) {\n" " int t;\n" " return (unsigned char)(t=1,t^c);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " int t;\n" " dostuff(t=1,t^c);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 't=1,t^c' depends on order of evaluation of side effects\n", errout.str()); check("void f(void) {\n" " int t;\n" " dostuff((t=1,t),2);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); // #8230 check("void hprf(const char* fp) {\n" " do\n" " ;\n" " while (++fp, (*fp) <= 0177);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void hprf(const char* fp) {\n" " do\n" " ;\n" " while (i++, ++fp, (*fp) <= 0177);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f(const char* fp) {\n" " do\n" " ;\n" " while (f(++fp, (*fp) <= 7));\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Expression '++fp,(*fp)<=7' depends on order of evaluation of side effects\n", errout.str()); } void testEvaluationOrderSizeof() { check("void f(char *buf) {\n" " dostuff(buf++, sizeof(*buf));" "}", "test.c"); ASSERT_EQUALS("", errout.str()); } void testUnsignedLessThanZero() { check("struct d {\n" " unsigned n;\n" "};\n" "void f(void) {\n" " struct d d;\n" " d.n = 3;\n" "\n" " if (d.n < 0) {\n" " return;\n" " }\n" "\n" " if (0 > d.n) {\n" " return;\n" " }\n" "}", "test.c"); ASSERT_EQUALS("[test.c:8]: (style) Checking if unsigned expression 'd.n' is less than zero.\n" "[test.c:12]: (style) Checking if unsigned expression 'd.n' is less than zero.\n", errout.str()); } void doubleMove1() { check("void g(A a);\n" "void f() {\n" " A a;\n" " g(std::move(a));\n" " g(std::move(a));\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n", errout.str()); } void doubleMoveMemberInitialization1() { check("class A\n" "{\n" " A(B && b)\n" " :b1(std::move(b))\n" " {\n" " b2 = std::move(b);\n" " }\n" " B b1;\n" " B b2;\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Access of moved variable 'b'.\n", errout.str()); } void doubleMoveMemberInitialization2() { check("class A\n" "{\n" " A(B && b)\n" " :b1(std::move(b)),\n" " b2(std::move(b))\n" " {}\n" " B b1;\n" " B b2;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'b'.\n", errout.str()); } void moveAndAssign1() { check("A g(A a);\n" "void f() {\n" " A a;\n" " a = g(std::move(a));\n" " a = g(std::move(a));\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndAssign2() { check("A g(A a);\n" "void f() {\n" " A a;\n" " B b = g(std::move(a));\n" " C c = g(std::move(a));\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAssignMoveAssign() { check("void h(A a);\n" "void f() {" " A a;\n" " g(std::move(a));\n" " h(a);\n" " a = b;\n" " h(a);\n" " g(std::move(a));\n" " h(a);\n" " a = b;\n" " h(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'a'.\n" "[test.cpp:8]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndReset1() { check("A g(A a);\n" "void f() {\n" " A a;\n" " a.reset(g(std::move(a)));\n" " a.reset(g(std::move(a)));\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndReset2() { check("A g(A a);\n" "void f() {\n" " A a;\n" " A b;\n" " A c;\n" " b.reset(g(std::move(a)));\n" " c.reset(g(std::move(a)));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveResetMoveReset() { check("void h(A a);\n" "void f() {" " A a;\n" " g(std::move(a));\n" " h(a);\n" " a.reset(b);\n" " h(a);\n" " g(std::move(a));\n" " h(a);\n" " a.reset(b);\n" " h(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'a'.\n" "[test.cpp:8]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndFunctionParameter() { check("void g(A a);\n" "void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n" "[test.cpp:6]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndFunctionParameterReference() { check("void g(A & a);\n" "void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndFunctionParameterConstReference() { check("void g(A const & a);\n" "void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n" "[test.cpp:6]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndFunctionParameterUnknown() { check("void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Access of moved variable 'a'.\n" "[test.cpp:5]: (warning, inconclusive) Access of moved variable 'a'.\n", errout.str()); } void moveAndReturn() { check("int f(int i) {\n" " A a;\n" " A b;\n" " g(std::move(a));\n" " if (i)\n" " return g(std::move(b));\n" " return h(std::move(a),std::move(b));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndClear() { check("void f() {\n" " V v;\n" " g(std::move(v));\n" " v.clear();\n" " if (v.empty()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void movedPointer() { check("void f() {\n" " P p;\n" " g(std::move(p));\n" " x = p->x;\n" " y = p->y;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'p'.\n" "[test.cpp:5]: (warning) Access of moved variable 'p'.\n", errout.str()); } void moveAndAddressOf() { check("void f() {\n" " std::string s1 = x;\n" " std::string s2 = std::move(s1);\n" " p = &s1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void partiallyMoved() { check("void f() {\n" " A a;\n" " gx(std::move(a).x());\n" " gy(std::move(a).y());\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndLambda() { check("void f() {\n" " A a;\n" " auto h = [a=std::move(a)](){return g(std::move(a));};" " b = a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void forwardAndUsed() { Settings keepTemplates; keepTemplates.checkUnusedTemplates = true; check("template\n" "void f(T && t) {\n" " g(std::forward(t));\n" " T s = t;\n" "}", &keepTemplates); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of forwarded variable 't'.\n", errout.str()); } void funcArgNamesDifferent() { check("void func1(int a, int b, int c);\n" "void func1(int a, int b, int c) { }\n" "void func2(int a, int b, int c);\n" "void func2(int A, int B, int C) { }\n" "class Fred {\n" " void func1(int a, int b, int c);\n" " void func2(int a, int b, int c);\n" " void func3(int a = 0, int b = 0, int c = 0);\n" " void func4(int a = 0, int b = 0, int c = 0);\n" "};\n" "void Fred::func1(int a, int b, int c) { }\n" "void Fred::func2(int A, int B, int C) { }\n" "void Fred::func3(int a, int b, int c) { }\n" "void Fred::func4(int A, int B, int C) { }"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 1 names different: declaration 'a' definition 'A'.\n" "[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 2 names different: declaration 'b' definition 'B'.\n" "[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 3 names different: declaration 'c' definition 'C'.\n" "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 1 names different: declaration 'a' definition 'A'.\n" "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 2 names different: declaration 'b' definition 'B'.\n" "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 3 names different: declaration 'c' definition 'C'.\n" "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 1 names different: declaration 'a' definition 'A'.\n" "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 2 names different: declaration 'b' definition 'B'.\n" "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 3 names different: declaration 'c' definition 'C'.\n", errout.str()); } void funcArgOrderDifferent() { check("void func1(int a, int b, int c);\n" "void func1(int a, int b, int c) { }\n" "void func2(int a, int b, int c);\n" "void func2(int c, int b, int a) { }\n" "void func3(int, int b, int c);\n" "void func3(int c, int b, int a) { }\n" "class Fred {\n" " void func1(int a, int b, int c);\n" " void func2(int a, int b, int c);\n" " void func3(int a = 0, int b = 0, int c = 0);\n" " void func4(int, int b = 0, int c = 0);\n" "};\n" "void Fred::func1(int a, int b, int c) { }\n" "void Fred::func2(int c, int b, int a) { }\n" "void Fred::func3(int c, int b, int a) { }\n" "void Fred::func4(int c, int b, int a) { }\n", nullptr, false, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Function 'func2' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" "[test.cpp:5] -> [test.cpp:6]: (warning) Function 'func3' argument order different: declaration ', b, c' definition 'c, b, a'\n" "[test.cpp:9] -> [test.cpp:14]: (warning) Function 'func2' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" "[test.cpp:10] -> [test.cpp:15]: (warning) Function 'func3' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" "[test.cpp:11] -> [test.cpp:16]: (warning) Function 'func4' argument order different: declaration ', b, c' definition 'c, b, a'\n", errout.str()); } // #7846 - Syntax error when using C++11 braced-initializer in default argument void cpp11FunctionArgInit() { // syntax error is not expected ASSERT_NO_THROW(check("\n void foo(int declaration = {}) {" "\n for (int i = 0; i < 10; i++) {}\n" "\n }" "\n ")); ASSERT_EQUALS("", errout.str()); } void shadowVariables() { check("int x;\n" "void f() { int x; }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable \'x\' shadows outer variable\n", errout.str()); check("int x();\n" "void f() { int x; }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable \'x\' shadows outer function\n", errout.str()); check("struct C {\n" " C(int x) : x(x) {}\n" // <- we do not want a FP here " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (cond) {int x;}\n" // <- not a shadow variable " int x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int size() {\n" " int size;\n" // <- not a shadow variable "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #8954 - lambda " int x;\n" " auto f = [](){ int x; }" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { int x; }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Local variable 'x' shadows outer argument\n", errout.str()); check("class C { C(); void foo() { static int C = 0; } }"); // #9195 - shadow constructor ASSERT_EQUALS("", errout.str()); check("struct C {\n" // #10091 - shadow destructor " ~C();\n" " void f() {\n" " bool C{};\n" " }\n" "};\n" "C::~C() = default;"); ASSERT_EQUALS("", errout.str()); // 10752 - no check("struct S {\n" " int i;\n" "\n" " static int foo() {\n" " int i = 0;\n" " return i;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void knownArgument() { check("void g(int);\n" "void f(int x) {\n" " g((x & 0x01) >> 7);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Argument '(x&0x01)>>7' to function g is always 0. It does not matter what value 'x' has.\n", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g((int)((x & 0x01) >> 7));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Argument '(int)((x&0x01)>>7)' to function g is always 0. It does not matter what value 'x' has.\n", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g(0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void h() { return 1; }\n" "void f(int x) {\n" " g(h());\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g(std::strlen(\"a\"));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g((int)0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(Foo *);\n" "void f() {\n" " g(reinterpret_cast(0));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f(int x) {\n" " x = 0;\n" " g(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f() {\n" " const int x = 0;\n" " g(x + 1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f() {\n" " char i = 1;\n" " g(static_cast(i));\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *yytext;\n" "void re_init_scanner() {\n" " int size = 256;\n" " yytext = xmalloc(size * sizeof *yytext);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *c) {\n" " if (*c == '+' && (operand || !isalnum(*c))) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #8986 check("void f(int);\n" "void g() {\n" " const int x[] = { 10, 10 };\n" " f(x[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int);\n" "void g() {\n" " int x[] = { 10, 10 };\n" " f(x[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { int x; };" "void g(int);\n" "void f(int x) {\n" " A y;\n" " y.x = 1;\n" " g(y.x);\n" "}"); ASSERT_EQUALS("", errout.str()); // allow known argument value in assert call check("void g(int);\n" "void f(int x) {\n" " ASSERT((int)((x & 0x01) >> 7));\n" "}"); ASSERT_EQUALS("", errout.str()); // #9905 - expression that does not use integer calculation at all check("void foo() {\n" " const std::string heading = \"Interval\";\n" " std::cout << std::setw(heading.length());\n" "}"); ASSERT_EQUALS("", errout.str()); // #9909 - struct member with known value check("struct LongStack {\n" " int maxsize;\n" "};\n" "\n" "void growLongStack(LongStack* self) {\n" " self->maxsize = 32;\n" " dostuff(self->maxsize * sizeof(intptr_t));\n" "}"); ASSERT_EQUALS("", errout.str()); } void knownArgumentHiddenVariableExpression() { // #9914 - variable expression is explicitly hidden check("void f(int x) {\n" " dostuff(x && false);\n" " dostuff(false && x);\n" " dostuff(x || true);\n" " dostuff(true || x);\n" " dostuff(x * 0);\n" " dostuff(0 * x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Argument 'false&&x' to function dostuff is always 0. Constant literal calculation disable/hide variable expression 'x'.\n" "[test.cpp:5]: (style) Argument 'true||x' to function dostuff is always 1. Constant literal calculation disable/hide variable expression 'x'.\n" "[test.cpp:6]: (style) Argument 'x*0' to function dostuff is always 0. Constant literal calculation disable/hide variable expression 'x'.\n" "[test.cpp:7]: (style) Argument '0*x' to function dostuff is always 0. Constant literal calculation disable/hide variable expression 'x'.\n", errout.str()); } void knownArgumentTernaryOperator() { // #10374 check("void f(bool a, bool b) {\n" " const T* P = nullptr; \n" " long N = 0; \n" " const bool c = foo(); \n" " bar(P, N); \n" " if (c ? a : b)\n" " baz(P, N); \n" "}"); ASSERT_EQUALS("", errout.str()); } void checkComparePointers() { check("int f() {\n" " const int foo[1] = {0};\n" " const int bar[1] = {0};\n" " int diff = 0;\n" " if(foo > bar) {\n" " diff = 1;\n" " }\n" " return diff;\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:5] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:5]: (error) Comparing pointers that point to different objects\n", errout.str()); check("bool f() {\n" " int x = 0;\n" " int y = 0;\n" " int* xp = &x;\n" " int* yp = &y;\n" " return xp > yp;\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:6]: (error) Comparing pointers that point to different objects\n", errout.str()); check("bool f() {\n" " int x = 0;\n" " int y = 1;\n" " return &x > &y;\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:4]: (error) Comparing pointers that point to different objects\n", errout.str()); check("struct A {int data;};\n" "bool f() {\n" " A x;\n" " A y;\n" " int* xp = &x.data;\n" " int* yp = &y.data;\n" " return xp > yp;\n" "}"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:5] -> [test.cpp:1] -> [test.cpp:6] -> [test.cpp:7]: (error) Comparing pointers that point to different objects\n", errout.str()); check("struct A {int data;};\n" "bool f(A ix, A iy) {\n" " A* x = &ix;\n" " A* y = &iy;\n" " int* xp = &x->data;\n" " int* yp = &y->data;\n" " return xp > yp;\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:2] -> [test.cpp:4] -> [test.cpp:6] -> [test.cpp:7]: (error) Comparing pointers that point to different objects\n", errout.str()); check("bool f(int * xp, int* yp) {\n" " return &xp > &yp;\n" "}"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:2]: (error) Comparing pointers that point to different objects\n", errout.str()); check("int f() {\n" " int x = 0;\n" " int y = 1;\n" " return &x - &y;\n" "}"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:4]: (error) Subtracting pointers that point to different objects\n", errout.str()); check("bool f() {\n" " int x[2] = {1, 2}m;\n" " int* xp = &x[0];\n" " int* yp = &x[1];\n" " return xp > yp;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(const int * xp, const int* yp) {\n" " return xp > yp;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(const int & x, const int& y) {\n" " return &x > &y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& g();\n" "bool f() {\n" " const int& x = g();\n" " const int& y = g();\n" " const int* xp = &x;\n" " const int* yp = &y;\n" " return xp > yp;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {int data;};\n" "bool f(A ix) {\n" " A* x = &ix;\n" " A* y = x;\n" " int* xp = &x->data;\n" " int* yp = &y->data;\n" " return xp > yp;\n" "}"); ASSERT_EQUALS("", errout.str()); } void unusedVariableValueTemplate() { check("#include \n" "class A\n" "{\n" "public:\n" " class Hash\n" " {\n" " public:\n" " std::size_t operator()(const A& a) const\n" " {\n" " (void)a;\n" " return 0;\n" " }\n" " };\n" "};\n" "namespace std\n" "{\n" " template <>\n" " struct hash\n" " {\n" " std::size_t operator()(const A& a) const noexcept\n" " {\n" " return A::Hash{}(a);\n" " }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void moduloOfOne() { check("void f(unsigned int x) {\n" " int y = x % 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Modulo of one is always equal to zero\n", errout.str()); check("void f() {\n" " for (int x = 1; x < 10; x++) {\n" " int y = 100 % x;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void sameExpressionPointers() { check("int f(int *i);\n" "void g(int *a, const int *b) {\n" " int c = *a;\n" " f(a);\n" " if (b && c != *a) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void checkOverlappingWrite() { // union check("void foo() {\n" " union { int i; float f; } u;\n" " u.i = 0;\n" " u.i = u.f;\n" // <- error "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Overlapping read/write of union is undefined behavior\n", errout.str()); // memcpy check("void foo() {\n" " char a[10];\n" " memcpy(&a[5], &a[4], 2u);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in memcpy() is undefined behavior\n", errout.str()); check("void foo() {\n" " char a[10];\n" " memcpy(a+5, a+4, 2u);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in memcpy() is undefined behavior\n", errout.str()); check("void foo() {\n" " char a[10];\n" " memcpy(a, a+1, 2u);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in memcpy() is undefined behavior\n", errout.str()); check("void foo() {\n" " char a[8];\n" " memcpy(&a[0], &a[4], 4u);\n" "}"); ASSERT_EQUALS("", errout.str()); // wmemcpy check("void foo() {\n" " wchar_t a[10];\n" " wmemcpy(&a[5], &a[4], 2u);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in wmemcpy() is undefined behavior\n", errout.str()); check("void foo() {\n" " wchar_t a[10];\n" " wmemcpy(a+5, a+4, 2u);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in wmemcpy() is undefined behavior\n", errout.str()); check("void foo() {\n" " wchar_t a[10];\n" " wmemcpy(a, a+1, 2u);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in wmemcpy() is undefined behavior\n", errout.str()); // strcpy check("void foo(char *ptr) {\n" " strcpy(ptr, ptr);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Overlapping read/write in strcpy() is undefined behavior\n", errout.str()); } void constVariableArrayMember() { // #10371 check("class Foo {\n" "public:\n" " Foo();\n" " int GetVal() const { return m_Arr[0]; }\n" " int m_Arr[1];\n" "};\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestOther) cppcheck-2.7/test/testpath.cpp000066400000000000000000000127031417746362400165020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "path.h" #include "testsuite.h" #include #include class TestPath : public TestFixture { public: TestPath() : TestFixture("TestPath") {} private: void run() OVERRIDE { TEST_CASE(removeQuotationMarks); TEST_CASE(acceptFile); TEST_CASE(getCurrentPath); TEST_CASE(isAbsolute); TEST_CASE(getRelative); TEST_CASE(is_c); TEST_CASE(is_cpp); TEST_CASE(get_path_from_filename); } void removeQuotationMarks() const { // Path::removeQuotationMarks() ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("index.cpp")); ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("\"index.cpp")); ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("index.cpp\"")); ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("\"index.cpp\"")); ASSERT_EQUALS("path to/index.cpp", Path::removeQuotationMarks("\"path to\"/index.cpp")); ASSERT_EQUALS("path to/index.cpp", Path::removeQuotationMarks("\"path to/index.cpp\"")); ASSERT_EQUALS("the/path to/index.cpp", Path::removeQuotationMarks("the/\"path to\"/index.cpp")); ASSERT_EQUALS("the/path to/index.cpp", Path::removeQuotationMarks("\"the/path to/index.cpp\"")); } void acceptFile() const { ASSERT(Path::acceptFile("index.cpp")); ASSERT(Path::acceptFile("index.invalid.cpp")); ASSERT(Path::acceptFile("index.invalid.Cpp")); ASSERT(Path::acceptFile("index.invalid.C")); ASSERT(Path::acceptFile("index.invalid.C++")); ASSERT(Path::acceptFile("index.")==false); ASSERT(Path::acceptFile("index")==false); ASSERT(Path::acceptFile("")==false); ASSERT(Path::acceptFile("C")==false); // don't accept any headers ASSERT_EQUALS(false, Path::acceptFile("index.h")); ASSERT_EQUALS(false, Path::acceptFile("index.hpp")); } void getCurrentPath() const { ASSERT_EQUALS(true, Path::isAbsolute(Path::getCurrentPath())); } void isAbsolute() const { #ifdef _WIN32 ASSERT_EQUALS(true, Path::isAbsolute("C:\\foo\\bar")); ASSERT_EQUALS(true, Path::isAbsolute("C:/foo/bar")); ASSERT_EQUALS(true, Path::isAbsolute("\\\\foo\\bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo\\bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo/bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo.cpp")); ASSERT_EQUALS(false, Path::isAbsolute("C:foo.cpp")); ASSERT_EQUALS(false, Path::isAbsolute("C:foo\\bar.cpp")); ASSERT_EQUALS(false, Path::isAbsolute("bar.cpp")); TODO_ASSERT_EQUALS(true, false, Path::isAbsolute("\\")); #else ASSERT_EQUALS(true, Path::isAbsolute("/foo/bar")); ASSERT_EQUALS(true, Path::isAbsolute("/")); ASSERT_EQUALS(false, Path::isAbsolute("foo/bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo.cpp")); #endif } void getRelative() const { const std::vector basePaths = { "", // Don't crash with empty paths "C:/foo", "C:/bar/", "C:/test.cpp" }; ASSERT_EQUALS("x.c", Path::getRelativePath("C:/foo/x.c", basePaths)); ASSERT_EQUALS("y.c", Path::getRelativePath("C:/bar/y.c", basePaths)); ASSERT_EQUALS("foo/y.c", Path::getRelativePath("C:/bar/foo/y.c", basePaths)); ASSERT_EQUALS("C:/test.cpp", Path::getRelativePath("C:/test.cpp", basePaths)); ASSERT_EQUALS("C:/foobar/test.cpp", Path::getRelativePath("C:/foobar/test.cpp", basePaths)); } void is_c() const { ASSERT(Path::isC("index.cpp")==false); ASSERT(Path::isC("")==false); ASSERT(Path::isC("c")==false); ASSERT(Path::isC("index.c")); ASSERT(Path::isC("C:\\foo\\index.c")); // In unix .C is considered C++ #ifdef _WIN32 ASSERT_EQUALS(true, Path::isC("C:\\foo\\index.C")); #else ASSERT_EQUALS(false, Path::isC("C:\\foo\\index.C")); #endif } void is_cpp() const { ASSERT(Path::isCPP("index.c")==false); // In unix .C is considered C++ #ifdef _WIN32 ASSERT_EQUALS(false, Path::isCPP("index.C")); #else ASSERT_EQUALS(true, Path::isCPP("index.C")); #endif ASSERT(Path::isCPP("index.cpp")); ASSERT(Path::isCPP("C:\\foo\\index.cpp")); ASSERT(Path::isCPP("C:\\foo\\index.Cpp")); } void get_path_from_filename() const { ASSERT_EQUALS("", Path::getPathFromFilename("index.h")); ASSERT_EQUALS("/tmp/", Path::getPathFromFilename("/tmp/index.h")); ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/index.h")); ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/")); } }; REGISTER_TEST(TestPath) cppcheck-2.7/test/testpathmatch.cpp000066400000000000000000000137421417746362400175230ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "pathmatch.h" #include "testsuite.h" #include #include class TestPathMatch : public TestFixture { public: TestPathMatch() : TestFixture("TestPathMatch") , emptyMatcher(std::vector()) , srcMatcher(std::vector(1, "src/")) , fooCppMatcher(std::vector(1, "foo.cpp")) , srcFooCppMatcher(std::vector(1, "src/foo.cpp")) {} private: const PathMatch emptyMatcher; const PathMatch srcMatcher; const PathMatch fooCppMatcher; const PathMatch srcFooCppMatcher; void run() OVERRIDE { TEST_CASE(emptymaskemptyfile); TEST_CASE(emptymaskpath1); TEST_CASE(emptymaskpath2); TEST_CASE(emptymaskpath3); TEST_CASE(onemaskemptypath); TEST_CASE(onemasksamepath); TEST_CASE(onemasksamepathdifferentcase); TEST_CASE(onemasksamepathwithfile); TEST_CASE(onemaskshorterpath); TEST_CASE(onemaskdifferentdir1); TEST_CASE(onemaskdifferentdir2); TEST_CASE(onemaskdifferentdir3); TEST_CASE(onemaskdifferentdir4); TEST_CASE(onemasklongerpath1); TEST_CASE(onemasklongerpath2); TEST_CASE(onemasklongerpath3); TEST_CASE(twomasklongerpath1); TEST_CASE(twomasklongerpath2); TEST_CASE(twomasklongerpath3); TEST_CASE(twomasklongerpath4); TEST_CASE(filemask1); TEST_CASE(filemaskdifferentcase); TEST_CASE(filemask2); TEST_CASE(filemask3); TEST_CASE(filemaskpath1); TEST_CASE(filemaskpath2); TEST_CASE(filemaskpath3); TEST_CASE(filemaskpath4); } // Test empty PathMatch void emptymaskemptyfile() const { ASSERT(!emptyMatcher.match("")); } void emptymaskpath1() const { ASSERT(!emptyMatcher.match("src/")); } void emptymaskpath2() const { ASSERT(!emptyMatcher.match("../src/")); } void emptymaskpath3() const { ASSERT(!emptyMatcher.match("/home/user/code/src/")); } // Test PathMatch containing "src/" void onemaskemptypath() const { ASSERT(!srcMatcher.match("")); } void onemasksamepath() const { ASSERT(srcMatcher.match("src/")); } void onemasksamepathdifferentcase() const { std::vector masks(1, "sRc/"); PathMatch match(masks, false); ASSERT(match.match("srC/")); } void onemasksamepathwithfile() const { ASSERT(srcMatcher.match("src/file.txt")); } void onemaskshorterpath() const { const std::string longerExclude("longersrc/"); const std::string shorterToMatch("src/"); ASSERT(shorterToMatch.length() < longerExclude.length()); PathMatch match(std::vector(1, longerExclude)); ASSERT(match.match(longerExclude)); ASSERT(!match.match(shorterToMatch)); } void onemaskdifferentdir1() const { ASSERT(!srcMatcher.match("srcfiles/file.txt")); } void onemaskdifferentdir2() const { ASSERT(!srcMatcher.match("proj/srcfiles/file.txt")); } void onemaskdifferentdir3() const { ASSERT(!srcMatcher.match("proj/mysrc/file.txt")); } void onemaskdifferentdir4() const { ASSERT(!srcMatcher.match("proj/mysrcfiles/file.txt")); } void onemasklongerpath1() const { ASSERT(srcMatcher.match("/tmp/src/")); } void onemasklongerpath2() const { ASSERT(srcMatcher.match("src/module/")); } void onemasklongerpath3() const { ASSERT(srcMatcher.match("project/src/module/")); } void twomasklongerpath1() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(!match.match("project/")); } void twomasklongerpath2() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(match.match("project/src/")); } void twomasklongerpath3() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(match.match("project/module/")); } void twomasklongerpath4() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(match.match("project/src/module/")); } // Test PathMatch containing "foo.cpp" void filemask1() const { ASSERT(fooCppMatcher.match("foo.cpp")); } void filemaskdifferentcase() const { std::vector masks(1, "foo.cPp"); PathMatch match(masks, false); ASSERT(match.match("fOo.cpp")); } void filemask2() const { ASSERT(fooCppMatcher.match("../foo.cpp")); } void filemask3() const { ASSERT(fooCppMatcher.match("src/foo.cpp")); } // Test PathMatch containing "src/foo.cpp" void filemaskpath1() const { ASSERT(srcFooCppMatcher.match("src/foo.cpp")); } void filemaskpath2() const { ASSERT(srcFooCppMatcher.match("proj/src/foo.cpp")); } void filemaskpath3() const { ASSERT(!srcFooCppMatcher.match("foo.cpp")); } void filemaskpath4() const { ASSERT(!srcFooCppMatcher.match("bar/foo.cpp")); } }; REGISTER_TEST(TestPathMatch) cppcheck-2.7/test/testplatform.cpp000066400000000000000000000357131417746362400174000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2021 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "platform.h" #include "testsuite.h" #include class TestPlatform : public TestFixture { public: TestPlatform() : TestFixture("TestPlatform") {} private: void run() OVERRIDE { TEST_CASE(empty); TEST_CASE(valid_config_native_1); TEST_CASE(valid_config_native_2); TEST_CASE(valid_config_file_1); TEST_CASE(valid_config_file_2); TEST_CASE(valid_config_file_3); TEST_CASE(valid_config_file_4); TEST_CASE(invalid_config_file_1); // TEST_CASE(empty_elements); // TODO: Trac issue #8409 } static bool readPlatform(cppcheck::Platform& platform, const char* xmldata) { tinyxml2::XMLDocument doc; doc.Parse(xmldata); return platform.loadFromXmlDocument(&doc); } void empty() const { // An empty platform file does not change values, only the type. const char xmldata[] = "\n"; cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win64)); ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(cppcheck::Platform::PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); } void valid_config_native_1() { // Verify if native Win32A platform is loaded correctly cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win32A)); ASSERT_EQUALS(cppcheck::Platform::Win32A, platform.platformType); ASSERT(platform.isWindowsPlatform()); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); ASSERT_EQUALS(16, platform.short_bit); ASSERT_EQUALS(32, platform.int_bit); ASSERT_EQUALS(32, platform.long_bit); ASSERT_EQUALS(64, platform.long_long_bit); } void valid_config_native_2() { // Verify if native Unix64 platform is loaded correctly cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Unix64)); ASSERT_EQUALS(cppcheck::Platform::Unix64, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(32, platform.int_bit); ASSERT_EQUALS(8, platform.sizeof_long); ASSERT_EQUALS(64, platform.long_bit); } void valid_config_file_1() { // Valid platform configuration with all possible values specified. // Similar to the avr8 platform file. const char xmldata[] = "\n" "\n" " 8\n" " unsigned\n" " \n" " 1\n" " 2\n" " 2\n" " 4\n" " 8\n" " 4\n" " 4\n" " 4\n" " 2\n" " 2\n" " 2\n" " \n" " "; cppcheck::Platform platform; ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('u', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(2, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); ASSERT_EQUALS(4, platform.sizeof_float); ASSERT_EQUALS(4, platform.sizeof_double); ASSERT_EQUALS(4, platform.sizeof_long_double); ASSERT_EQUALS(2, platform.sizeof_pointer); ASSERT_EQUALS(2, platform.sizeof_size_t); ASSERT_EQUALS(2, platform.sizeof_wchar_t); ASSERT_EQUALS(16, platform.short_bit); ASSERT_EQUALS(16, platform.int_bit); ASSERT_EQUALS(32, platform.long_bit); ASSERT_EQUALS(64, platform.long_long_bit); } void valid_config_file_2() { // Valid platform configuration with all possible values specified and // char_bit > 8. const char xmldata[] = "\n" "\n" " 20\n" " signed\n" " \n" " 1\n" " 2\n" " 3\n" " 4\n" " 5\n" " 6\n" " 7\n" " 8\n" " 9\n" " 10\n" " 11\n" " \n" " "; cppcheck::Platform platform; ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(20, platform.char_bit); ASSERT_EQUALS('s', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(3, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(5, platform.sizeof_long_long); ASSERT_EQUALS(6, platform.sizeof_float); ASSERT_EQUALS(7, platform.sizeof_double); ASSERT_EQUALS(8, platform.sizeof_long_double); ASSERT_EQUALS(9, platform.sizeof_pointer); ASSERT_EQUALS(10, platform.sizeof_size_t); ASSERT_EQUALS(11, platform.sizeof_wchar_t); ASSERT_EQUALS(40, platform.short_bit); ASSERT_EQUALS(60, platform.int_bit); ASSERT_EQUALS(80, platform.long_bit); ASSERT_EQUALS(100, platform.long_long_bit); } void valid_config_file_3() { // Valid platform configuration without any usable information. // Similar like an empty file. const char xmldata[] = "\n" "\n" " 8\n" " unsigned\n" " \n" " 1\n" " 2\n" " 3\n" " 4\n" " 5\n" " 6\n" " 7\n" " 8\n" " 9\n" " 10\n" " 11\n" " \n" " "; cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win64)); ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); } void valid_config_file_4() { // Valid platform configuration with all possible values specified and // set to 0. const char xmldata[] = "\n" "\n" " 0\n" " z\n" " \n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " \n" " "; cppcheck::Platform platform; ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(0, platform.char_bit); ASSERT_EQUALS('z', platform.defaultSign); ASSERT_EQUALS(0, platform.sizeof_bool); ASSERT_EQUALS(0, platform.sizeof_short); ASSERT_EQUALS(0, platform.sizeof_int); ASSERT_EQUALS(0, platform.sizeof_long); ASSERT_EQUALS(0, platform.sizeof_long_long); ASSERT_EQUALS(0, platform.sizeof_float); ASSERT_EQUALS(0, platform.sizeof_double); ASSERT_EQUALS(0, platform.sizeof_long_double); ASSERT_EQUALS(0, platform.sizeof_pointer); ASSERT_EQUALS(0, platform.sizeof_size_t); ASSERT_EQUALS(0, platform.sizeof_wchar_t); ASSERT_EQUALS(0, platform.short_bit); ASSERT_EQUALS(0, platform.int_bit); ASSERT_EQUALS(0, platform.long_bit); ASSERT_EQUALS(0, platform.long_long_bit); } void invalid_config_file_1() { // Invalid XML file: mismatching elements "boolt" vs "bool". const char xmldata[] = "\n" "\n" " 8\n" " unsigned\n" " \n" " 1\n" " 2\n" " 2\n" " 4\n" " 8\n" " 4\n" " 4\n" " 4\n" " 2\n" " 2\n" " 2\n" " \n" " "; cppcheck::Platform platform; ASSERT(!readPlatform(platform, xmldata)); } #if 0 // @TODO: Enable when Trac issue #8409 has been fixed void empty_elements() { // Valid platform configuration without any usable information. // Similar like an empty file. const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " "; cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win64)); ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); } #endif }; REGISTER_TEST(TestPlatform) cppcheck-2.7/test/testpostfixoperator.cpp000066400000000000000000000316421417746362400210210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkpostfixoperator.h" #include "config.h" #include "errortypes.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include class TestPostfixOperator : public TestFixture { public: TestPostfixOperator() : TestFixture("TestPostfixOperator") {} private: Settings settings; #define check(code) check_(code, __FILE__, __LINE__) void check_(const char code[], const char* file, int line) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for postfix operators.. CheckPostfixOperator checkPostfixOperator(&tokenizer, &settings, this); checkPostfixOperator.postfixOperator(); } void run() OVERRIDE { settings.severity.enable(Severity::performance); TEST_CASE(testsimple); TEST_CASE(testfor); TEST_CASE(testvolatile); TEST_CASE(testiterator); TEST_CASE(test2168); TEST_CASE(pointerSimplest); TEST_CASE(pointer); // #2321 - postincrement of pointer is OK TEST_CASE(testtemplate); // #4686 TEST_CASE(testmember); TEST_CASE(testcomma); TEST_CASE(testauto); // #8350 } void testsimple() { check("int main()\n" "{\n" " unsigned int k(0);\n" " std::cout << k << std::endl;\n" " k++;\n" " std::cout << k << std::endl;\n" " if(k) {\n" " k++;\n" " }\n" " std::cout << k << std::endl;\n" " k--;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " k++;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("struct K {};" "void foo()\n" "{\n" " K k(0);\n" " k++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("struct K {};\n" "void foo(K& k)\n" "{\n" " k++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("union K {};" "void foo()\n" "{\n" " K k(0);\n" " k++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(1);\n" " std::cout << k << std::endl;\n" " if(k) {\n" " k++;\n" " }\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(1);\n" " std::cout << k << std::endl;\n" " if(k) {\n" " ++k;\n" " }\n" " k++;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " k--;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " ++k;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " --k;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9042 check("template \n" "class c {\n" " int i = 0;\n" " c() { i--; }\n" "};\n" "template \n" "class s {};\n" "using BOOL = char;"); ASSERT_EQUALS("", errout.str()); } void testfor() { check("int main()\n" "{\n" " for ( unsigned int i=0; i <= 10; i++) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};\n" "int main()\n" "{\n" " for ( K i(0); i <= 10; i++) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};\n" "int main()\n" "{\n" " for ( K i(0); i <= 10; ++i) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};\n" "int main()\n" "{\n" " for ( K i(10); i > 1; i--) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};\n" "int main(int argc, char *argv[])\n" "{\n" " for ( K i=10; i > 1; --i) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testvolatile() { check("class K {};\n" "int main()\n" "{\n" " volatile K k(0);\n" " std::cout << k << std::endl;\n" " k++;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); } void testiterator() { check("class Base {};\n" "int main() {\n" " std::vector v;\n" " v.push_back(new Base());\n" " v.push_back(new Base());\n" " for (std::vector::iterator i=v.begin(); i!=v.end(); i++) {\n" " ;;\n" " }\n" " v.clear();\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("int main() {\n" " std::vector v;\n" " std::vector::iterator it;\n" " for( int i=0; i < 10; ++i ) v.push_back(i);\n" " unsigned int total = 0;\n" " it = v.begin();\n" " while( it != v.end() ) {\n" " total += *it;\n" " it++;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("int main() {\n" " std::vector v;\n" " std::vector::const_iterator it;\n" " for( int i=0; i < 10; ++i ) v.push_back(i);\n" " unsigned int total = 0;\n" " it = v.begin();\n" " while( it != v.end() ) {\n" " it++;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("int main() {\n" " std::vector v;\n" " std::vector::iterator it;\n" " for( int i=0; i < 10; ++i ) v.push_back(i);\n" " unsigned int total = 0;\n" " std::vector::reverse_iterator rit;\n" " rit= v.rend();\n" " while( rit != v.rbegin() ) {\n" " rit--;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); } void test2168() { check("--> declare allocator lock here\n" "int main(){}"); ASSERT_EQUALS("", errout.str()); } void pointerSimplest() { check("void f(int* p){\n" " p++;\n" " std::cout << *p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void pointer() { check("static struct class * ab;\n" "int * p;\n" "\n" "void f() {\n" " p++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testtemplate() { check("bool foo() {\n" " std::vector::iterator aIter(aImport.begin());\n" " aIter++;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); } void testmember() { check("bool foo() {\n" " class A {}; class B {A a;};\n" " B b;\n" " b.a++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("bool foo() {\n" " class A {}; class B {A a;};\n" " B b;\n" " foo(b.a++);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testcomma() { check("bool foo(int i) {\n" " class A {};\n" " A a;\n" " i++, a++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("bool foo(int i) {\n" " class A {};\n" " A a;\n" " foo(i, a++);\n" " foo(a++, i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testauto() { // #8350 check("enum class Color { Red = 0, Green = 1, };\n" "int fun(const Color color) {\n" " auto a = 0;\n" " for (auto i = static_cast(color); i < 10; i++) {\n" " a += i;\n" " }\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestPostfixOperator) cppcheck-2.7/test/testpreprocessor.cpp000066400000000000000000002564261417746362400203100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // The preprocessor that Cppcheck uses is a bit special. Instead of generating // the code for a known configuration, it generates the code for each configuration. #include "config.h" #include "errortypes.h" #include "platform.h" #include "preprocessor.h" #include "settings.h" #include "testsuite.h" #include #include #include #include #include #include #include #include #include #include class ErrorLogger; class TestPreprocessor : public TestFixture { public: TestPreprocessor() : TestFixture("TestPreprocessor") , preprocessor0(settings0, this) { settings0.severity.enable(Severity::information); } class OurPreprocessor : public Preprocessor { public: static std::string expandMacros(const char code[], ErrorLogger *errorLogger = nullptr) { std::istringstream istr(code); simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1 = simplecpp::TokenList(istr, files, "file.cpp", &outputList); std::map filedata; simplecpp::TokenList tokens2(files); simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI(), &outputList); if (errorLogger) { static Settings settings; Preprocessor p(settings, errorLogger); p.reportOutput(outputList, true); } return tokens2.stringify(); } }; private: Settings settings0; Preprocessor preprocessor0; void run() OVERRIDE { // The bug that started the whole work with the new preprocessor TEST_CASE(Bug2190219); TEST_CASE(error1); // #error => don't extract any code TEST_CASE(error2); // #error if symbol is not defined TEST_CASE(error3); TEST_CASE(error4); // #2919 - wrong filename is reported TEST_CASE(error5); TEST_CASE(error6); TEST_CASE(error7); TEST_CASE(error8); // #10170 -> previous #if configurations TEST_CASE(setPlatformInfo); // Handling include guards (don't create extra configuration for it) TEST_CASE(includeguard1); TEST_CASE(includeguard2); TEST_CASE(if0); TEST_CASE(if1); TEST_CASE(elif); TEST_CASE(if_cond1); TEST_CASE(if_cond2); TEST_CASE(if_cond3); TEST_CASE(if_cond4); TEST_CASE(if_cond5); TEST_CASE(if_cond6); TEST_CASE(if_cond8); TEST_CASE(if_cond9); TEST_CASE(if_cond10); TEST_CASE(if_cond11); TEST_CASE(if_cond12); TEST_CASE(if_cond13); TEST_CASE(if_cond14); TEST_CASE(if_or_1); TEST_CASE(if_or_2); TEST_CASE(if_macro_eq_macro); // #3536 TEST_CASE(ticket_3675); TEST_CASE(ticket_3699); TEST_CASE(ticket_4922); // #4922 // Macros.. TEST_CASE(macro_simple1); TEST_CASE(macro_simple2); TEST_CASE(macro_simple3); TEST_CASE(macro_simple4); TEST_CASE(macro_simple5); TEST_CASE(macro_simple6); TEST_CASE(macro_simple7); TEST_CASE(macro_simple8); TEST_CASE(macro_simple9); TEST_CASE(macro_simple10); TEST_CASE(macro_simple11); TEST_CASE(macro_simple12); TEST_CASE(macro_simple13); TEST_CASE(macro_simple14); TEST_CASE(macro_simple15); TEST_CASE(macro_simple16); // #4703: Macro parameters not trimmed TEST_CASE(macro_simple17); // #5074: isExpandedMacro not set TEST_CASE(macro_simple18); // (1e-7) TEST_CASE(macroInMacro1); TEST_CASE(macroInMacro2); TEST_CASE(macro_linenumbers); TEST_CASE(macro_nopar); TEST_CASE(macro_incdec); // separate ++ and -- with space when expanding such macro: '#define M(X) A-X' TEST_CASE(macro_switchCase); TEST_CASE(macro_NULL); // skip #define NULL .. it is replaced in the tokenizer TEST_CASE(string1); TEST_CASE(string2); TEST_CASE(string3); TEST_CASE(preprocessor_undef); TEST_CASE(defdef); // Defined multiple times TEST_CASE(preprocessor_doublesharp); TEST_CASE(preprocessor_include_in_str); TEST_CASE(va_args_1); //TEST_CASE(va_args_2); invalid code TEST_CASE(va_args_3); TEST_CASE(va_args_4); TEST_CASE(va_args_5); TEST_CASE(multi_character_character); TEST_CASE(stringify); TEST_CASE(stringify2); TEST_CASE(stringify3); TEST_CASE(stringify4); TEST_CASE(stringify5); TEST_CASE(ifdefwithfile); TEST_CASE(pragma); TEST_CASE(pragma_asm_1); TEST_CASE(pragma_asm_2); TEST_CASE(endifsemicolon); TEST_CASE(missing_doublequote); TEST_CASE(handle_error); TEST_CASE(dup_defines); TEST_CASE(define_part_of_func); TEST_CASE(conditionalDefine); TEST_CASE(macro_parameters); TEST_CASE(newline_in_macro); TEST_CASE(ifdef_ifdefined); // define and then ifdef TEST_CASE(define_if1); TEST_CASE(define_if2); TEST_CASE(define_if3); TEST_CASE(define_if4); // #4079 - #define X +123 TEST_CASE(define_if5); // #4516 - #define B (A & 0x00f0) TEST_CASE(define_if6); // #4863 - #define B (A?-1:1) TEST_CASE(define_ifdef); TEST_CASE(define_ifndef1); TEST_CASE(define_ifndef2); TEST_CASE(ifndef_define); TEST_CASE(undef_ifdef); TEST_CASE(endfile); TEST_CASE(redundant_config); TEST_CASE(invalid_define_1); // #2605 - hang for: '#define =' TEST_CASE(invalid_define_2); // #4036 - hang for: '#define () {(int f(x) }' // inline suppression, missingInclude TEST_CASE(inline_suppression_for_missing_include); // Using -D to predefine symbols TEST_CASE(predefine1); TEST_CASE(predefine2); TEST_CASE(predefine3); TEST_CASE(predefine4); TEST_CASE(predefine5); // automatically define __cplusplus TEST_CASE(invalidElIf); // #2942 segfault // Preprocessor::getConfigs TEST_CASE(getConfigs1); TEST_CASE(getConfigs2); TEST_CASE(getConfigs3); TEST_CASE(getConfigs4); TEST_CASE(getConfigs5); TEST_CASE(getConfigs7); TEST_CASE(getConfigs7a); TEST_CASE(getConfigs7b); TEST_CASE(getConfigs7c); TEST_CASE(getConfigs7d); TEST_CASE(getConfigs7e); TEST_CASE(getConfigs8); // #if A==1 => cfg: A=1 TEST_CASE(getConfigs10); // #5139 TEST_CASE(getConfigs11); // #9832 - include guards TEST_CASE(getConfigsError); TEST_CASE(getConfigsD1); TEST_CASE(getConfigsU1); TEST_CASE(getConfigsU2); TEST_CASE(getConfigsU3); TEST_CASE(getConfigsU4); TEST_CASE(getConfigsU5); TEST_CASE(getConfigsU6); TEST_CASE(getConfigsU7); TEST_CASE(validateCfg1); TEST_CASE(validateCfg2); TEST_CASE(if_sizeof); TEST_CASE(invalid_ifs); // #5909 TEST_CASE(garbage); TEST_CASE(wrongPathOnErrorDirective); TEST_CASE(testDirectiveIncludeTypes); TEST_CASE(testDirectiveIncludeLocations); TEST_CASE(testDirectiveIncludeComments); } void preprocess(const char* code, std::map& actual, const char filename[] = "file.c") { errout.str(""); std::istringstream istr(code); simplecpp::OutputList outputList; std::vector files; simplecpp::TokenList tokens(istr, files, filename, &outputList); tokens.removeComments(); preprocessor0.simplifyPragmaAsm(&tokens); const std::set configs(preprocessor0.getConfigs(tokens)); preprocessor0.setDirectives(tokens); for (const std::string & config : configs) { try { const std::string &cfgcode = preprocessor0.getcode(tokens, config, files, std::string(code).find("#file") != std::string::npos); actual[config] = cfgcode; } catch (const simplecpp::Output &) { actual[config] = ""; } catch (...) {} } } std::string getConfigsStr(const char filedata[], const char *arg = nullptr) { Settings settings; if (arg && std::strncmp(arg,"-D",2)==0) settings.userDefines = arg + 2; if (arg && std::strncmp(arg,"-U",2)==0) settings.userUndefs.insert(arg+2); Preprocessor preprocessor(settings, this); std::vector files; std::istringstream istr(filedata); simplecpp::TokenList tokens(istr,files); tokens.removeComments(); const std::set configs = preprocessor.getConfigs(tokens); std::string ret; for (const std::string & config : configs) ret += config + '\n'; return ret; } void Bug2190219() { const char filedata[] = "#ifdef __cplusplus\n" "cpp\n" "#else\n" "c\n" "#endif"; { // Preprocess => actual result.. std::map actual; preprocess(filedata, actual, "file.cpp"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\ncpp", actual[""]); } { // Ticket #7102 - skip __cplusplus in C code // Preprocess => actual result.. std::map actual; preprocess(filedata, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\nc", actual[""]); } } void error1() { const char filedata[] = "#ifdef A\n" ";\n" "#else\n" "#error abcd\n" "#endif\n"; ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); } void error2() { const char filedata1[] = "#ifndef A\n" "#error\n" "#endif\n"; ASSERT_EQUALS("A\n", getConfigsStr(filedata1)); const char filedata2[] = "#if !A\n" "#error\n" "#endif\n"; ASSERT_EQUALS("A\n", getConfigsStr(filedata2)); } void error3() { errout.str(""); Settings settings; settings.userDefines = "__cplusplus"; Preprocessor preprocessor(settings, this); const std::string code("#error hello world!\n"); preprocessor.getcode(code, "X", "test.c"); ASSERT_EQUALS("[test.c:1]: (error) #error hello world!\n", errout.str()); } // Ticket #2919 - wrong filename reported for #error void error4() { // In included file { errout.str(""); Settings settings; settings.userDefines = "TEST"; Preprocessor preprocessor(settings, this); const std::string code("#file \"ab.h\"\n#error hello world!\n#endfile"); preprocessor.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("[ab.h:1]: (error) #error hello world!\n", errout.str()); } // After including a file { errout.str(""); Settings settings; settings.userDefines = "TEST"; Preprocessor preprocessor(settings, this); const std::string code("#file \"ab.h\"\n\n#endfile\n#error aaa"); preprocessor.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("[test.c:2]: (error) #error aaa\n", errout.str()); } } void error5() { errout.str(""); Settings settings; settings.userDefines = "FOO"; settings.force = true; // No message if --force is given Preprocessor preprocessor(settings, this); const std::string code("#error hello world!\n"); preprocessor.getcode(code, "X", "test.c"); ASSERT_EQUALS("", errout.str()); } void error6() { const char filedata1[] = "#ifdef A\n" "#else\n" "#error 1\n" "#endif\n" "#ifdef B\n" "#else\n" "#error 2\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\nB\n", getConfigsStr(filedata1)); const char filedata2[] = "#ifndef A\n" "#error 1\n" "#endif\n" "#ifndef B\n" "#error 2\n" "#endif\n"; ASSERT_EQUALS("A;B\n", getConfigsStr(filedata2)); const char filedata3[] = "#if !A\n" "#error 1\n" "#endif\n" "#if !B\n" "#error 2\n" "#endif\n"; ASSERT_EQUALS("A;B\n", getConfigsStr(filedata3)); } void error7() { // #8074 const char filedata[] = "#define A\n" "\n" "#if defined(B)\n" "#else\n" "#error \"1\"\n" "#endif\n" "\n" "#if defined(A)\n" "#else\n" "#error \"2\"\n" "#endif\n"; ASSERT_EQUALS("\nB\n", getConfigsStr(filedata)); } void error8() { const char filedata[] = "#ifdef A\n" "#ifdef B\n" "#endif\n" "#else\n" "#endif\n" "\n" "#ifndef C\n" "#error aa\n" "#endif"; ASSERT_EQUALS("A;B;C\nA;C\nC\n", getConfigsStr(filedata)); } void setPlatformInfo() { Settings settings; Preprocessor preprocessor(settings, this); // read code with simplecpp.. const char filedata[] = "#if sizeof(long) == 4\n" "1\n" "#else\n" "2\n" "#endif\n"; std::istringstream istr(filedata); std::vector files; simplecpp::TokenList tokens(istr, files, "test.c"); // preprocess code with unix32 platform.. settings.platform(Settings::PlatformType::Unix32); preprocessor.setPlatformInfo(&tokens); ASSERT_EQUALS("\n1", preprocessor.getcode(tokens, "", files, false)); // preprocess code with unix64 platform.. settings.platform(Settings::PlatformType::Unix64); preprocessor.setPlatformInfo(&tokens); ASSERT_EQUALS("\n\n\n2", preprocessor.getcode(tokens, "", files, false)); } void includeguard1() { // Handling include guards.. const char filedata[] = "#file \"abc.h\"\n" "#ifndef abcH\n" "#define abcH\n" "#endif\n" "#endfile\n" "#ifdef ABC\n" "#endif"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void includeguard2() { // Handling include guards.. const char filedata[] = "#file \"abc.h\"\n" "foo\n" "#ifdef ABC\n" "\n" "#endif\n" "#endfile\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void ifdefwithfile() { // Handling include guards.. const char filedata[] = "#ifdef ABC\n" "#file \"abc.h\"\n" "class A{};/*\n\n\n\n\n\n\n*/\n" "#endfile\n" "#endif\n" "int main() {}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Expected configurations: "" and "ABC" ASSERT_EQUALS(2, actual.size()); ASSERT_EQUALS("\n\n\nint main ( ) { }", actual[""]); ASSERT_EQUALS("\n#line 1 \"abc.h\"\nclass A { } ;\n#line 4 \"file.c\"\n int main ( ) { }", actual["ABC"]); } void if0() { const char filedata[] = " # if /* comment */ 0 // comment\n" "#ifdef WIN32\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if1() { const char filedata[] = " # if /* comment */ 1 // comment\n" "ABC\n" " # endif \n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void elif() { { const char filedata[] = "#if DEF1\n" "ABC\n" "#elif DEF2\n" "DEF\n" "#endif\n"; ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); } { const char filedata[] = "#if(defined DEF1)\n" "ABC\n" "#elif(defined DEF2)\n" "DEF\n" "#else\n" "GHI\n" "#endif\n"; ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); } } void if_cond1() { const char filedata[] = "#if LIBVER>100\n" " A\n" "#else\n" " B\n" "#endif\n"; TODO_ASSERT_EQUALS("\nLIBVER=101\n", "\n", getConfigsStr(filedata)); } void if_cond2() { const char filedata[] = "#ifdef A\n" "a\n" "#endif\n" "#if defined(A) && defined(B)\n" "ab\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); if_cond2b(); if_cond2c(); if_cond2d(); if_cond2e(); } void if_cond2b() { const char filedata[] = "#ifndef A\n" "!a\n" "#ifdef B\n" "b\n" "#endif\n" "#else\n" "a\n" "#endif\n"; TODO_ASSERT_EQUALS("\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); } void if_cond2c() { const char filedata[] = "#ifndef A\n" "!a\n" "#ifdef B\n" "b\n" "#else\n" "!b\n" "#endif\n" "#else\n" "a\n" "#endif\n"; TODO_ASSERT_EQUALS("\nA\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); } void if_cond2d() { const char filedata[] = "#ifndef A\n" "!a\n" "#ifdef B\n" "b\n" "#else\n" "!b\n" "#endif\n" "#else\n" "a\n" "#ifdef B\n" "b\n" "#else\n" "!b\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\nB\n", getConfigsStr(filedata)); } void if_cond2e() { const char filedata[] = "#if !defined(A)\n" "!a\n" "#elif !defined(B)\n" "!b\n" "#endif\n"; ASSERT_EQUALS("\nA\nB\n", getConfigsStr(filedata)); } void if_cond3() { const char filedata[] = "#ifdef A\n" "a\n" "#if defined(B) && defined(C)\n" "abc\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B;C\n", getConfigsStr(filedata)); } void if_cond4() { { const char filedata[] = "#define A\n" "#define B\n" "#if defined A || defined B\n" "ab\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } { const char filedata[] = "#if A\n" "{\n" "#if (defined(B))\n" "foo();\n" "#endif\n" "}\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); } { const char filedata[] = "#define A\n" "#define B\n" "#if (defined A) || defined (B)\n" "ab\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } { const char filedata[] = "#if (A)\n" "foo();\n" "#endif\n"; ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); } { const char filedata[] = "#if! A\n" "foo();\n" "#endif\n"; ASSERT_EQUALS("\nA=0\n", getConfigsStr(filedata)); } } void if_cond5() { const char filedata[] = "#if defined(A) && defined(B)\n" "ab\n" "#endif\n" "cd\n" "#if defined(B) && defined(A)\n" "ef\n" "#endif\n"; ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); } void if_cond6() { const char filedata[] = "\n" "#if defined(A) && defined(B))\n" "#endif\n"; ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); } void if_cond8() { const char filedata[] = "#if defined(A) + defined(B) + defined(C) != 1\n" "#endif\n"; TODO_ASSERT_EQUALS("\nA\n", "\nA;B;C\n", getConfigsStr(filedata)); } void if_cond9() { const char filedata[] = "#if !defined _A\n" "abc\n" "#endif\n"; ASSERT_EQUALS("\n_A\n", getConfigsStr(filedata)); } void if_cond10() { const char filedata[] = "#if !defined(a) && !defined(b)\n" "#if defined(and)\n" "#endif\n" "#endif\n"; // Preprocess => don't crash.. std::map actual; preprocess(filedata, actual); } void if_cond11() { const char filedata[] = "#if defined(L_fixunssfdi) && LIBGCC2_HAS_SF_MODE\n" "#if LIBGCC2_HAS_DF_MODE\n" "#elif FLT_MANT_DIG < W_TYPE_SIZE\n" "#endif\n" "#endif\n"; std::map actual; preprocess(filedata, actual); ASSERT_EQUALS("", errout.str()); } void if_cond12() { const char filedata[] = "#define A (1)\n" "#if A == 1\n" ";\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_cond13() { const char filedata[] = "#if ('A' == 0x41)\n" "123\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_cond14() { const char filedata[] = "#if !(A)\n" "123\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_or_1() { const char filedata[] = "#if defined(DEF_10) || defined(DEF_11)\n" "a1;\n" "#endif\n"; ASSERT_EQUALS("\nDEF_10;DEF_11\n", getConfigsStr(filedata)); } void if_or_2() { const char filedata[] = "#if X || Y\n" "a1;\n" "#endif\n"; TODO_ASSERT_EQUALS("\nX;Y\n", "\n", getConfigsStr(filedata)); } void if_macro_eq_macro() { const char *code = "#define A B\n" "#define B 1\n" "#define C 1\n" "#if A == C\n" "Wilma\n" "#else\n" "Betty\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(code)); } void ticket_3675() { const char* code = "#ifdef YYSTACKSIZE\n" "#define YYMAXDEPTH YYSTACKSIZE\n" "#else\n" "#define YYSTACKSIZE YYMAXDEPTH\n" "#endif\n" "#if YYDEBUG\n" "#endif\n"; std::map actual; preprocess(code, actual); // There's nothing to assert. It just needs to not hang. } void ticket_3699() { const char* code = "#define INLINE __forceinline\n" "#define inline __forceinline\n" "#define __forceinline inline\n" "#if !defined(_WIN32)\n" "#endif\n" "INLINE inline __forceinline\n"; std::map actual; preprocess(code, actual); // First, it must not hang. Second, inline must becomes inline, and __forceinline must become __forceinline. ASSERT_EQUALS("\n\n\n\n\n$__forceinline $inline $__forceinline", actual[""]); } void ticket_4922() { // #4922 const char* code = "__asm__ \n" "{ int extern __value) 0; (double return (\"\" } extern\n" "__typeof __finite (__finite) __finite __inline \"__GI___finite\");"; std::map actual; preprocess(code, actual); } void macro_simple1() const { { const char filedata[] = "#define AAA(aa) f(aa)\n" "AAA(5);\n"; ASSERT_EQUALS("\nf ( 5 ) ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define AAA(aa) f(aa)\n" "AAA (5);\n"; ASSERT_EQUALS("\nf ( 5 ) ;", OurPreprocessor::expandMacros(filedata)); } } void macro_simple2() const { const char filedata[] = "#define min(x,y) x 0 ) return 1 ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple5() const { const char filedata[] = "#define ABC if( temp > 0 ) return 1;\n" "\n" "void foo()\n" "{\n" " int temp = 0;\n" " ABC\n" "}\n"; ASSERT_EQUALS("\n\nvoid foo ( )\n{\nint temp = 0 ;\nif ( temp > 0 ) return 1 ;\n}", OurPreprocessor::expandMacros(filedata)); } void macro_simple6() const { const char filedata[] = "#define ABC (a+b+c)\n" "ABC\n"; ASSERT_EQUALS("\n( a + b + c )", OurPreprocessor::expandMacros(filedata)); } void macro_simple7() const { const char filedata[] = "#define ABC(str) str\n" "ABC(\"(\")\n"; ASSERT_EQUALS("\n\"(\"", OurPreprocessor::expandMacros(filedata)); } void macro_simple8() const { const char filedata[] = "#define ABC 123\n" "#define ABCD 1234\n" "ABC ABCD\n"; ASSERT_EQUALS("\n\n123 1234", OurPreprocessor::expandMacros(filedata)); } void macro_simple9() const { const char filedata[] = "#define ABC(a) f(a)\n" "ABC( \"\\\"\" );\n" "ABC( \"g\" );\n"; ASSERT_EQUALS("\nf ( \"\\\"\" ) ;\nf ( \"g\" ) ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple10() const { const char filedata[] = "#define ABC(t) t x\n" "ABC(unsigned long);\n"; ASSERT_EQUALS("\nunsigned long x ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple11() const { const char filedata[] = "#define ABC(x) delete x\n" "ABC(a);\n"; ASSERT_EQUALS("\ndelete a ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple12() const { const char filedata[] = "#define AB ab.AB\n" "AB.CD\n"; ASSERT_EQUALS("\nab . AB . CD", OurPreprocessor::expandMacros(filedata)); } void macro_simple13() const { const char filedata[] = "#define TRACE(x)\n" "TRACE(;if(a))\n"; ASSERT_EQUALS("", OurPreprocessor::expandMacros(filedata)); } void macro_simple14() const { const char filedata[] = "#define A \" a \"\n" "printf(A);\n"; ASSERT_EQUALS("\nprintf ( \" a \" ) ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple15() const { const char filedata[] = "#define FOO\"foo\"\n" "FOO\n"; ASSERT_EQUALS("\n\"foo\"", OurPreprocessor::expandMacros(filedata)); } void macro_simple16() const { // # 4703 const char filedata[] = "#define MACRO( A, B, C ) class A##B##C##Creator {};\n" "MACRO( B\t, U , G )"; ASSERT_EQUALS("\nclass BUGCreator { } ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple17() const { // # 5074 - the Token::isExpandedMacro() doesn't always indicate properly if token comes from macro // It would probably be OK if the generated code was // "\n123+$123" since the first 123 comes from the source code const char filedata[] = "#define MACRO(A) A+123\n" "MACRO(123)"; ASSERT_EQUALS("\n123 + 123", OurPreprocessor::expandMacros(filedata)); } void macro_simple18() const { // (1e-7) const char filedata1[] = "#define A (1e-7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1e-7 ) ;", OurPreprocessor::expandMacros(filedata1)); const char filedata2[] = "#define A (1E-7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1E-7 ) ;", OurPreprocessor::expandMacros(filedata2)); const char filedata3[] = "#define A (1e+7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1e+7 ) ;", OurPreprocessor::expandMacros(filedata3)); const char filedata4[] = "#define A (1.e+7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1.e+7 ) ;", OurPreprocessor::expandMacros(filedata4)); const char filedata5[] = "#define A (1.7f)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1.7f ) ;", OurPreprocessor::expandMacros(filedata5)); const char filedata6[] = "#define A (.1)\n" "a=A;"; ASSERT_EQUALS("\na = ( .1 ) ;", OurPreprocessor::expandMacros(filedata6)); const char filedata7[] = "#define A (1.)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1. ) ;", OurPreprocessor::expandMacros(filedata7)); const char filedata8[] = "#define A (8.0E+007)\n" "a=A;"; ASSERT_EQUALS("\na = ( 8.0E+007 ) ;", OurPreprocessor::expandMacros(filedata8)); } void macroInMacro1() const { { const char filedata[] = "#define A(m) long n = m; n++;\n" "#define B(n) A(n)\n" "B(0)\n"; ASSERT_EQUALS("\n\nlong n = 0 ; n ++ ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A B\n" "#define B 3\n" "A\n"; ASSERT_EQUALS("\n\n3", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define BC(b, c...) 0##b * 0##c\n" "#define ABC(a, b...) a + BC(b)\n" "\n" "ABC(1);\n" // <- too few parameters "ABC(2,3);\n" "ABC(4,5,6);\n"; ASSERT_EQUALS("\n\n\n1 + 0 * 0 ;\n2 + 03 * 0 ;\n4 + 05 * 06 ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A 4\n" "#define B(a) a,A\n" "B(2);\n"; ASSERT_EQUALS("\n\n2 , 4 ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A(x) (x)\n" "#define B )A(\n" "#define C )A(\n"; ASSERT_EQUALS("", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A(x) (x*2)\n" "#define B A(\n" "foo B(i));\n"; ASSERT_EQUALS("\n\nfoo ( ( i ) * 2 ) ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define foo foo\n" "foo\n"; ASSERT_EQUALS("\nfoo", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define B(A1, A2) } while (0)\n" "#define A(name) void foo##name() { do { B(1, 2); }\n" "A(0)\n" "A(1)\n"; ASSERT_EQUALS("\n\nvoid foo0 ( ) { do { } while ( 0 ) ; }\nvoid foo1 ( ) { do { } while ( 0 ) ; }", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define B(x) (\n" "#define A() B(xx)\n" "B(1) A() ) )\n"; ASSERT_EQUALS("\n\n( ( ) )", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define PTR1 (\n" "#define PTR2 PTR1 PTR1\n" "int PTR2 PTR2 foo )))) = 0;\n"; ASSERT_EQUALS("\n\nint ( ( ( ( foo ) ) ) ) = 0 ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define PTR1 (\n" "PTR1 PTR1\n"; ASSERT_EQUALS("\n( (", OurPreprocessor::expandMacros(filedata)); } } void macroInMacro2() const { const char filedata[] = "#define A(x) a##x\n" "#define B 0\n" "A(B)\n"; ASSERT_EQUALS("\n\naB", OurPreprocessor::expandMacros(filedata)); } void macro_linenumbers() const { const char filedata[] = "#define AAA(a)\n" "AAA(5\n" "\n" ")\n" "int a;\n"; ASSERT_EQUALS("\n" "\n" "\n" "\n" "int a ;", OurPreprocessor::expandMacros(filedata)); } void macro_nopar() const { const char filedata[] = "#define AAA( ) { NULL }\n" "AAA()\n"; ASSERT_EQUALS("\n{ NULL }", OurPreprocessor::expandMacros(filedata)); } void macro_incdec() const { const char filedata[] = "#define M1(X) 1+X\n" "#define M2(X) 2-X\n" "M1(+1) M2(-1)\n"; ASSERT_EQUALS("\n\n1 + + 1 2 - - 1", OurPreprocessor::expandMacros(filedata)); } void macro_switchCase() const { { // Make sure "case 2" doesn't become "case2" const char filedata[] = "#define A( b ) " "switch( a ){ " "case 2: " " break; " "}\n" "A( 5 );\n"; ASSERT_EQUALS("\nswitch ( a ) { case 2 : break ; } ;", OurPreprocessor::expandMacros(filedata)); } { // Make sure "2 BB" doesn't become "2BB" const char filedata[] = "#define A() AA : 2 BB\n" "A();\n"; ASSERT_EQUALS("\nAA : 2 BB ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A }\n" "#define B() A\n" "#define C( a ) B() break;\n" "{C( 2 );\n"; ASSERT_EQUALS("\n\n\n{ } break ; ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A }\n" "#define B() A\n" "#define C( a ) B() _break;\n" "{C( 2 );\n"; ASSERT_EQUALS("\n\n\n{ } _break ; ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A }\n" "#define B() A\n" "#define C( a ) B() 5;\n" "{C( 2 );\n"; ASSERT_EQUALS("\n\n\n{ } 5 ; ;", OurPreprocessor::expandMacros(filedata)); } } void macro_NULL() const { // See ticket #4482 - UB when passing NULL to variadic function ASSERT_EQUALS("\n0", OurPreprocessor::expandMacros("#define null 0\nnull")); TODO_ASSERT_EQUALS("\nNULL", "\n0", OurPreprocessor::expandMacros("#define NULL 0\nNULL")); // TODO: Let the tokenizer handle NULL? } void string1() { const char filedata[] = "int main()" "{" " const char *a = \"#define A\";" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("int main ( ) { const char * a = \"#define A\" ; }", actual[""]); } void string2() const { const char filedata[] = "#define AAA 123\n" "str = \"AAA\"\n"; // Compare results.. ASSERT_EQUALS("\nstr = \"AAA\"", OurPreprocessor::expandMacros(filedata)); } void string3() const { const char filedata[] = "str(\";\");\n"; // Compare results.. ASSERT_EQUALS("str ( \";\" ) ;", OurPreprocessor::expandMacros(filedata)); } void preprocessor_undef() { { const char filedata[] = "#define AAA int a;\n" "#undef AAA\n" "#define AAA char b=0;\n" "AAA\n"; // Compare results.. ASSERT_EQUALS("\n\n\nchar b = 0 ;", OurPreprocessor::expandMacros(filedata)); } { // ticket #403 const char filedata[] = "#define z p[2]\n" "#undef z\n" "int z;\n" "z = 0;\n"; ASSERT_EQUALS("\n\nint z ;\nz = 0 ;", preprocessor0.getcode(filedata, "", "")); } } void defdef() const { const char filedata[] = "#define AAA 123\n" "#define AAA 456\n" "#define AAA 789\n" "AAA\n"; // Compare results.. ASSERT_EQUALS("\n\n\n789", OurPreprocessor::expandMacros(filedata)); } void preprocessor_doublesharp() const { // simple testcase without ## const char filedata1[] = "#define TEST(var,val) var = val\n" "TEST(foo,20);\n"; ASSERT_EQUALS("\nfoo = 20 ;", OurPreprocessor::expandMacros(filedata1)); // simple testcase with ## const char filedata2[] = "#define TEST(var,val) var##_##val = val\n" "TEST(foo,20);\n"; ASSERT_EQUALS("\nfoo_20 = 20 ;", OurPreprocessor::expandMacros(filedata2)); // concat macroname const char filedata3[] = "#define ABCD 123\n" "#define A(B) A##B\n" "A(BCD)\n"; ASSERT_EQUALS("\n\n123", OurPreprocessor::expandMacros(filedata3)); // Ticket #1802 - inner ## must be expanded before outer macro const char filedata4[] = "#define A(B) A##B\n" "#define a(B) A(B)\n" "a(A(B))\n"; ASSERT_EQUALS("\n\nAAB", OurPreprocessor::expandMacros(filedata4)); // Ticket #1802 - inner ## must be expanded before outer macro const char filedata5[] = "#define AB(A,B) A##B\n" "#define ab(A,B) AB(A,B)\n" "ab(a,AB(b,c))\n"; ASSERT_EQUALS("\n\nabc", OurPreprocessor::expandMacros(filedata5)); // Ticket #1802 const char filedata6[] = "#define AB_(A,B) A ## B\n" "#define AB(A,B) AB_(A,B)\n" "#define ab(suf) AB(X, AB_(_, suf))\n" "#define X x\n" "ab(y)\n"; ASSERT_EQUALS("\n\n\n\nx_y", OurPreprocessor::expandMacros(filedata6)); } void preprocessor_include_in_str() { const char filedata[] = "int main()\n" "{\n" "const char *a = \"#include \";\n" "return 0;\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("int main ( )\n{\nconst char * a = \"#include \" ;\nreturn 0 ;\n}", actual[""]); } void va_args_1() const { const char filedata[] = "#define DBG(fmt...) printf(fmt)\n" "DBG(\"[0x%lx-0x%lx)\", pstart, pend);\n"; // Preprocess.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\nprintf ( \"[0x%lx-0x%lx)\" , pstart , pend ) ;", actual); } /* void va_args_2() const { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" "DBG(\"hello\");\n"; // Preprocess.. std::string actual = OurPreprocessor::expandMacros(filedata); // invalid code ASSERT_EQUALS("\nprintf ( \"hello\" ) ;", actual); } */ void va_args_3() const { const char filedata[] = "#define FRED(...) { fred(__VA_ARGS__); }\n" "FRED(123)\n"; ASSERT_EQUALS("\n{ fred ( 123 ) ; }", OurPreprocessor::expandMacros(filedata)); } void va_args_4() const { const char filedata[] = "#define FRED(name, ...) name (__VA_ARGS__)\n" "FRED(abc, 123)\n"; ASSERT_EQUALS("\nabc ( 123 )", OurPreprocessor::expandMacros(filedata)); } void va_args_5() const { const char filedata1[] = "#define A(...) #__VA_ARGS__\n" "A(123)\n"; ASSERT_EQUALS("\n\"123\"", OurPreprocessor::expandMacros(filedata1)); const char filedata2[] = "#define A(X,...) X(#__VA_ARGS__)\n" "A(f,123)\n"; ASSERT_EQUALS("\nf ( \"123\" )", OurPreprocessor::expandMacros(filedata2)); } void multi_character_character() { const char filedata[] = "#define FOO 'ABCD'\n" "int main()\n" "{\n" "if( FOO == 0 );\n" "return 0;\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nint main ( )\n{\nif ( $'ABCD' == 0 ) ;\nreturn 0 ;\n}", actual[""]); } void stringify() const { const char filedata[] = "#define STRINGIFY(x) #x\n" "STRINGIFY(abc)\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n\"abc\"", actual); } void stringify2() const { const char filedata[] = "#define A(x) g(#x)\n" "A(abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\ng ( \"abc\" ) ;", actual); } void stringify3() const { const char filedata[] = "#define A(x) g(#x)\n" "A( abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\ng ( \"abc\" ) ;", actual); } void stringify4() const { const char filedata[] = "#define A(x) #x\n" "1 A(\n" "abc\n" ") 2\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n1 \"abc\"\n\n2", actual); } void stringify5() const { const char filedata[] = "#define A(x) a(#x,x)\n" "A(foo(\"\\\"\"))\n"; ASSERT_EQUALS("\na ( \"foo(\\\"\\\\\\\"\\\")\" , foo ( \"\\\"\" ) )", OurPreprocessor::expandMacros(filedata)); } void pragma() { const char filedata[] = "#pragma once\n" "void f()\n" "{\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nvoid f ( )\n{\n}", actual[""]); } void pragma_asm_1() { const char filedata[] = "#pragma asm\n" " mov r1, 11\n" "#pragma endasm\n" "aaa\n" "#pragma asm foo\n" " mov r1, 11\n" "#pragma endasm bar\n" "bbb"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("asm ( )\n;\n\naaa\nasm ( ) ;\n\n\nbbb", actual[""]); } void pragma_asm_2() { const char filedata[] = "#pragma asm\n" " mov @w1, 11\n" "#pragma endasm ( temp=@w1 )\n" "bbb"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("asm ( )\n;\n\nbbb", actual[""]); } void endifsemicolon() { const char filedata[] = "void f() {\n" "#ifdef A\n" "#endif;\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(2, actual.size()); const std::string expected("void f ( ) {\n\n\n}"); ASSERT_EQUALS(expected, actual[""]); ASSERT_EQUALS(expected, actual["A"]); } void handle_error() { { const char filedata[] = "#define A \n" "#define B don't want to \\\n" "more text\n" "void f()\n" "{\n" " char a = 'a'; // '\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("", errout.str()); } } void missing_doublequote() { { const char filedata[] = "#define a\n" "#ifdef 1\n" "\"\n" "#endif\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[file.cpp:3]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "#file \"abc.h\"\n" "#define a\n" "\"\n" "#endfile\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[abc.h:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "#file \"abc.h\"\n" "#define a\n" "#endfile\n" "\"\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[file.cpp:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "#define A 1\n" "#define B \"\n" "int a = A;\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[file.cpp:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "void foo()\n" "{\n" "\n" "\n" "\n" "int a = 0;\n" "printf(Text\");\n" "}\n"; // expand macros.. errout.str(""); OurPreprocessor::expandMacros(filedata, this); ASSERT_EQUALS("[file.cpp:7]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } } void define_part_of_func() { const char filedata[] = "#define A g(\n" "void f() {\n" " A );\n" " }\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nvoid f ( ) {\n$g $( ) ;\n}", actual[""]); ASSERT_EQUALS("", errout.str()); } void conditionalDefine() { const char filedata[] = "#ifdef A\n" "#define N 10\n" "#else\n" "#define N 20\n" "#endif\n" "N"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(2, actual.size()); ASSERT_EQUALS("\n\n\n\n\n$20", actual[""]); ASSERT_EQUALS("\n\n\n\n\n$10", actual["A"]); ASSERT_EQUALS("", errout.str()); } void macro_parameters() { errout.str(""); const char filedata[] = "#define BC(a, b, c, arg...) \\\n" "AB(a, b, c, ## arg)\n" "\n" "void f()\n" "{\n" " BC(3);\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("[file.c:6]: (error) failed to expand 'BC', Wrong number of parameters for macro 'BC'.\n", errout.str()); } void newline_in_macro() { const char filedata[] = "#define ABC(str) printf( str )\n" "void f()\n" "{\n" " ABC(\"\\n\");\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nvoid f ( )\n{\n$printf $( \"\\n\" $) ;\n}", actual[""]); ASSERT_EQUALS("", errout.str()); } void ifdef_ifdefined() { const char filedata[] = "#ifdef ABC\n" "A\n" "#endif\t\n" "#if defined ABC\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("\nA\n\n\nA", actual["ABC"]); ASSERT_EQUALS(2, actual.size()); } void define_if1() { { const char filedata[] = "#define A 0\n" "#if A\n" "FOO\n" "#endif"; ASSERT_EQUALS("", preprocessor0.getcode(filedata,"","")); } { const char filedata[] = "#define A 1\n" "#if A==1\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } } void define_if2() { const char filedata[] = "#define A 22\n" "#define B A\n" "#if (B==A) || (B==C)\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\n\nFOO", preprocessor0.getcode(filedata,"","")); } void define_if3() { const char filedata[] = "#define A 0\n" "#if (A==0)\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } void define_if4() { const char filedata[] = "#define X +123\n" "#if X==123\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } void define_if5() { // #4516 - #define B (A & 0x00f0) { const char filedata[] = "#define A 0x0010\n" "#define B (A & 0x00f0)\n" "#if B==0x0010\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\n\nFOO", preprocessor0.getcode(filedata,"","")); } { const char filedata[] = "#define A 0x00f0\n" "#define B (16)\n" "#define C (B & A)\n" "#if C==0x0010\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\n\n\nFOO", preprocessor0.getcode(filedata,"","")); } { const char filedata[] = "#define A (1+A)\n" // don't hang for recursive macros "#if A==1\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } } void define_if6() { // #4516 - #define B (A?1:-1) const char filedata[] = "#ifdef A\n" "#define B (A?1:-1)\n" "#endif\n" "\n" "#if B < 0\n" "123\n" "#endif\n" "\n" "#if B >= 0\n" "456\n" "#endif\n"; const std::string actualA0 = preprocessor0.getcode(filedata, "A=0", "test.c"); ASSERT_EQUALS(true, actualA0.find("123") != std::string::npos); ASSERT_EQUALS(false, actualA0.find("456") != std::string::npos); const std::string actualA1 = preprocessor0.getcode(filedata, "A=1", "test.c"); ASSERT_EQUALS(false, actualA1.find("123") != std::string::npos); ASSERT_EQUALS(true, actualA1.find("456") != std::string::npos); } void define_ifdef() { { const char filedata[] = "#define ABC\n" "#ifndef ABC\n" "A\n" "#else\n" "B\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n\n\nB", actual[""]); } { const char filedata[] = "#define A 1\n" "#ifdef A\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n$1", actual[""]); } { const char filedata[] = "#define A 1\n" "#if A==1\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n$1", actual[""]); } { const char filedata[] = "#define A 1\n" "#if A>0\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n$1", actual[""]); } { const char filedata[] = "#define A 1\n" "#if 0\n" "#undef A\n" "#endif\n" "A\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n\n\n$1", actual[""]); } } void define_ifndef1() { const char filedata[] = "#define A(x) (x)\n" "#ifndef A\n" ";\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("", actual[""]); } void define_ifndef2() { const char filedata[] = "#ifdef A\n" "#define B char\n" "#endif\n" "#ifndef B\n" "#define B int\n" "#endif\n" "B me;\n"; // Preprocess => actual result.. ASSERT_EQUALS("\n\n\n\n\n\n$int me ;", preprocessor0.getcode(filedata, "", "a.cpp")); ASSERT_EQUALS("\n\n\n\n\n\n$char me ;", preprocessor0.getcode(filedata, "A", "a.cpp")); } void ifndef_define() { const char filedata[] = "#ifndef A\n" "#define A(x) x\n" "#endif\n" "A(123);"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n123 ;", actual[""]); } void undef_ifdef() { const char filedata[] = "#undef A\n" "#ifdef A\n" "123\n" "#endif\n"; // Preprocess => actual result.. ASSERT_EQUALS("", preprocessor0.getcode(filedata, "", "a.cpp")); ASSERT_EQUALS("", preprocessor0.getcode(filedata, "A", "a.cpp")); } void redundant_config() { const char filedata[] = "int main() {\n" "#ifdef FOO\n" "#ifdef BAR\n" " std::cout << 1;\n" "#endif\n" "#endif\n" "\n" "#ifdef BAR\n" "#ifdef FOO\n" " std::cout << 2;\n" "#endif\n" "#endif\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(4, (int)actual.size()); ASSERT(actual.find("") != actual.end()); ASSERT(actual.find("BAR") != actual.end()); ASSERT(actual.find("FOO") != actual.end()); ASSERT(actual.find("BAR;FOO") != actual.end()); } void endfile() { const char filedata[] = "char a[] = \"#endfile\";\n" "char b[] = \"#endfile\";\n" "#include \"notfound.h\"\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS("char a [ ] = \"#endfile\" ;\nchar b [ ] = \"#endfile\" ;", actual[""]); ASSERT_EQUALS(1, (int)actual.size()); } void dup_defines() { const char filedata[] = "#ifdef A\n" "#define B\n" "#if defined(B) && defined(A)\n" "a\n" "#else\n" "b\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // B will always be defined if A is defined; the following test // cases should be fixed whenever this other bug is fixed ASSERT_EQUALS(2U, actual.size()); ASSERT_EQUALS_MSG(true, (actual.find("A") != actual.end()), "A is expected to be checked but it was not checked"); ASSERT_EQUALS_MSG(true, (actual.find("A;A;B") == actual.end()), "A;A;B is expected to NOT be checked but it was checked"); } void invalid_define_1() { std::map actual; preprocess("#define =\n", actual); // don't hang } void invalid_define_2() { // #4036 std::map actual; preprocess("#define () {(int f(x) }\n", actual); // don't hang } void inline_suppression_for_missing_include() { Preprocessor::missingIncludeFlag = false; Settings settings; settings.inlineSuppressions = true; settings.severity.fill(); Preprocessor preprocessor(settings, this); std::istringstream src("// cppcheck-suppress missingInclude\n" "#include \"missing.h\"\n" "int x;"); std::string processedFile; std::list cfg; std::list paths; // Don't report that the include is missing errout.str(""); preprocessor.preprocess(src, processedFile, cfg, "test.c", paths); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag); } void predefine1() { const std::string src("#if defined X || Y\n" "Fred & Wilma\n" "#endif\n"); std::string actual = preprocessor0.getcode(src, "X=1", "test.c"); ASSERT_EQUALS("\nFred & Wilma", actual); } void predefine2() { const std::string src("#if defined(X) && Y\n" "Fred & Wilma\n" "#endif\n"); { std::string actual = preprocessor0.getcode(src, "X=1", "test.c"); ASSERT_EQUALS("", actual); } { std::string actual = preprocessor0.getcode(src, "X=1;Y=2", "test.c"); ASSERT_EQUALS("\nFred & Wilma", actual); } } void predefine3() { // #2871 - define in source is not used if -D is used const char code[] = "#define X 1\n" "#define Y X\n" "#if (X == Y)\n" "Fred & Wilma\n" "#endif\n"; const std::string actual = preprocessor0.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("\n\n\nFred & Wilma", actual); } void predefine4() { // #3577 const char code[] = "char buf[X];\n"; const std::string actual = preprocessor0.getcode(code, "X=123", "test.c"); ASSERT_EQUALS("char buf [ $123 ] ;", actual); } void predefine5() { // #3737, #5119 - automatically define __cplusplus // #3737... const char code[] = "#ifdef __cplusplus\n123\n#endif"; ASSERT_EQUALS("", preprocessor0.getcode(code, "X=123", "test.c")); ASSERT_EQUALS("\n123", preprocessor0.getcode(code, "X=123", "test.cpp")); } void invalidElIf() { // #2942 - segfault const char code[] = "#elif (){\n"; const std::string actual = preprocessor0.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("", actual); } void getConfigs1() { const char filedata[] = "#ifdef WIN32 \n" " abcdef\n" "#else \n" " qwerty\n" "#endif \n"; ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); } void getConfigs2() { const char filedata[] = "# ifndef WIN32\n" " \" # ifdef WIN32\" // a comment\n" " # else \n" " qwerty\n" " # endif \n"; ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); } void getConfigs3() { const char filedata[] = "#ifdef ABC\n" "a\n" "#ifdef DEF\n" "b\n" "#endif\n" "c\n" "#endif\n"; ASSERT_EQUALS("\nABC\nABC;DEF\n", getConfigsStr(filedata)); } void getConfigs4() { const char filedata[] = "#ifdef ABC\n" "A\n" "#endif\t\n" "#ifdef ABC\n" "A\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs5() { const char filedata[] = "#ifdef ABC\n" "A\n" "#else\n" "B\n" "#ifdef DEF\n" "C\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\nDEF\n", getConfigsStr(filedata)); } void getConfigs7() { const char filedata[] = "#ifdef ABC\n" "A\n" "#ifdef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7a() { const char filedata[] = "#ifndef ABC\n" "A\n" "#ifndef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void getConfigs7b() { const char filedata[] = "#ifndef ABC\n" "A\n" "#ifdef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7c() { const char filedata[] = "#ifdef ABC\n" "A\n" "#ifndef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7d() { const char filedata[] = "#if defined(ABC)\n" "A\n" "#if defined(ABC)\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7e() { const char filedata[] = "#ifdef ABC\n" "#file \"test.h\"\n" "#ifndef test_h\n" "#define test_h\n" "#ifdef ABC\n" "#endif\n" "#endif\n" "#endfile\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs8() { const char filedata[] = "#if A == 1\n" "1\n" "#endif\n"; ASSERT_EQUALS("\nA=1\n", getConfigsStr(filedata)); } void getConfigs10() { // Ticket #5139 const char filedata[] = "#define foo a.foo\n" "#define bar foo\n" "#define baz bar+0\n" "#if 0\n" "#endif"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void getConfigs11() { // #9832 - include guards const char filedata[] = "#file \"test.h\"\n" "#if !defined(test_h)\n" "#define test_h\n" "123\n" "#endif\n" "#endfile\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void getConfigsError() { const char filedata1[] = "#ifndef X\n" "#error \"!X\"\n" "#endif\n"; ASSERT_EQUALS("X\n", getConfigsStr(filedata1)); const char filedata2[] = "#ifdef X\n" "#ifndef Y\n" "#error \"!Y\"\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nX;Y\nY\n", getConfigsStr(filedata2)); } void getConfigsD1() { const char filedata[] = "#ifdef X\n" "#else\n" "#ifdef Y\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-DX")); ASSERT_EQUALS("\nX\nY\n", getConfigsStr(filedata)); } void getConfigsU1() { const char filedata[] = "#ifdef X\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX\n", getConfigsStr(filedata)); } void getConfigsU2() { const char filedata[] = "#ifndef X\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\n", getConfigsStr(filedata)); // no #else } void getConfigsU3() { const char filedata[] = "#ifndef X\n" "Fred & Wilma\n" "#else\n" "Barney & Betty\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX\n", getConfigsStr(filedata)); } void getConfigsU4() { const char filedata[] = "#if defined(X) || defined(Y) || defined(Z)\n" "#else\n" "#endif\n"; ASSERT_EQUALS("\nY;Z\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX;Y;Z\n", getConfigsStr(filedata)); } void getConfigsU5() { const char filedata[] = "#if X==1\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX=1\n", getConfigsStr(filedata)); } void getConfigsU6() { const char filedata[] = "#if X==0\n" "#endif\n"; ASSERT_EQUALS("\nX=0\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX=0\n", getConfigsStr(filedata)); } void getConfigsU7() { const char code[] = "#ifndef Y\n" "#else\n" "#endif\n"; ASSERT_EQUALS("\nY\n", getConfigsStr(code, "-DX")); } void validateCfg1() { Preprocessor preprocessor(settings0, this); std::vector files(1, "test.c"); simplecpp::MacroUsage macroUsage(files, false); macroUsage.useLocation.fileIndex = 0; macroUsage.useLocation.line = 1; macroUsage.macroName = "X"; std::list macroUsageList(1, macroUsage); ASSERT_EQUALS(true, preprocessor.validateCfg("", macroUsageList)); ASSERT_EQUALS(false, preprocessor.validateCfg("X",macroUsageList)); ASSERT_EQUALS(false, preprocessor.validateCfg("A=42;X", macroUsageList)); ASSERT_EQUALS(true, preprocessor.validateCfg("X=1", macroUsageList)); ASSERT_EQUALS(true, preprocessor.validateCfg("Y", macroUsageList)); macroUsageList.front().macroValueKnown = true; // #8404 ASSERT_EQUALS(true, preprocessor.validateCfg("X", macroUsageList)); } void validateCfg2() { const char filedata[] = "#ifdef ABC\n" "#endif\n" "int i = ABC;"; std::map actual; preprocess(filedata, actual, "file.cpp"); ASSERT_EQUALS("[file.cpp:3]: (information) Skipping configuration 'ABC' since the value of 'ABC' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str()); } void if_sizeof() { // #4071 static const char* code = "#if sizeof(unsigned short) == 2\n" "Fred & Wilma\n" "#elif sizeof(unsigned short) == 4\n" "Fred & Wilma\n" "#else\n" "#endif"; std::map actual; preprocess(code, actual); ASSERT_EQUALS("\nFred & Wilma", actual[""]); } void invalid_ifs() { const char filedata[] = "#ifdef\n" "#endif\n" "#ifdef !\n" "#endif\n" "#if defined\n" "#endif\n" "#define f(x) x\n" "#if f(2\n" "#endif\n" "int x;\n"; // Preprocess => don't crash.. std::map actual; preprocess(filedata, actual); } void garbage() { const char filedata[] = "V\n" "#define X b #endif #line 0 \"x\" ;\n" "#if ! defined ( Y ) #endif"; // Preprocess => don't crash.. std::map actual; preprocess(filedata, actual); } void wrongPathOnErrorDirective() { errout.str(""); Settings settings; settings.userDefines = "foo"; Preprocessor preprocessor(settings, this); const std::string code("#error hello world!\n"); preprocessor.getcode(code, "X", "./././test.c"); ASSERT_EQUALS("[test.c:1]: (error) #error hello world!\n", errout.str()); } void testDirectiveIncludeTypes() { const char filedata[] = "#define macro some definition\n" "#undef macro\n" "#ifdef macro\n" "#elif some (complex) condition\n" "#else\n" "#endif\n" "#if some other condition\n" "#pragma some proprietary content\n" "#\n" /* may appear in old C code */ "#ident some text\n" /* may appear in old C code */ "#unknownmacro some unpredictable text\n" "#warning some warning message\n" "#error some error message\n"; const char dumpdata[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; std::ostringstream ostr; Preprocessor preprocessor(settings0, this); preprocessor.getcode(filedata, "", "test.c"); preprocessor.dump(ostr); ASSERT_EQUALS(dumpdata, ostr.str()); } void testDirectiveIncludeLocations() { const char filedata[] = "#define macro1 val\n" "#file \"inc1.h\"\n" "#define macro2 val\n" "#file \"inc2.h\"\n" "#define macro3 val\n" "#endfile\n" "#define macro4 val\n" "#endfile\n" "#define macro5 val\n"; const char dumpdata[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; std::ostringstream ostr; Preprocessor preprocessor(settings0, this); preprocessor.getcode(filedata, "", "test.c"); preprocessor.dump(ostr); ASSERT_EQUALS(dumpdata, ostr.str()); } void testDirectiveIncludeComments() { const char filedata[] = "#ifdef macro2 /* this will be removed */\n" "#else /* this will be removed too */\n" "#endif /* this will also be removed */\n"; const char dumpdata[] = " \n" " \n" " \n" " \n" " \n"; std::ostringstream ostr; Preprocessor preprocessor(settings0, this); preprocessor.getcode(filedata, "", "test.c"); preprocessor.dump(ostr); ASSERT_EQUALS(dumpdata, ostr.str()); } }; REGISTER_TEST(TestPreprocessor) cppcheck-2.7/test/testrunner.cpp000066400000000000000000000035631417746362400170630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "options.h" #include "preprocessor.h" #include "testsuite.h" #include #ifdef NDEBUG #include "errortypes.h" // for InternalError #include #include #include #endif int main(int argc, char *argv[]) { // MS Visual C++ memory leak debug tracing #if defined(_MSC_VER) && defined(_DEBUG) _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); #endif #ifdef NDEBUG try { #endif Preprocessor::macroChar = '$'; // While macroChar is char(1) per default outside test suite, we require it to be a human-readable character here. options args(argc, argv); if (args.help()) { TestFixture::printHelp(); return EXIT_SUCCESS; } const std::size_t failedTestsCount = TestFixture::runTests(args); return (failedTestsCount == 0) ? EXIT_SUCCESS : EXIT_FAILURE; #ifdef NDEBUG } catch (const InternalError& e) { std::cout << e.errorMessage << std::endl; } catch (const std::exception& error) { std::cout << error.what() << std::endl; } catch (...) { std::cout << "Unknown exception" << std::endl; } return EXIT_FAILURE; #endif } cppcheck-2.7/test/testrunner.vcxproj000077500000000000000000000444521417746362400200010ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {c183db5b-ad6c-423d-80ca-1f9549555a1a} Create Create Create Create {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4} testrunner 10.0 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 $(SolutionDir)bin\debug\ $(SolutionDir)bin\debug\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ testrunner testrunner true true $(SolutionDir)bin\ $(SolutionDir)bin\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ testrunner testrunner true true true true ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level4 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true Use precompiled.h precompiled.h true stdcpp14 /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) EnableFastChecks shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true true 8000000 8000000 ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL Level4 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true Use precompiled.h precompiled.h true stdcpp14 /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true 8000000 8000000 ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDLL Level4 AnySuitable true Speed true true true 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true Use precompiled.h precompiled.h /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpp14 shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true true 8000000 8000000 ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL Level4 AnySuitable true Speed true true true 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 true Use precompiled.h precompiled.h /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) true stdcpp14 shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true true true Console true true true true 8000000 8000000 cppcheck-2.7/test/testrunner.vcxproj.filters000066400000000000000000000210451417746362400214360ustar00rootroot00000000000000 {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files cppcheck-2.7/test/testsimplifytemplate.cpp000066400000000000000000011524311417746362400211420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "errortypes.h" #include "platform.h" #include "settings.h" #include "templatesimplifier.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include class TestSimplifyTemplate : public TestFixture { public: TestSimplifyTemplate() : TestFixture("TestSimplifyTemplate") {} private: Settings settings; void run() OVERRIDE { settings.severity.enable(Severity::portability); // If there are unused templates, keep those settings.checkUnusedTemplates = true; TEST_CASE(template1); TEST_CASE(template2); TEST_CASE(template3); TEST_CASE(template4); TEST_CASE(template5); TEST_CASE(template6); TEST_CASE(template7); TEST_CASE(template8); TEST_CASE(template9); TEST_CASE(template10); TEST_CASE(template11); TEST_CASE(template12); TEST_CASE(template13); TEST_CASE(template14); TEST_CASE(template15); // recursive templates TEST_CASE(template16); TEST_CASE(template17); TEST_CASE(template18); TEST_CASE(template19); TEST_CASE(template20); TEST_CASE(template21); TEST_CASE(template22); TEST_CASE(template23); TEST_CASE(template24); // #2648 - using sizeof in template parameter TEST_CASE(template25); // #2648 - another test for sizeof template parameter TEST_CASE(template26); // #2721 - passing 'char[2]' as template parameter TEST_CASE(template27); // #3350 - removing unused template in macro call TEST_CASE(template28); TEST_CASE(template30); // #3529 - template < template < .. TEST_CASE(template31); // #4010 - reference type TEST_CASE(template32); // #3818 - mismatching template not handled well TEST_CASE(template33); // #3818,#4544 - inner templates in template instantiation not handled well TEST_CASE(template34); // #3706 - namespace => hang TEST_CASE(template35); // #4074 - A<'x'> a; TEST_CASE(template36); // #4310 - passing unknown template instantiation as template argument TEST_CASE(template37); // #4544 - A a; TEST_CASE(template38); // #4832 - crash on C++11 right angle brackets TEST_CASE(template39); // #4742 - freeze TEST_CASE(template40); // #5055 - template specialization outside struct TEST_CASE(template41); // #4710 - const in instantiation not handled perfectly TEST_CASE(template42); // #4878 - variadic templates TEST_CASE(template43); // #5097 - assert due to '>>' not treated as end of template instantiation TEST_CASE(template44); // #5297 - TemplateSimplifier::simplifyCalculations not eager enough TEST_CASE(template45); // #5814 - syntax error reported for valid code TEST_CASE(template46); // #5816 - syntax error reported for valid code TEST_CASE(template47); // #6023 - syntax error reported for valid code TEST_CASE(template48); // #6134 - 100% CPU upon invalid code TEST_CASE(template49); // #6237 - template instantiation TEST_CASE(template50); // #4272 - simple partial specialization TEST_CASE(template52); // #6437 - crash upon valid code TEST_CASE(template53); // #4335 - bail out for valid code TEST_CASE(template54); // #6587 - memory corruption upon valid code TEST_CASE(template55); // #6604 - simplify "const const" to "const" in template instantiations TEST_CASE(template56); // #7117 - const ternary operator simplification as template parameter TEST_CASE(template57); // #7891 TEST_CASE(template58); // #6021 - use after free (deleted tokens in simplifyCalculations) TEST_CASE(template59); // #8051 - TemplateSimplifier::simplifyTemplateInstantiation failure TEST_CASE(template60); // handling of methods outside template definition TEST_CASE(template61); // daca2, kodi TEST_CASE(template62); // #8314 - inner template instantiation TEST_CASE(template63); // #8576 - qualified type TEST_CASE(template64); // #8683 TEST_CASE(template65); // #8321 TEST_CASE(template66); // #8725 TEST_CASE(template67); // #8122 TEST_CASE(template68); // union TEST_CASE(template69); // #8791 TEST_CASE(template70); // #5289 TEST_CASE(template71); // #8821 TEST_CASE(template72); TEST_CASE(template73); TEST_CASE(template74); TEST_CASE(template75); TEST_CASE(template76); TEST_CASE(template77); TEST_CASE(template78); TEST_CASE(template79); // #5133 TEST_CASE(template80); TEST_CASE(template81); TEST_CASE(template82); // #8603 TEST_CASE(template83); // #8867 TEST_CASE(template84); // #8880 TEST_CASE(template85); // #8902 crash TEST_CASE(template86); // crash TEST_CASE(template87); TEST_CASE(template88); // #6183 TEST_CASE(template89); // #8917 TEST_CASE(template90); // crash TEST_CASE(template91); TEST_CASE(template92); TEST_CASE(template93); // crash TEST_CASE(template94); // #8927 crash TEST_CASE(template95); // #7417 TEST_CASE(template96); // #7854 TEST_CASE(template97); TEST_CASE(template98); // #8959 TEST_CASE(template99); // #8960 TEST_CASE(template100); // #8967 TEST_CASE(template101); // #8968 TEST_CASE(template102); // #9005 TEST_CASE(template103); TEST_CASE(template104); // #9021 TEST_CASE(template105); // #9076 TEST_CASE(template106); TEST_CASE(template107); // #8663 TEST_CASE(template108); // #9109 TEST_CASE(template109); // #9144 TEST_CASE(template110); TEST_CASE(template111); // crash TEST_CASE(template112); // #9146 syntax error TEST_CASE(template113); TEST_CASE(template114); // #9155 TEST_CASE(template115); // #9153 TEST_CASE(template116); // #9178 TEST_CASE(template117); TEST_CASE(template118); TEST_CASE(template119); // #9186 TEST_CASE(template120); TEST_CASE(template121); // #9193 TEST_CASE(template122); // #9147 TEST_CASE(template123); // #9183 TEST_CASE(template124); // #9197 TEST_CASE(template125); TEST_CASE(template126); // #9217 TEST_CASE(template127); // #9225 TEST_CASE(template128); // #9224 TEST_CASE(template129); TEST_CASE(template130); // #9246 TEST_CASE(template131); // #9249 TEST_CASE(template132); // #9250 TEST_CASE(template133); TEST_CASE(template134); TEST_CASE(template135); TEST_CASE(template136); // #9287 TEST_CASE(template137); // #9288 TEST_CASE(template138); TEST_CASE(template139); TEST_CASE(template140); TEST_CASE(template141); // #9337 TEST_CASE(template142); // #9338 TEST_CASE(template143); TEST_CASE(template144); // #9046 TEST_CASE(template145); // syntax error TEST_CASE(template146); // syntax error TEST_CASE(template147); // syntax error TEST_CASE(template148); // syntax error TEST_CASE(template149); // unknown macro TEST_CASE(template150); // syntax error TEST_CASE(template151); // crash TEST_CASE(template152); // #9467 TEST_CASE(template153); // #9483 TEST_CASE(template154); // #9495 TEST_CASE(template155); // #9539 TEST_CASE(template156); TEST_CASE(template157); // #9854 TEST_CASE(template158); // daca crash TEST_CASE(template159); // #9886 TEST_CASE(template160); TEST_CASE(template161); TEST_CASE(template162); TEST_CASE(template163); // #9685 syntax error TEST_CASE(template164); // #9394 TEST_CASE(template165); // #10032 syntax error TEST_CASE(template166); // #10081 hang TEST_CASE(template167); TEST_CASE(template168); TEST_CASE(template169); TEST_CASE(template170); // crash TEST_CASE(template171); // crash TEST_CASE(template172); // #10258 crash TEST_CASE(template173); // #10332 crash TEST_CASE(template174); // #10506 hang TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); TEST_CASE(template_forward_declared_default_parameter); TEST_CASE(template_default_type); TEST_CASE(template_typename); TEST_CASE(template_constructor); // #3152 - template constructor is removed TEST_CASE(syntax_error_templates_1); TEST_CASE(template_member_ptr); // Ticket #5786 - crash upon valid code TEST_CASE(template_namespace_1); TEST_CASE(template_namespace_2); TEST_CASE(template_namespace_3); TEST_CASE(template_namespace_4); TEST_CASE(template_namespace_5); TEST_CASE(template_namespace_6); TEST_CASE(template_namespace_7); // #8768 TEST_CASE(template_namespace_8); TEST_CASE(template_namespace_9); TEST_CASE(template_namespace_10); TEST_CASE(template_namespace_11); // #7145 TEST_CASE(template_pointer_type); TEST_CASE(template_array_type); // Test TemplateSimplifier::templateParameters TEST_CASE(templateParameters); TEST_CASE(templateNamePosition); TEST_CASE(findTemplateDeclarationEnd); TEST_CASE(getTemplateParametersInDeclaration); TEST_CASE(expandSpecialized1); TEST_CASE(expandSpecialized2); TEST_CASE(expandSpecialized3); // #8671 TEST_CASE(expandSpecialized4); TEST_CASE(templateAlias1); TEST_CASE(templateAlias2); TEST_CASE(templateAlias3); // #8315 TEST_CASE(templateAlias4); // #9070 TEST_CASE(templateAlias5); // Test TemplateSimplifier::instantiateMatch TEST_CASE(instantiateMatchTest); TEST_CASE(templateParameterWithoutName); // #8602 Template default parameter without name yields syntax error TEST_CASE(templateTypeDeduction1); // #8962 TEST_CASE(templateTypeDeduction2); TEST_CASE(templateTypeDeduction3); TEST_CASE(templateTypeDeduction4); // #9983 TEST_CASE(templateTypeDeduction5); TEST_CASE(simplifyTemplateArgs1); TEST_CASE(simplifyTemplateArgs2); TEST_CASE(template_variadic_1); // #9144 TEST_CASE(template_variadic_2); // #4349 TEST_CASE(template_variadic_3); // #6172 TEST_CASE(template_variable_1); TEST_CASE(template_variable_2); TEST_CASE(template_variable_3); TEST_CASE(template_variable_4); TEST_CASE(simplifyDecltype); TEST_CASE(castInExpansion); TEST_CASE(fold_expression_1); TEST_CASE(fold_expression_2); TEST_CASE(fold_expression_3); TEST_CASE(fold_expression_4); TEST_CASE(concepts1); TEST_CASE(requires1); TEST_CASE(requires2); TEST_CASE(requires3); TEST_CASE(requires4); TEST_CASE(requires5); TEST_CASE(explicitBool1); TEST_CASE(explicitBool2); } #define tok(...) tok_(__FILE__, __LINE__, __VA_ARGS__) std::string tok_(const char* file, int line, const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { errout.str(""); settings.debugwarnings = debugwarnings; settings.platform(type); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); return tokenizer.tokens()->stringifyList(nullptr, true); } void template1() { const char code[] = "template T f(T val) { T a; }\n" "f(10);"; const char expected[] = "int f ( int val ) ; " "f ( 10 ) ; " "int f ( int val ) { int a ; }"; ASSERT_EQUALS(expected, tok(code)); } void template2() { const char code[] = "template class Fred { T a; };\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template3() { const char code[] = "template class Fred { T data[sz]; };\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template4() { const char code[] = "template class Fred { Fred(); };\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template5() { const char code[] = "template class Fred { };\n" "template Fred::Fred() { }\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { } ; " "Fred :: Fred ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template6() { const char code[] = "template class Fred { };\n" "Fred fred1;\n" "Fred fred2;"; const char expected[] = "class Fred ; " "Fred fred1 ; " "Fred fred2 ; " "class Fred { } ;"; ASSERT_EQUALS(expected, tok(code)); } void template7() { // A template class that is not used => no simplification { const char code[] = "template \n" "class ABC\n" "{\n" "public:\n" " typedef ABC m;\n" "};\n"; const char expected[] = "template < class T > class ABC { public: } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template class ABC {\n" "public:\n" " typedef std::vector type;\n" "};\n" "int main() {\n" " ABC::type v;\n" " v.push_back(4);\n" " return 0;\n" "}\n"; const char wanted[] = "class ABC ; " "int main ( ) { " "std :: vector < int > v ; " "v . push_back ( 4 ) ; " "return 0 ; " "} " "class ABC { public: } ;"; const char current[] = "class ABC ; " "int main ( ) { " "ABC :: type v ; " "v . push_back ( 4 ) ; " "return 0 ; " "} " "class ABC { public: } ;"; TODO_ASSERT_EQUALS(wanted, current, tok(code)); } { const char code[] = "template class ABC {\n" "public:\n" " typedef std::vector type;\n" " void f()\n" " {\n" " ABC::type v;\n" " v.push_back(4);\n" " }\n" "};\n"; const char expected[] = "template < typename T > class ABC { " "public: void f ( ) { " "ABC < int > :: type v ; " "v . push_back ( 4 ) ; " "} " "} ;"; ASSERT_EQUALS(expected, tok(code)); } } // Template definitions but no usage => no expansion void template8() { const char code[] = "template class A;\n" "template class B;\n" "\n" "typedef A x;\n" "typedef B y;\n" "\n" "template class A {\n" " void f() {\n" " B a = B::g();\n" " T b = 0;\n" " if (b)\n" " b = 0;\n" " }\n" "};\n" "\n" "template inline B h() { return B(); }\n"; ASSERT_EQUALS("template < typename T > class A ; " "template < typename T > class B ; " "template < typename T > class A { void f ( ) { B < T > a ; a = B < T > :: g ( ) ; T b ; b = 0 ; if ( b ) { b = 0 ; } } } ; " "template < typename T > B < T > h ( ) { return B < T > ( ) ; }", tok(code)); ASSERT_EQUALS("class A { template < typename T > int foo ( T d ) ; } ;", tok("class A{ template int foo(T d);};")); } void template9() { const char code[] = "template < typename T > class A { } ;\n" "\n" "void f ( ) {\n" " A < int > a ;\n" "}\n" "\n" "template < typename T >\n" "class B {\n" " void g ( ) {\n" " A < T > b = A < T > :: h ( ) ;\n" " }\n" "} ;\n"; // The expected result.. const char expected[] = "class A ; " "void f ( ) { A a ; } " "template < typename T > class B { void g ( ) { A < T > b ; b = A < T > :: h ( ) ; } } ; " "class A { } ;"; ASSERT_EQUALS(expected, tok(code)); } void template10() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " foo<3,int>();\n" "}\n"; // The expected result.. const char expected[] = "int * foo<3,int> ( ) ; " "void f ( ) " "{" " foo<3,int> ( ) ; " "} " "int * foo<3,int> ( ) { return new int [ 3 ] ; }"; ASSERT_EQUALS(expected, tok(code)); } void template11() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " char * p = foo<3,char>();\n" "}\n"; // The expected result.. const char expected[] = "char * foo<3,char> ( ) ; " "void f ( ) " "{" " char * p ; p = foo<3,char> ( ) ; " "} " "char * foo<3,char> ( ) { return new char [ 3 ] ; }"; ASSERT_EQUALS(expected, tok(code)); } void template12() { const char code[] = "template \n" "class A : public B\n" "{ };\n" "\n" "void f()\n" "{\n" " A<12,12,11> a;\n" "}\n"; const char expected[] = "class A<12,12,11> ; " "void f ( ) " "{" " A<12,12,11> a ; " "} " "class A<12,12,11> : public B < 12 , 12 , 0 > " "{ } ;"; ASSERT_EQUALS(expected, tok(code)); } void template13() { const char code[] = "class BB {};\n" "\n" "template \n" "class AA {\n" "public:\n" " static AA create(T* newObject);\n" " static int size();\n" "};\n" "\n" "class CC { public: CC(AA, int) {} };\n" "\n" "class XX {\n" " AA y;\n" "public:\n" " XX();\n" "};\n" "\n" "XX::XX():\n" " y(AA::create(new CC(AA(), 0)))\n" " {}\n" "\n" "int yy[AA::size()];"; const char expected[] = "class BB { } ; " "class AA ; " "class AA ; " "class CC { public: CC ( AA , int ) { } } ; " "class XX { " "AA y ; " "public: " "XX ( ) ; " "} ; " "XX :: XX ( ) : " "y ( AA :: create ( new CC ( AA ( ) , 0 ) ) ) " "{ } " "int yy [ AA :: size ( ) ] ; " "class AA { " "public: " "static AA create ( BB * newObject ) ; " "static int size ( ) ; " "} ; " "class AA { " "public: " "static AA create ( CC * newObject ) ; " "static int size ( ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template14() { const char code[] = "template <> void foo()\n" "{ x(); }\n" "\n" "int main()\n" "{\n" "foo();\n" "}\n"; const char expected[] = "void foo ( ) ; " "void foo ( ) " "{ x ( ) ; } " "int main ( ) " "{ foo ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); } void template15() { // recursive templates #3130 etc const char code[] = "template void a()\n" "{\n" " a();\n" "}\n" "\n" "template <> void a<0>()\n" "{ }\n" "\n" "int main()\n" "{\n" " a<2>();\n" " return 0;\n" "}\n"; // The expected result.. const char expected[] = "void a<0> ( ) ; " "void a<2> ( ) ; " "void a<1> ( ) ; " "void a<0> ( ) { } " "int main ( ) " "{ a<2> ( ) ; return 0 ; } " "void a<2> ( ) { a<1> ( ) ; } " "void a<1> ( ) { a<0> ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); // #3130 const char code2[] = "template struct vec {\n" " vec() {}\n" " vec(const vec& v) {}\n" // <- never used don't instantiate "};\n" "\n" "vec<4> v;"; const char expected2[] = "struct vec<4> ; " "vec<4> v ; " "struct vec<4> { " "vec<4> ( ) { } " "vec<4> ( const vec < 4 - 1 > & v ) { } " "} ;"; ASSERT_EQUALS(expected2, tok(code2)); } void template16() { const char code[] = "template void a()\n" "{ }\n" "\n" "template void b()\n" "{ a(); }\n" "\n" "int main()\n" "{\n" " b<2>();\n" " return 0;\n" "}\n"; const char expected[] = "void a<2> ( ) ; " "void b<2> ( ) ; " "int main ( ) { b<2> ( ) ; return 0 ; } " "void b<2> ( ) { a<2> ( ) ; } " "void a<2> ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template17() { const char code[] = "template\n" "class Fred\n" "{\n" " template\n" " static shared_ptr< Fred > CreateFred()\n" " {\n" " }\n" "};\n" "\n" "shared_ptr i;\n"; const char expected[] = "template < class T > " "class Fred " "{ " "template < class T > " "static shared_ptr < Fred < T > > CreateFred ( ) " "{ " "} " "} ; " "shared_ptr < int > i ;"; ASSERT_EQUALS(expected, tok(code)); } void template18() { const char code[] = "template class foo { T a; };\n" "foo *f;"; const char expected[] = "class foo ; " "foo * f ; " "class foo { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template19() { const char code[] = "template T & foo()\n" "{ static T temp; return temp; }\n" "\n" "void f ( )\n" "{\n" " char p = foo();\n" "}\n"; // The expected result.. const char expected[] = "char & foo ( ) ; " "void f ( ) " "{" " char p ; p = foo ( ) ; " "} " "char & foo ( ) { static char temp ; return temp ; }"; ASSERT_EQUALS(expected, tok(code)); } void template20() { // Ticket #1788 - the destructor implementation is lost const char code[] = "template class A { public: ~A(); };\n" "template A::~A() {}\n" "A a;\n"; // The expected result.. const char expected[] = "class A ; " "A a ; " "class A { public: ~ A ( ) ; } ; " "A :: ~ A ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template21() { { const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { T data[sz]; };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { Fred(); };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { };\n" "Fred fred1;\n" "Fred fred2;"; const char expected[] = "struct Fred ; " "Fred fred1 ; " "Fred fred2 ; " "struct Fred { } ;"; ASSERT_EQUALS(expected, tok(code)); } } void template22() { const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { std :: string a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template23() { const char code[] = "template void foo() { }\n" "void bar() {\n" " std::cout << (foo());\n" "}"; const char expected[] = "void foo ( ) ; " "void bar ( ) {" " std :: cout << ( foo ( ) ) ; " "} " "void foo ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template24() { // #2648 const char code[] = "template struct B\n" "{\n" " int a[n];\n" "};\n" "\n" "template class bitset: B\n" "{};\n" "\n" "bitset<1> z;"; const char expected[] = "struct B<4> ; " "class bitset<1> ; " "bitset<1> z ; " "class bitset<1> : B<4> { } ; " "struct B<4> { int a [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template25() { const char code[] = "template struct B\n" "{\n" " int a[n];\n" "};\n" "\n" "template class bitset: B<((sizeof(int)) ? : 1)>\n" "{};\n" "\n" "bitset<1> z;"; const char expected[] = "struct B<4> ; " "class bitset<1> ; " "bitset<1> z ; " "class bitset<1> : B<4> { } ; " "struct B<4> { int a [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template26() { // #2721 const char code[] = "template\n" "class A { public: T x; };\n" "\n" "template\n" "class C: public A {};\n" "\n" "C<2> a;\n"; ASSERT_EQUALS("class A ; class C<2> ; C<2> a ; class C<2> : public A { } ; class A { public: char [ 2 ] x ; } ;", tok(code)); } void template27() { // #3350 - template inside macro call const char code[] = "X(template class Fred);"; ASSERT_THROW(tok(code), InternalError); } void template28() { // #3226 - inner template const char code[] = "template class Fred {};\n" "Fred > x;\n"; ASSERT_EQUALS("class Fred ; " "class Fred> ; " "Fred> x ; " "class Fred { } ; " "class Fred> { } ;", tok(code)); } void template30() { // #3529 - template < template < .. const char code[] = "template class A, class B> void f(){}"; ASSERT_EQUALS("template < template < class > class A , class B > void f ( ) { }", tok(code)); } void template31() { // #4010 - template reference type const char code[] = "template struct A{}; A a;"; ASSERT_EQUALS("struct A ; " "A a ; " "struct A { } ;", tok(code)); // #7409 - rvalue const char code2[] = "template struct A{}; A a;"; ASSERT_EQUALS("struct A ; " "A a ; " "struct A { } ;", tok(code2)); } void template32() { // #3818 - mismatching template not handled well const char code[] = "template struct A { };\n" "\n" "template \n" "struct B\n" "{\n" " public:\n" " A < int, Pair, int > a;\n" // mismatching parameters => don't instantiate "};\n" "\n" "B b;\n"; ASSERT_EQUALS("template < class T1 , class T2 , class T3 , class T4 > struct A { } ; " "struct B ; " "B b ; " "struct B { public: A < int , Pair < int , int > , int > a ; } ;", tok(code)); } void template33() { { // #3818 - inner templates in template instantiation not handled well const char code[] = "template struct A { };\n" "template struct B { };\n" "template struct C { A > > ab; };\n" "C c;"; ASSERT_EQUALS("struct A>> ; " "struct B> ; " "struct C ; " "C c ; " "struct C { A>> ab ; } ; " "struct B> { } ; " // <- redundant.. but nevermind "struct A>> { } ;", tok(code)); } { // #4544 const char code[] = "struct A { };\n" "template struct B { };\n" "template struct C { };\n" "C< B > c;"; ASSERT_EQUALS("struct A { } ; " "template < class T > struct B { } ; " // <- redundant.. but nevermind "struct C> ; " "C> c ; " "struct C> { } ;", tok(code)); } } void template34() { // #3706 - namespace => hang const char code[] = "namespace abc {\n" "template struct X { void f(X &x) {} };\n" "}\n" "template <> int X::Y(0);"; tok(code); } void template35() { // #4074 - "A<'x'> a;" is not recognized as template instantiation const char code[] = "template class A {};\n" "A <'x'> a;"; ASSERT_EQUALS("class A<'x'> ; " "A<'x'> a ; " "class A<'x'> { } ;", tok(code)); } void template36() { // #4310 - Passing unknown template instantiation as template argument const char code[] = "template struct X { T t; };\n" "template struct Y { Foo < X< Bar > > _foo; };\n" // <- Bar is unknown "Y bar;"; ASSERT_EQUALS("struct X> ; " "struct Y ; " "Y bar ; " "struct Y { Foo < X> > _foo ; } ; " "struct X> { Bar < int > t ; } ;", tok(code)); } void template37() { // #4544 - A a; { const char code[] = "class A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("class A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } { const char code[] = "struct A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("struct A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } { const char code[] = "enum A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("enum A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } } void template_unhandled() { // An unhandled template usage should not be simplified.. ASSERT_EQUALS("x < int > ( ) ;", tok("x();")); } void template38() { // #4832 - Crash on C++11 right angle brackets const char code[] = "template class A {\n" " T mT;\n" "public:\n" " void foo() {}\n" "};\n" "\n" "int main() {\n" " A> gna1;\n" " A gna2;\n" "}\n"; const char expected[] = "class A ; " "class A> ; " "int main ( ) { " "A> gna1 ; " "A gna2 ; " "} " "class A { " "BLA mT ; " "public: " "void foo ( ) { } " "} ; " "class A> { " "A mT ; " "public: " "void foo ( ) { } " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template39() { // #4742 - Used to freeze in 1.60 const char code[] = "template struct vector {" " operator T() const;" "};" "void f() {" " vector> v;" " const vector vi = static_cast>(v);" "}"; tok(code); } void template40() { // #5055 - false negatives when there is template specialization outside struct const char code[] = "struct A {" " template struct X { T t; };" "};" "template<> struct A::X { int *t; };"; const char expected[] = "struct A { " "struct X ; " "template < typename T > struct X { T t ; } ; " "} ; " "struct A :: X { int * t ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template41() { // #4710 - const in template instantiation not handled perfectly const char code1[] = "template struct X { };\n" "void f(const X x) { }"; ASSERT_EQUALS("struct X ; " "void f ( const X x ) { } " "struct X { } ;", tok(code1)); const char code2[] = "template T f(T t) { return t; }\n" "int x() { return f(123); }"; ASSERT_EQUALS("int f ( int t ) ; " "int x ( ) { return f ( 123 ) ; } " "int f ( int t ) { return t ; }", tok(code2)); } void template42() { // #4878 cppcheck aborts in ext-blocks.cpp (clang testcode) const char code[] = "template\n" "int f0(Args ...args) {\n" " return ^ {\n" " return sizeof...(Args);\n" " }() + ^ {\n" " return sizeof...(args);\n" " }();\n" "}"; ASSERT_THROW(tok(code), InternalError); } void template43() { // #5097 - Assert due to '>>' in 'B>' not being treated as end of template instantiation const char code[] = "template struct E { typedef int Int; };\n" "template struct C { };\n" "template struct D { static int f() { return C::f(); } };\n" "template inline int f2() { return D::f(); }\n" "template int f1 (int x, T *) { int id = f2(); return id; }\n" "template struct B { void f3(B & other) { } };\n" "struct A { };\n" "template <> struct C> {\n" " static int f() { return f1>(0, reinterpret_cast*>(E::Int(-1))); }\n" "};\n" "int main(void) {\n" " C ca;\n" " return 0;\n" "}"; const char expected[] = "struct E ; " "struct C> ; " "struct C ; " "struct D> ; " "int f2> ( ) ; " "int f1> ( int x , B * ) ; " "struct B ; " "struct A { } ; " "struct C> { " "static int f ( ) { " "return f1> ( 0 , reinterpret_cast < B * > ( E :: Int ( -1 ) ) ) ; " "} " "} ; " "int main ( void ) { " "C ca ; " "return 0 ; " "} " "struct B { " "void f3 ( B & other ) { } " "} ; " "int f1> ( int x , B * ) { " "int id ; id = f2> ( ) ; " "return id ; " "} " "int f2> ( ) { " "return D> :: f ( ) ; " "} " "struct D> { " "static int f ( ) { " "return C> :: f ( ) ; " "} " "} ; " "struct C { } ; struct E { " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template44() { // #5297 const char code[] = "template struct StackContainer {" " void foo(int i) {" " if (0 >= 1 && i<0) {}" " }" "};" "template class ZContainer : public StackContainer {};" "struct FGSTensor {};" "class FoldedZContainer : public ZContainer {};"; const char expected[] = "struct StackContainer ; " "class ZContainer ; " "struct FGSTensor { } ; " "class FoldedZContainer : public ZContainer { } ; " "class ZContainer : public StackContainer { } ; " "struct StackContainer { " "void foo ( int i ) { " "if ( 0 >= 1 && i < 0 ) { } " "} " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template45() { // #5814 const char code[] = "namespace Constants { const int fourtytwo = 42; } " "template struct TypeMath { " " static const int mult = sizeof(T) * U; " "}; " "template struct FOO { " " enum { value = TypeMath::mult }; " "}; " "FOO foo;"; const char expected[] = "namespace Constants { const int fourtytwo = 42 ; } " "struct TypeMath ; " "struct FOO ; " "FOO foo ; " "struct FOO { " "enum Anonymous0 { value = TypeMath :: mult } ; " "} ; " "struct TypeMath { " "static const int mult = sizeof ( int ) * Constants :: fourtytwo ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true)); ASSERT_EQUALS("", errout.str()); } void template46() { // #5816 tok("template struct A { static const int value = 0; }; " "template struct B { " " enum { value = A::value }; " "};"); ASSERT_EQUALS("", errout.str()); tok("template struct A {}; " "enum { e = sizeof(A) }; " "template struct B {};"); ASSERT_EQUALS("", errout.str()); tok("template struct A { static const int value = 0; }; " "template struct B { typedef int type; }; " "template struct C { " " enum { value = A::type, int>::value }; " "};"); ASSERT_EQUALS("", errout.str()); } void template47() { // #6023 tok("template > class C1 {}; " "class C2 : public C1 {};"); ASSERT_EQUALS("", errout.str()); } void template48() { // #6134 tok("template int f( { } ); " "int foo = f<1>(0);"); ASSERT_EQUALS("", errout.str()); } void template49() { // #6237 const char code[] = "template class Fred { void f(); void g(); };\n" "template void Fred::f() { }\n" "template void Fred::g() { }\n" "template void Fred::f();\n" "template void Fred::g();\n"; const char expected[] = "class Fred ; " "class Fred ; " "class Fred { void f ( ) ; void g ( ) ; } ; " "void Fred :: f ( ) { } " "void Fred :: g ( ) { } " "class Fred { void f ( ) ; void g ( ) ; } ; " "void Fred :: f ( ) { } " "void Fred :: g ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template50() { // #4272 const char code[] = "template class Fred { void f(); };\n" "template void Fred::f() { }\n" "template<> void Fred::f() { }\n" "template<> void Fred::f() { }\n"; const char expected[] = "class Fred ; " "class Fred ; " "template < > void Fred :: f ( ) { } " "template < > void Fred :: f ( ) { } " "class Fred { void f ( ) ; } ; " "void Fred :: f ( ) { } " "class Fred { void f ( ) ; } ; " "void Fred :: f ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template52() { // #6437 const char code[] = "template int sum() { " " return value + sum(); " "} " "template int calculate_value() { " " if (x != y) { " " return sum(); " " } else { " " return 0; " " } " "} " "int value = calculate_value<1,1>();"; const char expected[] = "int sum<0> ( ) ; " "int calculate_value<1,1> ( ) ; " "int value ; value = calculate_value<1,1> ( ) ; " "int calculate_value<1,1> ( ) { " "if ( 1 != 1 ) { " "return sum<0> ( ) ; " "} else { " "return 0 ; " "} " "} " "int sum<0> ( ) { " "return 0 + sum<0> ( ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void template53() { // #4335 const char code[] = "template struct Factorial { " " enum { value = N * Factorial::value }; " "};" "template <> struct Factorial<0> { " " enum { value = 1 }; " "};" "const int x = Factorial<4>::value;"; const char expected[] = "struct Factorial<0> ; " "struct Factorial<4> ; " "struct Factorial<3> ; " "struct Factorial<2> ; " "struct Factorial<1> ; " "struct Factorial<0> { " "enum Anonymous1 { value = 1 } ; " "} ; " "const int x = Factorial<4> :: value ; " "struct Factorial<4> { " "enum Anonymous0 { value = 4 * Factorial<3> :: value } ; " "} ; " "struct Factorial<3> { " "enum Anonymous0 { value = 3 * Factorial<2> :: value } ; " "} ; " "struct Factorial<2> { " "enum Anonymous0 { value = 2 * Factorial<1> :: value } ; " "} ; " "struct Factorial<1> { " "enum Anonymous0 { value = 1 * Factorial<0> :: value } ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true)); ASSERT_EQUALS("", errout.str()); } void template54() { // #6587 tok("template _Tp* fn(); " "template struct A { " " template ())> " " struct B { }; " "}; " "A a;"); } void template55() { // #6604 // Avoid constconstconst in macro instantiations ASSERT_EQUALS( "template < class T > class AtSmartPtr : public ConstCastHelper < AtSmartPtr < const T > , T > { " "friend struct ConstCastHelper < AtSmartPtr < const T > , T > ; " "AtSmartPtr ( const AtSmartPtr < T > & r ) ; " "} ;", tok("template class AtSmartPtr : public ConstCastHelper, T>\n" "{\n" " friend struct ConstCastHelper, T>;\n" " AtSmartPtr(const AtSmartPtr& r);\n" "};")); // Similar problem can also happen with ... ASSERT_EQUALS( "struct A ; " "struct A ; " "A a ( 0 ) ; " "struct A { " "A ( int * p ) { ( A * ) ( p ) ; } " "} ; " "struct A { " "A ( int * p ) { " "( A * ) ( p ) ; " "} } ;", tok("template struct A\n" "{\n" " A(T* p) {\n" " (A*)(p);\n" " }\n" "};\n" "A a(0);")); } void template56() { // #7117 const char code[] = "template struct Foo { " " std::array mfoo; " "}; " "void foo() { " " Foo myFoo; " "}"; const char expected[] = "struct Foo ; " "void foo ( ) { " "Foo myFoo ; " "} struct Foo { " "std :: array < int , 1 > mfoo ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true)); ASSERT_EQUALS("", errout.str()); } void template57() { // #7891 const char code[] = "template struct Test { Test(T); };\n" "Test test( 0 );"; const char exp[] = "struct Test ; " "Test test ( 0 ) ; " "struct Test { Test ( unsigned long ) ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template58() { // #6021 const char code[] = "template \n" "void TestArithmetic() {\n" " x(1 * CheckedNumeric());\n" "}\n" "void foo() {\n" " TestArithmetic();\n" "}"; const char exp[] = "void TestArithmetic ( ) ; " "void foo ( ) {" " TestArithmetic ( ) ; " "} " "void TestArithmetic ( ) {" " x ( 1 * CheckedNumeric < int > ( ) ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template59() { // #8051 const char code[] = "template\n" "struct Factorial {\n" " enum FacHelper { value = N * Factorial::value };\n" "};\n" "template <>\n" "struct Factorial<0> {\n" " enum FacHelper { value = 1 };\n" "};\n" "template\n" "int diagonalGroupTest() {\n" " return Factorial::value;\n" "}\n" "int main () {\n" " return diagonalGroupTest<4>();\n" "}"; const char exp[] = "struct Factorial<0> ; " "struct Factorial<4> ; " "struct Factorial<3> ; " "struct Factorial<2> ; " "struct Factorial<1> ; " "struct Factorial<0> { enum FacHelper { value = 1 } ; } ; " "int diagonalGroupTest<4> ( ) ; " "int main ( ) { return diagonalGroupTest<4> ( ) ; } " "int diagonalGroupTest<4> ( ) { return Factorial<4> :: value ; } " "struct Factorial<4> { enum FacHelper { value = 4 * Factorial<3> :: value } ; } ; " "struct Factorial<3> { enum FacHelper { value = 3 * Factorial<2> :: value } ; } ; " "struct Factorial<2> { enum FacHelper { value = 2 * Factorial<1> :: value } ; } ; " "struct Factorial<1> { enum FacHelper { value = 1 * Factorial<0> :: value } ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template60() { // Extracted from Clang testfile const char code[] = "template struct S { typedef int type; };\n" "template void f() {}\n" "template void h() { f::type(0)>(); }\n" "\n" "void j() { h(); }"; const char exp[] = "struct S ; " "void f::type(0)> ( ) ; " "void h ( ) ; " "void j ( ) { h ( ) ; } " "void h ( ) { f::type(0)> ( ) ; } " "struct S { } ; " "void f::type(0)> ( ) { }"; const char act[] = "template < typename T > struct S { } ; " "void f::type(0)> ( ) ; " "void h ( ) ; " "void j ( ) { h ( ) ; } " "void h ( ) { f::type(0)> ( ) ; } " "void f::type(0)> ( ) { }"; TODO_ASSERT_EQUALS(exp, act, tok(code)); } void template61() { // hang in daca, code extracted from kodi const char code[] = "template struct Foo {};\n" "template struct Bar {\n" " void f1(Bar x) {}\n" " Foo> f2() { }\n" "};\n" "Bar c;"; const char exp[] = "struct Foo> ; " "struct Bar ; " "Bar c ; " "struct Bar {" " void f1 ( Bar x ) { }" " Foo> f2 ( ) { } " "} ; " "struct Foo> { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template62() { // #8314 const char code[] = "template struct C1 {};\n" "template void f() { x = y ? C1::allocate(1) : 0; }\n" "template class C3 {};\n" "template C3::C3(const C3 &v) { C1 c1; }\n" "C3 c3;"; const char exp[] = "struct C1 ; " "template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } " "class C3 ; " "C3 c3 ; " "class C3 { } ; " "C3 :: C3 ( const C3 & v ) { C1 c1 ; } " "struct C1 { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template63() { // #8576 const char code[] = "template struct TestClass { T m_hi; };" "TestClass> objTest3;"; const char exp[] = "struct TestClass> ; " "TestClass> objTest3 ; " "struct TestClass> { std :: auto_ptr < v > m_hi ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template64() { // #8683 const char code[] = "template \n" "bool foo(){return true;}\n" "struct A {\n" "template\n" "void t_func()\n" "{\n" " if( n != 0 || foo());\n" "}\n" "void t_caller()\n" "{\n" " t_func<0>();\n" " t_func<1>();\n" "}\n" "};"; const char exp[] = "bool foo ( ) ; " "struct A { " "void t_func<0> ( ) ; " "void t_func<1> ( ) ; " "void t_caller ( ) " "{ " "t_func<0> ( ) ; " "t_func<1> ( ) ; " "} " "} ; " "void A :: t_func<0> ( ) " "{ " "if ( 0 != 0 || foo ( ) ) { ; } " "} " "void A :: t_func<1> ( ) " "{ " "if ( 1 != 0 || foo ( ) ) { ; } " "} " "bool foo ( ) { return true ; }"; ASSERT_EQUALS(exp, tok(code)); } void template65() { // #8321 (crash) const char code[] = "namespace bpp\n" "{\n" "template\n" "class AssociationDAGraphImplObserver :\n" " public AssociationGraphImplObserver\n" "{};\n" "template\n" "using AssociationDAGlobalGraphObserver = AssociationDAGraphImplObserver;\n" "}\n" "using namespace bpp;\n" "using namespace std;\n" "int main() {\n" " AssociationDAGlobalGraphObserver grObs;\n" " return 1;\n" "}"; const char exp[] = "namespace bpp " "{ " "class AssociationDAGraphImplObserver ; " "} " "using namespace bpp ; " "int main ( ) { " "bpp :: AssociationDAGraphImplObserver grObs ; " "return 1 ; " "} class bpp :: AssociationDAGraphImplObserver : " "public AssociationGraphImplObserver < std :: string , unsigned int , DAGlobalGraph > " "{ } ;"; ASSERT_EQUALS(exp, tok(code)); } void template66() { // #8725 const char code[] = "template struct Fred {\n" " const int ** foo();\n" "};\n" "template const int ** Fred::foo() { return nullptr; }\n" "Fred fred;"; const char exp[] = "struct Fred ; " "Fred fred ; " "struct Fred { " "const int * * foo ( ) ; " "} ; " "const int * * Fred :: foo ( ) { return nullptr ; }"; ASSERT_EQUALS(exp, tok(code)); } void template67() { // ticket #8122 const char code[] = "template struct Container {\n" " Container();\n" " Container(const Container &);\n" " Container & operator = (const Container &);\n" " ~Container();\n" " T* mElements;\n" " const Container * c;\n" "};\n" "template Container::Container() : mElements(nullptr), c(nullptr) {}\n" "template Container::Container(const Container & x) { nElements = x.nElements; c = x.c; }\n" "template Container & Container::operator = (const Container & x) { mElements = x.mElements; c = x.c; return *this; }\n" "template Container::~Container() {}\n" "Container intContainer;"; const char expected[] = "struct Container ; " "Container intContainer ; " "struct Container { " "Container ( ) ; " "Container ( const Container & ) ; " "Container & operator= ( const Container & ) ; " "~ Container ( ) ; " "int * mElements ; " "const Container * c ; " "} ; " "Container :: Container ( ) : mElements ( nullptr ) , c ( nullptr ) { } " "Container :: Container ( const Container & x ) { nElements = x . nElements ; c = x . c ; } " "Container & Container :: operator= ( const Container & x ) { mElements = x . mElements ; c = x . c ; return * this ; } " "Container :: ~ Container ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template68() { const char code[] = "template union Fred {\n" " char dummy[sizeof(T)];\n" " T value;\n" "};\n" "Fred fred;"; const char exp[] = "union Fred ; " "Fred fred ; " "union Fred { " "char dummy [ sizeof ( int ) ] ; " "int value ; " "} ;"; ASSERT_EQUALS(exp, tok(code)); } void template69() { // #8791 const char code[] = "class Test {\n" " int test;\n" " template T lookup() { return test; }\n" " int Fun() { return lookup(); }\n" "};"; const char exp[] = "class Test { " "int test ; " "int lookup ( ) ; " "int Fun ( ) { return lookup ( ) ; } " "} ; " "int Test :: lookup ( ) { return test ; }"; ASSERT_EQUALS(exp, tok(code)); } void template70() { // #5289 const char code[] = "template class Bar;\n" "template<>\n" "class Bar {\n" "};\n" "template\n" "class Bar : private Bar {\n" " void foo() { }\n" "};"; const char exp[] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " "class Bar ; " "class Bar { " "} ; " "template < typename K , typename V , int KeySize = 0 > " "class Bar : private Bar { " "void foo ( ) { } " "} ;"; const char act[] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " "class Bar { " "} ; " "class Bar ; " "template < typename K , typename V , int KeySize = 0 > " "class Bar : private Bar { " "void foo ( ) { } " "} ;"; TODO_ASSERT_EQUALS(exp, act, tok(code)); } void template71() { // #8821 const char code[] = "int f1(int * pInterface, int x) { return 0; }\n" "\n" "template< class interface_type > class Reference {\n" " template< class interface_type > int i();\n" " int *pInterface;\n" "};\n" "\n" "template< class interface_type > int Reference< interface_type >::i() {\n" " return f1(pInterface, interface_type::static_type());\n" "}\n" "\n" "Reference< class XPropertyList > dostuff();"; const char exp[] = "int f1 ( int * pInterface , int x ) { return 0 ; } " "class Reference ; " "Reference dostuff ( ) ; " "class Reference { template < class XPropertyList > int i ( ) ; int * pInterface ; } ; " "int Reference :: i ( ) { return f1 ( pInterface , XPropertyList :: static_type ( ) ) ; }"; ASSERT_EQUALS(exp, tok(code)); } void template72() { const char code[] = "template class Tokenizer;\n" "const Tokenizer *tokenizer() const;\n" "template \n" "Tokenizer::Tokenizer() { }"; const char exp[] = "template < typename N , typename P > class Tokenizer ; " "const Tokenizer < Node , Path > * tokenizer ( ) const ; " "template < typename N , typename P > " "Tokenizer < N , P > :: Tokenizer ( ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template73() { const char code[] = "template\n" "void keep_range(T& value, const T mini, const T maxi){}\n" "template void keep_range(float& v, const float l, const float u);\n" "template void keep_range(int& v, const int l, const int u);"; const char exp[] = "void keep_range ( float & value , const float mini , const float maxi ) ; " "void keep_range ( int & value , const int mini , const int maxi ) ; " "void keep_range ( float & value , const float mini , const float maxi ) { } " "void keep_range ( int & value , const int mini , const int maxi ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template74() { const char code[] = "template class BTlist { };\n" "class PushBackStreamBuf {\n" "public:\n" " void pushBack(const BTlist &vec);\n" "};"; const char exp[] = "class BTlist ; " "class PushBackStreamBuf { " "public: " "void pushBack ( const BTlist & vec ) ; " "} ; " "class BTlist { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template75() { const char code[] = "template\n" "T foo(T& value){ return value; }\n" "template std::vector> foo>>(std::vector>& v);"; const char exp[] = "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) ; " "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; ASSERT_EQUALS(exp, tok(code)); } void template76() { const char code[] = "namespace NS {\n" " template T foo(T& value) { return value; }\n" " template std::vector> foo>>(std::vector>& v);\n" "}\n" "std::vector> v;\n" "v = foo>>(v);\n"; const char exp[] = "namespace NS { " "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) ; " "} " "std :: vector < std :: vector < int > > v ; " "v = foo>> ( v ) ; " "std :: vector < std :: vector < int > > NS :: foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; ASSERT_EQUALS(exp, tok(code)); } void template77() { const char code[] = "template\n" "struct is_void : std::false_type { };\n" "template<>\n" "struct is_void : std::true_type { };\n" "int main() {\n" " std::cout << is_void::value << std::endl;\n" " std::cout << is_void::value << std::endl;\n" "}"; const char exp[] = "struct is_void ; " "struct is_void ; " "struct is_void : std :: true_type { } ; " "int main ( ) { " "std :: cout << is_void :: value << std :: endl ; " "std :: cout << is_void :: value << std :: endl ; " "} " "struct is_void : std :: false_type { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template78() { const char code[] = "template \n" "struct Base { };\n" "struct S : Base ::Type { };"; const char exp[] = "struct Base ; " "struct S : Base :: Type { } ; " "struct Base { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template79() { // #5133 const char code[] = "class Foo {\n" "public:\n" " template void foo() { bar(); }\n" "private:\n" " template void bar() { bazz(); }\n" " void bazz() { }\n" "};\n" "void some_func() {\n" " Foo x;\n" " x.foo();\n" "}"; const char exp[] = "class Foo { " "public: " "void foo ( ) ; " "private: " "void bar ( ) ; " "void bazz ( ) { } " "} ; " "void some_func ( ) { " "Foo x ; " "x . foo ( ) ; " "} " "void Foo :: foo ( ) { bar ( ) ; } " "void Foo :: bar ( ) { bazz ( ) ; }"; ASSERT_EQUALS(exp, tok(code)); } void template80() { const char code[] = "class Fred {\n" " template T foo(T t) const { return t; }\n" "};\n" "const void * p = Fred::foo(nullptr);"; const char exp[] = "class Fred { " "const void * foo ( const void * t ) const ; " "} ; " "const void * p ; p = Fred :: foo ( nullptr ) ; " "const void * Fred :: foo ( const void * t ) const { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } void template81() { const char code[] = "template \n" "struct SortWith {\n" " SortWith(Type);\n" "};\n" "template \n" "SortWith::SortWith(Type) {}\n" "int main() {\n" " SortWith(0);\n" "}"; const char exp[] = "template < typename Type > " "struct SortWith { " "SortWith ( Type ) ; " "} ; " "SortWith :: SortWith ( int ) ; " "int main ( ) { " "SortWith ( 0 ) ; " "} " "SortWith :: SortWith ( int ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template82() { // 8603 const char code[] = "typedef int comp;\n" "const int f16=16;\n" "template\n" "class tvec2 {};\n" "template\n" "class tvec3 {};\n" "namespace swizzle {\n" "template void swizzle(tvec2 v) { }\n" "template void swizzle(tvec3 v) { }\n" "}\n" "void foo() {\n" " using namespace swizzle;\n" " tvec2 tt2;\n" " swizzle<1>(tt2);\n" " tvec3 tt3;\n" " swizzle<2,3>(tt3);\n" "}"; const char exp[] = "const int f16 = 16 ; " "class tvec2 ; " "class tvec3 ; " "namespace swizzle { " "void swizzle<1> ( tvec2 v ) ; " "void swizzle<2,3> ( tvec3 v ) ; " "} " "void foo ( ) { " "using namespace swizzle ; " "tvec2 tt2 ; " "swizzle :: swizzle<1> ( tt2 ) ; " "tvec3 tt3 ; " "swizzle :: swizzle<2,3> ( tt3 ) ; " "} " "void swizzle :: swizzle<2,3> ( tvec3 v ) { } " "void swizzle :: swizzle<1> ( tvec2 v ) { } " "class tvec3 { } ; " "class tvec2 { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template83() { // #8867 const char code[] = "template\n" "class MultiConsumer {\n" " MultiConsumer();\n" "};\n" "template\n" "MultiConsumer::MultiConsumer() : sizeBuffer(0) {}\n" "MultiReads::MultiReads() {\n" " mc = new MultiConsumer();\n" "}"; const char exp[] = "template < typename Task > " // TODO: this should be expanded "class MultiConsumer { " "MultiConsumer ( ) ; " "} ; " "MultiConsumer :: MultiConsumer ( ) ; " "MultiReads :: MultiReads ( ) { " "mc = new MultiConsumer ( ) ; " "} " "MultiConsumer :: MultiConsumer ( ) : sizeBuffer ( 0 ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template84() { // #8880 { const char code[] = "template \n" "auto d() -> typename a::e {\n" " d();\n" "}"; const char exp[] = "template < class b , int c , class > " "auto d ( ) . a < decltype ( b { } ) > :: e { " "d < int , c , int > ( ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "template \n" "auto d() -> typename a::e {\n" " d();\n" "}" "void foo() { d(); }"; const char exp[] = "auto d ( ) . a < char > :: e ; " "auto d ( ) . a < int > :: e ; " "void foo ( ) { d ( ) ; } " "auto d ( ) . a < char > :: e { " "d ( ) ; " "} " "auto d ( ) . a < int > :: e { " "d ( ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } } void template85() { // #8902 - crash const char code[] = "template\n" "struct C\n" "{\n" " template::value)>::type* = nullptr>\n" " void foo();\n" "};\n" "extern template void C::foo();\n" "template\n" "template::value)>::type>\n" "void C::foo() {}"; // @todo the output is very wrong but we are only worried about the crash for now tok(code); } void template86() { // crash const char code[] = "struct S {\n" " S();\n" "};\n" "template \n" "struct U {\n" " static S u;\n" "};\n" "template \n" "S U::u;\n" "template S U::u;\n" "S &i = U::u;"; tok(code); } void template87() { const char code[] = "template\n" "T f1(T t) { return t; }\n" "template const char * f1(const char *);\n" "template const char & f1(const char &);"; const char exp[] = "const char * f1 ( const char * t ) ; " "const char & f1 ( const char & t ) ; " "const char * f1 ( const char * t ) { return t ; } " "const char & f1 ( const char & t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } void template88() { // #6183.cpp const char code[] = "class CTest {\n" "public:\n" " template \n" " static void Greeting(T val) {\n" " std::cout << val << std::endl;\n" " }\n" "private:\n" " static void SayHello() {\n" " std::cout << \"Hello World!\" << std::endl;\n" " }\n" "};\n" "template<>\n" "void CTest::Greeting(bool) {\n" " CTest::SayHello();\n" "}\n" "int main() {\n" " CTest::Greeting(true);\n" " return 0;\n" "}"; const char exp[] = "class CTest { " "public: " "static void Greeting ( bool ) ; " "template < typename T > " "static void Greeting ( T val ) { " "std :: cout << val << std :: endl ; " "} " "private: " "static void SayHello ( ) { " "std :: cout << \"Hello World!\" << std :: endl ; " "} " "} ; " "void CTest :: Greeting ( bool ) { " "CTest :: SayHello ( ) ; " "} " "int main ( ) { " "CTest :: Greeting ( true ) ; " "return 0 ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template89() { // #8917 const char code[] = "struct Fred {\n" " template static void foo() { }\n" "};\n" "template void Fred::foo();\n" "template void Fred::foo();\n" "template <> void Fred::foo() { }\n" "template <> void Fred::foo() { }"; const char exp[] = "struct Fred { " "static void foo ( ) ; " "static void foo ( ) ; " "static void foo ( ) ; " "static void foo ( ) ; " "} ; " "void Fred :: foo ( ) { } " "void Fred :: foo ( ) { } " "void Fred :: foo ( ) { } " "void Fred :: foo ( ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template90() { // crash const char code[] = "template struct S1 {};\n" "void f(S1) {}\n" "template \n" "decltype(S1().~S1()) fun1() {};"; const char exp[] = "struct S1 ; " "void f ( S1 ) { } " "template < typename T > " "decltype ( S1 < T > ( ) . ~ S1 < T > ( ) ) fun1 ( ) { } ; " "struct S1 { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template91() { { const char code[] = "template T foo(T t) { return t; }\n" "template<> char foo(char a) { return a; }\n" "template<> int foo(int a) { return a; }\n" "template float foo(float);\n" "template double foo(double);"; const char exp[] = "int foo ( int a ) ; " "char foo ( char a ) ; " "float foo ( float t ) ; " "double foo ( double t ) ; " "char foo ( char a ) { return a ; } " "int foo ( int a ) { return a ; } " "float foo ( float t ) { return t ; } " "double foo ( double t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "struct Fred {\n" " template T foo(T t) { return t; }\n" " template<> char foo(char a) { return a; }\n" " template<> int foo(int a) { return a; }\n" "};\n" "template float Fred::foo(float);\n" "template double Fred::foo(double);"; const char exp[] = "struct Fred { " "int foo ( int a ) ; " "char foo ( char a ) ; " "float foo ( float t ) ; " "double foo ( double t ) ; " "char foo ( char a ) { return a ; } " "int foo ( int a ) { return a ; } " "} ; " "float Fred :: foo ( float t ) { return t ; } " "double Fred :: foo ( double t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "namespace NS1 {\n" " namespace NS2 {\n" " template T foo(T t) { return t; }\n" " template<> char foo(char a) { return a; }\n" " template<> int foo(int a) { return a; }\n" " template short NS2::foo(short);\n" " template long NS1::NS2::foo(long);\n" " }\n" " template float NS2::foo(float);\n" " template bool NS1::NS2::foo(bool);\n" "}\n" "template double NS1::NS2::foo(double);"; const char exp[] = "namespace NS1 { " "namespace NS2 { " "int foo ( int a ) ; " "char foo ( char a ) ; " "short foo ( short t ) ; " "long foo ( long t ) ; " "float foo ( float t ) ; " "bool foo ( bool t ) ; " "double foo ( double t ) ; " "char foo ( char a ) { return a ; } " "int foo ( int a ) { return a ; } " "} " "} " "short NS1 :: NS2 :: foo ( short t ) { return t ; } " "long NS1 :: NS2 :: foo ( long t ) { return t ; } " "float NS1 :: NS2 :: foo ( float t ) { return t ; } " "bool NS1 :: NS2 :: foo ( bool t ) { return t ; } " "double NS1 :: NS2 :: foo ( double t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "namespace NS1 {\n" " namespace NS {\n" " template T foo(T t) { return t; }\n" " template<> char foo(char a) { return a; }\n" " template<> int foo(int a) { return a; }\n" " template short NS::foo(short);\n" " template long NS1::NS::foo(long);\n" " }\n" " template float NS::foo(float);\n" " template bool NS1::NS::foo(bool);\n" "}\n" "template double NS1::NS::foo(double);"; const char exp[] = "namespace NS1 { " "namespace NS { " "int foo ( int a ) ; " "char foo ( char a ) ; " "short foo ( short t ) ; " "long foo ( long t ) ; " "float foo ( float t ) ; " "bool foo ( bool t ) ; " "double foo ( double t ) ; " "char foo ( char a ) { return a ; } " "int foo ( int a ) { return a ; } " "} " "} " "short NS1 :: NS :: foo ( short t ) { return t ; } " "long NS1 :: NS :: foo ( long t ) { return t ; } " "float NS1 :: NS :: foo ( float t ) { return t ; } " "bool NS1 :: NS :: foo ( bool t ) { return t ; } " "double NS1 :: NS :: foo ( double t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } } void template92() { const char code[] = "template void foo(T const& t) { }\n" "template<> void foo(double const& d) { }\n" "template void foo(float const& f);\n" "int main() {\n" " foo(2);\n" " foo(3.14);\n" " foo(3.14f);\n" "}"; const char exp[] = "void foo ( const double & d ) ; " "void foo ( const float & t ) ; " "void foo ( const int & t ) ; " "void foo ( const double & d ) { } " "int main ( ) { " "foo ( 2 ) ; " "foo ( 3.14 ) ; " "foo ( 3.14f ) ; " "} " "void foo ( const float & t ) { } " "void foo ( const int & t ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template93() { // crash const char code[] = "template \n" "void ForEach() { }\n" "template \n" "class Vector2 : public Vector {\n" " template \n" " void ForEach();\n" "public:\n" " void process();\n" "};\n" "template \n" "void Vector2::process() {\n" " ForEach();\n" "}\n" "Vector2 c;"; const char exp[] = "void ForEach ( ) ; " "class Vector2 ; " "Vector2 c ; " "class Vector2 : public Vector { " "template < typename Iterator > " "void ForEach ( ) ; " "public: " "void process ( ) ; " "} ; " "void Vector2 :: process ( ) { " "ForEach ( ) ; " "} " "void ForEach ( ) { " "}"; ASSERT_EQUALS(exp, tok(code)); } void template94() { // #8927 crash const char code[] = "template \n" "class Array { };\n" "template\n" "Array foo() {};\n" "template <> Array foo() { }\n" "template <> Array> foo>() { }\n" "template <> Array foo() { }\n" "template < typename T >\n" "Array matmul() {\n" " return foo( );\n" "}\n" "template Array> matmul>();"; const char exp[] = "class Array ; " "class Array> ; " "class Array ; " "Array foo ( ) ; " "Array> foo> ( ) ; " "Array foo ( ) ; " "template < typename T > " "Array < T > foo ( ) { } ; " "Array foo ( ) { } " "Array> foo> ( ) { } " "Array foo ( ) { } " "Array> matmul> ( ) ; " "Array> matmul> ( ) { " "return foo> ( ) ; " "} " "class Array { } ; " "class Array> { } ; " "class Array { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template95() { // #7417 const char code[] = "template \n" "T Value = 123;\n" "template<>\n" "int Value = 456;\n" "float f = Value;\n" "int i = Value;"; const char exp[] = "float Value ; Value = 123 ; " "int Value ; Value = 456 ; " "float f ; f = Value ; " "int i ; i = Value ;"; ASSERT_EQUALS(exp, tok(code)); } void template96() { // #7854 { const char code[] = "template\n" " constexpr long fib = fib + fib;\n" "template<>\n" " constexpr long fib<0> = 0;\n" "template<>\n" " constexpr long fib<1> = 1;\n" "long f0 = fib<0>;\n" "long f1 = fib<1>;\n" "long f2 = fib<2>;\n" "long f3 = fib<3>;"; const char exp[] = "constexpr long fib<2> = fib<1> + fib<0> ; " "constexpr long fib<3> = fib<2> + fib<1> ; " "constexpr long fib<0> = 0 ; " "constexpr long fib<1> = 1 ; " "long f0 ; f0 = fib<0> ; " "long f1 ; f1 = fib<1> ; " "long f2 ; f2 = fib<2> ; " "long f3 ; f3 = fib<3> ;"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "template\n" " constexpr long fib = fib + fib;\n" "template<>\n" " constexpr long fib<0> = 0;\n" "template<>\n" " constexpr long fib<1> = 1;\n" "long f5 = fib<5>;\n"; const char exp[] = "constexpr long fib<5> = fib<4> + fib<3> ; " "constexpr long fib<4> = fib<3> + fib<2> ; " "constexpr long fib<3> = fib<2> + fib<1> ; " "constexpr long fib<2> = fib<1> + fib<0> ; " "constexpr long fib<0> = 0 ; " "constexpr long fib<1> = 1 ; " "long f5 ; f5 = fib<5> ;"; ASSERT_EQUALS(exp, tok(code)); } } void template97() { const char code[] ="namespace NS1 {\n" " namespace NS2 {\n" " namespace NS3 {\n" " namespace NS4 {\n" " template\n" " class Fred {\n" " T * t;\n" " public:\n" " Fred() : t(nullptr) {}\n" " };\n" " }\n" " using namespace NS4;\n" " Fred fred_bool;\n" " NS4::Fred fred_char;\n" " }\n" " using namespace NS3;\n" " NS4::Fred fred_short;\n" " using namespace NS3::NS4;\n" " Fred fred_int;\n" " NS3::NS4::Fred fred_long;\n" " NS2::NS3::NS4::Fred fred_float;\n" " NS1::NS2::NS3::NS4::Fred fred_double;\n" " }\n" " using namespace NS2;\n" " NS3::NS4::Fred fred_float1;\n" " NS2::NS3::NS4::Fred fred_double1;\n" "}\n" "using namespace NS1::NS2::NS3::NS4;\n" "Fred fred_bool1;\n" "NS1::NS2::NS3::NS4::Fred fred_int1;"; const char exp[] = "namespace NS1 { " "namespace NS2 { " "namespace NS3 { " "namespace NS4 { " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "} " "using namespace NS4 ; " "NS4 :: Fred fred_bool ; " "NS4 :: Fred fred_char ; " "} " "using namespace NS3 ; " "NS3 :: NS4 :: Fred fred_short ; " "using namespace NS3 :: NS4 ; " "NS3 :: NS4 :: Fred fred_int ; " "NS3 :: NS4 :: Fred fred_long ; " "NS2 :: NS3 :: NS4 :: Fred fred_float ; " "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_double ; " "} " "using namespace NS2 ; " "NS2 :: NS3 :: NS4 :: Fred fred_float1 ; " "NS2 :: NS3 :: NS4 :: Fred fred_double1 ; " "} " "using namespace NS1 :: NS2 :: NS3 :: NS4 ; " "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_bool1 ; " "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_int1 ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "bool * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "char * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "short * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "int * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "long * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "float * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "double * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ;"; ASSERT_EQUALS(exp, tok(code)); } void template98() { // #8959 const char code[] = "template \n" "using unique_ptr_with_deleter = std::unique_ptr>;\n" "class A {};\n" "static void func() {\n" " unique_ptr_with_deleter tmp(new A(), [](A* a) {\n" " delete a;\n" " });\n" "}"; const char exp[] = "class A { } ; " "static void func ( ) { " "std :: unique_ptr < A , std :: function < void ( A * ) > > tmp ( new A ( ) , [ ] ( A * a ) { " "delete a ; " "} ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template99() { // #8960 const char code[] = "template \n" "class Base {\n" "public:\n" " using ArrayType = std::vector>;\n" "};\n" "using A = Base;\n" "static A::ArrayType array;\n"; const char exp[] = "class Base ; " "static std :: vector < Base > array ; " "class Base { " "public: " "} ;"; ASSERT_EQUALS(exp, tok(code)); } void template100() { // #8967 const char code[] = "enum class Device { I2C0, I2C1 };\n" "template \n" "const char* deviceFile;\n" "template <>\n" "const char* deviceFile = \"/tmp/i2c-0\";\n"; const char exp[] = "enum class Device { I2C0 , I2C1 } ; " "template < Device D > " "const char * deviceFile ; " "const char * deviceFile ; deviceFile = \"/tmp/i2c-0\" ;"; ASSERT_EQUALS(exp, tok(code)); } void template101() { // #8968 const char code[] = "class A {\n" "public:\n" " using ArrayType = std::vector;\n" " void func(typename ArrayType::size_type i) {\n" " }\n" "};"; const char exp[] = "class A { " "public: " "void func ( std :: vector < int > :: size_type i ) { " "} " "} ;"; ASSERT_EQUALS(exp, tok(code)); ASSERT_EQUALS("", errout.str()); } void template102() { // #9005 const char code[] = "namespace ns {\n" "template \n" "struct is_floating_point\n" ": std::integral_constant::value || true>\n" "{};\n" "}\n" "void f() {\n" " if(std::is_floating_point::value) {}\n" "}"; const char exp[] = "namespace ns { " "template < class T > " "struct is_floating_point " ": std :: integral_constant < bool , std :: is_floating_point < T > :: value || true > " "{ } ; " "} " "void f ( ) { " "if ( std :: is_floating_point < float > :: value ) { } " "}"; ASSERT_EQUALS(exp, tok(code)); } void template103() { const char code[] = "namespace sample {\n" " template \n" " class Sample {\n" " public:\n" " T function(T t);\n" " };\n" " template \n" " T Sample::function(T t) {\n" " return t;\n" " }\n" "}\n" "sample::Sample s1;"; const char exp[] = "namespace sample { " "class Sample ; " "} " "sample :: Sample s1 ; " "class sample :: Sample { " "public: " "int function ( int t ) ; " "} ; " "int sample :: Sample :: function ( int t ) { " "return t ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template104() { // #9021 const char code[] = "template < int i >\n" "auto key ( ) { return hana :: test :: ct_eq < i > { } ; }\n" "template < int i >\n" "auto val ( ) { return hana :: test :: ct_eq < - i > { } ; }\n" "template < int i , int j >\n" "auto p ( ) { return :: minimal_product ( key < i > ( ) , val < j > ( ) ) ; }\n" "int main ( ) {\n" " BOOST_HANA_CONSTANT_CHECK ( hana :: equal (\n" " hana :: at_key ( hana :: make_map ( p < 0 , 0 > ( ) ) , key < 0 > ( ) ) ,\n" " val < 0 > ( ) ) ) ;\n" "}"; const char exp[] = "auto key<0> ( ) ; " "auto val<0> ( ) ; " "auto p<0,0> ( ) ; " "int main ( ) { " "BOOST_HANA_CONSTANT_CHECK ( hana :: equal ( " "hana :: at_key ( hana :: make_map ( p<0,0> ( ) ) , key<0> ( ) ) , " "val<0> ( ) ) ) ; " "} " "auto p<0,0> ( ) { return :: minimal_product ( key<0> ( ) , val<0> ( ) ) ; } " "auto val<0> ( ) { return hana :: test :: ct_eq < - 0 > { } ; } " "auto key<0> ( ) { return hana :: test :: ct_eq < 0 > { } ; }"; ASSERT_EQUALS(exp, tok(code)); } void template105() { // #9076 const char code[] = "template